Make StencilOp, FrontFace and CullMode Dynamic States. Also prevent Vertex Input Binding Stride dynamic state from enabling with MoltenVK.

This commit is contained in:
sunshineinabox 2024-05-12 23:16:06 -07:00
parent e6e5829abf
commit 4933888a9d
4 changed files with 210 additions and 63 deletions

View file

@ -84,6 +84,8 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfEnabled;
private bool _tfActive;
private bool _supportExtDynamic;
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
private ulong _drawCountSinceBarrier;
@ -122,6 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
_storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets];
_supportExtDynamic = gd.Capabilities.SupportsExtendedDynamicState;
_newState.Initialize();
}
@ -685,7 +689,7 @@ namespace Ryujinx.Graphics.Vulkan
{
if (texture is TextureView srcTexture)
{
var oldCullMode = _newState.CullMode;
var oldCullMode = _supportExtDynamic ? DynamicState.CullMode : _newState.CullMode;
var oldStencilTestEnable = _newState.StencilTestEnable;
var oldDepthTestEnable = _newState.DepthTestEnable;
var oldDepthWriteEnable = _newState.DepthWriteEnable;
@ -693,7 +697,15 @@ namespace Ryujinx.Graphics.Vulkan
var oldViewports = DynamicState.Viewports;
var oldViewportsCount = _newState.ViewportsCount;
_newState.CullMode = CullModeFlags.None;
if (_supportExtDynamic)
{
DynamicState.SetCullMode(CullModeFlags.None);
}
else
{
_newState.CullMode = CullModeFlags.None;
}
_newState.StencilTestEnable = false;
_newState.DepthTestEnable = false;
_newState.DepthWriteEnable = false;
@ -707,7 +719,15 @@ namespace Ryujinx.Graphics.Vulkan
srcRegion,
dstRegion);
_newState.CullMode = oldCullMode;
if (_supportExtDynamic)
{
DynamicState.SetCullMode(oldCullMode);
}
else
{
_newState.CullMode = oldCullMode;
}
_newState.StencilTestEnable = oldStencilTestEnable;
_newState.DepthTestEnable = oldDepthTestEnable;
_newState.DepthWriteEnable = oldDepthWriteEnable;
@ -876,13 +896,29 @@ namespace Ryujinx.Graphics.Vulkan
public void SetFaceCulling(bool enable, Face face)
{
_newState.CullMode = enable ? face.Convert() : CullModeFlags.None;
if (_supportExtDynamic)
{
DynamicState.SetCullMode(enable ? face.Convert() : CullModeFlags.None);
}
else
{
_newState.CullMode = enable ? face.Convert() : CullModeFlags.None;
}
SignalStateChange();
}
public void SetFrontFace(FrontFace frontFace)
{
_newState.FrontFace = frontFace.Convert();
if (_supportExtDynamic)
{
DynamicState.SetFrontFace(frontFace.Convert());
}
else
{
_newState.FrontFace = frontFace.Convert();
}
SignalStateChange();
}
@ -1112,23 +1148,39 @@ namespace Ryujinx.Graphics.Vulkan
public void SetStencilTest(StencilTestDescriptor stencilTest)
{
DynamicState.SetStencilMasks(
(uint)stencilTest.BackFuncMask,
if (Gd.Capabilities.SupportsExtendedDynamicState)
{
DynamicState.SetStencilOp(
stencilTest.BackSFail.Convert(),
stencilTest.BackDpPass.Convert(),
stencilTest.BackDpFail.Convert(),
stencilTest.BackFunc.Convert(),
stencilTest.FrontSFail.Convert(),
stencilTest.FrontDpPass.Convert(),
stencilTest.FrontDpFail.Convert(),
stencilTest.FrontFunc.Convert());
}
else
{
_newState.StencilBackFailOp = stencilTest.BackSFail.Convert();
_newState.StencilBackPassOp = stencilTest.BackDpPass.Convert();
_newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert();
_newState.StencilBackCompareOp = stencilTest.BackFunc.Convert();
_newState.StencilFrontFailOp = stencilTest.FrontSFail.Convert();
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
}
_newState.StencilTestEnable = stencilTest.TestEnable;
DynamicState.SetStencilMask((uint)stencilTest.BackFuncMask,
(uint)stencilTest.BackMask,
(uint)stencilTest.BackFuncRef,
(uint)stencilTest.FrontFuncMask,
(uint)stencilTest.FrontMask,
(uint)stencilTest.FrontFuncRef);
_newState.StencilTestEnable = stencilTest.TestEnable;
_newState.StencilBackFailOp = stencilTest.BackSFail.Convert();
_newState.StencilBackPassOp = stencilTest.BackDpPass.Convert();
_newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert();
_newState.StencilBackCompareOp = stencilTest.BackFunc.Convert();
_newState.StencilFrontFailOp = stencilTest.FrontSFail.Convert();
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
SignalStateChange();
}

View file

@ -161,9 +161,6 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.Initialize();
// It is assumed that Dynamic State is enabled when this conversion is used.
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None;
pipeline.DepthBoundsTestEnable = false; // Not implemented.
pipeline.DepthClampEnable = state.DepthClampEnable;
@ -172,9 +169,7 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
pipeline.FrontFace = state.FrontFace.Convert();
pipeline.HasDepthStencil = state.DepthStencilEnable;
pipeline.LineWidth = state.LineWidth;
pipeline.LogicOpEnable = state.LogicOpEnable;
@ -203,16 +198,22 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.DepthBiasEnable = state.BiasEnable != 0;
// Stencil masks and ref are dynamic, so are 0 in the Vulkan pipeline.
if (!gd.Capabilities.SupportsExtendedDynamicState)
{
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None;
pipeline.FrontFace = state.FrontFace.Convert();
pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert();
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert();
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
}
pipeline.StencilTestEnable = state.StencilTest.TestEnable;

View file

@ -1,5 +1,6 @@
using Ryujinx.Common.Memory;
using Silk.NET.Vulkan;
using System;
namespace Ryujinx.Graphics.Vulkan
{
@ -18,12 +19,25 @@ namespace Ryujinx.Graphics.Vulkan
private uint _frontCompareMask;
private uint _frontWriteMask;
private uint _frontReference;
private bool _opToo;
private StencilOp _backfailop;
private StencilOp _backpassop;
private StencilOp _backdepthfailop;
private CompareOp _backcompareop;
private StencilOp _frontfailop;
private StencilOp _frontpassop;
private StencilOp _frontdepthfailop;
private CompareOp _frontcompareop;
private Array4<float> _blendConstants;
public uint ViewportsCount;
public Array16<Viewport> Viewports;
public CullModeFlags CullMode;
public FrontFace FrontFace;
[Flags]
private enum DirtyFlags
{
None = 0,
@ -32,7 +46,9 @@ namespace Ryujinx.Graphics.Vulkan
Scissor = 1 << 2,
Stencil = 1 << 3,
Viewport = 1 << 4,
All = Blend | DepthBias | Scissor | Stencil | Viewport,
CullMode = 1 << 5,
FrontFace = 1 << 6,
All = Blend | DepthBias | Scissor | Stencil | Viewport | CullMode | FrontFace,
}
private DirtyFlags _dirty;
@ -63,7 +79,7 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.Scissor;
}
public void SetStencilMasks(
public void SetStencilMask(
uint backCompareMask,
uint backWriteMask,
uint backReference,
@ -80,6 +96,27 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.Stencil;
}
public void SetStencilOp(StencilOp backFailOp = default,
StencilOp backPassOp = default,
StencilOp backDepthFailOp = default,
CompareOp backCompareOp = default,
StencilOp frontFailOp = default,
StencilOp frontPassOp = default,
StencilOp frontDepthFailOp = default,
CompareOp frontCompareOp = default)
{
_backfailop = backFailOp;
_backpassop = backPassOp;
_backdepthfailop = backDepthFailOp;
_backcompareop = backCompareOp;
_frontfailop = frontFailOp;
_frontpassop = frontPassOp;
_frontdepthfailop = frontDepthFailOp;
_frontcompareop = frontCompareOp;
_opToo = true;
}
public void SetViewport(int index, Viewport viewport)
{
@ -98,6 +135,20 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.Viewport;
}
}
public void SetCullMode(CullModeFlags cullMode)
{
CullMode = cullMode;
_dirty |= DirtyFlags.CullMode;
}
public void SetFrontFace(FrontFace frontFace)
{
FrontFace = frontFace;
_dirty |= DirtyFlags.FrontFace;
}
public void ForceAllDirty()
{
@ -123,13 +174,23 @@ namespace Ryujinx.Graphics.Vulkan
if (_dirty.HasFlag(DirtyFlags.Stencil))
{
RecordStencilMasks(api, commandBuffer);
RecordStencil(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.Viewport))
{
RecordViewport(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.CullMode))
{
RecordCullMode(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.FrontFace))
{
RecordFrontFace(api, commandBuffer);
}
_dirty = DirtyFlags.None;
}
@ -151,9 +212,17 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
}
}
private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer)
private readonly void RecordStencil(Vk api, CommandBuffer commandBuffer)
{
if (_opToo)
{
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceBackBit, _backfailop, _backpassop,
_backdepthfailop, _backcompareop);
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontfailop, _frontpassop,
_frontdepthfailop, _frontcompareop);
}
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask);
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask);
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceBackBit, _backReference);
@ -169,5 +238,15 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
}
private void RecordCullMode(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetCullMode(commandBuffer, CullMode);
}
private void RecordFrontFace(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetFrontFace(commandBuffer, FrontFace);
}
}
}

