From c88b66b308f9202b455dbbe06bbad20b4dd008da Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Tue, 23 Sep 2014 21:13:47 -0700 Subject: [PATCH] d3d9: Emulate some logic ops with blending. This makes Brave Story's intro visible. Also add for GLES2/GLES3, but doesn't seem to work on GLES2. --- Core/HLE/sceAtrac.cpp | 2 +- GPU/Directx9/PixelShaderGeneratorDX9.cpp | 41 +++++++++- GPU/Directx9/StateMappingDX9.cpp | 100 ++++++++++++++++++----- GPU/Directx9/TransformPipelineDX9.h | 3 +- GPU/GLES/FragmentShaderGenerator.cpp | 44 +++++++++- GPU/GLES/StateMapping.cpp | 100 ++++++++++++++++++----- GPU/GLES/TransformPipeline.h | 3 +- 7 files changed, 248 insertions(+), 45 deletions(-) diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index fe0702220c..dbb76ce768 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -44,7 +44,7 @@ // * Smaller than the audio, sliding with a loop at the end (state STREAMED WITH LOOP AT END = 5) // * Smaller with a second buffer to help with a loop in the middle (state STREAMED WITH SECOND BUF = 6) // * Not managed, decoding using "low level" manual looping etc. (LOW LEVEL = 8) -// * Not managed, reseved externally - possibly by sceSas - through low level (RESERVED = 16) +// * Not managed, reserved externally - possibly by sceSas - through low level (RESERVED = 16) #define ATRAC_ERROR_API_FAIL 0x80630002 #define ATRAC_ERROR_NO_ATRACID 0x80630003 diff --git a/GPU/Directx9/PixelShaderGeneratorDX9.cpp b/GPU/Directx9/PixelShaderGeneratorDX9.cpp index 9b25ea5d82..4d7dd5c342 100644 --- a/GPU/Directx9/PixelShaderGeneratorDX9.cpp +++ b/GPU/Directx9/PixelShaderGeneratorDX9.cpp @@ -376,6 +376,33 @@ static bool CanDoubleSrcBlendMode() { } } +enum LogicOpReplaceType { + LOGICOPTYPE_NORMAL, + LOGICOPTYPE_ONE, + LOGICOPTYPE_INVERT, +}; + +static inline LogicOpReplaceType ReplaceLogicOpType() { + if (gstate.isLogicOpEnabled()) { + switch (gstate.getLogicOp()) { + case GE_LOGIC_COPY_INVERTED: + case GE_LOGIC_AND_INVERTED: + case GE_LOGIC_OR_INVERTED: + case GE_LOGIC_NOR: + case GE_LOGIC_NAND: + case GE_LOGIC_EQUIV: + return LOGICOPTYPE_INVERT; + case GE_LOGIC_INVERTED: + return LOGICOPTYPE_ONE; + case GE_LOGIC_SET: + return LOGICOPTYPE_ONE; + default: + return LOGICOPTYPE_NORMAL; + } + } + return LOGICOPTYPE_NORMAL; +} + // Here we must take all the bits of the gstate that determine what the fragment shader will // look like, and concatenate them together into an ID. void ComputeFragmentShaderIDDX9(FragmentShaderIDDX9 *id) { @@ -448,7 +475,8 @@ void ComputeFragmentShaderIDDX9(FragmentShaderIDDX9 *id) { gpuStats.numNonAlphaTestedDraws++; id0 |= (gstate_c.bgraTexture & 1) << 29; - // 30 and 31 are free. + // 2 bits. + id0 |= ReplaceLogicOpType() << 30; // 3 bits. id1 |= replaceBlend << 0; @@ -790,6 +818,17 @@ void GenerateFragmentShaderDX9(char *buffer) { break; } + switch (ReplaceLogicOpType()) { + case LOGICOPTYPE_ONE: + WRITE(p, " v.rgb = float3(1.0, 1.0, 1.0);\n"); + break; + case LOGICOPTYPE_INVERT: + WRITE(p, " v.rgb = float3(1.0, 1.0, 1.0) - v.rgb;\n"); + break; + case LOGICOPTYPE_NORMAL: + break; + } + WRITE(p, " return v;\n"); WRITE(p, "}\n"); } diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 8c6dd6e964..8d31ded210 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -162,15 +162,76 @@ inline void TransformDrawEngineDX9::ResetShaderBlending() { } } -void TransformDrawEngineDX9::ApplyStencilReplaceOnly() { +void TransformDrawEngineDX9::ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil) { + StencilValueType stencilType = STENCIL_VALUE_KEEP; + if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { + stencilType = ReplaceAlphaWithStencilType(); + } + + // Normally, we would add src + 0, but the logic op may have us do differently. + D3DBLEND srcBlend = D3DBLEND_ONE; + D3DBLEND dstBlend = D3DBLEND_ZERO; + D3DBLENDOP blendOp = D3DBLENDOP_ADD; + if (gstate.isLogicOpEnabled()) { + switch (gstate.getLogicOp()) + { + case GE_LOGIC_CLEAR: + srcBlend = D3DBLEND_ZERO; + break; + case GE_LOGIC_AND: + case GE_LOGIC_AND_REVERSE: + WARN_LOG_REPORT_ONCE(d3dLogicOpAnd, G3D, "Unsupported AND logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_COPY: + // This is the same as off. + break; + case GE_LOGIC_COPY_INVERTED: + // Handled in the shader. + break; + case GE_LOGIC_AND_INVERTED: + case GE_LOGIC_NOR: + case GE_LOGIC_NAND: + case GE_LOGIC_EQUIV: + // Handled in the shader. + WARN_LOG_REPORT_ONCE(d3dLogicOpAndInverted, G3D, "Attempted invert for logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_INVERTED: + srcBlend = D3DBLEND_ONE; + dstBlend = D3DBLEND_ONE; + blendOp = D3DBLENDOP_SUBTRACT; + WARN_LOG_REPORT_ONCE(d3dLogicOpInverted, G3D, "Attempted inverse for logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_NOOP: + srcBlend = D3DBLEND_ZERO; + dstBlend = D3DBLEND_ONE; + break; + case GE_LOGIC_XOR: + WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_OR: + case GE_LOGIC_OR_INVERTED: + // Inverted in shader. + dstBlend = D3DBLEND_ONE; + WARN_LOG_REPORT_ONCE(d3dLogicOpOr, G3D, "Attempted or for logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_OR_REVERSE: + WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_SET: + dstBlend = D3DBLEND_ONE; + WARN_LOG_REPORT_ONCE(d3dLogicOpSet, G3D, "Attempted set for logic op: %x", gstate.getLogicOp()); + break; + } + } + // We're not blending, but we may still want to blend for stencil. // This is only useful for INCR/DECR/INVERT. Others can write directly. - switch (ReplaceAlphaWithStencilType()) { + switch (stencilType) { case STENCIL_VALUE_INCR_4: case STENCIL_VALUE_INCR_8: // We'll add the incremented value output by the shader. - dxstate.blendFunc.set(D3DBLEND_ONE, D3DBLEND_ZERO, D3DBLEND_ONE, D3DBLEND_ONE); - dxstate.blendEquation.set(D3DBLENDOP_ADD, D3DBLENDOP_ADD); + dxstate.blendFunc.set(srcBlend, dstBlend, D3DBLEND_ONE, D3DBLEND_ONE); + dxstate.blendEquation.set(blendOp, D3DBLENDOP_ADD); dxstate.blend.enable(); dxstate.blendSeparate.enable(); break; @@ -178,22 +239,29 @@ void TransformDrawEngineDX9::ApplyStencilReplaceOnly() { case STENCIL_VALUE_DECR_4: case STENCIL_VALUE_DECR_8: // We'll subtract the incremented value output by the shader. - dxstate.blendFunc.set(D3DBLEND_ONE, D3DBLEND_ZERO, D3DBLEND_ONE, D3DBLEND_ONE); - dxstate.blendEquation.set(D3DBLENDOP_ADD, D3DBLENDOP_SUBTRACT); + dxstate.blendFunc.set(srcBlend, dstBlend, D3DBLEND_ONE, D3DBLEND_ONE); + dxstate.blendEquation.set(blendOp, D3DBLENDOP_SUBTRACT); dxstate.blend.enable(); dxstate.blendSeparate.enable(); break; case STENCIL_VALUE_INVERT: // The shader will output one, and reverse subtracting will essentially invert. - dxstate.blendFunc.set(D3DBLEND_ONE, D3DBLEND_ZERO, D3DBLEND_ONE, D3DBLEND_ONE); - dxstate.blendEquation.set(D3DBLENDOP_ADD, D3DBLENDOP_REVSUBTRACT); + dxstate.blendFunc.set(srcBlend, dstBlend, D3DBLEND_ONE, D3DBLEND_ONE); + dxstate.blendEquation.set(blendOp, D3DBLENDOP_REVSUBTRACT); dxstate.blend.enable(); dxstate.blendSeparate.enable(); break; default: - dxstate.blend.disable(); + if (srcBlend == D3DBLEND_ONE && dstBlend == D3DBLEND_ZERO && blendOp == D3DBLENDOP_ADD) { + dxstate.blend.disable(); + } else { + dxstate.blendFunc.set(srcBlend, dstBlend, D3DBLEND_ONE, D3DBLEND_ZERO); + dxstate.blendEquation.set(blendOp, D3DBLENDOP_ADD); + dxstate.blend.enable(); + dxstate.blendSeparate.enable(); + } break; } } @@ -206,6 +274,7 @@ void TransformDrawEngineDX9::ApplyBlendState() { // These may clip incorrectly, so we avoid unfortunately. // * Direct3D only has one arbitrary fixed color. We premultiply the other in the shader. // * The written output alpha should actually be the stencil value. Alpha is not written. + // * We try to apply logical operations through blending. // // If we can't apply blending, we make a copy of the framebuffer and do it manually. @@ -220,22 +289,13 @@ void TransformDrawEngineDX9::ApplyBlendState() { case REPLACE_BLEND_NO: ResetShaderBlending(); // We may still want to do something about stencil -> alpha. - if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { - ApplyStencilReplaceOnly(); - } else { - dxstate.blend.disable(); - } + ApplyStencilReplaceAndLogicOp(replaceAlphaWithStencil); return; case REPLACE_BLEND_COPY_FBO: if (ApplyShaderBlending()) { // We may still want to do something about stencil -> alpha. - if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { - ApplyStencilReplaceOnly(); - } else { - // None of the below logic is interesting, we're gonna do it entirely in the shader. - dxstate.blend.disable(); - } + ApplyStencilReplaceAndLogicOp(replaceAlphaWithStencil); return; } // Until next time, force it off. diff --git a/GPU/Directx9/TransformPipelineDX9.h b/GPU/Directx9/TransformPipelineDX9.h index 74a2db807a..45d80ceb3a 100644 --- a/GPU/Directx9/TransformPipelineDX9.h +++ b/GPU/Directx9/TransformPipelineDX9.h @@ -25,6 +25,7 @@ #include "GPU/Common/IndexGenerator.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/DrawEngineCommon.h" +#include "GPU/Directx9/PixelShaderGeneratorDX9.h" struct DecVtxFormat; @@ -183,7 +184,7 @@ private: void ApplyDrawState(int prim); void ApplyDrawStateLate(); void ApplyBlendState(); - void ApplyStencilReplaceOnly(); + void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil); bool ApplyShaderBlending(); inline void ResetShaderBlending(); diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index ab49f23ebe..793c3f58b2 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -354,6 +354,35 @@ ReplaceBlendType ReplaceBlendWithShader() { } } +enum LogicOpReplaceType { + LOGICOPTYPE_NORMAL, + LOGICOPTYPE_ONE, + LOGICOPTYPE_INVERT, +}; + +static inline LogicOpReplaceType ReplaceLogicOpType() { +#if defined(USING_GLES2) + if (gstate.isLogicOpEnabled()) { + switch (gstate.getLogicOp()) { + case GE_LOGIC_COPY_INVERTED: + case GE_LOGIC_AND_INVERTED: + case GE_LOGIC_OR_INVERTED: + case GE_LOGIC_NOR: + case GE_LOGIC_NAND: + case GE_LOGIC_EQUIV: + return LOGICOPTYPE_INVERT; + case GE_LOGIC_INVERTED: + return LOGICOPTYPE_ONE; + case GE_LOGIC_SET: + return LOGICOPTYPE_ONE; + default: + return LOGICOPTYPE_NORMAL; + } + } +#endif + return LOGICOPTYPE_NORMAL; +} + // Here we must take all the bits of the gstate that determine what the fragment shader will // look like, and concatenate them together into an ID. void ComputeFragmentShaderID(FragmentShaderID *id) { @@ -425,7 +454,9 @@ void ComputeFragmentShaderID(FragmentShaderID *id) { else gpuStats.numNonAlphaTestedDraws++; - // 29 - 31 are free. + // 29 is free. + // 2 bits. + id0 |= ReplaceLogicOpType() << 30; // 3 bits. id1 |= replaceBlend << 0; @@ -1007,6 +1038,17 @@ void GenerateFragmentShader(char *buffer) { break; } + switch (ReplaceLogicOpType()) { + case LOGICOPTYPE_ONE: + WRITE(p, " %s.rgb = vec3(1.0, 1.0, 1.0);\n", fragColor0); + break; + case LOGICOPTYPE_INVERT: + WRITE(p, " %s.rgb = vec3(1.0, 1.0, 1.0) - %s.rgb;\n", fragColor0, fragColor0); + break; + case LOGICOPTYPE_NORMAL: + break; + } + #ifdef DEBUG_SHADER if (doTexture) { WRITE(p, " %s = texture2D(tex, v_texcoord.xy);\n", fragColor0); diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 1c4c0b214e..8a3f11a758 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -208,35 +208,104 @@ inline void TransformDrawEngine::ResetShaderBlending() { } } -void TransformDrawEngine::ApplyStencilReplaceOnly() { +void TransformDrawEngine::ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil) { + StencilValueType stencilType = STENCIL_VALUE_KEEP; + if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { + stencilType = ReplaceAlphaWithStencilType(); + } + + // Normally, we would add src + 0, but the logic op may have us do differently. + GLenum srcBlend = GL_ONE; + GLenum dstBlend = GL_ZERO; + GLenum blendOp = GL_FUNC_ADD; +#if defined(USING_GLES2) + if (gstate.isLogicOpEnabled()) { + switch (gstate.getLogicOp()) + { + case GE_LOGIC_CLEAR: + srcBlend = GL_ZERO; + break; + case GE_LOGIC_AND: + case GE_LOGIC_AND_REVERSE: + WARN_LOG_REPORT_ONCE(d3dLogicOpAnd, G3D, "Unsupported AND logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_COPY: + // This is the same as off. + break; + case GE_LOGIC_COPY_INVERTED: + // Handled in the shader. + break; + case GE_LOGIC_AND_INVERTED: + case GE_LOGIC_NOR: + case GE_LOGIC_NAND: + case GE_LOGIC_EQUIV: + // Handled in the shader. + WARN_LOG_REPORT_ONCE(d3dLogicOpAndInverted, G3D, "Attempted invert for logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_INVERTED: + srcBlend = GL_ONE; + dstBlend = GL_ONE; + blendOp = GL_FUNC_SUBTRACT; + WARN_LOG_REPORT_ONCE(d3dLogicOpInverted, G3D, "Attempted inverse for logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_NOOP: + srcBlend = GL_ZERO; + dstBlend = GL_ONE; + break; + case GE_LOGIC_XOR: + WARN_LOG_REPORT_ONCE(d3dLogicOpOrXor, G3D, "Unsupported XOR logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_OR: + case GE_LOGIC_OR_INVERTED: + // Inverted in shader. + dstBlend = GL_ONE; + WARN_LOG_REPORT_ONCE(d3dLogicOpOr, G3D, "Attempted or for logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_OR_REVERSE: + WARN_LOG_REPORT_ONCE(d3dLogicOpOrReverse, G3D, "Unsupported OR REVERSE logic op: %x", gstate.getLogicOp()); + break; + case GE_LOGIC_SET: + dstBlend = GL_ONE; + WARN_LOG_REPORT_ONCE(d3dLogicOpSet, G3D, "Attempted set for logic op: %x", gstate.getLogicOp()); + break; + } + } +#endif + // We're not blending, but we may still want to blend for stencil. // This is only useful for INCR/DECR/INVERT. Others can write directly. - switch (ReplaceAlphaWithStencilType()) { + switch (stencilType) { case STENCIL_VALUE_INCR_4: case STENCIL_VALUE_INCR_8: // We'll add the incremented value output by the shader. - glstate.blendFuncSeparate.set(GL_ONE, GL_ZERO, GL_ONE, GL_ONE); - glstate.blendEquationSeparate.set(GL_FUNC_ADD, GL_FUNC_ADD); + glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ONE); + glstate.blendEquationSeparate.set(blendOp, GL_FUNC_ADD); glstate.blend.enable(); break; case STENCIL_VALUE_DECR_4: case STENCIL_VALUE_DECR_8: // We'll subtract the incremented value output by the shader. - glstate.blendFuncSeparate.set(GL_ONE, GL_ZERO, GL_ONE, GL_ONE); - glstate.blendEquationSeparate.set(GL_FUNC_ADD, GL_FUNC_SUBTRACT); + glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ONE); + glstate.blendEquationSeparate.set(blendOp, GL_FUNC_SUBTRACT); glstate.blend.enable(); break; case STENCIL_VALUE_INVERT: // The shader will output one, and reverse subtracting will essentially invert. - glstate.blendFuncSeparate.set(GL_ONE, GL_ZERO, GL_ONE, GL_ONE); - glstate.blendEquationSeparate.set(GL_FUNC_ADD, GL_FUNC_REVERSE_SUBTRACT); + glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ONE); + glstate.blendEquationSeparate.set(blendOp, GL_FUNC_REVERSE_SUBTRACT); glstate.blend.enable(); break; default: - glstate.blend.disable(); + if (srcBlend == GL_ONE && dstBlend == GL_ZERO && blendOp == GL_FUNC_ADD) { + glstate.blend.disable(); + } else { + glstate.blendFuncSeparate.set(srcBlend, dstBlend, GL_ONE, GL_ZERO); + glstate.blendEquationSeparate.set(blendOp, GL_FUNC_ADD); + glstate.blend.enable(); + } break; } } @@ -261,22 +330,13 @@ void TransformDrawEngine::ApplyBlendState() { case REPLACE_BLEND_NO: ResetShaderBlending(); // We may still want to do something about stencil -> alpha. - if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { - ApplyStencilReplaceOnly(); - } else { - glstate.blend.disable(); - } + ApplyStencilReplaceAndLogicOp(replaceAlphaWithStencil); return; case REPLACE_BLEND_COPY_FBO: if (ApplyShaderBlending()) { // We may still want to do something about stencil -> alpha. - if (replaceAlphaWithStencil == REPLACE_ALPHA_YES) { - ApplyStencilReplaceOnly(); - } else { - // None of the below logic is interesting, we're gonna do it entirely in the shader. - glstate.blend.disable(); - } + ApplyStencilReplaceAndLogicOp(replaceAlphaWithStencil); return; } // Until next time, force it off. diff --git a/GPU/GLES/TransformPipeline.h b/GPU/GLES/TransformPipeline.h index e8bcab3c75..1b9d16bfc5 100644 --- a/GPU/GLES/TransformPipeline.h +++ b/GPU/GLES/TransformPipeline.h @@ -23,6 +23,7 @@ #include "GPU/Common/IndexGenerator.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/Common/DrawEngineCommon.h" +#include "GPU/GLES/FragmentShaderGenerator.h" #include "gfx/gl_common.h" #include "gfx/gl_lost_manager.h" @@ -180,7 +181,7 @@ private: void ApplyDrawState(int prim); void ApplyDrawStateLate(); void ApplyBlendState(); - void ApplyStencilReplaceOnly(); + void ApplyStencilReplaceAndLogicOp(ReplaceAlphaType replaceAlphaWithStencil); bool ApplyShaderBlending(); inline void ResetShaderBlending(); GLuint AllocateBuffer();