RenderStateCache.cpp (14197B)
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 28 RenderStateCache::RenderStateCache() 29 : mBlendStateCache(kMaxStates), 30 mRasterizerStateCache(kMaxStates), 31 mDepthStencilStateCache(kMaxStates), 32 mSamplerStateCache(kMaxStates) 33 {} 34 35 RenderStateCache::~RenderStateCache() {} 36 37 void RenderStateCache::clear() 38 { 39 mBlendStateCache.Clear(); 40 mRasterizerStateCache.Clear(); 41 mDepthStencilStateCache.Clear(); 42 mSamplerStateCache.Clear(); 43 } 44 45 // static 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 key.blendStateExt.setFactorsIndexed(keyBlendIndex, sourceIndex, blendStateExt); 97 } 98 keyBlendIndex++; 99 } 100 101 key.sampleAlphaToCoverage = sampleAlphaToCoverage ? 1 : 0; 102 return key; 103 } 104 105 angle::Result RenderStateCache::getBlendState(const gl::Context *context, 106 Renderer11 *renderer, 107 const d3d11::BlendStateKey &key, 108 const d3d11::BlendState **outBlendState) 109 { 110 auto keyIter = mBlendStateCache.Get(key); 111 if (keyIter != mBlendStateCache.end()) 112 { 113 *outBlendState = &keyIter->second; 114 return angle::Result::Continue; 115 } 116 117 TrimCache(kMaxStates, kGCLimit, "blend state", &mBlendStateCache); 118 119 // Create a new blend state and insert it into the cache 120 D3D11_BLEND_DESC blendDesc = {}; // avoid undefined fields 121 const gl::BlendStateExt &blendStateExt = key.blendStateExt; 122 123 blendDesc.AlphaToCoverageEnable = key.sampleAlphaToCoverage != 0 ? TRUE : FALSE; 124 blendDesc.IndependentBlendEnable = key.rtvMax > 1 ? TRUE : FALSE; 125 126 // D3D11 API always accepts an array of blend states. Its validity depends on the hardware 127 // feature level. Given that we do not expose GL entrypoints that set per-buffer blend states on 128 // systems lower than FL10_1, this array will be always valid. 129 130 for (size_t i = 0; i < blendStateExt.getDrawBufferCount(); i++) 131 { 132 D3D11_RENDER_TARGET_BLEND_DESC &rtDesc = blendDesc.RenderTarget[i]; 133 134 if (blendStateExt.getEnabledMask().test(i)) 135 { 136 rtDesc.BlendEnable = true; 137 rtDesc.SrcBlend = 138 gl_d3d11::ConvertBlendFunc(blendStateExt.getSrcColorIndexed(i), false); 139 rtDesc.DestBlend = 140 gl_d3d11::ConvertBlendFunc(blendStateExt.getDstColorIndexed(i), false); 141 rtDesc.BlendOp = gl_d3d11::ConvertBlendOp(blendStateExt.getEquationColorIndexed(i)); 142 rtDesc.SrcBlendAlpha = 143 gl_d3d11::ConvertBlendFunc(blendStateExt.getSrcAlphaIndexed(i), true); 144 rtDesc.DestBlendAlpha = 145 gl_d3d11::ConvertBlendFunc(blendStateExt.getDstAlphaIndexed(i), true); 146 rtDesc.BlendOpAlpha = 147 gl_d3d11::ConvertBlendOp(blendStateExt.getEquationAlphaIndexed(i)); 148 } 149 150 // blendStateExt.colorMask follows the same packing scheme as 151 // D3D11_RENDER_TARGET_BLEND_DESC.RenderTargetWriteMask 152 rtDesc.RenderTargetWriteMask = blendStateExt.getColorMaskIndexed(i); 153 } 154 155 d3d11::BlendState d3dBlendState; 156 ANGLE_TRY(renderer->allocateResource(GetImplAs<Context11>(context), blendDesc, &d3dBlendState)); 157 const auto &iter = mBlendStateCache.Put(key, std::move(d3dBlendState)); 158 159 *outBlendState = &iter->second; 160 161 return angle::Result::Continue; 162 } 163 164 angle::Result RenderStateCache::getRasterizerState(const gl::Context *context, 165 Renderer11 *renderer, 166 const gl::RasterizerState &rasterState, 167 bool scissorEnabled, 168 ID3D11RasterizerState **outRasterizerState) 169 { 170 d3d11::RasterizerStateKey key; 171 key.rasterizerState = rasterState; 172 key.scissorEnabled = scissorEnabled ? 1 : 0; 173 174 auto keyIter = mRasterizerStateCache.Get(key); 175 if (keyIter != mRasterizerStateCache.end()) 176 { 177 *outRasterizerState = keyIter->second.get(); 178 return angle::Result::Continue; 179 } 180 181 TrimCache(kMaxStates, kGCLimit, "rasterizer state", &mRasterizerStateCache); 182 183 D3D11_CULL_MODE cullMode = 184 gl_d3d11::ConvertCullMode(rasterState.cullFace, rasterState.cullMode); 185 186 // Disable culling if drawing points 187 if (rasterState.pointDrawMode) 188 { 189 cullMode = D3D11_CULL_NONE; 190 } 191 192 D3D11_RASTERIZER_DESC rasterDesc; 193 rasterDesc.FillMode = D3D11_FILL_SOLID; 194 rasterDesc.CullMode = cullMode; 195 rasterDesc.FrontCounterClockwise = (rasterState.frontFace == GL_CCW) ? FALSE : TRUE; 196 rasterDesc.DepthBiasClamp = 0.0f; // MSDN documentation of DepthBiasClamp implies a value of 197 // zero will preform no clamping, must be tested though. 198 rasterDesc.DepthClipEnable = TRUE; 199 rasterDesc.ScissorEnable = scissorEnabled ? TRUE : FALSE; 200 rasterDesc.MultisampleEnable = rasterState.multiSample; 201 rasterDesc.AntialiasedLineEnable = FALSE; 202 203 if (rasterState.polygonOffsetFill) 204 { 205 rasterDesc.SlopeScaledDepthBias = rasterState.polygonOffsetFactor; 206 rasterDesc.DepthBias = (INT)rasterState.polygonOffsetUnits; 207 } 208 else 209 { 210 rasterDesc.SlopeScaledDepthBias = 0.0f; 211 rasterDesc.DepthBias = 0; 212 } 213 214 d3d11::RasterizerState dx11RasterizerState; 215 ANGLE_TRY(renderer->allocateResource(GetImplAs<Context11>(context), rasterDesc, 216 &dx11RasterizerState)); 217 *outRasterizerState = dx11RasterizerState.get(); 218 mRasterizerStateCache.Put(key, std::move(dx11RasterizerState)); 219 220 return angle::Result::Continue; 221 } 222 223 angle::Result RenderStateCache::getDepthStencilState(const gl::Context *context, 224 Renderer11 *renderer, 225 const gl::DepthStencilState &glState, 226 const d3d11::DepthStencilState **outDSState) 227 { 228 auto keyIter = mDepthStencilStateCache.Get(glState); 229 if (keyIter != mDepthStencilStateCache.end()) 230 { 231 *outDSState = &keyIter->second; 232 return angle::Result::Continue; 233 } 234 235 TrimCache(kMaxStates, kGCLimit, "depth stencil state", &mDepthStencilStateCache); 236 237 D3D11_DEPTH_STENCIL_DESC dsDesc = {}; 238 dsDesc.DepthEnable = glState.depthTest ? TRUE : FALSE; 239 dsDesc.DepthWriteMask = ConvertDepthMask(glState.depthMask); 240 dsDesc.DepthFunc = ConvertComparison(glState.depthFunc); 241 dsDesc.StencilEnable = glState.stencilTest ? TRUE : FALSE; 242 dsDesc.StencilReadMask = ConvertStencilMask(glState.stencilMask); 243 dsDesc.StencilWriteMask = ConvertStencilMask(glState.stencilWritemask); 244 dsDesc.FrontFace.StencilFailOp = ConvertStencilOp(glState.stencilFail); 245 dsDesc.FrontFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilPassDepthFail); 246 dsDesc.FrontFace.StencilPassOp = ConvertStencilOp(glState.stencilPassDepthPass); 247 dsDesc.FrontFace.StencilFunc = ConvertComparison(glState.stencilFunc); 248 dsDesc.BackFace.StencilFailOp = ConvertStencilOp(glState.stencilBackFail); 249 dsDesc.BackFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilBackPassDepthFail); 250 dsDesc.BackFace.StencilPassOp = ConvertStencilOp(glState.stencilBackPassDepthPass); 251 dsDesc.BackFace.StencilFunc = ConvertComparison(glState.stencilBackFunc); 252 253 d3d11::DepthStencilState dx11DepthStencilState; 254 ANGLE_TRY( 255 renderer->allocateResource(GetImplAs<Context11>(context), dsDesc, &dx11DepthStencilState)); 256 const auto &iter = mDepthStencilStateCache.Put(glState, std::move(dx11DepthStencilState)); 257 258 *outDSState = &iter->second; 259 260 return angle::Result::Continue; 261 } 262 263 angle::Result RenderStateCache::getSamplerState(const gl::Context *context, 264 Renderer11 *renderer, 265 const gl::SamplerState &samplerState, 266 ID3D11SamplerState **outSamplerState) 267 { 268 auto keyIter = mSamplerStateCache.Get(samplerState); 269 if (keyIter != mSamplerStateCache.end()) 270 { 271 *outSamplerState = keyIter->second.get(); 272 return angle::Result::Continue; 273 } 274 275 TrimCache(kMaxStates, kGCLimit, "sampler state", &mSamplerStateCache); 276 277 const auto &featureLevel = renderer->getRenderer11DeviceCaps().featureLevel; 278 279 D3D11_SAMPLER_DESC samplerDesc; 280 samplerDesc.Filter = 281 gl_d3d11::ConvertFilter(samplerState.getMinFilter(), samplerState.getMagFilter(), 282 samplerState.getMaxAnisotropy(), samplerState.getCompareMode()); 283 samplerDesc.AddressU = gl_d3d11::ConvertTextureWrap(samplerState.getWrapS()); 284 samplerDesc.AddressV = gl_d3d11::ConvertTextureWrap(samplerState.getWrapT()); 285 samplerDesc.AddressW = gl_d3d11::ConvertTextureWrap(samplerState.getWrapR()); 286 samplerDesc.MipLODBias = 0; 287 samplerDesc.MaxAnisotropy = 288 gl_d3d11::ConvertMaxAnisotropy(samplerState.getMaxAnisotropy(), featureLevel); 289 samplerDesc.ComparisonFunc = gl_d3d11::ConvertComparison(samplerState.getCompareFunc()); 290 angle::ColorF borderColor; 291 if (samplerState.getBorderColor().type == angle::ColorGeneric::Type::Float) 292 { 293 borderColor = samplerState.getBorderColor().colorF; 294 } 295 samplerDesc.BorderColor[0] = borderColor.red; 296 samplerDesc.BorderColor[1] = borderColor.green; 297 samplerDesc.BorderColor[2] = borderColor.blue; 298 samplerDesc.BorderColor[3] = borderColor.alpha; 299 samplerDesc.MinLOD = samplerState.getMinLod(); 300 samplerDesc.MaxLOD = samplerState.getMaxLod(); 301 302 if (featureLevel <= D3D_FEATURE_LEVEL_9_3) 303 { 304 // Check that maxLOD is nearly FLT_MAX (1000.0f is the default), since 9_3 doesn't support 305 // anything other than FLT_MAX. Note that Feature Level 9_* only supports GL ES 2.0, so the 306 // consumer of ANGLE can't modify the Max LOD themselves. 307 ASSERT(samplerState.getMaxLod() >= 999.9f); 308 309 // Now just set MaxLOD to FLT_MAX. Other parts of the renderer (e.g. the non-zero max LOD 310 // workaround) should take account of this. 311 samplerDesc.MaxLOD = FLT_MAX; 312 } 313 314 d3d11::SamplerState dx11SamplerState; 315 ANGLE_TRY( 316 renderer->allocateResource(GetImplAs<Context11>(context), samplerDesc, &dx11SamplerState)); 317 *outSamplerState = dx11SamplerState.get(); 318 mSamplerStateCache.Put(samplerState, std::move(dx11SamplerState)); 319 320 return angle::Result::Continue; 321 } 322 323 } // namespace rx