xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/d3d/d3d11/RenderStateCache.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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