diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 61d7ee960b..da6731cc04 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -948,6 +948,10 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { // Blending case PICA_REG_INDEX(framebuffer.output_merger.alphablend_enable): + if (GLES) { + // With GLES, we need this in the fragment shader to emulate logic operations + shader_dirty = true; + } SyncBlendEnabled(); break; case PICA_REG_INDEX(framebuffer.output_merger.alpha_blending): @@ -1068,6 +1072,10 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { // Logic op case PICA_REG_INDEX(framebuffer.output_merger.logic_op): + if (GLES) { + // With GLES, we need this in the fragment shader to emulate logic operations + shader_dirty = true; + } SyncLogicOp(); break; @@ -1822,11 +1830,31 @@ void RasterizerOpenGL::SyncAlphaTest() { } void RasterizerOpenGL::SyncLogicOp() { - state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.framebuffer.output_merger.logic_op); + const auto& regs = Pica::g_state.regs; + state.logic_op = PicaToGL::LogicOp(regs.framebuffer.output_merger.logic_op); + + if (GLES) { + if (!regs.framebuffer.output_merger.alphablend_enable) { + if (regs.framebuffer.output_merger.logic_op == Pica::FramebufferRegs::LogicOp::NoOp) { + // Color output is disabled by logic operation. We use color write mask to skip + // color but allow depth write. + state.color_mask = {}; + } + } + } } void RasterizerOpenGL::SyncColorWriteMask() { const auto& regs = Pica::g_state.regs; + if (GLES) { + if (!regs.framebuffer.output_merger.alphablend_enable) { + if (regs.framebuffer.output_merger.logic_op == Pica::FramebufferRegs::LogicOp::NoOp) { + // Color output is disabled by logic operation. We use color write mask to skip + // color but allow depth write. Return early to avoid overwriting this. + return; + } + } + } auto IsColorWriteEnabled = [&](u32 value) { return (regs.framebuffer.framebuffer.allow_color_write != 0 && value != 0) ? GL_TRUE diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index d8be747f2b..4e03922b86 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp @@ -127,6 +127,17 @@ PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs) { state.texture2_use_coord1 = regs.texturing.main_config.texture2_use_coord1 != 0; + if (GLES) { + // With GLES, we need this in the fragment shader to emulate logic operations + state.alphablend_enable = + Pica::g_state.regs.framebuffer.output_merger.alphablend_enable == 1; + state.logic_op = regs.framebuffer.output_merger.logic_op; + } else { + // We don't need these otherwise, reset them to avoid unnecessary shader generation + state.alphablend_enable = {}; + state.logic_op = {}; + } + // Copy relevant tev stages fields. // We don't sync const_color here because of the high variance, it is a // shader uniform instead. @@ -1568,6 +1579,32 @@ do { out += "color = byteround(last_tex_env_out);\n"; } + if (GLES) { + if (!state.alphablend_enable) { + switch (state.logic_op) { + case FramebufferRegs::LogicOp::Clear: + out += "color = vec4(0);\n"; + break; + case FramebufferRegs::LogicOp::Set: + out += "color = vec4(1);\n"; + break; + case FramebufferRegs::LogicOp::Copy: + // Take the color output as-is + break; + case FramebufferRegs::LogicOp::CopyInverted: + out += "color = ~color;\n"; + break; + case FramebufferRegs::LogicOp::NoOp: + // We need to discard the color, but not necessarily the depth. This is not possible + // with fragment shader alone, so we emulate this behavior on GLES with glColorMask. + break; + default: + LOG_CRITICAL(HW_GPU, "Unhandled logic_op {:x}", static_cast(state.logic_op)); + UNIMPLEMENTED(); + } + } + } + out += '}'; return {std::move(out)}; diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 3b11fa88ce..eb0e4cc235 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h @@ -61,6 +61,8 @@ struct PicaFSConfigState { Pica::RasterizerRegs::DepthBuffering depthmap_enable; Pica::TexturingRegs::FogMode fog_mode; bool fog_flip; + bool alphablend_enable; + Pica::FramebufferRegs::LogicOp logic_op; struct { struct { diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 364280a08d..89944f80e0 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -170,10 +170,17 @@ void OpenGLState::Apply() const { if (blend.enabled != cur_state.blend.enabled) { if (blend.enabled) { glEnable(GL_BLEND); - glDisable(GL_COLOR_LOGIC_OP); } else { glDisable(GL_BLEND); - glEnable(GL_COLOR_LOGIC_OP); + } + + // GLES does not support glLogicOp + if (!GLES) { + if (blend.enabled) { + glDisable(GL_COLOR_LOGIC_OP); + } else { + glEnable(GL_COLOR_LOGIC_OP); + } } } @@ -197,13 +204,11 @@ void OpenGLState::Apply() const { glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); } - // GLES3 does not support glLogicOp + // GLES does not support glLogicOp if (!GLES) { if (logic_op != cur_state.logic_op) { glLogicOp(logic_op); } - } else { - LOG_TRACE(Render_OpenGL, "glLogicOps are unimplemented..."); } // Textures