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"
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);
62 vshaderchanged
= false;
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
);
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
);
96 vertexshader
= shader
;
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
)
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()
131 // input layout (only needs to be updated if the vertex shader signature changed)
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
);
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
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");
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
);
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");
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");
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
);
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
);
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
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!");