123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- // Copyright 2014 Dolphin Emulator Project
- // SPDX-License-Identifier: GPL-2.0-or-later
- #include "VideoBackends/D3D/D3DState.h"
- #include <algorithm>
- #include <array>
- #include <bit>
- #include "Common/Assert.h"
- #include "Common/CommonTypes.h"
- #include "Common/Logging/Log.h"
- #include "Common/MsgHandler.h"
- #include "VideoBackends/D3D/D3DBase.h"
- #include "VideoBackends/D3D/DXTexture.h"
- #include "VideoBackends/D3DCommon/D3DCommon.h"
- #include "VideoCommon/VideoConfig.h"
- namespace DX11
- {
- namespace D3D
- {
- std::unique_ptr<StateManager> stateman;
- StateManager::StateManager() = default;
- StateManager::~StateManager() = default;
- void StateManager::Apply()
- {
- if (m_dirtyFlags.none())
- return;
- // Framebuffer changes must occur before texture changes, otherwise the D3D runtime messes with
- // our bindings and sets them to null to prevent hazards.
- if (m_dirtyFlags.test(DirtyFlag_Framebuffer))
- {
- if (g_ActiveConfig.backend_info.bSupportsBBox)
- {
- D3D::context->OMSetRenderTargetsAndUnorderedAccessViews(
- m_pending.framebuffer->GetNumRTVs(),
- m_pending.use_integer_rtv ? m_pending.framebuffer->GetIntegerRTVArray() :
- m_pending.framebuffer->GetRTVArray(),
- m_pending.framebuffer->GetDSV(), m_pending.framebuffer->GetNumRTVs() + 1, 1,
- &m_pending.uav, nullptr);
- }
- else
- {
- D3D::context->OMSetRenderTargets(m_pending.framebuffer->GetNumRTVs(),
- m_pending.use_integer_rtv ?
- m_pending.framebuffer->GetIntegerRTVArray() :
- m_pending.framebuffer->GetRTVArray(),
- m_pending.framebuffer->GetDSV());
- }
- m_current.framebuffer = m_pending.framebuffer;
- m_current.uav = m_pending.uav;
- m_current.use_integer_rtv = m_pending.use_integer_rtv;
- }
- const bool dirtyConstants = m_dirtyFlags.test(DirtyFlag_PixelConstants) ||
- m_dirtyFlags.test(DirtyFlag_VertexConstants) ||
- m_dirtyFlags.test(DirtyFlag_GeometryConstants);
- const bool dirtyShaders = m_dirtyFlags.test(DirtyFlag_PixelShader) ||
- m_dirtyFlags.test(DirtyFlag_VertexShader) ||
- m_dirtyFlags.test(DirtyFlag_GeometryShader);
- const bool dirtyBuffers =
- m_dirtyFlags.test(DirtyFlag_VertexBuffer) || m_dirtyFlags.test(DirtyFlag_IndexBuffer);
- if (dirtyConstants)
- {
- if (m_current.pixelConstants[0] != m_pending.pixelConstants[0] ||
- m_current.pixelConstants[1] != m_pending.pixelConstants[1] ||
- m_current.pixelConstants[2] != m_pending.pixelConstants[2])
- {
- u32 count = 1;
- if (m_pending.pixelConstants[1])
- count++;
- if (m_pending.pixelConstants[2])
- count++;
- D3D::context->PSSetConstantBuffers(0, count, m_pending.pixelConstants.data());
- m_current.pixelConstants[0] = m_pending.pixelConstants[0];
- m_current.pixelConstants[1] = m_pending.pixelConstants[1];
- m_current.pixelConstants[2] = m_pending.pixelConstants[2];
- }
- if (m_current.vertexConstants != m_pending.vertexConstants)
- {
- D3D::context->VSSetConstantBuffers(0, 1, &m_pending.vertexConstants);
- D3D::context->VSSetConstantBuffers(1, 1, &m_pending.vertexConstants);
- m_current.vertexConstants = m_pending.vertexConstants;
- }
- if (m_current.geometryConstants != m_pending.geometryConstants)
- {
- D3D::context->GSSetConstantBuffers(0, 1, &m_pending.geometryConstants);
- m_current.geometryConstants = m_pending.geometryConstants;
- }
- }
- if (dirtyBuffers || (m_dirtyFlags.test(DirtyFlag_InputAssembler)))
- {
- if (m_current.vertexBuffer != m_pending.vertexBuffer ||
- m_current.vertexBufferStride != m_pending.vertexBufferStride ||
- m_current.vertexBufferOffset != m_pending.vertexBufferOffset)
- {
- D3D::context->IASetVertexBuffers(0, 1, &m_pending.vertexBuffer, &m_pending.vertexBufferStride,
- &m_pending.vertexBufferOffset);
- m_current.vertexBuffer = m_pending.vertexBuffer;
- m_current.vertexBufferStride = m_pending.vertexBufferStride;
- m_current.vertexBufferOffset = m_pending.vertexBufferOffset;
- }
- if (m_current.indexBuffer != m_pending.indexBuffer)
- {
- D3D::context->IASetIndexBuffer(m_pending.indexBuffer, DXGI_FORMAT_R16_UINT, 0);
- m_current.indexBuffer = m_pending.indexBuffer;
- }
- if (m_current.topology != m_pending.topology)
- {
- D3D::context->IASetPrimitiveTopology(m_pending.topology);
- m_current.topology = m_pending.topology;
- }
- if (m_current.inputLayout != m_pending.inputLayout)
- {
- D3D::context->IASetInputLayout(m_pending.inputLayout);
- m_current.inputLayout = m_pending.inputLayout;
- }
- }
- if (dirtyShaders)
- {
- if (m_current.pixelShader != m_pending.pixelShader)
- {
- D3D::context->PSSetShader(m_pending.pixelShader, nullptr, 0);
- m_current.pixelShader = m_pending.pixelShader;
- }
- if (m_current.vertexShader != m_pending.vertexShader)
- {
- D3D::context->VSSetShader(m_pending.vertexShader, nullptr, 0);
- m_current.vertexShader = m_pending.vertexShader;
- }
- if (m_current.geometryShader != m_pending.geometryShader)
- {
- D3D::context->GSSetShader(m_pending.geometryShader, nullptr, 0);
- m_current.geometryShader = m_pending.geometryShader;
- }
- }
- if (m_dirtyFlags.test(DirtyFlag_BlendState))
- {
- D3D::context->OMSetBlendState(m_pending.blendState, nullptr, 0xFFFFFFFF);
- m_current.blendState = m_pending.blendState;
- }
- if (m_dirtyFlags.test(DirtyFlag_DepthState))
- {
- D3D::context->OMSetDepthStencilState(m_pending.depthState, 0);
- m_current.depthState = m_pending.depthState;
- }
- if (m_dirtyFlags.test(DirtyFlag_RasterizerState))
- {
- D3D::context->RSSetState(m_pending.rasterizerState);
- m_current.rasterizerState = m_pending.rasterizerState;
- }
- ApplyTextures();
- m_dirtyFlags = 0;
- }
- void StateManager::ApplyTextures()
- {
- for (u32 i = 0; i < VideoCommon::MAX_PIXEL_SHADER_SAMPLERS; i++)
- {
- const u32 flag = i;
- if (m_dirtyFlags.test(flag))
- {
- if (m_current.textures[i] != m_pending.textures[i])
- {
- D3D::context->PSSetShaderResources(i, 1, &m_pending.textures[i]);
- m_current.textures[i] = m_pending.textures[i];
- }
- m_dirtyFlags.reset(flag);
- }
- }
- for (u32 i = 0; i < VideoCommon::MAX_PIXEL_SHADER_SAMPLERS; i++)
- {
- const u32 flag = i + VideoCommon::MAX_PIXEL_SHADER_SAMPLERS;
- if (m_dirtyFlags.test(flag))
- {
- if (m_current.samplers[i] != m_pending.samplers[i])
- {
- D3D::context->PSSetSamplers(i, 1, &m_pending.samplers[i]);
- m_current.samplers[i] = m_pending.samplers[i];
- }
- m_dirtyFlags.reset(flag);
- }
- }
- }
- u32 StateManager::UnsetTexture(ID3D11ShaderResourceView* srv)
- {
- u32 mask = 0;
- for (u32 index = 0; index < VideoCommon::MAX_PIXEL_SHADER_SAMPLERS; ++index)
- {
- if (m_current.textures[index] == srv)
- {
- SetTexture(index, nullptr);
- mask |= 1 << index;
- }
- }
- return mask;
- }
- void StateManager::SetTextureByMask(u32 textureSlotMask, ID3D11ShaderResourceView* srv)
- {
- while (textureSlotMask)
- {
- const int index = std::countr_zero(textureSlotMask);
- SetTexture(index, srv);
- textureSlotMask &= ~(1 << index);
- }
- }
- void StateManager::SetComputeUAV(u32 index, ID3D11UnorderedAccessView* uav)
- {
- if (m_compute_images[index] == uav)
- return;
- m_compute_images[index] = uav;
- D3D::context->CSSetUnorderedAccessViews(0, static_cast<u32>(m_compute_images.size()),
- m_compute_images.data(), nullptr);
- }
- void StateManager::SetComputeShader(ID3D11ComputeShader* shader)
- {
- if (m_compute_shader == shader)
- return;
- m_compute_shader = shader;
- D3D::context->CSSetShader(shader, nullptr, 0);
- }
- void StateManager::SyncComputeBindings()
- {
- if (m_compute_constants != m_pending.pixelConstants[0])
- {
- m_compute_constants = m_pending.pixelConstants[0];
- D3D::context->CSSetConstantBuffers(0, 1, &m_compute_constants);
- }
- for (u32 start = 0; start < static_cast<u32>(m_compute_textures.size());)
- {
- if (m_compute_textures[start] == m_pending.textures[start])
- {
- start++;
- continue;
- }
- m_compute_textures[start] = m_pending.textures[start];
- u32 end = start + 1;
- for (; end < static_cast<u32>(m_compute_textures.size()); end++)
- {
- if (m_compute_textures[end] == m_pending.textures[end])
- break;
- m_compute_textures[end] = m_pending.textures[end];
- }
- D3D::context->CSSetShaderResources(start, end - start, &m_compute_textures[start]);
- start = end;
- }
- for (u32 start = 0; start < static_cast<u32>(m_compute_samplers.size());)
- {
- if (m_compute_samplers[start] == m_pending.samplers[start])
- {
- start++;
- continue;
- }
- m_compute_samplers[start] = m_pending.samplers[start];
- u32 end = start + 1;
- for (; end < static_cast<u32>(m_compute_samplers.size()); end++)
- {
- if (m_compute_samplers[end] == m_pending.samplers[end])
- break;
- m_compute_samplers[end] = m_pending.samplers[end];
- }
- D3D::context->CSSetSamplers(start, end - start, &m_compute_samplers[start]);
- start = end;
- }
- }
- } // namespace D3D
- StateCache::~StateCache() = default;
- ID3D11SamplerState* StateCache::Get(SamplerState state)
- {
- std::lock_guard<std::mutex> guard(m_lock);
- auto it = m_sampler.find(state);
- if (it != m_sampler.end())
- return it->second.Get();
- D3D11_SAMPLER_DESC sampdc = CD3D11_SAMPLER_DESC(CD3D11_DEFAULT());
- if (state.tm0.mipmap_filter == FilterMode::Linear)
- {
- if (state.tm0.min_filter == FilterMode::Linear)
- sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ?
- D3D11_FILTER_MIN_MAG_MIP_LINEAR :
- D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
- else
- sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ?
- D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR :
- D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR;
- }
- else
- {
- if (state.tm0.min_filter == FilterMode::Linear)
- sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ?
- D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT :
- D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT;
- else
- sampdc.Filter = (state.tm0.mag_filter == FilterMode::Linear) ?
- D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT :
- D3D11_FILTER_MIN_MAG_MIP_POINT;
- }
- static constexpr std::array<D3D11_TEXTURE_ADDRESS_MODE, 3> address_modes = {
- {D3D11_TEXTURE_ADDRESS_CLAMP, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_MIRROR}};
- sampdc.AddressU = address_modes[static_cast<u32>(state.tm0.wrap_u.Value())];
- sampdc.AddressV = address_modes[static_cast<u32>(state.tm0.wrap_v.Value())];
- sampdc.MaxLOD = state.tm1.max_lod / 16.f;
- sampdc.MinLOD = state.tm1.min_lod / 16.f;
- sampdc.MipLODBias = state.tm0.lod_bias / 256.f;
- if (state.tm0.anisotropic_filtering)
- {
- sampdc.Filter = D3D11_FILTER_ANISOTROPIC;
- sampdc.MaxAnisotropy = 1u << g_ActiveConfig.iMaxAnisotropy;
- }
- ComPtr<ID3D11SamplerState> res;
- HRESULT hr = D3D::device->CreateSamplerState(&sampdc, res.GetAddressOf());
- ASSERT_MSG(VIDEO, SUCCEEDED(hr), "Creating D3D sampler state failed: {}", DX11HRWrap(hr));
- return m_sampler.emplace(state, std::move(res)).first->second.Get();
- }
- ID3D11BlendState* StateCache::Get(BlendingState state)
- {
- std::lock_guard<std::mutex> guard(m_lock);
- auto it = m_blend.find(state.hex);
- if (it != m_blend.end())
- return it->second.Get();
- if (state.logicopenable && g_ActiveConfig.backend_info.bSupportsLogicOp)
- {
- D3D11_BLEND_DESC1 desc = {};
- D3D11_RENDER_TARGET_BLEND_DESC1& tdesc = desc.RenderTarget[0];
- if (state.colorupdate)
- tdesc.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN |
- D3D11_COLOR_WRITE_ENABLE_BLUE;
- else
- tdesc.RenderTargetWriteMask = 0;
- if (state.alphaupdate)
- tdesc.RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
- static constexpr std::array<D3D11_LOGIC_OP, 16> logic_ops = {
- {D3D11_LOGIC_OP_CLEAR, D3D11_LOGIC_OP_AND, D3D11_LOGIC_OP_AND_REVERSE, D3D11_LOGIC_OP_COPY,
- D3D11_LOGIC_OP_AND_INVERTED, D3D11_LOGIC_OP_NOOP, D3D11_LOGIC_OP_XOR, D3D11_LOGIC_OP_OR,
- D3D11_LOGIC_OP_NOR, D3D11_LOGIC_OP_EQUIV, D3D11_LOGIC_OP_INVERT, D3D11_LOGIC_OP_OR_REVERSE,
- D3D11_LOGIC_OP_COPY_INVERTED, D3D11_LOGIC_OP_OR_INVERTED, D3D11_LOGIC_OP_NAND,
- D3D11_LOGIC_OP_SET}};
- tdesc.LogicOpEnable = TRUE;
- tdesc.LogicOp = logic_ops[u32(state.logicmode.Value())];
- ComPtr<ID3D11BlendState1> res;
- HRESULT hr = D3D::device1->CreateBlendState1(&desc, res.GetAddressOf());
- if (SUCCEEDED(hr))
- {
- return m_blend.emplace(state.hex, std::move(res)).first->second.Get();
- }
- WARN_LOG_FMT(VIDEO, "Creating D3D blend state failed with an error: {}", DX11HRWrap(hr));
- }
- D3D11_BLEND_DESC desc = {};
- desc.AlphaToCoverageEnable = FALSE;
- desc.IndependentBlendEnable = FALSE;
- D3D11_RENDER_TARGET_BLEND_DESC& tdesc = desc.RenderTarget[0];
- tdesc.BlendEnable = state.blendenable;
- if (state.colorupdate)
- tdesc.RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_RED | D3D11_COLOR_WRITE_ENABLE_GREEN |
- D3D11_COLOR_WRITE_ENABLE_BLUE;
- else
- tdesc.RenderTargetWriteMask = 0;
- if (state.alphaupdate)
- tdesc.RenderTargetWriteMask |= D3D11_COLOR_WRITE_ENABLE_ALPHA;
- const bool use_dual_source = state.usedualsrc;
- const std::array<D3D11_BLEND, 8> src_factors = {
- {D3D11_BLEND_ZERO, D3D11_BLEND_ONE, D3D11_BLEND_DEST_COLOR, D3D11_BLEND_INV_DEST_COLOR,
- use_dual_source ? D3D11_BLEND_SRC1_ALPHA : D3D11_BLEND_SRC_ALPHA,
- use_dual_source ? D3D11_BLEND_INV_SRC1_ALPHA : D3D11_BLEND_INV_SRC_ALPHA,
- D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA}};
- const std::array<D3D11_BLEND, 8> dst_factors = {
- {D3D11_BLEND_ZERO, D3D11_BLEND_ONE, D3D11_BLEND_SRC_COLOR, D3D11_BLEND_INV_SRC_COLOR,
- use_dual_source ? D3D11_BLEND_SRC1_ALPHA : D3D11_BLEND_SRC_ALPHA,
- use_dual_source ? D3D11_BLEND_INV_SRC1_ALPHA : D3D11_BLEND_INV_SRC_ALPHA,
- D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA}};
- tdesc.SrcBlend = src_factors[u32(state.srcfactor.Value())];
- tdesc.SrcBlendAlpha = src_factors[u32(state.srcfactoralpha.Value())];
- tdesc.DestBlend = dst_factors[u32(state.dstfactor.Value())];
- tdesc.DestBlendAlpha = dst_factors[u32(state.dstfactoralpha.Value())];
- tdesc.BlendOp = state.subtract ? D3D11_BLEND_OP_REV_SUBTRACT : D3D11_BLEND_OP_ADD;
- tdesc.BlendOpAlpha = state.subtractAlpha ? D3D11_BLEND_OP_REV_SUBTRACT : D3D11_BLEND_OP_ADD;
- ComPtr<ID3D11BlendState> res;
- HRESULT hr = D3D::device->CreateBlendState(&desc, res.GetAddressOf());
- ASSERT_MSG(VIDEO, SUCCEEDED(hr), "Creating D3D blend state failed: {}", DX11HRWrap(hr));
- return m_blend.emplace(state.hex, std::move(res)).first->second.Get();
- }
- ID3D11RasterizerState* StateCache::Get(RasterizationState state)
- {
- std::lock_guard<std::mutex> guard(m_lock);
- auto it = m_raster.find(state.hex);
- if (it != m_raster.end())
- return it->second.Get();
- static constexpr std::array<D3D11_CULL_MODE, 4> cull_modes = {
- {D3D11_CULL_NONE, D3D11_CULL_BACK, D3D11_CULL_FRONT, D3D11_CULL_BACK}};
- D3D11_RASTERIZER_DESC desc = {};
- desc.FillMode = D3D11_FILL_SOLID;
- desc.CullMode = cull_modes[u32(state.cullmode.Value())];
- desc.ScissorEnable = TRUE;
- ComPtr<ID3D11RasterizerState> res;
- HRESULT hr = D3D::device->CreateRasterizerState(&desc, res.GetAddressOf());
- ASSERT_MSG(VIDEO, SUCCEEDED(hr), "Creating D3D rasterizer state failed: {}", DX11HRWrap(hr));
- return m_raster.emplace(state.hex, std::move(res)).first->second.Get();
- }
- ID3D11DepthStencilState* StateCache::Get(DepthState state)
- {
- std::lock_guard<std::mutex> guard(m_lock);
- auto it = m_depth.find(state.hex);
- if (it != m_depth.end())
- return it->second.Get();
- D3D11_DEPTH_STENCIL_DESC depthdc = CD3D11_DEPTH_STENCIL_DESC(CD3D11_DEFAULT());
- depthdc.DepthEnable = TRUE;
- depthdc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
- depthdc.DepthFunc = D3D11_COMPARISON_GREATER;
- depthdc.StencilEnable = FALSE;
- depthdc.StencilReadMask = D3D11_DEFAULT_STENCIL_READ_MASK;
- depthdc.StencilWriteMask = D3D11_DEFAULT_STENCIL_WRITE_MASK;
- // Less/greater are swapped due to inverted depth.
- const D3D11_COMPARISON_FUNC d3dCmpFuncs[8] = {
- D3D11_COMPARISON_NEVER, D3D11_COMPARISON_GREATER, D3D11_COMPARISON_EQUAL,
- D3D11_COMPARISON_GREATER_EQUAL, D3D11_COMPARISON_LESS, D3D11_COMPARISON_NOT_EQUAL,
- D3D11_COMPARISON_LESS_EQUAL, D3D11_COMPARISON_ALWAYS};
- if (state.testenable)
- {
- depthdc.DepthEnable = TRUE;
- depthdc.DepthWriteMask =
- state.updateenable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
- depthdc.DepthFunc = d3dCmpFuncs[u32(state.func.Value())];
- }
- else
- {
- // if the test is disabled write is disabled too
- depthdc.DepthEnable = FALSE;
- depthdc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ZERO;
- }
- ComPtr<ID3D11DepthStencilState> res;
- HRESULT hr = D3D::device->CreateDepthStencilState(&depthdc, res.GetAddressOf());
- ASSERT_MSG(VIDEO, SUCCEEDED(hr), "Creating D3D depth stencil state failed: {}", DX11HRWrap(hr));
- return m_depth.emplace(state.hex, std::move(res)).first->second.Get();
- }
- D3D11_PRIMITIVE_TOPOLOGY StateCache::GetPrimitiveTopology(PrimitiveType primitive)
- {
- static constexpr std::array<D3D11_PRIMITIVE_TOPOLOGY, 4> primitives = {
- {D3D11_PRIMITIVE_TOPOLOGY_POINTLIST, D3D11_PRIMITIVE_TOPOLOGY_LINELIST,
- D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP}};
- return primitives[static_cast<u32>(primitive)];
- }
- } // namespace DX11
|