GBA Video: Add GL mode 2

This commit is contained in:
Vicki Pfau 2019-05-10 14:56:27 -07:00
parent 7edf7cdb15
commit 3e0675f539
2 changed files with 131 additions and 32 deletions

View File

@ -93,6 +93,8 @@ struct GBAVideoGLRenderer {
uint16_t bldy; uint16_t bldy;
GBAMosaicControl mosaic; GBAMosaicControl mosaic;
int scale;
}; };
void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer); void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer);

View File

@ -54,8 +54,8 @@ static const char* const _vertexShader =
"}"; "}";
static const char* const _renderTile16 = static const char* const _renderTile16 =
"vec4 renderTile(int tile, int tileBase, int paletteId, ivec2 localCoord) {\n" "vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
" int address = tileBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n" " int address = charBase + tile * 16 + (localCoord.x >> 2) + (localCoord.y << 1);\n"
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n" " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
" int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n" " int entry = int(halfrow[3 - (localCoord.x & 3)] * 15.9);\n"
" vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n" " vec4 color = texelFetch(palette, ivec2(entry, paletteId), 0);\n"
@ -68,8 +68,8 @@ static const char* const _renderTile16 =
"}"; "}";
static const char* const _renderTile256 = static const char* const _renderTile256 =
"vec4 renderTile(int tile, int tileBase, int paletteId, ivec2 localCoord) {\n" "vec4 renderTile(int tile, int paletteId, ivec2 localCoord) {\n"
" int address = tileBase + tile * 32 + (localCoord.x >> 1) + (localCoord.y << 2);\n" " int address = charBase + tile * 32 + (localCoord.x >> 1) + (localCoord.y << 2);\n"
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n" " vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
" int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n" " int entry = int(halfrow[3 - 2 * (localCoord.x & 1)] * 15.9);\n"
" int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n" " int pal2 = int(halfrow[2 - 2 * (localCoord.x & 1)] * 15.9);\n"
@ -91,7 +91,7 @@ static const char* const _renderMode0 =
"uniform int size;\n" "uniform int size;\n"
"uniform ivec2 offset;\n" "uniform ivec2 offset;\n"
"vec4 renderTile(int tile, int tileBase, int paletteId, ivec2 localCoord);\n" "vec4 renderTile(int tile, int paletteId, ivec2 localCoord);\n"
"void main() {\n" "void main() {\n"
" ivec2 coord = ivec2(texCoord) + offset;\n" " ivec2 coord = ivec2(texCoord) + offset;\n"
@ -109,15 +109,70 @@ static const char* const _renderMode0 =
" coord.y ^= 7;\n" " coord.y ^= 7;\n"
" }\n" " }\n"
" int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (flags & 0x3) * 256;\n" " int tile = int(map.a * 15.9) + int(map.b * 15.9) * 16 + (flags & 0x3) * 256;\n"
" gl_FragColor = renderTile(tile, charBase, int(map.r * 15.9), coord & 7);\n" " gl_FragColor = renderTile(tile, int(map.r * 15.9), coord & 7);\n"
"}";
static const char* const _fetchTileOverflow =
"vec4 fetchTile(ivec2 coord) {\n"
" int sizeAdjusted = (0x8000 << size) - 1;\n"
" coord &= sizeAdjusted;\n"
" return renderTile(coord);\n"
"}";
static const char* const _fetchTileNoOverflow =
"vec4 fetchTile(ivec2 coord) {\n"
" int sizeAdjusted = (0x8000 << size) - 1;\n"
" ivec2 outerCoord = coord & ~sizeAdjusted;\n"
" if ((outerCoord.x | outerCoord.y) != 0) {\n"
" vec4 color = texelFetch(palette, ivec2(0, 0), 0);\n"
" color.a = 0;\n"
" return color;\n"
" }\n"
" return renderTile(coord);\n"
"}";
static const char* const _renderMode2 =
"varying vec2 texCoord;\n"
"uniform sampler2D vram;\n"
"uniform sampler2D palette;\n"
"uniform int screenBase;\n"
"uniform int charBase;\n"
"uniform int size;\n"
"uniform ivec2 offset;\n"
"uniform mat2x2 transform;\n"
"vec4 fetchTile(ivec2 coord);\n"
"vec4 renderTile(ivec2 coord) {\n"
" int map = (coord.x >> 11) + (((coord.y >> 7) & 0x7F0) << size);\n"
" int mapAddress = screenBase + (map >> 1);\n"
" vec4 twomaps = texelFetch(vram, ivec2(mapAddress & 255, mapAddress >> 8), 0);\n"
" int tile = int(twomaps[3 - 2 * (map & 1)] * 15.9) + int(twomaps[2 - 2 * (map & 1)] * 15.9) * 16;\n"
" int address = charBase + tile * 32 + ((coord.x >> 9) & 3) + ((coord.y >> 6) & 0x1C);\n"
" vec4 halfrow = texelFetch(vram, ivec2(address & 255, address >> 8), 0);\n"
" int entry = int(halfrow[3 - ((coord.x >> 7) & 2)] * 15.9);\n"
" int pal2 = int(halfrow[2 - ((coord.x >> 7) & 2)] * 15.9);\n"
" vec4 color = texelFetch(palette, ivec2(entry, pal2), 0);\n"
" if (pal2 > 0 || entry > 0) {\n"
" color.a = 1.;\n"
" } else {\n"
" color.a = 0.;\n"
" }\n"
" return color;\n"
"}"
"void main() {\n"
" ivec2 coord = ivec2(transform[0] * texCoord.x) + ivec2(transform[1] * fract(texCoord.y)) + offset;\n"
" gl_FragColor = fetchTile(coord);\n"
"}"; "}";
static const char* const _composite = static const char* const _composite =
"varying vec2 texCoord;\n" "varying vec2 texCoord;\n"
"uniform sampler2D layer;\n" "uniform sampler2D layer;\n"
"uniform int scale;\n"
"void main() {\n" "void main() {\n"
" vec4 color = texelFetch(layer, ivec2(texCoord), 0);\n" " vec4 color = texelFetch(layer, ivec2(texCoord * scale), 0);\n"
" if (color.a == 0) {\n" " if (color.a == 0) {\n"
" discard;\n" " discard;\n"
" }\n" " }\n"
@ -149,6 +204,8 @@ void GBAVideoGLRendererCreate(struct GBAVideoGLRenderer* renderer) {
renderer->d.disableBG[2] = false; renderer->d.disableBG[2] = false;
renderer->d.disableBG[3] = false; renderer->d.disableBG[3] = false;
renderer->d.disableOBJ = false; renderer->d.disableOBJ = false;
renderer->scale = 1;
} }
void _compileBackground(struct GBAVideoGLRenderer* glRenderer, GLuint program, const char** shaderBuffer, int shaderBufferLines, GLuint vs, char* log) { void _compileBackground(struct GBAVideoGLRenderer* glRenderer, GLuint program, const char** shaderBuffer, int shaderBufferLines, GLuint vs, char* log) {
@ -196,7 +253,7 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->layers[2], 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->layers[2], 0);
glBindTexture(GL_TEXTURE_2D, glRenderer->layers[3]); glBindTexture(GL_TEXTURE_2D, glRenderer->layers[3]);
@ -204,7 +261,7 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, glRenderer->layers[3], 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, glRenderer->layers[3], 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@ -220,7 +277,7 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->bg[i].tex, 0); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glRenderer->bg[i].tex, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
@ -255,6 +312,14 @@ void GBAVideoGLRendererInit(struct GBAVideoRenderer* renderer) {
shaderBuffer[2] = _renderTile256; shaderBuffer[2] = _renderTile256;
_compileBackground(glRenderer, glRenderer->bgProgram[1], shaderBuffer, 3, vs, log); _compileBackground(glRenderer, glRenderer->bgProgram[1], shaderBuffer, 3, vs, log);
shaderBuffer[1] = _renderMode2;
shaderBuffer[2] = _fetchTileOverflow;
_compileBackground(glRenderer, glRenderer->bgProgram[2], shaderBuffer, 3, vs, log);
shaderBuffer[2] = _fetchTileNoOverflow;
_compileBackground(glRenderer, glRenderer->bgProgram[3], shaderBuffer, 3, vs, log);
shaderBuffer[1] = _composite; shaderBuffer[1] = _composite;
_compileBackground(glRenderer, glRenderer->compositeProgram, shaderBuffer, 2, vs, log); _compileBackground(glRenderer, glRenderer->compositeProgram, shaderBuffer, 2, vs, log);
@ -530,7 +595,7 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f); glClearColor(((backdrop >> 16) & 0xFF) / 256., ((backdrop >> 8) & 0xFF) / 256., (backdrop & 0xFF) / 256., 0.f);
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
glScissor(0, y, GBA_VIDEO_HORIZONTAL_PIXELS, 1); glScissor(0, y * glRenderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, glRenderer->scale);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
if (y == 0) { if (y == 0) {
@ -554,7 +619,7 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
break; break;
case 1: case 1:
case 2: case 2:
//GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y); GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[2], y);
break; break;
case 3: case 3:
//GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y); //GBAVideoGLRendererDrawBackgroundMode3(glRenderer, &glRenderer->bg[2], y);
@ -573,7 +638,7 @@ void GBAVideoGLRendererDrawScanline(struct GBAVideoRenderer* renderer, int y) {
GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[3], y); GBAVideoGLRendererDrawBackgroundMode0(glRenderer, &glRenderer->bg[3], y);
break; break;
case 2: case 2:
//GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y); GBAVideoGLRendererDrawBackgroundMode2(glRenderer, &glRenderer->bg[3], y);
break; break;
} }
} }
@ -591,7 +656,7 @@ void GBAVideoGLRendererFinishFrame(struct GBAVideoRenderer* renderer) {
glFinish(); glFinish();
glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]); glBindFramebuffer(GL_FRAMEBUFFER, glRenderer->fbo[1]);
glPixelStorei(GL_PACK_ROW_LENGTH, glRenderer->outputBufferStride); glPixelStorei(GL_PACK_ROW_LENGTH, glRenderer->outputBufferStride);
glReadPixels(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS, GL_RGBA, GL_UNSIGNED_BYTE, glRenderer->outputBuffer); glReadPixels(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * glRenderer->scale, GBA_VIDEO_VERTICAL_PIXELS * glRenderer->scale, GL_RGBA, GL_UNSIGNED_BYTE, glRenderer->outputBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }
@ -676,6 +741,27 @@ static void GBAVideoGLRendererWriteBLDCNT(struct GBAVideoGLRenderer* renderer, u
renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value); renderer->target2Bd = GBARegisterBLDCNTGetTarget2Bd(value);
} }
static void _compositeLayer(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
if ((y & 0x1F) != 0x1F) {
return;
}
glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[1]);
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
glScissor(0, (y * renderer->scale) % (0x20 * renderer->scale), GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, 0x20 * renderer->scale);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
glUseProgram(renderer->compositeProgram);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, background->tex);
glUniform3i(0, 0x20, y & ~0x1F, (background->priority << 3) + (background->index << 1) + 1);
glUniform1i(1, 0);
glUniform1i(2, renderer->scale);
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
glDisable(GL_DEPTH_TEST);
}
void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) { void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
int inY = y + background->y; int inY = y + background->y;
int yBase = inY & 0xFF; int yBase = inY & 0xFF;
@ -685,8 +771,8 @@ void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer,
yBase += (inY & 0x100) << 1; yBase += (inY & 0x100) << 1;
} }
glBindFramebuffer(GL_FRAMEBUFFER, background->fbo); glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
glScissor(0, y, GBA_VIDEO_HORIZONTAL_PIXELS, 1); glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
glUseProgram(renderer->bgProgram[background->multipalette ? 1 : 0]); glUseProgram(renderer->bgProgram[background->multipalette ? 1 : 0]);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, renderer->vramTex); glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
@ -703,22 +789,33 @@ void GBAVideoGLRendererDrawBackgroundMode0(struct GBAVideoGLRenderer* renderer,
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
if ((y & 0x1F) == 0x1F) { _compositeLayer(renderer, background, y);
glBindFramebuffer(GL_FRAMEBUFFER, renderer->fbo[1]);
glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS, GBA_VIDEO_VERTICAL_PIXELS); glBindFramebuffer(GL_FRAMEBUFFER, 0);
glScissor(0, y & ~0x1F, GBA_VIDEO_HORIZONTAL_PIXELS, 0x20); }
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST); void GBAVideoGLRendererDrawBackgroundMode2(struct GBAVideoGLRenderer* renderer, struct GBAVideoGLBackground* background, int y) {
glUseProgram(renderer->compositeProgram); glBindFramebuffer(GL_FRAMEBUFFER, background->fbo);
glActiveTexture(GL_TEXTURE0); glViewport(0, 0, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, GBA_VIDEO_VERTICAL_PIXELS * renderer->scale);
glBindTexture(GL_TEXTURE_2D, background->tex); glScissor(0, y * renderer->scale, GBA_VIDEO_HORIZONTAL_PIXELS * renderer->scale, renderer->scale);
glUniform3i(0, 0x20, y & ~0x1F, (background->priority << 3) + (background->index << 1) + 1); glUseProgram(renderer->bgProgram[background->overflow ? 2 : 3]);
glUniform1i(1, 0); glActiveTexture(GL_TEXTURE0);
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices); glBindTexture(GL_TEXTURE_2D, renderer->vramTex);
glEnableVertexAttribArray(0); glActiveTexture(GL_TEXTURE0 + 1);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4); glBindTexture(GL_TEXTURE_2D, renderer->paletteTex);
glDisable(GL_DEPTH_TEST); glUniform3i(0, 1, y, 0);
} glUniform1i(1, 0);
glUniform1i(2, 1);
glUniform1i(3, background->screenBase);
glUniform1i(4, background->charBase);
glUniform1i(5, background->size);
glUniform2i(6, background->sx, background->sy);
glUniformMatrix2fv(7, 1, GL_FALSE, (GLfloat[]) { background->dx, background->dy, background->dmx, background->dmy });
glVertexAttribPointer(0, 2, GL_INT, GL_FALSE, 0, _vertices);
glEnableVertexAttribArray(0);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
_compositeLayer(renderer, background, y);
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
} }