Allow skipping draws with broken pipeline variants on Vulkan (#5807)

* Allow skipping draws with broken pipeline variants on Vulkan

* Move IsLinked check to CreatePipeline

* Restore throw on error behaviour for background compile

* Can't remove SetAlphaTest pragmas yet

* Double new line
This commit is contained in:
gdkchan 2024-01-26 13:58:57 -03:00 committed by GitHub
parent a620cbcc90
commit b8d992e5a7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 110 additions and 57 deletions

View File

@ -34,7 +34,8 @@ namespace Ryujinx.Graphics.Vulkan
protected PipelineDynamicState DynamicState; protected PipelineDynamicState DynamicState;
private PipelineState _newState; private PipelineState _newState;
private bool _stateDirty; private bool _graphicsStateDirty;
private bool _computeStateDirty;
private PrimitiveTopology _topology; private PrimitiveTopology _topology;
private ulong _currentPipelineHandle; private ulong _currentPipelineHandle;
@ -353,7 +354,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
EndRenderPass(); EndRenderPass();
RecreatePipelineIfNeeded(PipelineBindPoint.Compute); RecreateComputePipelineIfNeeded();
Gd.Api.CmdDispatch(CommandBuffer, (uint)groupsX, (uint)groupsY, (uint)groupsZ); Gd.Api.CmdDispatch(CommandBuffer, (uint)groupsX, (uint)groupsY, (uint)groupsZ);
} }
@ -366,19 +367,23 @@ namespace Ryujinx.Graphics.Vulkan
} }
EndRenderPass(); EndRenderPass();
RecreatePipelineIfNeeded(PipelineBindPoint.Compute); RecreateComputePipelineIfNeeded();
Gd.Api.CmdDispatchIndirect(CommandBuffer, indirectBuffer.Get(Cbs, indirectBufferOffset, 12).Value, (ulong)indirectBufferOffset); Gd.Api.CmdDispatchIndirect(CommandBuffer, indirectBuffer.Get(Cbs, indirectBufferOffset, 12).Value, (ulong)indirectBufferOffset);
} }
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
{ {
if (!_program.IsLinked || vertexCount == 0) if (vertexCount == 0)
{
return;
}
if (!RecreateGraphicsPipelineIfNeeded())
{ {
return; return;
} }
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
BeginRenderPass(); BeginRenderPass();
DrawCount++; DrawCount++;
@ -437,13 +442,18 @@ namespace Ryujinx.Graphics.Vulkan
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
{ {
if (!_program.IsLinked || indexCount == 0) if (indexCount == 0)
{ {
return; return;
} }
UpdateIndexBufferPattern(); UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
if (!RecreateGraphicsPipelineIfNeeded())
{
return;
}
BeginRenderPass(); BeginRenderPass();
DrawCount++; DrawCount++;
@ -476,17 +486,17 @@ namespace Ryujinx.Graphics.Vulkan
public void DrawIndexedIndirect(BufferRange indirectBuffer) public void DrawIndexedIndirect(BufferRange indirectBuffer)
{ {
if (!_program.IsLinked)
{
return;
}
var buffer = Gd.BufferManager var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
UpdateIndexBufferPattern(); UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
if (!RecreateGraphicsPipelineIfNeeded())
{
return;
}
BeginRenderPass(); BeginRenderPass();
DrawCount++; DrawCount++;
@ -522,11 +532,6 @@ namespace Ryujinx.Graphics.Vulkan
public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride)
{ {
if (!_program.IsLinked)
{
return;
}
var countBuffer = Gd.BufferManager var countBuffer = Gd.BufferManager
.GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false) .GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, false)
.Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value; .Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
@ -536,7 +541,12 @@ namespace Ryujinx.Graphics.Vulkan
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value; .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
UpdateIndexBufferPattern(); UpdateIndexBufferPattern();
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics);
if (!RecreateGraphicsPipelineIfNeeded())
{
return;
}
BeginRenderPass(); BeginRenderPass();
DrawCount++; DrawCount++;
@ -614,18 +624,17 @@ namespace Ryujinx.Graphics.Vulkan
public void DrawIndirect(BufferRange indirectBuffer) public void DrawIndirect(BufferRange indirectBuffer)
{ {
if (!_program.IsLinked)
{
return;
}
// TODO: Support quads and other unsupported topologies. // TODO: Support quads and other unsupported topologies.
var buffer = Gd.BufferManager var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value; .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value;
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); if (!RecreateGraphicsPipelineIfNeeded())
{
return;
}
BeginRenderPass(); BeginRenderPass();
ResumeTransformFeedbackInternal(); ResumeTransformFeedbackInternal();
DrawCount++; DrawCount++;
@ -641,11 +650,6 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotSupportedException(); throw new NotSupportedException();
} }
if (!_program.IsLinked)
{
return;
}
var buffer = Gd.BufferManager var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false) .GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, false)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value; .Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size, false).Value;
@ -656,7 +660,11 @@ namespace Ryujinx.Graphics.Vulkan
// TODO: Support quads and other unsupported topologies. // TODO: Support quads and other unsupported topologies.
RecreatePipelineIfNeeded(PipelineBindPoint.Graphics); if (!RecreateGraphicsPipelineIfNeeded())
{
return;
}
BeginRenderPass(); BeginRenderPass();
ResumeTransformFeedbackInternal(); ResumeTransformFeedbackInternal();
DrawCount++; DrawCount++;
@ -1576,10 +1584,23 @@ namespace Ryujinx.Graphics.Vulkan
protected void SignalStateChange() protected void SignalStateChange()
{ {
_stateDirty = true; _graphicsStateDirty = true;
_computeStateDirty = true;
} }
private void RecreatePipelineIfNeeded(PipelineBindPoint pbp) private void RecreateComputePipelineIfNeeded()
{
if (_computeStateDirty || Pbp != PipelineBindPoint.Compute)
{
CreatePipeline(PipelineBindPoint.Compute);
_computeStateDirty = false;
Pbp = PipelineBindPoint.Compute;
}
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
}
private bool RecreateGraphicsPipelineIfNeeded()
{ {
if (AutoFlush.ShouldFlushDraw(DrawCount)) if (AutoFlush.ShouldFlushDraw(DrawCount))
{ {
@ -1620,17 +1641,23 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs); _vertexBufferUpdater.Commit(Cbs);
} }
if (_stateDirty || Pbp != pbp) if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
{ {
CreatePipeline(pbp); if (!CreatePipeline(PipelineBindPoint.Graphics))
_stateDirty = false; {
Pbp = pbp; return false;
} }
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, pbp); _graphicsStateDirty = false;
Pbp = PipelineBindPoint.Graphics;
} }
private void CreatePipeline(PipelineBindPoint pbp) _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
return true;
}
private bool CreatePipeline(PipelineBindPoint pbp)
{ {
// We can only create a pipeline if the have the shader stages set. // We can only create a pipeline if the have the shader stages set.
if (_newState.Stages != null) if (_newState.Stages != null)
@ -1640,10 +1667,25 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass(); CreateRenderPass();
} }
if (!_program.IsLinked)
{
// Background compile failed, we likely can't create the pipeline because the shader is broken
// or the driver failed to compile it.
return false;
}
var pipeline = pbp == PipelineBindPoint.Compute var pipeline = pbp == PipelineBindPoint.Compute
? _newState.CreateComputePipeline(Gd, Device, _program, PipelineCache) ? _newState.CreateComputePipeline(Gd, Device, _program, PipelineCache)
: _newState.CreateGraphicsPipeline(Gd, Device, _program, PipelineCache, _renderPass.Get(Cbs).Value); : _newState.CreateGraphicsPipeline(Gd, Device, _program, PipelineCache, _renderPass.Get(Cbs).Value);
if (pipeline == null)
{
// Host failed to create the pipeline, likely due to driver bugs.
return false;
}
ulong pipelineHandle = pipeline.GetUnsafe().Value.Handle; ulong pipelineHandle = pipeline.GetUnsafe().Value.Handle;
if (_currentPipelineHandle != pipelineHandle) if (_currentPipelineHandle != pipelineHandle)
@ -1655,6 +1697,8 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdBindPipeline(CommandBuffer, pbp, Pipeline.Get(Cbs).Value); Gd.Api.CmdBindPipeline(CommandBuffer, pbp, Pipeline.Get(Cbs).Value);
} }
} }
return true;
} }
private unsafe void BeginRenderPass() private unsafe void BeginRenderPass()

