#include "gl4.h" #include "rend/gles/glcache.h" #include "rend/tileclip.h" /* Drawing and related state management Takes vertex, textures and renders to the currently set up target */ static gl4PipelineShader* CurrentShader; extern u32 gcflip; GLuint geom_fbo; GLuint stencilTexId; GLuint opaqueTexId; GLuint depthTexId; GLuint texSamplers[2]; GLuint depth_fbo; GLuint depthSaveTexId; static gl4PipelineShader *gl4GetProgram(bool cp_AlphaTest, bool pp_InsideClipping, bool pp_Texture, bool pp_UseAlpha, bool pp_IgnoreTexA, u32 pp_ShadInstr, bool pp_Offset, u32 pp_FogCtrl, bool pp_TwoVolumes, bool pp_Gouraud, bool pp_BumpMap, bool fog_clamping, bool palette, Pass pass) { u32 rv=0; rv |= pp_InsideClipping; rv <<= 1; rv |= cp_AlphaTest; rv <<= 1; rv |= pp_Texture; rv <<= 1; rv |= pp_UseAlpha; rv <<= 1; rv |= pp_IgnoreTexA; rv <<= 2; rv |= pp_ShadInstr; rv <<= 1; rv |= pp_Offset; rv <<= 2; rv |= pp_FogCtrl; rv <<= 1; rv |= pp_TwoVolumes; rv <<= 1; rv |= pp_Gouraud; rv <<= 1; rv |= pp_BumpMap; rv <<= 1; rv |= fog_clamping; rv <<= 1; rv |= palette; rv <<= 2; rv |= (int)pass; gl4PipelineShader *shader = &gl4.shaders[rv]; if (shader->program == 0) { shader->cp_AlphaTest = cp_AlphaTest; shader->pp_InsideClipping = pp_InsideClipping; shader->pp_Texture = pp_Texture; shader->pp_UseAlpha = pp_UseAlpha; shader->pp_IgnoreTexA = pp_IgnoreTexA; shader->pp_ShadInstr = pp_ShadInstr; shader->pp_Offset = pp_Offset; shader->pp_FogCtrl = pp_FogCtrl; shader->pp_TwoVolumes = pp_TwoVolumes; shader->pp_Gouraud = pp_Gouraud; shader->pp_BumpMap = pp_BumpMap; shader->fog_clamping = fog_clamping; shader->palette = palette; shader->pass = pass; gl4CompilePipelineShader(shader); } return shader; } static void SetTextureRepeatMode(int index, GLuint dir, u32 clamp, u32 mirror) { if (clamp) glSamplerParameteri(texSamplers[index], dir, GL_CLAMP_TO_EDGE); else glSamplerParameteri(texSamplers[index], dir, mirror ? GL_MIRRORED_REPEAT : GL_REPEAT); } static void SetBaseClipping() { if (gl4ShaderUniforms.base_clipping.enabled) { glcache.Enable(GL_SCISSOR_TEST); glcache.Scissor(gl4ShaderUniforms.base_clipping.x, gl4ShaderUniforms.base_clipping.y, gl4ShaderUniforms.base_clipping.width, gl4ShaderUniforms.base_clipping.height); } else glcache.Disable(GL_SCISSOR_TEST); } template static void SetGPState(const PolyParam* gp) { // Trilinear filtering. Ignore if texture isn't mipmapped (shenmue snowflakes) if (gp->pcw.Texture && gp->tsp.FilterMode > 1 && Type != ListType_Punch_Through && gp->tcw.MipMapped == 1) { gl4ShaderUniforms.trilinear_alpha = 0.25 * (gp->tsp.MipMapD & 0x3); if (gp->tsp.FilterMode == 2) // Trilinear pass A gl4ShaderUniforms.trilinear_alpha = 1.0 - gl4ShaderUniforms.trilinear_alpha; } else gl4ShaderUniforms.trilinear_alpha = 1.0; int clip_rect[4] = {}; TileClipping clipmode = GetTileClip(gp->tileclip, ViewportMatrix, clip_rect); bool palette = false; if (pass == Pass::Depth) { CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? 1 : 0, clipmode == TileClipping::Inside, Type == ListType_Punch_Through ? gp->pcw.Texture : 0, 1, gp->tsp.IgnoreTexA, 0, 0, 2, false, // TODO Can PT have two different textures for area 0 and 1 ?? false, false, false, false, pass); } else { // Two volumes mode only supported for OP and PT bool two_volumes_mode = (gp->tsp1.full != (u32)-1) && Type != ListType_Translucent; bool color_clamp = gp->tsp.ColorClamp && (pvrrc.fog_clamp_min != 0 || pvrrc.fog_clamp_max != 0xffffffff); int fog_ctrl = config::Fog ? gp->tsp.FogCtrl : 2; palette = BaseTextureCacheData::IsGpuHandledPaletted(gp->tsp, gp->tcw); CurrentShader = gl4GetProgram(Type == ListType_Punch_Through ? 1 : 0, clipmode == TileClipping::Inside, gp->pcw.Texture, gp->tsp.UseAlpha, gp->tsp.IgnoreTexA, gp->tsp.ShadInstr, gp->pcw.Offset, fog_ctrl, two_volumes_mode, gp->pcw.Gouraud, gp->tcw.PixelFmt == PixelBumpMap, color_clamp, palette, pass); } glcache.UseProgram(CurrentShader->program); if (palette) { if (gp->tcw.PixelFmt == PixelPal4) gl4ShaderUniforms.palette_index = gp->tcw.PalSelect << 4; else gl4ShaderUniforms.palette_index = (gp->tcw.PalSelect >> 4) << 8; } gl4ShaderUniforms.tsp0 = gp->tsp; gl4ShaderUniforms.tsp1 = gp->tsp1; gl4ShaderUniforms.tcw0 = gp->tcw; gl4ShaderUniforms.tcw1 = gp->tcw1; gl4ShaderUniforms.Set(CurrentShader); if (pass == Pass::Color && (Type == ListType_Translucent || Type == ListType_Punch_Through)) { glcache.Enable(GL_BLEND); glcache.BlendFunc(SrcBlendGL[gp->tsp.SrcInstr], DstBlendGL[gp->tsp.DstInstr]); } else glcache.Disable(GL_BLEND); if (clipmode == TileClipping::Inside) glUniform4f(CurrentShader->pp_ClipTest, clip_rect[0], clip_rect[1], clip_rect[0] + clip_rect[2], clip_rect[1] + clip_rect[3]); if (clipmode == TileClipping::Outside) { glcache.Enable(GL_SCISSOR_TEST); glcache.Scissor(clip_rect[0], clip_rect[1], clip_rect[2], clip_rect[3]); } else SetBaseClipping(); // This bit controls which pixels are affected by modvols const u32 stencil = gp->pcw.Shadow != 0 ? 0x80 : 0x0; glcache.StencilFunc(GL_ALWAYS, stencil, stencil); if (CurrentShader->pp_Texture) { for (int i = 0; i < 2; i++) { glActiveTexture(GL_TEXTURE0 + i); GLuint texid = (GLuint)(i == 0 ? gp->texid : gp->texid1); glBindTexture(GL_TEXTURE_2D, texid == (GLuint)-1 ? 0 : texid); if (texid != (GLuint)-1) { TSP tsp = i == 0 ? gp->tsp : gp->tsp1; glBindSampler(i, texSamplers[i]); SetTextureRepeatMode(i, GL_TEXTURE_WRAP_S, tsp.ClampU, tsp.FlipU); SetTextureRepeatMode(i, GL_TEXTURE_WRAP_T, tsp.ClampV, tsp.FlipV); //set texture filter mode if (tsp.FilterMode == 0) { //disable filtering, mipmaps glSamplerParameteri(texSamplers[i], GL_TEXTURE_MIN_FILTER, GL_NEAREST); glSamplerParameteri(texSamplers[i], GL_TEXTURE_MAG_FILTER, GL_NEAREST); } else { //bilinear filtering //PowerVR supports also trilinear via two passes, but we ignore that for now bool mipmapped = gp->tcw.MipMapped != 0 && gp->tcw.ScanOrder == 0 && config::UseMipmaps; glSamplerParameteri(texSamplers[i], GL_TEXTURE_MIN_FILTER, mipmapped ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); glSamplerParameteri(texSamplers[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (mipmapped) glSamplerParameterf(texSamplers[i], GL_TEXTURE_LOD_BIAS, D_Adjust_LoD_Bias[tsp.MipMapD]); } } } glActiveTexture(GL_TEXTURE0); } //set cull mode ! //cflip is required when exploding triangles for triangle sorting //gcflip is global clip flip, needed for when rendering to texture due to mirrored Y direction SetCull(gp->isp.CullMode ^ gcflip); //set Z mode, only if required if (Type == ListType_Punch_Through || (pass == Pass::Depth && SortingEnabled)) { glcache.DepthFunc(GL_GEQUAL); } else if (Type == ListType_Opaque || (Type == ListType_Translucent && !SortingEnabled)) { glcache.DepthFunc(Zfunction[gp->isp.DepthMode]); } if (pass == Pass::Depth || pass == Pass::Color) { // Z Write Disable seems to be ignored for punch-through polys // Fixes Worms World Party, Bust-a-Move 4 and Re-Volt if (Type == ListType_Punch_Through) glcache.DepthMask(GL_TRUE); else glcache.DepthMask(!gp->isp.ZWriteDis); } else glcache.DepthMask(GL_FALSE); } template static void DrawList(const List& gply, int first, int count) { PolyParam* params = &gply.head()[first]; if (count == 0) return; while (count-- > 0) { if (params->count > 2) { if ((Type == ListType_Opaque || (Type == ListType_Translucent && !SortingEnabled)) && params->isp.DepthMode == 0) { // depthFunc = never params++; continue; } if (pass == Pass::OIT && Type == ListType_Translucent && params->tsp.SrcInstr == 0 && params->tsp.DstInstr == 1) { // dst = dst params++; continue; } gl4ShaderUniforms.poly_number = params - gply.head(); SetGPState(params); glDrawElements(GL_TRIANGLE_STRIP, params->count, GL_UNSIGNED_INT, (GLvoid*)(sizeof(u32) * params->first)); glCheck(); } params++; } } void gl4SetupMainVBO() { glBindVertexArray(gl4.vbo.main_vao); glBindBuffer(GL_ARRAY_BUFFER, gl4.vbo.geometry); glCheck(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl4.vbo.idxs); glCheck(); //setup vertex buffers attrib pointers glEnableVertexAttribArray(VERTEX_POS_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,x)); glCheck(); glEnableVertexAttribArray(VERTEX_COL_BASE_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_COL_BASE_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex,col)); glCheck(); glEnableVertexAttribArray(VERTEX_COL_OFFS_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_COL_OFFS_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex,spc)); glCheck(); glEnableVertexAttribArray(VERTEX_UV_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex,u)); glCheck(); glEnableVertexAttribArray(VERTEX_COL_BASE1_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_COL_BASE1_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, col1)); glCheck(); glEnableVertexAttribArray(VERTEX_COL_OFFS1_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_COL_OFFS1_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(Vertex), (void*)offsetof(Vertex, spc1)); glCheck(); glEnableVertexAttribArray(VERTEX_UV1_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_UV1_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, u1)); glCheck(); } void gl4SetupModvolVBO() { glBindVertexArray(gl4.vbo.modvol_vao); glBindBuffer(GL_ARRAY_BUFFER, gl4.vbo.modvols); glCheck(); //setup vertex buffers attrib pointers glEnableVertexAttribArray(VERTEX_POS_ARRAY); glCheck(); glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(float)*3, (void*)0); glCheck(); } static void DrawModVols(int first, int count) { if (count == 0 || pvrrc.modtrig.used() == 0) return; glBindVertexArray(gl4.vbo.modvol_vao); glcache.Disable(GL_BLEND); SetBaseClipping(); glcache.UseProgram(gl4.modvol_shader.program); glcache.Enable(GL_DEPTH_TEST); glcache.DepthMask(GL_FALSE); glcache.DepthFunc(GL_GREATER); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); ModifierVolumeParam* params = &pvrrc.global_param_mvo.head()[first]; int mod_base = -1; for (int cmv = 0; cmv < count; cmv++) { ModifierVolumeParam& param = params[cmv]; if (param.count == 0) continue; u32 mv_mode = param.isp.DepthMode; if (mod_base == -1) mod_base = param.first; if (!param.isp.VolumeLast && mv_mode > 0) SetMVS_Mode(Or, param.isp); // OR'ing (open volume or quad) else SetMVS_Mode(Xor, param.isp); // XOR'ing (closed volume) glDrawArrays(GL_TRIANGLES, param.first * 3, param.count * 3); if (mv_mode == 1 || mv_mode == 2) { // Sum the area SetMVS_Mode(mv_mode == 1 ? Inclusion : Exclusion, param.isp); glDrawArrays(GL_TRIANGLES, mod_base * 3, (param.first + param.count - mod_base) * 3); mod_base = -1; } } //restore states glBindVertexArray(gl4.vbo.main_vao); glcache.Enable(GL_DEPTH_TEST); glcache.DepthMask(GL_TRUE); } void renderABuffer(); void DrawTranslucentModVols(int first, int count); void checkOverflowAndReset(); static GLuint CreateColorFBOTexture(int width, int height) { GLuint texId = glcache.GenTexture(); glcache.BindTexture(GL_TEXTURE_2D, texId); glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texId, 0); glCheck(); return texId; } void gl4CreateTextures(int width, int height) { if (geom_fbo == 0) { glGenFramebuffers(1, &geom_fbo); } glBindFramebuffer(GL_FRAMEBUFFER, geom_fbo); stencilTexId = glcache.GenTexture(); glBindTexture(GL_TEXTURE_2D, stencilTexId); glCheck(); glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX); // OpenGL >= 4.3 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // Using glTexStorage2D instead of glTexImage2D to satisfy requirement GL_TEXTURE_IMMUTABLE_FORMAT=true, needed for glTextureView below glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH32F_STENCIL8, width, height); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, stencilTexId, 0); glCheck(); glCheck(); opaqueTexId = CreateColorFBOTexture(width, height); depthTexId = glcache.GenTexture(); glTextureView(depthTexId, GL_TEXTURE_2D, stencilTexId, GL_DEPTH32F_STENCIL8, 0, 1, 0, 1); glCheck(); glBindTexture(GL_TEXTURE_2D, depthTexId); glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glCheck(); GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); verify(uStatus == GL_FRAMEBUFFER_COMPLETE); } void gl4DrawStrips(GLuint output_fbo, int width, int height) { checkOverflowAndReset(); glBindFramebuffer(GL_FRAMEBUFFER, geom_fbo); if (texSamplers[0] == 0) glGenSamplers(2, texSamplers); glcache.DepthMask(GL_TRUE); glClearDepthf(0.0); glStencilMask(0xFF); glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glCheck(); //Draw the strips ! //We use sampler 0 glActiveTexture(GL_TEXTURE0); glProvokingVertex(GL_LAST_VERTEX_CONVENTION); RenderPass previous_pass = {}; int render_pass_count = pvrrc.render_passes.used(); for (int render_pass = 0; render_pass < render_pass_count; render_pass++) { const RenderPass& current_pass = pvrrc.render_passes.head()[render_pass]; // Check if we can skip this pass, in part or completely, in case nothing is drawn (Cosmic Smash) bool skip_op_pt = true; bool skip_tr = true; for (u32 j = previous_pass.op_count; skip_op_pt && j < current_pass.op_count; j++) { if (pvrrc.global_param_op.head()[j].count > 2) skip_op_pt = false; } for (u32 j = previous_pass.pt_count; skip_op_pt && j < current_pass.pt_count; j++) { if (pvrrc.global_param_pt.head()[j].count > 2) skip_op_pt = false; } for (u32 j = previous_pass.tr_count; skip_tr && j < current_pass.tr_count; j++) { if (pvrrc.global_param_tr.head()[j].count > 2) skip_tr = false; } if (skip_op_pt && skip_tr) { previous_pass = current_pass; continue; } DEBUG_LOG(RENDERER, "Render pass %d/%d OP %d PT %d TR %d autosort %d", render_pass + 1, render_pass_count, current_pass.op_count - previous_pass.op_count, current_pass.pt_count - previous_pass.pt_count, current_pass.tr_count - previous_pass.tr_count, current_pass.autosort); glBindVertexArray(gl4.vbo.main_vao); if (!skip_op_pt) { // // PASS 1: Geometry pass to update depth and stencil // if (render_pass > 0) { // Make a copy of the depth buffer that will be reused in pass 2 if (depth_fbo == 0) glGenFramebuffers(1, &depth_fbo); glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo); if (depthSaveTexId == 0) { depthSaveTexId = glcache.GenTexture(); glBindTexture(GL_TEXTURE_2D, depthSaveTexId); glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH32F_STENCIL8, max_image_width, max_image_height, 0, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, NULL); glCheck(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthSaveTexId, 0); glCheck(); } GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER); verify(uStatus == GL_FRAMEBUFFER_COMPLETE); glBindFramebuffer(GL_READ_FRAMEBUFFER, geom_fbo); glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_DEPTH_BUFFER_BIT, GL_NEAREST); glCheck(); glBindFramebuffer(GL_FRAMEBUFFER, geom_fbo); } glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glcache.Enable(GL_DEPTH_TEST); glcache.DepthMask(GL_TRUE); glcache.Enable(GL_STENCIL_TEST); glcache.StencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); DrawList(pvrrc.global_param_op, previous_pass.op_count, current_pass.op_count - previous_pass.op_count); DrawList(pvrrc.global_param_pt, previous_pass.pt_count, current_pass.pt_count - previous_pass.pt_count); // Modifier volumes if (config::ModifierVolumes) DrawModVols(previous_pass.mvo_count, current_pass.mvo_count - previous_pass.mvo_count); // // PASS 2: Render OP and PT to fbo // if (render_pass == 0) { glcache.DepthMask(GL_TRUE); glClearDepthf(0.0); glClear(GL_DEPTH_BUFFER_BIT); } else { // Restore the depth buffer from the last render pass // FIXME This is pretty slow apparently (CS) glBindFramebuffer(GL_DRAW_FRAMEBUFFER, geom_fbo); glBindFramebuffer(GL_READ_FRAMEBUFFER, depth_fbo); glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_DEPTH_BUFFER_BIT, GL_NEAREST); glCheck(); glBindFramebuffer(GL_FRAMEBUFFER, geom_fbo); } glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glcache.Disable(GL_STENCIL_TEST); // Bind stencil buffer for the fragment shader (shadowing) glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, stencilTexId); glActiveTexture(GL_TEXTURE0); glCheck(); //Opaque DrawList(pvrrc.global_param_op, previous_pass.op_count, current_pass.op_count - previous_pass.op_count); //Alpha tested DrawList(pvrrc.global_param_pt, previous_pass.pt_count, current_pass.pt_count - previous_pass.pt_count); // Unbind stencil glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); } if (!skip_tr) { // // PASS 3: Render TR to a-buffers // if (current_pass.autosort) { glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); glcache.Disable(GL_DEPTH_TEST); // Although the depth test is disabled and thus writes to the depth buffer are also disabled, // AMD cards have serious issues when the depth/stencil texture is still bound to the framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, depthTexId); glActiveTexture(GL_TEXTURE0); DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); // Translucent modifier volumes if (config::ModifierVolumes) { SetBaseClipping(); DrawTranslucentModVols(previous_pass.mvo_tr_count, current_pass.mvo_tr_count - previous_pass.mvo_tr_count); } // Rebind the depth/stencil texture to the framebuffer glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, stencilTexId, 0); if (render_pass < render_pass_count - 1) { // // PASS 3b: Geometry pass with TR to update the depth for the next TA render pass // // Unbind depth texture glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); glcache.Enable(GL_DEPTH_TEST); DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); } } else { glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glcache.Enable(GL_DEPTH_TEST); DrawList(pvrrc.global_param_tr, previous_pass.tr_count, current_pass.tr_count - previous_pass.tr_count); glcache.Disable(GL_BLEND); } glCheck(); if (render_pass < render_pass_count - 1) { // // PASS 3c: Render a-buffer to temporary texture // GLuint texId = CreateColorFBOTexture(max_image_width, max_image_height); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glActiveTexture(GL_TEXTURE0); glBindSampler(0, 0); glBindTexture(GL_TEXTURE_2D, opaqueTexId); renderABuffer(); glcache.DeleteTextures(1, &opaqueTexId); opaqueTexId = texId; glCheck(); } } if (!skip_op_pt && render_pass < render_pass_count - 1) { // Clear the stencil from this pass glStencilMask(0xFF); glClear(GL_STENCIL_BUFFER_BIT); } previous_pass = current_pass; } // // PASS 4: Render a-buffers to screen // glBindFramebuffer(GL_FRAMEBUFFER, output_fbo); glCheck(); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glActiveTexture(GL_TEXTURE0); glBindSampler(0, 0); glBindTexture(GL_TEXTURE_2D, opaqueTexId); renderABuffer(); } static void gl4_draw_quad_texture(GLuint texture, float w, float h) { glcache.Disable(GL_SCISSOR_TEST); glcache.Disable(GL_DEPTH_TEST); glcache.Disable(GL_STENCIL_TEST); glcache.Disable(GL_CULL_FACE); glcache.Disable(GL_BLEND); gl4ShaderUniforms.trilinear_alpha = 1.0; CurrentShader = gl4GetProgram(0, false, 1, 0, 1, 0, 0, 2, false, false, false, false, false, Pass::Color); glcache.UseProgram(CurrentShader->program); gl4ShaderUniforms.Set(CurrentShader); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture); struct Vertex vertices[] = { { 0, 0 + h, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, 0, 1 }, { 0, 0, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, 0, 0 }, { 0 + w, 0 + h, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, 1, 1 }, { 0 + w, 0, 1, { 255, 255, 255, 255 }, { 0, 0, 0, 0 }, 1, 0 }, }; GLushort indices[] = { 0, 1, 2, 1, 3 }; glBindVertexArray(gl4.vbo.main_vao); glBindBuffer(GL_ARRAY_BUFFER, gl4.vbo.geometry); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gl4.vbo.idxs); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STREAM_DRAW); glDrawElements(GL_TRIANGLE_STRIP, 5, GL_UNSIGNED_SHORT, (void *)0); glCheck(); } void gl4DrawFramebuffer(float w, float h) { gl4_draw_quad_texture(fbTextureId, w, h); glcache.DeleteTextures(1, &fbTextureId); fbTextureId = 0; } bool gl4_render_output_framebuffer() { glViewport(0, 0, screen_width, screen_height); glcache.Disable(GL_SCISSOR_TEST); if (gl.ofbo.fbo == 0) return false; glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBlitFramebuffer(0, 0, gl.ofbo.width, gl.ofbo.height, 0, 0, screen_width, screen_height, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBindFramebuffer(GL_FRAMEBUFFER, 0); return true; }