I'm using my own software renderer to do all my game's rendering, and then OpenGL does the final display of the texture that comes back from the game.
In order to keep things as simple as possible, I decided to allocate the memory for the texture that is passed to the game big enough for the whole screen. That way I don't need to reallocate it when the window changes size, and I can just pass in the new width & height to the game. As far as dealing with the OpenGL side of things, I first tried just changing the current width & height values, but that resulted in a GL_INVALID_VALUE (1281) error when calling glTexSubImage2D, and really, that's not too surprising since the texture was initialized with a smaller size in the setup code with the call to glTexImage2D.
Here's the setup code for OpenGL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | CGSize screenSize = ...; _textureSize.width = screenSize.width; _textureSize.height = screenSize.height; _currentSize.width = (GLsizei)self.bounds.size.width; // This is the actual width of the window. _currentSize.height = (GLsizei)self.bounds.size.height; // This is the actual height of the window. // Adds apron around the texture. GLsizei allocWidth = _textureSize.width + 2*CLIP_REGION_PIXELS; GLsizei allocHeight = _textureSize.height + 2*CLIP_REGION_PIXELS; _textureMemory = (uint32_t *)osx_allocate_memory(allocWidth * allocHeight * sizeof(uint32_t)); memset(_textureMemory, 0, allocWidth * allocHeight * sizeof(uint32_t)); _texture = _textureMemory + (intptr_t)(CLIP_REGION_PIXELS * allocWidth + CLIP_REGION_PIXELS); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glGenTextures(1, &_textureId); glBindTexture(GL_TEXTURE_2D, _textureId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _currentSize.width, _currentSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); |
At the end of the frame, the buffer is drawn to the screen like this (at first, when the window changed size I would just update the values of _currentSize.width and _currentSize.height):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | - (void)render { glClear(GL_COLOR_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D, _textureId); glEnable(GL_TEXTURE_2D); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, _currentSize.width, _currentSize.height, GL_RGBA, GL_UNSIGNED_BYTE, _texture); // ** here I would get the GL_INVALID_VALUE error **// glBegin(GL_QUADS); glTexCoord2f(0.f, 1.f); glVertex2f(-1.f, -1.f); glTexCoord2f(0.f, 0.f); glVertex2f(-1.f, 1.f); glTexCoord2f(1.f, 0.f); glVertex2f(1.f, 1.f); glTexCoord2f(1.f, 1.f); glVertex2f(1.f, -1.f); glEnd(); } |
When I exited full screen mode, everything was fine again, so it's clearly an issue with calling glTexSubImage2D with the new & bigger size. But then I tried resizing the texture by calling glTexImage2D with the new size when the window changed size, but that still resulted in the same error. Next, I tried deleting the texture and re-generating it when the window changes size:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | - (void)reshape { GLsizei newWidth = self.bounds.size.width; GLsizei newHeight = self.bounds.size.height; if (newWidth != _currentSize.with || newHeight != _currentSize.height) { glDeleteTextures(1, &_textureId); _textureId = 0; glGenTextures(1, &_textureId); glBindTexture(GL_TEXTURE_2D, _textureId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, newWidth, newHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glBindTexture(GL_TEXTURE_2D, 0); _currentSize.width = newWidth; _currentSize.height = newHeight; } } |
Instead of getting a GL_INVALID_VALUE error, I get a GL_INVALID_OPERATION error (1282). It's worth noting that the render method and the reshape method are called on a different thread, and I did try adding synchronization between the two methods but that didn't help.
The one way I did get it to work was to call glTexImage2D instead of glTexSubImage2D in the render call. As far as I understand though, glTexImage2D is for creating a texture and glTexSubImage2D is for copying pixels into the texture, so it's much more efficient to call glTexSubImage2D instead of glTexImage2D.
Any ideas what I'm doing wrong here or how I need to approach resizing the OpenGL texture when the window changes size?