View File

@ -246,8 +246,11 @@ namespace Ryujinx.Graphics.Vulkan
SignalCommandBufferChange(); SignalCommandBufferChange();
if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
{
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
} }
}
public void FlushCommandsImpl() public void FlushCommandsImpl()
{ {

View File

@ -312,7 +312,6 @@ namespace Ryujinx.Graphics.Vulkan
} }
public NativeArray<PipelineShaderStageCreateInfo> Stages; public NativeArray<PipelineShaderStageCreateInfo> Stages;
public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes;
public PipelineLayout PipelineLayout; public PipelineLayout PipelineLayout;
public SpecData SpecializationData; public SpecData SpecializationData;
@ -321,16 +320,6 @@ namespace Ryujinx.Graphics.Vulkan
public void Initialize() public void Initialize()
{ {
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages); Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
StageRequiredSubgroupSizes = new NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT>(Constants.MaxShaderStages);
for (int index = 0; index < Constants.MaxShaderStages; index++)
{
StageRequiredSubgroupSizes[index] = new PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT
{
SType = StructureType.PipelineShaderStageRequiredSubgroupSizeCreateInfoExt,
RequiredSubgroupSize = RequiredSubgroupSize,
};
}
AdvancedBlendSrcPreMultiplied = true; AdvancedBlendSrcPreMultiplied = true;
AdvancedBlendDstPreMultiplied = true; AdvancedBlendDstPreMultiplied = true;
@ -397,7 +386,8 @@ namespace Ryujinx.Graphics.Vulkan
Device device, Device device,
ShaderCollection program, ShaderCollection program,
PipelineCache cache, PipelineCache cache,
RenderPass renderPass) RenderPass renderPass,
bool throwOnError = false)
{ {
if (program.TryGetGraphicsPipeline(ref Internal, out var pipeline)) if (program.TryGetGraphicsPipeline(ref Internal, out var pipeline))
{ {
@ -630,7 +620,18 @@ namespace Ryujinx.Graphics.Vulkan
BasePipelineIndex = -1, BasePipelineIndex = -1,
}; };
gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError(); Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle);
if (throwOnError)
{
result.ThrowOnError();
}
else if (result.IsError())
{
program.AddGraphicsPipeline(ref Internal, null);
return null;
}
// Restore previous blend enable values if we changed it. // Restore previous blend enable values if we changed it.
while (blendEnables != 0) while (blendEnables != 0)
@ -708,7 +709,6 @@ namespace Ryujinx.Graphics.Vulkan
public readonly void Dispose() public readonly void Dispose()
{ {
Stages.Dispose(); Stages.Dispose();
StageRequiredSubgroupSizes.Dispose();
} }
} }
} }

