Multithreaded Pipeline Compilation

This commit is contained in:
riperiperi 2022-02-22 18:47:46 +00:00
parent 7882c0498b
commit f6196fe07b
16 changed files with 704 additions and 103 deletions

View file

@ -14,7 +14,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
public ITexture Base; public ITexture Base;
public int Width => _info.Width; public int Width => _info.Width;
public int Height => _info.Height; public int Height => _info.Height;
public float ScaleFactor { get; } public float ScaleFactor { get; }

View file

@ -262,7 +262,14 @@ namespace Ryujinx.Graphics.GAL.Multithreading
public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info)
{ {
var program = new ThreadedProgram(this); var program = new ThreadedProgram(this);
if (info.State.HasValue)
{
info.BackgroundCompile = true;
}
SourceProgramRequest request = new SourceProgramRequest(program, shaders, info); SourceProgramRequest request = new SourceProgramRequest(program, shaders, info);
Programs.Add(request); Programs.Add(request);
New<CreateProgramCommand>().Set(Ref((IProgramRequest)request)); New<CreateProgramCommand>().Set(Ref((IProgramRequest)request));

View file

@ -0,0 +1,78 @@
using Ryujinx.Common.Memory;
using System;
namespace Ryujinx.Graphics.GAL
{
/// <summary>
/// Descriptor for a pipeline buffer binding.
/// </summary>
public struct BufferPipelineDescriptor
{
public bool Enable { get; }
public int Stride { get; }
public int Divisor { get; }
public BufferPipelineDescriptor(bool enable, int stride, int divisor)
{
Enable = enable;
Stride = stride;
Divisor = divisor;
}
}
/// <summary>
/// State required for a program to compile shaders.
/// </summary>
public struct ProgramPipelineState
{
// Some state is considered always dynamic and should not be included:
// - Viewports/Scissors
// - Bias values (not enable)
public int SamplesCount;
public Array8<bool> AttachmentEnable;
public Array8<Format> AttachmentFormats;
public bool DepthStencilEnable;
public Format DepthStencilFormat;
public bool LogicOpEnable;
public LogicalOp LogicOp;
public Array8<BlendDescriptor> BlendDescriptors;
public Array8<uint> ColorWriteMask;
public int VertexAttribCount;
public Array32<VertexAttribDescriptor> VertexAttribs;
public int VertexBufferCount;
public Array33<BufferPipelineDescriptor> VertexBuffers;
// TODO: Min/max depth bounds.
public DepthTestDescriptor DepthTest;
public StencilTestDescriptor StencilTest;
public FrontFace FrontFace;
public Face CullMode;
public bool CullEnable;
public PolygonModeMask BiasEnable;
public float LineWidth;
// TODO: Polygon mode.
public bool DepthClampEnable;
public bool RasterizerDiscard;
public PrimitiveTopology Topology;
public bool PrimitiveRestartEnable;
public uint PatchControlPoints;
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
{
VertexAttribCount = vertexAttribs.Length;
vertexAttribs.CopyTo(VertexAttribs.ToSpan());
}
public void SetLogicOpState(bool enable, LogicalOp op)
{
LogicOp = op;
LogicOpEnable = enable;
}
}
}

View file

@ -3,10 +3,21 @@ namespace Ryujinx.Graphics.GAL
public struct ShaderInfo public struct ShaderInfo
{ {
public int FragmentOutputMap { get; } public int FragmentOutputMap { get; }
public ProgramPipelineState? State { get; }
public bool BackgroundCompile { get; set; }
public ShaderInfo(int fragmentOutputMap, ProgramPipelineState state)
{
FragmentOutputMap = fragmentOutputMap;
State = state;
BackgroundCompile = false;
}
public ShaderInfo(int fragmentOutputMap) public ShaderInfo(int fragmentOutputMap)
{ {
FragmentOutputMap = fragmentOutputMap; FragmentOutputMap = fragmentOutputMap;
State = null;
BackgroundCompile = false;
} }
} }
} }

View file