View file

@ -404,6 +404,8 @@ namespace Ryujinx.Graphics.Vulkan
{
UpdateVertexAttributeDescriptions(gd);
}
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
@ -467,11 +469,15 @@ namespace Ryujinx.Graphics.Vulkan
RasterizerDiscardEnable = RasterizerDiscardEnable,
PolygonMode = PolygonMode,
LineWidth = LineWidth,
CullMode = CullMode,
FrontFace = FrontFace,
DepthBiasEnable = DepthBiasEnable,
};
if (!supportsExtDynamicState)
{
rasterizationState.CullMode = CullMode;
rasterizationState.FrontFace = FrontFace;
}
var viewportState = new PipelineViewportStateCreateInfo
{
SType = StructureType.PipelineViewportStateCreateInfo,
@ -500,24 +506,6 @@ namespace Ryujinx.Graphics.Vulkan
AlphaToOneEnable = AlphaToOneEnable,
};
var stencilFront = new StencilOpState(
StencilFrontFailOp,
StencilFrontPassOp,
StencilFrontDepthFailOp,
StencilFrontCompareOp,
null,
null,
null);
var stencilBack = new StencilOpState(
StencilBackFailOp,
StencilBackPassOp,
StencilBackDepthFailOp,
StencilBackCompareOp,
null,
null,
null);
var depthStencilState = new PipelineDepthStencilStateCreateInfo
{
SType = StructureType.PipelineDepthStencilStateCreateInfo,
@ -526,15 +514,37 @@ namespace Ryujinx.Graphics.Vulkan
DepthCompareOp = DepthCompareOp,
DepthBoundsTestEnable = DepthBoundsTestEnable,
StencilTestEnable = StencilTestEnable,
Front = stencilFront,
Back = stencilBack,
MinDepthBounds = MinDepthBounds,
MaxDepthBounds = MaxDepthBounds,
};
if (!supportsExtDynamicState)
{
var stencilFront = new StencilOpState(
StencilFrontFailOp,
StencilFrontPassOp,
StencilFrontDepthFailOp,
StencilFrontCompareOp,
null,
null,
null);
var stencilBack = new StencilOpState(
StencilBackFailOp,
StencilBackPassOp,
StencilBackDepthFailOp,
StencilBackCompareOp,
null,
null,
null);
depthStencilState.Front = stencilFront;
depthStencilState.Back = stencilBack;
}
uint blendEnables = 0;
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
if (isMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
{
// Blend can't be enabled for integer formats, so let's make sure it is disabled.
uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask;
@ -582,9 +592,8 @@ namespace Ryujinx.Graphics.Vulkan
colorBlendState.PNext = &colorBlendAdvancedState;
}
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
int dynamicStatesCount = supportsExtDynamicState ? (isMoltenVk ? 10 : 11) : 7;
DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
@ -598,7 +607,13 @@ namespace Ryujinx.Graphics.Vulkan
if (supportsExtDynamicState)
{
dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
int index = 7;
if (!isMoltenVk) {
dynamicStates[index++] = DynamicState.VertexInputBindingStrideExt;
}
dynamicStates[index++] = DynamicState.CullMode;
dynamicStates[index++] = DynamicState.FrontFace;
dynamicStates[index] = DynamicState.StencilOp;
}
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo