| // |
| // Copyright (c) 2012 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // RenderStateCache.cpp: Defines rx::RenderStateCache, a cache of Direct3D render |
| // state objects. |
| |
| #include "libGLESv2/renderer/RenderStateCache.h" |
| #include "libGLESv2/renderer/renderer11_utils.h" |
| |
| #include "common/debug.h" |
| #include "third_party/murmurhash/MurmurHash3.h" |
| |
| namespace rx |
| { |
| |
| RenderStateCache::RenderStateCache() : mDevice(NULL), mCounter(0), |
| mBlendStateCache(kMaxBlendStates, hashBlendState, compareBlendStates), |
| mRasterizerStateCache(kMaxRasterizerStates, hashRasterizerState, compareRasterizerStates) |
| { |
| } |
| |
| RenderStateCache::~RenderStateCache() |
| { |
| clear(); |
| } |
| |
| void RenderStateCache::initialize(ID3D11Device* device) |
| { |
| clear(); |
| mDevice = device; |
| } |
| |
| void RenderStateCache::clear() |
| { |
| for (BlendStateMap::iterator i = mBlendStateCache.begin(); i != mBlendStateCache.end(); i++) |
| { |
| i->second.first->Release(); |
| } |
| mBlendStateCache.clear(); |
| |
| for (RasterizerStateMap::iterator i = mRasterizerStateCache.begin(); i != mRasterizerStateCache.end(); i++) |
| { |
| i->second.first->Release(); |
| } |
| mRasterizerStateCache.clear(); |
| } |
| |
| std::size_t RenderStateCache::hashBlendState(const gl::BlendState &blendState) |
| { |
| static const unsigned int seed = 0xABCDEF98; |
| |
| std::size_t hash = 0; |
| MurmurHash3_x86_32(&blendState, sizeof(gl::BlendState), seed, &hash); |
| return hash; |
| } |
| |
| bool RenderStateCache::compareBlendStates(const gl::BlendState &a, const gl::BlendState &b) |
| { |
| return memcmp(&a, &b, sizeof(gl::BlendState)) == 0; |
| } |
| |
| // MSDN's documentation of ID3D11Device::CreateBlendState claims the maximum number of |
| // unique blend states an application can create is 4096 |
| const unsigned int RenderStateCache::kMaxBlendStates = 4096; |
| |
| ID3D11BlendState *RenderStateCache::getBlendState(const gl::BlendState &blendState) |
| { |
| if (!mDevice) |
| { |
| ERR("RenderStateCache is not initialized."); |
| return NULL; |
| } |
| |
| BlendStateMap::iterator i = mBlendStateCache.find(blendState); |
| if (i != mBlendStateCache.end()) |
| { |
| BlendStateCounterPair &state = i->second; |
| state.first->AddRef(); |
| state.second = mCounter++; |
| return state.first; |
| } |
| else |
| { |
| if (mBlendStateCache.size() >= kMaxBlendStates) |
| { |
| TRACE("Overflowed the limit of %u blend states, removing the least recently used " |
| "to make room.", kMaxBlendStates); |
| |
| BlendStateMap::iterator leastRecentlyUsed = mBlendStateCache.begin(); |
| for (BlendStateMap::iterator i = mBlendStateCache.begin(); i != mBlendStateCache.end(); i++) |
| { |
| if (i->second.second < leastRecentlyUsed->second.second) |
| { |
| leastRecentlyUsed = i; |
| } |
| } |
| leastRecentlyUsed->second.first->Release(); |
| mBlendStateCache.erase(leastRecentlyUsed); |
| } |
| |
| // Create a new blend state and insert it into the cache |
| D3D11_BLEND_DESC blendDesc = { 0 }; |
| blendDesc.AlphaToCoverageEnable = blendState.sampleAlphaToCoverage; |
| blendDesc.IndependentBlendEnable = FALSE; |
| |
| for (unsigned int i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++) |
| { |
| D3D11_RENDER_TARGET_BLEND_DESC &rtBlend = blendDesc.RenderTarget[i]; |
| |
| rtBlend.BlendEnable = blendState.blend; |
| if (blendState.blend) |
| { |
| rtBlend.SrcBlend = gl_d3d11::ConvertBlendFunc(blendState.sourceBlendRGB); |
| rtBlend.DestBlend = gl_d3d11::ConvertBlendFunc(blendState.destBlendRGB); |
| rtBlend.BlendOp = gl_d3d11::ConvertBlendOp(blendState.blendEquationRGB); |
| |
| rtBlend.SrcBlendAlpha = gl_d3d11::ConvertBlendFunc(blendState.sourceBlendAlpha); |
| rtBlend.DestBlendAlpha = gl_d3d11::ConvertBlendFunc(blendState.destBlendAlpha); |
| rtBlend.BlendOpAlpha = gl_d3d11::ConvertBlendOp(blendState.blendEquationAlpha); |
| |
| rtBlend.RenderTargetWriteMask = gl_d3d11::ConvertColorMask(blendState.colorMaskRed, |
| blendState.colorMaskGreen, |
| blendState.colorMaskBlue, |
| blendState.colorMaskAlpha); |
| } |
| } |
| |
| ID3D11BlendState* dx11BlendState = NULL; |
| HRESULT result = mDevice->CreateBlendState(&blendDesc, &dx11BlendState); |
| if (FAILED(result) || !dx11BlendState) |
| { |
| ERR("Unable to create a ID3D11BlendState, HRESULT: 0x%X.", result); |
| return NULL; |
| } |
| |
| mBlendStateCache.insert(std::make_pair(blendState, std::make_pair(dx11BlendState, mCounter++))); |
| |
| dx11BlendState->AddRef(); |
| return dx11BlendState; |
| } |
| } |
| |
| std::size_t RenderStateCache::hashRasterizerState(const RasterizerStateKey &rasterState) |
| { |
| static const unsigned int seed = 0xABCDEF98; |
| |
| std::size_t hash = 0; |
| MurmurHash3_x86_32(&rasterState, sizeof(RasterizerStateKey), seed, &hash); |
| return hash; |
| } |
| |
| bool RenderStateCache::compareRasterizerStates(const RasterizerStateKey &a, const RasterizerStateKey &b) |
| { |
| return memcmp(&a, &b, sizeof(RasterizerStateKey)) == 0; |
| } |
| |
| // MSDN's documentation of ID3D11Device::CreateRasterizerState claims the maximum number of |
| // unique rasterizer states an application can create is 4096 |
| const unsigned int RenderStateCache::kMaxRasterizerStates = 4096; |
| |
| ID3D11RasterizerState *RenderStateCache::getRasterizerState(const gl::RasterizerState &rasterState, |
| unsigned int depthSize) |
| { |
| if (!mDevice) |
| { |
| ERR("RenderStateCache is not initialized."); |
| return NULL; |
| } |
| |
| RasterizerStateKey key; |
| key.rasterizerState = rasterState; |
| key.depthSize = depthSize; |
| |
| RasterizerStateMap::iterator i = mRasterizerStateCache.find(key); |
| if (i != mRasterizerStateCache.end()) |
| { |
| RasterizerStateCounterPair &state = i->second; |
| state.first->AddRef(); |
| state.second = mCounter++; |
| return state.first; |
| } |
| else |
| { |
| if (mRasterizerStateCache.size() >= kMaxRasterizerStates) |
| { |
| TRACE("Overflowed the limit of %u rasterizer states, removing the least recently used " |
| "to make room.", kMaxRasterizerStates); |
| |
| RasterizerStateMap::iterator leastRecentlyUsed = mRasterizerStateCache.begin(); |
| for (RasterizerStateMap::iterator i = mRasterizerStateCache.begin(); i != mRasterizerStateCache.end(); i++) |
| { |
| if (i->second.second < leastRecentlyUsed->second.second) |
| { |
| leastRecentlyUsed = i; |
| } |
| } |
| leastRecentlyUsed->second.first->Release(); |
| mRasterizerStateCache.erase(leastRecentlyUsed); |
| } |
| |
| D3D11_RASTERIZER_DESC rasterDesc; |
| rasterDesc.FillMode = D3D11_FILL_SOLID; |
| rasterDesc.CullMode = gl_d3d11::ConvertCullMode(rasterState.cullFace, rasterState.cullMode); |
| rasterDesc.FrontCounterClockwise = (rasterState.frontFace == GL_CCW) ? TRUE : FALSE; |
| rasterDesc.DepthBias = ldexp(rasterState.polygonOffsetUnits, -static_cast<int>(depthSize)); |
| rasterDesc.DepthBiasClamp = 0.0f; // MSDN documentation of DepthBiasClamp implies a value of zero will preform no clamping, must be tested though. |
| rasterDesc.SlopeScaledDepthBias = rasterState.polygonOffsetUnits; |
| rasterDesc.DepthClipEnable = TRUE; |
| rasterDesc.ScissorEnable = rasterState.scissorTest ? TRUE : FALSE; |
| rasterDesc.MultisampleEnable = TRUE; |
| rasterDesc.AntialiasedLineEnable = FALSE; |
| |
| ID3D11RasterizerState* dx11RasterizerState = NULL; |
| HRESULT result = mDevice->CreateRasterizerState(&rasterDesc, &dx11RasterizerState); |
| if (FAILED(result) || !dx11RasterizerState) |
| { |
| ERR("Unable to create a ID3D11RasterizerState, HRESULT: 0x%X.", result); |
| return NULL; |
| } |
| |
| mRasterizerStateCache.insert(std::make_pair(key, std::make_pair(dx11RasterizerState, mCounter++))); |
| |
| dx11RasterizerState->AddRef(); |
| return dx11RasterizerState; |
| } |
| } |
| |
| } |