@ -200,8 +200,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Starts draw. /// Starts draw.
/// This sets primitive type and instanced draw parameters. /// This sets primitive type and instanced draw parameters.
/// </summary> /// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
public void DrawBegin(int argument) public void DrawBegin(ThreedClass engine, int argument)
{ {
bool incrementInstance = (argument & (1 << 26)) != 0; bool incrementInstance = (argument & (1 << 26)) != 0;
bool resetInstance = (argument & (1 << 27)) == 0; bool resetInstance = (argument & (1 << 27)) == 0;
@ -209,12 +210,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_state.State.PrimitiveTypeOverrideEnable) if (_state.State.PrimitiveTypeOverrideEnable)
{ {
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride; PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
DrawBegin(incrementInstance, resetInstance, typeOverride.Convert()); DrawBegin(engine, incrementInstance, resetInstance, typeOverride.Convert());
} }
else else
{ {
PrimitiveType type = (PrimitiveType)(argument & 0xffff); PrimitiveType type = (PrimitiveType)(argument & 0xffff);
DrawBegin(incrementInstance, resetInstance, type.Convert()); DrawBegin(engine, incrementInstance, resetInstance, type.Convert());
} }
} }
@ -222,10 +223,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// Starts draw. /// Starts draw.
/// This sets primitive type and instanced draw parameters. /// This sets primitive type and instanced draw parameters.
/// </summary> /// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="incrementInstance">Indicates if the current instance should be incremented</param> /// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
/// <param name="resetInstance">Indicates if the current instance should be set to zero</param> /// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
/// <param name="topology">Primitive topology</param> /// <param name="topology">Primitive topology</param>
private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology) private void DrawBegin(ThreedClass engine, bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
{ {
if (incrementInstance) if (incrementInstance)
{ {
@ -240,6 +242,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (_drawState.Topology != topology || !_topologySet) if (_drawState.Topology != topology || !_topologySet)
{ {
engine.SetPipelineTopology(topology);
_context.Renderer.Pipeline.SetPrimitiveTopology(topology); _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
_drawState.Topology = topology; _drawState.Topology = topology;
_topologySet = true; _topologySet = true;
@ -309,7 +312,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride; PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
DrawBegin(instanced, !instanced, typeOverride.Convert()); DrawBegin(engine, instanced, !instanced, typeOverride.Convert());
int firstIndex = argument & 0xffff; int firstIndex = argument & 0xffff;
int indexCount = (argument >> 16) & 0xfff; int indexCount = (argument >> 16) & 0xfff;
@ -403,6 +406,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
engine.Write(IndexBufferCountMethodOffset * 4, indexCount); engine.Write(IndexBufferCountMethodOffset * 4, indexCount);
engine.SetPipelineTopology(topology);
_context.Renderer.Pipeline.SetPrimitiveTopology(topology); _context.Renderer.Pipeline.SetPrimitiveTopology(topology);
_drawState.Topology = topology; _drawState.Topology = topology;
_topologySet = true; _topologySet = true;

View file

@ -15,11 +15,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
class StateUpdater class StateUpdater
{ {
public const int ShaderStateIndex = 0; public const int ShaderStateIndex = 16;
public const int RasterizerStateIndex = 1; public const int RasterizerStateIndex = 15;
public const int ScissorStateIndex = 2; public const int ScissorStateIndex = 17;
public const int VertexBufferStateIndex = 3; public const int VertexBufferStateIndex = 0;
public const int PrimitiveRestartStateIndex = 4; public const int PrimitiveRestartStateIndex = 12;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
@ -31,6 +31,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private readonly ShaderProgramInfo[] _currentProgramInfo; private readonly ShaderProgramInfo[] _currentProgramInfo;
private ShaderSpecializationState _shaderSpecState; private ShaderSpecializationState _shaderSpecState;
private ProgramPipelineState _pipeline;
private bool _vtgWritesRtLayer; private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten; private byte _vsClipDistancesWritten;
@ -54,7 +56,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState = drawState; _drawState = drawState;
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages]; _currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
// ShaderState must be the first, as other state updates depends on information from the currently bound shader. // ShaderState must be updated after other state updates, as pipeline state is sent to the backend when compiling new shaders.
// Render target state must appear after shader state as it depends on information from the currently bound shader.
// Rasterizer and scissor states are checked by render target clear, their indexes // Rasterizer and scissor states are checked by render target clear, their indexes
// must be updated on the constants "RasterizerStateIndex" and "ScissorStateIndex" if modified. // must be updated on the constants "RasterizerStateIndex" and "ScissorStateIndex" if modified.
// The vertex buffer state may be forced dirty when a indexed draw starts, the "VertexBufferStateIndex" // The vertex buffer state may be forced dirty when a indexed draw starts, the "VertexBufferStateIndex"
@ -62,53 +65,39 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
// The order of the other state updates doesn't matter. // The order of the other state updates doesn't matter.
_updateTracker = new StateUpdateTracker<ThreedClassState>(new[] _updateTracker = new StateUpdateTracker<ThreedClassState>(new[]
{ {
new StateUpdateCallbackEntry(UpdateShaderState,
nameof(ThreedClassState.ShaderBaseAddress),
nameof(ThreedClassState.ShaderState)),
new StateUpdateCallbackEntry(UpdateRasterizerState, nameof(ThreedClassState.RasterizeEnable)),
new StateUpdateCallbackEntry(UpdateScissorState,
nameof(ThreedClassState.ScissorState),
nameof(ThreedClassState.ScreenScissorState)),
new StateUpdateCallbackEntry(UpdateVertexBufferState, new StateUpdateCallbackEntry(UpdateVertexBufferState,
nameof(ThreedClassState.VertexBufferDrawState), nameof(ThreedClassState.VertexBufferDrawState),
nameof(ThreedClassState.VertexBufferInstanced), nameof(ThreedClassState.VertexBufferInstanced),
nameof(ThreedClassState.VertexBufferState), nameof(ThreedClassState.VertexBufferState),
nameof(ThreedClassState.VertexBufferEndAddress)), nameof(ThreedClassState.VertexBufferEndAddress)),
new StateUpdateCallbackEntry(UpdatePrimitiveRestartState, new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
nameof(ThreedClassState.PrimitiveRestartDrawArrays),
nameof(ThreedClassState.PrimitiveRestartState)),
new StateUpdateCallbackEntry(UpdateTessellationState, new StateUpdateCallbackEntry(UpdateBlendState,
nameof(ThreedClassState.TessOuterLevel), nameof(ThreedClassState.BlendIndependent),
nameof(ThreedClassState.TessInnerLevel), nameof(ThreedClassState.BlendConstant),
nameof(ThreedClassState.PatchVertices)), nameof(ThreedClassState.BlendStateCommon),
nameof(ThreedClassState.BlendEnableCommon),
nameof(ThreedClassState.BlendEnable),
nameof(ThreedClassState.BlendState)),
new StateUpdateCallbackEntry(UpdateTfBufferState, nameof(ThreedClassState.TfBufferState)), new StateUpdateCallbackEntry(UpdateFaceState, nameof(ThreedClassState.FaceState)),
new StateUpdateCallbackEntry(UpdateUserClipState, nameof(ThreedClassState.ClipDistanceEnable)),
new StateUpdateCallbackEntry(UpdateRenderTargetState, new StateUpdateCallbackEntry(UpdateStencilTestState,
nameof(ThreedClassState.RtColorState), nameof(ThreedClassState.StencilBackMasks),
nameof(ThreedClassState.RtDepthStencilState), nameof(ThreedClassState.StencilTestState),
nameof(ThreedClassState.RtControl), nameof(ThreedClassState.StencilBackTestState)),
nameof(ThreedClassState.RtDepthStencilSize),
nameof(ThreedClassState.RtDepthStencilEnable)),
new StateUpdateCallbackEntry(UpdateDepthClampState, nameof(ThreedClassState.ViewVolumeClipControl)),
new StateUpdateCallbackEntry(UpdateAlphaTestState,
nameof(ThreedClassState.AlphaTestEnable),
nameof(ThreedClassState.AlphaTestRef),
nameof(ThreedClassState.AlphaTestFunc)),
new StateUpdateCallbackEntry(UpdateDepthTestState, new StateUpdateCallbackEntry(UpdateDepthTestState,
nameof(ThreedClassState.DepthTestEnable), nameof(ThreedClassState.DepthTestEnable),
nameof(ThreedClassState.DepthWriteEnable), nameof(ThreedClassState.DepthWriteEnable),
nameof(ThreedClassState.DepthTestFunc)), nameof(ThreedClassState.DepthTestFunc)),
new StateUpdateCallbackEntry(UpdateTessellationState,
nameof(ThreedClassState.TessOuterLevel),
nameof(ThreedClassState.TessInnerLevel),
nameof(ThreedClassState.PatchVertices)),
new StateUpdateCallbackEntry(UpdateViewportTransform, new StateUpdateCallbackEntry(UpdateViewportTransform,
nameof(ThreedClassState.DepthMode), nameof(ThreedClassState.DepthMode),
nameof(ThreedClassState.ViewportTransform), nameof(ThreedClassState.ViewportTransform),
@ -116,6 +105,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
nameof(ThreedClassState.YControl), nameof(ThreedClassState.YControl),
nameof(ThreedClassState.ViewportTransformEnable)), nameof(ThreedClassState.ViewportTransformEnable)),
new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState)),
new StateUpdateCallbackEntry(UpdateDepthClampState, nameof(ThreedClassState.ViewVolumeClipControl)),
new StateUpdateCallbackEntry(UpdatePolygonMode, new StateUpdateCallbackEntry(UpdatePolygonMode,
nameof(ThreedClassState.PolygonModeFront), nameof(ThreedClassState.PolygonModeFront),
nameof(ThreedClassState.PolygonModeBack)), nameof(ThreedClassState.PolygonModeBack)),
@ -126,21 +119,46 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
nameof(ThreedClassState.DepthBiasUnits), nameof(ThreedClassState.DepthBiasUnits),
nameof(ThreedClassState.DepthBiasClamp)), nameof(ThreedClassState.DepthBiasClamp)),
new StateUpdateCallbackEntry(UpdateStencilTestState, new StateUpdateCallbackEntry(UpdatePrimitiveRestartState, nameof(ThreedClassState.PrimitiveRestartState)),
nameof(ThreedClassState.StencilBackMasks),
nameof(ThreedClassState.StencilTestState), new StateUpdateCallbackEntry(UpdateLineState,
nameof(ThreedClassState.StencilBackTestState)), nameof(ThreedClassState.LineWidthSmooth),
nameof(ThreedClassState.LineSmoothEnable)),
new StateUpdateCallbackEntry(UpdateRtColorMask,
nameof(ThreedClassState.RtColorMaskShared),
nameof(ThreedClassState.RtColorMask)),
new StateUpdateCallbackEntry(UpdateRasterizerState, nameof(ThreedClassState.RasterizeEnable)),
new StateUpdateCallbackEntry(UpdateShaderState,
nameof(ThreedClassState.ShaderBaseAddress),
nameof(ThreedClassState.ShaderState)),
new StateUpdateCallbackEntry(UpdateRenderTargetState,
nameof(ThreedClassState.RtColorState),
nameof(ThreedClassState.RtDepthStencilState),
nameof(ThreedClassState.RtControl),
nameof(ThreedClassState.RtDepthStencilSize),
nameof(ThreedClassState.RtDepthStencilEnable)),
new StateUpdateCallbackEntry(UpdateScissorState,
nameof(ThreedClassState.ScissorState),
nameof(ThreedClassState.ScreenScissorState)),
new StateUpdateCallbackEntry(UpdateTfBufferState, nameof(ThreedClassState.TfBufferState)),
new StateUpdateCallbackEntry(UpdateUserClipState, nameof(ThreedClassState.ClipDistanceEnable)),
new StateUpdateCallbackEntry(UpdateAlphaTestState,
nameof(ThreedClassState.AlphaTestEnable),
nameof(ThreedClassState.AlphaTestRef),
nameof(ThreedClassState.AlphaTestFunc)),
new StateUpdateCallbackEntry(UpdateSamplerPoolState, new StateUpdateCallbackEntry(UpdateSamplerPoolState,
nameof(ThreedClassState.SamplerPoolState), nameof(ThreedClassState.SamplerPoolState),
nameof(ThreedClassState.SamplerIndex)), nameof(ThreedClassState.SamplerIndex)),
new StateUpdateCallbackEntry(UpdateTexturePoolState, nameof(ThreedClassState.TexturePoolState)), new StateUpdateCallbackEntry(UpdateTexturePoolState, nameof(ThreedClassState.TexturePoolState)),
new StateUpdateCallbackEntry(UpdateVertexAttribState, nameof(ThreedClassState.VertexAttribState)),
new StateUpdateCallbackEntry(UpdateLineState,
nameof(ThreedClassState.LineWidthSmooth),
nameof(ThreedClassState.LineSmoothEnable)),
new StateUpdateCallbackEntry(UpdatePointState, new StateUpdateCallbackEntry(UpdatePointState,
nameof(ThreedClassState.PointSize), nameof(ThreedClassState.PointSize),
@ -151,22 +169,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
new StateUpdateCallbackEntry(UpdateIndexBufferState, new StateUpdateCallbackEntry(UpdateIndexBufferState,
nameof(ThreedClassState.IndexBufferState), nameof(ThreedClassState.IndexBufferState),
nameof(ThreedClassState.IndexBufferCount)), nameof(ThreedClassState.IndexBufferCount)),
new StateUpdateCallbackEntry(UpdateFaceState, nameof(ThreedClassState.FaceState)),
new StateUpdateCallbackEntry(UpdateRtColorMask,
nameof(ThreedClassState.RtColorMaskShared),
nameof(ThreedClassState.RtColorMask)),
new StateUpdateCallbackEntry(UpdateBlendState,
nameof(ThreedClassState.BlendIndependent),
nameof(ThreedClassState.BlendConstant),
nameof(ThreedClassState.BlendStateCommon),
nameof(ThreedClassState.BlendEnableCommon),
nameof(ThreedClassState.BlendEnable),
nameof(ThreedClassState.BlendState)),
new StateUpdateCallbackEntry(UpdateLogicOpState, nameof(ThreedClassState.LogicOpState))
}); });
} }
@ -190,6 +192,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_updateTracker.SetAllDirty(); _updateTracker.SetAllDirty();
} }
/// <summary>
/// Sets topology for the current pipeline.
/// </summary>
/// <param name="topology">New topology value</param>
public void SetPipelineTopology(PrimitiveTopology topology)
{
_pipeline.Topology = topology;
}
/// <summary> /// <summary>
/// Updates host state for any modified guest state, since the last time this function was called. /// Updates host state for any modified guest state, since the last time this function was called.
/// </summary> /// </summary>
@ -320,6 +331,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
private void UpdateTessellationState() private void UpdateTessellationState()
{ {
_pipeline.PatchControlPoints = (uint)_state.State.PatchVertices;
_context.Renderer.Pipeline.SetPatchParameters( _context.Renderer.Pipeline.SetPatchParameters(
_state.State.PatchVertices, _state.State.PatchVertices,
_state.State.TessOuterLevel.ToSpan(), _state.State.TessOuterLevel.ToSpan(),
@ -352,6 +365,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private void UpdateRasterizerState() private void UpdateRasterizerState()
{ {
bool enable = _state.State.RasterizeEnable; bool enable = _state.State.RasterizeEnable;
_pipeline.RasterizerDiscard = !enable;
_context.Renderer.Pipeline.SetRasterizerDiscard(!enable); _context.Renderer.Pipeline.SetRasterizerDiscard(!enable);
} }
@ -555,7 +569,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private void UpdateDepthClampState() private void UpdateDepthClampState()
{ {
ViewVolumeClipControl clip = _state.State.ViewVolumeClipControl; ViewVolumeClipControl clip = _state.State.ViewVolumeClipControl;
_context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampDisabled) == 0); bool clamp = (clip & ViewVolumeClipControl.DepthClampDisabled) == 0;
_pipeline.DepthClampEnable = clamp;
_context.Renderer.Pipeline.SetDepthClamp(clamp);
} }
/// <summary> /// <summary>
@ -574,10 +591,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
private void UpdateDepthTestState() private void UpdateDepthTestState()
{ {
_context.Renderer.Pipeline.SetDepthTest(new DepthTestDescriptor( DepthTestDescriptor descriptor = new DepthTestDescriptor(
_state.State.DepthTestEnable, _state.State.DepthTestEnable,
_state.State.DepthWriteEnable, _state.State.DepthWriteEnable,
_state.State.DepthTestFunc)); _state.State.DepthTestFunc);
_pipeline.DepthTest = descriptor;
_context.Renderer.Pipeline.SetDepthTest(descriptor);
} }
/// <summary> /// <summary>
@ -728,6 +748,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
enables |= (depthBias.LineEnable ? PolygonModeMask.Line : 0); enables |= (depthBias.LineEnable ? PolygonModeMask.Line : 0);
enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0); enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
_pipeline.BiasEnable = enables;
_context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp); _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
} }
@ -769,7 +790,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
backMask = test.FrontMask; backMask = test.FrontMask;
} }
_context.Renderer.Pipeline.SetStencilTest(new StencilTestDescriptor( StencilTestDescriptor descriptor = new StencilTestDescriptor(
test.Enable, test.Enable,
test.FrontFunc, test.FrontFunc,
test.FrontSFail, test.FrontSFail,
@ -784,7 +805,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
backDpFail, backDpFail,
backFuncRef, backFuncRef,
backFuncMask, backFuncMask,
backMask)); backMask);
_pipeline.StencilTest = descriptor;
_context.Renderer.Pipeline.SetStencilTest(descriptor);
} }
/// <summary> /// <summary>
@ -853,6 +877,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
format); format);
} }
_pipeline.SetVertexAttribs(vertexAttribs);
_context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs); _context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
} }
@ -864,6 +889,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
float width = _state.State.LineWidthSmooth; float width = _state.State.LineWidthSmooth;
bool smooth = _state.State.LineSmoothEnable; bool smooth = _state.State.LineSmoothEnable;
_pipeline.LineWidth = width;
_context.Renderer.Pipeline.SetLineParameters(width, smooth); _context.Renderer.Pipeline.SetLineParameters(width, smooth);
} }
@ -890,6 +916,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
PrimitiveRestartState primitiveRestart = _state.State.PrimitiveRestartState; PrimitiveRestartState primitiveRestart = _state.State.PrimitiveRestartState;
bool enable = primitiveRestart.Enable && (_drawState.DrawIndexed || _state.State.PrimitiveRestartDrawArrays); bool enable = primitiveRestart.Enable && (_drawState.DrawIndexed || _state.State.PrimitiveRestartDrawArrays);
_pipeline.PrimitiveRestartEnable = enable;
_context.Renderer.Pipeline.SetPrimitiveRestart(enable, primitiveRestart.Index); _context.Renderer.Pipeline.SetPrimitiveRestart(enable, primitiveRestart.Index);
} }
@ -936,6 +963,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (!vertexBuffer.UnpackEnable()) if (!vertexBuffer.UnpackEnable())
{ {
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(false, 0, 0);
_channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0); _channel.BufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
continue; continue;
@ -988,6 +1016,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
size = Math.Min(vbSize, (ulong)((firstInstance + drawState.First + drawState.Count) * stride)); size = Math.Min(vbSize, (ulong)((firstInstance + drawState.First + drawState.Count) * stride));
} }
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor);
_channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor); _channel.BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
} }
} }
@ -1000,6 +1029,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var yControl = _state.State.YControl; var yControl = _state.State.YControl;
var face = _state.State.FaceState; var face = _state.State.FaceState;
_pipeline.CullEnable = face.CullEnable;
_pipeline.CullMode = face.CullFace;
_context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace); _context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
UpdateFrontFace(yControl, face.FrontFace); UpdateFrontFace(yControl, face.FrontFace);
@ -1019,6 +1050,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
frontFace = frontFace == FrontFace.CounterClockwise ? FrontFace.Clockwise : FrontFace.CounterClockwise; frontFace = frontFace == FrontFace.CounterClockwise ? FrontFace.Clockwise : FrontFace.CounterClockwise;
} }
_pipeline.FrontFace = frontFace;
_context.Renderer.Pipeline.SetFrontFace(frontFace); _context.Renderer.Pipeline.SetFrontFace(frontFace);
} }
@ -1044,6 +1076,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u); componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
componentMasks[index] = componentMask; componentMasks[index] = componentMask;
_pipeline.ColorWriteMask[index] = componentMask;
} }
_context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks); _context.Renderer.Pipeline.SetRenderTargetColorMasks(componentMasks);
@ -1092,6 +1125,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
blend.AlphaDstFactor); blend.AlphaDstFactor);
} }
_pipeline.BlendDescriptors[index] = descriptor;
_context.Renderer.Pipeline.SetBlendState(index, descriptor); _context.Renderer.Pipeline.SetBlendState(index, descriptor);
} }
} }
@ -1103,6 +1137,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
LogicalOpState logicOpState = _state.State.LogicOpState; LogicalOpState logicOpState = _state.State.LogicOpState;
_pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
_context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp); _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp);
} }
@ -1134,7 +1169,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
GpuChannelPoolState poolState = GetPoolState(); GpuChannelPoolState poolState = GetPoolState();
GpuChannelGraphicsState graphicsState = GetGraphicsState(); GpuChannelGraphicsState graphicsState = GetGraphicsState();
CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, _channel, poolState, graphicsState, addresses); CachedShaderProgram gs = shaderCache.GetGraphicsShader(ref _state.State, ref _pipeline, _channel, poolState, graphicsState, addresses);
_shaderSpecState = gs.SpecializationState; _shaderSpecState = gs.SpecializationState;

