blob: feda997191bedd368775331fff1074e01b3228f2 [file] [log] [blame]
//
// Copyright 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 "libANGLE/renderer/d3d/d3d11/RenderStateCache.h"
#include <float.h>
#include "common/Color.h"
#include "common/debug.h"
#include "libANGLE/Context.h"
#include "libANGLE/Framebuffer.h"
#include "libANGLE/FramebufferAttachment.h"
#include "libANGLE/renderer/d3d/d3d11/Context11.h"
#include "libANGLE/renderer/d3d/d3d11/Framebuffer11.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
#include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h"
namespace rx
{
using namespace gl_d3d11;
RenderStateCache::RenderStateCache()
: mBlendStateCache(kMaxStates),
mRasterizerStateCache(kMaxStates),
mDepthStencilStateCache(kMaxStates),
mSamplerStateCache(kMaxStates)
{}
RenderStateCache::~RenderStateCache() {}
void RenderStateCache::clear()
{
mBlendStateCache.Clear();
mRasterizerStateCache.Clear();
mDepthStencilStateCache.Clear();
mSamplerStateCache.Clear();
}
// static
d3d11::BlendStateKey RenderStateCache::GetBlendStateKey(const gl::Context *context,
Framebuffer11 *framebuffer11,
const gl::BlendStateArray &blendStateArray,
bool sampleAlphaToCoverage)
{
d3d11::BlendStateKey key;
// All elements of the BlendStateArray inside the key should be initialized for the caching to
// work correctly. Due to mrt_perf_workaround, the actual indices of active draw buffers may be
// different, so both arrays should be tracked.
const gl::AttachmentList &colorbuffers = framebuffer11->getColorAttachmentsForRender(context);
const gl::DrawBufferMask colorAttachmentsForRenderMask =
framebuffer11->getLastColorAttachmentsForRenderMask();
ASSERT(blendStateArray.size() == colorAttachmentsForRenderMask.size());
ASSERT(colorbuffers.size() == colorAttachmentsForRenderMask.count());
size_t keyBlendIndex = 0;
for (size_t sourceIndex : colorAttachmentsForRenderMask)
{
const gl::BlendState &blendState = blendStateArray[sourceIndex];
gl::BlendState &keyBlendState = key.blendStateArray[keyBlendIndex];
// With blending disabled, factors and equations are ignored when building
// D3D11_RENDER_TARGET_BLEND_DESC, so we can reduce the amount of unique keys by
// enforcing default values.
keyBlendState = gl::BlendState();
ASSERT(keyBlendIndex < colorbuffers.size());
const gl::FramebufferAttachment *attachment = colorbuffers[keyBlendIndex];
// Do not set blend state for null attachments that may be present when
// mrt_perf_workaround is disabled.
if (attachment == nullptr)
{
keyBlendIndex++;
continue;
}
// These values are used only for caching (hash calculation) purposes.
// Actual write color mask value is derived below.
keyBlendState.colorMaskRed = blendState.colorMaskRed;
keyBlendState.colorMaskGreen = blendState.colorMaskGreen;
keyBlendState.colorMaskBlue = blendState.colorMaskBlue;
keyBlendState.colorMaskAlpha = blendState.colorMaskAlpha;
const gl::InternalFormat &internalFormat = *attachment->getFormat().info;
key.rtvMax = static_cast<uint16_t>(keyBlendIndex) + 1;
key.rtvMasks[keyBlendIndex] =
gl_d3d11::GetColorMask(internalFormat) &
gl_d3d11::ConvertColorMask(blendState.colorMaskRed, blendState.colorMaskGreen,
blendState.colorMaskBlue, blendState.colorMaskAlpha);
// Some D3D11 drivers produce unexpected results when blending is enabled for integer
// attachments. Per OpenGL ES spec, it must be ignored anyway. When blending is disabled,
// the state remains default to reduce the number of unique keys.
if (blendState.blend && !internalFormat.isInt())
{
keyBlendState.blend = true;
keyBlendState.sourceBlendRGB = blendState.sourceBlendRGB;
keyBlendState.sourceBlendAlpha = blendState.sourceBlendAlpha;
keyBlendState.destBlendRGB = blendState.destBlendRGB;
keyBlendState.destBlendAlpha = blendState.destBlendAlpha;
keyBlendState.blendEquationRGB = blendState.blendEquationRGB;
keyBlendState.blendEquationAlpha = blendState.blendEquationAlpha;
}
keyBlendIndex++;
}
// Initialize remaining slots in key.blendStateArray
if (keyBlendIndex < key.blendStateArray.size())
{
std::fill(key.blendStateArray.begin() + keyBlendIndex, key.blendStateArray.end(),
gl::BlendState());
}
key.sampleAlphaToCoverage = sampleAlphaToCoverage ? 1 : 0;
return key;
}
angle::Result RenderStateCache::getBlendState(const gl::Context *context,
Renderer11 *renderer,
const d3d11::BlendStateKey &key,
const d3d11::BlendState **outBlendState)
{
auto keyIter = mBlendStateCache.Get(key);
if (keyIter != mBlendStateCache.end())
{
*outBlendState = &keyIter->second;
return angle::Result::Continue;
}
TrimCache(kMaxStates, kGCLimit, "blend state", &mBlendStateCache);
// Create a new blend state and insert it into the cache
D3D11_BLEND_DESC blendDesc;
const gl::BlendStateArray &blendStateArray = key.blendStateArray;
blendDesc.AlphaToCoverageEnable = key.sampleAlphaToCoverage != 0 ? TRUE : FALSE;
blendDesc.IndependentBlendEnable = key.rtvMax > 1 ? TRUE : FALSE;
// D3D11 API always accepts an array of blend states. Its validity depends on the hardware
// feature level. Given that we do not expose GL entrypoints that set per-buffer blend states on
// systems lower than FL10_1, this array will be always valid.
ASSERT(blendStateArray.size() >= D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT);
for (size_t i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; i++)
{
D3D11_RENDER_TARGET_BLEND_DESC &rtDesc = blendDesc.RenderTarget[i];
rtDesc = {}; // otherwise its content may be undefined
const gl::BlendState &blendState = blendStateArray[i];
if (blendState.blend)
{
rtDesc.BlendEnable = true;
rtDesc.SrcBlend = gl_d3d11::ConvertBlendFunc(blendState.sourceBlendRGB, false);
rtDesc.DestBlend = gl_d3d11::ConvertBlendFunc(blendState.destBlendRGB, false);
rtDesc.BlendOp = gl_d3d11::ConvertBlendOp(blendState.blendEquationRGB);
rtDesc.SrcBlendAlpha = gl_d3d11::ConvertBlendFunc(blendState.sourceBlendAlpha, true);
rtDesc.DestBlendAlpha = gl_d3d11::ConvertBlendFunc(blendState.destBlendAlpha, true);
rtDesc.BlendOpAlpha = gl_d3d11::ConvertBlendOp(blendState.blendEquationAlpha);
}
rtDesc.RenderTargetWriteMask = key.rtvMasks[i];
}
d3d11::BlendState d3dBlendState;
ANGLE_TRY(renderer->allocateResource(GetImplAs<Context11>(context), blendDesc, &d3dBlendState));
const auto &iter = mBlendStateCache.Put(key, std::move(d3dBlendState));
*outBlendState = &iter->second;
return angle::Result::Continue;
}
angle::Result RenderStateCache::getRasterizerState(const gl::Context *context,
Renderer11 *renderer,
const gl::RasterizerState &rasterState,
bool scissorEnabled,
ID3D11RasterizerState **outRasterizerState)
{
d3d11::RasterizerStateKey key;
key.rasterizerState = rasterState;
key.scissorEnabled = scissorEnabled ? 1 : 0;
auto keyIter = mRasterizerStateCache.Get(key);
if (keyIter != mRasterizerStateCache.end())
{
*outRasterizerState = keyIter->second.get();
return angle::Result::Continue;
}
TrimCache(kMaxStates, kGCLimit, "rasterizer state", &mRasterizerStateCache);
D3D11_CULL_MODE cullMode =
gl_d3d11::ConvertCullMode(rasterState.cullFace, rasterState.cullMode);
// Disable culling if drawing points
if (rasterState.pointDrawMode)
{
cullMode = D3D11_CULL_NONE;
}
D3D11_RASTERIZER_DESC rasterDesc;
rasterDesc.FillMode = D3D11_FILL_SOLID;
rasterDesc.CullMode = cullMode;
rasterDesc.FrontCounterClockwise = (rasterState.frontFace == GL_CCW) ? FALSE : TRUE;
rasterDesc.DepthBiasClamp = 0.0f; // MSDN documentation of DepthBiasClamp implies a value of
// zero will preform no clamping, must be tested though.
rasterDesc.DepthClipEnable = TRUE;
rasterDesc.ScissorEnable = scissorEnabled ? TRUE : FALSE;
rasterDesc.MultisampleEnable = rasterState.multiSample;
rasterDesc.AntialiasedLineEnable = FALSE;
if (rasterState.polygonOffsetFill)
{
rasterDesc.SlopeScaledDepthBias = rasterState.polygonOffsetFactor;
rasterDesc.DepthBias = (INT)rasterState.polygonOffsetUnits;
}
else
{
rasterDesc.SlopeScaledDepthBias = 0.0f;
rasterDesc.DepthBias = 0;
}
d3d11::RasterizerState dx11RasterizerState;
ANGLE_TRY(renderer->allocateResource(GetImplAs<Context11>(context), rasterDesc,
&dx11RasterizerState));
*outRasterizerState = dx11RasterizerState.get();
mRasterizerStateCache.Put(key, std::move(dx11RasterizerState));
return angle::Result::Continue;
}
angle::Result RenderStateCache::getDepthStencilState(const gl::Context *context,
Renderer11 *renderer,
const gl::DepthStencilState &glState,
const d3d11::DepthStencilState **outDSState)
{
auto keyIter = mDepthStencilStateCache.Get(glState);
if (keyIter != mDepthStencilStateCache.end())
{
*outDSState = &keyIter->second;
return angle::Result::Continue;
}
TrimCache(kMaxStates, kGCLimit, "depth stencil state", &mDepthStencilStateCache);
D3D11_DEPTH_STENCIL_DESC dsDesc = {};
dsDesc.DepthEnable = glState.depthTest ? TRUE : FALSE;
dsDesc.DepthWriteMask = ConvertDepthMask(glState.depthMask);
dsDesc.DepthFunc = ConvertComparison(glState.depthFunc);
dsDesc.StencilEnable = glState.stencilTest ? TRUE : FALSE;
dsDesc.StencilReadMask = ConvertStencilMask(glState.stencilMask);
dsDesc.StencilWriteMask = ConvertStencilMask(glState.stencilWritemask);
dsDesc.FrontFace.StencilFailOp = ConvertStencilOp(glState.stencilFail);
dsDesc.FrontFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilPassDepthFail);
dsDesc.FrontFace.StencilPassOp = ConvertStencilOp(glState.stencilPassDepthPass);
dsDesc.FrontFace.StencilFunc = ConvertComparison(glState.stencilFunc);
dsDesc.BackFace.StencilFailOp = ConvertStencilOp(glState.stencilBackFail);
dsDesc.BackFace.StencilDepthFailOp = ConvertStencilOp(glState.stencilBackPassDepthFail);
dsDesc.BackFace.StencilPassOp = ConvertStencilOp(glState.stencilBackPassDepthPass);
dsDesc.BackFace.StencilFunc = ConvertComparison(glState.stencilBackFunc);
d3d11::DepthStencilState dx11DepthStencilState;
ANGLE_TRY(
renderer->allocateResource(GetImplAs<Context11>(context), dsDesc, &dx11DepthStencilState));
const auto &iter = mDepthStencilStateCache.Put(glState, std::move(dx11DepthStencilState));
*outDSState = &iter->second;
return angle::Result::Continue;
}
angle::Result RenderStateCache::getSamplerState(const gl::Context *context,
Renderer11 *renderer,
const gl::SamplerState &samplerState,
ID3D11SamplerState **outSamplerState)
{
auto keyIter = mSamplerStateCache.Get(samplerState);
if (keyIter != mSamplerStateCache.end())
{
*outSamplerState = keyIter->second.get();
return angle::Result::Continue;
}
TrimCache(kMaxStates, kGCLimit, "sampler state", &mSamplerStateCache);
const auto &featureLevel = renderer->getRenderer11DeviceCaps().featureLevel;
D3D11_SAMPLER_DESC samplerDesc;
samplerDesc.Filter =
gl_d3d11::ConvertFilter(samplerState.getMinFilter(), samplerState.getMagFilter(),
samplerState.getMaxAnisotropy(), samplerState.getCompareMode());
samplerDesc.AddressU = gl_d3d11::ConvertTextureWrap(samplerState.getWrapS());
samplerDesc.AddressV = gl_d3d11::ConvertTextureWrap(samplerState.getWrapT());
samplerDesc.AddressW = gl_d3d11::ConvertTextureWrap(samplerState.getWrapR());
samplerDesc.MipLODBias = 0;
samplerDesc.MaxAnisotropy =
gl_d3d11::ConvertMaxAnisotropy(samplerState.getMaxAnisotropy(), featureLevel);
samplerDesc.ComparisonFunc = gl_d3d11::ConvertComparison(samplerState.getCompareFunc());
angle::ColorF borderColor;
if (samplerState.getBorderColor().type == angle::ColorGeneric::Type::Float)
{
borderColor = samplerState.getBorderColor().colorF;
}
samplerDesc.BorderColor[0] = borderColor.red;
samplerDesc.BorderColor[1] = borderColor.green;
samplerDesc.BorderColor[2] = borderColor.blue;
samplerDesc.BorderColor[3] = borderColor.alpha;
samplerDesc.MinLOD = samplerState.getMinLod();
samplerDesc.MaxLOD = samplerState.getMaxLod();
if (featureLevel <= D3D_FEATURE_LEVEL_9_3)
{
// Check that maxLOD is nearly FLT_MAX (1000.0f is the default), since 9_3 doesn't support
// anything other than FLT_MAX. Note that Feature Level 9_* only supports GL ES 2.0, so the
// consumer of ANGLE can't modify the Max LOD themselves.
ASSERT(samplerState.getMaxLod() >= 999.9f);
// Now just set MaxLOD to FLT_MAX. Other parts of the renderer (e.g. the non-zero max LOD
// workaround) should take account of this.
samplerDesc.MaxLOD = FLT_MAX;
}
d3d11::SamplerState dx11SamplerState;
ANGLE_TRY(
renderer->allocateResource(GetImplAs<Context11>(context), samplerDesc, &dx11SamplerState));
*outSamplerState = dx11SamplerState.get();
mSamplerStateCache.Put(samplerState, std::move(dx11SamplerState));
return angle::Result::Continue;
}
} // namespace rx