Merge pull request #6940 from unknownbrackets/blend-logicop

d3d9: Emulate some logic ops with blending
This commit is contained in:
Henrik Rydgård
2014-09-24 09:30:58 +02:00
7 changed files with 248 additions and 45 deletions
+1 -1
View File
@@ -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
+40 -1
View File
@@ -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");
}
+80 -20
View File
@@ -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.
+2 -1
View File
@@ -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();
+43 -1
View File
@@ -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);
+80 -20
View File
@@ -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.
+2 -1
View File
@@ -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();