thin3d: Make writeMask and compareMask dynamic in all backends. D3D11 needs emulation.

This commit is contained in:
Henrik Rydgård 2022-07-24 16:31:02 +02:00
parent 4a18629e63
commit e43b5e2081
6 changed files with 117 additions and 70 deletions

View file

@ -13,6 +13,8 @@
#include "Common/Data/Encoding/Utf8.h"
#include "Common/Log.h"
#include <map>
#include <cfloat>
#include <D3Dcommon.h>
#include <d3d11.h>
@ -39,6 +41,24 @@ class D3D11SamplerState;
class D3D11RasterState;
class D3D11Framebuffer;
// This must stay POD for the memcmp to work reliably.
struct D3D11DepthStencilKey {
DepthStencilStateDesc desc;
u8 writeMask;
u8 compareMask;
bool operator < (const D3D11DepthStencilKey &other) const {
return memcmp(this, &other, sizeof(D3D11DepthStencilKey)) < 0;
}
};
class D3D11DepthStencilState : public DepthStencilState {
public:
~D3D11DepthStencilState() {}
DepthStencilStateDesc desc;
};
class D3D11DrawContext : public DrawContext {
public:
D3D11DrawContext(ID3D11Device *device, ID3D11DeviceContext *deviceContext, ID3D11Device1 *device1, ID3D11DeviceContext1 *deviceContext1, D3D_FEATURE_LEVEL featureLevel, HWND hWnd, std::vector<std::string> deviceList);
@ -102,9 +122,11 @@ public:
blendFactorDirty_ = true;
}
}
void SetStencilRef(uint8_t ref) override {
stencilRef_ = ref;
stencilRefDirty_ = true;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override {
stencilRef_ = refValue;
stencilWriteMask_ = writeMask;
stencilCompareMask_ = compareMask;
stencilDirty_ = true;
}
void EndFrame() override;
@ -174,6 +196,8 @@ public:
private:
void ApplyCurrentState();
ID3D11DepthStencilState *GetCachedDepthStencilState(D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask);
HWND hWnd_;
ID3D11Device *device_;
ID3D11DeviceContext *context_;
@ -200,8 +224,11 @@ private:
DeviceCaps caps_{};
AutoRef<D3D11BlendState> curBlend_;
AutoRef<D3D11DepthStencilState> curDepth_;
AutoRef<D3D11DepthStencilState> curDepthStencil_;
AutoRef<D3D11RasterState> curRaster_;
std::map<D3D11DepthStencilKey, ID3D11DepthStencilState *> depthStencilCache_;
ID3D11InputLayout *curInputLayout_ = nullptr;
ID3D11VertexShader *curVS_ = nullptr;
ID3D11PixelShader *curPS_ = nullptr;
@ -219,7 +246,9 @@ private:
float blendFactor_[4]{};
bool blendFactorDirty_ = false;
uint8_t stencilRef_ = 0;
bool stencilRefDirty_ = true;
uint8_t stencilWriteMask_ = 0xFF;
uint8_t stencilCompareMask_ = 0xFF;
bool stencilDirty_ = true;
// Temporaries
ID3D11Texture2D *packTexture_ = nullptr;
@ -415,14 +444,6 @@ void D3D11DrawContext::SetScissorRect(int left, int top, int width, int height)
context_->RSSetScissorRects(1, &rc);
}
class D3D11DepthStencilState : public DepthStencilState {
public:
~D3D11DepthStencilState() {
dss->Release();
}
ID3D11DepthStencilState *dss;
};
static const D3D11_COMPARISON_FUNC compareToD3D11[] = {
D3D11_COMPARISON_NEVER,
D3D11_COMPARISON_LESS,
@ -494,21 +515,6 @@ inline void CopyStencilSide(D3D11_DEPTH_STENCILOP_DESC &side, const StencilSetup
side.StencilPassOp = stencilOpToD3D11[(int)input.passOp];
}
DepthStencilState *D3D11DrawContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
D3D11DepthStencilState *ds = new D3D11DepthStencilState();
D3D11_DEPTH_STENCIL_DESC d3ddesc{};
d3ddesc.DepthEnable = desc.depthTestEnabled;
d3ddesc.DepthWriteMask = desc.depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
d3ddesc.DepthFunc = compareToD3D11[(int)desc.depthCompare];
d3ddesc.StencilEnable = desc.stencilEnabled;
CopyStencilSide(d3ddesc.FrontFace, desc.stencil);
CopyStencilSide(d3ddesc.BackFace, desc.stencil);
if (SUCCEEDED(device_->CreateDepthStencilState(&d3ddesc, &ds->dss)))
return ds;
delete ds;
return nullptr;
}
static const D3D11_BLEND_OP blendOpToD3D11[] = {
D3D11_BLEND_OP_ADD,
D3D11_BLEND_OP_SUBTRACT,
@ -547,6 +553,44 @@ public:
float blendFactor[4];
};
ID3D11DepthStencilState *D3D11DrawContext::GetCachedDepthStencilState(D3D11DepthStencilState *state, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
D3D11DepthStencilKey key;
key.desc = state->desc;
key.writeMask = stencilWriteMask;
key.compareMask = stencilCompareMask;
auto findResult = depthStencilCache_.find(key);
if (findResult != depthStencilCache_.end()) {
return findResult->second;
}
// OK, create and insert.
D3D11_DEPTH_STENCIL_DESC d3ddesc{};
d3ddesc.DepthEnable = state->desc.depthTestEnabled;
d3ddesc.DepthWriteMask = state->desc.depthWriteEnabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
d3ddesc.DepthFunc = compareToD3D11[(int)state->desc.depthCompare];
d3ddesc.StencilEnable = state->desc.stencilEnabled;
if (d3ddesc.StencilEnable) {
CopyStencilSide(d3ddesc.FrontFace, state->desc.stencil);
CopyStencilSide(d3ddesc.BackFace, state->desc.stencil);
}
ID3D11DepthStencilState *dss = nullptr;
if (SUCCEEDED(device_->CreateDepthStencilState(&d3ddesc, &dss))) {
depthStencilCache_[key] = dss;
return dss;
} else {
return nullptr;
}
}
DepthStencilState *D3D11DrawContext::CreateDepthStencilState(const DepthStencilStateDesc &desc) {
D3D11DepthStencilState *dss = new D3D11DepthStencilState();
dss->desc = desc;
return dynamic_cast<DepthStencilState *>(dss);
}
BlendState *D3D11DrawContext::CreateBlendState(const BlendStateDesc &desc) {
D3D11BlendState *bs = new D3D11BlendState();
D3D11_BLEND_DESC d3ddesc{};
@ -677,8 +721,7 @@ InputLayout *D3D11DrawContext::CreateInputLayout(const InputLayoutDesc &desc) {
class D3D11ShaderModule : public ShaderModule {
public:
D3D11ShaderModule(const std::string &tag) : tag_(tag) {
}
D3D11ShaderModule(const std::string &tag) : tag_(tag) { }
~D3D11ShaderModule() {
if (vs)
vs->Release();
@ -716,8 +759,11 @@ public:
AutoRef<D3D11InputLayout> input;
ID3D11InputLayout *il = nullptr;
AutoRef<D3D11BlendState> blend;
AutoRef<D3D11DepthStencilState> depth;
AutoRef<D3D11RasterState> raster;
// Combined with dynamic state to key into cached D3D11DepthStencilState, to emulate dynamic parameters.
AutoRef<D3D11DepthStencilState> depthStencil;
ID3D11VertexShader *vs = nullptr;
ID3D11PixelShader *ps = nullptr;
ID3D11GeometryShader *gs = nullptr;
@ -969,7 +1015,7 @@ ShaderModule *D3D11DrawContext::CreateShaderModule(ShaderStage stage, ShaderLang
Pipeline *D3D11DrawContext::CreateGraphicsPipeline(const PipelineDesc &desc) {
D3D11Pipeline *dPipeline = new D3D11Pipeline();
dPipeline->blend = (D3D11BlendState *)desc.blend;
dPipeline->depth = (D3D11DepthStencilState *)desc.depthStencil;
dPipeline->depthStencil = (D3D11DepthStencilState *)desc.depthStencil;
dPipeline->input = (D3D11InputLayout *)desc.inputLayout;
dPipeline->raster = (D3D11RasterState *)desc.raster;
dPipeline->topology = primToD3D11[(int)desc.prim];
@ -1042,7 +1088,7 @@ void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) {
void D3D11DrawContext::InvalidateCachedState() {
// This is a signal to forget all our state caching.
curBlend_ = nullptr;
curDepth_ = nullptr;
curDepthStencil_ = nullptr;
curRaster_ = nullptr;
curPS_ = nullptr;
curVS_ = nullptr;
@ -1065,10 +1111,11 @@ void D3D11DrawContext::ApplyCurrentState() {
curBlend_ = curPipeline_->blend;
blendFactorDirty_ = false;
}
if (curDepth_ != curPipeline_->depth || stencilRefDirty_) {
context_->OMSetDepthStencilState(curPipeline_->depth->dss, stencilRef_);
curDepth_ = curPipeline_->depth;
stencilRefDirty_ = false;
if (curDepthStencil_ != curPipeline_->depthStencil || stencilDirty_) {
ID3D11DepthStencilState *dss = GetCachedDepthStencilState(curPipeline_->depthStencil, stencilWriteMask_, stencilCompareMask_);
context_->OMSetDepthStencilState(dss, stencilRef_);
curDepthStencil_ = curPipeline_->depthStencil;
stencilDirty_ = false;
}
if (curRaster_ != curPipeline_->raster) {
context_->RSSetState(curPipeline_->raster->rs);
@ -1375,8 +1422,8 @@ void D3D11DrawContext::BeginFrame() {
if (curBlend_ != nullptr) {
context_->OMSetBlendState(curBlend_->bs, blendFactor_, 0xFFFFFFFF);
}
if (curDepth_ != nullptr) {
context_->OMSetDepthStencilState(curDepth_->dss, stencilRef_);
if (curDepthStencil_ != nullptr) {
context_->OMSetDepthStencilState(GetCachedDepthStencilState(curDepthStencil_, stencilWriteMask_, stencilCompareMask_), stencilRef_);
}
if (curRaster_ != nullptr) {
context_->RSSetState(curRaster_->rs);

View file

@ -556,9 +556,7 @@ public:
void SetScissorRect(int left, int top, int width, int height) override;
void SetViewports(int count, Viewport *viewports) override;
void SetBlendFactor(float color[4]) override;
void SetStencilRef(uint8_t ref) override {
stencilRef_ = ref;
}
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
void Draw(int vertexCount, int offset) override;
void DrawIndexed(int vertexCount, int offset) override;
@ -734,8 +732,6 @@ DepthStencilState *D3D9Context::CreateDepthStencilState(const DepthStencilStateD
ds->stencilPass = stencilOpToD3D9[(int)desc.stencil.passOp];
ds->stencilFail = stencilOpToD3D9[(int)desc.stencil.failOp];
ds->stencilZFail = stencilOpToD3D9[(int)desc.stencil.depthFailOp];
ds->stencilWriteMask = desc.stencil.writeMask;
ds->stencilCompareMask = desc.stencil.compareMask;
return ds;
}

View file

@ -175,10 +175,8 @@ public:
GLuint stencilZFail;
GLuint stencilPass;
GLuint stencilCompareOp;
uint8_t stencilCompareMask;
uint8_t stencilWriteMask;
void Apply(GLRenderManager *render, uint8_t stencilRef) {
void Apply(GLRenderManager *render, uint8_t stencilRef, uint8_t stencilWriteMask, uint8_t stencilCompareMask) {
render->SetDepth(depthTestEnabled, depthWriteEnabled, depthComp);
render->SetStencilFunc(stencilEnabled, stencilCompareOp, stencilRef, stencilCompareMask);
render->SetStencilOp(stencilWriteMask, stencilFail, stencilZFail, stencilPass);
@ -388,13 +386,21 @@ public:
renderManager_.SetBlendFactor(color);
}
void SetStencilRef(uint8_t ref) override {
stencilRef_ = ref;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override {
stencilRef_ = refValue;
stencilWriteMask_ = writeMask;
stencilCompareMask_ = compareMask;
// Do we need to update on the fly here?
renderManager_.SetStencilFunc(
curPipeline_->depthStencil->stencilEnabled,
curPipeline_->depthStencil->stencilCompareOp,
ref,
curPipeline_->depthStencil->stencilCompareMask);
refValue,
compareMask);
renderManager_.SetStencilOp(
writeMask,
curPipeline_->depthStencil->stencilFail,
curPipeline_->depthStencil->stencilZFail,
curPipeline_->depthStencil->stencilPass);
}
void BindTextures(int start, int count, Texture **textures) override;
@ -491,6 +497,8 @@ private:
AutoRef<Framebuffer> curRenderTarget_;
uint8_t stencilRef_ = 0;
uint8_t stencilWriteMask_ = 0;
uint8_t stencilCompareMask_ = 0;
// Frames in flight is not such a strict concept as with Vulkan until we start using glBufferStorage and fences.
// But might as well have the structure ready, and can't hurt to rotate buffers.
@ -940,8 +948,6 @@ DepthStencilState *OpenGLContext::CreateDepthStencilState(const DepthStencilStat
ds->stencilPass = stencilOpToGL[(int)desc.stencil.passOp];
ds->stencilFail = stencilOpToGL[(int)desc.stencil.failOp];
ds->stencilZFail = stencilOpToGL[(int)desc.stencil.depthFailOp];
ds->stencilWriteMask = desc.stencil.writeMask;
ds->stencilCompareMask = desc.stencil.compareMask;
return ds;
}
@ -1185,7 +1191,7 @@ void OpenGLContext::BindPipeline(Pipeline *pipeline) {
return;
}
curPipeline_->blend->Apply(&renderManager_);
curPipeline_->depthStencil->Apply(&renderManager_, stencilRef_);
curPipeline_->depthStencil->Apply(&renderManager_, stencilRef_, stencilWriteMask_, stencilCompareMask_);
curPipeline_->raster->Apply(&renderManager_);
renderManager_.BindProgram(curPipeline_->program_);
}

View file

@ -407,7 +407,7 @@ public:
void SetScissorRect(int left, int top, int width, int height) override;
void SetViewports(int count, Viewport *viewports) override;
void SetBlendFactor(float color[4]) override;
void SetStencilRef(uint8_t stencilRef) override;
void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) override;
void BindSamplerStates(int start, int count, SamplerState **state) override;
void BindTextures(int start, int count, Texture **textures) override;
@ -554,6 +554,8 @@ private:
DeviceCaps caps_{};
uint8_t stencilRef_ = 0;
uint8_t stencilWriteMask_ = 0xFF;
uint8_t stencilCompareMask_ = 0xFF;
};
static int GetBpp(VkFormat format) {
@ -1160,10 +1162,12 @@ void VKContext::SetBlendFactor(float color[4]) {
renderManager_.SetBlendFactor(col);
}
void VKContext::SetStencilRef(uint8_t stencilRef) {
void VKContext::SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) {
if (curPipeline_->usesStencil)
renderManager_.SetStencilParams(curPipeline_->stencilWriteMask, curPipeline_->stencilTestMask, stencilRef);
stencilRef_ = stencilRef;
renderManager_.SetStencilParams(writeMask, compareMask, refValue);
stencilRef_ = refValue;
stencilWriteMask_ = refValue;
stencilCompareMask_ = refValue;
}
InputLayout *VKContext::CreateInputLayout(const InputLayoutDesc &desc) {
@ -1208,8 +1212,6 @@ Texture *VKContext::CreateTexture(const TextureDesc &desc) {
}
static inline void CopySide(VkStencilOpState &dest, const StencilSetup &src) {
dest.compareMask = src.compareMask;
dest.writeMask = src.writeMask;
dest.compareOp = compToVK[(int)src.compareOp];
dest.failOp = stencilOpToVK[(int)src.failOp];
dest.passOp = stencilOpToVK[(int)src.passOp];

View file

@ -458,8 +458,6 @@ struct StencilSetup {
StencilOp passOp;
StencilOp depthFailOp;
Comparison compareOp;
uint8_t compareMask;
uint8_t writeMask;
};
struct DepthStencilStateDesc {
@ -651,7 +649,7 @@ public:
virtual void SetScissorRect(int left, int top, int width, int height) = 0;
virtual void SetViewports(int count, Viewport *viewports) = 0;
virtual void SetBlendFactor(float color[4]) = 0;
virtual void SetStencilRef(uint8_t ref) = 0;
virtual void SetStencilParams(uint8_t refValue, uint8_t writeMask, uint8_t compareMask) = 0;
virtual void BindSamplerStates(int start, int count, SamplerState **state) = 0;
virtual void BindTextures(int start, int count, Texture **textures) = 0;

View file

@ -329,12 +329,10 @@ void GPUDriverTestScreen::DiscardTest() {
dsDesc.depthWriteEnabled = true;
dsDesc.depthCompare = Comparison::ALWAYS;
dsDesc.stencilEnabled = true;
dsDesc.stencil.compareMask = 0xFF;
dsDesc.stencil.compareOp = Comparison::ALWAYS;
dsDesc.stencil.passOp = StencilOp::REPLACE;
dsDesc.stencil.failOp = StencilOp::REPLACE; // These two shouldn't matter, because the test that fails is discard, not stencil.
dsDesc.stencil.depthFailOp = StencilOp::REPLACE;
dsDesc.stencil.writeMask = 0xFF;
DepthStencilState *depthStencilWrite = draw->CreateDepthStencilState(dsDesc);
// Write only depth.
@ -355,7 +353,6 @@ void GPUDriverTestScreen::DiscardTest() {
dsDesc.stencil.compareOp = Comparison::EQUAL;
dsDesc.stencil.failOp = StencilOp::KEEP;
dsDesc.stencil.depthFailOp = StencilOp::KEEP;
dsDesc.stencil.writeMask = 0x0;
DepthStencilState *stencilEqualDepthAlways = draw->CreateDepthStencilState(dsDesc);
dsDesc.depthTestEnabled = false;
@ -500,27 +497,28 @@ void GPUDriverTestScreen::DiscardTest() {
dc.BeginPipeline(writePipelines[j], samplerNearest_);
// Draw the rectangle with stencil value 0, depth 0.1f and the text with stencil 0xFF, depth 0.9. Then set 0xFF as the stencil value and draw the rectangles at depth 0.5.
draw->SetStencilRef(0x0);
draw->SetStencilParams(0, 0xFF, 0xFF);
dc.SetCurZ(0.1f);
dc.FillRect(UI::Drawable(bgColorBAD), bounds);
// test bounds
dc.Flush();
draw->SetStencilRef(0xff);
draw->SetStencilParams(0xff, 0xFF, 0xFF);
dc.SetCurZ(0.9f);
dc.DrawTextRect("TEST OK", bounds, textColorBAD, ALIGN_HCENTER | ALIGN_VCENTER | FLAG_DYNAMIC_ASCII);
dc.Flush();
// Draw rectangle that should result in the text
dc.BeginPipeline(testPipeline1[i], samplerNearest_);
draw->SetStencilRef(0xff);
draw->SetStencilParams(0xff, 0, 0xFF);
dc.SetCurZ(0.5f);
dc.FillRect(UI::Drawable(textColorOK), bounds);
dc.Flush();
// Draw rectangle that should result in the bg
dc.BeginPipeline(testPipeline2[i], samplerNearest_);
draw->SetStencilRef(0xff);
draw->SetStencilParams(0xff, 0, 0xFF);
dc.SetCurZ(0.5f);
dc.FillRect(UI::Drawable(bgColorOK), bounds);
dc.Flush();