diff --git a/Source/Core/VideoBackends/OGL/Render.cpp b/Source/Core/VideoBackends/OGL/Render.cpp index f1a0a50897..8ca1a7d833 100644 --- a/Source/Core/VideoBackends/OGL/Render.cpp +++ b/Source/Core/VideoBackends/OGL/Render.cpp @@ -39,6 +39,7 @@ #include "VideoCommon/OnScreenDisplay.h" #include "VideoCommon/PixelEngine.h" #include "VideoCommon/PixelShaderManager.h" +#include "VideoCommon/RenderState.h" #include "VideoCommon/VertexShaderManager.h" #include "VideoCommon/VideoConfig.h" @@ -63,8 +64,6 @@ static int s_last_multisamples = 1; static bool s_last_stereo_mode = false; static bool s_last_xfb_mode = false; -static u32 s_blendMode; - static bool s_vsync; // EFB cache related @@ -328,8 +327,6 @@ static void InitDriverInfo() // Init functions Renderer::Renderer() { - s_blendMode = 0; - bool bSuccess = true; // Init extension support. @@ -832,20 +829,6 @@ void Renderer::SetScissorRect(const EFBRectangle& rc) glScissor(trc.left, trc.bottom, trc.GetWidth(), trc.GetHeight()); } -void Renderer::SetColorMask() -{ - // Only enable alpha channel if it's supported by the current EFB format - GLenum ColorMask = GL_FALSE, AlphaMask = GL_FALSE; - if (bpmem.alpha_test.TestResult() != AlphaTest::FAIL) - { - if (bpmem.blendmode.colorupdate) - ColorMask = GL_TRUE; - if (bpmem.blendmode.alphaupdate && (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24)) - AlphaMask = GL_TRUE; - } - glColorMask(ColorMask, ColorMask, ColorMask, AlphaMask); -} - void ClearEFBCache() { if (!s_efbCacheIsCleared) @@ -1224,111 +1207,72 @@ void Renderer::ReinterpretPixelData(unsigned int convtype) void Renderer::SetBlendMode(bool forceUpdate) { - // Our render target always uses an alpha channel, so we need to override the blend functions to - // assume a destination alpha of 1 if the render target isn't supposed to have an alpha channel - // Example: D3DBLEND_DESTALPHA needs to be D3DBLEND_ONE since the result without an alpha channel - // is assumed to always be 1. - bool target_has_alpha = bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24; + BlendingState state; + state.Generate(bpmem); - bool useDstAlpha = bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate && target_has_alpha; - bool useDualSource = g_ActiveConfig.backend_info.bSupportsDualSourceBlend; + bool useDualSource = + g_ActiveConfig.backend_info.bSupportsDualSourceBlend && + (!DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) || state.dstalpha); - // Only use dual-source blending when required on drivers that don't support it very well. - if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_DUAL_SOURCE_BLENDING) && !useDstAlpha) - useDualSource = false; - - const GLenum glSrcFactors[8] = { + const GLenum src_factors[8] = { GL_ZERO, GL_ONE, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR, - (useDualSource) ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, - (useDualSource) ? GL_ONE_MINUS_SRC1_ALPHA : (GLenum)GL_ONE_MINUS_SRC_ALPHA, - (target_has_alpha) ? GL_DST_ALPHA : (GLenum)GL_ONE, - (target_has_alpha) ? GL_ONE_MINUS_DST_ALPHA : (GLenum)GL_ZERO}; - const GLenum glDestFactors[8] = { + useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, + useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : (GLenum)GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA}; + const GLenum dst_factors[8] = { GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, - (useDualSource) ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, - (useDualSource) ? GL_ONE_MINUS_SRC1_ALPHA : (GLenum)GL_ONE_MINUS_SRC_ALPHA, - (target_has_alpha) ? GL_DST_ALPHA : (GLenum)GL_ONE, - (target_has_alpha) ? GL_ONE_MINUS_DST_ALPHA : (GLenum)GL_ZERO}; + useDualSource ? GL_SRC1_ALPHA : (GLenum)GL_SRC_ALPHA, + useDualSource ? GL_ONE_MINUS_SRC1_ALPHA : (GLenum)GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA}; - // blend mode bit mask - // 0 - blend enable - // 1 - dst alpha enabled - // 2 - reverse subtract enable (else add) - // 3-5 - srcRGB function - // 6-8 - dstRGB function - - u32 newval = useDstAlpha << 1; - newval |= bpmem.blendmode.subtract << 2; - - if (bpmem.blendmode.subtract) + if (state.blendenable) { - newval |= 0x0049; // enable blending src 1 dst 1 - } - else if (bpmem.blendmode.blendenable) - { - newval |= 1; // enable blending - newval |= bpmem.blendmode.srcfactor << 3; - newval |= bpmem.blendmode.dstfactor << 6; - } - - u32 changes = forceUpdate ? 0xFFFFFFFF : newval ^ s_blendMode; - - if (changes & 1) - { - // blend enable change - (newval & 1) ? glEnable(GL_BLEND) : glDisable(GL_BLEND); - } - - if (changes & 4) - { - // subtract enable change - GLenum equation = newval & 4 ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; - GLenum equationAlpha = useDstAlpha ? GL_FUNC_ADD : equation; + GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; + GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD; + glEnable(GL_BLEND); glBlendEquationSeparate(equation, equationAlpha); + glBlendFuncSeparate(src_factors[state.srcfactor], dst_factors[state.dstfactor], + src_factors[state.srcfactoralpha], dst_factors[state.dstfactoralpha]); } - - if (changes & 0x1FA) + else { - u32 srcidx = (newval >> 3) & 7; - u32 dstidx = (newval >> 6) & 7; - GLenum srcFactor = glSrcFactors[srcidx]; - GLenum dstFactor = glDestFactors[dstidx]; - - // adjust alpha factors - if (useDstAlpha) - { - srcidx = BlendMode::ONE; - dstidx = BlendMode::ZERO; - } - else - { - // we can't use GL_DST_COLOR or GL_ONE_MINUS_DST_COLOR for source in alpha channel so use - // their alpha equivalent instead - if (srcidx == BlendMode::DSTCLR) - srcidx = BlendMode::DSTALPHA; - else if (srcidx == BlendMode::INVDSTCLR) - srcidx = BlendMode::INVDSTALPHA; - - // we can't use GL_SRC_COLOR or GL_ONE_MINUS_SRC_COLOR for destination in alpha channel so use - // their alpha equivalent instead - if (dstidx == BlendMode::SRCCLR) - dstidx = BlendMode::SRCALPHA; - else if (dstidx == BlendMode::INVSRCCLR) - dstidx = BlendMode::INVSRCALPHA; - } - GLenum srcFactorAlpha = glSrcFactors[srcidx]; - GLenum dstFactorAlpha = glDestFactors[dstidx]; - // blend RGB change - glBlendFuncSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha); + glDisable(GL_BLEND); } - s_blendMode = newval; + + const GLenum logic_op_codes[16] = { + GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP, + GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE, + GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET}; + + if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL) + { + // Logic ops aren't available in GLES3 + } + else if (state.logicopenable) + { + glEnable(GL_COLOR_LOGIC_OP); + glLogicOp(logic_op_codes[state.logicmode]); + } + else + { + glDisable(GL_COLOR_LOGIC_OP); + } + + if (state.dither) + glEnable(GL_DITHER); + else + glDisable(GL_DITHER); + + glColorMask(state.colorupdate, state.colorupdate, state.colorupdate, state.alphaupdate); } // This function has the final picture. We adjust the aspect ratio here. @@ -1783,10 +1727,8 @@ void Renderer::RestoreAPIState() } SetGenerationMode(); BPFunctions::SetScissor(); - SetColorMask(); SetDepthMode(); SetBlendMode(true); - SetLogicOpMode(); SetViewport(); const VertexManager* const vm = static_cast(g_vertex_manager.get()); @@ -1833,35 +1775,6 @@ void Renderer::SetDepthMode() } } -void Renderer::SetLogicOpMode() -{ - if (GLInterface->GetMode() != GLInterfaceMode::MODE_OPENGL) - return; - // Logic ops aren't available in GLES3/GLES2 - const GLenum glLogicOpCodes[16] = { - GL_CLEAR, GL_AND, GL_AND_REVERSE, GL_COPY, GL_AND_INVERTED, GL_NOOP, - GL_XOR, GL_OR, GL_NOR, GL_EQUIV, GL_INVERT, GL_OR_REVERSE, - GL_COPY_INVERTED, GL_OR_INVERTED, GL_NAND, GL_SET}; - - if (bpmem.blendmode.logicopenable && !bpmem.blendmode.blendenable) - { - glEnable(GL_COLOR_LOGIC_OP); - glLogicOp(glLogicOpCodes[bpmem.blendmode.logicmode]); - } - else - { - glDisable(GL_COLOR_LOGIC_OP); - } -} - -void Renderer::SetDitherMode() -{ - if (bpmem.blendmode.dither) - glEnable(GL_DITHER); - else - glDisable(GL_DITHER); -} - void Renderer::SetSamplerState(int stage, int texindex, bool custom_tex) { auto const& tex = bpmem.tex[texindex]; diff --git a/Source/Core/VideoBackends/OGL/Render.h b/Source/Core/VideoBackends/OGL/Render.h index e9f4cbe2a4..7ed66b2282 100644 --- a/Source/Core/VideoBackends/OGL/Render.h +++ b/Source/Core/VideoBackends/OGL/Render.h @@ -72,13 +72,10 @@ public: static void Init(); static void Shutdown(); - void SetColorMask() override; void SetBlendMode(bool forceUpdate) override; void SetScissorRect(const EFBRectangle& rc) override; void SetGenerationMode() override; void SetDepthMode() override; - void SetLogicOpMode() override; - void SetDitherMode() override; void SetSamplerState(int stage, int texindex, bool custom_tex) override; void SetInterlacingMode() override; void SetViewport() override; diff --git a/Source/Core/VideoCommon/BPStructs.cpp b/Source/Core/VideoCommon/BPStructs.cpp index 5bd1b6027f..56bce65c27 100644 --- a/Source/Core/VideoCommon/BPStructs.cpp +++ b/Source/Core/VideoCommon/BPStructs.cpp @@ -142,6 +142,10 @@ static void BPWritten(const BPCmd& bp) (int)bpmem.blendmode.dstfactor, (int)bpmem.blendmode.srcfactor, (int)bpmem.blendmode.subtract, (int)bpmem.blendmode.logicmode); + // Set Blending Mode + if (bp.changes) + SetBlendMode(); + // Set LogicOp Blending Mode if (bp.changes & 0xF002) // logicopenable | logicmode SetLogicOpMode(); @@ -150,10 +154,6 @@ static void BPWritten(const BPCmd& bp) if (bp.changes & 4) // dither SetDitherMode(); - // Set Blending Mode - if (bp.changes & 0xFF1) // blendenable | alphaupdate | dstfactor | srcfactor | subtract - SetBlendMode(); - // Set Color Mask if (bp.changes & 0x18) // colorupdate | alphaupdate SetColorMask(); @@ -316,7 +316,10 @@ static void BPWritten(const BPCmd& bp) if (bp.changes & 0xFFFF) PixelShaderManager::SetAlpha(); if (bp.changes) + { g_renderer->SetColorMask(); + SetBlendMode(); + } return; case BPMEM_BIAS: // BIAS PRIM_LOG("ztex bias=0x%x", bpmem.ztex1.bias); diff --git a/Source/Core/VideoCommon/CMakeLists.txt b/Source/Core/VideoCommon/CMakeLists.txt index 38829432f9..cb9bc61720 100644 --- a/Source/Core/VideoCommon/CMakeLists.txt +++ b/Source/Core/VideoCommon/CMakeLists.txt @@ -25,6 +25,7 @@ set(SRCS AsyncRequests.cpp PixelShaderManager.cpp PostProcessing.cpp RenderBase.cpp + RenderState.cpp Statistics.cpp TextureCacheBase.cpp TextureConversionShader.cpp diff --git a/Source/Core/VideoCommon/RenderState.cpp b/Source/Core/VideoCommon/RenderState.cpp new file mode 100644 index 0000000000..2e84646ce4 --- /dev/null +++ b/Source/Core/VideoCommon/RenderState.cpp @@ -0,0 +1,128 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include "VideoCommon/RenderState.h" + +// If the framebuffer format has no alpha channel, it is assumed to +// ONE on blending. As the backends may emulate this framebuffer +// configuration with an alpha channel, we just drop all references +// to the destination alpha channel. +static BlendMode::BlendFactor RemoveDstAlphaUsage(BlendMode::BlendFactor factor) +{ + switch (factor) + { + case BlendMode::DSTALPHA: + return BlendMode::ONE; + case BlendMode::INVDSTALPHA: + return BlendMode::ZERO; + default: + return factor; + } +} + +// We separate the blending parameter for rgb and alpha. For blending +// the alpha component, CLR and ALPHA are indentical. So just always +// use ALPHA as this makes it easier for the backends to use the second +// alpha value of dual source blending. +static BlendMode::BlendFactor RemoveSrcColorUsage(BlendMode::BlendFactor factor) +{ + switch (factor) + { + case BlendMode::SRCCLR: + return BlendMode::SRCALPHA; + case BlendMode::INVSRCCLR: + return BlendMode::INVSRCALPHA; + default: + return factor; + } +} + +// Same as RemoveSrcColorUsage, but because of the overlapping enum, +// this must be written as another function. +static BlendMode::BlendFactor RemoveDstColorUsage(BlendMode::BlendFactor factor) +{ + switch (factor) + { + case BlendMode::DSTCLR: + return BlendMode::DSTALPHA; + case BlendMode::INVDSTCLR: + return BlendMode::INVDSTALPHA; + default: + return factor; + } +} + +void BlendingState::Generate(const BPMemory& bp) +{ + // Start with everything disabled. + hex = 0; + + bool target_has_alpha = bp.zcontrol.pixel_format == PEControl::RGBA6_Z24; + bool alpha_test_may_success = bp.alpha_test.TestResult() != AlphaTest::FAIL; + + dither = bp.blendmode.dither; + colorupdate = bp.blendmode.colorupdate && alpha_test_may_success; + alphaupdate = bp.blendmode.alphaupdate && target_has_alpha && alpha_test_may_success; + dstalpha = bp.dstalpha.enable && alphaupdate; + + // The subtract bit has the highest priority + if (bp.blendmode.subtract) + { + blendenable = true; + subtractAlpha = subtract = true; + srcfactoralpha = srcfactor = BlendMode::ONE; + dstfactoralpha = dstfactor = BlendMode::ONE; + + if (dstalpha) + { + subtractAlpha = false; + srcfactoralpha = BlendMode::ONE; + dstfactoralpha = BlendMode::ZERO; + } + } + + // The blendenable bit has the middle priority + else if (bp.blendmode.blendenable) + { + blendenable = true; + srcfactor = bp.blendmode.srcfactor; + dstfactor = bp.blendmode.dstfactor; + if (!target_has_alpha) + { + // uses ONE instead of DSTALPHA + srcfactor = RemoveDstAlphaUsage(srcfactor); + dstfactor = RemoveDstAlphaUsage(dstfactor); + } + // replaces SRCCLR with SRCALPHA + srcfactoralpha = RemoveSrcColorUsage(srcfactor); + dstfactoralpha = RemoveDstColorUsage(dstfactor); + + if (dstalpha) + { + srcfactoralpha = BlendMode::ONE; + dstfactoralpha = BlendMode::ZERO; + } + } + + // The logicop bit has the lowest priority + else if (bp.blendmode.logicopenable) + { + if (bp.blendmode.logicmode == BlendMode::NOOP) + { + // Fast path for Kirby's Return to Dreamland, they use it with dstAlpha. + colorupdate = false; + alphaupdate = alphaupdate && dstalpha; + } + else + { + logicopenable = true; + logicmode = bp.blendmode.logicmode; + + if (dstalpha) + { + // TODO: Not supported by backends. + } + } + } +} diff --git a/Source/Core/VideoCommon/RenderState.h b/Source/Core/VideoCommon/RenderState.h new file mode 100644 index 0000000000..cadd83e1a3 --- /dev/null +++ b/Source/Core/VideoCommon/RenderState.h @@ -0,0 +1,31 @@ +// Copyright 2016 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "Common/BitField.h" + +#include "VideoCommon/BPMemory.h" +#include "VideoCommon/BPStructs.h" + +union BlendingState +{ + void Generate(const BPMemory& bp); + + BitField<0, 1, u32> blendenable; + BitField<1, 1, u32> logicopenable; + BitField<2, 1, u32> dstalpha; + BitField<3, 1, u32> dither; + BitField<4, 1, u32> colorupdate; + BitField<5, 1, u32> alphaupdate; + BitField<6, 1, u32> subtract; + BitField<7, 1, u32> subtractAlpha; + BitField<8, 3, BlendMode::BlendFactor> dstfactor; + BitField<11, 3, BlendMode::BlendFactor> srcfactor; + BitField<14, 3, BlendMode::BlendFactor> dstfactoralpha; + BitField<17, 3, BlendMode::BlendFactor> srcfactoralpha; + BitField<20, 4, BlendMode::LogicOp> logicmode; + + u32 hex; +}; diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj b/Source/Core/VideoCommon/VideoCommon.vcxproj index 205f225101..e742711edf 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj @@ -80,6 +80,7 @@ + @@ -134,6 +135,7 @@ + @@ -183,4 +185,4 @@ - \ No newline at end of file + diff --git a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters index 32e0a870cf..5340250808 100644 --- a/Source/Core/VideoCommon/VideoCommon.vcxproj.filters +++ b/Source/Core/VideoCommon/VideoCommon.vcxproj.filters @@ -44,6 +44,9 @@ Base + + Base + Base @@ -182,6 +185,9 @@ Base + + Base + Base @@ -312,4 +318,4 @@ - \ No newline at end of file +