View file

@ -109,6 +109,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_state.State.SetMmeShadowRamControl = (uint)control; _state.State.SetMmeShadowRamControl = (uint)control;
} }
/// <summary>
/// Sets topology for the current pipeline.
/// </summary>
/// <param name="topology">New topology value</param>
public void SetPipelineTopology(PrimitiveTopology topology)
{
_stateUpdater.SetPipelineTopology(topology);
}
/// <summary> /// <summary>
/// Updates current host state for all registers modified since the last call to this method. /// Updates current host state for all registers modified since the last call to this method.
/// </summary> /// </summary>
@ -343,7 +352,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
private void DrawBegin(int argument) private void DrawBegin(int argument)
{ {
_drawManager.DrawBegin(argument); _drawManager.DrawBegin(this, argument);
} }
/// <summary> /// <summary>

View file

@ -60,6 +60,6 @@ namespace Ryujinx.Graphics.Gpu
/// <summary> /// <summary>
/// Enables or disables shader SPIR-V compilation. /// Enables or disables shader SPIR-V compilation.
/// </summary> /// </summary>
public static bool EnableSpirvCompilation; public static bool EnableSpirvCompilation = true;
} }
} }

View file

@ -1,6 +1,8 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Threed; using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader.Cache; using Ryujinx.Graphics.Gpu.Shader.Cache;
using Ryujinx.Graphics.Gpu.Shader.DiskCache; using Ryujinx.Graphics.Gpu.Shader.DiskCache;
@ -230,6 +232,41 @@ namespace Ryujinx.Graphics.Gpu.Shader
return cpShader; return cpShader;
} }
private void UpdatePipelineInfo(ref ThreedClassState state, ref ProgramPipelineState pipeline, GpuChannel channel)
{
channel.TextureManager.UpdateRenderTargets();
var rtControl = state.RtControl;
var msaaMode = state.RtMsaaMode;
pipeline.SamplesCount = msaaMode.SamplesInX() * msaaMode.SamplesInY();
int count = rtControl.UnpackCount();
for (int index = 0; index < Constants.TotalRenderTargets; index++)
{
int rtIndex = rtControl.UnpackPermutationIndex(index);
var colorState = state.RtColorState[rtIndex];
if (index >= count || colorState.Format == 0 || colorState.WidthOrStride == 0)
{
pipeline.AttachmentEnable[index] = false;
pipeline.AttachmentFormats[index] = Format.R8G8B8A8Unorm;
}
else
{
pipeline.AttachmentEnable[index] = true;
pipeline.AttachmentFormats[index] = colorState.Format.Convert().Format;
}
}
pipeline.DepthStencilEnable = state.RtDepthStencilEnable;
pipeline.DepthStencilFormat = pipeline.DepthStencilEnable ? state.RtDepthStencilState.Format.Convert().Format : Format.D24UnormS8Uint;
pipeline.VertexBufferCount = Constants.TotalVertexBuffers;
}
/// <summary> /// <summary>
/// Gets a graphics shader program from the shader cache. /// Gets a graphics shader program from the shader cache.
/// This includes all the specified shader stages. /// This includes all the specified shader stages.
@ -238,6 +275,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// This automatically translates, compiles and adds the code to the cache if not present. /// This automatically translates, compiles and adds the code to the cache if not present.
/// </remarks> /// </remarks>
/// <param name="state">GPU state</param> /// <param name="state">GPU state</param>
/// <param name="pipeline">Pipeline state</param>
/// <param name="channel">GPU channel</param> /// <param name="channel">GPU channel</param>
/// <param name="poolState">Texture pool state</param> /// <param name="poolState">Texture pool state</param>
/// <param name="graphicsState">3D engine state</param> /// <param name="graphicsState">3D engine state</param>
@ -245,6 +283,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <returns>Compiled graphics shader code</returns> /// <returns>Compiled graphics shader code</returns>
public CachedShaderProgram GetGraphicsShader( public CachedShaderProgram GetGraphicsShader(
ref ThreedClassState state, ref ThreedClassState state,
ref ProgramPipelineState pipeline,
GpuChannel channel, GpuChannel channel,
GpuChannelPoolState poolState, GpuChannelPoolState poolState,
GpuChannelGraphicsState graphicsState, GpuChannelGraphicsState graphicsState,
@ -351,8 +390,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
} }
} }
UpdatePipelineInfo(ref state, ref pipeline, channel);
int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1; int fragmentOutputMap = shaders[5]?.Info.FragmentOutputMap ?? -1;
IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources.ToArray(), new ShaderInfo(fragmentOutputMap)); IProgram hostProgram = _context.Renderer.CreateProgram(shaderSources.ToArray(), new ShaderInfo(fragmentOutputMap, pipeline));
gpShaders = new CachedShaderProgram(hostProgram, specState, shaders); gpShaders = new CachedShaderProgram(hostProgram, specState, shaders);
@ -614,12 +655,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
private static ShaderBindings GetBindings(ShaderProgramInfo info) private static ShaderBindings GetBindings(ShaderProgramInfo info)
{ {
static bool IsBuffer(TextureDescriptor descriptor) static bool IsBuffer(Graphics.Shader.TextureDescriptor descriptor)
{ {
return (descriptor.Type & SamplerType.Mask) == SamplerType.TextureBuffer; return (descriptor.Type & SamplerType.Mask) == SamplerType.TextureBuffer;
} }
static bool IsNotBuffer(TextureDescriptor descriptor) static bool IsNotBuffer(Graphics.Shader.TextureDescriptor descriptor)
{ {
return !IsBuffer(descriptor); return !IsBuffer(descriptor);
} }

View file

@ -27,9 +27,12 @@ namespace Ryujinx.Graphics.Vulkan
{ {
foreach (Entry[] bucket in _hashTable) foreach (Entry[] bucket in _hashTable)
{ {
foreach (Entry entry in bucket) if (bucket != null)
{ {
yield return entry.Key; foreach (Entry entry in bucket)
{
yield return entry.Key;
}
} }
} }
} }

View file

@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Vulkan
protected PipelineBindPoint Pbp; protected PipelineBindPoint Pbp;
private PipelineCache _pipelineCache; public PipelineCache _pipelineCache;
protected CommandBufferScoped Cbs; protected CommandBufferScoped Cbs;
protected CommandBufferScoped? PreloadCbs; protected CommandBufferScoped? PreloadCbs;

View file

@ -0,0 +1,271 @@
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
namespace Ryujinx.Graphics.Vulkan
{
static class PipelineConverter
{
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanGraphicsDevice gd, Device device)
{
const int MaxAttachments = Constants.MaxRenderTargets + 1;
AttachmentDescription[] attachmentDescs = null;
var subpass = new SubpassDescription()
{
PipelineBindPoint = PipelineBindPoint.Graphics
};
AttachmentReference* attachmentReferences = stackalloc AttachmentReference[MaxAttachments];
Span<int> attachmentIndices = stackalloc int[MaxAttachments];
Span<Silk.NET.Vulkan.Format> attachmentFormats = stackalloc Silk.NET.Vulkan.Format[MaxAttachments];
int attachmentCount = 0;
int colorCount = 0;
int maxColorAttachmentIndex = 0;
for (int i = 0; i < state.AttachmentEnable.Length; i++)
{
if (state.AttachmentEnable[i])
{
maxColorAttachmentIndex = i;
attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
attachmentIndices[attachmentCount++] = i;
colorCount++;
}
}
if (state.DepthStencilEnable)
{
attachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
}
if (attachmentCount != 0)
{
attachmentDescs = new AttachmentDescription[attachmentCount];
for (int i = 0; i < attachmentCount; i++)
{
int bindIndex = attachmentIndices[i];
var format = state.AttachmentFormats[i];
attachmentDescs[i] = new AttachmentDescription(
0,
attachmentFormats[i],
SampleCountFlags.SampleCount1Bit,
AttachmentLoadOp.Load,
AttachmentStoreOp.Store,
AttachmentLoadOp.Load,
AttachmentStoreOp.Store,
ImageLayout.General,
ImageLayout.General);
}
int colorAttachmentsCount = colorCount;
if (colorAttachmentsCount > MaxAttachments - 1)
{
colorAttachmentsCount = MaxAttachments - 1;
}
if (colorAttachmentsCount != 0)
{
int maxAttachmentIndex = Constants.MaxRenderTargets - 1;
subpass.ColorAttachmentCount = (uint)maxAttachmentIndex + 1;
subpass.PColorAttachments = &attachmentReferences[0];
// Fill with VK_ATTACHMENT_UNUSED to cover any gaps.
for (int i = 0; i <= maxAttachmentIndex; i++)
{
subpass.PColorAttachments[i] = new AttachmentReference(Vk.AttachmentUnused, ImageLayout.Undefined);
}
for (int i = 0; i < colorAttachmentsCount; i++)
{
int bindIndex = attachmentIndices[i];
subpass.PColorAttachments[bindIndex] = new AttachmentReference((uint)i, ImageLayout.General);
}
}
if (state.DepthStencilEnable)
{
uint dsIndex = (uint)attachmentCount - 1;
subpass.PDepthStencilAttachment = &attachmentReferences[MaxAttachments - 1];
*subpass.PDepthStencilAttachment = new AttachmentReference(dsIndex, ImageLayout.General);
}
}
var subpassDependency = new SubpassDependency(
0,
0,
PipelineStageFlags.PipelineStageAllGraphicsBit,
PipelineStageFlags.PipelineStageAllGraphicsBit,
AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit,
AccessFlags.AccessMemoryReadBit | AccessFlags.AccessMemoryWriteBit,
0);
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
var renderPassCreateInfo = new RenderPassCreateInfo()
{
SType = StructureType.RenderPassCreateInfo,
PAttachments = pAttachmentDescs,
AttachmentCount = attachmentDescs != null ? (uint)attachmentDescs.Length : 0,
PSubpasses = &subpass,
SubpassCount = 1,
PDependencies = &subpassDependency,
DependencyCount = 1
};
gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
return new DisposableRenderPass(gd.Api, device, renderPass);
}
}
public static unsafe PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanGraphicsDevice gd)
{
PipelineState pipeline = new PipelineState();
pipeline.Initialize();
// It is assumed that Dynamic State is enabled when this conversion is used.
pipeline.BlendConstantA = state.BlendDescriptors[0].BlendConstant.Alpha;
pipeline.BlendConstantB = state.BlendDescriptors[0].BlendConstant.Blue;
pipeline.BlendConstantG = state.BlendDescriptors[0].BlendConstant.Green;
pipeline.BlendConstantR = state.BlendDescriptors[0].BlendConstant.Red;
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.CullModeNone;
pipeline.DepthBoundsTestEnable = false; // Not implemented.
pipeline.DepthClampEnable = state.DepthClampEnable;
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.FrontFace = state.FrontFace.Convert();
pipeline.HasDepthStencil = state.DepthStencilEnable;
pipeline.LineWidth = state.LineWidth;
pipeline.LogicOpEnable = state.LogicOpEnable;
pipeline.LogicOp = state.LogicOp.Convert();
pipeline.MinDepthBounds = 0f; // Not implemented.
pipeline.MaxDepthBounds = 0f; // Not implemented.
pipeline.PatchControlPoints = state.PatchControlPoints;
pipeline.PolygonMode = Silk.NET.Vulkan.PolygonMode.Fill; // Not implemented.
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
pipeline.SamplesCount = (uint)state.SamplesCount;
pipeline.ScissorsCount = 16;
pipeline.ViewportsCount = 16;
pipeline.DepthBiasEnable = state.BiasEnable != 0;
// Stencil masks and ref are dynamic, so are 0 in the Vulkan pipeline.
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.StencilFrontCompareMask = 0;
pipeline.StencilFrontWriteMask = 0;
pipeline.StencilFrontReference = 0;
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.StencilBackCompareMask = 0;
pipeline.StencilBackWriteMask = 0;
pipeline.StencilBackReference = 0;
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
pipeline.Topology = state.Topology.Convert();
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
for (int i = 0; i < vaCount; i++)
{
var attribute = state.VertexAttribs[i];
var bufferIndex = attribute.IsZero ? 0 : attribute.BufferIndex + 1;
pipeline.Internal.VertexAttributeDescriptions[i] = new VertexInputAttributeDescription(
(uint)i,
(uint)bufferIndex,
FormatTable.GetFormat(attribute.Format),
(uint)attribute.Offset);
}
int descriptorIndex = 1;
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
for (int i = 0; i < vbCount; i++)
{
var vertexBuffer = state.VertexBuffers[i];
if (vertexBuffer.Enable)
{
var inputRate = vertexBuffer.Divisor != 0 ? VertexInputRate.Instance : VertexInputRate.Vertex;
// TODO: Support divisor > 1
pipeline.Internal.VertexBindingDescriptions[descriptorIndex++] = new VertexInputBindingDescription(
(uint)i + 1,
(uint)vertexBuffer.Stride,
inputRate);
}
}
pipeline.VertexBindingDescriptionsCount = (uint)descriptorIndex;
// NOTE: Viewports, Scissors are dynamic.
for (int i = 0; i < 8; i++)
{
var blend = state.BlendDescriptors[i];
pipeline.Internal.ColorBlendAttachmentState[i] = new PipelineColorBlendAttachmentState(
blend.Enable,
blend.ColorSrcFactor.Convert(),
blend.ColorDstFactor.Convert(),
blend.ColorOp.Convert(),
blend.AlphaSrcFactor.Convert(),
blend.AlphaDstFactor.Convert(),
blend.AlphaOp.Convert(),
(ColorComponentFlags)state.ColorWriteMask[i]);
}
int maxAttachmentIndex = 0;
for (int i = 0; i < 8; i++)
{
if (state.AttachmentEnable[i])
{
pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
}
}
if (state.DepthStencilEnable)
{
pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
}
pipeline.ColorBlendAttachmentStateCount = 8;
pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
return pipeline;
}
}
}

