From cb453a0fb3209bc7753bc0dbbc36d4e07b7964e6 Mon Sep 17 00:00:00 2001 From: Nolan Check Date: Wed, 20 Oct 2010 03:11:22 +0000 Subject: [PATCH] DX11 plugin: Do destination-alpha in a single pass. No more drawing the same geometry twice! SMG is faster now because it uses destination-alpha extensively. This uses dual-source color blending, a DirectX 10-level feature. The equivalent OpenGL function comes from the GL_ARB_blend_func_extended extension, which was made core in OpenGL 3.3. git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@6294 8ced0084-cf51-0410-be5f-012b33b47a6e --- Source/Core/Common/Src/LinearDiskCache.cpp | 2 +- .../Core/VideoCommon/Src/PixelShaderGen.cpp | 22 ++++-- .../Plugins/Plugin_VideoDX11/Src/GfxState.cpp | 76 +++++++++++-------- .../Plugins/Plugin_VideoDX11/Src/GfxState.h | 5 +- .../Plugins/Plugin_VideoDX11/Src/Render.cpp | 8 +- .../Plugin_VideoDX11/Src/VertexManager.cpp | 26 +++---- .../Plugin_VideoDX11/Src/VertexManager.h | 2 +- 7 files changed, 79 insertions(+), 62 deletions(-) diff --git a/Source/Core/Common/Src/LinearDiskCache.cpp b/Source/Core/Common/Src/LinearDiskCache.cpp index 37dfb8a69a..25665257ab 100644 --- a/Source/Core/Common/Src/LinearDiskCache.cpp +++ b/Source/Core/Common/Src/LinearDiskCache.cpp @@ -22,7 +22,7 @@ static const char ID[4] = {'D', 'C', 'A', 'C'}; // Update this to the current SVN revision every time you change shader generation code. // We don't automatically get this from SVN_REV because that would mean regenerating the // shader cache for every revision, graphics-related or not, which is simply annoying. -const int version = 6267; +const int version = 6294; LinearDiskCache::LinearDiskCache() : file_(NULL), num_entries_(0) { diff --git a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp index e528c6a1b3..7b638a1cbd 100644 --- a/Source/Core/VideoCommon/Src/PixelShaderGen.cpp +++ b/Source/Core/VideoCommon/Src/PixelShaderGen.cpp @@ -521,7 +521,7 @@ const char *GeneratePixelShaderCode(bool dstAlphaEnable, API_TYPE ApiType,u32 co if(ApiType != API_D3D11) WRITE(p, " out float4 ocol0 : COLOR0,%s\n in float4 rawpos : %s,\n",DepthTextureEnable ? "\n out float depth : DEPTH," : "", ApiType == API_OPENGL ? "WPOS" : "POSITION"); else - WRITE(p, " out float4 ocol0 : SV_Target,%s\n in float4 rawpos : SV_Position,\n",DepthTextureEnable ? "\n out float depth : SV_Depth," : ""); + WRITE(p, " out float4 ocol0 : SV_Target0,\n out float4 ocol1 : SV_Target1,%s\n in float4 rawpos : SV_Position,\n",DepthTextureEnable ? "\n out float depth : SV_Depth," : ""); WRITE(p, " in float4 colors_0 : COLOR0,\n"); WRITE(p, " in float4 colors_1 : COLOR1"); @@ -544,7 +544,8 @@ const char *GeneratePixelShaderCode(bool dstAlphaEnable, API_TYPE ApiType,u32 co char* pmainstart = p; int Pretest = AlphaPreTest(); - if (dstAlphaEnable && !DepthTextureEnable && Pretest >= 0) + // TODO: Re-enable the early discard on D3D11 + if (dstAlphaEnable && !DepthTextureEnable && Pretest >= 0 && ApiType != API_D3D11) { if (!Pretest) { @@ -806,13 +807,24 @@ const char *GeneratePixelShaderCode(bool dstAlphaEnable, API_TYPE ApiType,u32 co WRITE(p, "depth = zCoord;\n"); } - if (dstAlphaEnable) + if (dstAlphaEnable && ApiType != API_D3D11) WRITE(p, " ocol0 = float4(prev.rgb, "I_ALPHA"[0].a);\n"); else { WriteFog(p); WRITE(p, " ocol0 = prev;\n"); - } + } + + // On D3D11, use dual-source color blending to perform dst alpha in a + // single pass + if (ApiType == API_D3D11) + { + // Colors will be blended against the alpha from ocol1... + WRITE(p, " ocol1 = ocol0;\n"); + // ...and the alpha from ocol0 will be written to the framebuffer. + if (dstAlphaEnable) + WRITE(p, " ocol0.a = "I_ALPHA"[0].a;\n"); + } } WRITE(p, "}\n"); if (text[sizeof(text) - 1] != 0x7C) @@ -1256,7 +1268,7 @@ static bool WriteAlphaTest(char *&p, API_TYPE ApiType) int Pretest = AlphaPreTest(); if(Pretest >= 0) { - return Pretest; + return Pretest != 0; } // using discard then return works the same in cg and dx9 but not in dx11 diff --git a/Source/Plugins/Plugin_VideoDX11/Src/GfxState.cpp b/Source/Plugins/Plugin_VideoDX11/Src/GfxState.cpp index 10d2dd2d3a..b9a3ca4b65 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/GfxState.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/GfxState.cpp @@ -34,6 +34,8 @@ EmuGfxState::EmuGfxState() : vertexshader(NULL), vsbytecode(NULL), pixelshader(N if(g_ActiveConfig.iMaxAnisotropy > 1) samplerdesc[k].Filter = D3D11_FILTER_ANISOTROPIC; } + m_useDstAlpha = false; + memset(&blenddesc, 0, sizeof(blenddesc)); blenddesc.AlphaToCoverageEnable = FALSE; blenddesc.IndependentBlendEnable = FALSE; @@ -70,7 +72,7 @@ EmuGfxState::EmuGfxState() : vertexshader(NULL), vsbytecode(NULL), pixelshader(N EmuGfxState::~EmuGfxState() { for (unsigned int k = 0;k < 8;k++) - SAFE_RELEASE(shader_resources[k]) + SAFE_RELEASE(shader_resources[k]); SAFE_RELEASE(vsbytecode); SAFE_RELEASE(psbytecode); @@ -224,27 +226,6 @@ void EmuGfxState::ApplyState() apply_called = true; } -void EmuGfxState::AlphaPass() -{ - if (!apply_called) ERROR_LOG(VIDEO, "EmuGfxState::AlphaPass called without having called ApplyState before!") - else stateman->PopBlendState(); - - // pixel shader for alpha pass is different, so update it - context->PSSetShader(pixelshader, NULL, 0); - - ID3D11BlendState* blstate; - D3D11_BLEND_DESC desc = blenddesc; - desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA; - desc.RenderTarget[0].BlendEnable = FALSE; - HRESULT hr = device->CreateBlendState(&desc, &blstate); - if (FAILED(hr)) PanicAlert("Failed to create blend state at %s %d\n", __FILE__, __LINE__); - SetDebugObjectName((ID3D11DeviceChild*)blstate, "a blend state of EmuGfxState (created during alpha pass)"); - stateman->PushBlendState(blstate); - blstate->Release(); - - stateman->Apply(); -} - void EmuGfxState::Reset() { for (unsigned int k = 0;k < 8;k++) @@ -274,28 +255,57 @@ void EmuGfxState::SetSrcBlend(D3D11_BLEND val) { // TODO: Check whether e.g. the dest color check is needed here blenddesc.RenderTarget[0].SrcBlend = val; - if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; - else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; - else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_DEST_ALPHA; - else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; - else blenddesc.RenderTarget[0].SrcBlendAlpha = val; + if (m_useDstAlpha) + { + blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; + } + else + { + if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA; + else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_DEST_ALPHA; + else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; + else blenddesc.RenderTarget[0].SrcBlendAlpha = val; + } } void EmuGfxState::SetDestBlend(D3D11_BLEND val) { // TODO: Check whether e.g. the source color check is needed here blenddesc.RenderTarget[0].DestBlend = val; - if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_SRC_ALPHA; - else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; - else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA; - else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; - else blenddesc.RenderTarget[0].DestBlendAlpha = val; + if (m_useDstAlpha) + { + blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; + } + else + { + if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_SRC_ALPHA; + else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA; + else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA; + else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA; + else blenddesc.RenderTarget[0].DestBlendAlpha = val; + } } void EmuGfxState::SetBlendOp(D3D11_BLEND_OP val) { blenddesc.RenderTarget[0].BlendOp = val; - blenddesc.RenderTarget[0].BlendOpAlpha = val; + if (m_useDstAlpha) + { + blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; + } + else + { + blenddesc.RenderTarget[0].BlendOpAlpha = val; + } +} + +void EmuGfxState::SetDstAlpha(bool enable) +{ + m_useDstAlpha = enable; + SetSrcBlend(blenddesc.RenderTarget[0].SrcBlend); + SetDestBlend(blenddesc.RenderTarget[0].DestBlend); + SetBlendOp(blenddesc.RenderTarget[0].BlendOp); } void EmuGfxState::SetSamplerFilter(DWORD stage, D3D11_FILTER filter) diff --git a/Source/Plugins/Plugin_VideoDX11/Src/GfxState.h b/Source/Plugins/Plugin_VideoDX11/Src/GfxState.h index 507eb76c34..90151a7905 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/GfxState.h +++ b/Source/Plugins/Plugin_VideoDX11/Src/GfxState.h @@ -39,7 +39,6 @@ public: void SetShaderResource(unsigned int stage, ID3D11ShaderResourceView* srv); void ApplyState(); // apply current state - void AlphaPass(); // only modify the current state to enable the alpha pass void Reset(); // blend state @@ -49,6 +48,8 @@ public: void SetDestBlend(D3D11_BLEND val); void SetBlendOp(D3D11_BLEND_OP val); + void SetDstAlpha(bool enable); + // sampler states void SetSamplerFilter(DWORD stage, D3D11_FILTER filter); @@ -80,6 +81,8 @@ private: ID3D11ShaderResourceView* shader_resources[8]; D3D11_BLEND_DESC blenddesc; + bool m_useDstAlpha; + bool apply_called; }; diff --git a/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp b/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp index c06ff9c9de..5ab0290b82 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/Render.cpp @@ -90,8 +90,8 @@ static const D3D11_BLEND d3dSrcFactors[8] = D3D11_BLEND_ONE, D3D11_BLEND_DEST_COLOR, D3D11_BLEND_INV_DEST_COLOR, - D3D11_BLEND_SRC_ALPHA, - D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_SRC1_ALPHA, + D3D11_BLEND_INV_SRC1_ALPHA, // Use dual-source color blending for dst alpha D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA }; @@ -102,8 +102,8 @@ static const D3D11_BLEND d3dDestFactors[8] = D3D11_BLEND_ONE, D3D11_BLEND_SRC_COLOR, D3D11_BLEND_INV_SRC_COLOR, - D3D11_BLEND_SRC_ALPHA, - D3D11_BLEND_INV_SRC_ALPHA, + D3D11_BLEND_SRC1_ALPHA, + D3D11_BLEND_INV_SRC1_ALPHA, // Use dual-source color blending for dst alpha D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA }; diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp index ff4571c054..6f9d90feb8 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.cpp @@ -144,12 +144,9 @@ void VertexManager::LoadBuffers() m_indexBufferCursor += iCount; } -void VertexManager::Draw(UINT stride, bool alphapass) +void VertexManager::Draw(UINT stride) { - if (!alphapass) - D3D::gfxstate->ApplyState(); - else - D3D::gfxstate->AlphaPass(); + D3D::gfxstate->ApplyState(); D3D::context->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &m_vertexDrawOffset); D3D::context->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R16_UINT, 0); @@ -221,7 +218,12 @@ void VertexManager::vFlush() VertexShaderManager::SetConstants(); PixelShaderManager::SetConstants(); - if (!PixelShaderCache::SetShader(false,g_nativeVertexFmt->m_components)) + bool useDstAlpha = bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate && + bpmem.zcontrol.pixel_format == PIXELFMT_RGBA6_Z24; + + D3D::gfxstate->SetDstAlpha(useDstAlpha); + + if (!PixelShaderCache::SetShader(useDstAlpha,g_nativeVertexFmt->m_components)) goto shader_fail; if (!VertexShaderCache::SetShader(g_nativeVertexFmt->m_components)) goto shader_fail; @@ -230,18 +232,8 @@ void VertexManager::vFlush() g_nativeVertexFmt->SetupVertexPointers(); LoadBuffers(); + Draw(stride); - Draw(stride, false); - - if (bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate) - { - DWORD write = 0; - if (!PixelShaderCache::SetShader(true,g_nativeVertexFmt->m_components)) - goto shader_fail; - - // update alpha only - Draw(stride, true); - } D3D::gfxstate->Reset(); shader_fail: diff --git a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.h b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.h index b6e3ebd545..8efc08d373 100644 --- a/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.h +++ b/Source/Plugins/Plugin_VideoDX11/Src/VertexManager.h @@ -37,7 +37,7 @@ private: void CreateDeviceObjects(); void DestroyDeviceObjects(); void LoadBuffers(); - void Draw(UINT stride, bool alphapass); + void Draw(UINT stride); // temp void vFlush();