Merge pull request #4581 from degasus/blendstate

OGL: Move blending logic in VideoCommon.
This commit is contained in:
Markus Wick 2017-01-06 16:28:12 +01:00 committed by GitHub
commit c380dceb78
8 changed files with 227 additions and 146 deletions

View File

@ -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);
}
if (changes & 0x1FA)
{
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;
glBlendFuncSeparate(src_factors[state.srcfactor], dst_factors[state.dstfactor],
src_factors[state.srcfactoralpha], dst_factors[state.dstfactoralpha]);
}
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;
glDisable(GL_BLEND);
}
// 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;
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
}
GLenum srcFactorAlpha = glSrcFactors[srcidx];
GLenum dstFactorAlpha = glDestFactors[dstidx];
// blend RGB change
glBlendFuncSeparate(srcFactor, dstFactor, srcFactorAlpha, dstFactorAlpha);
else if (state.logicopenable)
{
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(logic_op_codes[state.logicmode]);
}
s_blendMode = newval;
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<VertexManager*>(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];

View File

@ -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;

View File

@ -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);

View File

@ -25,6 +25,7 @@ set(SRCS AsyncRequests.cpp
PixelShaderManager.cpp
PostProcessing.cpp
RenderBase.cpp
RenderState.cpp
Statistics.cpp
TextureCacheBase.cpp
TextureConversionShader.cpp

View File

@ -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.
}
}
}
}

View File

@ -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;
};

View File

@ -80,6 +80,7 @@
<ClCompile Include="PixelShaderManager.cpp" />
<ClCompile Include="PostProcessing.cpp" />
<ClCompile Include="RenderBase.cpp" />
<ClCompile Include="RenderState.cpp" />
<ClCompile Include="LightingShaderGen.cpp" />
<ClCompile Include="Statistics.cpp" />
<ClCompile Include="GeometryShaderGen.cpp" />
@ -134,6 +135,7 @@
<ClInclude Include="PixelShaderManager.h" />
<ClInclude Include="PostProcessing.h" />
<ClInclude Include="RenderBase.h" />
<ClInclude Include="RenderState.h" />
<ClInclude Include="SamplerCommon.h" />
<ClInclude Include="ShaderGenCommon.h" />
<ClInclude Include="Statistics.h" />

View File

@ -44,6 +44,9 @@
<ClCompile Include="RenderBase.cpp">
<Filter>Base</Filter>
</ClCompile>
<ClCompile Include="RenderState.cpp">
<Filter>Base</Filter>
</ClCompile>
<ClCompile Include="TextureCacheBase.cpp">
<Filter>Base</Filter>
</ClCompile>
@ -182,6 +185,9 @@
<ClInclude Include="RenderBase.h">
<Filter>Base</Filter>
</ClInclude>
<ClInclude Include="RenderState.h">
<Filter>Base</Filter>
</ClInclude>
<ClInclude Include="TextureCacheBase.h">
<Filter>Base</Filter>
</ClInclude>