View file

@ -497,6 +497,7 @@ namespace Ryujinx.Graphics.Vulkan
var pipelineCreateInfo = new GraphicsPipelineCreateInfo() var pipelineCreateInfo = new GraphicsPipelineCreateInfo()
{ {
SType = StructureType.GraphicsPipelineCreateInfo, SType = StructureType.GraphicsPipelineCreateInfo,
Flags = 0,
StageCount = StagesCount, StageCount = StagesCount,
PStages = Stages.Pointer, PStages = Stages.Pointer,
PVertexInputState = &vertexInputState, PVertexInputState = &vertexInputState,

View file

@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Vk _api; private readonly Vk _api;
private readonly Device _device; private readonly Device _device;
private readonly ShaderStageFlags _stage; private readonly ShaderStageFlags _stage;
private readonly Task _compileTask;
private IntPtr _entryPointName; private IntPtr _entryPointName;
private ShaderModule _module; private ShaderModule _module;
@ -30,6 +29,8 @@ namespace Ryujinx.Graphics.Vulkan
public ProgramLinkStatus CompileStatus { private set; get; } public ProgramLinkStatus CompileStatus { private set; get; }
public readonly Task CompileTask;
public unsafe Shader(Vk api, Device device, ShaderStage stage, ShaderBindings bindings, string glsl) public unsafe Shader(Vk api, Device device, ShaderStage stage, ShaderBindings bindings, string glsl)
{ {
_api = api; _api = api;
@ -39,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
Bindings = bindings; Bindings = bindings;
_compileTask = Task.Run(() => CompileTask = Task.Run(() =>
{ {
glsl = glsl.Replace("gl_VertexID", "(gl_VertexIndex - gl_BaseVertex)"); glsl = glsl.Replace("gl_VertexID", "(gl_VertexIndex - gl_BaseVertex)");
glsl = glsl.Replace("gl_InstanceID", "(gl_InstanceIndex - gl_BaseInstance)"); glsl = glsl.Replace("gl_InstanceID", "(gl_InstanceIndex - gl_BaseInstance)");
@ -103,22 +104,27 @@ namespace Ryujinx.Graphics.Vulkan
_device = device; _device = device;
Bindings = bindings; Bindings = bindings;
CompileStatus = ProgramLinkStatus.Success; CompileStatus = ProgramLinkStatus.Incomplete;
fixed (byte* pCode = spirv)
{
var shaderModuleCreateInfo = new ShaderModuleCreateInfo()
{
SType = StructureType.ShaderModuleCreateInfo,
CodeSize = (uint)spirv.Length,
PCode = (uint*)pCode
};
api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
}
_stage = stage.Convert(); _stage = stage.Convert();
_entryPointName = Marshal.StringToHGlobalAnsi("main"); _entryPointName = Marshal.StringToHGlobalAnsi("main");
CompileTask = Task.Run(() =>
{
fixed (byte* pCode = spirv)
{
var shaderModuleCreateInfo = new ShaderModuleCreateInfo()
{
SType = StructureType.ShaderModuleCreateInfo,
CodeSize = (uint)spirv.Length,
PCode = (uint*)pCode
};
api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
}
CompileStatus = ProgramLinkStatus.Success;
});
} }
private static uint[] LoadShaderData(string filePath, out int codeSize) private static uint[] LoadShaderData(string filePath, out int codeSize)
@ -169,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
public void WaitForCompile() public void WaitForCompile()
{ {
_compileTask.Wait(); CompileTask.Wait();
} }
public unsafe void Dispose() public unsafe void Dispose()

View file

@ -1,8 +1,10 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@ -40,8 +42,17 @@ namespace Ryujinx.Graphics.Vulkan
private VulkanGraphicsDevice _gd; private VulkanGraphicsDevice _gd;
private Device _device; private Device _device;
private bool _initialized; private bool _initialized;
private bool _isCompute;
public ShaderCollection(VulkanGraphicsDevice gd, Device device, IShader[] shaders) private ProgramPipelineState _state;
private DisposableRenderPass _dummyRenderPass;
private Task _compileTask;
private bool _firstBackgroundUse;
public ShaderCollection(
VulkanGraphicsDevice gd,
Device device,
IShader[] shaders)
{ {
_gd = gd; _gd = gd;
_device = device; _device = device;
@ -70,6 +81,11 @@ namespace Ryujinx.Graphics.Vulkan
_ => 0 _ => 0
}; };
if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit)
{
_isCompute = true;
}
internalShaders[i] = shader; internalShaders[i] = shader;
} }
@ -101,6 +117,42 @@ namespace Ryujinx.Graphics.Vulkan
GrabAll(x => x.BufferTextureBindings), GrabAll(x => x.BufferTextureBindings),
GrabAll(x => x.BufferImageBindings) GrabAll(x => x.BufferImageBindings)
}; };
_compileTask = Task.CompletedTask;
_firstBackgroundUse = false;
}
public ShaderCollection(
VulkanGraphicsDevice gd,
Device device,
IShader[] shaders,
ProgramPipelineState state) : this(gd, device, shaders)
{
_state = state;
_compileTask = BackgroundCompilation();
_firstBackgroundUse = true;
}
private async Task BackgroundCompilation()
{
await Task.WhenAll(_shaders.Select(shader => ((Shader)shader).CompileTask));
try
{
if (_isCompute)
{
CreateBackgroundComputePipeline();
}
else
{
CreateBackgroundGraphicsPipeline();
}
}
catch (VulkanException e)
{
Common.Logging.Logger.Error?.PrintMsg(Common.Logging.LogClass.Gpu, $"Background Compilation failed: {e.Message}");
}
} }
private void EnsureShadersReady() private void EnsureShadersReady()
@ -136,6 +188,54 @@ namespace Ryujinx.Graphics.Vulkan
return _infos; return _infos;
} }
protected unsafe DisposableRenderPass CreateDummyRenderPass()
{
if (_dummyRenderPass.Value.Handle != 0)
{
return _dummyRenderPass;
}
return _dummyRenderPass = _state.ToRenderPass(_gd, _device);
}
public unsafe void CreateBackgroundComputePipeline()
{
PipelineState pipeline = new PipelineState();
pipeline.Initialize();
pipeline.Stages[0] = ((Shader)_shaders[0]).GetInfo();
pipeline.StagesCount = 1;
pipeline.CreateComputePipeline(_gd.Api, _device, this, (_gd.Pipeline as PipelineBase)._pipelineCache);
}
public unsafe void CreateBackgroundGraphicsPipeline()
{
// To compile shaders in the background in Vulkan, we need to create valid pipelines using the shader modules.
// The GPU provides pipeline state via the GAL that can be converted into our internal Vulkan pipeline state.
// This should match the pipeline state at the time of the first draw. If it doesn't, then it'll likely be
// close enough that the GPU driver will reuse the compiled shader for the different state.
// First, we need to create a render pass object compatible with the one that will be used at runtime.
// The active attachment formats have been provided by the abstraction layer.
var renderPass = CreateDummyRenderPass();
PipelineState pipeline = _state.ToVulkanPipelineState(_gd);
// Copy the shader stage info to the pipeline.
var stages = pipeline.Stages.ToSpan();
for (int i = 0; i < _shaders.Length; i++)
{
stages[i] = ((Shader)_shaders[i]).GetInfo();
}
pipeline.StagesCount = (uint)_shaders.Length;
pipeline.PipelineLayout = PipelineLayout;
pipeline.CreateGraphicsPipeline(_gd, _device, this, (_gd.Pipeline as PipelineBase)._pipelineCache, renderPass.Value);
}
public ProgramLinkStatus CheckProgramLink(bool blocking) public ProgramLinkStatus CheckProgramLink(bool blocking)
{ {
if (LinkStatus == ProgramLinkStatus.Incomplete) if (LinkStatus == ProgramLinkStatus.Incomplete)
@ -163,6 +263,18 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
if (!_compileTask.IsCompleted)
{
if (blocking)
{
_compileTask.Wait();
}
else
{
return ProgramLinkStatus.Incomplete;
}
}
return resultStatus; return resultStatus;
} }
@ -203,7 +315,20 @@ namespace Ryujinx.Graphics.Vulkan
return false; return false;
} }
return _graphicsPipelineCache.TryGetValue(ref key, out pipeline); if (!_graphicsPipelineCache.TryGetValue(ref key, out pipeline))
{
if (_firstBackgroundUse)
{
Logger.Warning?.Print(LogClass.Gpu, "Background pipeline compile missed on draw - incorrect pipeline state?");
_firstBackgroundUse = false;
}
return false;
}
_firstBackgroundUse = false;
return true;
} }
public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection( public Auto<DescriptorSetCollection> GetNewDescriptorSetCollection(
@ -238,6 +363,10 @@ namespace Ryujinx.Graphics.Vulkan
} }
_computePipeline?.Dispose(); _computePipeline?.Dispose();
if (_dummyRenderPass.Value.Handle != 0)
{
_dummyRenderPass.Dispose();
}
} }
} }

View file

@ -204,7 +204,14 @@ namespace Ryujinx.Graphics.Vulkan
public IProgram CreateProgram(IShader[] shaders, ShaderInfo info) public IProgram CreateProgram(IShader[] shaders, ShaderInfo info)
{ {
return new ShaderCollection(this, _device, shaders); if (info.BackgroundCompile && info.State.HasValue && VulkanConfiguration.UseDynamicState)
{
return new ShaderCollection(this, _device, shaders, info.State.Value);
}
else
{
return new ShaderCollection(this, _device, shaders);
}
} }
public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info) public IProgram CreateProgram(ShaderSource[] sources, ShaderInfo info)