Compare commits

...

8 Commits

Author SHA1 Message Date
Babib3l
e756ad4556 Fix ProcessLoader stale PID validation against kernel process table (#102)
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()

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/102
2026-05-16 01:11:06 +00:00
AsperTheDog
58bd19a2f3 Fix Vulkan validation errors (#92)
This PR fixes several validation errors caused by invalid Vulkan usage. These validation errors often end up invoking Undefined Behavior on the driver side, which can lead to artifacts or crashes which are driver specific and otherwise incredibly hard to track. I don't think it should have any impact on performance, but it would be good to test it with as many games as possible (maybe a bug in a game was fixed?).

Each commit fixes an error. I added to each a description with the validation error that was fixed and a small explanation on what was causing it and how I fixed it.

Co-authored-by: AsperTheDog <guillerman0000@gmail.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/92
2026-05-15 15:36:14 +00:00
AsperTheDog
48888bd014 Change non-unform shader extension to be more conservative (#99)
The previous fix for Tomodachi Life (#91) included the extension to all shaders, independently on if it was needed or not. This PR fixes that by lazily adding the extension only when it is actually needed.

This change should not be noticed by anyone, but it avoids having to modify shaders that do not perform any type of dynamic indexing, which apparently is something some modders care about.

Co-authored-by: AsperTheDog <guillerman0000@gmail.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/99
2026-05-15 15:19:12 +00:00
Renovate Bot
d307afc454 Update dependency Sep to 0.14.1 (#98)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [Sep](https://github.com/nietras/Sep) | `0.13.0` → `0.14.1` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/Sep/0.14.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Sep/0.13.0/0.14.1?slim=true) |

---

### Release Notes

<details>
<summary>nietras/Sep (Sep)</summary>

### [`v0.14.1`](https://github.com/nietras/Sep/releases/tag/v0.14.1): 0.14.1

#### What's Changed

- Improve SepReaderOptions.Unescape/.Trim comments by [@&#8203;nietras](https://github.com/nietras) in [#&#8203;542](https://github.com/nietras/Sep/pull/542)
- Improve SepReader/WriterExtensions.Strict() comments by [@&#8203;nietras](https://github.com/nietras) in [#&#8203;543](https://github.com/nietras/Sep/pull/543)

**Full Changelog**: <https://github.com/nietras/Sep/compare/v0.14.0...v0.14.1>

### [`v0.14.0`](https://github.com/nietras/Sep/releases/tag/v0.14.0): 0.14.0

#### What's Changed

- Add `leaveOpen` overloads for SepReaderOptions.From\* via SepTextReaderDisposers by [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;530](https://github.com/nietras/Sep/pull/530)
- Bump MSTest from 4.2.1 to 4.2.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;533](https://github.com/nietras/Sep/pull/533)
- Bump github/codeql-action from 4.35.2 to 4.35.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;532](https://github.com/nietras/Sep/pull/532)
- Bump step-security/harden-runner from 2.19.0 to 2.19.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;531](https://github.com/nietras/Sep/pull/531)
- Confirm statuses:write is the correct minimum permission for super-linter (not checks:write) by [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;540](https://github.com/nietras/Sep/pull/540)

**Full Changelog**: <https://github.com/nietras/Sep/compare/v0.13.0...v0.14.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:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzguMCIsInVwZGF0ZWRJblZlciI6IjQzLjE3OC4wIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/98
2026-05-15 15:14:23 +00:00
Renovate Bot
134453e62b Update dependency SharpCompress to 0.48.1 (#97)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [SharpCompress](https://github.com/adamhathcock/sharpcompress) | `0.48.0` → `0.48.1` | ![age](https://developer.mend.io/api/mc/badges/age/nuget/SharpCompress/0.48.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/SharpCompress/0.48.0/0.48.1?slim=true) |

---

### Release Notes

<details>
<summary>adamhathcock/sharpcompress (SharpCompress)</summary>

### [`v0.48.1`](https://github.com/adamhathcock/sharpcompress/releases/tag/0.48.1): - GZip writing fix

[Compare Source](https://github.com/adamhathcock/sharpcompress/compare/0.48.0...0.48.1)

#### What's Changed

- release test cleanup by [@&#8203;adamhathcock](https://github.com/adamhathcock) in [#&#8203;1322](https://github.com/adamhathcock/sharpcompress/pull/1322)
- Fix GZip write async by [@&#8203;adamhathcock](https://github.com/adamhathcock) in [#&#8203;1320](https://github.com/adamhathcock/sharpcompress/pull/1320)

**Full Changelog**: <https://github.com/adamhathcock/sharpcompress/compare/0.48.0...0.48.1>

</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:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzguMCIsInVwZGF0ZWRJblZlciI6IjQzLjE3OC4wIiwidGFyZ2V0QnJhbmNoIjoibWFzdGVyIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/97
2026-05-15 15:13:49 +00:00
Max
298b6c3959 [HLE] Stub ILibrarySelfAccessor:ExitAndReturn (10) (#96)
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/96
2026-05-15 15:13:23 +00:00
shinyoyo
c96433eb13 Updated Simplified Chinese translation (#95)
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/95
2026-05-14 14:00:16 +00:00
AsperTheDog
c0078088dd Add shader non-uniform indexing support (#91)
This PR marks ALL texture indexes as nonuniform to fix an issue with the paths in Tomodachi Life: Living the Dream on AMD cards. It should have a negligible impact on performance (and it should not have an impact at all on NVIDIA cards!)

It's caused by what is called 'implicit non-uniform sampler array indexing'. The idea is basically that some GPUs optimize texture lookups from indexed texture arrays, by assuming that you are never going to index different textures within a single workgroup. What this causes is that visual glitch where a subgroup is tasked with rendering a block of the screen, and in the boundaries some cores are indexing the wrong texture.

Co-authored-by: AsperTheDog <guillerman0000@gmail.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/91
2026-05-14 08:59:10 +00:00
23 changed files with 269 additions and 41 deletions

View File

@@ -8,7 +8,7 @@
<PackageVersion Include="Avalonia.Desktop" Version="11.3.15" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.15" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="11.3.15" />
<PackageVersion Include="SharpCompress" Version="0.48.0" />
<PackageVersion Include="SharpCompress" Version="0.48.1" />
<PackageVersion Include="Svg.Controls.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Svg.Controls.Skia.Avalonia" Version="11.3.9.5" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.4" />
@@ -51,7 +51,7 @@
<PackageVersion Include="Ryujinx.Systems.Update.Common" Version="2.0.6" />
<PackageVersion Include="Gommon" Version="2.8.1.2" />
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
<PackageVersion Include="Sep" Version="0.13.0" />
<PackageVersion Include="Sep" Version="0.14.1" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.22.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.22.0" />

View File

@@ -596,7 +596,7 @@
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_CN": "重启模拟",
"zh_TW": "重新啟動模擬"
}
},
@@ -6121,7 +6121,7 @@
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_CN": "开启网络日志",
"zh_TW": ""
}
},
@@ -11371,7 +11371,7 @@
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_CN": "保存",
"zh_TW": "儲存"
}
},
@@ -17121,7 +17121,7 @@
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_CN": "在控制台中显示网络日志",
"zh_TW": ""
}
},
@@ -21496,7 +21496,7 @@
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_CN": "控制台将会在下次启动 Ryujinx 时可用。",
"zh_TW": ""
}
},

View File

@@ -42,6 +42,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64;
public readonly bool SupportsShaderNonUniformIndexing;
public readonly bool SupportsTextureGatherOffsets;
public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsVertexStoreAndAtomics;
@@ -110,6 +111,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64,
bool supportsShaderNonUniformIndexing,
bool supportsTextureGatherOffsets,
bool supportsTextureShadowLod,
bool supportsVertexStoreAndAtomics,
@@ -172,6 +174,7 @@ namespace Ryujinx.Graphics.GAL
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64;
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
SupportsTextureGatherOffsets = supportsTextureGatherOffsets;
SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsVertexStoreAndAtomics = supportsVertexStoreAndAtomics;

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 7353;
private const uint CodeGenVersion = 7354;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -231,6 +231,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64;
public bool QueryHostSupportsShaderNonUniformIndexing() => _context.Capabilities.SupportsShaderNonUniformIndexing;
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
public bool QueryHostSupportsTextureGatherOffsets() => _context.Capabilities.SupportsTextureGatherOffsets;

View File

@@ -184,6 +184,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
supportsShaderFloat64: true,
supportsShaderNonUniformIndexing: false,
supportsTextureGatherOffsets: true,
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
supportsVertexStoreAndAtomics: true,

View File

@@ -82,6 +82,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
public bool IsMainFunction { get; private set; }
public bool MayHaveReturned { get; set; }
public bool WasNonUniformAccessDeclared { get; set; }
public CodeGenContext(
StructuredProgramInfo info,
@@ -89,6 +90,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
GeneratorPool<Instruction> instPool,
GeneratorPool<LiteralInteger> integerPool) : base(SpirvVersionPacked, instPool, integerPool)
{
WasNonUniformAccessDeclared = false;
Info = info;
AttributeUsage = parameters.AttributeUsage;
Definitions = parameters.Definitions;

View File

@@ -587,6 +587,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
return OperationResult.Invalid;
}
private static void MarkNonUniform(CodeGenContext context, SpvInstruction inst)
{
if (context.HostCapabilities.SupportsShaderNonUniformIndexing)
{
if (!context.WasNonUniformAccessDeclared)
{
context.AddExtension("SPV_EXT_descriptor_indexing");
context.AddCapability(Capability.ShaderNonUniform);
context.AddCapability(Capability.SampledImageArrayNonUniformIndexing);
context.AddCapability(Capability.StorageImageArrayNonUniformIndexing);
}
context.Decorate(inst, Decoration.NonUniform);
context.WasNonUniformAccessDeclared = true;
}
}
private static OperationResult GenerateImageAtomic(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;
@@ -613,6 +630,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(imagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -683,15 +701,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (isIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -740,15 +764,21 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
ImageDeclaration declaration = context.Images[texOp.GetTextureSetAndBinding()];
SpvInstruction image = declaration.Image;
bool isIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (isIndexed)
{
SpvInstruction textureIndex = Src(AggregateType.S32);
image = context.AccessChain(declaration.ImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
image = context.Load(declaration.ImageType, image);
if (isIndexed)
{
MarkNonUniform(context, image);
}
int coordsCount = texOp.Type.Dimensions;
@@ -1878,35 +1908,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static SpvInstruction GenerateSampledImageLoad(CodeGenContext context, AstTextureOperation texOp, SamplerDeclaration declaration, ref int srcIndex)
{
SpvInstruction image = declaration.Image;
bool imageIndexed = declaration.IsIndexed;
if (declaration.IsIndexed)
if (imageIndexed)
{
SpvInstruction textureIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
image = context.AccessChain(declaration.SampledImagePointerType, image, textureIndex);
MarkNonUniform(context, image);
}
if (texOp.IsSeparate)
{
image = context.Load(declaration.ImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
SamplerDeclaration samplerDeclaration = context.Samplers[texOp.GetSamplerSetAndBinding()];
SpvInstruction sampler = samplerDeclaration.Image;
bool samplerIndexed = samplerDeclaration.IsIndexed;
if (samplerDeclaration.IsIndexed)
if (samplerIndexed)
{
SpvInstruction samplerIndex = context.Get(AggregateType.S32, texOp.GetSource(srcIndex++));
sampler = context.AccessChain(samplerDeclaration.SampledImagePointerType, sampler, samplerIndex);
MarkNonUniform(context, sampler);
}
sampler = context.Load(samplerDeclaration.ImageType, sampler);
if (samplerIndexed)
{
MarkNonUniform(context, sampler);
}
image = context.SampledImage(declaration.SampledImageType, image, sampler);
if (imageIndexed || samplerIndexed)
{
MarkNonUniform(context, image);
}
}
else
{
image = context.Load(declaration.SampledImageType, image);
if (imageIndexed)
{
MarkNonUniform(context, image);
}
}
return image;

View File

@@ -336,6 +336,10 @@ namespace Ryujinx.Graphics.Shader
{
return true;
}
bool QueryHostSupportsShaderNonUniformIndexing()
{
return false;
}
/// <summary>
/// Queries host GPU support for signed normalized buffer texture formats.

View File

@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public readonly bool SupportsShaderBallot;
public readonly bool SupportsShaderBarrierDivergence;
public readonly bool SupportsShaderFloat64;
public readonly bool SupportsShaderNonUniformIndexing;
public readonly bool SupportsTextureShadowLod;
public readonly bool SupportsViewportMask;
@@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Shader.Translation
bool supportsShaderBallot,
bool supportsShaderBarrierDivergence,
bool supportsShaderFloat64,
bool supportsShaderNonUniformIndexing,
bool supportsTextureShadowLod,
bool supportsViewportMask)
{
@@ -30,6 +32,7 @@ namespace Ryujinx.Graphics.Shader.Translation
SupportsShaderBallot = supportsShaderBallot;
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
SupportsShaderFloat64 = supportsShaderFloat64;
SupportsShaderNonUniformIndexing = supportsShaderNonUniformIndexing;
SupportsTextureShadowLod = supportsTextureShadowLod;
SupportsViewportMask = supportsViewportMask;
}

View File

@@ -364,6 +364,7 @@ namespace Ryujinx.Graphics.Shader.Translation
GpuAccessor.QueryHostSupportsShaderBallot(),
GpuAccessor.QueryHostSupportsShaderBarrierDivergence(),
GpuAccessor.QueryHostSupportsShaderFloat64(),
GpuAccessor.QueryHostSupportsShaderNonUniformIndexing(),
GpuAccessor.QueryHostSupportsTextureShadowLod(),
GpuAccessor.QueryHostSupportsViewportMask());

View File

@@ -46,7 +46,14 @@ namespace Ryujinx.Graphics.Vulkan
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
{
AccessFlags access = BufferAccess;
AccessFlags access = BufferAccess |
AccessFlags.ShaderReadBit |
AccessFlags.ShaderWriteBit |
AccessFlags.ColorAttachmentReadBit |
AccessFlags.ColorAttachmentWriteBit |
AccessFlags.DepthStencilAttachmentReadBit |
AccessFlags.DepthStencilAttachmentWriteBit;
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
if (gd.TransformFeedbackApi != null)

View File

@@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Vulkan
Image dstImage,
TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo,
TextureCreateInfo srcStorageInfo,
TextureCreateInfo dstStorageInfo,
Extents2D srcRegion,
Extents2D dstRegion,
int srcLayer,
@@ -40,6 +42,13 @@ namespace Ryujinx.Graphics.Vulkan
return (xy1, xy2);
}
static (Offset3D, Offset3D) ClampOffsetsToMip(Offset3D xy1, Offset3D xy2, int mipW, int mipH)
{
return (
new Offset3D(Math.Min(xy1.X, mipW), Math.Min(xy1.Y, mipH), xy1.Z),
new Offset3D(Math.Min(xy2.X, mipW), Math.Min(xy2.Y, mipH), xy2.Z));
}
if (srcAspectFlags == 0)
{
srcAspectFlags = srcInfo.Format.ConvertAspectFlags();
@@ -80,6 +89,14 @@ namespace Ryujinx.Graphics.Vulkan
(srcOffsets.Element0, srcOffsets.Element1) = ExtentsToOffset3D(srcRegion, srcInfo.Width, srcInfo.Height, level);
(dstOffsets.Element0, dstOffsets.Element1) = ExtentsToOffset3D(dstRegion, dstInfo.Width, dstInfo.Height, level);
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (int)copySrcLevel);
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (int)copySrcLevel);
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (int)copyDstLevel);
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (int)copyDstLevel);
(srcOffsets.Element0, srcOffsets.Element1) = ClampOffsetsToMip(srcOffsets.Element0, srcOffsets.Element1, srcMipW, srcMipH);
(dstOffsets.Element0, dstOffsets.Element1) = ClampOffsetsToMip(dstOffsets.Element0, dstOffsets.Element1, dstMipW, dstMipH);
ImageBlit region = new()
{
SrcSubresource = srcSl,
@@ -121,6 +138,8 @@ namespace Ryujinx.Graphics.Vulkan
Image dstImage,
TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo,
TextureCreateInfo srcStorageInfo,
TextureCreateInfo dstStorageInfo,
int srcViewLayer,
int dstViewLayer,
int srcViewLevel,
@@ -151,6 +170,8 @@ namespace Ryujinx.Graphics.Vulkan
dstImage,
srcInfo,
dstInfo,
srcStorageInfo,
dstStorageInfo,
srcViewLayer,
dstViewLayer,
srcViewLevel,
@@ -186,6 +207,8 @@ namespace Ryujinx.Graphics.Vulkan
Image dstImage,
TextureCreateInfo srcInfo,
TextureCreateInfo dstInfo,
TextureCreateInfo srcStorageInfo,
TextureCreateInfo dstStorageInfo,
int srcViewLayer,
int dstViewLayer,
int srcViewLevel,
@@ -314,6 +337,14 @@ namespace Ryujinx.Graphics.Vulkan
int copyWidth = sizeInBlocks ? BitUtils.DivRoundUp(width, blockWidth) : width;
int copyHeight = sizeInBlocks ? BitUtils.DivRoundUp(height, blockHeight) : height;
int srcMipW = Math.Max(1, srcStorageInfo.Width >> (srcViewLevel + srcLevel + level));
int srcMipH = Math.Max(1, srcStorageInfo.Height >> (srcViewLevel + srcLevel + level));
int dstMipW = Math.Max(1, dstStorageInfo.Width >> (dstViewLevel + dstLevel + level));
int dstMipH = Math.Max(1, dstStorageInfo.Height >> (dstViewLevel + dstLevel + level));
copyWidth = Math.Min(copyWidth, Math.Min(srcMipW, dstMipW));
copyHeight = Math.Min(copyHeight, Math.Min(srcMipH, dstMipH));
Extent3D extent = new((uint)copyWidth, (uint)copyHeight, (uint)srcDepth);
if (srcInfo.Samples > 1 && srcInfo.Samples != dstInfo.Samples)

View File

@@ -67,6 +67,8 @@ namespace Ryujinx.Graphics.Vulkan
public VkFormat VkFormat { get; }
public ImageUsageFlags UsageFlags { get; }
public unsafe TextureStorage(
VulkanRenderer gd,
Device device,
@@ -93,7 +95,8 @@ namespace Ryujinx.Graphics.Vulkan
SampleCountFlags sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
ImageUsageFlags usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, true);
ImageUsageFlags usage = GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported);
UsageFlags = usage;
ImageCreateFlags flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
@@ -159,7 +162,7 @@ namespace Ryujinx.Graphics.Vulkan
_imageAuto = new Auto<DisposableImage>(new DisposableImage(_gd.Api, device, _image));
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
InitialTransition(ImageLayout.Undefined, ImageLayout.General);
}
_slices = new TextureSliceInfo[levels * _depthOrLayers];
@@ -307,7 +310,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported, bool extendedUsage)
public static ImageUsageFlags GetImageUsage(Format format, in HardwareCapabilities capabilities, bool isMsImageStorageSupported)
{
ImageUsageFlags usage = DefaultUsageFlags;
@@ -320,7 +323,7 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit;
}
if ((format.IsImageCompatible && isMsImageStorageSupported) || extendedUsage)
if (format.IsImageCompatible && isMsImageStorageSupported)
{
usage |= ImageUsageFlags.StorageBit;
}

View File

@@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
bool isMsImageStorageSupported = gd.Capabilities.SupportsShaderStorageImageMultisample || !info.Target.IsMultisample;
VkFormat format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format, isMsImageStorageSupported);
ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported, false);
ImageUsageFlags usage = TextureStorage.GetImageUsage(info.Format, gd.Capabilities, isMsImageStorageSupported) & storage.UsageFlags;
uint levels = (uint)info.Levels;
uint layers = (uint)info.GetLayers();
@@ -133,6 +133,8 @@ namespace Ryujinx.Graphics.Vulkan
shaderUsage |= ImageUsageFlags.StorageBit;
}
shaderUsage &= storage.UsageFlags;
_imageView = CreateImageView(componentMapping, subresourceRange, type, shaderUsage);
// Framebuffer attachments and storage images requires a identity component mapping.
@@ -257,6 +259,8 @@ namespace Ryujinx.Graphics.Vulkan
dstImage,
src.Info,
dst.Info,
src.Storage.Info,
dst.Storage.Info,
src.FirstLayer,
dst.FirstLayer,
src.FirstLevel,
@@ -310,6 +314,8 @@ namespace Ryujinx.Graphics.Vulkan
dstImage,
src.Info,
dst.Info,
src.Storage.Info,
dst.Storage.Info,
src.FirstLayer,
dst.FirstLayer,
src.FirstLevel,
@@ -385,6 +391,8 @@ namespace Ryujinx.Graphics.Vulkan
dst.GetImage().Get(cbs).Value,
src.Info,
dst.Info,
src.Storage.Info,
dst.Storage.Info,
src.FirstLayer,
dst.FirstLayer,
src.FirstLevel,
@@ -410,6 +418,8 @@ namespace Ryujinx.Graphics.Vulkan
dst.GetImage().Get(cbs).Value,
src.Info,
dst.Info,
src.Storage.Info,
dst.Storage.Info,
srcRegion,
dstRegion,
src.FirstLayer,
@@ -463,6 +473,8 @@ namespace Ryujinx.Graphics.Vulkan
dstImage.Get(cbs).Value,
src.Info,
dst.Info,
src.Storage.Info,
dst.Storage.Info,
srcRegion,
dstRegion,
src.FirstLayer,

View File

@@ -68,9 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
int stride = (_stride + (alignment - 1)) & -alignment;
int newSize = (_size / _stride) * stride;
Buffer buffer = autoBuffer.Get(cbs, 0, newSize).Value;
updater.BindVertexBuffer(cbs, binding, buffer, 0, (ulong)newSize, (ulong)stride);
updater.BindVertexBuffer(cbs, binding, autoBuffer, 0, newSize, (ulong)stride);
_buffer = autoBuffer;
@@ -93,11 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
if (autoBuffer != null)
{
int offset = _offset;
bool mirrorable = _size <= VertexBufferMaxMirrorable;
Buffer buffer = mirrorable ? autoBuffer.GetMirrorable(cbs, ref offset, _size, out _).Value : autoBuffer.Get(cbs, offset, _size).Value;
updater.BindVertexBuffer(cbs, binding, buffer, (ulong)offset, (ulong)_size, (ulong)_stride);
updater.BindVertexBuffer(cbs, binding, autoBuffer, _offset, _size, (ulong)_stride);
}
}

View File

@@ -15,6 +15,10 @@ namespace Ryujinx.Graphics.Vulkan
private readonly NativeArray<ulong> _sizes;
private readonly NativeArray<ulong> _strides;
private readonly Auto<DisposableBuffer>[] _bufferAutos;
private readonly int[] _bufferOffsetsForGet;
private readonly int[] _bufferSizesForGet;
public VertexBufferUpdater(VulkanRenderer gd)
{
_gd = gd;
@@ -23,9 +27,13 @@ namespace Ryujinx.Graphics.Vulkan
_offsets = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_sizes = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_strides = new NativeArray<ulong>(Constants.MaxVertexBuffers);
_bufferAutos = new Auto<DisposableBuffer>[Constants.MaxVertexBuffers];
_bufferOffsetsForGet = new int[Constants.MaxVertexBuffers];
_bufferSizesForGet = new int[Constants.MaxVertexBuffers];
}
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, VkBuffer buffer, ulong offset, ulong size, ulong stride)
public void BindVertexBuffer(CommandBufferScoped cbs, uint binding, Auto<DisposableBuffer> autoBuffer, int offset, int size, ulong stride)
{
if (_count == 0)
{
@@ -39,9 +47,11 @@ namespace Ryujinx.Graphics.Vulkan
int index = (int)_count;
_buffers[index] = buffer;
_offsets[index] = offset;
_sizes[index] = size;
_bufferAutos[index] = autoBuffer;
_bufferOffsetsForGet[index] = offset;
_bufferSizesForGet[index] = size;
_offsets[index] = (ulong)offset;
_sizes[index] = (ulong)size;
_strides[index] = stride;
_count++;
@@ -51,6 +61,12 @@ namespace Ryujinx.Graphics.Vulkan
{
if (_count != 0)
{
for (int i = 0; i < _count; i++)
{
_buffers[i] = _bufferAutos[i].Get(cbs, _bufferOffsetsForGet[i], _bufferSizesForGet[i]).Value;
_bufferAutos[i] = null;
}
if (_gd.Capabilities.SupportsExtendedDynamicState)
{
_gd.ExtendedDynamicStateApi.CmdBindVertexBuffers2(

View File

@@ -494,6 +494,8 @@ namespace Ryujinx.Graphics.Vulkan
UniformBufferStandardLayout = supportedPhysicalDeviceVulkan12Features.UniformBufferStandardLayout,
UniformAndStorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.UniformAndStorageBuffer8BitAccess,
StorageBuffer8BitAccess = supportedPhysicalDeviceVulkan12Features.StorageBuffer8BitAccess,
ShaderSampledImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderSampledImageArrayNonUniformIndexing,
ShaderStorageImageArrayNonUniformIndexing = supportedPhysicalDeviceVulkan12Features.ShaderStorageImageArrayNonUniformIndexing,
};
pExtendedFeatures = &featuresVk12;

View File

@@ -775,6 +775,9 @@ namespace Ryujinx.Graphics.Vulkan
supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
supportsShaderNonUniformIndexing:
featuresVk12.ShaderSampledImageArrayNonUniformIndexing &&
featuresVk12.ShaderStorageImageArrayNonUniformIndexing,
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk,
supportsTextureShadowLod: false,
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,

View File

@@ -391,12 +391,12 @@ namespace Ryujinx.Graphics.Vulkan
{
if (_effect != null)
{
_gd.FlushAllCommands();
_gd.CommandBufferPool.Return(
cbs,
null,
[PipelineStageFlags.ColorAttachmentOutputBit],
null);
_gd.FlushAllCommands();
cbs.GetFence().Wait();
cbs = _gd.CommandBufferPool.Rent();
}
@@ -455,6 +455,8 @@ namespace Ryujinx.Graphics.Vulkan
ImageLayout.General,
ImageLayout.PresentSrcKhr);
_gd.FlushAllCommands();
_gd.CommandBufferPool.Return(
cbs,
[_imageAvailableSemaphores[semaphoreIndex]],

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy
@@ -60,6 +61,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
return ResultCode.Success;
}
[CommandCmif(10)]
// ExitProcessAndReturn -> nn::am::service::LibraryAppletInfo
public ResultCode ExitProcessAndReturn(ServiceCtx context)
{
// Exits the LibraryApplet and returns to running the title which launched this LibraryApplet (qlaunch for example).
// On success, official sw will enter an infinite loop with sleep-thread value 86400000000000.
// Since we don't currently support qlaunch, it's fine to stub it.
Logger.Stub?.PrintStub(LogClass.Service);
return ResultCode.Success;
}
[CommandCmif(11)]
// GetLibraryAppletInfo() -> nn::am::service::LibraryAppletInfo
@@ -83,7 +97,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
AppletIdentifyInfo appletIdentifyInfo = new()
{
AppletId = AppletId.QLaunch,
TitleId = 0x0100000000001000,
// 0x4 padding
TitleId = 0x0100000000001000, // qlaunch systemAppletMenu title ID
};
context.ResponseData.WriteStruct(appletIdentifyInfo);

View File

@@ -28,21 +28,61 @@ namespace Ryujinx.HLE.Loaders.Processes
private ulong _latestPid;
private readonly object _pidLock = new();
#nullable enable
public ProcessResult? ActiveApplication
{
get
{
return _processesByPid.GetValueOrDefault(_latestPid);
// Using this if statement locks up the UI and prevents a new game from loading.
// Haven't quite deduced why yet.
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
throw new RyujinxException(
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
lock (_pidLock)
{
// Check if _latestPid is still valid
if (_latestPid == 0)
{
return null;
}
return value;
// 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;
TitleIDs.CurrentApplication.Value = null;
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;
TitleIDs.CurrentApplication.Value = null;
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
@@ -285,5 +325,39 @@ namespace Ryujinx.HLE.Loaders.Processes
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;
TitleIDs.CurrentApplication.Value = null;
}
}
}
}
/// <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;
TitleIDs.CurrentApplication.Value = null;
}
}
}
}

View File

@@ -183,6 +183,7 @@ namespace Ryujinx.HLE
{
if (disposing)
{
Processes.ClearAllProcesses();
System.Dispose();
AudioDeviceDriver.Dispose();
FileSystem.Dispose();