From c48841cfebff118af634135bc0f6a6a02bf4e7d4 Mon Sep 17 00:00:00 2001 From: S2S Date: Tue, 28 Apr 2020 17:24:26 +0200 Subject: [PATCH] Implement dual source blending. Fixes Donkey Kong 64: Gate textures change from a distance. #585 Fixes Pokemon Stadium wrong transparency in warning sign #728 --- src/Graphics/Context.cpp | 4 +- src/Graphics/Context.h | 4 +- .../GLSL/glsl_CombinerProgramBuilder.cpp | 162 ++++++++++++++---- .../GLSL/glsl_CombinerProgramBuilder.h | 1 + .../glsl_CombinerProgramUniformFactory.cpp | 63 ++++--- .../GLSL/glsl_SpecialShadersFactory.cpp | 3 +- .../OpenGLContext/opengl_ContextImpl.cpp | 4 + src/Graphics/OpenGLContext/opengl_GLInfo.cpp | 2 + src/Graphics/OpenGLContext/opengl_GLInfo.h | 1 + .../OpenGLContext/opengl_Parameters.cpp | 4 + src/Graphics/Parameters.h | 4 + src/GraphicsDrawer.cpp | 123 ++++++++----- src/GraphicsDrawer.h | 7 +- src/TexrectDrawer.cpp | 2 +- 14 files changed, 277 insertions(+), 107 deletions(-) diff --git a/src/Graphics/Context.cpp b/src/Graphics/Context.cpp index ddfc6289..3f6219d5 100644 --- a/src/Graphics/Context.cpp +++ b/src/Graphics/Context.cpp @@ -17,6 +17,7 @@ bool Context::FramebufferFetch = false; bool Context::TextureBarrier = false; bool Context::EglImage = false; bool Context::EglImageFramebuffer = false; +bool Context::DualSourceBlending = false; Context::Context() {} @@ -41,7 +42,8 @@ void Context::init() FramebufferFetch = m_impl->isSupported(SpecialFeatures::FramebufferFetch); TextureBarrier = m_impl->isSupported(SpecialFeatures::TextureBarrier); EglImage = m_impl->isSupported(SpecialFeatures::EglImage); - EglImageFramebuffer = m_impl->isSupported(SpecialFeatures::EglImageFramebuffer); + EglImageFramebuffer = m_impl->isSupported(SpecialFeatures::EglImageFramebuffer); + DualSourceBlending = m_impl->isSupported(SpecialFeatures::DualSourceBlending); } void Context::destroy() diff --git a/src/Graphics/Context.h b/src/Graphics/Context.h index 917a454b..3cab2eb9 100644 --- a/src/Graphics/Context.h +++ b/src/Graphics/Context.h @@ -27,7 +27,8 @@ namespace graphics { FramebufferFetch, TextureBarrier, EglImage, - EglImageFramebuffer + EglImageFramebuffer, + DualSourceBlending }; enum class ClampMode { @@ -302,6 +303,7 @@ namespace graphics { static bool TextureBarrier; static bool EglImage; static bool EglImageFramebuffer; + static bool DualSourceBlending; private: std::unique_ptr m_impl; diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp index 2cffd40e..51668f70 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp +++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.cpp @@ -514,6 +514,8 @@ public: ss << "#version " << Utils::to_string(_glinfo.majorVersion) << Utils::to_string(_glinfo.minorVersion) << "0 es " << std::endl; if (_glinfo.noPerspective) ss << "#extension GL_NV_shader_noperspective_interpolation : enable" << std::endl; + if (_glinfo.dual_source_blending) + ss << "#extension GL_EXT_blend_func_extended : enable" << std::endl; if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast) { if (_glinfo.imageTextures && _glinfo.fragment_interlockNV) { ss << "#extension GL_NV_fragment_shader_interlock : enable" << std::endl @@ -560,21 +562,40 @@ public: class ShaderBlender1 : public ShaderPart { public: - ShaderBlender1() + ShaderBlender1(const opengl::GLInfo & _glinfo) { #if 1 - m_part = - " #define MUXA(pos) dot(muxA, STVEC(pos)) \n" - " #define MUXB(pos) dot(muxB, STVEC(pos)) \n" - " #define MUXPM(pos) muxPM*(STVEC(pos)) \n" - " muxPM[0] = clampedColor; \n" - " if (uForceBlendCycle1 != 0) { \n" - " muxA[0] = clampedColor.a; \n" - " muxB[0] = 1.0 - MUXA(uBlendMux1[1]); \n" - " lowp vec4 blend1 = MUXPM(uBlendMux1[0]) * MUXA(uBlendMux1[1]) + MUXPM(uBlendMux1[2]) * MUXB(uBlendMux1[3]); \n" - " clampedColor.rgb = clamp(blend1.rgb, 0.0, 1.0); \n" - " } else clampedColor.rgb = (MUXPM(uBlendMux1[0])).rgb; \n" + m_part = + " srcColor1 = vec4(0.0); \n" + " dstFactor1 = 0.0; \n" + " muxPM[0] = clampedColor; \n" + " muxA[0] = clampedColor.a; \n" + " muxa = MUXA(uBlendMux1[1]); \n" + " muxB[0] = 1.0 - muxa; \n" + " muxb = MUXB(uBlendMux1[3]); \n" + " muxp = MUXPM(uBlendMux1[0]); \n" + " muxm = MUXPM(uBlendMux1[2]); \n" + " muxaf = MUXF(uBlendMux1[0]); \n" + " muxbf = MUXF(uBlendMux1[2]); \n" + " if (uForceBlendCycle1 != 0) { \n" + " srcColor1 = muxp * muxa + muxm * muxb; \n" + " dstFactor1 = muxaf * muxa + muxbf * muxb; \n" + " srcColor1 = clamp(srcColor1, 0.0, 1.0); \n" + " } else { \n" + " srcColor1 = muxp; \n" + " dstFactor1 = muxaf; \n" + " } \n" + ; + if (_glinfo.dual_source_blending) { + m_part += + " fragColor = srcColor1; \n" + " fragColor1 = vec4(dstFactor1); \n" ; + } else { + m_part += + " fragColor = vec4(srcColor1.rgb, clampedColor.a); \n" + ; + } #else // Keep old code for reference m_part = @@ -593,19 +614,41 @@ public: class ShaderBlender2 : public ShaderPart { public: - ShaderBlender2() + ShaderBlender2(const opengl::GLInfo & _glinfo) { #if 1 m_part = - " muxPM[0] = clampedColor; \n" - " muxPM[1] = vec4(0.0); \n" - " if (uForceBlendCycle2 != 0) { \n" - " muxA[0] = clampedColor.a; \n" - " muxB[0] = 1.0 - MUXA(uBlendMux2[1]); \n" - " lowp vec4 blend2 = MUXPM(uBlendMux2[0]) * MUXA(uBlendMux2[1]) + MUXPM(uBlendMux2[2]) * MUXB(uBlendMux2[3]); \n" - " clampedColor.rgb = clamp(blend2.rgb, 0.0, 1.0); \n" - " } else clampedColor.rgb = (MUXPM(uBlendMux2[0])).rgb; \n" + " srcColor2 = vec4(0.0); \n" + " dstFactor2 = 0.0; \n" + " muxPM[0] = srcColor1; \n" + " muxa = MUXA(uBlendMux2[1]); \n" + " muxB[0] = 1.0 - muxa; \n" + " muxb = MUXB(uBlendMux2[3]); \n" + " muxp = MUXPM(uBlendMux2[0]); \n" + " muxm = MUXPM(uBlendMux2[2]); \n" + " muxF[0] = dstFactor1; \n" + " muxaf = MUXF(uBlendMux2[0]); \n" + " muxbf = MUXF(uBlendMux2[2]); \n" + " if (uForceBlendCycle2 != 0) { \n" + " srcColor2 = muxp * muxa + muxm * muxb; \n" + " dstFactor2 = muxaf * muxa + muxbf * muxb; \n" + " srcColor2 = clamp(srcColor2, 0.0, 1.0); \n" + " } else { \n" + " srcColor2 = muxp; \n" + " dstFactor2 = muxaf; \n" + " } \n" ; + if (_glinfo.dual_source_blending) { + m_part += + " fragColor = srcColor2; \n" + " fragColor1 = vec4(dstFactor2); \n" + ; + } else { + m_part += + " fragColor = vec4(srcColor2.rgb, clampedColor.a); \n" + ; + } + #else // Keep old code for reference m_part = @@ -622,6 +665,24 @@ public: } }; +class ShaderBlenderAlpha : public ShaderPart +{ +public: + ShaderBlenderAlpha(const opengl::GLInfo & _glinfo) + { + if (_glinfo.dual_source_blending) + m_part += + "lowp float cvg = clampedColor.a; \n" + "lowp vec4 srcAlpha = vec4(cvg, cvg, 1.0, 0.0); \n" + "lowp vec4 dstFactorAlpha = vec4(1.0, 1.0, 0.0, 1.0); \n" + "if (uForceBlendAlpha == 0) \n" + " dstFactorAlpha[0] = 0.0; \n" + "fragColor.a = srcAlpha[uCvgDest]; \n" + "fragColor1.a = dstFactorAlpha[uCvgDest]; \n" + ; + } +}; + class ShaderLegacyBlender : public ShaderPart { public: @@ -871,7 +932,9 @@ public: "highp vec2 texCoord1; \n" "highp vec2 tcData0[5]; \n" "highp vec2 tcData1[5]; \n" - ; + "uniform lowp int uCvgDest; \n" + "uniform lowp int uForceBlendAlpha; \n" + ; if (config.generalEmulation.enableLegacyBlending != 0) { m_part += @@ -924,15 +987,21 @@ public: "IN lowp float vNumLights; \n" ; - if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.ext_fetch) { + if (_glinfo.dual_source_blending) { m_part += - "layout(location = 0) OUT lowp vec4 fragColor; \n" - "layout(location = 1) inout highp vec4 depthZ; \n" - "layout(location = 2) inout highp vec4 depthDeltaZ; \n" + "layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n" + "layout(location = 0, index = 1) OUT lowp vec4 fragColor1; \n" ; } else { m_part += "OUT lowp vec4 fragColor; \n" + ; + } + + if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.ext_fetch) { + m_part += + "layout(location = 1) inout highp vec4 depthZ; \n" + "layout(location = 2) inout highp vec4 depthDeltaZ; \n" ; } } @@ -964,6 +1033,9 @@ public: "uniform highp float uPrimDepth; \n" "uniform mediump vec2 uScreenScale; \n" "uniform lowp int uScreenSpaceTriangle; \n" + "uniform lowp int uCvgDest; \n" + "uniform lowp int uForceBlendAlpha; \n" + ; if (config.generalEmulation.enableLegacyBlending != 0) { @@ -1006,17 +1078,23 @@ public: "IN lowp float vNumLights; \n" ; - if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.ext_fetch) { + if (_glinfo.dual_source_blending) { m_part += - "layout(location = 0) OUT lowp vec4 fragColor; \n" - "layout(location = 1) inout highp vec4 depthZ; \n" - "layout(location = 2) inout highp vec4 depthDeltaZ; \n" + "layout(location = 0, index = 0) OUT lowp vec4 fragColor; \n" + "layout(location = 0, index = 1) OUT lowp vec4 fragColor1; \n" ; } else { m_part += "OUT lowp vec4 fragColor; \n" - ; + ; } + + if (config.frameBufferEmulation.N64DepthCompare == Config::dcFast && _glinfo.ext_fetch) { + m_part += + "layout(location = 1) inout highp vec4 depthZ; \n" + "layout(location = 2) inout highp vec4 depthDeltaZ; \n" + ; + } } }; @@ -1418,9 +1496,16 @@ public: { if (config.generalEmulation.enableLegacyBlending == 0) { m_part = + " #define MUXA(pos) dot(muxA, STVEC(pos)) \n" + " #define MUXB(pos) dot(muxB, STVEC(pos)) \n" + " #define MUXPM(pos) muxPM*(STVEC(pos)) \n" + " #define MUXF(pos) dot(muxF, STVEC(pos)) \n" " lowp mat4 muxPM = mat4(vec4(0.0), vec4(0.0), uBlendColor, uFogColor); \n" " lowp vec4 muxA = vec4(0.0, uFogColor.a, shadeColor.a, 0.0); \n" " lowp vec4 muxB = vec4(0.0, 1.0, 1.0, 0.0); \n" + " lowp vec4 muxF = vec4(0.0, 1.0, 0.0, 0.0); \n" + " lowp vec4 muxp, muxm, srcColor1, srcColor2; \n" + " lowp float muxa, muxb, dstFactor1, dstFactor2, muxaf, muxbf; \n" ; } } @@ -2572,12 +2657,14 @@ CombinerInputs CombinerProgramBuilder::compileCombiner(const CombinerKey & _key, m_callDither->write(ssShader); if (config.generalEmulation.enableLegacyBlending == 0) { - if (g_cycleType <= G_CYC_2CYCLE) + if (g_cycleType <= G_CYC_2CYCLE) { m_blender1->write(ssShader); - if (g_cycleType == G_CYC_2CYCLE) - m_blender2->write(ssShader); + if (g_cycleType == G_CYC_2CYCLE) + m_blender2->write(ssShader); + m_blenderAlpha->write(ssShader); + } else + ssShader << " fragColor = clampedColor;" << std::endl; - ssShader << " fragColor = clampedColor;" << std::endl; } else { ssShader << " fragColor = clampedColor;" << std::endl; @@ -2792,8 +2879,9 @@ GLuint _createVertexShader(ShaderPart * _header, ShaderPart * _body, ShaderPart } CombinerProgramBuilder::CombinerProgramBuilder(const opengl::GLInfo & _glinfo, opengl::CachedUseProgram * _useProgram) -: m_blender1(new ShaderBlender1) -, m_blender2(new ShaderBlender2) +: m_blender1(new ShaderBlender1(_glinfo)) +, m_blender2(new ShaderBlender2(_glinfo)) +, m_blenderAlpha (new ShaderBlenderAlpha(_glinfo)) , m_legacyBlender(new ShaderLegacyBlender) , m_clamp(new ShaderClamp) , m_signExtendColorC(new ShaderSignExtendColorC) diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h index f69fe927..b7f8bf37 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h +++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramBuilder.h @@ -39,6 +39,7 @@ namespace glsl { typedef std::unique_ptr ShaderPartPtr; ShaderPartPtr m_blender1; ShaderPartPtr m_blender2; + ShaderPartPtr m_blenderAlpha; ShaderPartPtr m_legacyBlender; ShaderPartPtr m_clamp; ShaderPartPtr m_signExtendColorC; diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp index 7ab58813..a1c3f61c 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp +++ b/src/Graphics/OpenGLContext/GLSL/glsl_CombinerProgramUniformFactory.cpp @@ -369,30 +369,33 @@ public: const int forceBlend2 = gDP.otherMode.forceBlender; uForceBlendCycle2.set(forceBlend2, _force); - // Modes, which shader blender can't emulate - const u32 mode = _SHIFTR(gDP.otherMode.l, 16, 16); - switch (mode) { - case 0x0040: - // Mia Hamm Soccer - // clr_in * a_in + clr_mem * (1-a) - // clr_in * a_in + clr_in * (1-a) - case 0x0050: - // A Bug's Life - // clr_in * a_in + clr_mem * (1-a) - // clr_in * a_in + clr_mem * (1-a) - uForceBlendCycle1.set(0, _force); - uForceBlendCycle2.set(0, _force); - break; - case 0x0150: - // Tony Hawk - // clr_in * a_in + clr_mem * (1-a) - // clr_in * a_fog + clr_mem * (1-a_fog) - if ((config.generalEmulation.hacks & hack_TonyHawk) != 0) { + if (!graphics::Context::DualSourceBlending) { + // Modes, which shader blender can't emulate + const u32 mode = _SHIFTR(gDP.otherMode.l, 16, 16); + switch (mode) { + case 0x0040: + // Mia Hamm Soccer + // clr_in * a_in + clr_mem * (1-a) + // clr_in * a_in + clr_in * (1-a) + case 0x0050: + // A Bug's Life + // clr_in * a_in + clr_mem * (1-a) + // clr_in * a_in + clr_mem * (1-a) uForceBlendCycle1.set(0, _force); uForceBlendCycle2.set(0, _force); + break; + case 0x0150: + // Tony Hawk + // clr_in * a_in + clr_mem * (1-a) + // clr_in * a_fog + clr_mem * (1-a_fog) + if ((config.generalEmulation.hacks & hack_TonyHawk) != 0) { + uForceBlendCycle1.set(0, _force); + uForceBlendCycle2.set(0, _force); + } + break; } - break; } + } private: @@ -402,6 +405,24 @@ private: iUniform uForceBlendCycle2; }; +class UBlendCvg : public UniformGroup +{ +public: + UBlendCvg(GLuint _program) { + LocateUniform(uCvgDest); + LocateUniform(uForceBlendAlpha); + } + + void update(bool _force) override + { + uCvgDest.set(gDP.otherMode.cvgDest, _force); + uForceBlendAlpha.set(gDP.otherMode.forceBlender, _force); + } +private: + iUniform uCvgDest; + iUniform uForceBlendAlpha; +}; + class UDitherMode : public UniformGroup { public: @@ -1126,6 +1147,8 @@ void CombinerProgramUniformFactory::buildUniforms(GLuint _program, } } + _uniforms.emplace_back(new UBlendCvg(_program)); + _uniforms.emplace_back(new UDitherMode(_program, _inputs.usesNoise())); _uniforms.emplace_back(new UScreenScale(_program)); diff --git a/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp b/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp index c400f8e2..a5ccc96b 100644 --- a/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp +++ b/src/Graphics/OpenGLContext/GLSL/glsl_SpecialShadersFactory.cpp @@ -313,8 +313,7 @@ namespace glsl { "{ \n" " TEX_FILTER(fragColor, uTex0, vTexCoord0); \n" ; - if (!_glinfo.isGLES2 && - config.generalEmulation.enableFragmentDepthWrite != 0 && + if (config.generalEmulation.enableFragmentDepthWrite != 0 && config.frameBufferEmulation.N64DepthCompare == Config::dcDisable) { m_part += " gl_FragDepth = uPrimDepth; \n" diff --git a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp index 6d141561..b481c9d5 100644 --- a/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp +++ b/src/Graphics/OpenGLContext/opengl_ContextImpl.cpp @@ -156,11 +156,13 @@ void ContextImpl::setScissor(s32 _x, s32 _y, s32 _width, s32 _height) void ContextImpl::setBlending(graphics::BlendParam _sfactor, graphics::BlendParam _dfactor) { m_cachedFunctions->getCachedBlending()->setBlending(_sfactor, _dfactor); + m_cachedFunctions->getCachedBlendingSeparate()->reset(); } void ContextImpl::setBlendingSeparate(graphics::BlendParam _sfactorcolor, graphics::BlendParam _dfactorcolor, graphics::BlendParam _sfactoralpha, graphics::BlendParam _dfactoralpha) { m_cachedFunctions->getCachedBlendingSeparate()->setBlendingSeparate(_sfactorcolor, _dfactorcolor, _sfactoralpha, _dfactoralpha); + m_cachedFunctions->getCachedBlending()->reset(); } void ContextImpl::setBlendColor(f32 _red, f32 _green, f32 _blue, f32 _alpha) @@ -518,6 +520,8 @@ bool ContextImpl::isSupported(graphics::SpecialFeatures _feature) const return m_glInfo.eglImage; case graphics::SpecialFeatures::EglImageFramebuffer: return m_glInfo.eglImageFramebuffer; + case graphics::SpecialFeatures::DualSourceBlending: + return m_glInfo.dual_source_blending; } return false; } diff --git a/src/Graphics/OpenGLContext/opengl_GLInfo.cpp b/src/Graphics/OpenGLContext/opengl_GLInfo.cpp index afc5f133..e7f8187c 100644 --- a/src/Graphics/OpenGLContext/opengl_GLInfo.cpp +++ b/src/Graphics/OpenGLContext/opengl_GLInfo.cpp @@ -160,6 +160,8 @@ void GLInfo::init() { ext_fetch = Utils::isExtensionSupported(*this, "GL_EXT_shader_framebuffer_fetch") && !isGLES2 && (!isGLESX || ext_draw_buffers_indexed) && !imageTexturesInterlock; eglImage = (Utils::isEGLExtensionSupported("EGL_KHR_image_base") || Utils::isEGLExtensionSupported("EGL_KHR_image")); + dual_source_blending = !(isGLESX) || Utils::isExtensionSupported(*this, "GL_EXT_blend_func_extended"); + #ifdef OS_ANDROID eglImage = eglImage && ( (isGLES2 && GraphicBufferWrapper::isSupportAvailable()) || (isGLESX && GraphicBufferWrapper::isPublicSupportAvailable()) ) && diff --git a/src/Graphics/OpenGLContext/opengl_GLInfo.h b/src/Graphics/OpenGLContext/opengl_GLInfo.h index 764c48be..69959241 100644 --- a/src/Graphics/OpenGLContext/opengl_GLInfo.h +++ b/src/Graphics/OpenGLContext/opengl_GLInfo.h @@ -35,6 +35,7 @@ struct GLInfo { bool ext_fetch = false; bool eglImage = false; bool eglImageFramebuffer = false; + bool dual_source_blending = false; Renderer renderer = Renderer::Other; void init(); diff --git a/src/Graphics/OpenGLContext/opengl_Parameters.cpp b/src/Graphics/OpenGLContext/opengl_Parameters.cpp index 2233894e..ea8e4496 100644 --- a/src/Graphics/OpenGLContext/opengl_Parameters.cpp +++ b/src/Graphics/OpenGLContext/opengl_Parameters.cpp @@ -117,6 +117,10 @@ namespace graphics { BlendParam ONE_MINUS_SRC_ALPHA(GL_ONE_MINUS_SRC_ALPHA); BlendParam CONSTANT_ALPHA(GL_CONSTANT_ALPHA); BlendParam ONE_MINUS_CONSTANT_ALPHA(GL_ONE_MINUS_CONSTANT_ALPHA); + BlendParam SRC1_COLOR(GL_SRC1_COLOR); + BlendParam ONE_MINUS_SRC1_COLOR(GL_ONE_MINUS_SRC1_COLOR); + BlendParam SRC1_ALPHA(GL_SRC1_ALPHA); + BlendParam ONE_MINUS_SRC1_ALPHA(GL_ONE_MINUS_SRC1_ALPHA); } namespace drawmode { diff --git a/src/Graphics/Parameters.h b/src/Graphics/Parameters.h index e8643e17..86f92a43 100644 --- a/src/Graphics/Parameters.h +++ b/src/Graphics/Parameters.h @@ -117,6 +117,10 @@ namespace graphics { extern BlendParam ONE_MINUS_SRC_ALPHA; extern BlendParam CONSTANT_ALPHA; extern BlendParam ONE_MINUS_CONSTANT_ALPHA; + extern BlendParam SRC1_COLOR; + extern BlendParam ONE_MINUS_SRC1_COLOR; + extern BlendParam SRC1_ALPHA; + extern BlendParam ONE_MINUS_SRC1_ALPHA; } namespace drawmode { diff --git a/src/GraphicsDrawer.cpp b/src/GraphicsDrawer.cpp index 2f044a61..2628c56a 100644 --- a/src/GraphicsDrawer.cpp +++ b/src/GraphicsDrawer.cpp @@ -314,8 +314,7 @@ void GraphicsDrawer::_updateScreenCoordsViewport(const FrameBuffer * _pBuffer) c gSP.changed |= CHANGED_VIEWPORT; } -static -void _legacySetBlendMode() +void GraphicsDrawer::_legacyBlending() const { const u32 blendmode = gDP.otherMode.l >> 16; // 0x7000 = CVG_X_ALPHA|ALPHA_CVG_SEL|FORCE_BL @@ -478,48 +477,36 @@ void _legacySetBlendMode() } } -bool GraphicsDrawer::_setUnsupportedBlendMode() const +void GraphicsDrawer::_ordinaryBlending() const { - if (gDP.otherMode.cycleType != G_CYC_2CYCLE) - return false; - // Modes, which shader blender can't emulate - const u32 mode = _SHIFTR(gDP.otherMode.l, 16, 16); - switch (mode) { - case 0x0040: - // Mia Hamm Soccer - // clr_in * a_in + clr_mem * (1-a) - // clr_in * a_in + clr_in * (1-a) - case 0x0050: - // A Bug's Life - // clr_in * a_in + clr_mem * (1-a) - // clr_in * a_in + clr_mem * (1-a) - gfxContext.enable(enable::BLEND, true); - gfxContext.setBlending(blend::SRC_ALPHA, blend::ONE_MINUS_SRC_ALPHA); - return true; - case 0x0150: - // Tony Hawk - // clr_in * a_in + clr_mem * (1-a) - // clr_in * a_fog + clr_mem * (1-a_fog) - if ((config.generalEmulation.hacks & hack_TonyHawk) != 0) { + // Set unsupported blend modes + if (gDP.otherMode.cycleType == G_CYC_2CYCLE) { + const u32 mode = _SHIFTR(gDP.otherMode.l, 16, 16); + switch (mode) { + case 0x0040: + // Mia Hamm Soccer + // clr_in * a_in + clr_mem * (1-a) + // clr_in * a_in + clr_in * (1-a) + case 0x0050: + // A Bug's Life + // clr_in * a_in + clr_mem * (1-a) + // clr_in * a_in + clr_mem * (1-a) gfxContext.enable(enable::BLEND, true); gfxContext.setBlending(blend::SRC_ALPHA, blend::ONE_MINUS_SRC_ALPHA); - return true; + return; + case 0x0150: + // Tony Hawk + // clr_in * a_in + clr_mem * (1-a) + // clr_in * a_fog + clr_mem * (1-a_fog) + if ((config.generalEmulation.hacks & hack_TonyHawk) != 0) { + gfxContext.enable(enable::BLEND, true); + gfxContext.setBlending(blend::SRC_ALPHA, blend::ONE_MINUS_SRC_ALPHA); + return; + } + break; } - break; } - return false; -} - -void GraphicsDrawer::_setBlendMode() const -{ - if (config.generalEmulation.enableLegacyBlending != 0) { - _legacySetBlendMode(); - return; - } - - if (_setUnsupportedBlendMode()) - return; if (gDP.otherMode.forceBlender != 0 && gDP.otherMode.cycleType < G_CYC_COPY) { BlendParam srcFactor = blend::ONE; @@ -535,7 +522,8 @@ void GraphicsDrawer::_setBlendMode() const return; } memFactorSource = 0; - } else if (gDP.otherMode.c2_m2a == 1) { + } + else if (gDP.otherMode.c2_m2a == 1) { memFactorSource = 1; } if (gDP.otherMode.c2_m2a == 0 && gDP.otherMode.c2_m2b == 1) { @@ -552,8 +540,7 @@ void GraphicsDrawer::_setBlendMode() const return; } memFactorSource = 0; - } - else if (gDP.otherMode.c1_m2a == 1) { + } else if (gDP.otherMode.c1_m2a == 1) { memFactorSource = 1; } if (gDP.otherMode.c1_m2a == 0 && gDP.otherMode.c1_m2b == 1) { @@ -641,6 +628,56 @@ void GraphicsDrawer::_setBlendMode() const } } +void GraphicsDrawer::_dualSourceBlending() const +{ + if (gDP.otherMode.cycleType < G_CYC_COPY) { + BlendParam srcFactor = blend::ONE; + BlendParam dstFactor = blend::SRC1_COLOR; + BlendParam srcFactorAlpha = blend::ONE; + BlendParam dstFactorAlpha = blend::SRC1_ALPHA; + if (gDP.otherMode.forceBlender != 0) { + if (gDP.otherMode.cycleType == G_CYC_2CYCLE) { + if (gDP.otherMode.c2_m2a != 1 && gDP.otherMode.c2_m2b == 1) { + srcFactor = blend::DST_ALPHA; + } + if (gDP.otherMode.c2_m2a == 1 && gDP.otherMode.c2_m2b == 1) { + dstFactor = blend::DST_ALPHA; + } + } else { + if (gDP.otherMode.c1_m2a != 1 && gDP.otherMode.c1_m2b == 1) { + srcFactor = blend::DST_ALPHA; + } + if (gDP.otherMode.c1_m2a == 1 && gDP.otherMode.c2_m2b == 1) { + dstFactor = blend::DST_ALPHA; + } + } + } else if ((config.generalEmulation.hacks & hack_blastCorps) != 0 && + gSP.texture.on == 0 && currentCombiner()->usesTexture()) { // Blast Corps + srcFactor = blend::ZERO; + dstFactor = blend::ONE; + } + gfxContext.enable(enable::BLEND, true); + gfxContext.setBlendingSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha); + } else { + gfxContext.enable(enable::BLEND, false); + } +} + +void GraphicsDrawer::setBlendMode(bool _forceLegacyBlending) const +{ + if (_forceLegacyBlending || config.generalEmulation.enableLegacyBlending != 0) { + _legacyBlending(); + return; + } + + if (Context::DualSourceBlending) { + _dualSourceBlending(); + return; + } + + _ordinaryBlending(); +} + void GraphicsDrawer::_updateTextures() const { //For some reason updating the texture cache on the first frame of LOZ:OOT causes a nullptr Pointer exception... @@ -685,7 +722,7 @@ void GraphicsDrawer::_updateStates(DrawingState _drawingState) const } if ((gDP.changed & (CHANGED_RENDERMODE | CHANGED_CYCLETYPE))) { - _setBlendMode(); + setBlendMode(); gDP.changed &= ~(CHANGED_RENDERMODE | CHANGED_CYCLETYPE); } @@ -1053,7 +1090,7 @@ bool texturedRectShadowMap(const GraphicsDrawer::TexturedRectParams &) pCurrentBuffer->m_pDepthBuffer->activateDepthBufferTexture(pCurrentBuffer); CombinerInfo::get().setDepthFogCombiner(); // DepthFogCombiner does not support shader blending. - _legacySetBlendMode(); + dwnd().getDrawer().setBlendMode(true); return false; } } diff --git a/src/GraphicsDrawer.h b/src/GraphicsDrawer.h index 67e339c7..75e8f30b 100644 --- a/src/GraphicsDrawer.h +++ b/src/GraphicsDrawer.h @@ -158,6 +158,8 @@ public: void setBackgroundDrawingMode(bool _mode) { m_bBGMode = _mode; } + void setBlendMode(bool _forceLegacyBlending = false) const; + private: friend class DisplayWindow; friend TexrectDrawer; @@ -173,8 +175,9 @@ private: void _setSpecialTexrect() const; - void _setBlendMode() const; - bool _setUnsupportedBlendMode() const; + void _legacyBlending() const; + void _ordinaryBlending() const; + void _dualSourceBlending() const; void _updateCullFace() const; void _updateViewport() const; void _updateScreenCoordsViewport(const FrameBuffer * _pBuffer = nullptr) const; diff --git a/src/TexrectDrawer.cpp b/src/TexrectDrawer.cpp index 3d75b499..e8335235 100644 --- a/src/TexrectDrawer.cpp +++ b/src/TexrectDrawer.cpp @@ -350,7 +350,7 @@ bool TexrectDrawer::draw() ValueKeeper scissor(gDP.scissor, m_scissor); DisplayWindow & wnd = dwnd(); GraphicsDrawer & drawer = wnd.getDrawer(); - drawer._setBlendMode(); + drawer.setBlendMode(); gDP.changed |= CHANGED_RENDERMODE; // Force update of depth compare parameters drawer._updateDepthCompare();