DX9/DX11: Workaround the viewpoint/EFB creation issues in e.g. SMG2 on NVIDIA hardwar...
[dolphin.git] / Source / Plugins / Plugin_VideoDX11 / Src / GfxState.cpp
blob8ce9d412558b01048600af68163b97d15a5523f8
1 // Copyright (C) 2003 Dolphin Project.
3 // This program is free software: you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation, version 2.0.
7 // This program is distributed in the hope that it will be useful,
8 // but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // GNU General Public License 2.0 for more details.
12 // A copy of the GPL 2.0 should have been included with the program.
13 // If not, see http://www.gnu.org/licenses/
15 // Official SVN repository and contact information can be found at
16 // http://code.google.com/p/dolphin-emu/
18 #include "VideoConfig.h"
19 #include "GfxState.h"
21 namespace D3D
24 EmuGfxState* gfxstate;
25 StateManager* stateman;
27 EmuGfxState::EmuGfxState() : vertexshader(NULL), vsbytecode(NULL), pixelshader(NULL), psbytecode(NULL), apply_called(false)
29 for (unsigned int k = 0;k < 8;k++)
31 float border[4] = {0.f, 0.f, 0.f, 0.f};
32 shader_resources[k] = NULL;
33 samplerdesc[k] = CD3D11_SAMPLER_DESC(D3D11_FILTER_MIN_MAG_MIP_LINEAR, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_CLAMP, 0.f, 16, D3D11_COMPARISON_ALWAYS, border, -D3D11_FLOAT32_MAX, D3D11_FLOAT32_MAX);
34 if(g_ActiveConfig.iMaxAnisotropy > 1) samplerdesc[k].Filter = D3D11_FILTER_ANISOTROPIC;
37 memset(&blenddesc, 0, sizeof(blenddesc));
38 blenddesc.AlphaToCoverageEnable = FALSE;
39 blenddesc.IndependentBlendEnable = FALSE;
40 blenddesc.RenderTarget[0].BlendEnable = FALSE;
41 blenddesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
42 blenddesc.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
43 blenddesc.RenderTarget[0].DestBlend = D3D11_BLEND_ZERO;
44 blenddesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
45 blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
46 blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
47 blenddesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
49 memset(&depthdesc, 0, sizeof(depthdesc));
50 depthdesc.DepthEnable = TRUE;
51 depthdesc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
52 depthdesc.DepthFunc = D3D11_COMPARISON_LESS;
53 depthdesc.StencilEnable = FALSE;
54 depthdesc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
55 depthdesc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
57 // this probably must be changed once multisampling support gets added
58 rastdesc = CD3D11_RASTERIZER_DESC(D3D11_FILL_SOLID, D3D11_CULL_NONE, false, 0, 0.f, 0, false, true, false, false);
60 pscbuf = NULL;
61 vscbuf = NULL;
62 vshaderchanged = false;
63 inp_layout = NULL;
64 num_inp_elems = 0;
66 pscbufchanged = false;
67 vscbufchanged = false;
70 EmuGfxState::~EmuGfxState()
72 for (unsigned int k = 0;k < 8;k++)
73 SAFE_RELEASE(shader_resources[k])
75 SAFE_RELEASE(vsbytecode);
76 SAFE_RELEASE(psbytecode);
77 SAFE_RELEASE(vertexshader);
78 SAFE_RELEASE(pixelshader);
80 SAFE_RELEASE(pscbuf);
81 SAFE_RELEASE(vscbuf);
83 SAFE_RELEASE(inp_layout);
86 // TODO: No need to store the whole bytecode, signature might be enough (?)
87 void EmuGfxState::SetVShader(ID3D11VertexShader* shader, D3DBlob* bcode)
89 // TODO: vshaderchanged actually just needs to be true if the signature changed
90 if (bcode && vsbytecode != bcode) vshaderchanged = true;
91 SAFE_RELEASE(vsbytecode);
92 SAFE_RELEASE(vertexshader);
94 if (shader && bcode)
96 vertexshader = shader;
97 shader->AddRef();
98 vsbytecode = bcode;
99 bcode->AddRef();
101 else if (shader || bcode)
103 PanicAlert("Invalid parameters!\n");
107 void EmuGfxState::SetPShader(ID3D11PixelShader* shader)
109 if (pixelshader) pixelshader->Release();
110 pixelshader = shader;
111 if (shader) shader->AddRef();
114 void EmuGfxState::SetInputElements(const D3D11_INPUT_ELEMENT_DESC* elems, UINT num)
116 num_inp_elems = num;
117 memcpy(inp_elems, elems, num*sizeof(D3D11_INPUT_ELEMENT_DESC));
120 void EmuGfxState::SetShaderResource(unsigned int stage, ID3D11ShaderResourceView* srv)
122 if (shader_resources[stage]) shader_resources[stage]->Release();
123 shader_resources[stage] = srv;
124 if (srv) srv->AddRef();
127 void EmuGfxState::ApplyState()
129 HRESULT hr;
131 // input layout (only needs to be updated if the vertex shader signature changed)
132 if (vshaderchanged)
134 if (inp_layout) inp_layout->Release();
135 hr = D3D::device->CreateInputLayout(inp_elems, num_inp_elems, vsbytecode->Data(), vsbytecode->Size(), &inp_layout);
136 if (FAILED(hr)) PanicAlert("Failed to create input layout, %s %d\n", __FILE__, __LINE__);
137 SetDebugObjectName((ID3D11DeviceChild*)inp_layout, "an input layout of EmuGfxState");
138 vshaderchanged = false;
140 D3D::context->IASetInputLayout(inp_layout);
142 // vertex shader
143 // TODO: divide the global variables of the generated shaders into about 5 constant buffers
144 // TODO: improve interaction between EmuGfxState and global state management, so that we don't need to set the constant buffers every time
145 if (!vscbuf)
147 unsigned int size = ((sizeof(vsconstants))&(~0xf))+0x10; // must be a multiple of 16
148 D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(size, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
149 hr = device->CreateBuffer(&cbdesc, NULL, &vscbuf);
150 CHECK(hr==S_OK, "Create vertex shader constant buffer (size=%u)", size);
151 SetDebugObjectName((ID3D11DeviceChild*)vscbuf, "a vertex shader constant buffer of EmuGfxState");
153 if (vscbufchanged)
155 D3D11_MAPPED_SUBRESOURCE map;
156 context->Map(vscbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
157 memcpy(map.pData, vsconstants, sizeof(vsconstants));
158 context->Unmap(vscbuf, 0);
160 D3D::context->VSSetConstantBuffers(0, 1, &vscbuf);
162 // pixel shader
163 if (!pscbuf)
165 unsigned int size = ((sizeof(psconstants))&(~0xf))+0x10; // must be a multiple of 16
166 D3D11_BUFFER_DESC cbdesc = CD3D11_BUFFER_DESC(size, D3D11_BIND_CONSTANT_BUFFER, D3D11_USAGE_DYNAMIC, D3D11_CPU_ACCESS_WRITE);
167 device->CreateBuffer(&cbdesc, NULL, &pscbuf);
168 CHECK(hr==S_OK, "Create pixel shader constant buffer (size=%u)", size);
169 SetDebugObjectName((ID3D11DeviceChild*)pscbuf, "a pixel shader constant buffer of EmuGfxState");
171 if (pscbufchanged)
173 D3D11_MAPPED_SUBRESOURCE map;
174 context->Map(pscbuf, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
175 memcpy(map.pData, psconstants, sizeof(psconstants));
176 context->Unmap(pscbuf, 0);
177 pscbufchanged = false;
179 D3D::context->PSSetConstantBuffers(0, 1, &pscbuf);
181 ID3D11SamplerState* samplerstate[8];
182 for (unsigned int stage = 0; stage < 8; stage++)
184 if (shader_resources[stage])
186 if(g_ActiveConfig.iMaxAnisotropy > 1) samplerdesc[stage].Filter = D3D11_FILTER_ANISOTROPIC;
187 hr = D3D::device->CreateSamplerState(&samplerdesc[stage], &samplerstate[stage]);
188 if (FAILED(hr)) PanicAlert("Fail %s %d, stage=%d\n", __FILE__, __LINE__, stage);
189 else SetDebugObjectName((ID3D11DeviceChild*)samplerstate[stage], "a sampler state of EmuGfxState");
191 else samplerstate[stage] = NULL;
193 D3D::context->PSSetSamplers(0, 8, samplerstate);
194 for (unsigned int stage = 0; stage < 8; stage++)
195 SAFE_RELEASE(samplerstate[stage]);
197 ID3D11BlendState* blstate;
198 hr = device->CreateBlendState(&blenddesc, &blstate);
199 if (FAILED(hr)) PanicAlert("Failed to create blend state at %s %d\n", __FILE__, __LINE__);
200 stateman->PushBlendState(blstate);
201 SetDebugObjectName((ID3D11DeviceChild*)blstate, "a blend state of EmuGfxState");
202 blstate->Release();
204 rastdesc.FillMode = (g_ActiveConfig.bWireFrame) ? D3D11_FILL_WIREFRAME : D3D11_FILL_SOLID;
205 ID3D11RasterizerState* raststate;
206 hr = device->CreateRasterizerState(&rastdesc, &raststate);
207 if (FAILED(hr)) PanicAlert("Failed to create rasterizer state at %s %d\n", __FILE__, __LINE__);
208 SetDebugObjectName((ID3D11DeviceChild*)raststate, "a rasterizer state of EmuGfxState");
209 stateman->PushRasterizerState(raststate);
210 raststate->Release();
212 ID3D11DepthStencilState* depth_state;
213 hr = device->CreateDepthStencilState(&depthdesc, &depth_state);
214 if (SUCCEEDED(hr)) SetDebugObjectName((ID3D11DeviceChild*)depth_state, "a depth-stencil state of EmuGfxState");
215 else PanicAlert("Failed to create depth state at %s %d\n", __FILE__, __LINE__);
216 D3D::stateman->PushDepthState(depth_state);
217 depth_state->Release();
219 context->PSSetShader(pixelshader, NULL, 0);
220 context->VSSetShader(vertexshader, NULL, 0);
221 context->PSSetShaderResources(0, 8, shader_resources);
223 stateman->Apply();
224 apply_called = true;
227 void EmuGfxState::AlphaPass()
229 if (!apply_called) ERROR_LOG(VIDEO, "EmuGfxState::AlphaPass called without having called ApplyState before!")
230 else stateman->PopBlendState();
232 // pixel shader for alpha pass is different, so update it
233 context->PSSetShader(pixelshader, NULL, 0);
235 ID3D11BlendState* blstate;
236 D3D11_BLEND_DESC desc = blenddesc;
237 desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALPHA;
238 desc.RenderTarget[0].BlendEnable = FALSE;
239 HRESULT hr = device->CreateBlendState(&desc, &blstate);
240 if (FAILED(hr)) PanicAlert("Failed to create blend state at %s %d\n", __FILE__, __LINE__);
241 SetDebugObjectName((ID3D11DeviceChild*)blstate, "a blend state of EmuGfxState (created during alpha pass)");
242 stateman->PushBlendState(blstate);
243 blstate->Release();
245 stateman->Apply();
248 void EmuGfxState::Reset()
250 for (unsigned int k = 0;k < 8;k++)
251 SAFE_RELEASE(shader_resources[k]);
253 context->PSSetShaderResources(0, 8, shader_resources); // unbind all textures
254 if (apply_called)
256 stateman->PopBlendState();
257 stateman->PopDepthState();
258 stateman->PopRasterizerState();
259 apply_called = false;
263 void EmuGfxState::SetAlphaBlendEnable(bool enable)
265 blenddesc.RenderTarget[0].BlendEnable = enable;
268 void EmuGfxState::SetRenderTargetWriteMask(UINT8 mask)
270 blenddesc.RenderTarget[0].RenderTargetWriteMask = mask;
273 void EmuGfxState::SetSrcBlend(D3D11_BLEND val)
275 // TODO: Check whether e.g. the dest color check is needed here
276 blenddesc.RenderTarget[0].SrcBlend = val;
277 if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
278 else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
279 else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_DEST_ALPHA;
280 else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA;
281 else blenddesc.RenderTarget[0].SrcBlendAlpha = val;
284 void EmuGfxState::SetDestBlend(D3D11_BLEND val)
286 // TODO: Check whether e.g. the source color check is needed here
287 blenddesc.RenderTarget[0].DestBlend = val;
288 if (val == D3D11_BLEND_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_SRC_ALPHA;
289 else if (val == D3D11_BLEND_INV_SRC_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
290 else if (val == D3D11_BLEND_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_DEST_ALPHA;
291 else if (val == D3D11_BLEND_INV_DEST_COLOR) blenddesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_DEST_ALPHA;
292 else blenddesc.RenderTarget[0].DestBlendAlpha = val;
295 void EmuGfxState::SetBlendOp(D3D11_BLEND_OP val)
297 blenddesc.RenderTarget[0].BlendOp = val;
298 blenddesc.RenderTarget[0].BlendOpAlpha = val;
301 void EmuGfxState::SetSamplerFilter(DWORD stage, D3D11_FILTER filter)
303 samplerdesc[stage].Filter = filter;
306 template<typename T> AutoState<T>::AutoState(const T* object) : state(object)
308 ((IUnknown*)state)->AddRef();
311 template<typename T> AutoState<T>::AutoState(const AutoState<T> &source)
313 state = source.GetPtr();
314 ((T*)state)->AddRef();
317 template<typename T> AutoState<T>::~AutoState()
319 ((IUnknown*)state)->Release();
322 StateManager::StateManager() : cur_blendstate(NULL), cur_depthstate(NULL), cur_raststate(NULL) {}
324 void StateManager::PushBlendState(const ID3D11BlendState* state) { blendstates.push(AutoBlendState(state)); }
325 void StateManager::PushDepthState(const ID3D11DepthStencilState* state) { depthstates.push(AutoDepthStencilState(state)); }
326 void StateManager::PushRasterizerState(const ID3D11RasterizerState* state) { raststates.push(AutoRasterizerState(state)); }
327 void StateManager::PopBlendState() { blendstates.pop(); }
328 void StateManager::PopDepthState() { depthstates.pop(); }
329 void StateManager::PopRasterizerState() { raststates.pop(); }
331 void StateManager::Apply()
333 if (!blendstates.empty())
335 if (cur_blendstate != blendstates.top().GetPtr())
337 cur_blendstate = (ID3D11BlendState*)blendstates.top().GetPtr();
338 D3D::context->OMSetBlendState(cur_blendstate, NULL, 0xFFFFFFFF);
341 else ERROR_LOG(VIDEO, "Tried to apply without blend state!");
343 if (!depthstates.empty())
345 if (cur_depthstate != depthstates.top().GetPtr())
347 cur_depthstate = (ID3D11DepthStencilState*)depthstates.top().GetPtr();
348 D3D::context->OMSetDepthStencilState(cur_depthstate, 0);
351 else ERROR_LOG(VIDEO, "Tried to apply without depth state!");
353 if (!raststates.empty())
355 if (cur_raststate != raststates.top().GetPtr())
357 cur_raststate = (ID3D11RasterizerState*)raststates.top().GetPtr();
358 D3D::context->RSSetState(cur_raststate);
361 else ERROR_LOG(VIDEO, "Tried to apply without rasterizer state!");
364 } // namespace