View File

@ -374,7 +374,7 @@ namespace Ryujinx.Graphics.Vulkan
pipeline.StagesCount = (uint)_shaders.Length; pipeline.StagesCount = (uint)_shaders.Length;
pipeline.PipelineLayout = PipelineLayout; pipeline.PipelineLayout = PipelineLayout;
pipeline.CreateGraphicsPipeline(_gd, _device, this, (_gd.Pipeline as PipelineBase).PipelineCache, renderPass.Value); pipeline.CreateGraphicsPipeline(_gd, _device, this, (_gd.Pipeline as PipelineBase).PipelineCache, renderPass.Value, throwOnError: true);
pipeline.Dispose(); pipeline.Dispose();
} }
@ -511,7 +511,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
foreach (Auto<DisposablePipeline> pipeline in _graphicsPipelineCache.Values) foreach (Auto<DisposablePipeline> pipeline in _graphicsPipelineCache.Values)
{ {
pipeline.Dispose(); pipeline?.Dispose();
} }
} }

View File

@ -6,10 +6,16 @@ namespace Ryujinx.Graphics.Vulkan
{ {
static class ResultExtensions static class ResultExtensions
{ {
public static bool IsError(this Result result)
{
// Only negative result codes are errors.
return result < Result.Success;
}
public static void ThrowOnError(this Result result) public static void ThrowOnError(this Result result)
{ {
// Only negative result codes are errors. // Only negative result codes are errors.
if ((int)result < (int)Result.Success) if (result.IsError())
{ {
throw new VulkanException(result); throw new VulkanException(result);
} }