From e6ace388156735d3f5e2942cafc3d5f5d46b121b Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 25 May 2015 18:39:03 -0400 Subject: [PATCH] Pica: Implement LogicOp function. --- src/video_core/pica.h | 27 +++++++-- src/video_core/rasterizer.cpp | 59 ++++++++++++++++++- .../renderer_opengl/gl_rasterizer.cpp | 10 ++++ .../renderer_opengl/gl_rasterizer.h | 3 + src/video_core/renderer_opengl/gl_state.cpp | 11 ++++ src/video_core/renderer_opengl/gl_state.h | 2 + src/video_core/renderer_opengl/pica_to_gl.h | 31 ++++++++++ 7 files changed, 135 insertions(+), 8 deletions(-) diff --git a/src/video_core/pica.h b/src/video_core/pica.h index fbc95a4b6..f8313d20d 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h @@ -162,6 +162,25 @@ struct Regs { ETC1A4 = 13, // compressed }; + enum class LogicOp : u32 { + Clear = 0, + And = 1, + AndReverse = 2, + Copy = 3, + Set = 4, + CopyInverted = 5, + NoOp = 6, + Invert = 7, + Nand = 8, + Or = 9, + Nor = 10, + Xor = 11, + Equiv = 12, + AndInverted = 13, + OrReverse = 14, + OrInverted = 15, + }; + static unsigned NibblesPerPixel(TextureFormat format) { switch (format) { case TextureFormat::RGBA8: @@ -413,12 +432,8 @@ struct Regs { } alpha_blending; union { - enum Op { - Set = 4, - }; - - BitField<0, 4, Op> op; - } logic_op; + BitField<0, 4, LogicOp> logic_op; + }; union { BitField< 0, 8, u32> r; diff --git a/src/video_core/rasterizer.cpp b/src/video_core/rasterizer.cpp index 8f39e609b..2613e398f 100644 --- a/src/video_core/rasterizer.cpp +++ b/src/video_core/rasterizer.cpp @@ -873,8 +873,63 @@ static void ProcessTriangleInternal(const VertexShader::OutputVertex& v0, blend_output = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_rgb); blend_output.a() = EvaluateBlendEquation(combiner_output, srcfactor, dest, dstfactor, params.blend_equation_a).a(); } else { - LOG_CRITICAL(HW_GPU, "logic op: %x", output_merger.logic_op); - UNIMPLEMENTED(); + static auto LogicOp = [](u8 src, u8 dest, Regs::LogicOp op) -> u8 { + switch (op) { + case Regs::LogicOp::Clear: + return 0; + + case Regs::LogicOp::And: + return src & dest; + + case Regs::LogicOp::AndReverse: + return src & ~dest; + + case Regs::LogicOp::Copy: + return src; + + case Regs::LogicOp::Set: + return 255; + + case Regs::LogicOp::CopyInverted: + return ~src; + + case Regs::LogicOp::NoOp: + return dest; + + case Regs::LogicOp::Invert: + return ~dest; + + case Regs::LogicOp::Nand: + return ~(src & dest); + + case Regs::LogicOp::Or: + return src | dest; + + case Regs::LogicOp::Nor: + return ~(src | dest); + + case Regs::LogicOp::Xor: + return src ^ dest; + + case Regs::LogicOp::Equiv: + return ~(src ^ dest); + + case Regs::LogicOp::AndInverted: + return ~src & dest; + + case Regs::LogicOp::OrReverse: + return src | ~dest; + + case Regs::LogicOp::OrInverted: + return ~src | dest; + } + }; + + blend_output = Math::MakeVec( + LogicOp(combiner_output.r(), dest.r(), output_merger.logic_op), + LogicOp(combiner_output.g(), dest.g(), output_merger.logic_op), + LogicOp(combiner_output.b(), dest.b(), output_merger.logic_op), + LogicOp(combiner_output.a(), dest.a(), output_merger.logic_op)); } const Math::Vec4 result = { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index bacdb7172..b51f8efdf 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -135,6 +135,7 @@ void RasterizerOpenGL::Reset() { SyncBlendFuncs(); SyncBlendColor(); SyncAlphaTest(); + SyncLogicOp(); SyncStencilTest(); SyncDepthTest(); @@ -249,6 +250,11 @@ void RasterizerOpenGL::NotifyPicaRegisterChanged(u32 id) { SyncDepthTest(); break; + // Logic op + case PICA_REG_INDEX(output_merger.logic_op): + SyncLogicOp(); + break; + // TEV stage 0 case PICA_REG_INDEX(tev_stage0.color_source1): SyncTevSources(0, regs.tev_stage0); @@ -633,6 +639,10 @@ void RasterizerOpenGL::SyncAlphaTest() { glUniform1f(uniform_alphatest_ref, regs.output_merger.alpha_test.ref / 255.0f); } +void RasterizerOpenGL::SyncLogicOp() { + state.logic_op = PicaToGL::LogicOp(Pica::g_state.regs.output_merger.logic_op); +} + void RasterizerOpenGL::SyncStencilTest() { // TODO: Implement stencil test, mask, and op } diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 9896f8d04..d7d422b1f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -125,6 +125,9 @@ private: /// Syncs the alpha test states to match the PICA register void SyncAlphaTest(); + /// Syncs the logic op states to match the PICA register + void SyncLogicOp(); + /// Syncs the stencil test states to match the PICA register void SyncStencilTest(); diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 0d7ba1983..9c5f38f94 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -32,6 +32,8 @@ OpenGLState::OpenGLState() { blend.color.blue = 0.0f; blend.color.alpha = 0.0f; + logic_op = GL_COPY; + for (auto& texture_unit : texture_units) { texture_unit.enabled_2d = false; texture_unit.texture_2d = 0; @@ -99,8 +101,13 @@ void OpenGLState::Apply() { if (blend.enabled != cur_state.blend.enabled) { if (blend.enabled) { glEnable(GL_BLEND); + + cur_state.logic_op = GL_COPY; + glLogicOp(cur_state.logic_op); + glDisable(GL_COLOR_LOGIC_OP); } else { glDisable(GL_BLEND); + glEnable(GL_COLOR_LOGIC_OP); } } @@ -118,6 +125,10 @@ void OpenGLState::Apply() { glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, blend.dst_a_func); } + if (logic_op != cur_state.logic_op) { + glLogicOp(logic_op); + } + // Textures for (unsigned texture_index = 0; texture_index < ARRAY_SIZE(texture_units); ++texture_index) { if (texture_units[texture_index].enabled_2d != cur_state.texture_units[texture_index].enabled_2d) { diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 63dba2761..6b97721d6 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -42,6 +42,8 @@ public: } color; // GL_BLEND_COLOR } blend; + GLenum logic_op; // GL_LOGIC_OP_MODE + // 3 texture units - one for each that is used in PICA fragment shader emulation struct { bool enabled_2d; // GL_TEXTURE_2D diff --git a/src/video_core/renderer_opengl/pica_to_gl.h b/src/video_core/renderer_opengl/pica_to_gl.h index f8763e71b..e566f9f7a 100644 --- a/src/video_core/renderer_opengl/pica_to_gl.h +++ b/src/video_core/renderer_opengl/pica_to_gl.h @@ -71,6 +71,37 @@ inline GLenum BlendFunc(Pica::Regs::BlendFactor factor) { return blend_func_table[(unsigned)factor]; } +inline GLenum LogicOp(Pica::Regs::LogicOp op) { + static const GLenum logic_op_table[] = { + GL_CLEAR, // Clear + GL_AND, // And + GL_AND_REVERSE, // AndReverse + GL_COPY, // Copy + GL_SET, // Set + GL_COPY_INVERTED, // CopyInverted + GL_NOOP, // NoOp + GL_INVERT, // Invert + GL_NAND, // Nand + GL_OR, // Or + GL_NOR, // Nor + GL_XOR, // Xor + GL_EQUIV, // Equiv + GL_AND_INVERTED, // AndInverted + GL_OR_REVERSE, // OrReverse + GL_OR_INVERTED, // OrInverted + }; + + // Range check table for input + if ((unsigned)op >= ARRAY_SIZE(logic_op_table)) { + LOG_CRITICAL(Render_OpenGL, "Unknown logic op %d", op); + UNREACHABLE(); + + return GL_COPY; + } + + return logic_op_table[(unsigned)op]; +} + inline GLenum CompareFunc(Pica::Regs::CompareFunc func) { static const GLenum compare_func_table[] = { GL_NEVER, // CompareFunc::Never