Author SHA1 Message Date
sunshineinabox 0678a06284 Support VK_EXT_extended_dynamic_state and VK_EXT_extended_dynamic_state2 2026-09-19 23:59:40 -05:00
KeatonTheBot c62976f290 Heap adjustments
- Increase heap limit to 8 GiB for games that crash with > 8 GiB heap. This was inadvertently limited to 6 GiB for 10 & 12 GiB DRAM sizes.
2026-09-19 23:56:11 -05:00
Babib3l 25e88be7f1 Fix ProcessLoader stale PID validation against kernel process table
This PR adresses the following issue : `ProcessLoader.ActiveApplication` could return invalid results when `_latestPid` pointed to a process that no longer existed in the kernel's process table. The original exception path was commented out and bypassed (by sh0inx?) with `GetValueOrDefault` to prevent UI lockups, but this only resolved the symptoms without fixing the root cause.

This was due to sevral factors :
- `_latestPid` was never reset or validated against the actual process state
- ProcessLoader maintained its own `_processesByPid` dictionary separate from the kernel's `KernelContext.Processes`
- No cleanups happened when processes exited or were terminated
- ProcessLoader state could drift out of sync with the kernel process table

**Solution/Fixes**
- Validate` _latestPid` against the kernel process table before returning `ActiveApplication`
- Check process state (Exited/Exiting) and automatically clear stale references
- Add thread-safe cleanup methods (`ClearProcess`, `ClearAllProcesses`)
- Integrate `ClearAllProcesses` into Switch.Dispose for proper shutdown cleanup
- Add warning logs when stale PID is detected and cleared for debugging

**Code Changes**:
- `ProcessLoader.cs`: Add `_pidLock`, update `ActiveApplication` with validation, add cleanup methods
- `Switch.cs`: Call `Processes.ClearAllProcesses()` in Dispose()
2026-09-19 16:52:04 -05:00
Renovate Bot abc03d7325 Update dependency Ryujinx.SDL3-CS to 2026.918.0 (#23)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [Ryujinx.SDL3-CS](https://github.com/Kenji-NX/SDL3-CS) | `2026.707.0` → `2026.918.0` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Ryujinx.SDL3-CS/2026.918.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Ryujinx.SDL3-CS/2026.707.0/2026.918.0?slim=true) |

---

### Release Notes

<details>
<summary>Kenji-NX/SDL3-CS (Ryujinx.SDL3-CS)</summary>

### [`v2026.918.0`](https://github.com/Kenji-NX/SDL3-CS/compare/2026.707.0...2026.918.0)

[Compare Source](https://github.com/Kenji-NX/SDL3-CS/compare/2026.707.0...2026.918.0)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNDkuNSIsInVwZGF0ZWRJblZlciI6IjQzLjI0OS41IiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Kenji-NX/pulls/23
2026-09-18 22:41:02 +00:00
Coxxs 096e054bb3 Implement CreateLibraryAppletEx in ILibraryAppletCreator 2026-09-18 00:11:05 -05:00
KeatonTheBot 0f5aea82a0 Raise application pool sizes for DRAM selections (again)
- Fixes Ultracam 20+ (beta) support
2026-09-18 00:11:05 -05:00
Neo 1385c60875 HLE: Update ProcessResult in ProcessLoader.cs
Some games/applications may crash with a specific error:

00:00:00.690 |E| Application : Unhandled exception caught: System.Collections.Generic.KeyNotFoundException: The given key '0' was not present in the dictionary.

(or a different key)

However, this message is misleading and not indicative of the actual error (it masks it).

As an example, the error above was seen when attempting to load a mod onto Yo-kai Watch 1 (base game + update worked). However, after patching the ProcessResult, the resulting error was more revealing:

00:00:00.716 |E| Application : Unhandled exception caught: LibHac.Common.HorizonResultException: ResultFsInvalidCharacter (2002-6004): Error creating LocalFileSystem. at LibHac.FsSystem.LocalFileSystem..ctor(String rootPath)

(and so forth)

The actual error was due to an incorrect folder name in the Mods folder.
2026-09-17 15:18:48 -05:00
65 changed files with 1743 additions and 1545 deletions
+1 -1
View File
@@ -38,7 +38,7 @@
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.4-build6" /> <PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies.Windows" Version="6.1.4-build6" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.3-ryujinx.1" /> <PackageVersion Include="Ryujinx.Graphics.Vulkan.MoltenVK" Version="1.4.3-ryujinx.1" />
<PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" /> <PackageVersion Include="Ryujinx.LibHac" Version="0.21.0-alpha.133" />
<PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.707.0" /> <PackageVersion Include="Ryujinx.SDL3-CS" Version="2026.918.0" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" /> <PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.2" /> <PackageVersion Include="SharpZipLib" Version="1.4.2" />
<PackageVersion Include="Silk.NET.Shaderc" Version="2.23.0" /> <PackageVersion Include="Silk.NET.Shaderc" Version="2.23.0" />
@@ -413,10 +413,6 @@ namespace ARMeilleure.Translation
context.CurrOp = opCode; context.CurrOp = opCode;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand dispAddressAddr = context.Add(nativeContext, Const((ulong)NativeContext.GetDispatchAddressOffset()));
context.Store(dispAddressAddr, Const(opCode.Address));
bool isLastOp = opcIndex == block.OpCodes.Count - 1; bool isLastOp = opcIndex == block.OpCodes.Count - 1;
if (isLastOp) if (isLastOp)
@@ -8,7 +8,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32
{ {
static class Decoder<T> where T : IInstEmit static class Decoder<T> where T : IInstEmit
{ {
public static MultiBlock DecodeMulti(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, bool singleBlock, bool isThumb) public static MultiBlock DecodeMulti(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, bool isThumb)
{ {
List<Block> blocks = []; List<Block> blocks = [];
List<ulong> branchTargets = []; List<ulong> branchTargets = [];
@@ -24,7 +24,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32
blocks.Add(block); blocks.Add(block);
if (block.IsTruncated || (singleBlock && block.EndsWithBranch) || !HasNextBlock(block, block.EndAddress - 4UL, branchTargets)) if (block.IsTruncated || !HasNextBlock(block, block.EndAddress - 4UL, branchTargets))
{ {
break; break;
} }
@@ -227,7 +227,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb) public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb)
{ {
MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, singleBlock: true, isThumb); MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb);
Dictionary<ulong, int> targets = new(); Dictionary<ulong, int> targets = new();
@@ -305,7 +305,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr) public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr)
{ {
MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address, singleBlock: true); MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address);
Dictionary<ulong, int> targets = new(); Dictionary<ulong, int> targets = new();
List<PendingBranch> pendingBranches = []; List<PendingBranch> pendingBranches = [];
@@ -13,7 +13,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
private const uint NzcvFlags = 0xfu << 28; private const uint NzcvFlags = 0xfu << 28;
private const uint CFlag = 0x1u << 29; private const uint CFlag = 0x1u << 29;
public static MultiBlock DecodeMulti(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, bool singleBlock) public static MultiBlock DecodeMulti(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address)
{ {
List<Block> blocks = []; List<Block> blocks = [];
List<ulong> branchTargets = []; List<ulong> branchTargets = [];
@@ -35,7 +35,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
blocks.Add(block); blocks.Add(block);
if (block.IsTruncated || (singleBlock && block.EndsWithBranch) || !HasNextBlock(block, block.EndAddress - 4UL, branchTargets)) if (block.IsTruncated || !HasNextBlock(block, block.EndAddress - 4UL, branchTargets))
{ {
break; break;
} }
+12 -6
View File
@@ -32,7 +32,6 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsGeometryShader; public readonly bool SupportsGeometryShader;
public readonly bool SupportsGeometryShaderPassthrough; public readonly bool SupportsGeometryShaderPassthrough;
public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedback;
public readonly bool SupportsImageBufferPixelAlignment;
public readonly bool SupportsImageLoadFormatted; public readonly bool SupportsImageLoadFormatted;
public readonly bool SupportsLayerVertexTessellation; public readonly bool SupportsLayerVertexTessellation;
public readonly bool SupportsMismatchingViewFormat; public readonly bool SupportsMismatchingViewFormat;
@@ -44,7 +43,6 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsShaderBarrierDivergence; public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64; public readonly bool SupportsShaderFloat64;
public readonly bool SupportsShaderNonUniformIndexing; public readonly bool SupportsShaderNonUniformIndexing;
public readonly bool SupportsTextureBufferPixelAlignment;
public readonly bool SupportsTextureGatherOffsets; public readonly bool SupportsTextureGatherOffsets;
public readonly bool SupportsTextureShadowLod; public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsVertexStoreAndAtomics; public readonly bool SupportsVertexStoreAndAtomics;
@@ -53,6 +51,10 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsViewportSwizzle; public readonly bool SupportsViewportSwizzle;
public readonly bool SupportsIndirectParameters; public readonly bool SupportsIndirectParameters;
public readonly bool SupportsDepthClipControl; public readonly bool SupportsDepthClipControl;
public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsExtendedDynamicState2;
public readonly bool SupportsLogicOpDynamicState;
public readonly bool SupportsPatchControlPointsDynamicState;
public readonly int UniformBufferSetIndex; public readonly int UniformBufferSetIndex;
public readonly int StorageBufferSetIndex; public readonly int StorageBufferSetIndex;
@@ -103,7 +105,6 @@ namespace Ryujinx.Graphics.GAL
bool supportsGeometryShader, bool supportsGeometryShader,
bool supportsGeometryShaderPassthrough, bool supportsGeometryShaderPassthrough,
bool supportsTransformFeedback, bool supportsTransformFeedback,
bool supportsImageBufferPixelAlignment,
bool supportsImageLoadFormatted, bool supportsImageLoadFormatted,
bool supportsLayerVertexTessellation, bool supportsLayerVertexTessellation,
bool supportsMismatchingViewFormat, bool supportsMismatchingViewFormat,
@@ -115,7 +116,6 @@ namespace Ryujinx.Graphics.GAL
bool supportsShaderBarrierDivergence, bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64, bool supportsShaderFloat64,
bool supportsShaderNonUniformIndexing, bool supportsShaderNonUniformIndexing,
bool supportsTextureBufferPixelAlignment,
bool supportsTextureGatherOffsets, bool supportsTextureGatherOffsets,
bool supportsTextureShadowLod, bool supportsTextureShadowLod,
bool supportsVertexStoreAndAtomics, bool supportsVertexStoreAndAtomics,
@@ -124,6 +124,10 @@ namespace Ryujinx.Graphics.GAL
bool supportsViewportSwizzle, bool supportsViewportSwizzle,
bool supportsIndirectParameters, bool supportsIndirectParameters,
bool supportsDepthClipControl, bool supportsDepthClipControl,
bool supportsExtendedDynamicState,
bool supportsExtendedDynamicState2,
bool supportsLogicOpDynamicState,
bool supportsPatchControlPointsDynamicState,
int uniformBufferSetIndex, int uniformBufferSetIndex,
int storageBufferSetIndex, int storageBufferSetIndex,
int textureSetIndex, int textureSetIndex,
@@ -168,7 +172,6 @@ namespace Ryujinx.Graphics.GAL
SupportsGeometryShader = supportsGeometryShader; SupportsGeometryShader = supportsGeometryShader;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough; SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedback = supportsTransformFeedback;
SupportsImageBufferPixelAlignment = supportsImageBufferPixelAlignment;
SupportsImageLoadFormatted = supportsImageLoadFormatted; SupportsImageLoadFormatted = supportsImageLoadFormatted;
SupportsLayerVertexTessellation = supportsLayerVertexTessellation; SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
SupportsMismatchingViewFormat = supportsMismatchingViewFormat; SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
@@ -180,7 +183,6 @@ namespace Ryujinx.Graphics.GAL
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence; SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64; SupportsShaderFloat64 = supportsShaderFloat64;
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing; SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
SupportsTextureBufferPixelAlignment = supportsTextureBufferPixelAlignment;
SupportsTextureGatherOffsets = supportsTextureGatherOffsets; SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
SupportsTextureShadowLod = supportsTextureShadowLod; SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics; SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;
@@ -189,6 +191,10 @@ namespace Ryujinx.Graphics.GAL
SupportsViewportSwizzle = supportsViewportSwizzle; SupportsViewportSwizzle = supportsViewportSwizzle;
SupportsIndirectParameters = supportsIndirectParameters; SupportsIndirectParameters = supportsIndirectParameters;
SupportsDepthClipControl = supportsDepthClipControl; SupportsDepthClipControl = supportsDepthClipControl;
SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
SupportsLogicOpDynamicState = supportsLogicOpDynamicState;
SupportsPatchControlPointsDynamicState = supportsPatchControlPointsDynamicState;
UniformBufferSetIndex = uniformBufferSetIndex; UniformBufferSetIndex = uniformBufferSetIndex;
StorageBufferSetIndex = storageBufferSetIndex; StorageBufferSetIndex = storageBufferSetIndex;
TextureSetIndex = textureSetIndex; TextureSetIndex = textureSetIndex;
+1
View File
@@ -2,6 +2,7 @@ namespace Ryujinx.Graphics.GAL
{ {
public enum Face public enum Face
{ {
None = 0,
Front = 0x404, Front = 0x404,
Back = 0x405, Back = 0x405,
FrontAndBack = 0x408, FrontAndBack = 0x408,
+7 -7
View File
@@ -50,9 +50,9 @@ namespace Ryujinx.Graphics.GAL
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
void SetDepthClamp(bool clamp); void SetDepthClamp(bool clamp);
void SetDepthMode(DepthMode mode); void SetDepthMode(DepthMode mode);
void SetDepthTest(DepthTestDescriptor depthTest); void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true);
void SetFaceCulling(bool enable, Face face); void SetFaceCulling(Face face);
void SetFrontFace(FrontFace frontFace); void SetFrontFace(FrontFace frontFace);
@@ -75,16 +75,16 @@ namespace Ryujinx.Graphics.GAL
void SetPrimitiveRestart(bool enable, int index); void SetPrimitiveRestart(bool enable, int index);
void SetPrimitiveTopology(PrimitiveTopology topology); void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true);
void SetProgram(IProgram program); void SetProgram(IProgram program, bool signalChange = true);
void SetRasterizerDiscard(bool discard); void SetRasterizerDiscard(bool discard);
void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask); void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true);
void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil); void SetRenderTargets(Span<ITexture> colors, ITexture depthStencil);
void SetScissors(ReadOnlySpan<Rectangle<int>> regions); void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true);
void SetStencilTest(StencilTestDescriptor stencilTest); void SetStencilTest(StencilTestDescriptor stencilTest);
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.GAL
void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs); void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs);
void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers); void SetVertexBuffers(ReadOnlySpan<VertexBufferDescriptor> vertexBuffers);
void SetViewports(ReadOnlySpan<Viewport> viewports); void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true);
void TextureBarrier(); void TextureBarrier();
void TextureBarrierTiled(); void TextureBarrierTiled();
@@ -3,18 +3,16 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand> struct SetFaceCullingCommand : IGALCommand, IGALCommand<SetFaceCullingCommand>
{ {
public readonly CommandType CommandType => CommandType.SetFaceCulling; public readonly CommandType CommandType => CommandType.SetFaceCulling;
private bool _enable;
private Face _face; private Face _face;
public void Set(bool enable, Face face) public void Set(Face face)
{ {
_enable = enable;
_face = face; _face = face;
} }
public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer) public static void Run(ref SetFaceCullingCommand command, ThreadedRenderer threaded, IRenderer renderer)
{ {
renderer.Pipeline.SetFaceCulling(command._enable, command._face); renderer.Pipeline.SetFaceCulling(command._face);
} }
} }
} }
@@ -158,15 +158,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public unsafe void SetDepthTest(DepthTestDescriptor depthTest) public unsafe void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
{ {
_renderer.New<SetDepthTestCommand>()->Set(depthTest); _renderer.New<SetDepthTestCommand>()->Set(depthTest);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public unsafe void SetFaceCulling(bool enable, Face face) public unsafe void SetFaceCulling(Face face)
{ {
_renderer.New<SetFaceCullingCommand>()->Set(enable, face); _renderer.New<SetFaceCullingCommand>()->Set(face);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
@@ -242,13 +242,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public unsafe void SetPrimitiveTopology(PrimitiveTopology topology) public unsafe void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
{ {
_renderer.New<SetPrimitiveTopologyCommand>()->Set(topology); _renderer.New<SetPrimitiveTopologyCommand>()->Set(topology);
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public unsafe void SetProgram(IProgram program) public unsafe void SetProgram(IProgram program, bool signalChange = true)
{ {
_renderer.New<SetProgramCommand>()->Set(Ref(program)); _renderer.New<SetProgramCommand>()->Set(Ref(program));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -260,7 +260,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public unsafe void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask) public unsafe void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true)
{ {
_renderer.New<SetRenderTargetColorMasksCommand>()->Set(_renderer.CopySpan(componentMask)); _renderer.New<SetRenderTargetColorMasksCommand>()->Set(_renderer.CopySpan(componentMask));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -275,7 +275,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public unsafe void SetScissors(ReadOnlySpan<Rectangle<int>> scissors) public unsafe void SetScissors(ReadOnlySpan<Rectangle<int>> scissors, bool signalChange = true)
{ {
_renderer.New<SetScissorsCommand>()->Set(_renderer.CopySpan(scissors)); _renderer.New<SetScissorsCommand>()->Set(_renderer.CopySpan(scissors));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -341,7 +341,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _renderer.QueueCommand();
} }
public unsafe void SetViewports(ReadOnlySpan<Viewport> viewports) public unsafe void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
{ {
_renderer.New<SetViewportsCommand>()->Set(_renderer.CopySpan(viewports)); _renderer.New<SetViewportsCommand>()->Set(_renderer.CopySpan(viewports));
_renderer.QueueCommand(); _renderer.QueueCommand();
@@ -51,11 +51,12 @@ namespace Ryujinx.Graphics.GAL
public StencilTestDescriptor StencilTest; public StencilTestDescriptor StencilTest;
public FrontFace FrontFace; public FrontFace FrontFace;
public Face CullMode; public Face CullMode;
public bool CullEnable;
public PolygonModeMask BiasEnable; public PolygonModeMask BiasEnable;
public float LineWidth; public bool AlphaToCoverageEnable;
public bool AlphaToOneEnable;
// TODO: Polygon mode. // TODO: Polygon mode.
public bool DepthClampEnable; public bool DepthClampEnable;
public bool RasterizerDiscard; public bool RasterizerDiscard;
@@ -63,6 +64,9 @@ namespace Ryujinx.Graphics.GAL
public bool PrimitiveRestartEnable; public bool PrimitiveRestartEnable;
public uint PatchControlPoints; public uint PatchControlPoints;
public float DepthBiasUnits;
public float DepthBiasFactor;
public DepthMode DepthMode; public DepthMode DepthMode;
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs) public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
@@ -466,7 +466,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex)); engine.UpdateState(ulong.MaxValue & ~(1UL << StateUpdater.ShaderStateIndex));
_channel.TextureManager.SignalRenderTargetsModifiable();
_channel.TextureManager.UpdateRenderTargets(); _channel.TextureManager.UpdateRenderTargets();
int textureId = _state.State.DrawTextureTextureId; int textureId = _state.State.DrawTextureTextureId;
@@ -804,7 +803,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int index = (argument >> 6) & 0xf; int index = (argument >> 6) & 0xf;
int layer = (argument >> 10) & 0x3ff; int layer = (argument >> 10) & 0x3ff;
RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor | RenderTargetUpdateFlags.ForClear; RenderTargetUpdateFlags updateFlags = RenderTargetUpdateFlags.SingleColor;
if (layer != 0 || layerCount > 1) if (layer != 0 || layerCount > 1)
{ {
@@ -38,11 +38,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
DiscardClip = 1 << 4, DiscardClip = 1 << 4,
/// <summary>
/// Indicates that the render target will be used for a clear operation.
/// </summary>
ForClear = 1 << 5,
/// <summary> /// <summary>
/// Default update flags for draw. /// Default update flags for draw.
/// </summary> /// </summary>
@@ -45,7 +45,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ProgramPipelineState _pipeline; private ProgramPipelineState _pipeline;
private bool _fsReadsFragCoord; private bool _fsReadsFragCoord;
private bool _fsAlwaysDiscards;
private bool _vsUsesDrawParameters; private bool _vsUsesDrawParameters;
private bool _vtgWritesRtLayer; private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten; private byte _vsClipDistancesWritten;
@@ -492,8 +491,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Span<RtColorState> rtColorStateSpan = _state.State.RtColorState.AsSpan(); Span<RtColorState> rtColorStateSpan = _state.State.RtColorState.AsSpan();
bool rtModifiable = updateFlags.HasFlag(RenderTargetUpdateFlags.ForClear) || !_fsAlwaysDiscards;
for (int index = 0; index < Constants.TotalRenderTargets; index++) for (int index = 0; index < Constants.TotalRenderTargets; index++)
{ {
int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index; int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
@@ -502,7 +499,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse)) if (index >= count || !IsRtEnabled(colorState) || (singleColor && index != singleUse))
{ {
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null, rtModifiable); changedScale |= _channel.TextureManager.SetRenderTargetColor(index, null);
continue; continue;
} }
@@ -521,7 +518,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
samplesInY, samplesInY,
sizeHint); sizeHint);
changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color, rtModifiable); changedScale |= _channel.TextureManager.SetRenderTargetColor(index, color);
if (color != null) if (color != null)
{ {
@@ -575,7 +572,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
} }
changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil, rtModifiable); changedScale |= _channel.TextureManager.SetRenderTargetDepthStencil(depthStencil);
if (changedScale) if (changedScale)
{ {
@@ -777,11 +774,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
float width = scaleX * 2; float width = scaleX * 2;
float height = scaleY * 2; float height = scaleY * 2;
if (yNegate)
{
y += _state.State.ScreenScissorState.Height - MathF.Abs(height);
}
float scale = _channel.TextureManager.RenderTargetScale; float scale = _channel.TextureManager.RenderTargetScale;
if (scale != 1f) if (scale != 1f)
{ {
@@ -869,6 +861,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0); enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
_pipeline.BiasEnable = enables; _pipeline.BiasEnable = enables;
_pipeline.DepthBiasUnits = units / 2f;
_pipeline.DepthBiasFactor = factor;
_context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp); _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp);
} }
@@ -1042,7 +1037,6 @@ 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);
} }
@@ -1224,9 +1218,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
YControl yControl = _state.State.YControl; YControl yControl = _state.State.YControl;
FaceState face = _state.State.FaceState; FaceState face = _state.State.FaceState;
_pipeline.CullEnable = face.CullEnable; if (face.CullEnable)
_pipeline.CullMode = face.CullFace; {
_context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace); _pipeline.CullMode = face.CullFace;
_context.Renderer.Pipeline.SetFaceCulling(face.CullFace);
}
else
{
_pipeline.CullMode = Face.None;
_context.Renderer.Pipeline.SetFaceCulling(Face.None);
}
UpdateFrontFace(yControl, face.FrontFace); UpdateFrontFace(yControl, face.FrontFace);
} }
@@ -1424,6 +1425,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0; bool alphaToCoverageEnable = (_state.State.MultisampleControl & 1) != 0;
bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0; bool alphaToOneEnable = (_state.State.MultisampleControl & 0x10) != 0;
_pipeline.AlphaToCoverageEnable = alphaToCoverageEnable;
_pipeline.AlphaToOneEnable = alphaToOneEnable;
_context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor( _context.Renderer.Pipeline.SetMultisampleState(new MultisampleDescriptor(
alphaToCoverageEnable, alphaToCoverageEnable,
_state.State.AlphaToCoverageDitherEnable, _state.State.AlphaToCoverageDitherEnable,
@@ -1525,38 +1528,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_currentProgramInfo[stageIndex] = info; _currentProgramInfo[stageIndex] = info;
} }
_fsReadsFragCoord = false; if (gs.Shaders[5]?.Info.UsesFragCoord == true)
ShaderProgramInfo fragmentShaderInfo = gs.Shaders[5]?.Info;
if (fragmentShaderInfo != null)
{ {
if (fragmentShaderInfo.UsesFragCoord) // Make sure we update the viewport size on the support buffer if it will be consumed on the new shader.
if (!_fsReadsFragCoord && (_state.State.YControl & YControl.NegateY) != 0)
{ {
// Make sure we update the viewport size on the support buffer if it will be consumed on the new shader. UpdateSupportBufferViewportSize();
if (!_fsReadsFragCoord && (_state.State.YControl & YControl.NegateY) != 0)
{
UpdateSupportBufferViewportSize();
}
_fsReadsFragCoord = true;
} }
if (_fsAlwaysDiscards != fragmentShaderInfo.HasUnconditionalDiscard) _fsReadsFragCoord = true;
{
_fsAlwaysDiscards = fragmentShaderInfo.HasUnconditionalDiscard;
if (!_fsAlwaysDiscards)
{
_channel.TextureManager.RefreshModifiedTextures();
_channel.TextureManager.SignalRenderTargetsModifiable();
}
}
} }
else else
{ {
_fsAlwaysDiscards = false; _fsReadsFragCoord = false;
} }
if (gs.VertexAsCompute != null) if (gs.VertexAsCompute != null)
+5 -9
View File
@@ -1572,6 +1572,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param> /// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
public void SignalModifying(bool bound) public void SignalModifying(bool bound)
{ {
if (bound)
{
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
}
if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer) if (_modifiedStale || Group.HasCopyDependencies || Group.HasFlushBuffer)
{ {
_modifiedStale = false; _modifiedStale = false;
@@ -1579,18 +1584,9 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
_physicalMemory.TextureCache.Lift(this); _physicalMemory.TextureCache.Lift(this);
}
/// <summary>
/// Signals that a render target texture has been either bound or unbound.
/// </summary>
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
public void SignalBindingChange(bool bound)
{
if (bound) if (bound)
{ {
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
IncrementReferenceCount(); IncrementReferenceCount();
} }
else else
@@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Ryujinx.Memory.Range;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -68,9 +67,6 @@ namespace Ryujinx.Graphics.Gpu.Image
private int _lastFragmentTotal; private int _lastFragmentTotal;
private readonly int _bufferTextureAlignment;
private readonly int _bufferImageAlignment;
/// <summary> /// <summary>
/// Constructs a new instance of the texture bindings manager. /// Constructs a new instance of the texture bindings manager.
/// </summary> /// </summary>
@@ -112,10 +108,6 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
_textureCounts = []; _textureCounts = [];
int alignment = context.Capabilities.TextureBufferOffsetAlignment;
_bufferTextureAlignment = context.Capabilities.SupportsTextureBufferPixelAlignment ? 0 : alignment;
_bufferImageAlignment = context.Capabilities.SupportsImageBufferPixelAlignment ? 0 : alignment;
} }
/// <summary> /// <summary>
@@ -529,12 +521,10 @@ namespace Ryujinx.Graphics.Gpu.Image
if (hostTexture != null && texture.Target == Target.TextureBuffer) if (hostTexture != null && texture.Target == Target.TextureBuffer)
{ {
MultiRange range = GetAlignedBufferTextureRange(texture, index, stageIndex, false);
// Ensure that the buffer texture is using the correct buffer as storage. // Ensure that the buffer texture is using the correct buffer as storage.
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind // Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted. // to ensure we're not using a old buffer that was already deleted.
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, range, bindingInfo, false); _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, false);
// Cache is not used for buffer texture, it must always rebind. // Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null; state.CachedTexture = null;
@@ -669,9 +659,7 @@ namespace Ryujinx.Graphics.Gpu.Image
// Buffers are frequently re-created to accommodate larger data, so we need to re-bind // Buffers are frequently re-created to accommodate larger data, so we need to re-bind
// to ensure we're not using a old buffer that was already deleted. // to ensure we're not using a old buffer that was already deleted.
MultiRange range = GetAlignedBufferTextureRange(texture, index, stageIndex, true); _channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, texture.Range, bindingInfo, true);
_channel.BufferManager.SetBufferTextureStorage(stage, hostTexture, range, bindingInfo, true);
// Cache is not used for buffer texture, it must always rebind. // Cache is not used for buffer texture, it must always rebind.
state.CachedTexture = null; state.CachedTexture = null;
@@ -704,61 +692,6 @@ namespace Ryujinx.Graphics.Gpu.Image
return specStateMatches; return specStateMatches;
} }
/// <summary>
/// Gets the aligned address of the texture according to the host requirements.
/// </summary>
/// <param name="texture">Texture to have its address aligned</param>
/// <param name="index">Index of the texture in the shader</param>
/// <param name="stageIndex">Index of the shader stage</param>
/// <param name="isImage">True if the texture is bound as image, false for sampled textures</param>
/// <returns>Aligned address and size of the buffer texture</returns>
private MultiRange GetAlignedBufferTextureRange(Texture texture, int index, int stageIndex, bool isImage)
{
MultiRange range = texture.Range;
int alignment = isImage ? _bufferImageAlignment : _bufferTextureAlignment;
if (alignment != 0)
{
MemoryRange firstRange = range.GetSubRange(0);
ulong misalign = firstRange.Address & ((ulong)alignment - 1);
int offset = misalign != 0 ? (int)misalign / texture.Info.FormatInfo.BytesPerPixel : 0;
if (misalign != 0)
{
firstRange = new MemoryRange(firstRange.Address - misalign, firstRange.Size + misalign);
if (range.Count > 1)
{
MemoryRange[] ranges = new MemoryRange[range.Count];
ranges[0] = firstRange;
for (int i = 1; i < range.Count; i++)
{
ranges[i] = range.GetSubRange(i);
}
range = new MultiRange(ranges);
}
else
{
range = new MultiRange(firstRange.Address, firstRange.Size);
}
}
if (isImage)
{
_context.SupportBufferUpdater.UpdateBufferImageOffset(stageIndex, index, offset);
}
else
{
_context.SupportBufferUpdater.UpdateBufferTextureOffset(stageIndex, index, offset);
}
}
return range;
}
/// <summary> /// <summary>
/// Gets the texture descriptor for a given texture handle. /// Gets the texture descriptor for a given texture handle.
/// </summary> /// </summary>
@@ -19,39 +19,12 @@ namespace Ryujinx.Graphics.Gpu.Image
private readonly TexturePoolCache _texturePoolCache; private readonly TexturePoolCache _texturePoolCache;
private readonly SamplerPoolCache _samplerPoolCache; private readonly SamplerPoolCache _samplerPoolCache;
/// <summary>
/// Bound render target texture modification report state.
/// </summary>
[Flags]
private enum BindState : byte
{
/// <summary>
/// Render target texture has not been signalled for modification, and can't be modified on the next render operations.
/// </summary>
None = 0,
/// <summary>
/// Render target texture has been signalled for modification.
/// </summary>
Bound = 1 << 0,
/// <summary>
/// Render target texture might be modified on the next render operations.
/// </summary>
Modified = 1 << 1,
/// <summary>
/// Render target texture has been signalled for modification and might be modified on the next render operations.
/// </summary>
BoundModified = Bound | Modified,
}
private readonly Texture[] _rtColors; private readonly Texture[] _rtColors;
private readonly ITexture[] _rtHostColors; private readonly ITexture[] _rtHostColors;
private readonly BindState[] _rtColorsBound; private readonly bool[] _rtColorsBound;
private Texture _rtDepthStencil; private Texture _rtDepthStencil;
private ITexture _rtHostDs; private ITexture _rtHostDs;
private BindState _rtDsBound; private bool _rtDsBound;
public int ClipRegionWidth { get; private set; } public int ClipRegionWidth { get; private set; }
public int ClipRegionHeight { get; private set; } public int ClipRegionHeight { get; private set; }
@@ -82,7 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Image
_rtColors = new Texture[Constants.TotalRenderTargets]; _rtColors = new Texture[Constants.TotalRenderTargets];
_rtHostColors = new ITexture[Constants.TotalRenderTargets]; _rtHostColors = new ITexture[Constants.TotalRenderTargets];
_rtColorsBound = new BindState[Constants.TotalRenderTargets]; _rtColorsBound = new bool[Constants.TotalRenderTargets];
} }
/// <summary> /// <summary>
@@ -178,39 +151,27 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
/// <param name="index">The index of the color buffer to set (up to 8)</param> /// <param name="index">The index of the color buffer to set (up to 8)</param>
/// <param name="color">The color buffer texture</param> /// <param name="color">The color buffer texture</param>
/// <param name="modified">Indicates if the following render operations will modidify <paramref name="color"/> contents</param>
/// <returns>True if render target scale must be updated.</returns> /// <returns>True if render target scale must be updated.</returns>
public bool SetRenderTargetColor(int index, Texture color, bool modified) public bool SetRenderTargetColor(int index, Texture color)
{ {
bool hasValue = color != null; bool hasValue = color != null;
bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor); bool changesScale = (hasValue != (_rtColors[index] != null)) || (hasValue && RenderTargetScale != color.ScaleFactor);
if (_rtColors[index] != color) if (_rtColors[index] != color)
{ {
Texture oldColor = _rtColors[index]; if (_rtColorsBound[index])
if (oldColor != null)
{ {
if (_rtColorsBound[index].HasFlag(BindState.Bound)) _rtColors[index]?.SignalModifying(false);
{ }
oldColor.SignalModifying(false); else
} {
_rtColorsBound[index] = true;
oldColor.SignalBindingChange(false);
} }
_rtColorsBound[index] = modified ? BindState.BoundModified : BindState.None;
if (color != null) if (color != null)
{ {
color.SynchronizeMemory(); color.SynchronizeMemory();
color.SignalModifying(true);
if (modified)
{
color.SignalModifying(true);
}
color.SignalBindingChange(true);
} }
_rtColors[index] = color; _rtColors[index] = color;
@@ -223,39 +184,27 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Sets the render target depth-stencil buffer. /// Sets the render target depth-stencil buffer.
/// </summary> /// </summary>
/// <param name="depthStencil">The depth-stencil buffer texture</param> /// <param name="depthStencil">The depth-stencil buffer texture</param>
/// <param name="modified">Indicates if the following render operations will modidify <paramref name="depthStencil"/> contents</param>
/// <returns>True if render target scale must be updated.</returns> /// <returns>True if render target scale must be updated.</returns>
public bool SetRenderTargetDepthStencil(Texture depthStencil, bool modified) public bool SetRenderTargetDepthStencil(Texture depthStencil)
{ {
bool hasValue = depthStencil != null; bool hasValue = depthStencil != null;
bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor); bool changesScale = (hasValue != (_rtDepthStencil != null)) || (hasValue && RenderTargetScale != depthStencil.ScaleFactor);
if (_rtDepthStencil != depthStencil) if (_rtDepthStencil != depthStencil)
{ {
Texture oldDepthStencil = _rtDepthStencil; if (_rtDsBound)
if (oldDepthStencil != null)
{ {
if (_rtDsBound.HasFlag(BindState.Bound)) _rtDepthStencil?.SignalModifying(false);
{ }
oldDepthStencil.SignalModifying(false); else
} {
_rtDsBound = true;
oldDepthStencil.SignalBindingChange(false);
} }
_rtDsBound = modified ? BindState.BoundModified : BindState.None;
if (depthStencil != null) if (depthStencil != null)
{ {
depthStencil.SynchronizeMemory(); depthStencil.SynchronizeMemory();
depthStencil.SignalModifying(true);
if (modified)
{
depthStencil.SignalModifying(true);
}
depthStencil.SignalBindingChange(true);
} }
_rtDepthStencil = depthStencil; _rtDepthStencil = depthStencil;
@@ -494,10 +443,10 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
hostDsTexture = dsTexture.HostTexture; hostDsTexture = dsTexture.HostTexture;
if (_rtDsBound == BindState.Modified) if (!_rtDsBound)
{ {
dsTexture.SignalModifying(true); dsTexture.SignalModifying(true);
_rtDsBound |= BindState.Bound; _rtDsBound = true;
} }
} }
@@ -521,10 +470,10 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
hostTexture = texture.HostTexture; hostTexture = texture.HostTexture;
if (_rtColorsBound[index] == BindState.Modified) if (!_rtColorsBound[index])
{ {
texture.SignalModifying(true); texture.SignalModifying(true);
_rtColorsBound[index] |= BindState.Bound; _rtColorsBound[index] = true;
} }
} }
@@ -569,37 +518,24 @@ namespace Ryujinx.Graphics.Gpu.Image
{ {
Texture dsTexture = _rtDepthStencil; Texture dsTexture = _rtDepthStencil;
if (dsTexture != null && _rtDsBound.HasFlag(BindState.Bound)) if (dsTexture != null && _rtDsBound)
{ {
dsTexture.SignalModifying(false); dsTexture.SignalModifying(false);
_rtDsBound &= ~BindState.Bound; _rtDsBound = false;
} }
for (int index = 0; index < _rtColors.Length; index++) for (int index = 0; index < _rtColors.Length; index++)
{ {
Texture texture = _rtColors[index]; Texture texture = _rtColors[index];
if (texture != null && _rtColorsBound[index].HasFlag(BindState.Bound)) if (texture != null && _rtColorsBound[index])
{ {
texture.SignalModifying(false); texture.SignalModifying(false);
_rtColorsBound[index] &= ~BindState.Bound; _rtColorsBound[index] = false;
} }
} }
} }
/// <summary>
/// Indicates that the currently bound render targets might be modified, if they are used on the next render operation.
/// </summary>
public void SignalRenderTargetsModifiable()
{
_rtDsBound |= BindState.Modified;
for (int index = 0; index < _rtColorsBound.Length; index++)
{
_rtColorsBound[index] |= BindState.Modified;
}
}
/// <summary> /// <summary>
/// Forces the texture and sampler pools to be re-loaded from the cache on next use. /// Forces the texture and sampler pools to be re-loaded from the cache on next use.
/// </summary> /// </summary>
@@ -636,11 +572,19 @@ namespace Ryujinx.Graphics.Gpu.Image
for (int i = 0; i < _rtColors.Length; i++) for (int i = 0; i < _rtColors.Length; i++)
{ {
_rtColors[i]?.DecrementReferenceCount(); if (_rtColorsBound[i])
{
_rtColors[i]?.DecrementReferenceCount();
}
_rtColors[i] = null; _rtColors[i] = null;
} }
_rtDepthStencil?.DecrementReferenceCount(); if (_rtDsBound)
{
_rtDepthStencil?.DecrementReferenceCount();
}
_rtDepthStencil = null; _rtDepthStencil = null;
} }
} }
@@ -129,38 +129,6 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
} }
/// <summary>
/// Updates the offset used for accessing buffer textures that are not aligned to the host requirements.
/// </summary>
/// <param name="stageIndex">Index of the shader stage where the texture is used</param>
/// <param name="bindingIndex">Index of the texture binding in the shader</param>
/// <param name="offset">Offset for the misaligned part of the address</param>
public void UpdateBufferTextureOffset(int stageIndex, int bindingIndex, int offset)
{
if (_data.BufferTextureOffset[stageIndex][bindingIndex].X != offset)
{
_data.BufferTextureOffset[stageIndex][bindingIndex].X = offset;
int index = stageIndex * SupportBuffer.TextureCount + bindingIndex;
MarkDirty(SupportBuffer.BufferTextureOffsetOffset + index * sizeof(int) * 4, sizeof(int));
}
}
/// <summary>
/// Updates the offset used for accessing buffer images that are not aligned to the host requirements.
/// </summary>
/// <param name="stageIndex">Index of the shader stage where the image is used</param>
/// <param name="bindingIndex">Index of the image binding in the shader</param>
/// <param name="offset">Offset for the misaligned part of the address</param>
public void UpdateBufferImageOffset(int stageIndex, int bindingIndex, int offset)
{
if (_data.BufferTextureOffset[stageIndex][bindingIndex].Y != offset)
{
_data.BufferTextureOffset[stageIndex][bindingIndex].Y = offset;
int index = stageIndex * SupportBuffer.TextureCount + bindingIndex;
MarkDirty(SupportBuffer.BufferTextureOffsetOffset + index * sizeof(int) * 4 + sizeof(int), sizeof(int));
}
}
/// <summary> /// <summary>
/// Sets whether the format of a given render target is a BGRA format. /// Sets whether the format of a given render target is a BGRA format.
/// </summary> /// </summary>
@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 6371; private const uint CodeGenVersion = 7354;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";
@@ -170,11 +170,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// </summary> /// </summary>
public bool UsesRtLayer; public bool UsesRtLayer;
/// <summary>
/// Indicates that the fragment shader always discards the fragment, not producing any output for the bound render targets.
/// </summary>
public bool HasUnconditionalDiscard;
/// <summary> /// <summary>
/// Bit mask with the clip distances written on the vertex stage. /// Bit mask with the clip distances written on the vertex stage.
/// </summary> /// </summary>
@@ -811,7 +806,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
dataInfo.UsesInstanceId, dataInfo.UsesInstanceId,
dataInfo.UsesDrawParameters, dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer, dataInfo.UsesRtLayer,
dataInfo.HasUnconditionalDiscard,
dataInfo.ClipDistancesWritten, dataInfo.ClipDistancesWritten,
dataInfo.FragmentOutputMap); dataInfo.FragmentOutputMap);
} }
@@ -842,7 +836,6 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
UsesInstanceId = info.UsesInstanceId, UsesInstanceId = info.UsesInstanceId,
UsesDrawParameters = info.UsesDrawParameters, UsesDrawParameters = info.UsesDrawParameters,
UsesRtLayer = info.UsesRtLayer, UsesRtLayer = info.UsesRtLayer,
HasUnconditionalDiscard = info.HasUnconditionalDiscard,
ClipDistancesWritten = info.ClipDistancesWritten, ClipDistancesWritten = info.ClipDistancesWritten,
FragmentOutputMap = info.FragmentOutputMap, FragmentOutputMap = info.FragmentOutputMap,
}; };
@@ -22,6 +22,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private readonly CancellationToken _cancellationToken; private readonly CancellationToken _cancellationToken;
private readonly Action<ShaderCacheState, int, int> _stateChangeCallback; private readonly Action<ShaderCacheState, int, int> _stateChangeCallback;
private readonly HashSet<ProgramPipelineState> _pipelineStateSet = new();
/// <summary> /// <summary>
/// Indicates if the cache should be loaded. /// Indicates if the cache should be loaded.
/// </summary> /// </summary>
@@ -232,10 +234,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
for (int index = 0; index < ThreadCount; index++) for (int index = 0; index < ThreadCount; index++)
{ {
workThreads[index] = new Thread(ProcessAsyncQueue) workThreads[index] = new Thread(ProcessAsyncQueue) { Name = $"GPU.AsyncTranslationThread.{index}", };
{
Name = $"GPU.AsyncTranslationThread.{index}",
};
} }
int programCount = _hostStorage.GetProgramCount(); int programCount = _hostStorage.GetProgramCount();
@@ -305,6 +304,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
using DiskCacheOutputStreams streams = _hostStorage.GetOutputStreams(_context); using DiskCacheOutputStreams streams = _hostStorage.GetOutputStreams(_context);
int packagedShaders = 0; int packagedShaders = 0;
ProgramPipelineState currentPipelineState = default;
foreach (KeyValuePair<int, (CachedShaderProgram, byte[])> kv in _programList) foreach (KeyValuePair<int, (CachedShaderProgram, byte[])> kv in _programList)
{ {
if (!Active) if (!Active)
@@ -314,12 +315,53 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
(CachedShaderProgram program, byte[] binaryCode) = kv.Value; (CachedShaderProgram program, byte[] binaryCode) = kv.Value;
_hostStorage.AddShader(_context, program, binaryCode, streams); if (program.SpecializationState.PipelineState.HasValue && _context.Capabilities.SupportsExtendedDynamicState)
{
currentPipelineState = program.SpecializationState.PipelineState.Value;
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count); if (_context.Capabilities.SupportsExtendedDynamicState)
{
currentPipelineState.StencilTest = default;
currentPipelineState.CullMode = 0;
currentPipelineState.FrontFace = 0;
currentPipelineState.DepthTest = default;
currentPipelineState.Topology = ConvertToClass(currentPipelineState.Topology);
}
if (_context.Capabilities.SupportsExtendedDynamicState2)
{
currentPipelineState.PrimitiveRestartEnable = false;
currentPipelineState.BiasEnable = 0;
currentPipelineState.RasterizerDiscard = false;
}
if (_context.Capabilities.SupportsLogicOpDynamicState)
{
currentPipelineState.LogicOp = 0;
}
if (_context.Capabilities.SupportsPatchControlPointsDynamicState)
{
currentPipelineState.PatchControlPoints = 0;
}
}
if (!_pipelineStateSet.Contains(currentPipelineState) ||
!_context.Capabilities.SupportsExtendedDynamicState ||
!program.SpecializationState.PipelineState.HasValue)
{
_hostStorage.AddShader(_context, program, binaryCode, streams);
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
if (_context.Capabilities.SupportsExtendedDynamicState)
{
_pipelineStateSet.Add(currentPipelineState);
}
}
} }
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {packagedShaders} shaders successfully.");
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");
} }
else else
{ {
@@ -343,6 +385,29 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
_stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount); _stateChangeCallback(ShaderCacheState.Loaded, programCount, programCount);
} }
private PrimitiveTopology ConvertToClass(PrimitiveTopology topology)
{
return topology switch
{
PrimitiveTopology.Points => PrimitiveTopology.Points,
PrimitiveTopology.Lines or
PrimitiveTopology.LineStrip or
PrimitiveTopology.LinesAdjacency or
PrimitiveTopology.LineStripAdjacency => PrimitiveTopology.Lines,
PrimitiveTopology.Triangles or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan or
PrimitiveTopology.TrianglesAdjacency or
PrimitiveTopology.TriangleStripAdjacency or
PrimitiveTopology.Polygon => PrimitiveTopology.TriangleStrip,
PrimitiveTopology.Patches => PrimitiveTopology.Patches,
PrimitiveTopology.Quads => PrimitiveTopology.Quads,
PrimitiveTopology.QuadStrip => PrimitiveTopology.QuadStrip,
PrimitiveTopology.LineLoop => PrimitiveTopology.LineLoop,
_ => PrimitiveTopology.TriangleStrip,
};
}
/// <summary> /// <summary>
/// Enqueues a host program for compilation. /// Enqueues a host program for compilation.
/// </summary> /// </summary>
@@ -207,10 +207,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat; public bool QueryHostSupportsBgraFormat() => _context.Capabilities.SupportsBgraFormat;
public bool QueryHostSupportsBufferImagePixelAlignment() => _context.Capabilities.SupportsImageBufferPixelAlignment;
public bool QueryHostSupportsBufferTexturePixelAlignment() => _context.Capabilities.SupportsTextureBufferPixelAlignment;
public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock; public bool QueryHostSupportsFragmentShaderInterlock() => _context.Capabilities.SupportsFragmentShaderInterlock;
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel; public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
@@ -430,8 +430,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage; TranslatorContext lastInVertexPipeline = geometryToCompute ? translatorContexts[4] ?? currentStage : currentStage;
(program, ShaderProgramInfo vacInfo) = lastInVertexPipeline.GenerateVertexPassthroughForCompute(); program = lastInVertexPipeline.GenerateVertexPassthroughForCompute();
infoBuilder.AddStageInfoVac(vacInfo);
} }
else else
{ {
@@ -536,7 +535,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled) private ShaderAsCompute CreateHostVertexAsComputeProgram(ShaderProgram program, TranslatorContext context, bool tfEnabled)
{ {
ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language); ShaderSource source = new(program.Code, program.BinaryCode, ShaderStage.Compute, program.Language);
ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, context.GetVertexAsComputeInfo(), tfEnabled); ShaderInfo info = ShaderInfoBuilder.BuildForVertexAsCompute(_context, program.Info, tfEnabled);
return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations()); return new(_context.Renderer.CreateProgram([source], info), program.Info, context.GetResourceReservations());
} }
@@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false) private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
{ {
AddDescriptor(stages, type, setIndex, start, count); AddDescriptor(stages, type, setIndex, start, count);
// AddUsage(stages, type, setIndex, start, count, write); AddUsage(stages, type, setIndex, start, count, write);
} }
/// <summary> /// <summary>
@@ -159,25 +159,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
AddUsage(info.Images, stages, isImage: true); AddUsage(info.Images, stages, isImage: true);
} }
public void AddStageInfoVac(ShaderProgramInfo info)
{
ResourceStages stages = info.Stage switch
{
ShaderStage.Compute => ResourceStages.Compute,
ShaderStage.Vertex => ResourceStages.Vertex,
ShaderStage.TessellationControl => ResourceStages.TessellationControl,
ShaderStage.TessellationEvaluation => ResourceStages.TessellationEvaluation,
ShaderStage.Geometry => ResourceStages.Geometry,
ShaderStage.Fragment => ResourceStages.Fragment,
_ => ResourceStages.None,
};
AddUsage(info.CBuffers, stages, isStorage: false);
AddUsage(info.SBuffers, stages, isStorage: true);
AddUsage(info.Textures, stages, isImage: false);
AddUsage(info.Images, stages, isImage: true);
}
/// <summary> /// <summary>
/// Adds a resource descriptor to the list of descriptors. /// Adds a resource descriptor to the list of descriptors.
/// </summary> /// </summary>
@@ -441,11 +422,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param> /// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
/// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param> /// <param name="fromCache">True if the compute shader comes from a disk cache, false otherwise</param>
/// <returns>Shader information</returns> /// <returns>Shader information</returns>
public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, ShaderProgramInfo info2, bool tfEnabled, bool fromCache = false) public static ShaderInfo BuildForVertexAsCompute(GpuContext context, ShaderProgramInfo info, bool tfEnabled, bool fromCache = false)
{ {
ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true); ShaderInfoBuilder builder = new(context, tfEnabled, vertexAsCompute: true);
builder.AddStageInfoVac(info2);
builder.AddStageInfo(info, vertexAsCompute: true); builder.AddStageInfo(info, vertexAsCompute: true);
return builder.Build(null, fromCache); return builder.Build(null, fromCache);
@@ -174,7 +174,6 @@ namespace Ryujinx.Graphics.OpenGL
supportsGeometryShader: true, supportsGeometryShader: true,
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough, supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
supportsTransformFeedback: true, supportsTransformFeedback: true,
supportsImageBufferPixelAlignment: false,
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted, supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray, supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat, supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
@@ -186,7 +185,6 @@ namespace Ryujinx.Graphics.OpenGL
supportsShaderBarrierDivergence: !(intelWindows || intelUnix), supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
supportsShaderFloat64: true, supportsShaderFloat64: true,
supportsShaderNonUniformIndexing: false, supportsShaderNonUniformIndexing: false,
supportsTextureBufferPixelAlignment: false,
supportsTextureGatherOffsets: true, supportsTextureGatherOffsets: true,
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod, supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
supportsVertexStoreAndAtomics: true, supportsVertexStoreAndAtomics: true,
@@ -195,6 +193,10 @@ namespace Ryujinx.Graphics.OpenGL
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle, supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters, supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
supportsDepthClipControl: true, supportsDepthClipControl: true,
supportsExtendedDynamicState: false,
supportsExtendedDynamicState2: false,
supportsLogicOpDynamicState: false,
supportsPatchControlPointsDynamicState: false,
uniformBufferSetIndex: 0, uniformBufferSetIndex: 0,
storageBufferSetIndex: 1, storageBufferSetIndex: 1,
textureSetIndex: 2, textureSetIndex: 2,
+9 -9
View File
@@ -898,7 +898,7 @@ namespace Ryujinx.Graphics.OpenGL
} }
} }
public void SetDepthTest(DepthTestDescriptor depthTest) public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
{ {
if (depthTest.TestEnable) if (depthTest.TestEnable)
{ {
@@ -915,11 +915,11 @@ namespace Ryujinx.Graphics.OpenGL
_depthTestEnable = depthTest.TestEnable; _depthTestEnable = depthTest.TestEnable;
} }
public void SetFaceCulling(bool enable, Face face) public void SetFaceCulling(Face face)
{ {
_cullEnable = enable; _cullEnable = face != Face.None;
if (!enable) if (!_cullEnable)
{ {
GL.Disable(EnableCap.CullFace); GL.Disable(EnableCap.CullFace);
return; return;
@@ -1107,12 +1107,12 @@ namespace Ryujinx.Graphics.OpenGL
GL.Enable(EnableCap.PrimitiveRestart); GL.Enable(EnableCap.PrimitiveRestart);
} }
public void SetPrimitiveTopology(PrimitiveTopology topology) public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
{ {
_primitiveType = topology.Convert(); _primitiveType = topology.Convert();
} }
public void SetProgram(IProgram program) public void SetProgram(IProgram program, bool signalChange = true)
{ {
Program prg = (Program)program; Program prg = (Program)program;
@@ -1154,7 +1154,7 @@ namespace Ryujinx.Graphics.OpenGL
_rasterizerDiscard = discard; _rasterizerDiscard = discard;
} }
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMasks, bool signalChange = true)
{ {
_componentMasks = 0; _componentMasks = 0;
@@ -1195,7 +1195,7 @@ namespace Ryujinx.Graphics.OpenGL
_framebuffer.SetDrawBuffers(colors.Length); _framebuffer.SetDrawBuffers(colors.Length);
} }
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions) public void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true)
{ {
int count = Math.Min(regions.Length, Constants.MaxViewports); int count = Math.Min(regions.Length, Constants.MaxViewports);
@@ -1388,7 +1388,7 @@ namespace Ryujinx.Graphics.OpenGL
_vertexArray.SetVertexBuffers(vertexBuffers); _vertexArray.SetVertexBuffers(vertexBuffers);
} }
public void SetViewports(ReadOnlySpan<Viewport> viewports) public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
{ {
Array.Resize(ref _viewportArray, viewports.Length * 4); Array.Resize(ref _viewportArray, viewports.Length * 4);
Array.Resize(ref _depthRangeArray, viewports.Length * 2); Array.Resize(ref _depthRangeArray, viewports.Length * 2);
@@ -522,7 +522,6 @@ namespace Ryujinx.Graphics.Shader.Decoders
enum PMode enum PMode
{ {
Idx = 0,
F4e = 1, F4e = 1,
B4e = 2, B4e = 2,
Rc8 = 3, Rc8 = 3,
@@ -234,24 +234,6 @@ namespace Ryujinx.Graphics.Shader
return true; return true;
} }
/// <summary>
/// Queries host support for buffer image access with the buffer offset aligned to a single pixel rather than a fixed alignment.
/// </summary>
/// <returns>True if the host supports buffer image access with pixel alignment, false otherwise</returns>
bool QueryHostSupportsBufferImagePixelAlignment()
{
return true;
}
/// <summary>
/// Queries host support for buffer texture access with the buffer offset aligned to a single pixel rather than a fixed alignment.
/// </summary>
/// <returns>True if the host supports buffer texture access with pixel alignment, false otherwise</returns>
bool QueryHostSupportsBufferTexturePixelAlignment()
{
return true;
}
/// <summary> /// <summary>
/// Queries host support for fragment shader ordering critical sections on the shader code. /// Queries host support for fragment shader ordering critical sections on the shader code.
/// </summary> /// </summary>
@@ -215,6 +215,34 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.TranslatorContext.GpuAccessor.Log("Shader instruction Pret is not implemented."); context.TranslatorContext.GpuAccessor.Log("Shader instruction Pret is not implemented.");
} }
public static void PrmtR(EmitterContext context)
{
context.GetOp<InstPrmtR>();
context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtR is not implemented.");
}
public static void PrmtI(EmitterContext context)
{
context.GetOp<InstPrmtI>();
context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtI is not implemented.");
}
public static void PrmtC(EmitterContext context)
{
context.GetOp<InstPrmtC>();
context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtC is not implemented.");
}
public static void PrmtRc(EmitterContext context)
{
context.GetOp<InstPrmtRc>();
context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtRc is not implemented.");
}
public static void R2b(EmitterContext context) public static void R2b(EmitterContext context)
{ {
context.GetOp<InstR2b>(); context.GetOp<InstR2b>();
@@ -1,7 +1,7 @@
using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.Decoders;
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using System;
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -37,38 +37,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Copy(GetDest(op.Dest), GetSrcImm(context, op.Imm32)); context.Copy(GetDest(op.Dest), GetSrcImm(context, op.Imm32));
} }
public static void PrmtR(EmitterContext context)
{
context.GetOp<InstPrmtR>();
context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtR is not implemented.");
}
public static void PrmtI(EmitterContext context)
{
InstPrmtI op = context.GetOp<InstPrmtI>();
Operand op1 = GetSrcReg(context, op.SrcA);
Operand control = GetSrcImm(context, op.Imm20);
Operand op2 = GetSrcReg(context, op.SrcC);
EmitPrmt(context, op1, control, op2, op.PMode, op.Dest);
}
public static void PrmtC(EmitterContext context)
{
context.GetOp<InstPrmtC>();
context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtC is not implemented.");
}
public static void PrmtRc(EmitterContext context)
{
context.GetOp<InstPrmtRc>();
context.TranslatorContext.GpuAccessor.Log("Shader instruction PrmtRc is not implemented.");
}
public static void R2pR(EmitterContext context) public static void R2pR(EmitterContext context)
{ {
InstR2pR op = context.GetOp<InstR2pR>(); InstR2pR op = context.GetOp<InstR2pR>();
@@ -201,39 +169,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
context.Copy(GetDest(op.Dest), src); context.Copy(GetDest(op.Dest), src);
} }
public static void SelR(EmitterContext context)
{
InstSelR op = context.GetOp<InstSelR>();
Operand srcA = GetSrcReg(context, op.SrcA);
Operand srcB = GetSrcReg(context, op.SrcB);
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
EmitSel(context, srcA, srcB, srcPred, op.Dest);
}
public static void SelI(EmitterContext context)
{
InstSelI op = context.GetOp<InstSelI>();
Operand srcA = GetSrcReg(context, op.SrcA);
Operand srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
EmitSel(context, srcA, srcB, srcPred, op.Dest);
}
public static void SelC(EmitterContext context)
{
InstSelC op = context.GetOp<InstSelC>();
Operand srcA = GetSrcReg(context, op.SrcA);
Operand srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
EmitSel(context, srcA, srcB, srcPred, op.Dest);
}
private static Operand EmitLoadSubgroupLaneId(EmitterContext context) private static Operand EmitLoadSubgroupLaneId(EmitterContext context)
{ {
if (context.TranslatorContext.GpuAccessor.QueryHostSubgroupSize() <= 32) if (context.TranslatorContext.GpuAccessor.QueryHostSubgroupSize() <= 32)
@@ -280,34 +215,37 @@ namespace Ryujinx.Graphics.Shader.Instructions
} }
} }
private static void EmitPrmt(EmitterContext context, Operand op1, Operand control, Operand op2, PMode pMode, int rd) public static void SelR(EmitterContext context)
{ {
if (pMode == PMode.Idx) InstSelR op = context.GetOp<InstSelR>();
{
Operand res = Const(0);
for (int b = 0; b < 4; b++) Operand srcA = GetSrcReg(context, op.SrcA);
{ Operand srcB = GetSrcReg(context, op.SrcB);
Operand sel = context.ShiftRightU32(control, Const(b * 4)); Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
Operand byteSel = context.BitwiseAnd(sel, Const(7));
Operand copySign = context.BitwiseAnd(sel, Const(8));
Operand srcOp = context.ConditionalSelect(context.BitwiseAnd(byteSel, Const(4)), op2, op1); EmitSel(context, srcA, srcB, srcPred, op.Dest);
Operand srcValue = context.ShiftRightU32(srcOp, context.ShiftLeft(context.BitwiseAnd(byteSel, Const(3)), Const(3))); }
Operand srcSign = context.ShiftRightS32(context.ShiftLeft(srcValue, Const(24)), Const(31));
srcValue = context.ConditionalSelect(copySign, srcSign, srcValue); public static void SelI(EmitterContext context)
srcValue = context.BitwiseAnd(srcValue, Const(0xff)); {
InstSelI op = context.GetOp<InstSelI>();
res = context.BitfieldInsert(res, srcValue, Const(b * 8), Const(8)); Operand srcA = GetSrcReg(context, op.SrcA);
} Operand srcB = GetSrcImm(context, Imm20ToSInt(op.Imm20));
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
context.Copy(GetDest(rd), res); EmitSel(context, srcA, srcB, srcPred, op.Dest);
} }
else
{ public static void SelC(EmitterContext context)
throw new NotImplementedException(pMode.ToString()); {
} InstSelC op = context.GetOp<InstSelC>();
Operand srcA = GetSrcReg(context, op.SrcA);
Operand srcB = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
Operand srcPred = GetPredicate(context, op.SrcPred, op.SrcPredInv);
EmitSel(context, srcA, srcB, srcPred, op.Dest);
} }
private static void EmitR2p(EmitterContext context, Operand value, Operand mask, ByteSel byteSel, bool ccpr) private static void EmitR2p(EmitterContext context, Operand value, Operand mask, ByteSel byteSel, bool ccpr)
@@ -23,7 +23,6 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
set => _branch = AddSuccessor(_branch, value); set => _branch = AddSuccessor(_branch, value);
} }
public bool HasSuccessor => _branch != null || _next != null;
public bool HasBranch => _branch != null; public bool HasBranch => _branch != null;
public bool Reachable => Index == 0 || Predecessors.Count != 0; public bool Reachable => Index == 0 || Predecessors.Count != 0;
@@ -18,7 +18,6 @@ namespace Ryujinx.Graphics.Shader
public bool UsesInstanceId { get; } public bool UsesInstanceId { get; }
public bool UsesDrawParameters { get; } public bool UsesDrawParameters { get; }
public bool UsesRtLayer { get; } public bool UsesRtLayer { get; }
public bool HasUnconditionalDiscard { get; }
public byte ClipDistancesWritten { get; } public byte ClipDistancesWritten { get; }
public int FragmentOutputMap { get; } public int FragmentOutputMap { get; }
@@ -35,7 +34,6 @@ namespace Ryujinx.Graphics.Shader
bool usesInstanceId, bool usesInstanceId,
bool usesDrawParameters, bool usesDrawParameters,
bool usesRtLayer, bool usesRtLayer,
bool hasUnconditionalDiscard,
byte clipDistancesWritten, byte clipDistancesWritten,
int fragmentOutputMap) int fragmentOutputMap)
{ {
@@ -52,7 +50,6 @@ namespace Ryujinx.Graphics.Shader
UsesInstanceId = usesInstanceId; UsesInstanceId = usesInstanceId;
UsesDrawParameters = usesDrawParameters; UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer; UsesRtLayer = usesRtLayer;
HasUnconditionalDiscard = hasUnconditionalDiscard;
ClipDistancesWritten = clipDistancesWritten; ClipDistancesWritten = clipDistancesWritten;
FragmentOutputMap = fragmentOutputMap; FragmentOutputMap = fragmentOutputMap;
} }
+2 -10
View File
@@ -24,7 +24,6 @@ namespace Ryujinx.Graphics.Shader
RenderScale, RenderScale,
TfeOffset, TfeOffset,
TfeVertexCount, TfeVertexCount,
BufferTextureOffset,
} }
public struct SupportBuffer public struct SupportBuffer
@@ -43,13 +42,10 @@ namespace Ryujinx.Graphics.Shader
public static readonly int ComputeRenderScaleOffset; public static readonly int ComputeRenderScaleOffset;
public static readonly int TfeOffsetOffset; public static readonly int TfeOffsetOffset;
public static readonly int TfeVertexCountOffset; public static readonly int TfeVertexCountOffset;
public static readonly int BufferTextureOffsetOffset;
public const int FragmentIsBgraCount = 8; public const int FragmentIsBgraCount = 8;
public const int TextureCount = 64;
// One for the render target, 64 for the textures, and 8 for the images. // One for the render target, 64 for the textures, and 8 for the images.
public const int RenderScaleMaxCount = 1 + TextureCount + 8; public const int RenderScaleMaxCount = 1 + 64 + 8;
private static int OffsetOf<T>(ref SupportBuffer storage, ref T target) private static int OffsetOf<T>(ref SupportBuffer storage, ref T target)
{ {
@@ -72,7 +68,6 @@ namespace Ryujinx.Graphics.Shader
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize; ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
TfeOffsetOffset = OffsetOf(ref instance, ref instance.TfeOffset); TfeOffsetOffset = OffsetOf(ref instance, ref instance.TfeOffset);
TfeVertexCountOffset = OffsetOf(ref instance, ref instance.TfeVertexCount); TfeVertexCountOffset = OffsetOf(ref instance, ref instance.TfeVertexCount);
BufferTextureOffsetOffset = OffsetOf(ref instance, ref instance.BufferTextureOffset);
} }
internal static StructureType GetStructureType() internal static StructureType GetStructureType()
@@ -85,8 +80,7 @@ namespace Ryujinx.Graphics.Shader
new StructureField(AggregateType.S32, "frag_scale_count"), new StructureField(AggregateType.S32, "frag_scale_count"),
new StructureField(AggregateType.Array | AggregateType.FP32, "render_scale", RenderScaleMaxCount), new StructureField(AggregateType.Array | AggregateType.FP32, "render_scale", RenderScaleMaxCount),
new StructureField(AggregateType.Vector4 | AggregateType.S32, "tfe_offset"), new StructureField(AggregateType.Vector4 | AggregateType.S32, "tfe_offset"),
new StructureField(AggregateType.S32, "tfe_vertex_count"), new StructureField(AggregateType.S32, "tfe_vertex_count")
new StructureField(AggregateType.Array | AggregateType.Vector2 | AggregateType.S32, "buffer_texture_offset")
]); ]);
} }
@@ -101,7 +95,5 @@ namespace Ryujinx.Graphics.Shader
public Vector4<int> TfeOffset; public Vector4<int> TfeOffset;
public Vector4<int> TfeVertexCount; public Vector4<int> TfeVertexCount;
public Array5<Array64<Vector4<int>>> BufferTextureOffset;
} }
} }
@@ -26,6 +26,5 @@ namespace Ryujinx.Graphics.Shader.Translation
SharedMemory = 1 << 11, SharedMemory = 1 << 11,
Store = 1 << 12, Store = 1 << 12,
VtgAsCompute = 1 << 13, VtgAsCompute = 1 << 13,
UnconditionalDiscard = 1 << 14,
} }
} }
@@ -1,38 +0,0 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
namespace Ryujinx.Graphics.Shader.Translation
{
static class FeatureIdentification
{
public static void RunPass(BasicBlock[] blocks, ShaderStage stage, ref FeatureFlags usedFeatures)
{
if (stage == ShaderStage.Fragment)
{
bool endsWithDiscardOnly = true;
for (int blockIndex = 0; blockIndex < blocks.Length; blockIndex++)
{
BasicBlock block = blocks[blockIndex];
if (block.HasSuccessor)
{
continue;
}
if (block.Operations.Count == 0 ||
block.Operations.Last.Value is not Operation operation ||
operation.Inst != Instruction.Discard)
{
endsWithDiscardOnly = false;
break;
}
}
if (endsWithDiscardOnly)
{
usedFeatures |= FeatureFlags.UnconditionalDiscard;
}
}
}
}
}
@@ -43,11 +43,6 @@ namespace Ryujinx.Graphics.Shader.Translation
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures; private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages; private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
private readonly List<BufferDefinition> _vacConstantBuffers;
private readonly List<BufferDefinition> _vacStorageBuffers;
private readonly List<TextureDefinition> _vacTextures;
private readonly List<TextureDefinition> _vacImages;
public int LocalMemoryId { get; private set; } public int LocalMemoryId { get; private set; }
public int SharedMemoryId { get; private set; } public int SharedMemoryId { get; private set; }
@@ -83,11 +78,6 @@ namespace Ryujinx.Graphics.Shader.Translation
_usedTextures = new(); _usedTextures = new();
_usedImages = new(); _usedImages = new();
_vacConstantBuffers = new();
_vacStorageBuffers = new();
_vacTextures = new();
_vacImages = new();
Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, SupportBuffer.Binding, "support_buffer", SupportBuffer.GetStructureType())); Properties.AddOrUpdateConstantBuffer(new(BufferLayout.Std140, 0, SupportBuffer.Binding, "support_buffer", SupportBuffer.GetStructureType()));
LocalMemoryId = -1; LocalMemoryId = -1;
@@ -573,76 +563,6 @@ namespace Ryujinx.Graphics.Shader.Translation
return descriptors.ToArray(); return descriptors.ToArray();
} }
public ShaderProgramInfo GetVertexAsComputeInfo(bool isVertex = false)
{
var cbDescriptors = new BufferDescriptor[_vacConstantBuffers.Count];
int cbDescriptorIndex = 0;
foreach (BufferDefinition definition in _vacConstantBuffers)
{
cbDescriptors[cbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.None);
}
var sbDescriptors = new BufferDescriptor[_vacStorageBuffers.Count];
int sbDescriptorIndex = 0;
foreach (BufferDefinition definition in _vacStorageBuffers)
{
sbDescriptors[sbDescriptorIndex++] = new BufferDescriptor(definition.Set, definition.Binding, 0, 0, 0, BufferUsageFlags.Write);
}
var tDescriptors = new TextureDescriptor[_vacTextures.Count];
int tDescriptorIndex = 0;
foreach (TextureDefinition definition in _vacTextures)
{
tDescriptors[tDescriptorIndex++] = new TextureDescriptor(
definition.Set,
definition.Binding,
definition.Type,
definition.Format,
0,
0,
definition.ArrayLength,
definition.Separate,
definition.Flags);
}
var iDescriptors = new TextureDescriptor[_vacImages.Count];
int iDescriptorIndex = 0;
foreach (TextureDefinition definition in _vacImages)
{
iDescriptors[iDescriptorIndex++] = new TextureDescriptor(
definition.Set,
definition.Binding,
definition.Type,
definition.Format,
0,
0,
definition.ArrayLength,
definition.Separate,
definition.Flags);
}
return new ShaderProgramInfo(
cbDescriptors,
sbDescriptors,
tDescriptors,
iDescriptors,
isVertex ? ShaderStage.Vertex : ShaderStage.Compute,
0,
0,
0,
false,
false,
false,
false,
false,
0,
0);
}
public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle) public bool TryGetCbufSlotAndHandleForTexture(int binding, out int cbufSlot, out int handle)
{ {
foreach ((TextureInfo info, TextureMeta meta) in _usedTextures) foreach ((TextureInfo info, TextureMeta meta) in _usedTextures)
@@ -707,30 +627,6 @@ namespace Ryujinx.Graphics.Shader.Translation
Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type)); Properties.AddOrUpdateStorageBuffer(new(BufferLayout.Std430, setIndex, binding, name, type));
} }
public void AddVertexAsComputeConstantBuffer(BufferDefinition definition)
{
_vacConstantBuffers.Add(definition);
Properties.AddOrUpdateConstantBuffer(definition);
}
public void AddVertexAsComputeStorageBuffer(BufferDefinition definition)
{
_vacStorageBuffers.Add(definition);
Properties.AddOrUpdateStorageBuffer(definition);
}
public void AddVertexAsComputeTexture(TextureDefinition definition)
{
_vacTextures.Add(definition);
Properties.AddOrUpdateTexture(definition);
}
public void AddVertexAsComputeImage(TextureDefinition definition)
{
_vacImages.Add(definition);
Properties.AddOrUpdateImage(definition);
}
public static string GetShaderStagePrefix(ShaderStage stage) public static string GetShaderStagePrefix(ShaderStage stage)
{ {
uint index = (uint)stage; uint index = (uint)stage;
@@ -1,5 +1,4 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using Ryujinx.Graphics.Shader.Translation.Optimizations;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
@@ -28,19 +27,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat()) if (texOp.Type == SamplerType.TextureBuffer && !context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat())
{ {
if (!context.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat()) node = InsertSnormNormalization(node, context.ResourceManager, context.GpuAccessor);
{
node = InsertSnormNormalization(node, context.ResourceManager, context.GpuAccessor);
}
if (!context.GpuAccessor.QueryHostSupportsBufferTexturePixelAlignment())
{
node = InsertCoordOffset(node, context.ResourceManager, context.Stage, isImage: false);
}
}
else if (!context.GpuAccessor.QueryHostSupportsBufferTexturePixelAlignment() && texOp.Inst.IsImage())
{
node = InsertCoordOffset(node, context.ResourceManager, context.Stage, isImage: true);
} }
} }
} }
@@ -291,87 +278,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Transforms
return node; return node;
} }
private static LinkedListNode<INode> InsertCoordOffset(LinkedListNode<INode> node, ResourceManager resourceManager, ShaderStage stage, bool isImage)
{
// Some GPUs have fixed alignment requirements for buffer textures.
// For those cases, we bind the aligned buffer offset, and apply the remaining offset on the shader.
TextureOperation texOp = (TextureOperation)node.Value;
if ((texOp.Type & SamplerType.Mask) != SamplerType.TextureBuffer)
{
return node;
}
Operand[] sources = new Operand[texOp.SourcesCount];
for (int i = 0; i < texOp.SourcesCount; i++)
{
sources[i] = texOp.GetSource(i);
}
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool isIndexed = resourceManager.IsArrayOfTexturesOrImages(texOp.Binding, isImage);
int coordsIndex = isBindless || isIndexed ? 1 : 0;
Operand[] dests = new Operand[texOp.DestsCount];
for (int i = 0; i < texOp.DestsCount; i++)
{
dests[i] = texOp.GetDest(i);
}
LinkedListNode<INode> oldNode = node;
Operand source = sources[coordsIndex];
Operand offset = Local();
Operand coordPlusOffset = Local();
int stageIndex = stage switch
{
ShaderStage.TessellationControl => 1,
ShaderStage.TessellationEvaluation => 2,
ShaderStage.Geometry => 3,
ShaderStage.Fragment => 4,
_ => 0,
};
int bindingIndex = isImage
? resourceManager.FindImageDescriptorIndex(texOp.Binding)
: resourceManager.FindTextureDescriptorIndex(texOp.Binding);
node.List.AddBefore(node, new Operation(
Instruction.Load,
StorageKind.ConstantBuffer,
offset,
Const(SupportBuffer.Binding),
Const((int)SupportBufferField.BufferTextureOffset),
Const(stageIndex * SupportBuffer.TextureCount + bindingIndex),
Const(isImage ? 1 : 0)));
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offset));
sources[coordsIndex] = coordPlusOffset;
TextureOperation newTexOp = new(
texOp.Inst,
texOp.Type,
texOp.Format,
texOp.Flags,
texOp.Set,
texOp.Binding,
texOp.Index,
dests,
sources);
node = node.List.AddBefore(node, newTexOp);
Utils.DeleteNode(oldNode, texOp);
return node;
}
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor, ShaderStage stage) private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ResourceManager resourceManager, IGpuAccessor gpuAccessor, ShaderStage stage)
{ {
// Non-constant texture offsets are not allowed (according to the spec), // Non-constant texture offsets are not allowed (according to the spec),
@@ -301,11 +301,6 @@ namespace Ryujinx.Graphics.Shader.Translation
Optimizer.RunPass(context); Optimizer.RunPass(context);
TransformPasses.RunPass(context); TransformPasses.RunPass(context);
if (i == 0)
{
FeatureIdentification.RunPass(cfg.Blocks, Definitions.Stage, ref usedFeatures);
}
} }
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount); funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
@@ -358,7 +353,6 @@ namespace Ryujinx.Graphics.Shader.Translation
usedFeatures.HasFlag(FeatureFlags.InstanceId), usedFeatures.HasFlag(FeatureFlags.InstanceId),
usedFeatures.HasFlag(FeatureFlags.DrawParameters), usedFeatures.HasFlag(FeatureFlags.DrawParameters),
usedFeatures.HasFlag(FeatureFlags.RtLayer), usedFeatures.HasFlag(FeatureFlags.RtLayer),
usedFeatures.HasFlag(FeatureFlags.UnconditionalDiscard),
clipDistancesWritten, clipDistancesWritten,
originalDefinitions.OmapTargets); originalDefinitions.OmapTargets);
@@ -399,7 +393,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
int binding = resourceManager.Reservations.GetTfeBufferStorageBufferBinding(i); int binding = resourceManager.Reservations.GetTfeBufferStorageBufferBinding(i);
BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct); BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct);
resourceManager.AddVertexAsComputeStorageBuffer(tfeDataBuffer); resourceManager.Properties.AddOrUpdateStorageBuffer(tfeDataBuffer);
} }
} }
@@ -407,7 +401,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding; int vertexInfoCbBinding = resourceManager.Reservations.VertexInfoConstantBufferBinding;
BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType());
resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer);
StructureType vertexOutputStruct = new([ StructureType vertexOutputStruct = new([
new StructureField(AggregateType.Array | AggregateType.FP32, "data", 0) new StructureField(AggregateType.Array | AggregateType.FP32, "data", 0)
@@ -415,13 +409,13 @@ namespace Ryujinx.Graphics.Shader.Translation
int vertexOutputSbBinding = resourceManager.Reservations.VertexOutputStorageBufferBinding; int vertexOutputSbBinding = resourceManager.Reservations.VertexOutputStorageBufferBinding;
BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexOutputSbBinding, "vertex_output", vertexOutputStruct); BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexOutputSbBinding, "vertex_output", vertexOutputStruct);
resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer);
if (Stage == ShaderStage.Vertex) if (Stage == ShaderStage.Vertex)
{ {
SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding(); SetBindingPair ibSetAndBinding = resourceManager.Reservations.GetIndexBufferTextureSetAndBinding();
TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer); TextureDefinition indexBuffer = new(ibSetAndBinding.SetIndex, ibSetAndBinding.Binding, "ib_data", SamplerType.TextureBuffer);
resourceManager.AddVertexAsComputeTexture(indexBuffer); resourceManager.Properties.AddOrUpdateTexture(indexBuffer);
int inputMap = _program.AttributeUsage.UsedInputAttributes; int inputMap = _program.AttributeUsage.UsedInputAttributes;
@@ -430,7 +424,7 @@ namespace Ryujinx.Graphics.Shader.Translation
int location = BitOperations.TrailingZeroCount(inputMap); int location = BitOperations.TrailingZeroCount(inputMap);
SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location); SetBindingPair setAndBinding = resourceManager.Reservations.GetVertexBufferTextureSetAndBinding(location);
TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer); TextureDefinition vaBuffer = new(setAndBinding.SetIndex, setAndBinding.Binding, $"vb_data{location}", SamplerType.TextureBuffer);
resourceManager.AddVertexAsComputeTexture(vaBuffer); resourceManager.Properties.AddOrUpdateTexture(vaBuffer);
inputMap &= ~(1 << location); inputMap &= ~(1 << location);
} }
@@ -439,11 +433,11 @@ namespace Ryujinx.Graphics.Shader.Translation
{ {
SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding(); SetBindingPair trbSetAndBinding = resourceManager.Reservations.GetTopologyRemapBufferTextureSetAndBinding();
TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer); TextureDefinition remapBuffer = new(trbSetAndBinding.SetIndex, trbSetAndBinding.Binding, "trb_data", SamplerType.TextureBuffer);
resourceManager.AddVertexAsComputeTexture(remapBuffer); resourceManager.Properties.AddOrUpdateTexture(remapBuffer);
int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding; int geometryVbOutputSbBinding = resourceManager.Reservations.GeometryVertexOutputStorageBufferBinding;
BufferDefinition geometryVbOutputBuffer = new(BufferLayout.Std430, 1, geometryVbOutputSbBinding, "geometry_vb_output", vertexOutputStruct); BufferDefinition geometryVbOutputBuffer = new(BufferLayout.Std430, 1, geometryVbOutputSbBinding, "geometry_vb_output", vertexOutputStruct);
resourceManager.AddVertexAsComputeStorageBuffer(geometryVbOutputBuffer); resourceManager.Properties.AddOrUpdateStorageBuffer(geometryVbOutputBuffer);
StructureType geometryIbOutputStruct = new([ StructureType geometryIbOutputStruct = new([
new StructureField(AggregateType.Array | AggregateType.U32, "data", 0) new StructureField(AggregateType.Array | AggregateType.U32, "data", 0)
@@ -451,7 +445,7 @@ namespace Ryujinx.Graphics.Shader.Translation
int geometryIbOutputSbBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding; int geometryIbOutputSbBinding = resourceManager.Reservations.GeometryIndexOutputStorageBufferBinding;
BufferDefinition geometryIbOutputBuffer = new(BufferLayout.Std430, 1, geometryIbOutputSbBinding, "geometry_ib_output", geometryIbOutputStruct); BufferDefinition geometryIbOutputBuffer = new(BufferLayout.Std430, 1, geometryIbOutputSbBinding, "geometry_ib_output", geometryIbOutputStruct);
resourceManager.AddVertexAsComputeStorageBuffer(geometryIbOutputBuffer); resourceManager.Properties.AddOrUpdateStorageBuffer(geometryIbOutputBuffer);
} }
resourceManager.SetVertexAsComputeLocalMemories(Definitions.Stage, Definitions.InputTopology); resourceManager.SetVertexAsComputeLocalMemories(Definitions.Stage, Definitions.InputTopology);
@@ -484,17 +478,12 @@ namespace Ryujinx.Graphics.Shader.Translation
return new ResourceReservations(GpuAccessor, IsTransformFeedbackEmulated, vertexAsCompute: true, _vertexOutput, ioUsage); return new ResourceReservations(GpuAccessor, IsTransformFeedbackEmulated, vertexAsCompute: true, _vertexOutput, ioUsage);
} }
public ShaderProgramInfo GetVertexAsComputeInfo()
{
return CreateResourceManager(true).GetVertexAsComputeInfo();
}
public void SetVertexOutputMapForGeometryAsCompute(TranslatorContext vertexContext) public void SetVertexOutputMapForGeometryAsCompute(TranslatorContext vertexContext)
{ {
_vertexOutput = vertexContext._program.GetIoUsage(); _vertexOutput = vertexContext._program.GetIoUsage();
} }
public (ShaderProgram, ShaderProgramInfo) GenerateVertexPassthroughForCompute() public ShaderProgram GenerateVertexPassthroughForCompute()
{ {
AttributeUsage attributeUsage = new(GpuAccessor); AttributeUsage attributeUsage = new(GpuAccessor);
ResourceManager resourceManager = new(ShaderStage.Vertex, GpuAccessor); ResourceManager resourceManager = new(ShaderStage.Vertex, GpuAccessor);
@@ -506,7 +495,7 @@ namespace Ryujinx.Graphics.Shader.Translation
if (Stage == ShaderStage.Vertex) if (Stage == ShaderStage.Vertex)
{ {
BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType()); BufferDefinition vertexInfoBuffer = new(BufferLayout.Std140, 0, vertexInfoCbBinding, "vb_info", VertexInfoBuffer.GetStructureType());
resourceManager.AddVertexAsComputeConstantBuffer(vertexInfoBuffer); resourceManager.Properties.AddOrUpdateConstantBuffer(vertexInfoBuffer);
} }
StructureType vertexInputStruct = new([ StructureType vertexInputStruct = new([
@@ -515,7 +504,7 @@ namespace Ryujinx.Graphics.Shader.Translation
int vertexDataSbBinding = reservations.VertexOutputStorageBufferBinding; int vertexDataSbBinding = reservations.VertexOutputStorageBufferBinding;
BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexDataSbBinding, "vb_input", vertexInputStruct); BufferDefinition vertexOutputBuffer = new(BufferLayout.Std430, 1, vertexDataSbBinding, "vb_input", vertexInputStruct);
resourceManager.AddVertexAsComputeStorageBuffer(vertexOutputBuffer); resourceManager.Properties.AddOrUpdateStorageBuffer(vertexOutputBuffer);
EmitterContext context = new(); EmitterContext context = new();
@@ -573,14 +562,14 @@ namespace Ryujinx.Graphics.Shader.Translation
LastInVertexPipeline = true LastInVertexPipeline = true
}; };
return (Generate( return Generate(
[function], [function],
attributeUsage, attributeUsage,
definitions, definitions,
definitions, definitions,
resourceManager, resourceManager,
FeatureFlags.None, FeatureFlags.None,
0), resourceManager.GetVertexAsComputeInfo(isVertex: true)); 0);
} }
public ShaderProgram GenerateGeometryPassthrough() public ShaderProgram GenerateGeometryPassthrough()
@@ -36,7 +36,6 @@ namespace Ryujinx.Graphics.Vulkan
queueLock, queueLock,
_gd.QueueFamilyIndex, _gd.QueueFamilyIndex,
_gd.IsQualcommProprietary, _gd.IsQualcommProprietary,
_gd.IsTurnip,
isLight: true); isLight: true);
} }
} }
@@ -19,7 +19,6 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Queue _queue; private readonly Queue _queue;
private readonly Lock _queueLock; private readonly Lock _queueLock;
private readonly bool _concurrentFenceWaitUnsupported; private readonly bool _concurrentFenceWaitUnsupported;
private readonly bool _fenceAlwaysWaits;
private readonly CommandPool _pool; private readonly CommandPool _pool;
private readonly Thread _owner; private readonly Thread _owner;
@@ -67,7 +66,6 @@ namespace Ryujinx.Graphics.Vulkan
Lock queueLock, Lock queueLock,
uint queueFamilyIndex, uint queueFamilyIndex,
bool concurrentFenceWaitUnsupported, bool concurrentFenceWaitUnsupported,
bool fenceAlwaysWaits,
bool isLight = false) bool isLight = false)
{ {
_api = api; _api = api;
@@ -75,7 +73,6 @@ namespace Ryujinx.Graphics.Vulkan
_queue = queue; _queue = queue;
_queueLock = queueLock; _queueLock = queueLock;
_concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported; _concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported;
_fenceAlwaysWaits = fenceAlwaysWaits;
_owner = Thread.CurrentThread; _owner = Thread.CurrentThread;
CommandPoolCreateInfo commandPoolCreateInfo = new() CommandPoolCreateInfo commandPoolCreateInfo = new()
@@ -210,7 +207,7 @@ namespace Ryujinx.Graphics.Vulkan
ref ReservedCommandBuffer entry = ref _commandBuffers[index]; ref ReservedCommandBuffer entry = ref _commandBuffers[index];
if (wait || !entry.InConsumption || entry.Fence.IsSignaledLazy()) if (wait || !entry.InConsumption || entry.Fence.IsSignaled())
{ {
WaitAndDecrementRef(index); WaitAndDecrementRef(index);
@@ -352,7 +349,7 @@ namespace Ryujinx.Graphics.Vulkan
if (refreshFence) if (refreshFence)
{ {
entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported, _fenceAlwaysWaits); entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported);
} }
else else
{ {
@@ -255,8 +255,8 @@ namespace Ryujinx.Graphics.Vulkan.Effects
scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height); scissors[0] = new Rectangle<int>(0, 0, texture.Width, texture.Height);
_pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height); _pipeline.SetRenderTarget(texture, (uint)texture.Width, (uint)texture.Height, false);
_pipeline.SetRenderTargetColorMasks(colorMasks); _pipeline.SetRenderTargetColorMasks(colorMasks, false);
_pipeline.SetScissors(scissors); _pipeline.SetScissors(scissors);
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
} }
@@ -238,6 +238,7 @@ namespace Ryujinx.Graphics.Vulkan
Face.Back => CullModeFlags.BackBit, Face.Back => CullModeFlags.BackBit,
Face.Front => CullModeFlags.FrontBit, Face.Front => CullModeFlags.FrontBit,
Face.FrontAndBack => CullModeFlags.FrontAndBack, Face.FrontAndBack => CullModeFlags.FrontAndBack,
Face.None => CullModeFlags.None,
_ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit), _ => LogInvalidAndReturn(face, nameof(Face), CullModeFlags.BackBit),
}; };
} }
@@ -310,6 +311,25 @@ namespace Ryujinx.Graphics.Vulkan
}; };
} }
public static PrimitiveTopology ConvertToClass(this PrimitiveTopology topology)
{
return topology switch
{
PrimitiveTopology.PointList => PrimitiveTopology.PointList,
PrimitiveTopology.LineList or
PrimitiveTopology.LineStrip or
PrimitiveTopology.LineListWithAdjacency or
PrimitiveTopology.LineStripWithAdjacency => PrimitiveTopology.LineList,
PrimitiveTopology.TriangleList or
PrimitiveTopology.TriangleStrip or
PrimitiveTopology.TriangleFan or
PrimitiveTopology.TriangleListWithAdjacency or
PrimitiveTopology.TriangleStripWithAdjacency => PrimitiveTopology.TriangleStrip,
PrimitiveTopology.PatchList => PrimitiveTopology.PatchList,
_ => LogInvalidAndReturn(topology, nameof(PrimitiveTopology), PrimitiveTopology.TriangleStrip),
};
}
public static StencilOp Convert(this GAL.StencilOp op) public static StencilOp Convert(this GAL.StencilOp op)
{ {
return op switch return op switch
+1 -13
View File
@@ -12,15 +12,13 @@ namespace Ryujinx.Graphics.Vulkan
private int _referenceCount; private int _referenceCount;
private int _lock; private int _lock;
private readonly bool _concurrentWaitUnsupported; private readonly bool _concurrentWaitUnsupported;
private readonly bool _alwaysWaits;
private bool _disposed; private bool _disposed;
public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported, bool alwaysWaits) public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
{ {
_api = api; _api = api;
_device = device; _device = device;
_concurrentWaitUnsupported = concurrentWaitUnsupported; _concurrentWaitUnsupported = concurrentWaitUnsupported;
_alwaysWaits = alwaysWaits;
FenceCreateInfo fenceCreateInfo = new() FenceCreateInfo fenceCreateInfo = new()
{ {
@@ -125,16 +123,6 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
public bool IsSignaledLazy()
{
if (_alwaysWaits)
{
return false;
}
return IsSignaled();
}
public bool IsSignaled() public bool IsSignaled()
{ {
if (_concurrentWaitUnsupported) if (_concurrentWaitUnsupported)
@@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsShaderStorageImageMultisample; public readonly bool SupportsShaderStorageImageMultisample;
public readonly bool SupportsConditionalRendering; public readonly bool SupportsConditionalRendering;
public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsExtendedDynamicState;
public readonly PhysicalDeviceExtendedDynamicState2FeaturesEXT SupportsExtendedDynamicState2;
public readonly bool SupportsMultiView; public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors; public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPushDescriptors; public readonly bool SupportsPushDescriptors;
@@ -46,6 +47,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsViewportArray2; public readonly bool SupportsViewportArray2;
public readonly bool SupportsHostImportedMemory; public readonly bool SupportsHostImportedMemory;
public readonly bool SupportsDepthClipControl; public readonly bool SupportsDepthClipControl;
public readonly bool SupportsWideLines;
public readonly bool SupportsAttachmentFeedbackLoop; public readonly bool SupportsAttachmentFeedbackLoop;
public readonly bool SupportsDynamicAttachmentFeedbackLoop; public readonly bool SupportsDynamicAttachmentFeedbackLoop;
public readonly uint SubgroupSize; public readonly uint SubgroupSize;
@@ -54,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly uint VertexBufferAlignment; public readonly uint VertexBufferAlignment;
public readonly uint SubTexelPrecisionBits; public readonly uint SubTexelPrecisionBits;
public readonly ulong MinResourceAlignment; public readonly ulong MinResourceAlignment;
public readonly uint MaxTessellationPatchSize;
public readonly ulong MinTexelBufferAlignment; public readonly ulong MinTexelBufferAlignment;
public HardwareCapabilities( public HardwareCapabilities(
@@ -72,6 +75,8 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsShaderStorageImageMultisample, bool supportsShaderStorageImageMultisample,
bool supportsConditionalRendering, bool supportsConditionalRendering,
bool supportsExtendedDynamicState, bool supportsExtendedDynamicState,
PhysicalDeviceExtendedDynamicState2FeaturesEXT supportsExtendedDynamicState2,
uint maxTessellationPatchSize,
bool supportsMultiView, bool supportsMultiView,
bool supportsNullDescriptors, bool supportsNullDescriptors,
bool supportsPushDescriptors, bool supportsPushDescriptors,
@@ -87,6 +92,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsViewportArray2, bool supportsViewportArray2,
bool supportsHostImportedMemory, bool supportsHostImportedMemory,
bool supportsDepthClipControl, bool supportsDepthClipControl,
bool supportsWideLines,
bool supportsAttachmentFeedbackLoop, bool supportsAttachmentFeedbackLoop,
bool supportsDynamicAttachmentFeedbackLoop, bool supportsDynamicAttachmentFeedbackLoop,
uint subgroupSize, uint subgroupSize,
@@ -112,6 +118,8 @@ namespace Ryujinx.Graphics.Vulkan
SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample; SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample;
SupportsConditionalRendering = supportsConditionalRendering; SupportsConditionalRendering = supportsConditionalRendering;
SupportsExtendedDynamicState = supportsExtendedDynamicState; SupportsExtendedDynamicState = supportsExtendedDynamicState;
SupportsExtendedDynamicState2 = supportsExtendedDynamicState2;
MaxTessellationPatchSize = maxTessellationPatchSize;
SupportsMultiView = supportsMultiView; SupportsMultiView = supportsMultiView;
SupportsNullDescriptors = supportsNullDescriptors; SupportsNullDescriptors = supportsNullDescriptors;
SupportsPushDescriptors = supportsPushDescriptors; SupportsPushDescriptors = supportsPushDescriptors;
@@ -127,6 +135,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsViewportArray2 = supportsViewportArray2; SupportsViewportArray2 = supportsViewportArray2;
SupportsHostImportedMemory = supportsHostImportedMemory; SupportsHostImportedMemory = supportsHostImportedMemory;
SupportsDepthClipControl = supportsDepthClipControl; SupportsDepthClipControl = supportsDepthClipControl;
SupportsWideLines = supportsWideLines;
SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop; SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop; SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
SubgroupSize = subgroupSize; SubgroupSize = subgroupSize;
+14 -14
View File
@@ -406,20 +406,20 @@ namespace Ryujinx.Graphics.Vulkan
if (dstIsDepthOrStencil) if (dstIsDepthOrStencil)
{ {
_pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit, false);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always), false);
} }
else if (src.Info.Target.IsMultisample) else if (src.Info.Target.IsMultisample)
{ {
_pipeline.SetProgram(_programColorBlitMs); _pipeline.SetProgram(_programColorBlitMs, false);
} }
else if (clearAlpha) else if (clearAlpha)
{ {
_pipeline.SetProgram(_programColorBlitClearAlpha); _pipeline.SetProgram(_programColorBlitClearAlpha, false);
} }
else else
{ {
_pipeline.SetProgram(_programColorBlit); _pipeline.SetProgram(_programColorBlit, false);
} }
int dstWidth = dst.Width; int dstWidth = dst.Width;
@@ -434,7 +434,7 @@ namespace Ryujinx.Graphics.Vulkan
_pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f)); _pipeline.ClearRenderTargetColor(0, 0, 1, new ColorF(0f, 0f, 0f, 1f));
} }
_pipeline.SetViewports(viewports); _pipeline.SetViewports(viewports, false);
_pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); _pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
_pipeline.Draw(4, 1, 0, 0); _pipeline.Draw(4, 1, 0, 0);
@@ -566,12 +566,12 @@ namespace Ryujinx.Graphics.Vulkan
if (isDepth) if (isDepth)
{ {
_pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit); _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programDepthBlitMs : _programDepthBlit, false);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
} }
else else
{ {
_pipeline.SetProgram(src.Info.Target.IsMultisample ? _programStencilBlitMs : _programStencilBlit); _pipeline.SetProgram(src.Info.Target.IsMultisample ? _programStencilBlitMs : _programStencilBlit, false);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
} }
@@ -771,8 +771,8 @@ namespace Ryujinx.Graphics.Vulkan
0f, 0f,
1f); 1f);
pipeline.SetProgram(_programColorBlit); pipeline.SetProgram(_programColorBlit, false);
pipeline.SetViewports(viewports); pipeline.SetViewports(viewports, false);
pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip); pipeline.SetPrimitiveTopology(PrimitiveTopology.TriangleStrip);
pipeline.Draw(4, 1, 0, 0); pipeline.Draw(4, 1, 0, 0);
@@ -1229,7 +1229,7 @@ namespace Ryujinx.Graphics.Vulkan
TextureView srcView = Create2DLayerView(src, srcLayer + z, 0); TextureView srcView = Create2DLayerView(src, srcLayer + z, 0);
TextureView dstView = Create2DLayerView(dst, dstLayer + z, 0); TextureView dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height); _pipeline.SetRenderTarget(dstView, (uint)dst.Width, (uint)dst.Height, false);
CopyMSDraw(srcView, aspectFlags, fromMS: false); CopyMSDraw(srcView, aspectFlags, fromMS: false);
@@ -1246,7 +1246,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
else else
{ {
_pipeline.SetProgram(_programColorDrawToMs); _pipeline.SetProgram(_programColorDrawToMs, false);
Format format = GetFormat(src.Info.BytesPerPixel); Format format = GetFormat(src.Info.BytesPerPixel);
VkFormat vkFormat = FormatTable.GetFormat(format); VkFormat vkFormat = FormatTable.GetFormat(format);
@@ -1323,12 +1323,12 @@ namespace Ryujinx.Graphics.Vulkan
if (isDepth) if (isDepth)
{ {
_pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs); _pipeline.SetProgram(fromMS ? _programDepthDrawToNonMs : _programDepthDrawToMs, false);
_pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always)); _pipeline.SetDepthTest(new DepthTestDescriptor(true, true, CompareOp.Always));
} }
else else
{ {
_pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs); _pipeline.SetProgram(fromMS ? _programStencilDrawToNonMs : _programStencilDrawToMs, false);
_pipeline.SetStencilTest(CreateStencilTestDescriptor(true)); _pipeline.SetStencilTest(CreateStencilTestDescriptor(true));
} }
+339 -95
View File
@@ -76,6 +76,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly BufferState[] _transformFeedbackBuffers; private readonly BufferState[] _transformFeedbackBuffers;
private readonly VertexBufferState[] _vertexBuffers; private readonly VertexBufferState[] _vertexBuffers;
private ulong _vertexBuffersDirty; private ulong _vertexBuffersDirty;
private bool _bindingsSet;
protected Rectangle<int> ClearScissor; protected Rectangle<int> ClearScissor;
private readonly VertexBufferUpdater _vertexBufferUpdater; private readonly VertexBufferUpdater _vertexBufferUpdater;
@@ -89,6 +90,9 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfEnabled; private bool _tfEnabled;
private bool _tfActive; private bool _tfActive;
private readonly bool _supportExtDynamic;
private readonly bool _supportExtDynamic2;
private FeedbackLoopAspects _feedbackLoop; private FeedbackLoopAspects _feedbackLoop;
private bool _passWritesDepthStencil; private bool _passWritesDepthStencil;
@@ -96,6 +100,8 @@ namespace Ryujinx.Graphics.Vulkan
public ulong DrawCount { get; private set; } public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; } public bool RenderPassActive { get; private set; }
private readonly int[] _vertexBufferBindings;
public unsafe PipelineBase(VulkanRenderer gd, Device device) public unsafe PipelineBase(VulkanRenderer gd, Device device)
{ {
Gd = gd; Gd = gd;
@@ -128,7 +134,19 @@ namespace Ryujinx.Graphics.Vulkan
_storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets]; _storedBlend = new PipelineColorBlendAttachmentState[Constants.MaxRenderTargets];
_newState.Initialize(); _supportExtDynamic = gd.Capabilities.SupportsExtendedDynamicState;
_supportExtDynamic2 = gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2;
_bindingsSet = false;
_vertexBufferBindings = new int[Constants.MaxVertexBuffers];
for (int i = 0; i < Constants.MaxVertexBuffers; i++)
{
_vertexBufferBindings[i] = i + 1;
}
_newState.Initialize(gd.Capabilities);
} }
public void Initialize() public void Initialize()
@@ -634,19 +652,46 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (texture is TextureView srcTexture) if (texture is TextureView srcTexture)
{ {
CullModeFlags oldCullMode = _newState.CullMode; CullModeFlags oldCullMode;
bool oldStencilTestEnable = _newState.StencilTestEnable; bool oldStencilTestEnable;
bool oldDepthTestEnable = _newState.DepthTestEnable; bool oldDepthTestEnable;
bool oldDepthWriteEnable = _newState.DepthWriteEnable; bool oldDepthWriteEnable;
PrimitiveTopology oldTopology;
Array16<Silk.NET.Vulkan.Viewport> oldViewports = DynamicState.Viewports; Array16<Silk.NET.Vulkan.Viewport> oldViewports = DynamicState.Viewports;
uint oldViewportsCount = _newState.ViewportsCount; uint oldViewportsCount;
PrimitiveTopology oldTopology = _topology;
_newState.CullMode = CullModeFlags.None; if (_supportExtDynamic)
_newState.StencilTestEnable = false; {
_newState.DepthTestEnable = false; oldCullMode = DynamicState.CullMode;
_newState.DepthWriteEnable = false; oldStencilTestEnable = DynamicState.StencilTestEnable;
SignalStateChange(); oldDepthTestEnable = DynamicState.DepthTestEnable;
oldDepthWriteEnable = DynamicState.DepthWriteEnable;
oldTopology = _topology;
oldViewportsCount = DynamicState.ViewportsCount;
}
else
{
oldCullMode = _newState.CullMode;
oldStencilTestEnable = _newState.StencilTestEnable;
oldDepthTestEnable = _newState.DepthTestEnable;
oldDepthWriteEnable = _newState.DepthWriteEnable;
oldTopology = _topology;
oldViewportsCount = _newState.ViewportsCount;
}
if (_supportExtDynamic)
{
DynamicState.SetCullMode(CullModeFlags.None);
DynamicState.SetDepthTestBool(false, false);
DynamicState.SetStencilTest(false);
}
else
{
_newState.CullMode = CullModeFlags.None;
_newState.StencilTestEnable = false;
_newState.DepthTestEnable = false;
_newState.DepthWriteEnable = false;
}
Gd.HelperShader.DrawTexture( Gd.HelperShader.DrawTexture(
Gd, Gd,
@@ -656,16 +701,24 @@ namespace Ryujinx.Graphics.Vulkan
srcRegion, srcRegion,
dstRegion); dstRegion);
_newState.CullMode = oldCullMode; if (_supportExtDynamic)
_newState.StencilTestEnable = oldStencilTestEnable; {
_newState.DepthTestEnable = oldDepthTestEnable; DynamicState.SetCullMode(oldCullMode);
_newState.DepthWriteEnable = oldDepthWriteEnable; DynamicState.SetStencilTest(oldStencilTestEnable);
DynamicState.SetDepthTestBool(oldDepthTestEnable, oldDepthWriteEnable);
}
else
{
_newState.CullMode = oldCullMode;
_newState.StencilTestEnable = oldStencilTestEnable;
_newState.DepthTestEnable = oldDepthTestEnable;
_newState.DepthWriteEnable = oldDepthWriteEnable;
_newState.ViewportsCount = oldViewportsCount;
}
SetPrimitiveTopology(oldTopology); SetPrimitiveTopology(oldTopology);
DynamicState.SetViewports(ref oldViewports, oldViewportsCount); DynamicState.SetViewports(ref oldViewports, oldViewportsCount);
_newState.ViewportsCount = oldViewportsCount;
SignalStateChange();
} }
} }
@@ -796,48 +849,102 @@ namespace Ryujinx.Graphics.Vulkan
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
{ {
DynamicState.SetDepthBias(factor, units, clamp); bool depthBiasEnable = (enables != 0) && (factor != 0 && units != 0);
bool changed = false;
_newState.DepthBiasEnable = enables != 0; if (_supportExtDynamic2)
SignalStateChange(); {
} DynamicState.SetDepthBiasEnable(depthBiasEnable);
}
else if (_newState.DepthBiasEnable != depthBiasEnable)
{
_newState.DepthBiasEnable = depthBiasEnable;
changed = true;
}
public void SetDepthClamp(bool clamp) if (depthBiasEnable)
{ {
_newState.DepthClampEnable = clamp; DynamicState.SetDepthBias(factor, units, clamp);
SignalStateChange(); }
}
public void SetDepthMode(DepthMode mode) if (changed)
{
bool oldMode = _newState.DepthMode;
_newState.DepthMode = mode == DepthMode.MinusOneToOne;
if (_newState.DepthMode != oldMode)
{ {
SignalStateChange(); SignalStateChange();
} }
} }
public void SetDepthTest(DepthTestDescriptor depthTest) public void SetDepthClamp(bool clamp)
{ {
_newState.DepthTestEnable = depthTest.TestEnable; _newState.DepthClampEnable = clamp;
_newState.DepthWriteEnable = depthTest.WriteEnable;
_newState.DepthCompareOp = depthTest.Func.Convert();
UpdatePassDepthStencil();
SignalStateChange(); SignalStateChange();
} }
public void SetFaceCulling(bool enable, Face face) public void SetDepthMode(DepthMode mode)
{ {
_newState.CullMode = enable ? face.Convert() : CullModeFlags.None; bool newMode = mode == DepthMode.MinusOneToOne;
if (_newState.DepthMode == newMode)
{
return;
}
_newState.DepthMode = newMode;
SignalStateChange(); SignalStateChange();
} }
public void SetDepthTest(DepthTestDescriptor depthTest, bool signalChange = true)
{
if (_supportExtDynamic)
{
DynamicState.SetDepthTestBool(depthTest.TestEnable, depthTest.WriteEnable);
if (depthTest.TestEnable)
{
DynamicState.SetDepthTestCompareOp(depthTest.Func.Convert());
}
}
else
{
_newState.DepthTestEnable = depthTest.TestEnable;
_newState.DepthWriteEnable = depthTest.WriteEnable;
_newState.DepthCompareOp = depthTest.Func.Convert();
if (signalChange)
{
SignalStateChange();
}
}
UpdatePassDepthStencil();
}
public void SetFaceCulling(Face face)
{
if (_supportExtDynamic)
{
DynamicState.SetCullMode(face.Convert());
}
else
{
_newState.CullMode = face.Convert();
SignalStateChange();
}
}
public void SetFrontFace(FrontFace frontFace) public void SetFrontFace(FrontFace frontFace)
{ {
_newState.FrontFace = frontFace.Convert(); if (_supportExtDynamic)
SignalStateChange(); {
DynamicState.SetFrontFace(frontFace.Convert());
}
else
{
_newState.FrontFace = frontFace.Convert();
SignalStateChange();
}
} }
public void SetImage(ShaderStage stage, int binding, ITexture image) public void SetImage(ShaderStage stage, int binding, ITexture image)
@@ -881,28 +988,56 @@ namespace Ryujinx.Graphics.Vulkan
public void SetLineParameters(float width, bool smooth) public void SetLineParameters(float width, bool smooth)
{ {
_newState.LineWidth = width; if (!Gd.IsMoltenVk)
SignalStateChange(); {
DynamicState.SetLineWidth(Gd.Capabilities.SupportsWideLines ? width : 1.0f);
}
} }
public void SetLogicOpState(bool enable, LogicalOp op) public void SetLogicOpState(bool enable, LogicalOp op)
{ {
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
// so we need to force disable them here.
bool logicOpEnable = enable && (Gd.Vendor == Vendor.Nvidia || _newState.Internal.LogicOpsAllowed);
_newState.LogicOpEnable = enable; _newState.LogicOpEnable = enable;
_newState.LogicOp = op.Convert();
if (Gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp)
{
if (logicOpEnable)
{
DynamicState.SetLogicOp(op.Convert());
}
}
else
{
_newState.LogicOp = op.Convert();
}
SignalStateChange(); SignalStateChange();
} }
public void SetMultisampleState(MultisampleDescriptor multisample) public void SetMultisampleState(MultisampleDescriptor multisample)
{ {
_newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable; _newState.AlphaToCoverageEnable = multisample.AlphaToCoverageEnable;
_newState.AlphaToOneEnable = multisample.AlphaToOneEnable; _newState.AlphaToOneEnable = multisample.AlphaToOneEnable;
SignalStateChange(); SignalStateChange();
} }
public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel) public void SetPatchParameters(int vertices, ReadOnlySpan<float> defaultOuterLevel, ReadOnlySpan<float> defaultInnerLevel)
{ {
_newState.PatchControlPoints = (uint)vertices; if (Gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
SignalStateChange(); {
DynamicState.SetPatchControlPoints((uint)vertices);
}
else
{
_newState.PatchControlPoints = (uint)vertices;
SignalStateChange();
}
// TODO: Default levels (likely needs emulation on shaders?) // TODO: Default levels (likely needs emulation on shaders?)
} }
@@ -919,12 +1054,21 @@ namespace Ryujinx.Graphics.Vulkan
public void SetPrimitiveRestart(bool enable, int index) public void SetPrimitiveRestart(bool enable, int index)
{ {
_newState.PrimitiveRestartEnable = enable; if (_supportExtDynamic2)
{
DynamicState.SetPrimitiveRestartEnable(enable);
}
else
{
_newState.PrimitiveRestartEnable = enable;
SignalStateChange();
}
// TODO: What to do about the index? // TODO: What to do about the index?
SignalStateChange();
} }
public void SetPrimitiveTopology(PrimitiveTopology topology) public void SetPrimitiveTopology(PrimitiveTopology topology, bool signalChange = true)
{ {
_topology = topology; _topology = topology;
@@ -932,10 +1076,18 @@ namespace Ryujinx.Graphics.Vulkan
_newState.Topology = vkTopology; _newState.Topology = vkTopology;
SignalStateChange(); if (_supportExtDynamic)
{
DynamicState.SetPrimitiveTopology(vkTopology);
}
if (signalChange)
{
SignalStateChange();
}
} }
public void SetProgram(IProgram program) public void SetProgram(IProgram program, bool signalChange = true)
{ {
ShaderCollection internalProgram = (ShaderCollection)program; ShaderCollection internalProgram = (ShaderCollection)program;
PipelineShaderStageCreateInfo[] stages = internalProgram.GetInfos(); PipelineShaderStageCreateInfo[] stages = internalProgram.GetInfos();
@@ -951,7 +1103,10 @@ namespace Ryujinx.Graphics.Vulkan
stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]); stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]);
SignalStateChange(); if (signalChange)
{
SignalStateChange();
}
if (internalProgram.IsCompute) if (internalProgram.IsCompute)
{ {
@@ -977,18 +1132,26 @@ namespace Ryujinx.Graphics.Vulkan
public void SetRasterizerDiscard(bool discard) public void SetRasterizerDiscard(bool discard)
{ {
_newState.RasterizerDiscardEnable = discard; if (_supportExtDynamic2)
SignalStateChange(); {
DynamicState.SetRasterizerDiscard(discard);
}
else
{
_newState.RasterizerDiscardEnable = discard;
SignalStateChange();
}
if (!discard && Gd.IsQualcommProprietary) if (!discard && Gd.IsQualcommProprietary)
{ {
// On Adreno, enabling rasterizer discard somehow corrupts the viewport state. // On Adreno, enabling rasterizer discard somehow corrupts the viewport state.
// Force it to be updated on next use to work around this bug. // Force it to be updated on next use to work around this bug.
DynamicState.ForceAllDirty(); DynamicState.ForceAllDirty(Gd);
} }
} }
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask, bool signalChange = true)
{ {
int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length); int count = Math.Min(Constants.MaxRenderTargets, componentMask.Length);
int writtenAttachments = 0; int writtenAttachments = 0;
@@ -1030,7 +1193,10 @@ namespace Ryujinx.Graphics.Vulkan
} }
else else
{ {
SignalStateChange(); if (signalChange)
{
SignalStateChange();
}
if (writtenAttachments != _writtenAttachmentCount) if (writtenAttachments != _writtenAttachmentCount)
{ {
@@ -1054,7 +1220,7 @@ namespace Ryujinx.Graphics.Vulkan
SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR); SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
} }
public void SetScissors(ReadOnlySpan<Rectangle<int>> regions) public void SetScissors(ReadOnlySpan<Rectangle<int>> regions, bool signalChange = true)
{ {
int maxScissors = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; int maxScissors = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1;
int count = Math.Min(maxScissors, regions.Length); int count = Math.Min(maxScissors, regions.Length);
@@ -1063,6 +1229,8 @@ namespace Ryujinx.Graphics.Vulkan
ClearScissor = regions[0]; ClearScissor = regions[0];
} }
DynamicState.ScissorsCount = count;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
Rectangle<int> region = regions[i]; Rectangle<int> region = regions[i];
@@ -1072,34 +1240,56 @@ namespace Ryujinx.Graphics.Vulkan
DynamicState.SetScissor(i, new Rect2D(offset, extent)); DynamicState.SetScissor(i, new Rect2D(offset, extent));
} }
DynamicState.ScissorsCount = count; if (!_supportExtDynamic)
{
_newState.ScissorsCount = (uint)count;
_newState.ScissorsCount = (uint)count; if (signalChange)
SignalStateChange(); {
SignalStateChange();
}
}
} }
public void SetStencilTest(StencilTestDescriptor stencilTest) public void SetStencilTest(StencilTestDescriptor stencilTest)
{ {
DynamicState.SetStencilMasks( if (_supportExtDynamic)
(uint)stencilTest.BackFuncMask, {
DynamicState.SetStencilTestandOp(
stencilTest.BackSFail.Convert(),
stencilTest.BackDpPass.Convert(),
stencilTest.BackDpFail.Convert(),
stencilTest.BackFunc.Convert(),
stencilTest.FrontSFail.Convert(),
stencilTest.FrontDpPass.Convert(),
stencilTest.FrontDpFail.Convert(),
stencilTest.FrontFunc.Convert(),
stencilTest.TestEnable);
UpdatePassDepthStencil();
}
else
{
_newState.StencilBackFailOp = stencilTest.BackSFail.Convert();
_newState.StencilBackPassOp = stencilTest.BackDpPass.Convert();
_newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert();
_newState.StencilBackCompareOp = stencilTest.BackFunc.Convert();
_newState.StencilFrontFailOp = stencilTest.FrontSFail.Convert();
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
_newState.StencilTestEnable = stencilTest.TestEnable;
UpdatePassDepthStencil();
SignalStateChange();
}
DynamicState.SetStencilMask((uint)stencilTest.BackFuncMask,
(uint)stencilTest.BackMask, (uint)stencilTest.BackMask,
(uint)stencilTest.BackFuncRef, (uint)stencilTest.BackFuncRef,
(uint)stencilTest.FrontFuncMask, (uint)stencilTest.FrontFuncMask,
(uint)stencilTest.FrontMask, (uint)stencilTest.FrontMask,
(uint)stencilTest.FrontFuncRef); (uint)stencilTest.FrontFuncRef);
_newState.StencilTestEnable = stencilTest.TestEnable;
_newState.StencilBackFailOp = stencilTest.BackSFail.Convert();
_newState.StencilBackPassOp = stencilTest.BackDpPass.Convert();
_newState.StencilBackDepthFailOp = stencilTest.BackDpFail.Convert();
_newState.StencilBackCompareOp = stencilTest.BackFunc.Convert();
_newState.StencilFrontFailOp = stencilTest.FrontSFail.Convert();
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
UpdatePassDepthStencil();
SignalStateChange();
} }
public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers) public void SetStorageBuffers(ReadOnlySpan<BufferAssignment> buffers)
@@ -1220,12 +1410,24 @@ namespace Ryujinx.Graphics.Vulkan
{ {
int count = Math.Min(Constants.MaxVertexBuffers, vertexBuffers.Length); int count = Math.Min(Constants.MaxVertexBuffers, vertexBuffers.Length);
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex);
int validCount = 1; int validCount = 1;
if (!_bindingsSet)
{
_newState.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, _supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex);
for (int i = 1; i < count; i++)
{
_newState.Internal.VertexBindingDescriptions[i] = new VertexInputBindingDescription((uint)i);
}
_bindingsSet = true;
}
BufferHandle lastHandle = default; BufferHandle lastHandle = default;
Auto<DisposableBuffer> lastBuffer = null; Auto<DisposableBuffer> lastBuffer = null;
bool vertexBindingDescriptionChanged = false;
bool vertexDescriptionCountChanged = false;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@@ -1244,13 +1446,32 @@ namespace Ryujinx.Graphics.Vulkan
if (vb != null) if (vb != null)
{ {
int binding = i + 1;
int descriptorIndex = validCount++; int descriptorIndex = validCount++;
_newState.Internal.VertexBindingDescriptions[descriptorIndex] = new VertexInputBindingDescription( if (_supportExtDynamic && (!Gd.IsMoltenVk || Gd.SupportsMTL31))
(uint)binding, {
(uint)vertexBuffer.Stride, if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate ||
inputRate); _newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i])
{
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
vertexBindingDescriptionChanged = true;
}
}
else
{
if (_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate != inputRate ||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride != vertexBuffer.Stride ||
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding != _vertexBufferBindings[i])
{
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Binding = (uint)_vertexBufferBindings[i];
_newState.Internal.VertexBindingDescriptions[descriptorIndex].Stride = (uint)vertexBuffer.Stride;
_newState.Internal.VertexBindingDescriptions[descriptorIndex].InputRate = inputRate;
vertexBindingDescriptionChanged = true;
}
}
int vbSize = vertexBuffer.Buffer.Size; int vbSize = vertexBuffer.Buffer.Size;
@@ -1266,7 +1487,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
ref VertexBufferState buffer = ref _vertexBuffers[binding]; ref VertexBufferState buffer = ref _vertexBuffers[_vertexBufferBindings[i]];
int oldScalarAlign = buffer.AttributeScalarAlignment; int oldScalarAlign = buffer.AttributeScalarAlignment;
if (Gd.Capabilities.VertexBufferAlignment < 2 && if (Gd.Capabilities.VertexBufferAlignment < 2 &&
@@ -1283,7 +1504,7 @@ namespace Ryujinx.Graphics.Vulkan
vbSize, vbSize,
vertexBuffer.Stride); vertexBuffer.Stride);
buffer.BindVertexBuffer(Gd, Cbs, (uint)binding, ref _newState, _vertexBufferUpdater); buffer.BindVertexBuffer(Gd, Cbs, (uint)_vertexBufferBindings[i], ref _newState, _vertexBufferUpdater);
} }
} }
else else
@@ -1299,7 +1520,7 @@ namespace Ryujinx.Graphics.Vulkan
vbSize, vbSize,
vertexBuffer.Stride); vertexBuffer.Stride);
_vertexBuffersDirty |= 1UL << binding; _vertexBuffersDirty |= 1UL << _vertexBufferBindings[i];
} }
buffer.AttributeScalarAlignment = oldScalarAlign; buffer.AttributeScalarAlignment = oldScalarAlign;
@@ -1309,11 +1530,19 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs); _vertexBufferUpdater.Commit(Cbs);
_newState.VertexBindingDescriptionsCount = (uint)validCount; if (_newState.VertexBindingDescriptionsCount != validCount)
SignalStateChange(); {
_newState.VertexBindingDescriptionsCount = (uint)validCount;
vertexDescriptionCountChanged = true;
}
if (vertexDescriptionCountChanged || vertexBindingDescriptionChanged)
{
SignalStateChange();
}
} }
public void SetViewports(ReadOnlySpan<Viewport> viewports) public void SetViewports(ReadOnlySpan<Viewport> viewports, bool signalChange = true)
{ {
int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1; int maxViewports = Gd.Capabilities.SupportsMultiView ? Constants.MaxViewports : 1;
int count = Math.Min(maxViewports, viewports.Length); int count = Math.Min(maxViewports, viewports.Length);
@@ -1338,8 +1567,15 @@ namespace Ryujinx.Graphics.Vulkan
Clamp(viewport.DepthFar))); Clamp(viewport.DepthFar)));
} }
_newState.ViewportsCount = (uint)count; if (!_supportExtDynamic)
SignalStateChange(); {
_newState.ViewportsCount = (uint)count;
if (signalChange)
{
SignalStateChange();
}
}
} }
public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) public void SwapBuffer(Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
@@ -1388,7 +1624,7 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length); _vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
_descriptorSetUpdater.SignalCommandBufferChange(); _descriptorSetUpdater.SignalCommandBufferChange();
DynamicState.ForceAllDirty(); DynamicState.ForceAllDirty(Gd);
_currentPipelineHandle = 0; _currentPipelineHandle = 0;
} }
@@ -1546,8 +1782,9 @@ namespace Ryujinx.Graphics.Vulkan
if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{ {
DynamicState.SetFeedbackLoop(aspects); DynamicState.SetFeedbackLoop(aspects);
_newState.FeedbackLoopDynamicState = true;
} }
else else if (Gd.Capabilities.SupportsAttachmentFeedbackLoop)
{ {
_newState.FeedbackLoopAspects = aspects; _newState.FeedbackLoopAspects = aspects;
} }
@@ -1607,7 +1844,14 @@ namespace Ryujinx.Graphics.Vulkan
} }
// Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check. // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
_passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable; if (_supportExtDynamic)
{
_passWritesDepthStencil |= (DynamicState.DepthTestEnable && DynamicState.DepthWriteEnable) || DynamicState.StencilTestEnable;
}
else
{
_passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
}
} }
private bool RecreateGraphicsPipelineIfNeeded() private bool RecreateGraphicsPipelineIfNeeded()
@@ -5,6 +5,7 @@ using System;
using VulkanFormat = Silk.NET.Vulkan.Format; using VulkanFormat = Silk.NET.Vulkan.Format;
using GALFormat = Ryujinx.Graphics.GAL.Format; using GALFormat = Ryujinx.Graphics.GAL.Format;
using PolygonMode = Silk.NET.Vulkan.PolygonMode; using PolygonMode = Silk.NET.Vulkan.PolygonMode;
using PrimitiveTopology = Ryujinx.Graphics.GAL.PrimitiveTopology;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -164,64 +165,84 @@ namespace Ryujinx.Graphics.Vulkan
public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd) public static PipelineState ToVulkanPipelineState(this ProgramPipelineState state, VulkanRenderer gd)
{ {
var extendedDynamicState2 = gd.Capabilities.SupportsExtendedDynamicState2;
var extendedDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
PipelineState pipeline = new(); PipelineState pipeline = new();
pipeline.Initialize(); pipeline.Initialize(gd.Capabilities);
// It is assumed that Dynamic State is enabled when this conversion is used. // It is assumed that Dynamic State is enabled when this conversion is used.
pipeline.CullMode = state.CullEnable ? state.CullMode.Convert() : CullModeFlags.None;
pipeline.DepthBoundsTestEnable = false; // Not implemented. pipeline.DepthBoundsTestEnable = false; // Not implemented.
pipeline.DepthClampEnable = state.DepthClampEnable; pipeline.DepthClampEnable = state.DepthClampEnable;
pipeline.DepthTestEnable = state.DepthTest.TestEnable; pipeline.AlphaToCoverageEnable = state.AlphaToCoverageEnable;
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable; pipeline.AlphaToOneEnable = state.AlphaToOneEnable;
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne; pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
pipeline.FrontFace = state.FrontFace.Convert();
pipeline.HasDepthStencil = state.DepthStencilEnable; pipeline.HasDepthStencil = state.DepthStencilEnable;
pipeline.LineWidth = state.LineWidth;
pipeline.LogicOpEnable = state.LogicOpEnable;
pipeline.LogicOp = state.LogicOp.Convert();
pipeline.PatchControlPoints = state.PatchControlPoints;
pipeline.PolygonMode = PolygonMode.Fill; // Not implemented. pipeline.PolygonMode = PolygonMode.Fill; // Not implemented.
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
pipeline.SamplesCount = (uint)state.SamplesCount;
if (gd.Capabilities.SupportsMultiView)
{
pipeline.ScissorsCount = Constants.MaxViewports;
pipeline.ViewportsCount = Constants.MaxViewports;
}
else
{
pipeline.ScissorsCount = 1;
pipeline.ViewportsCount = 1;
}
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.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
pipeline.Topology = gd.TopologyRemap(state.Topology).Convert(); pipeline.Topology = gd.TopologyRemap(state.Topology).Convert();
if (!extendedDynamicState)
{
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
pipeline.CullMode = state.CullMode.Convert();
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
pipeline.FrontFace = state.FrontFace.Convert();
if (gd.Capabilities.SupportsMultiView)
{
pipeline.ScissorsCount = Constants.MaxViewports;
pipeline.ViewportsCount = Constants.MaxViewports;
}
else
{
pipeline.ScissorsCount = 1;
pipeline.ViewportsCount = 1;
}
pipeline.StencilTestEnable = state.StencilTest.TestEnable;
pipeline.StencilFrontFailOp = state.StencilTest.FrontSFail.Convert();
pipeline.StencilFrontPassOp = state.StencilTest.FrontDpPass.Convert();
pipeline.StencilFrontDepthFailOp = state.StencilTest.FrontDpFail.Convert();
pipeline.StencilFrontCompareOp = state.StencilTest.FrontFunc.Convert();
pipeline.StencilBackFailOp = state.StencilTest.BackSFail.Convert();
pipeline.StencilBackPassOp = state.StencilTest.BackDpPass.Convert();
pipeline.StencilBackDepthFailOp = state.StencilTest.BackDpFail.Convert();
pipeline.StencilBackCompareOp = state.StencilTest.BackFunc.Convert();
}
if (!extendedDynamicState2.ExtendedDynamicState2)
{
pipeline.PrimitiveRestartEnable = state.PrimitiveRestartEnable;
pipeline.RasterizerDiscardEnable = state.RasterizerDiscard;
pipeline.DepthBiasEnable = (state.BiasEnable != 0) &&
(state.DepthBiasFactor != 0 && state.DepthBiasUnits != 0);
}
if (!extendedDynamicState2.ExtendedDynamicState2LogicOp)
{
pipeline.LogicOp = state.LogicOp.Convert();
}
if (!extendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
pipeline.PatchControlPoints = state.PatchControlPoints;
}
pipeline.SamplesCount = (uint)state.SamplesCount;
pipeline.LogicOpEnable = state.LogicOpEnable;
int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); int vaCount = Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount); int vbCount = Math.Min(Constants.MaxVertexBuffers, state.VertexBufferCount);
@@ -247,7 +268,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
int descriptorIndex = 1; int descriptorIndex = 1;
pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, 0, VertexInputRate.Vertex); pipeline.Internal.VertexBindingDescriptions[0] = new VertexInputBindingDescription(0, extendedDynamicState && (!gd.IsMoltenVk || gd.SupportsMTL31) ? null : 0, VertexInputRate.Vertex);
Span<BufferPipelineDescriptor> vertexBuffersSpan = state.VertexBuffers.AsSpan(); Span<BufferPipelineDescriptor> vertexBuffersSpan = state.VertexBuffers.AsSpan();
Span<VertexInputBindingDescription> vertexBindingDescriptionsSpan = pipeline.Internal.VertexBindingDescriptions.AsSpan(); Span<VertexInputBindingDescription> vertexBindingDescriptionsSpan = pipeline.Internal.VertexBindingDescriptions.AsSpan();
@@ -270,7 +291,7 @@ namespace Ryujinx.Graphics.Vulkan
// TODO: Support divisor > 1 // TODO: Support divisor > 1
vertexBindingDescriptionsSpan[descriptorIndex++] = new VertexInputBindingDescription( vertexBindingDescriptionsSpan[descriptorIndex++] = new VertexInputBindingDescription(
(uint)i + 1, (uint)i + 1,
(uint)alignedStride, extendedDynamicState && (!gd.IsMoltenVk || gd.SupportsMTL31) ? null : (uint)alignedStride,
inputRate); inputRate);
} }
} }
@@ -2,6 +2,7 @@ using Ryujinx.Common.Memory;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT; using Silk.NET.Vulkan.Extensions.EXT;
using System; using System;
using System.Numerics;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -10,6 +11,7 @@ namespace Ryujinx.Graphics.Vulkan
private float _depthBiasSlopeFactor; private float _depthBiasSlopeFactor;
private float _depthBiasConstantFactor; private float _depthBiasConstantFactor;
private float _depthBiasClamp; private float _depthBiasClamp;
private bool _depthBiasEnable;
public int ScissorsCount; public int ScissorsCount;
private Array16<Rect2D> _scissors; private Array16<Rect2D> _scissors;
@@ -21,6 +23,23 @@ namespace Ryujinx.Graphics.Vulkan
private uint _frontWriteMask; private uint _frontWriteMask;
private uint _frontReference; private uint _frontReference;
private StencilOp _backFailOp;
private StencilOp _backPassOp;
private StencilOp _backDepthFailOp;
private CompareOp _backCompareOp;
private StencilOp _frontFailOp;
private StencilOp _frontPassOp;
private StencilOp _frontDepthFailOp;
private CompareOp _frontCompareOp;
private float _lineWidth;
public bool StencilTestEnable;
public bool DepthTestEnable;
public bool DepthWriteEnable;
private CompareOp _depthCompareOp;
private Array4<float> _blendConstants; private Array4<float> _blendConstants;
private FeedbackLoopAspects _feedbackLoopAspects; private FeedbackLoopAspects _feedbackLoopAspects;
@@ -28,6 +47,19 @@ namespace Ryujinx.Graphics.Vulkan
public uint ViewportsCount; public uint ViewportsCount;
public Array16<Viewport> Viewports; public Array16<Viewport> Viewports;
public CullModeFlags CullMode;
private FrontFace _frontFace;
private bool _discard;
private LogicOp _logicOp;
private uint _patchControlPoints;
public PrimitiveTopology Topology;
private bool _primitiveRestartEnable;
[Flags] [Flags]
private enum DirtyFlags private enum DirtyFlags
{ {
@@ -38,7 +70,21 @@ namespace Ryujinx.Graphics.Vulkan
Stencil = 1 << 3, Stencil = 1 << 3,
Viewport = 1 << 4, Viewport = 1 << 4,
FeedbackLoop = 1 << 5, FeedbackLoop = 1 << 5,
All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop, CullMode = 1 << 6,
FrontFace = 1 << 7,
DepthTestBool = 1 << 8,
DepthTestCompareOp = 1 << 9,
StencilTestEnableAndStencilOp = 1 << 10,
LineWidth = 1 << 11,
RasterDiscard = 1 << 12,
LogicOp = 1 << 13,
PatchControlPoints = 1 << 14,
PrimitiveRestart = 1 << 15,
PrimitiveTopology = 1 << 16,
DepthBiasEnable = 1 << 17,
Standard = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
Extended = CullMode | FrontFace | DepthTestBool | DepthTestCompareOp | StencilTestEnableAndStencilOp | PrimitiveTopology,
Extended2 = RasterDiscard | PrimitiveRestart | DepthBiasEnable,
} }
private DirtyFlags _dirty; private DirtyFlags _dirty;
@@ -49,7 +95,6 @@ namespace Ryujinx.Graphics.Vulkan
_blendConstants[1] = g; _blendConstants[1] = g;
_blendConstants[2] = b; _blendConstants[2] = b;
_blendConstants[3] = a; _blendConstants[3] = a;
_dirty |= DirtyFlags.Blend; _dirty |= DirtyFlags.Blend;
} }
@@ -62,15 +107,64 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.DepthBias; _dirty |= DirtyFlags.DepthBias;
} }
public void SetDepthBiasEnable(bool enable)
{
_depthBiasEnable = enable;
_dirty |= DirtyFlags.DepthBiasEnable;
}
public void SetScissor(int index, Rect2D scissor) public void SetScissor(int index, Rect2D scissor)
{ {
_scissors[index] = scissor; _scissors[index] = scissor;
_dirty |= DirtyFlags.Scissor; _dirty |= DirtyFlags.Scissor;
} }
public void SetStencilMasks( public void SetDepthTestBool(bool testEnable, bool writeEnable)
uint backCompareMask, {
DepthTestEnable = testEnable;
DepthWriteEnable = writeEnable;
_dirty |= DirtyFlags.DepthTestBool;
}
public void SetDepthTestCompareOp(CompareOp depthTestOp)
{
_depthCompareOp = depthTestOp;
_dirty |= DirtyFlags.DepthTestCompareOp;
}
public void SetStencilTestandOp(
StencilOp backFailOp,
StencilOp backPassOp,
StencilOp backDepthFailOp,
CompareOp backCompareOp,
StencilOp frontFailOp,
StencilOp frontPassOp,
StencilOp frontDepthFailOp,
CompareOp frontCompareOp,
bool stencilTestEnable)
{
_backFailOp = backFailOp;
_backPassOp = backPassOp;
_backDepthFailOp = backDepthFailOp;
_backCompareOp = backCompareOp;
_frontFailOp = frontFailOp;
_frontPassOp = frontPassOp;
_frontDepthFailOp = frontDepthFailOp;
_frontCompareOp = frontCompareOp;
StencilTestEnable = stencilTestEnable;
_dirty |= DirtyFlags.StencilTestEnableAndStencilOp;
}
public void SetStencilTest(bool stencilTestEnable)
{
StencilTestEnable = stencilTestEnable;
_dirty |= DirtyFlags.StencilTestEnableAndStencilOp;
}
public void SetStencilMask(uint backCompareMask,
uint backWriteMask, uint backWriteMask,
uint backReference, uint backReference,
uint frontCompareMask, uint frontCompareMask,
@@ -83,28 +177,46 @@ namespace Ryujinx.Graphics.Vulkan
_frontCompareMask = frontCompareMask; _frontCompareMask = frontCompareMask;
_frontWriteMask = frontWriteMask; _frontWriteMask = frontWriteMask;
_frontReference = frontReference; _frontReference = frontReference;
_dirty |= DirtyFlags.Stencil; _dirty |= DirtyFlags.Stencil;
} }
public void SetViewport(int index, Viewport viewport) public void SetViewport(int index, Viewport viewport)
{ {
Viewports[index] = viewport; Viewports[index] = viewport;
_dirty |= DirtyFlags.Viewport; _dirty |= DirtyFlags.Viewport;
} }
public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount) public void SetViewports(ref Array16<Viewport> viewports, uint viewportsCount)
{ {
Viewports = viewports; if (!Viewports.Equals(viewports) || ViewportsCount != viewportsCount)
ViewportsCount = viewportsCount;
if (ViewportsCount != 0)
{ {
_dirty |= DirtyFlags.Viewport; Viewports = viewports;
ViewportsCount = viewportsCount;
if (ViewportsCount != 0)
{
_dirty |= DirtyFlags.Viewport;
}
} }
} }
public void SetCullMode(CullModeFlags cullMode)
{
CullMode = cullMode;
_dirty |= DirtyFlags.CullMode;
}
public void SetFrontFace(FrontFace frontFace)
{
_frontFace = frontFace;
_dirty |= DirtyFlags.FrontFace;
}
public void SetLineWidth(float width)
{
_lineWidth = width;
_dirty |= DirtyFlags.LineWidth;
}
public void SetFeedbackLoop(FeedbackLoopAspects aspects) public void SetFeedbackLoop(FeedbackLoopAspects aspects)
{ {
_feedbackLoopAspects = aspects; _feedbackLoopAspects = aspects;
@@ -112,43 +224,149 @@ namespace Ryujinx.Graphics.Vulkan
_dirty |= DirtyFlags.FeedbackLoop; _dirty |= DirtyFlags.FeedbackLoop;
} }
public void ForceAllDirty() public void SetRasterizerDiscard(bool discard)
{ {
_dirty = DirtyFlags.All; _discard = discard;
_dirty |= DirtyFlags.RasterDiscard;
}
public void SetPrimitiveRestartEnable(bool primitiveRestart)
{
_primitiveRestartEnable = primitiveRestart;
_dirty |= DirtyFlags.PrimitiveRestart;
}
public void SetPrimitiveTopology(PrimitiveTopology primitiveTopology)
{
Topology = primitiveTopology;
_dirty |= DirtyFlags.PrimitiveTopology;
}
public void SetLogicOp(LogicOp op)
{
_logicOp = op;
_dirty |= DirtyFlags.LogicOp;
}
public void SetPatchControlPoints(uint points)
{
_patchControlPoints = points;
_dirty |= DirtyFlags.PatchControlPoints;
}
public void ForceAllDirty(VulkanRenderer gd)
{
_dirty = DirtyFlags.Standard;
if (gd.Capabilities.SupportsExtendedDynamicState)
{
_dirty |= DirtyFlags.Extended;
}
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2)
{
_dirty |= DirtyFlags.Extended2;
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp)
{
_dirty |= DirtyFlags.LogicOp;
}
if (gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
_dirty |= DirtyFlags.PatchControlPoints;
}
}
if (!gd.IsMoltenVk)
{
_dirty |= DirtyFlags.LineWidth;
}
if (gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
{
_dirty |= DirtyFlags.FeedbackLoop;
}
} }
public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer) public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
{ {
Vk api = gd.Api; if (_dirty == DirtyFlags.None)
if (_dirty.HasFlag(DirtyFlags.Blend))
{ {
RecordBlend(api, commandBuffer); return;
} }
if (_dirty.HasFlag(DirtyFlags.DepthBias)) var api = gd.Api;
{ var extendedStateApi = gd.ExtendedDynamicStateApi;
RecordDepthBias(api, commandBuffer); var extendedState2Api = gd.ExtendedDynamicState2Api;
} var dynamicFeedbackLoopApi = gd.DynamicFeedbackLoopApi;
if (_dirty.HasFlag(DirtyFlags.Scissor)) DirtyFlags dirtyFlags = _dirty;
{
RecordScissor(api, commandBuffer);
}
if (_dirty.HasFlag(DirtyFlags.Stencil)) while (dirtyFlags != DirtyFlags.None)
{ {
RecordStencilMasks(api, commandBuffer); int bitIndex = BitOperations.TrailingZeroCount((uint)dirtyFlags);
} DirtyFlags currentFlag = (DirtyFlags)(1 << bitIndex);
if (_dirty.HasFlag(DirtyFlags.Viewport)) switch (currentFlag)
{ {
RecordViewport(api, commandBuffer); case DirtyFlags.Blend:
} RecordBlend(api, commandBuffer);
break;
case DirtyFlags.DepthBias:
RecordDepthBias(api, commandBuffer);
break;
case DirtyFlags.Scissor:
RecordScissor(gd, commandBuffer);
break;
case DirtyFlags.Stencil:
RecordStencil(api, commandBuffer);
break;
case DirtyFlags.Viewport:
RecordViewport(gd, commandBuffer);
break;
case DirtyFlags.FeedbackLoop:
RecordFeedbackLoop(dynamicFeedbackLoopApi, commandBuffer);
break;
case DirtyFlags.CullMode:
RecordCullMode(extendedStateApi, commandBuffer);
break;
case DirtyFlags.FrontFace:
RecordFrontFace(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthTestBool:
RecordDepthTestBool(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthTestCompareOp:
RecordDepthTestCompareOp(extendedStateApi, commandBuffer);
break;
case DirtyFlags.StencilTestEnableAndStencilOp:
RecordStencilTestAndOp(extendedStateApi, commandBuffer);
break;
case DirtyFlags.LineWidth:
RecordLineWidth(api, commandBuffer);
break;
case DirtyFlags.RasterDiscard:
RecordRasterizationDiscard(extendedState2Api, commandBuffer);
break;
case DirtyFlags.LogicOp:
RecordLogicOp(extendedState2Api, commandBuffer);
break;
case DirtyFlags.PatchControlPoints:
RecordPatchControlPoints(extendedState2Api, commandBuffer);
break;
case DirtyFlags.PrimitiveRestart:
RecordPrimitiveRestartEnable(gd, commandBuffer);
break;
case DirtyFlags.PrimitiveTopology:
RecordPrimitiveTopology(extendedStateApi, commandBuffer);
break;
case DirtyFlags.DepthBiasEnable:
RecordDepthBiasEnable(extendedState2Api, commandBuffer);
break;
}
if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) dirtyFlags &= ~currentFlag;
{
RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
} }
_dirty = DirtyFlags.None; _dirty = DirtyFlags.None;
@@ -164,15 +382,27 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor); api.CmdSetDepthBias(commandBuffer, _depthBiasConstantFactor, _depthBiasClamp, _depthBiasSlopeFactor);
} }
private void RecordScissor(Vk api, CommandBuffer commandBuffer) private readonly void RecordDepthBiasEnable(ExtExtendedDynamicState2 gd, CommandBuffer commandBuffer)
{
gd.CmdSetDepthBiasEnable(commandBuffer, _depthBiasEnable);
}
private void RecordScissor(VulkanRenderer gd, CommandBuffer commandBuffer)
{ {
if (ScissorsCount != 0) if (ScissorsCount != 0)
{ {
api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan()); if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdSetScissorWithCount(commandBuffer, (uint)ScissorsCount, _scissors.AsSpan());
}
else
{
gd.Api.CmdSetScissor(commandBuffer, 0, (uint)ScissorsCount, _scissors.AsSpan());
}
} }
} }
private readonly void RecordStencilMasks(Vk api, CommandBuffer commandBuffer) private readonly void RecordStencil(Vk api, CommandBuffer commandBuffer)
{ {
api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask); api.CmdSetStencilCompareMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backCompareMask);
api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask); api.CmdSetStencilWriteMask(commandBuffer, StencilFaceFlags.FaceBackBit, _backWriteMask);
@@ -182,12 +412,107 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference); api.CmdSetStencilReference(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontReference);
} }
private void RecordViewport(Vk api, CommandBuffer commandBuffer) private readonly void RecordStencilTestAndOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{ {
if (ViewportsCount != 0) api.CmdSetStencilTestEnable(commandBuffer, StencilTestEnable);
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceBackBit, _backFailOp, _backPassOp, _backDepthFailOp, _backCompareOp);
api.CmdSetStencilOp(commandBuffer, StencilFaceFlags.FaceFrontBit, _frontFailOp, _frontPassOp, _frontDepthFailOp, _frontCompareOp);
}
private void RecordViewport(VulkanRenderer gd, CommandBuffer commandBuffer)
{
if (ViewportsCount == 0)
{ {
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan()); return;
} }
if (gd.Capabilities.SupportsExtendedDynamicState)
{
gd.ExtendedDynamicStateApi.CmdSetViewportWithCount(commandBuffer, ViewportsCount, Viewports.AsSpan());
}
else
{
gd.Api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
}
private readonly void RecordCullMode(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetCullMode(commandBuffer, CullMode);
}
private readonly void RecordFrontFace(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetFrontFace(commandBuffer, _frontFace);
}
private readonly void RecordDepthTestBool(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetDepthTestEnable(commandBuffer, DepthTestEnable);
api.CmdSetDepthWriteEnable(commandBuffer, DepthWriteEnable);
}
private readonly void RecordDepthTestCompareOp(ExtExtendedDynamicState api, CommandBuffer commandBuffer)
{
api.CmdSetDepthCompareOp(commandBuffer, _depthCompareOp);
}
private readonly void RecordRasterizationDiscard(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetRasterizerDiscardEnable(commandBuffer, _discard);
}
private readonly void RecordPrimitiveRestartEnable(VulkanRenderer gd, CommandBuffer commandBuffer)
{
bool primitiveRestartEnable = _primitiveRestartEnable;
bool topologySupportsRestart;
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
{
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart ||
Topology != PrimitiveTopology.PatchList;
}
else
{
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
}
primitiveRestartEnable &= topologySupportsRestart;
// Cannot disable primitiveRestartEnable for these Topologies on MacOS.
if (gd.IsMoltenVk)
{
primitiveRestartEnable = true;
}
gd.ExtendedDynamicState2Api.CmdSetPrimitiveRestartEnable(commandBuffer, primitiveRestartEnable);
}
private readonly void RecordPrimitiveTopology(ExtExtendedDynamicState extendedDynamicStateApi, CommandBuffer commandBuffer)
{
extendedDynamicStateApi.CmdSetPrimitiveTopology(commandBuffer, Topology);
}
private readonly void RecordLogicOp(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetLogicOp(commandBuffer, _logicOp);
}
private readonly void RecordPatchControlPoints(ExtExtendedDynamicState2 extendedDynamicState2Api, CommandBuffer commandBuffer)
{
extendedDynamicState2Api.CmdSetPatchControlPoints(commandBuffer, _patchControlPoints);
}
private readonly void RecordLineWidth(Vk api, CommandBuffer commandBuffer)
{
api.CmdSetLineWidth(commandBuffer, _lineWidth);
} }
private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer) private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
@@ -8,11 +8,15 @@ namespace Ryujinx.Graphics.Vulkan
{ {
} }
public void SetRenderTarget(TextureView view, uint width, uint height) public void SetRenderTarget(TextureView view, uint width, uint height, bool signalChange = true)
{ {
CreateFramebuffer(view, width, height); CreateFramebuffer(view, width, height);
CreateRenderPass(); CreateRenderPass();
SignalStateChange();
if (signalChange)
{
SignalStateChange();
}
} }
private void CreateFramebuffer(TextureView view, uint width, uint height) private void CreateFramebuffer(TextureView view, uint width, uint height)
+442 -340
View File
@@ -7,312 +7,258 @@ namespace Ryujinx.Graphics.Vulkan
{ {
struct PipelineState : IDisposable struct PipelineState : IDisposable
{ {
private const int MaxDynamicStatesCount = 9; private const int MaxDynamicStatesCount = 23;
public PipelineUid Internal; public PipelineUid Internal;
public float LineWidth public PolygonMode PolygonMode
{ {
readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 0) & 0xFFFFFFFF)); readonly get => (PolygonMode)((Internal.Id0 >> 0) & 0x3FFFFFFF);
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFFC0000000) | ((ulong)value << 0);
} }
public float DepthBiasClamp public uint StagesCount
{ {
readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id0 >> 32) & 0xFFFFFFFF)); readonly get => (byte)((Internal.Id0 >> 30) & 0xFF);
set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); set => Internal.Id0 = (Internal.Id0 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30);
} }
public float DepthBiasConstantFactor public uint VertexAttributeDescriptionsCount
{ {
readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 0) & 0xFFFFFFFF)); readonly get => (byte)((Internal.Id0 >> 38) & 0xFF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF00000000) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 0); set => Internal.Id0 = (Internal.Id0 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38);
} }
public float DepthBiasSlopeFactor public uint VertexBindingDescriptionsCount
{ {
readonly get => BitConverter.Int32BitsToSingle((int)((Internal.Id1 >> 32) & 0xFFFFFFFF)); readonly get => (byte)((Internal.Id0 >> 46) & 0xFF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFF) | ((ulong)(uint)BitConverter.SingleToInt32Bits(value) << 32); set => Internal.Id0 = (Internal.Id0 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46);
} }
public uint StencilFrontCompareMask public uint ViewportsCount
{
readonly get => (byte)((Internal.Id0 >> 54) & 0xFF);
set => Internal.Id0 = (Internal.Id0 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54);
}
public uint ScissorsCount
{
readonly get => (byte)((Internal.Id1 >> 0) & 0xFF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
}
public uint ColorBlendAttachmentStateCount
{
readonly get => (byte)((Internal.Id1 >> 8) & 0xFF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
}
public PrimitiveTopology Topology
{
readonly get => (PrimitiveTopology)((Internal.Id1 >> 16) & 0xF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
}
public LogicOp LogicOp
{
readonly get => (LogicOp)((Internal.Id1 >> 20) & 0xF);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20);
}
public CompareOp DepthCompareOp
{
readonly get => (CompareOp)((Internal.Id1 >> 24) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24);
}
public StencilOp StencilFrontFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 27) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27);
}
public StencilOp StencilFrontPassOp
{
readonly get => (StencilOp)((Internal.Id1 >> 30) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30);
}
public StencilOp StencilFrontDepthFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 33) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33);
}
public CompareOp StencilFrontCompareOp
{
readonly get => (CompareOp)((Internal.Id1 >> 36) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36);
}
public StencilOp StencilBackFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 39) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39);
}
public StencilOp StencilBackPassOp
{
readonly get => (StencilOp)((Internal.Id1 >> 42) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42);
}
public StencilOp StencilBackDepthFailOp
{
readonly get => (StencilOp)((Internal.Id1 >> 45) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45);
}
public CompareOp StencilBackCompareOp
{
readonly get => (CompareOp)((Internal.Id1 >> 48) & 0x7);
set => Internal.Id1 = (Internal.Id1 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48);
}
public CullModeFlags CullMode
{
readonly get => (CullModeFlags)((Internal.Id1 >> 51) & 0x3);
set => Internal.Id1 = (Internal.Id1 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51);
}
public bool PrimitiveRestartEnable
{
readonly get => ((Internal.Id1 >> 53) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53);
}
public bool DepthClampEnable
{
readonly get => ((Internal.Id1 >> 54) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54);
}
public bool RasterizerDiscardEnable
{
readonly get => ((Internal.Id1 >> 55) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55);
}
public FrontFace FrontFace
{
readonly get => (FrontFace)((Internal.Id1 >> 56) & 0x1);
set => Internal.Id1 = (Internal.Id1 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56);
}
public bool DepthBiasEnable
{
readonly get => ((Internal.Id1 >> 57) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57);
}
public bool DepthTestEnable
{
readonly get => ((Internal.Id1 >> 58) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58);
}
public bool DepthWriteEnable
{
readonly get => ((Internal.Id1 >> 59) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59);
}
public bool DepthBoundsTestEnable
{
readonly get => ((Internal.Id1 >> 60) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60);
}
public bool StencilTestEnable
{
readonly get => ((Internal.Id1 >> 61) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61);
}
public bool LogicOpEnable
{
readonly get => ((Internal.Id1 >> 62) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62);
}
public bool HasDepthStencil
{
readonly get => ((Internal.Id1 >> 63) & 0x1) != 0UL;
set => Internal.Id1 = (Internal.Id1 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
}
public uint PatchControlPoints
{ {
readonly get => (uint)((Internal.Id2 >> 0) & 0xFFFFFFFF); readonly get => (uint)((Internal.Id2 >> 0) & 0xFFFFFFFF);
set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF00000000) | ((ulong)value << 0); set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
} }
public uint StencilFrontWriteMask public uint SamplesCount
{ {
readonly get => (uint)((Internal.Id2 >> 32) & 0xFFFFFFFF); readonly get => (uint)((Internal.Id2 >> 32) & 0xFFFFFFFF);
set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF) | ((ulong)value << 32); set => Internal.Id2 = (Internal.Id2 & 0xFFFFFFFF) | ((ulong)value << 32);
} }
public uint StencilFrontReference
{
readonly get => (uint)((Internal.Id3 >> 0) & 0xFFFFFFFF);
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
}
public uint StencilBackCompareMask
{
readonly get => (uint)((Internal.Id3 >> 32) & 0xFFFFFFFF);
set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFF) | ((ulong)value << 32);
}
public uint StencilBackWriteMask
{
readonly get => (uint)((Internal.Id4 >> 0) & 0xFFFFFFFF);
set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
}
public uint StencilBackReference
{
readonly get => (uint)((Internal.Id4 >> 32) & 0xFFFFFFFF);
set => Internal.Id4 = (Internal.Id4 & 0xFFFFFFFF) | ((ulong)value << 32);
}
public PolygonMode PolygonMode
{
readonly get => (PolygonMode)((Internal.Id5 >> 0) & 0x3FFFFFFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFFFC0000000) | ((ulong)value << 0);
}
public uint StagesCount
{
readonly get => (byte)((Internal.Id5 >> 30) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFFFFFC03FFFFFFF) | ((ulong)value << 30);
}
public uint VertexAttributeDescriptionsCount
{
readonly get => (byte)((Internal.Id5 >> 38) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFFFC03FFFFFFFFF) | ((ulong)value << 38);
}
public uint VertexBindingDescriptionsCount
{
readonly get => (byte)((Internal.Id5 >> 46) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xFFC03FFFFFFFFFFF) | ((ulong)value << 46);
}
public uint ViewportsCount
{
readonly get => (byte)((Internal.Id5 >> 54) & 0xFF);
set => Internal.Id5 = (Internal.Id5 & 0xC03FFFFFFFFFFFFF) | ((ulong)value << 54);
}
public uint ScissorsCount
{
readonly get => (byte)((Internal.Id6 >> 0) & 0xFF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFFFF00) | ((ulong)value << 0);
}
public uint ColorBlendAttachmentStateCount
{
readonly get => (byte)((Internal.Id6 >> 8) & 0xFF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFFF00FF) | ((ulong)value << 8);
}
public PrimitiveTopology Topology
{
readonly get => (PrimitiveTopology)((Internal.Id6 >> 16) & 0xF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFFF0FFFF) | ((ulong)value << 16);
}
public LogicOp LogicOp
{
readonly get => (LogicOp)((Internal.Id6 >> 20) & 0xF);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFFF0FFFFF) | ((ulong)value << 20);
}
public CompareOp DepthCompareOp
{
readonly get => (CompareOp)((Internal.Id6 >> 24) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFF8FFFFFF) | ((ulong)value << 24);
}
public StencilOp StencilFrontFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 27) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFFC7FFFFFF) | ((ulong)value << 27);
}
public StencilOp StencilFrontPassOp
{
readonly get => (StencilOp)((Internal.Id6 >> 30) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFFE3FFFFFFF) | ((ulong)value << 30);
}
public StencilOp StencilFrontDepthFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 33) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFFF1FFFFFFFF) | ((ulong)value << 33);
}
public CompareOp StencilFrontCompareOp
{
readonly get => (CompareOp)((Internal.Id6 >> 36) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFF8FFFFFFFFF) | ((ulong)value << 36);
}
public StencilOp StencilBackFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 39) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFFC7FFFFFFFFF) | ((ulong)value << 39);
}
public StencilOp StencilBackPassOp
{
readonly get => (StencilOp)((Internal.Id6 >> 42) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFFE3FFFFFFFFFF) | ((ulong)value << 42);
}
public StencilOp StencilBackDepthFailOp
{
readonly get => (StencilOp)((Internal.Id6 >> 45) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFFF1FFFFFFFFFFF) | ((ulong)value << 45);
}
public CompareOp StencilBackCompareOp
{
readonly get => (CompareOp)((Internal.Id6 >> 48) & 0x7);
set => Internal.Id6 = (Internal.Id6 & 0xFFF8FFFFFFFFFFFF) | ((ulong)value << 48);
}
public CullModeFlags CullMode
{
readonly get => (CullModeFlags)((Internal.Id6 >> 51) & 0x3);
set => Internal.Id6 = (Internal.Id6 & 0xFFE7FFFFFFFFFFFF) | ((ulong)value << 51);
}
public bool PrimitiveRestartEnable
{
readonly get => ((Internal.Id6 >> 53) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFFDFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 53);
}
public bool DepthClampEnable
{
readonly get => ((Internal.Id6 >> 54) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFFBFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 54);
}
public bool RasterizerDiscardEnable
{
readonly get => ((Internal.Id6 >> 55) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFF7FFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 55);
}
public FrontFace FrontFace
{
readonly get => (FrontFace)((Internal.Id6 >> 56) & 0x1);
set => Internal.Id6 = (Internal.Id6 & 0xFEFFFFFFFFFFFFFF) | ((ulong)value << 56);
}
public bool DepthBiasEnable
{
readonly get => ((Internal.Id6 >> 57) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFDFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 57);
}
public bool DepthTestEnable
{
readonly get => ((Internal.Id6 >> 58) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xFBFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 58);
}
public bool DepthWriteEnable
{
readonly get => ((Internal.Id6 >> 59) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xF7FFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 59);
}
public bool DepthBoundsTestEnable
{
readonly get => ((Internal.Id6 >> 60) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xEFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 60);
}
public bool StencilTestEnable
{
readonly get => ((Internal.Id6 >> 61) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xDFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 61);
}
public bool LogicOpEnable
{
readonly get => ((Internal.Id6 >> 62) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0xBFFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 62);
}
public bool HasDepthStencil
{
readonly get => ((Internal.Id6 >> 63) & 0x1) != 0UL;
set => Internal.Id6 = (Internal.Id6 & 0x7FFFFFFFFFFFFFFF) | ((value ? 1UL : 0UL) << 63);
}
public uint PatchControlPoints
{
readonly get => (uint)((Internal.Id7 >> 0) & 0xFFFFFFFF);
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF00000000) | ((ulong)value << 0);
}
public uint SamplesCount
{
readonly get => (uint)((Internal.Id7 >> 32) & 0xFFFFFFFF);
set => Internal.Id7 = (Internal.Id7 & 0xFFFFFFFF) | ((ulong)value << 32);
}
public bool AlphaToCoverageEnable public bool AlphaToCoverageEnable
{ {
readonly get => ((Internal.Id8 >> 0) & 0x1) != 0UL; readonly get => ((Internal.Id3 >> 0) & 0x1) != 0UL;
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFFE) | ((value ? 1UL : 0UL) << 0);
} }
public bool AlphaToOneEnable public bool AlphaToOneEnable
{ {
readonly get => ((Internal.Id8 >> 1) & 0x1) != 0UL; readonly get => ((Internal.Id3 >> 1) & 0x1) != 0UL;
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFFD) | ((value ? 1UL : 0UL) << 1);
} }
public bool AdvancedBlendSrcPreMultiplied public bool AdvancedBlendSrcPreMultiplied
{ {
readonly get => ((Internal.Id8 >> 2) & 0x1) != 0UL; readonly get => ((Internal.Id3 >> 2) & 0x1) != 0UL;
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFFB) | ((value ? 1UL : 0UL) << 2);
} }
public bool AdvancedBlendDstPreMultiplied public bool AdvancedBlendDstPreMultiplied
{ {
readonly get => ((Internal.Id8 >> 3) & 0x1) != 0UL; readonly get => ((Internal.Id3 >> 3) & 0x1) != 0UL;
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFF7) | ((value ? 1UL : 0UL) << 3);
} }
public BlendOverlapEXT AdvancedBlendOverlap public BlendOverlapEXT AdvancedBlendOverlap
{ {
readonly get => (BlendOverlapEXT)((Internal.Id8 >> 4) & 0x3); readonly get => (BlendOverlapEXT)((Internal.Id3 >> 4) & 0x3);
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
} }
public bool DepthMode public bool DepthMode
{ {
readonly get => ((Internal.Id8 >> 6) & 0x1) != 0UL; readonly get => ((Internal.Id3 >> 6) & 0x1) != 0UL;
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
} }
public FeedbackLoopAspects FeedbackLoopAspects public FeedbackLoopAspects FeedbackLoopAspects
{ {
readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3); readonly get => (FeedbackLoopAspects)((Internal.Id3 >> 7) & 0x3);
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7); set => Internal.Id3 = (Internal.Id3 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
} }
public bool HasTessellationControlShader; public bool HasTessellationControlShader;
public bool FeedbackLoopDynamicState;
public NativeArray<PipelineShaderStageCreateInfo> Stages; public NativeArray<PipelineShaderStageCreateInfo> Stages;
public PipelineLayout PipelineLayout; public PipelineLayout PipelineLayout;
public SpecData SpecializationData; public SpecData SpecializationData;
private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2; private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2;
public void Initialize() private bool _supportsExtDynamicState;
private PhysicalDeviceExtendedDynamicState2FeaturesEXT _supportsExtDynamicState2;
private bool _supportsFeedBackLoopDynamicState;
private uint _blendEnables;
public void Initialize(HardwareCapabilities capabilities)
{ {
HasTessellationControlShader = false; HasTessellationControlShader = false;
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages); Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
@@ -321,9 +267,54 @@ namespace Ryujinx.Graphics.Vulkan
AdvancedBlendDstPreMultiplied = true; AdvancedBlendDstPreMultiplied = true;
AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt; AdvancedBlendOverlap = BlendOverlapEXT.UncorrelatedExt;
LineWidth = 1f;
SamplesCount = 1;
DepthMode = true; DepthMode = true;
PolygonMode = PolygonMode.Fill;
DepthBoundsTestEnable = false;
_supportsExtDynamicState = capabilities.SupportsExtendedDynamicState;
_supportsExtDynamicState2 = capabilities.SupportsExtendedDynamicState2;
_supportsFeedBackLoopDynamicState = capabilities.SupportsDynamicAttachmentFeedbackLoop;
if (_supportsExtDynamicState)
{
StencilFrontFailOp = 0;
StencilFrontPassOp = 0;
StencilFrontDepthFailOp = 0;
StencilFrontCompareOp = 0;
StencilBackFailOp = 0;
StencilBackPassOp = 0;
StencilBackDepthFailOp = 0;
StencilBackCompareOp = 0;
ViewportsCount = 0;
ScissorsCount = 0;
CullMode = 0;
FrontFace = 0;
DepthTestEnable = false;
DepthWriteEnable = false;
DepthCompareOp = 0;
StencilTestEnable = false;
}
if (_supportsExtDynamicState2.ExtendedDynamicState2)
{
PrimitiveRestartEnable = false;
DepthBiasEnable = false;
RasterizerDiscardEnable = false;
if (_supportsExtDynamicState2.ExtendedDynamicState2LogicOp)
{
LogicOp = 0;
}
if (_supportsExtDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
PatchControlPoints = 0;
}
}
} }
public unsafe Auto<DisposablePipeline> CreateComputePipeline( public unsafe Auto<DisposablePipeline> CreateComputePipeline(
@@ -341,7 +332,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.ComputePipelineCreateInfo, SType = StructureType.ComputePipelineCreateInfo,
Stage = Stages[0], Stage = Stages[0],
BasePipelineIndex = -1,
Layout = PipelineLayout, Layout = PipelineLayout,
}; };
@@ -377,6 +367,74 @@ namespace Ryujinx.Graphics.Vulkan
return pipeline; return pipeline;
} }
private void CheckCapability(VulkanRenderer gd)
{
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
// so we need to force disable them here.
LogicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed);
if (!_supportsExtDynamicState)
{
DepthWriteEnable = DepthWriteEnable && DepthTestEnable;
DepthCompareOp = DepthTestEnable ? DepthCompareOp : default;
}
if (!_supportsExtDynamicState2.ExtendedDynamicState2LogicOp)
{
LogicOp = LogicOpEnable ? LogicOp : default;
}
if (!_supportsExtDynamicState2.ExtendedDynamicState2)
{
bool topologySupportsRestart;
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
{
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart ||
Topology != PrimitiveTopology.PatchList;
}
else
{
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
}
PrimitiveRestartEnable &= topologySupportsRestart;
}
if (_supportsExtDynamicState)
{
Topology = Topology.ConvertToClass();
}
Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology;
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
{
_blendEnables = 0;
// Blend can't be enabled for integer formats, so let's make sure it is disabled.
uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask;
while (attachmentIntegerFormatMask != 0)
{
int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
if (Internal.ColorBlendAttachmentState[i].BlendEnable)
{
_blendEnables |= 1u << i;
}
Internal.ColorBlendAttachmentState[i].BlendEnable = false;
attachmentIntegerFormatMask &= ~(1u << i);
}
}
}
public unsafe Auto<DisposablePipeline> CreateGraphicsPipeline( public unsafe Auto<DisposablePipeline> CreateGraphicsPipeline(
VulkanRenderer gd, VulkanRenderer gd,
Device device, Device device,
@@ -385,6 +443,17 @@ namespace Ryujinx.Graphics.Vulkan
RenderPass renderPass, RenderPass renderPass,
bool throwOnError = false) bool throwOnError = false)
{ {
CheckCapability(gd);
// Using patches topology without a tessellation shader is invalid.
// If we find such a case, return null pipeline to skip the draw.
if (Topology == PrimitiveTopology.PatchList && !HasTessellationControlShader)
{
program.AddGraphicsPipeline(ref Internal, null);
return null;
}
if (program.TryGetGraphicsPipeline(ref Internal, out Auto<DisposablePipeline> pipeline)) if (program.TryGetGraphicsPipeline(ref Internal, out Auto<DisposablePipeline> pipeline))
{ {
return pipeline; return pipeline;
@@ -394,7 +463,7 @@ namespace Ryujinx.Graphics.Vulkan
bool isMoltenVk = gd.IsMoltenVk; bool isMoltenVk = gd.IsMoltenVk;
if (isMoltenVk) if (isMoltenVk && !_supportsExtDynamicState)
{ {
UpdateVertexAttributeDescriptions(gd); UpdateVertexAttributeDescriptions(gd);
} }
@@ -408,7 +477,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PipelineVertexInputStateCreateInfo, SType = StructureType.PipelineVertexInputStateCreateInfo,
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount, VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, PVertexAttributeDescriptions = isMoltenVk && !_supportsExtDynamicState ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
VertexBindingDescriptionCount = VertexBindingDescriptionsCount, VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
PVertexBindingDescriptions = pVertexBindingDescriptions, PVertexBindingDescriptions = pVertexBindingDescriptions,
}; };
@@ -444,33 +513,22 @@ namespace Ryujinx.Graphics.Vulkan
PipelineInputAssemblyStateCreateInfo inputAssemblyState = new() PipelineInputAssemblyStateCreateInfo inputAssemblyState = new()
{ {
SType = StructureType.PipelineInputAssemblyStateCreateInfo, SType = StructureType.PipelineInputAssemblyStateCreateInfo,
PrimitiveRestartEnable = primitiveRestartEnable, Topology = Topology,
Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology,
}; };
PipelineTessellationStateCreateInfo tessellationState = new() PipelineTessellationStateCreateInfo tessellationState;
{
SType = StructureType.PipelineTessellationStateCreateInfo,
PatchControlPoints = PatchControlPoints,
};
PipelineRasterizationStateCreateInfo rasterizationState = new() PipelineRasterizationStateCreateInfo rasterizationState = new()
{ {
SType = StructureType.PipelineRasterizationStateCreateInfo, SType = StructureType.PipelineRasterizationStateCreateInfo,
DepthClampEnable = DepthClampEnable, DepthClampEnable = DepthClampEnable,
RasterizerDiscardEnable = RasterizerDiscardEnable, // When widelines feature is not supported it must be 1.0f, this will be ignored if Line Width dynamic state is supported
PolygonMode = PolygonMode, LineWidth = 1.0f,
LineWidth = LineWidth,
CullMode = CullMode,
FrontFace = FrontFace,
DepthBiasEnable = DepthBiasEnable,
}; };
PipelineViewportStateCreateInfo viewportState = new() PipelineViewportStateCreateInfo viewportState = new()
{ {
SType = StructureType.PipelineViewportStateCreateInfo, SType = StructureType.PipelineViewportStateCreateInfo,
ViewportCount = ViewportsCount,
ScissorCount = ScissorsCount,
}; };
if (gd.Capabilities.SupportsDepthClipControl) if (gd.Capabilities.SupportsDepthClipControl)
@@ -494,64 +552,70 @@ namespace Ryujinx.Graphics.Vulkan
AlphaToOneEnable = AlphaToOneEnable, AlphaToOneEnable = AlphaToOneEnable,
}; };
StencilOpState stencilFront = new(
StencilFrontFailOp,
StencilFrontPassOp,
StencilFrontDepthFailOp,
StencilFrontCompareOp);
StencilOpState stencilBack = new(
StencilBackFailOp,
StencilBackPassOp,
StencilBackDepthFailOp,
StencilBackCompareOp);
PipelineDepthStencilStateCreateInfo depthStencilState = new() PipelineDepthStencilStateCreateInfo depthStencilState = new()
{ {
SType = StructureType.PipelineDepthStencilStateCreateInfo, SType = StructureType.PipelineDepthStencilStateCreateInfo,
DepthTestEnable = DepthTestEnable, DepthBoundsTestEnable = DepthBoundsTestEnable,
DepthWriteEnable = DepthWriteEnable,
DepthCompareOp = DepthCompareOp,
DepthBoundsTestEnable = false,
StencilTestEnable = StencilTestEnable,
Front = stencilFront,
Back = stencilBack,
}; };
uint blendEnables = 0; if (!_supportsExtDynamicState)
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
{ {
// Blend can't be enabled for integer formats, so let's make sure it is disabled. rasterizationState.CullMode = CullMode;
uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask; rasterizationState.FrontFace = FrontFace;
while (attachmentIntegerFormatMask != 0) viewportState.ViewportCount = ViewportsCount;
{ viewportState.ScissorCount = ScissorsCount;
int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
if (Internal.ColorBlendAttachmentState[i].BlendEnable) StencilOpState stencilFront = new(
{ StencilFrontFailOp,
blendEnables |= 1u << i; StencilFrontPassOp,
} StencilFrontDepthFailOp,
StencilFrontCompareOp);
Internal.ColorBlendAttachmentState[i].BlendEnable = false; StencilOpState stencilBack = new(
attachmentIntegerFormatMask &= ~(1u << i); StencilBackFailOp,
} StencilBackPassOp,
StencilBackDepthFailOp,
StencilBackCompareOp);
depthStencilState.Front = stencilFront;
depthStencilState.Back = stencilBack;
depthStencilState.StencilTestEnable = StencilTestEnable;
depthStencilState.DepthTestEnable = DepthTestEnable;
depthStencilState.DepthWriteEnable = DepthWriteEnable;
depthStencilState.DepthCompareOp = DepthCompareOp;
} }
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats, if (!_supportsExtDynamicState2.ExtendedDynamicState2)
// so we need to force disable them here. {
bool logicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed);
inputAssemblyState.PrimitiveRestartEnable = PrimitiveRestartEnable;
rasterizationState.DepthBiasEnable = DepthBiasEnable;
rasterizationState.RasterizerDiscardEnable = RasterizerDiscardEnable;
}
if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
tessellationState = new PipelineTessellationStateCreateInfo
{
SType = StructureType.PipelineTessellationStateCreateInfo,
PatchControlPoints = PatchControlPoints,
};
}
PipelineColorBlendStateCreateInfo colorBlendState = new() PipelineColorBlendStateCreateInfo colorBlendState = new()
{ {
SType = StructureType.PipelineColorBlendStateCreateInfo, SType = StructureType.PipelineColorBlendStateCreateInfo,
LogicOpEnable = logicOpEnable,
LogicOp = LogicOp,
AttachmentCount = ColorBlendAttachmentStateCount, AttachmentCount = ColorBlendAttachmentStateCount,
PAttachments = pColorBlendAttachmentState, PAttachments = pColorBlendAttachmentState,
LogicOpEnable = LogicOpEnable,
}; };
if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp)
{
colorBlendState.LogicOp = LogicOp;
}
if (!AdvancedBlendSrcPreMultiplied || if (!AdvancedBlendSrcPreMultiplied ||
!AdvancedBlendDstPreMultiplied || !AdvancedBlendDstPreMultiplied ||
AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt) AdvancedBlendOverlap != BlendOverlapEXT.UncorrelatedExt)
@@ -567,66 +631,99 @@ namespace Ryujinx.Graphics.Vulkan
colorBlendState.PNext = &colorBlendAdvancedState; colorBlendState.PNext = &colorBlendAdvancedState;
} }
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount]; DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
int dynamicStatesCount = 7; uint dynamicStatesCount = 7;
dynamicStates[0] = DynamicState.Viewport; dynamicStates[0] = DynamicState.Viewport;
dynamicStates[1] = DynamicState.Scissor; dynamicStates[1] = DynamicState.Scissor;
dynamicStates[2] = DynamicState.DepthBias; dynamicStates[2] = DynamicState.StencilCompareMask;
dynamicStates[3] = DynamicState.StencilCompareMask; dynamicStates[3] = DynamicState.StencilWriteMask;
dynamicStates[4] = DynamicState.StencilWriteMask; dynamicStates[4] = DynamicState.StencilReference;
dynamicStates[5] = DynamicState.StencilReference; dynamicStates[5] = DynamicState.BlendConstants;
dynamicStates[6] = DynamicState.BlendConstants; dynamicStates[6] = DynamicState.DepthBias;
if (supportsExtDynamicState && (gd.SupportsMTL31 || !gd.IsMoltenVk)) if (!isMoltenVk)
{ {
// Requires Metal 3.1 and new MoltenVK, however extended dynamic states extension is not //LineWidth dynamic state is only supported on macOS when using Metal Private API on newer version of MoltenVK
// available on older versions of MVK, so we can safely check only OS version. dynamicStates[dynamicStatesCount++] = DynamicState.LineWidth;
dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
} }
if (supportsFeedbackLoopDynamicState) if (_supportsExtDynamicState)
{ {
dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt; if (gd.SupportsMTL31 || !gd.IsMoltenVk)
{
// Requires Metal 3.1 and new MoltenVK, however extended dynamic states extension is not
// available on older versions of MVK, so we can safely check only OS version.
dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
}
dynamicStates[0] = DynamicState.ViewportWithCountExt;
dynamicStates[1] = DynamicState.ScissorWithCountExt;
dynamicStates[dynamicStatesCount++] = DynamicState.CullModeExt;
dynamicStates[dynamicStatesCount++] = DynamicState.FrontFaceExt;
dynamicStates[dynamicStatesCount++] = DynamicState.DepthTestEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.DepthWriteEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.DepthCompareOpExt;
dynamicStates[dynamicStatesCount++] = DynamicState.StencilTestEnableExt;
dynamicStates[dynamicStatesCount++] = DynamicState.StencilOpExt;
dynamicStates[dynamicStatesCount++] = DynamicState.PrimitiveTopologyExt;
} }
PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = new() if (_supportsExtDynamicState2.ExtendedDynamicState2)
{ {
SType = StructureType.PipelineDynamicStateCreateInfo, dynamicStates[dynamicStatesCount++] = DynamicState.DepthBiasEnableExt;
DynamicStateCount = (uint)dynamicStatesCount, dynamicStates[dynamicStatesCount++] = DynamicState.RasterizerDiscardEnableExt;
PDynamicStates = dynamicStates, dynamicStates[dynamicStatesCount++] = DynamicState.PrimitiveRestartEnableExt;
};
PipelineCreateFlags flags = 0; if (_supportsExtDynamicState2.ExtendedDynamicState2LogicOp)
{
dynamicStates[dynamicStatesCount++] = DynamicState.LogicOpExt;
}
if (_supportsExtDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
dynamicStates[dynamicStatesCount++] = DynamicState.PatchControlPointsExt;
}
}
if (gd.Capabilities.SupportsAttachmentFeedbackLoop) PipelineCreateFlags pipelineCreateFlags = 0;
if (gd.Capabilities.SupportsAttachmentFeedbackLoop && !_supportsFeedBackLoopDynamicState)
{ {
FeedbackLoopAspects aspects = FeedbackLoopAspects; FeedbackLoopAspects aspects = FeedbackLoopAspects;
if ((aspects & FeedbackLoopAspects.Color) != 0) if ((aspects & FeedbackLoopAspects.Color) != 0)
{ {
flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt; pipelineCreateFlags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
} }
if ((aspects & FeedbackLoopAspects.Depth) != 0) if ((aspects & FeedbackLoopAspects.Depth) != 0)
{ {
flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt; pipelineCreateFlags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
} }
} }
if (_supportsFeedBackLoopDynamicState && FeedbackLoopDynamicState)
{
dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
FeedbackLoopDynamicState = false;
}
PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = new()
{
SType = StructureType.PipelineDynamicStateCreateInfo,
DynamicStateCount = dynamicStatesCount,
PDynamicStates = dynamicStates,
};
GraphicsPipelineCreateInfo pipelineCreateInfo = new() GraphicsPipelineCreateInfo pipelineCreateInfo = new()
{ {
SType = StructureType.GraphicsPipelineCreateInfo, SType = StructureType.GraphicsPipelineCreateInfo,
Flags = flags,
StageCount = StagesCount, StageCount = StagesCount,
Flags = pipelineCreateFlags,
PStages = Stages.Pointer, PStages = Stages.Pointer,
PVertexInputState = &vertexInputState, PVertexInputState = &vertexInputState,
PInputAssemblyState = &inputAssemblyState, PInputAssemblyState = &inputAssemblyState,
PTessellationState = &tessellationState,
PViewportState = &viewportState, PViewportState = &viewportState,
PRasterizationState = &rasterizationState, PRasterizationState = &rasterizationState,
PMultisampleState = &multisampleState, PMultisampleState = &multisampleState,
@@ -637,6 +734,11 @@ namespace Ryujinx.Graphics.Vulkan
RenderPass = renderPass, RenderPass = renderPass,
}; };
if (!gd.Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints)
{
pipelineCreateInfo.PTessellationState = &tessellationState;
}
Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle); Result result = gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle);
if (throwOnError) if (throwOnError)
@@ -649,21 +751,21 @@ namespace Ryujinx.Graphics.Vulkan
return null; return null;
} }
// Restore previous blend enable values if we changed it.
while (blendEnables != 0)
{
int i = BitOperations.TrailingZeroCount(blendEnables);
Internal.ColorBlendAttachmentState[i].BlendEnable = true;
blendEnables &= ~(1u << i);
}
} }
pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle)); pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));
program.AddGraphicsPipeline(ref Internal, pipeline); program.AddGraphicsPipeline(ref Internal, pipeline);
// Restore previous blend enable values if we changed it.
while (_blendEnables != 0)
{
int i = BitOperations.TrailingZeroCount(_blendEnables);
Internal.ColorBlendAttachmentState[i].BlendEnable = true;
_blendEnables &= ~(1u << i);
}
return pipeline; return pipeline;
} }
+8 -21
View File
@@ -11,23 +11,17 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public ulong Id0; public ulong Id0;
public ulong Id1; public ulong Id1;
public ulong Id2; public ulong Id2;
public ulong Id3; public ulong Id3;
public ulong Id4; private readonly uint VertexAttributeDescriptionsCount => (byte)((Id0 >> 38) & 0xFF);
public ulong Id5; private readonly uint VertexBindingDescriptionsCount => (byte)((Id0 >> 46) & 0xFF);
public ulong Id6; private readonly uint ColorBlendAttachmentStateCount => (byte)((Id1 >> 8) & 0xFF);
private readonly bool HasDepthStencil => ((Id1 >> 63) & 0x1) != 0UL;
public ulong Id7;
public ulong Id8;
private readonly uint VertexAttributeDescriptionsCount => (byte)((Id5 >> 38) & 0xFF);
private readonly uint VertexBindingDescriptionsCount => (byte)((Id5 >> 46) & 0xFF);
private readonly uint ColorBlendAttachmentStateCount => (byte)((Id6 >> 8) & 0xFF);
private readonly bool HasDepthStencil => ((Id6 >> 63) & 0x1) != 0UL;
public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions; public Array32<VertexInputAttributeDescription> VertexAttributeDescriptions;
public Array33<VertexInputBindingDescription> VertexBindingDescriptions; public Array32<VertexInputBindingDescription> VertexBindingDescriptions;
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState; public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
public Array9<Format> AttachmentFormats; public Array9<Format> AttachmentFormats;
public uint AttachmentIntegerFormatMask; public uint AttachmentIntegerFormatMask;
@@ -40,9 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
public bool Equals(ref PipelineUid other) public bool Equals(ref PipelineUid other)
{ {
if (!Unsafe.As<ulong, Vector256<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id0)) || if (!Unsafe.As<ulong, Vector256<byte>>(ref Id0).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id0)))
!Unsafe.As<ulong, Vector256<byte>>(ref Id4).Equals(Unsafe.As<ulong, Vector256<byte>>(ref other.Id4)) ||
!Unsafe.As<ulong, Vector128<byte>>(ref Id7).Equals(Unsafe.As<ulong, Vector128<byte>>(ref other.Id7)))
{ {
return false; return false;
} }
@@ -80,12 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
ulong hash64 = Id0 * 23 ^ ulong hash64 = Id0 * 23 ^
Id1 * 23 ^ Id1 * 23 ^
Id2 * 23 ^ Id2 * 23 ^
Id3 * 23 ^ Id3 * 23;
Id4 * 23 ^
Id5 * 23 ^
Id6 * 23 ^
Id7 * 23 ^
Id8 * 23;
ReadOnlySpan<VertexInputAttributeDescription> vertexAttributeDescriptionsSpan = VertexAttributeDescriptions.AsSpan(); ReadOnlySpan<VertexInputAttributeDescription> vertexAttributeDescriptionsSpan = VertexAttributeDescriptions.AsSpan();
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using PrimitiveTopology = Silk.NET.Vulkan.PrimitiveTopology;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -60,7 +61,7 @@ namespace Ryujinx.Graphics.Vulkan
private ProgramPipelineState _state; private ProgramPipelineState _state;
private DisposableRenderPass _dummyRenderPass; private DisposableRenderPass _dummyRenderPass;
private ShaderCompilationRequest _compileRequest; private readonly Task _compileTask;
private bool _firstBackgroundUse; private bool _firstBackgroundUse;
public ShaderCollection( public ShaderCollection(
@@ -139,7 +140,7 @@ namespace Ryujinx.Graphics.Vulkan
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows. // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures; UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
_compileRequest = new ShaderCompilationRequest(Task.CompletedTask); _compileTask = Task.CompletedTask;
_firstBackgroundUse = false; _firstBackgroundUse = false;
} }
@@ -153,9 +154,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_state = state; _state = state;
_compileRequest = gd.ShaderCompilationQueue != null _compileTask = BackgroundCompilation();
? gd.ShaderCompilationQueue.Add(BackgroundCompilation)
: new ShaderCompilationRequest(BackgroundCompilationAsync());
_firstBackgroundUse = !fromCache; _firstBackgroundUse = !fromCache;
} }
@@ -460,25 +459,10 @@ namespace Ryujinx.Graphics.Vulkan
return (buffer, texture); return (buffer, texture);
} }
private async Task BackgroundCompilationAsync() private async Task BackgroundCompilation()
{ {
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask)); await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
BackgroundCompilationImpl();
}
private void BackgroundCompilation()
{
foreach (var shader in _shaders)
{
shader.CompileTask.Wait();
}
BackgroundCompilationImpl();
}
private void BackgroundCompilationImpl()
{
if (Array.Exists(_shaders, shader => shader.CompileStatus == ProgramLinkStatus.Failure)) if (Array.Exists(_shaders, shader => shader.CompileStatus == ProgramLinkStatus.Failure))
{ {
LinkStatus = ProgramLinkStatus.Failure; LinkStatus = ProgramLinkStatus.Failure;
@@ -555,7 +539,7 @@ namespace Ryujinx.Graphics.Vulkan
public void CreateBackgroundComputePipeline() public void CreateBackgroundComputePipeline()
{ {
PipelineState pipeline = new(); PipelineState pipeline = new();
pipeline.Initialize(); pipeline.Initialize(_gd.Capabilities);
pipeline.Stages[0] = _shaders[0].GetInfo(); pipeline.Stages[0] = _shaders[0].GetInfo();
pipeline.StagesCount = 1; pipeline.StagesCount = 1;
@@ -621,11 +605,11 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
if (!_compileRequest.IsCompleted) if (!_compileTask.IsCompleted)
{ {
if (blocking) if (blocking)
{ {
_compileRequest.Wait(); _compileTask.Wait();
if (LinkStatus == ProgramLinkStatus.Failure) if (LinkStatus == ProgramLinkStatus.Failure)
{ {
@@ -1,131 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Graphics.Vulkan
{
class ShaderCompilationQueue
{
private const int MaxParallelCompilations = 8;
private const int MaxThreadStackSize = 2 * 1024 * 1024; // MB
private struct Request
{
public readonly ulong Id;
public readonly Action Callback;
public Request(ulong id, Action callback)
{
Id = id;
Callback = callback;
}
}
private readonly Thread[] _workerThreads;
private readonly CancellationTokenSource _cts;
private readonly BlockingCollection<Request>[] _queues;
private readonly ulong[] _finishedIds;
private ulong _currentId;
private int _currentQueueIndex;
public ShaderCompilationQueue()
{
_workerThreads = new Thread[MaxParallelCompilations];
_queues = new BlockingCollection<Request>[MaxParallelCompilations];
_finishedIds = new ulong[MaxParallelCompilations];
_cts = new CancellationTokenSource();
for (int i = 0; i < MaxParallelCompilations; i++)
{
_queues[i] = new BlockingCollection<Request>();
Thread thread = new Thread(DoWork, MaxThreadStackSize) { Name = $"BackgroundShaderCompiler.{i}" };
thread.IsBackground = true;
thread.Start(i);
_workerThreads[i] = thread;
}
}
private void DoWork(object threadId)
{
int queueIndex = (int)threadId;
try
{
var queue = _queues[queueIndex];
foreach (var request in queue.GetConsumingEnumerable(_cts.Token))
{
request.Callback();
lock (queue)
{
_finishedIds[queueIndex] = request.Id;
Monitor.PulseAll(queue);
}
}
}
catch (OperationCanceledException)
{
}
}
public ShaderCompilationRequest Add(Action callback)
{
ulong newId = Interlocked.Increment(ref _currentId);
// Let's keep rotating between the queues to increase the chances
// that the selected queue thread is currently idle.
int queueIndex = Interlocked.Increment(ref _currentQueueIndex) % MaxParallelCompilations;
_queues[queueIndex].Add(new Request(newId, callback));
return new ShaderCompilationRequest(this, queueIndex, newId);
}
public void Wait(int queueIndex, ulong id)
{
var queue = _queues[queueIndex];
lock (queue)
{
while (_finishedIds[queueIndex] < id)
{
Monitor.Wait(queue);
}
}
}
public bool IsCompleted(int queueIndex, ulong id)
{
var queue = _queues[queueIndex];
lock (queue)
{
return _finishedIds[queueIndex] >= id;
}
}
public void Dispose()
{
for (int i = 0; i < MaxParallelCompilations; i++)
{
_queues[i].CompleteAdding();
}
_cts.Cancel();
for (int i = 0; i < MaxParallelCompilations; i++)
{
_workerThreads[i].Join();
_queues[i].Dispose();
}
_cts.Dispose();
}
}
}
@@ -1,55 +0,0 @@
using System.Threading.Tasks;
namespace Ryujinx.Graphics.Vulkan
{
struct ShaderCompilationRequest
{
private readonly Task _task;
private readonly ShaderCompilationQueue _queue;
private readonly int _queueIndex;
private readonly ulong _requestId;
public bool IsCompleted
{
get
{
if (_task != null)
{
return _task.IsCompleted;
}
else
{
return _queue.IsCompleted(_queueIndex, _requestId);
}
}
}
public ShaderCompilationRequest(Task task)
{
_task = task;
_queue = null;
_queueIndex = 0;
_requestId = 0;
}
public ShaderCompilationRequest(ShaderCompilationQueue queue, int queueIndex, ulong requestId)
{
_task = null;
_queue = queue;
_queueIndex = queueIndex;
_requestId = requestId;
}
public void Wait()
{
if (_task != null)
{
_task.Wait();
}
else
{
_queue.Wait(_queueIndex, _requestId);
}
}
}
}
+1 -1
View File
@@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan
public void FreeCompleted() public void FreeCompleted()
{ {
FenceHolder signalledFence = null; FenceHolder signalledFence = null;
while (_pendingCopies.TryPeek(out PendingCopy pc) && pc.Fence != null && (pc.Fence == signalledFence || pc.Fence.IsSignaledLazy())) while (_pendingCopies.TryPeek(out PendingCopy pc) && pc.Fence != null && (pc.Fence == signalledFence || pc.Fence.IsSignaled()))
{ {
signalledFence = pc.Fence; // Already checked - don't need to do it again. signalledFence = pc.Fence; // Already checked - don't need to do it again.
PendingCopy dequeued = _pendingCopies.Dequeue(); PendingCopy dequeued = _pendingCopies.Dequeue();
@@ -71,7 +71,10 @@ namespace Ryujinx.Graphics.Vulkan
_buffer = autoBuffer; _buffer = autoBuffer;
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride; if (!gd.Capabilities.SupportsExtendedDynamicState)
{
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
}
} }
return; return;
@@ -79,8 +82,11 @@ namespace Ryujinx.Graphics.Vulkan
autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size); autoBuffer = gd.BufferManager.GetBuffer(cbs.CommandBuffer, _handle, false, out int size);
// The original stride must be reapplied in case it was rewritten. if (!gd.Capabilities.SupportsExtendedDynamicState)
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; {
// The original stride must be reapplied in case it was rewritten.
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride;
}
if (_offset >= size) if (_offset >= size)
{ {
@@ -101,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan
_strides[i] = strides[i]; _strides[i] = strides[i];
} }
if (_gd.Capabilities.SupportsExtendedDynamicState) if (_gd.Capabilities.SupportsExtendedDynamicState && (_gd.SupportsMTL31 || !_gd.IsMoltenVk))
{ {
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2( _gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(
cbs.CommandBuffer, cbs.CommandBuffer,
@@ -25,6 +25,7 @@ namespace Ryujinx.Graphics.Vulkan
[ [
ExtConditionalRendering.ExtensionName, ExtConditionalRendering.ExtensionName,
ExtExtendedDynamicState.ExtensionName, ExtExtendedDynamicState.ExtensionName,
ExtExtendedDynamicState2.ExtensionName,
ExtTransformFeedback.ExtensionName, ExtTransformFeedback.ExtensionName,
KhrDrawIndirectCount.ExtensionName, KhrDrawIndirectCount.ExtensionName,
KhrPushDescriptor.ExtensionName, KhrPushDescriptor.ExtensionName,
@@ -319,6 +320,17 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &supportedFeaturesCustomBorderColor; features2.PNext = &supportedFeaturesCustomBorderColor;
} }
PhysicalDeviceExtendedDynamicState2FeaturesEXT supportedFeaturesExtExtendedDynamicState2 = new()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = features2.PNext,
};
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
features2.PNext = &supportedFeaturesExtExtendedDynamicState2;
}
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new() PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new()
{ {
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
@@ -443,6 +455,7 @@ namespace Ryujinx.Graphics.Vulkan
TessellationShader = supportedFeatures.TessellationShader, TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics, VertexPipelineStoresAndAtomics = supportedFeatures.VertexPipelineStoresAndAtomics,
RobustBufferAccess = useRobustBufferAccess, RobustBufferAccess = useRobustBufferAccess,
WideLines = supportedFeatures.WideLines,
SampleRateShading = supportedFeatures.SampleRateShading, SampleRateShading = supportedFeatures.SampleRateShading,
ShaderStorageImageExtendedFormats = supportedFeatures.ShaderStorageImageExtendedFormats, ShaderStorageImageExtendedFormats = supportedFeatures.ShaderStorageImageExtendedFormats,
}; };
@@ -495,6 +508,20 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresExtendedDynamicState; pExtendedFeatures = &featuresExtendedDynamicState;
if (physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
var featuresExtendedDynamicState2 = new PhysicalDeviceExtendedDynamicState2FeaturesEXT()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
PNext = pExtendedFeatures,
ExtendedDynamicState2 = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2,
ExtendedDynamicState2LogicOp = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2LogicOp,
ExtendedDynamicState2PatchControlPoints = supportedFeaturesExtExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
};
pExtendedFeatures = &featuresExtendedDynamicState2;
}
PhysicalDeviceVulkan11Features featuresVk11 = new() PhysicalDeviceVulkan11Features featuresVk11 = new()
{ {
SType = StructureType.PhysicalDeviceVulkan11Features, SType = StructureType.PhysicalDeviceVulkan11Features,
+29 -15
View File
@@ -39,6 +39,7 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrSwapchain SwapchainApi { get; private set; } internal KhrSwapchain SwapchainApi { get; private set; }
internal ExtConditionalRendering ConditionalRenderingApi { get; private set; } internal ExtConditionalRendering ConditionalRenderingApi { get; private set; }
internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; } internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; }
internal ExtExtendedDynamicState2 ExtendedDynamicState2Api { get; private set; }
internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
@@ -55,7 +56,6 @@ namespace Ryujinx.Graphics.Vulkan
internal CommandBufferPool CommandBufferPool { get; private set; } internal CommandBufferPool CommandBufferPool { get; private set; }
internal PipelineLayoutCache PipelineLayoutCache { get; private set; } internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
internal BackgroundResources BackgroundResources { get; private set; } internal BackgroundResources BackgroundResources { get; private set; }
internal ShaderCompilationQueue ShaderCompilationQueue { get; private set; }
internal Action<Action> InterruptAction { get; private set; } internal Action<Action> InterruptAction { get; private set; }
internal SyncManager SyncManager { get; private set; } internal SyncManager SyncManager { get; private set; }
@@ -97,7 +97,6 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsNvidiaPreTuring { get; private set; } internal bool IsNvidiaPreTuring { get; private set; }
internal bool IsIntelArc { get; private set; } internal bool IsIntelArc { get; private set; }
internal bool IsQualcommProprietary { get; private set; } internal bool IsQualcommProprietary { get; private set; }
internal bool IsTurnip { get; private set; }
internal bool IsMoltenVk { get; private set; } internal bool IsMoltenVk { get; private set; }
internal bool SupportsMTL31 { get; private set; } internal bool SupportsMTL31 { get; private set; }
internal bool IsTBDR { get; private set; } internal bool IsTBDR { get; private set; }
@@ -129,12 +128,6 @@ namespace Ryujinx.Graphics.Vulkan
// Any device running on MacOS is using MoltenVK, even Intel and AMD vendors. // Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
IsMoltenVk = true; IsMoltenVk = true;
// The default thread stack size on MacOS is low, and can cause stack overflow
// on SPIR-V Cross during shader compilation.
// As a workaround, we use this custom queue which allows us to specify the stack
// size of the threads used for compilation.
ShaderCompilationQueue = new ShaderCompilationQueue();
} }
SupportsMTL31 = OperatingSystem.IsMacOSVersionAtLeast(14); SupportsMTL31 = OperatingSystem.IsMacOSVersionAtLeast(14);
@@ -154,6 +147,11 @@ namespace Ryujinx.Graphics.Vulkan
ExtendedDynamicStateApi = extendedDynamicStateApi; ExtendedDynamicStateApi = extendedDynamicStateApi;
} }
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExtendedDynamicState2 extendedDynamicState2Api))
{
ExtendedDynamicState2Api = extendedDynamicState2Api;
}
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi)) if (Api.TryGetDeviceExtension(_instance.Instance, _device, out KhrPushDescriptor pushDescriptorApi))
{ {
PushDescriptorApi = pushDescriptorApi; PushDescriptorApi = pushDescriptorApi;
@@ -256,6 +254,11 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt, SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
}; };
PhysicalDeviceExtendedDynamicState2FeaturesEXT featuresExtendedDynamicState2 = new()
{
SType = StructureType.PhysicalDeviceExtendedDynamicState2FeaturesExt,
};
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new() PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new()
{ {
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
@@ -296,6 +299,12 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresPrimitiveTopologyListRestart; features2.PNext = &featuresPrimitiveTopologyListRestart;
} }
if (_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState2.ExtensionName))
{
featuresExtendedDynamicState2.PNext = features2.PNext;
features2.PNext = &featuresExtendedDynamicState2;
}
if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2")) if (_physicalDevice.IsDeviceExtensionPresent("VK_EXT_robustness2"))
{ {
featuresRobustness2.PNext = features2.PNext; featuresRobustness2.PNext = features2.PNext;
@@ -404,8 +413,6 @@ namespace Ryujinx.Graphics.Vulkan
IsFeedbackLoopDevice = IsAmdRdna3; IsFeedbackLoopDevice = IsAmdRdna3;
IsTurnip = GpuRenderer.StartsWith("Turnip");
if (Vendor == Vendor.Nvidia) if (Vendor == Vendor.Nvidia)
{ {
Match match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer); Match match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
@@ -438,6 +445,10 @@ namespace Ryujinx.Graphics.Vulkan
properties.Limits.FramebufferDepthSampleCounts & properties.Limits.FramebufferDepthSampleCounts &
properties.Limits.FramebufferStencilSampleCounts; properties.Limits.FramebufferStencilSampleCounts;
// Temporarily disable this, can be added back at a later date, make it easy to re-enable.
// Disabled because currently causing Device Lost error on NVIDIA.
featuresExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints = false;
Capabilities = new HardwareCapabilities( Capabilities = new HardwareCapabilities(
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"), _physicalDevice.IsDeviceExtensionPresent("VK_EXT_index_type_uint8"),
supportsCustomBorderColor, supportsCustomBorderColor,
@@ -454,6 +465,8 @@ namespace Ryujinx.Graphics.Vulkan
features2.Features.ShaderStorageImageMultisample, features2.Features.ShaderStorageImageMultisample,
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
featuresExtendedDynamicState2,
_physicalDevice.PhysicalDeviceProperties.Limits.MaxTessellationPatchSize,
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk, featuresRobustness2.NullDescriptor || IsMoltenVk,
supportsPushDescriptors, supportsPushDescriptors,
@@ -469,6 +482,7 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
_physicalDevice.PhysicalDeviceFeatures.WideLines,
supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout && IsFeedbackLoopDevice, supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout && IsFeedbackLoopDevice,
supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState && IsFeedbackLoopDevice, supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState && IsFeedbackLoopDevice,
propertiesSubgroup.SubgroupSize, propertiesSubgroup.SubgroupSize,
@@ -486,7 +500,7 @@ namespace Ryujinx.Graphics.Vulkan
Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi); Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device); HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsQualcommProprietary, IsTurnip); CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsQualcommProprietary);
PipelineLayoutCache = new PipelineLayoutCache(); PipelineLayoutCache = new PipelineLayoutCache();
@@ -787,7 +801,6 @@ namespace Ryujinx.Graphics.Vulkan
supportsGeometryShader: Capabilities.SupportsGeometryShader, supportsGeometryShader: Capabilities.SupportsGeometryShader,
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough, supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
supportsTransformFeedback: Capabilities.SupportsTransformFeedback, supportsTransformFeedback: Capabilities.SupportsTransformFeedback,
supportsImageBufferPixelAlignment: false,
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat, supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer, supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
supportsMismatchingViewFormat: true, supportsMismatchingViewFormat: true,
@@ -801,7 +814,6 @@ namespace Ryujinx.Graphics.Vulkan
supportsShaderNonUniformIndexing: supportsShaderNonUniformIndexing:
featuresVk12.ShaderSampledImageArrayNonUniformIndexing && featuresVk12.ShaderSampledImageArrayNonUniformIndexing &&
featuresVk12.ShaderStorageImageArrayNonUniformIndexing, featuresVk12.ShaderStorageImageArrayNonUniformIndexing,
supportsTextureBufferPixelAlignment: false,
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended, supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended,
supportsTextureShadowLod: false, supportsTextureShadowLod: false,
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics, supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,
@@ -810,6 +822,10 @@ namespace Ryujinx.Graphics.Vulkan
supportsViewportSwizzle: false, supportsViewportSwizzle: false,
supportsIndirectParameters: true, supportsIndirectParameters: true,
supportsDepthClipControl: Capabilities.SupportsDepthClipControl, supportsDepthClipControl: Capabilities.SupportsDepthClipControl,
supportsExtendedDynamicState: Capabilities.SupportsExtendedDynamicState,
supportsExtendedDynamicState2: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2,
supportsLogicOpDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2LogicOp,
supportsPatchControlPointsDynamicState: Capabilities.SupportsExtendedDynamicState2.ExtendedDynamicState2PatchControlPoints,
uniformBufferSetIndex: PipelineBase.UniformSetIndex, uniformBufferSetIndex: PipelineBase.UniformSetIndex,
storageBufferSetIndex: PipelineBase.StorageSetIndex, storageBufferSetIndex: PipelineBase.StorageSetIndex,
textureSetIndex: PipelineBase.TextureSetIndex, textureSetIndex: PipelineBase.TextureSetIndex,
@@ -1124,8 +1140,6 @@ namespace Ryujinx.Graphics.Vulkan
SurfaceApi.DestroySurface(_instance.Instance, _surface, null); SurfaceApi.DestroySurface(_instance.Instance, _surface, null);
ShaderCompilationQueue?.Dispose();
Api.DestroyDevice(_device, null); Api.DestroyDevice(_device, null);
_debugMessenger.Dispose(); _debugMessenger.Dispose();
@@ -26,12 +26,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{ {
MemoryArrange.MemoryArrange4GiB or MemoryArrange.MemoryArrange4GiB or
MemoryArrange.MemoryArrange4GiBSystemDev or MemoryArrange.MemoryArrange4GiBSystemDev or
MemoryArrange.MemoryArrange6GiBAppletDev => 3173 * MiB, MemoryArrange.MemoryArrange6GiBAppletDev => 3236 * MiB,
MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB, MemoryArrange.MemoryArrange4GiBAppletDev => 2048 * MiB,
MemoryArrange.MemoryArrange6GiB => 4804 * MiB, MemoryArrange.MemoryArrange6GiB => 4867 * MiB,
MemoryArrange.MemoryArrange8GiB => 6852 * MiB, MemoryArrange.MemoryArrange8GiB => 6915 * MiB,
MemoryArrange.MemoryArrange10GiB => 8900 * MiB, MemoryArrange.MemoryArrange10GiB => 8963 * MiB,
MemoryArrange.MemoryArrange12GiB => 10948 * MiB, MemoryArrange.MemoryArrange12GiB => 11011 * MiB,
_ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."), _ => throw new ArgumentException($"Invalid memory arrange \"{arrange}\"."),
}; };
} }
@@ -203,7 +203,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
heapRegion.Size = memConfig switch { heapRegion.Size = memConfig switch {
MemoryConfiguration.MemoryConfiguration8GiB => 0x200000000u, MemoryConfiguration.MemoryConfiguration8GiB => 0x200000000u,
MemoryConfiguration.MemoryConfiguration10GiB when !isHeapLimited => 0x280000000u, MemoryConfiguration.MemoryConfiguration10GiB when !isHeapLimited => 0x280000000u,
MemoryConfiguration.MemoryConfiguration10GiB => 0x200000000u,
MemoryConfiguration.MemoryConfiguration12GiB when !isHeapLimited => 0x300000000u, MemoryConfiguration.MemoryConfiguration12GiB when !isHeapLimited => 0x300000000u,
MemoryConfiguration.MemoryConfiguration12GiB => 0x200000000u,
_ => 0x180000000u _ => 0x180000000u
}; };
stackRegion.Size = 0; stackRegion.Size = 0;
@@ -238,7 +240,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
heapRegion.Size = memConfig switch { heapRegion.Size = memConfig switch {
MemoryConfiguration.MemoryConfiguration8GiB => 0x200000000u, MemoryConfiguration.MemoryConfiguration8GiB => 0x200000000u,
MemoryConfiguration.MemoryConfiguration10GiB when !isHeapLimited => 0x280000000u, MemoryConfiguration.MemoryConfiguration10GiB when !isHeapLimited => 0x280000000u,
MemoryConfiguration.MemoryConfiguration10GiB => 0x200000000u,
MemoryConfiguration.MemoryConfiguration12GiB when !isHeapLimited => 0x300000000u, MemoryConfiguration.MemoryConfiguration12GiB when !isHeapLimited => 0x300000000u,
MemoryConfiguration.MemoryConfiguration12GiB => 0x200000000u,
_ => 0x180000000u _ => 0x180000000u
}; };
stackRegion.Size = 1UL << (addressSpaceWidth - 8); stackRegion.Size = 1UL << (addressSpaceWidth - 8);
@@ -257,7 +261,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
heapRegion.Size = memConfig switch { heapRegion.Size = memConfig switch {
MemoryConfiguration.MemoryConfiguration8GiB => 0x200000000u, MemoryConfiguration.MemoryConfiguration8GiB => 0x200000000u,
MemoryConfiguration.MemoryConfiguration10GiB when !isHeapLimited => 0x280000000u, MemoryConfiguration.MemoryConfiguration10GiB when !isHeapLimited => 0x280000000u,
MemoryConfiguration.MemoryConfiguration10GiB => 0x200000000u,
MemoryConfiguration.MemoryConfiguration12GiB when !isHeapLimited => 0x300000000u, MemoryConfiguration.MemoryConfiguration12GiB when !isHeapLimited => 0x300000000u,
MemoryConfiguration.MemoryConfiguration12GiB => 0x200000000u,
_ => 0x180000000u _ => 0x180000000u
}; };
stackRegion.Size = 0x80000000; stackRegion.Size = 0x80000000;
@@ -21,6 +21,21 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(3)] // 20.0.0+
// CreateLibraryAppletEx(u32, u32, u64) -> object<nn::am::service::ILibraryAppletAccessor>
public ResultCode CreateLibraryAppletEx(ServiceCtx context)
{
AppletId appletId = (AppletId)context.RequestData.ReadInt32();
_ = context.RequestData.ReadInt32(); // libraryAppletMode
_ = context.RequestData.ReadUInt64(); // threadId
MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System));
return ResultCode.Success;
}
[CommandCmif(10)] [CommandCmif(10)]
// CreateStorage(u64) -> object<nn::am::service::IStorage> // CreateStorage(u64) -> object<nn::am::service::IStorage>
public ResultCode CreateStorage(ServiceCtx context) public ResultCode CreateStorage(ServiceCtx context)
@@ -7,6 +7,7 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem; using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils; using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Executables;
using Ryujinx.HLE.Loaders.Processes.Extensions; using Ryujinx.HLE.Loaders.Processes.Extensions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -23,7 +24,62 @@ namespace Ryujinx.HLE.Loaders.Processes
private ulong _latestPid; private ulong _latestPid;
public ProcessResult ActiveApplication => _processesByPid[_latestPid]; private readonly object _pidLock = new();
#nullable enable
public ProcessResult? ActiveApplication
{
get
{
lock (_pidLock)
{
// Check if _latestPid is still valid
if (_latestPid == 0)
{
return null;
}
// Verify process still exists in kernel (authoritative source)
if (!_device.System.KernelContext.Processes.TryGetValue(_latestPid, out HOS.Kernel.Process.KProcess? kernelProcess))
{
// Process no longer exists in kernel, clear stale state
Logger.Warning?.Print(LogClass.Loader,
$"ActiveApplication PID {_latestPid} no longer exists in kernel, clearing stale state");
_processesByPid.TryRemove(_latestPid, out _);
_latestPid = 0;
return null;
}
// Verify process still exists in ProcessLoader's dictionary
if (_processesByPid.TryGetValue(_latestPid, out ProcessResult? processResult))
{
// Additional check: verify process state
if (kernelProcess.State == HOS.Kernel.Process.ProcessState.Exited ||
kernelProcess.State == HOS.Kernel.Process.ProcessState.Exiting)
{
Logger.Warning?.Print(LogClass.Loader,
$"ActiveApplication PID {_latestPid} is in state {kernelProcess.State}, clearing");
_processesByPid.TryRemove(_latestPid, out _);
_latestPid = 0;
return null;
}
return processResult;
}
// Fallback: clear stale PID if not in our dictionary
Logger.Warning?.Print(LogClass.Loader,
$"ActiveApplication PID {_latestPid} not in ProcessLoader dictionary, clearing");
_latestPid = 0;
return null;
}
}
}
#nullable disable
public ProcessLoader(Switch device) public ProcessLoader(Switch device)
{ {
@@ -226,7 +282,7 @@ namespace Ryujinx.HLE.Loaders.Processes
} }
// Explicitly null TitleId to disable the shader cache. // Explicitly null TitleId to disable the shader cache.
Graphics.Gpu.GraphicsConfig.TitleId = null; GraphicsConfig.TitleId = null;
_device.Gpu.HostInitalized.Set(); _device.Gpu.HostInitalized.Set();
ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device, ProcessResult processResult = ProcessLoaderHelper.LoadNsos(_device,
@@ -265,5 +321,37 @@ namespace Ryujinx.HLE.Loaders.Processes
return false; return false;
} }
/// <summary>
/// Clears a specific process from the ProcessLoader's tracking.
/// This should be called when a process exits or is terminated.
/// </summary>
/// <param name="pid">The process ID to clear</param>
public void ClearProcess(ulong pid)
{
lock (_pidLock)
{
if (_processesByPid.TryRemove(pid, out _))
{
if (_latestPid == pid)
{
_latestPid = 0;
}
}
}
}
/// <summary>
/// Clears all processes from the ProcessLoader's tracking.
/// This should be called during system shutdown.
/// </summary>
public void ClearAllProcesses()
{
lock (_pidLock)
{
_processesByPid.Clear();
_latestPid = 0;
}
}
} }
} }
+1
View File
@@ -208,6 +208,7 @@ namespace Ryujinx.HLE
{ {
if (disposing) if (disposing)
{ {
Processes.ClearAllProcesses();
System.Dispose(); System.Dispose();
AudioDeviceDriver.Dispose(); AudioDeviceDriver.Dispose();
FileSystem.Dispose(); FileSystem.Dispose();
+1 -1
View File
@@ -116,7 +116,7 @@ namespace Ryujinx.ShaderTools
if (options.VertexPassthrough) if (options.VertexPassthrough)
{ {
(program, _) = translatorContext.GenerateVertexPassthroughForCompute(); program = translatorContext.GenerateVertexPassthroughForCompute();
} }
else else
{ {