dolphin/Source/Core/VideoBackends/D3D/D3DState.h
Pokechu22 4a9b26de86 VideoCommon: Expose SamplerState to shaders
The benefit to exposing this over the raw BP state is that adjustments Dolphin makes, such as LOD biases from arbitrary mipmap detection, will work properly.
2021-11-17 20:04:34 -08:00

304 lines
8.1 KiB
C++

// Copyright 2014 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <array>
#include <cstddef>
#include <memory>
#include <mutex>
#include <unordered_map>
#include "Common/BitField.h"
#include "Common/CommonTypes.h"
#include "VideoBackends/D3D/D3DBase.h"
#include "VideoCommon/RenderState.h"
namespace DX11
{
class DXFramebuffer;
class StateCache
{
public:
~StateCache();
// Get existing or create new render state.
// Returned objects is owned by the cache and does not need to be released.
ID3D11SamplerState* Get(SamplerState state);
ID3D11BlendState* Get(BlendingState state);
ID3D11RasterizerState* Get(RasterizationState state);
ID3D11DepthStencilState* Get(DepthState state);
// Convert RasterState primitive type to D3D11 primitive topology.
static D3D11_PRIMITIVE_TOPOLOGY GetPrimitiveTopology(PrimitiveType primitive);
private:
std::unordered_map<u32, ComPtr<ID3D11DepthStencilState>> m_depth;
std::unordered_map<u32, ComPtr<ID3D11RasterizerState>> m_raster;
std::unordered_map<u32, ComPtr<ID3D11BlendState>> m_blend;
std::unordered_map<SamplerState, ComPtr<ID3D11SamplerState>> m_sampler;
std::mutex m_lock;
};
namespace D3D
{
class StateManager
{
public:
StateManager();
~StateManager();
void SetBlendState(ID3D11BlendState* state)
{
if (m_current.blendState != state)
m_dirtyFlags |= DirtyFlag_BlendState;
m_pending.blendState = state;
}
void SetDepthState(ID3D11DepthStencilState* state)
{
if (m_current.depthState != state)
m_dirtyFlags |= DirtyFlag_DepthState;
m_pending.depthState = state;
}
void SetRasterizerState(ID3D11RasterizerState* state)
{
if (m_current.rasterizerState != state)
m_dirtyFlags |= DirtyFlag_RasterizerState;
m_pending.rasterizerState = state;
}
void SetTexture(size_t index, ID3D11ShaderResourceView* texture)
{
if (m_current.textures[index] != texture)
m_dirtyFlags |= DirtyFlag_Texture0 << index;
m_pending.textures[index] = texture;
}
void SetSampler(size_t index, ID3D11SamplerState* sampler)
{
if (m_current.samplers[index] != sampler)
m_dirtyFlags |= DirtyFlag_Sampler0 << index;
m_pending.samplers[index] = sampler;
}
void SetPixelConstants(ID3D11Buffer* buffer0, ID3D11Buffer* buffer1 = nullptr)
{
if (m_current.pixelConstants[0] != buffer0 || m_current.pixelConstants[1] != buffer1)
m_dirtyFlags |= DirtyFlag_PixelConstants;
m_pending.pixelConstants[0] = buffer0;
m_pending.pixelConstants[1] = buffer1;
}
void SetVertexConstants(ID3D11Buffer* buffer)
{
if (m_current.vertexConstants != buffer)
m_dirtyFlags |= DirtyFlag_VertexConstants;
m_pending.vertexConstants = buffer;
}
void SetGeometryConstants(ID3D11Buffer* buffer)
{
if (m_current.geometryConstants != buffer)
m_dirtyFlags |= DirtyFlag_GeometryConstants;
m_pending.geometryConstants = buffer;
}
void SetVertexBuffer(ID3D11Buffer* buffer, u32 stride, u32 offset)
{
if (m_current.vertexBuffer != buffer || m_current.vertexBufferStride != stride ||
m_current.vertexBufferOffset != offset)
m_dirtyFlags |= DirtyFlag_VertexBuffer;
m_pending.vertexBuffer = buffer;
m_pending.vertexBufferStride = stride;
m_pending.vertexBufferOffset = offset;
}
void SetIndexBuffer(ID3D11Buffer* buffer)
{
if (m_current.indexBuffer != buffer)
m_dirtyFlags |= DirtyFlag_IndexBuffer;
m_pending.indexBuffer = buffer;
}
void SetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY topology)
{
if (m_current.topology != topology)
m_dirtyFlags |= DirtyFlag_InputAssembler;
m_pending.topology = topology;
}
void SetInputLayout(ID3D11InputLayout* layout)
{
if (m_current.inputLayout != layout)
m_dirtyFlags |= DirtyFlag_InputAssembler;
m_pending.inputLayout = layout;
}
void SetPixelShader(ID3D11PixelShader* shader)
{
if (m_current.pixelShader != shader)
m_dirtyFlags |= DirtyFlag_PixelShader;
m_pending.pixelShader = shader;
}
void SetPixelShaderDynamic(ID3D11PixelShader* shader, ID3D11ClassInstance* const* classInstances,
u32 classInstancesCount)
{
D3D::context->PSSetShader(shader, classInstances, classInstancesCount);
m_current.pixelShader = shader;
m_pending.pixelShader = shader;
}
void SetVertexShader(ID3D11VertexShader* shader)
{
if (m_current.vertexShader != shader)
m_dirtyFlags |= DirtyFlag_VertexShader;
m_pending.vertexShader = shader;
}
void SetGeometryShader(ID3D11GeometryShader* shader)
{
if (m_current.geometryShader != shader)
m_dirtyFlags |= DirtyFlag_GeometryShader;
m_pending.geometryShader = shader;
}
void SetFramebuffer(DXFramebuffer* fb)
{
if (m_current.framebuffer != fb)
m_dirtyFlags |= DirtyFlag_Framebuffer;
m_pending.framebuffer = fb;
}
void SetOMUAV(ID3D11UnorderedAccessView* uav)
{
if (m_current.uav != uav)
m_dirtyFlags |= DirtyFlag_Framebuffer;
m_pending.uav = uav;
}
void SetIntegerRTV(bool enable)
{
if (m_current.use_integer_rtv != enable)
m_dirtyFlags |= DirtyFlag_Framebuffer;
m_pending.use_integer_rtv = enable;
}
// removes currently set texture from all slots, returns mask of previously bound slots
u32 UnsetTexture(ID3D11ShaderResourceView* srv);
void SetTextureByMask(u32 textureSlotMask, ID3D11ShaderResourceView* srv);
void ApplyTextures();
// call this immediately before any drawing operation or to explicitly apply pending resource
// state changes
void Apply();
// Binds constant buffers/textures/samplers to the compute shader stage.
// We don't track these explicitly because it's not often-used.
void SetComputeUAV(ID3D11UnorderedAccessView* uav);
void SetComputeShader(ID3D11ComputeShader* shader);
void SyncComputeBindings();
private:
enum DirtyFlags
{
DirtyFlag_Texture0 = 1 << 0,
DirtyFlag_Texture1 = 1 << 1,
DirtyFlag_Texture2 = 1 << 2,
DirtyFlag_Texture3 = 1 << 3,
DirtyFlag_Texture4 = 1 << 4,
DirtyFlag_Texture5 = 1 << 5,
DirtyFlag_Texture6 = 1 << 6,
DirtyFlag_Texture7 = 1 << 7,
DirtyFlag_Sampler0 = 1 << 8,
DirtyFlag_Sampler1 = 1 << 9,
DirtyFlag_Sampler2 = 1 << 10,
DirtyFlag_Sampler3 = 1 << 11,
DirtyFlag_Sampler4 = 1 << 12,
DirtyFlag_Sampler5 = 1 << 13,
DirtyFlag_Sampler6 = 1 << 14,
DirtyFlag_Sampler7 = 1 << 15,
DirtyFlag_PixelConstants = 1 << 16,
DirtyFlag_VertexConstants = 1 << 17,
DirtyFlag_GeometryConstants = 1 << 18,
DirtyFlag_VertexBuffer = 1 << 19,
DirtyFlag_IndexBuffer = 1 << 20,
DirtyFlag_PixelShader = 1 << 21,
DirtyFlag_VertexShader = 1 << 22,
DirtyFlag_GeometryShader = 1 << 23,
DirtyFlag_InputAssembler = 1 << 24,
DirtyFlag_BlendState = 1 << 25,
DirtyFlag_DepthState = 1 << 26,
DirtyFlag_RasterizerState = 1 << 27,
DirtyFlag_Framebuffer = 1 << 28
};
u32 m_dirtyFlags = ~0u;
struct Resources
{
std::array<ID3D11ShaderResourceView*, 8> textures;
std::array<ID3D11SamplerState*, 8> samplers;
std::array<ID3D11Buffer*, 2> pixelConstants;
ID3D11Buffer* vertexConstants;
ID3D11Buffer* geometryConstants;
ID3D11Buffer* vertexBuffer;
ID3D11Buffer* indexBuffer;
u32 vertexBufferStride;
u32 vertexBufferOffset;
D3D11_PRIMITIVE_TOPOLOGY topology;
ID3D11InputLayout* inputLayout;
ID3D11PixelShader* pixelShader;
ID3D11VertexShader* vertexShader;
ID3D11GeometryShader* geometryShader;
ID3D11BlendState* blendState;
ID3D11DepthStencilState* depthState;
ID3D11RasterizerState* rasterizerState;
DXFramebuffer* framebuffer;
ID3D11UnorderedAccessView* uav;
bool use_integer_rtv;
};
Resources m_pending = {};
Resources m_current = {};
// Compute resources are synced with the graphics resources when we need them.
ID3D11Buffer* m_compute_constants = nullptr;
std::array<ID3D11ShaderResourceView*, 8> m_compute_textures{};
std::array<ID3D11SamplerState*, 8> m_compute_samplers{};
ID3D11UnorderedAccessView* m_compute_image = nullptr;
ID3D11ComputeShader* m_compute_shader = nullptr;
};
extern std::unique_ptr<StateManager> stateman;
} // namespace D3D
} // namespace DX11