mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-02-21 16:43:35 +00:00
Multithreaded Pipeline Compilation
This commit is contained in:
parent
7882c0498b
commit
f6196fe07b
16 changed files with 704 additions and 103 deletions
|
@ -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; }
|
||||||
|
|
|
@ -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));
|
||||||
|
|
78
Ryujinx.Graphics.GAL/ProgramPipelineState.cs
Normal file
78
Ryujinx.Graphics.GAL/ProgramPipelineState.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
271
Ryujinx.Graphics.Vulkan/PipelineConverter.cs
Normal file
271
Ryujinx.Graphics.Vulkan/PipelineConverter.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue