1 //
2 // Copyright 2012 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // RenderStateCache.cpp: Defines rx::RenderStateCache, a cache of Direct3D render
8 // state objects.
9
10 #include "libANGLE/renderer/d3d/d3d11/RenderStateCache.h"
11
12 #include <float.h>
13
14 #include "common/Color.h"
15 #include "common/debug.h"
16 #include "libANGLE/Context.h"
17 #include "libANGLE/Framebuffer.h"
18 #include "libANGLE/FramebufferAttachment.h"
19 #include "libANGLE/renderer/d3d/d3d11/Context11.h"
20 #include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h"
21 #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
22 #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
23
24 namespace rx
25 {
26 using namespace gl_d3d11;
27
RenderStateCache()28 RenderStateCache::RenderStateCache()
29 : mBlendStateCache(kMaxStates),
30 mRasterizerStateCache(kMaxStates),
31 mDepthStencilStateCache(kMaxStates),
32 mSamplerStateCache(kMaxStates)
33 {}
34
~RenderStateCache()35 RenderStateCache::~RenderStateCache() {}
36
clear()37 void RenderStateCache::clear()
38 {
39 mBlendStateCache.Clear();
40 mRasterizerStateCache.Clear();
41 mDepthStencilStateCache.Clear();
42 mSamplerStateCache.Clear();
43 }
44
45 // static
GetBlendStateKey(const gl::Context * context,Framebuffer11 * framebuffer11,const gl::BlendStateExt & blendStateExt,bool sampleAlphaToCoverage)46 d3d11::BlendStateKey RenderStateCache::GetBlendStateKey(const gl::Context *context,
47 Framebuffer11 *framebuffer11,
48 const gl::BlendStateExt &blendStateExt,
49 bool sampleAlphaToCoverage)
50 {
51 d3d11::BlendStateKey key;
52 // All fields of the BlendStateExt inside the key should be initialized for the caching to
53 // work correctly. Due to mrt_perf_workaround, the actual indices of active draw buffers may be
54 // different, so both arrays should be tracked.
55 key.blendStateExt = gl::BlendStateExt(blendStateExt.getDrawBufferCount());
56 const gl::AttachmentList &colorbuffers = framebuffer11->getColorAttachmentsForRender(context);
57 const gl::DrawBufferMask colorAttachmentsForRenderMask =
58 framebuffer11->getLastColorAttachmentsForRenderMask();
59
60 ASSERT(blendStateExt.getDrawBufferCount() <= colorAttachmentsForRenderMask.size());
61 ASSERT(colorbuffers.size() == colorAttachmentsForRenderMask.count());
62
63 size_t keyBlendIndex = 0;
64
65 // With blending disabled, factors and equations are ignored when building
66 // D3D11_RENDER_TARGET_BLEND_DESC, so we can reduce the amount of unique keys by
67 // enforcing default values.
68 for (size_t sourceIndex : colorAttachmentsForRenderMask)
69 {
70 ASSERT(keyBlendIndex < colorbuffers.size());
71 const gl::FramebufferAttachment *attachment = colorbuffers[keyBlendIndex];
72
73 // Do not set blend state for null attachments that may be present when
74 // mrt_perf_workaround is disabled.
75 if (attachment == nullptr)
76 {
77 keyBlendIndex++;
78 continue;
79 }
80
81 const uint8_t colorMask = blendStateExt.getColorMaskIndexed(sourceIndex);
82
83 const gl::InternalFormat &internalFormat = *attachment->getFormat().info;
84
85 key.blendStateExt.setColorMaskIndexed(keyBlendIndex,
86 gl_d3d11::GetColorMask(internalFormat) & colorMask);
87 key.rtvMax = static_cast<uint16_t>(keyBlendIndex) + 1;
88
89 // Some D3D11 drivers produce unexpected results when blending is enabled for integer
90 // attachments. Per OpenGL ES spec, it must be ignored anyway. When blending is disabled,
91 // the state remains default to reduce the number of unique keys.
92 if (blendStateExt.getEnabledMask().test(sourceIndex) && !internalFormat.isInt())
93 {
94 key.blendStateExt.setEnabledIndexed(keyBlendIndex, true);
95 key.blendStateExt.setEquationsIndexed(keyBlendIndex, sourceIndex, blendStateExt);
96
97 // MIN and MAX operations do not need factors, so use default values to further
98 // reduce the number of unique keys. Additionally, ID3D11Device::CreateBlendState
99 // fails if SRC1 factors are specified together with MIN or MAX operations.
100 const gl::BlendEquationType equationColor =
101 blendStateExt.getEquationColorIndexed(sourceIndex);
102 const gl::BlendEquationType equationAlpha =
103 blendStateExt.getEquationAlphaIndexed(sourceIndex);
104 const bool setColorFactors = equationColor != gl::BlendEquationType::Min &&
105 equationColor != gl::BlendEquationType::Max;
106 const bool setAlphaFactors = equationAlpha != gl::BlendEquationType::Min &&
107 equationAlpha != gl::BlendEquationType::Max;
108 if (setColorFactors || setAlphaFactors)
109 {
110 const gl::BlendFactorType srcColor =
111 setColorFactors ? blendStateExt.getSrcColorIndexed(sourceIndex)
112 : gl::BlendFactorType::One;
113 const gl::BlendFactorType dstColor =
114 setColorFactors ? blendStateExt.getDstColorIndexed(sourceIndex)
115 : gl::BlendFactorType::Zero;
116 const gl::BlendFactorType srcAlpha =
117 setAlphaFactors ? blendStateExt.getSrcAlphaIndexed(sourceIndex)
118 : gl::BlendFactorType::One;
119 const gl::BlendFactorType dstAlpha =
120 setAlphaFactors ? blendStateExt.getDstAlphaIndexed(sourceIndex)
121 : gl::BlendFactorType::Zero;
122 key.blendStateExt.setFactorsIndexed(keyBlendIndex, srcColor, dstColor, srcAlpha,
123 dstAlpha);
124 }
125 }
126 keyBlendIndex++;
127 }
128
129 key.sampleAlphaToCoverage = sampleAlphaToCoverage ? 1 : 0;
130 return key;
131 }
132
getBlendState(const gl::Context * context,Renderer11 * renderer,const d3d11::BlendStateKey & key,const d3d11::BlendState ** outBlendState)133 angle::Result RenderStateCache::getBlendState(const gl::Context *context,
134 Renderer11 *renderer,
135 const d3d11::BlendStateKey &key,
136 const d3d11::BlendState **outBlendState)
137 {
138 auto keyIter = mBlendStateCache.Get(key);
139 if (keyIter != mBlendStateCache.end())
140 {
141 *outBlendState = &keyIter->second;
142 return angle::Result::Continue;
143 }
144
145 TrimCache(kMaxStates, kGCLimit, "blend state", &mBlendStateCache);
146
147 // Create a new blend state and insert it into the cache
148 D3D11_BLEND_DESC blendDesc = {}; // avoid undefined fields
149 const gl::BlendStateExt &blendStateExt = key.blendStateExt;
150
151 blendDesc.AlphaToCoverageEnable = key.sampleAlphaToCoverage != 0 ? TRUE : FALSE;
152 blendDesc.IndependentBlendEnable = key.rtvMax > 1 ? TRUE : FALSE;
153
154 // D3D11 API always accepts an array of blend states. Its validity depends on the hardware
155 // feature level. Given that we do not expose GL entrypoints that set per-buffer blend states on
156 // systems lower than FL10_1, this array will be always valid.
157
158 for (size_t i = 0; i < blendStateExt.getDrawBufferCount(); i++)
159 {
160 D3D11_RENDER_TARGET_BLEND_DESC &rtDesc = blendDesc.RenderTarget[i];
161
162 if (blendStateExt.getEnabledMask().test(i))
163 {
164 rtDesc.BlendEnable = true;
165 rtDesc.SrcBlend =
166 gl_d3d11::ConvertBlendFunc(blendStateExt.getSrcColorIndexed(i), false);
167 rtDesc.DestBlend =
168 gl_d3d11::ConvertBlendFunc(blendStateExt.getDstColorIndexed(i), false);
169 rtDesc.BlendOp = gl_d3d11::ConvertBlendOp(blendStateExt.getEquationColorIndexed(i));
170 rtDesc.SrcBlendAlpha =
171 gl_d3d11::ConvertBlendFunc(blendStateExt.getSrcAlphaIndexed(i), true);
172 rtDesc.DestBlendAlpha =
173 gl_d3d11::ConvertBlendFunc(blendStateExt.getDstAlphaIndexed(i), true);
174 rtDesc.BlendOpAlpha =
175 gl_d3d11::ConvertBlendOp(blendStateExt.getEquationAlphaIndexed(i));
176 }
177
178 // blendStateExt.colorMask follows the same packing scheme as
179 // D3D11_RENDER_TARGET_BLEND_DESC.RenderTargetWriteMask
180 rtDesc.RenderTargetWriteMask = blendStateExt.getColorMaskIndexed(i);
181 }
182
183 d3d11::BlendState d3dBlendState;
184 ANGLE_TRY(renderer->allocateResource(GetImplAs<Context11>(context), blendDesc, &d3dBlendState));
185 const auto &iter = mBlendStateCache.Put(key, std::move(d3dBlendState));
186
187 *outBlendState = &iter->second;
188
189 return angle::Result::Continue;
190 }
191
getRasterizerState(const gl::Context * context,Renderer11 * renderer,const gl::RasterizerState & rasterState,bool scissorEnabled,ID3D11RasterizerState ** outRasterizerState)192 angle::Result RenderStateCache::getRasterizerState(const gl::Context *context,
193 Renderer11 *renderer,
194 const gl::RasterizerState &rasterState,
195 bool scissorEnabled,
196 ID3D11RasterizerState **outRasterizerState)
197 {
198 d3d11::RasterizerStateKey key;
199 key.rasterizerState = rasterState;
200 key.scissorEnabled = scissorEnabled ? 1 : 0;
201
202 auto keyIter = mRasterizerStateCache.Get(key);
203 if (keyIter != mRasterizerStateCache.end())
204 {
205 *outRasterizerState = keyIter->second.get();
206 return angle::Result::Continue;
207 }
208
209 TrimCache(kMaxStates, kGCLimit, "rasterizer state", &mRasterizerStateCache);
210
211 D3D11_CULL_MODE cullMode =
212 gl_d3d11::ConvertCullMode(rasterState.cullFace, rasterState.cullMode);
213
214 // Disable culling if drawing points
215 if (rasterState.pointDrawMode)
216 {
217 cullMode = D3D11_CULL_NONE;
218 }
219
220 D3D11_RASTERIZER_DESC rasterDesc;
221 rasterDesc.FillMode =
222 rasterState.polygonMode == gl::PolygonMode::Fill ? D3D11_FILL_SOLID : D3D11_FILL_WIREFRAME;
223 rasterDesc.CullMode = cullMode;
224 rasterDesc.FrontCounterClockwise = (rasterState.frontFace == GL_CCW) ? FALSE : TRUE;
225 rasterDesc.DepthClipEnable = !rasterState.depthClamp;
226 rasterDesc.ScissorEnable = scissorEnabled ? TRUE : FALSE;
227 rasterDesc.MultisampleEnable = rasterState.multiSample;
228 rasterDesc.AntialiasedLineEnable = FALSE;
229
230 if (rasterState.isPolygonOffsetEnabled())
231 {
232 rasterDesc.DepthBias = (INT)rasterState.polygonOffsetUnits;
233 rasterDesc.DepthBiasClamp = rasterState.polygonOffsetClamp;
234 rasterDesc.SlopeScaledDepthBias = rasterState.polygonOffsetFactor;
235 }
236 else
237 {
238 rasterDesc.DepthBias = 0;
239 rasterDesc.DepthBiasClamp = 0.0f;
240 rasterDesc.SlopeScaledDepthBias = 0.0f;
241 }
242
243 d3d11::RasterizerState dx11RasterizerState;
244 ANGLE_TRY(renderer->allocateResource(GetImplAs<Context11>(context), rasterDesc,
245 &dx11RasterizerState));
246 *outRasterizerState = dx11RasterizerState.get();
247 mRasterizerStateCache.Put(key, std::move(dx11RasterizerState));
248
249 return angle::Result::Continue;
250 }
251
getDepthStencilState(const gl::Context * context,Renderer11 * renderer,const gl::DepthStencilState & glState,const d3d11::DepthStencilState ** outDSState)252 angle::Result RenderStateCache::getDepthStencilState(const gl::Context *context,
253 Renderer11 *renderer,
254 const gl::DepthStencilState &glState,
255 const d3d11::DepthStencilState **outDSState)
256 {
257 auto keyIter = mDepthStencilStateCache.Get(glState);
258 if (keyIter != mDepthStencilStateCache.end())
259 {
260 *outDSState = &keyIter->second;
261 return angle::Result::Continue;
262 }
263
264 TrimCache(kMaxStates, kGCLimit, "depth stencil state", &mDepthStencilStateCache);
265
266 D3D11_DEPTH_STENCIL_DESC dsDesc = {};
267 dsDesc.DepthEnable = glState.depthTest ? TRUE : FALSE;
268 dsDesc.DepthWriteMask = ConvertDepthMask(glState.depthMask);
269 dsDesc.DepthFunc = ConvertComparison(glState.depthFunc);
270 dsDesc.StencilEnable = glState.stencilTest ? TRUE : FALSE;
271 dsDesc.StencilReadMask = ConvertStencilMask(glState.stencilMask);
272 dsDesc.StencilWriteMask = ConvertStencilMask(glState.stencilWritemask);
273 dsDesc.FrontFace.StencilFailOp = ConvertStencilOp(glState.stencilFail);
274 dsDesc.FrontFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilPassDepthFail);
275 dsDesc.FrontFace.StencilPassOp = ConvertStencilOp(glState.stencilPassDepthPass);
276 dsDesc.FrontFace.StencilFunc = ConvertComparison(glState.stencilFunc);
277 dsDesc.BackFace.StencilFailOp = ConvertStencilOp(glState.stencilBackFail);
278 dsDesc.BackFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilBackPassDepthFail);
279 dsDesc.BackFace.StencilPassOp = ConvertStencilOp(glState.stencilBackPassDepthPass);
280 dsDesc.BackFace.StencilFunc = ConvertComparison(glState.stencilBackFunc);
281
282 d3d11::DepthStencilState dx11DepthStencilState;
283 ANGLE_TRY(
284 renderer->allocateResource(GetImplAs<Context11>(context), dsDesc, &dx11DepthStencilState));
285 const auto &iter = mDepthStencilStateCache.Put(glState, std::move(dx11DepthStencilState));
286
287 *outDSState = &iter->second;
288
289 return angle::Result::Continue;
290 }
291
getSamplerState(const gl::Context * context,Renderer11 * renderer,const gl::SamplerState & samplerState,ID3D11SamplerState ** outSamplerState)292 angle::Result RenderStateCache::getSamplerState(const gl::Context *context,
293 Renderer11 *renderer,
294 const gl::SamplerState &samplerState,
295 ID3D11SamplerState **outSamplerState)
296 {
297 auto keyIter = mSamplerStateCache.Get(samplerState);
298 if (keyIter != mSamplerStateCache.end())
299 {
300 *outSamplerState = keyIter->second.get();
301 return angle::Result::Continue;
302 }
303
304 TrimCache(kMaxStates, kGCLimit, "sampler state", &mSamplerStateCache);
305
306 const auto &featureLevel = renderer->getRenderer11DeviceCaps().featureLevel;
307
308 D3D11_SAMPLER_DESC samplerDesc;
309 samplerDesc.Filter =
310 gl_d3d11::ConvertFilter(samplerState.getMinFilter(), samplerState.getMagFilter(),
311 samplerState.getMaxAnisotropy(), samplerState.getCompareMode());
312 samplerDesc.AddressU = gl_d3d11::ConvertTextureWrap(samplerState.getWrapS());
313 samplerDesc.AddressV = gl_d3d11::ConvertTextureWrap(samplerState.getWrapT());
314 samplerDesc.AddressW = gl_d3d11::ConvertTextureWrap(samplerState.getWrapR());
315 samplerDesc.MipLODBias = 0;
316 samplerDesc.MaxAnisotropy =
317 gl_d3d11::ConvertMaxAnisotropy(samplerState.getMaxAnisotropy(), featureLevel);
318 samplerDesc.ComparisonFunc = gl_d3d11::ConvertComparison(samplerState.getCompareFunc());
319 samplerDesc.BorderColor[0] = samplerState.getBorderColor().colorF.red;
320 samplerDesc.BorderColor[1] = samplerState.getBorderColor().colorF.green;
321 samplerDesc.BorderColor[2] = samplerState.getBorderColor().colorF.blue;
322 samplerDesc.BorderColor[3] = samplerState.getBorderColor().colorF.alpha;
323 samplerDesc.MinLOD = samplerState.getMinLod();
324 samplerDesc.MaxLOD = samplerState.getMaxLod();
325
326 if (featureLevel <= D3D_FEATURE_LEVEL_9_3)
327 {
328 // Check that maxLOD is nearly FLT_MAX (1000.0f is the default), since 9_3 doesn't support
329 // anything other than FLT_MAX. Note that Feature Level 9_* only supports GL ES 2.0, so the
330 // consumer of ANGLE can't modify the Max LOD themselves.
331 ASSERT(samplerState.getMaxLod() >= 999.9f);
332
333 // Now just set MaxLOD to FLT_MAX. Other parts of the renderer (e.g. the non-zero max LOD
334 // workaround) should take account of this.
335 samplerDesc.MaxLOD = FLT_MAX;
336 }
337
338 d3d11::SamplerState dx11SamplerState;
339 ANGLE_TRY(
340 renderer->allocateResource(GetImplAs<Context11>(context), samplerDesc, &dx11SamplerState));
341 *outSamplerState = dx11SamplerState.get();
342 mSamplerStateCache.Put(samplerState, std::move(dx11SamplerState));
343
344 return angle::Result::Continue;
345 }
346
347 } // namespace rx
348