So I decided it was time to add support for full screen mode in my game. Previously when entering full screen, the buffer would just get scaled up, but now I want to do it properly.
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?