Compare commits

..

10 Commits

Author SHA1 Message Date
Isaac Marovitz
6ce49a2dc7 Ava UI: Handle updates containing non numeric characters (#7043)
* Handle updates containing non numeric characters

Smh

Dont be stupid

* Use Berry’s method

* Thanks gdk

* Remove using
2024-07-25 16:44:33 -03:00
riperiperi
ccd330ba0f Vulkan: Add missing barriers for texture to buffer copy (#7092)
This barrier has always been missing, but it only became apparent when #7012 merged.

I also added some barriers in case the target buffer used here is used by other commands, though right now it isn't.

Fixes a regression where water would turn white on AMD GPUs with the proprietary driver. May fix other issues on this driver.
2024-07-25 16:34:30 -03:00
gdkchan
95d252b7b8 Update kernel GetInfo SVC for firmware 18.0.0 (#7075)
* Implement kernel GetInfo AliasRegionExtraSize

* Implement IsSvcPermitted

* Remove warning supressions that are no longer needed

* Remove useless cast
2024-07-22 12:46:04 -03:00
TSRBerry
add681144b Fix checking for the wrong metadata files for applications launched with a different program index (#7055)
* Fix checking for the wrong update metadata file

* Apply the same fix for dlc.json

* Use the base application ids for updates and DLCs in the GUI too

This shouldn't actually change anything, since the program index part of the application id
should always be 0 for all applications currently seen by the GUI.

This was just done for completeness.
2024-07-21 14:42:23 -03:00
TSRBerry
c6dc00815a Make sure TryGetApplicationsFromFile() doesn't throw exceptions anymore (#7046)
* Add docstrings for exceptions to methods near TryGetApplicationsFromFile()

* Make sure TryGetApplicationsFromFile() doesn't throw exceptions anymore

* Add missing filePath to ApplicationData when loading applications from ExeFS

* Fix typo

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2024-07-20 16:35:43 -03:00
gdkchan
99f04ac1a6 Fix Skia saving screenshot with transparent background and incorrect origin (#7073)
* Fix Skia saving screenshot with transparent background and incorrect origin

* Remove code that is no longer necessary
2024-07-20 16:27:40 -03:00
gdkchan
ce09450743 Unlink server sessions from multi-wait when service stops processing requests (#7072) 2024-07-20 16:17:40 -03:00
Isaac Marovitz
2cb80f37d4 Ava UI: Auto select newly added updates & DLC (#7026)
* Fix DLC not being selected

* FIx conflicts

* Apply suggestions from code review

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

---------

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
2024-07-19 19:00:15 +02:00
gdkchan
827069e784 Add missing Buffer attribute on NGC Check method (#7051) 2024-07-18 15:11:00 -03:00
riperiperi
1a919e99b2 Vulkan: Defer guest barriers, and improve image barrier timings (#7012)
* More guarantees for buffer correct placement, defer guest requested buffers

* Split RP on indirect barrier rn

* Better handling for feedback loops.

* Qualcomm barriers suck too

* Fix condition

* Remove unused field

* Allow render pass barriers on turnip for now
2024-07-17 20:21:32 -03:00
49 changed files with 852 additions and 536 deletions

View File

@@ -74,13 +74,15 @@ namespace Ryujinx.Graphics.GAL
public int ArrayLength { get; }
public ResourceType Type { get; }
public ResourceStages Stages { get; }
public bool Write { get; }
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages)
public ResourceUsage(int binding, int arrayLength, ResourceType type, ResourceStages stages, bool write)
{
Binding = binding;
ArrayLength = arrayLength;
Type = type;
Stages = stages;
Write = write;
}
public override int GetHashCode()

View File

@@ -78,9 +78,9 @@ namespace Ryujinx.Graphics.Gpu.Shader
ResourceStages stages = vertexAsCompute ? ResourceStages.Compute | ResourceStages.Vertex : VtgStages;
PopulateDescriptorAndUsages(stages, ResourceType.UniformBuffer, uniformSetIndex, 1, rrc.ReservedConstantBuffers - 1);
PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers);
PopulateDescriptorAndUsages(stages, ResourceType.StorageBuffer, storageSetIndex, 0, rrc.ReservedStorageBuffers, true);
PopulateDescriptorAndUsages(stages, ResourceType.BufferTexture, textureSetIndex, 0, rrc.ReservedTextures);
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages);
PopulateDescriptorAndUsages(stages, ResourceType.BufferImage, imageSetIndex, 0, rrc.ReservedImages, true);
}
/// <summary>
@@ -91,10 +91,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="setIndex">Resource set index where the resources are used</param>
/// <param name="start">First binding number</param>
/// <param name="count">Amount of bindings</param>
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count)
/// <param name="write">True if the binding is written from the shader, false otherwise</param>
private void PopulateDescriptorAndUsages(ResourceStages stages, ResourceType type, int setIndex, int start, int count, bool write = false)
{
AddDescriptor(stages, type, setIndex, start, count);
AddUsage(stages, type, setIndex, start, count);
AddUsage(stages, type, setIndex, start, count, write);
}
/// <summary>
@@ -216,11 +217,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
/// <param name="binding">Binding number where the resource will be bound</param>
/// <param name="count">Number of resources bound at the binding location</param>
private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
/// <param name="write">True if the binding is written from the shader, false otherwise</param>
private void AddUsage(ResourceStages stages, ResourceType type, int setIndex, int binding, int count, bool write = false)
{
for (int index = 0; index < count; index++)
{
_resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages));
_resourceUsages[setIndex].Add(new ResourceUsage(binding + index, 1, type, stages, write));
}
}
@@ -238,7 +240,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
buffer.Binding,
1,
isStorage ? ResourceType.StorageBuffer : ResourceType.UniformBuffer,
stages));
stages,
buffer.Flags.HasFlag(BufferUsageFlags.Write)));
}
}
@@ -254,7 +257,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
{
ResourceType type = GetTextureResourceType(texture, isImage);
GetUsages(texture.Set).Add(new ResourceUsage(texture.Binding, texture.ArrayLength, type, stages));
GetUsages(texture.Set).Add(new ResourceUsage(
texture.Binding,
texture.ArrayLength,
type,
stages,
texture.Flags.HasFlag(TextureUsageFlags.ImageStore)));
}
}

View File

@@ -1,6 +1,7 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vulkan
{
@@ -8,22 +9,64 @@ namespace Ryujinx.Graphics.Vulkan
{
private const int MaxBarriersPerCall = 16;
private const AccessFlags BaseAccess = AccessFlags.ShaderReadBit | AccessFlags.ShaderWriteBit;
private const AccessFlags BufferAccess = AccessFlags.IndexReadBit | AccessFlags.VertexAttributeReadBit | AccessFlags.UniformReadBit;
private const AccessFlags CommandBufferAccess = AccessFlags.IndirectCommandReadBit;
private readonly VulkanRenderer _gd;
private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new();
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new();
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new();
private readonly List<BarrierWithStageFlags<MemoryBarrier, int>> _memoryBarriers = new();
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier, int>> _bufferBarriers = new();
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage>> _imageBarriers = new();
private int _queuedBarrierCount;
private enum IncoherentBarrierType
{
None,
Texture,
All,
CommandBuffer
}
private PipelineStageFlags _incoherentBufferWriteStages;
private PipelineStageFlags _incoherentTextureWriteStages;
private PipelineStageFlags _extraStages;
private IncoherentBarrierType _queuedIncoherentBarrier;
public BarrierBatch(VulkanRenderer gd)
{
_gd = gd;
}
public static (AccessFlags Access, PipelineStageFlags Stages) GetSubpassAccessSuperset(VulkanRenderer gd)
{
AccessFlags access = BufferAccess;
PipelineStageFlags stages = PipelineStageFlags.AllGraphicsBit;
if (gd.TransformFeedbackApi != null)
{
access |= AccessFlags.TransformFeedbackWriteBitExt;
stages |= PipelineStageFlags.TransformFeedbackBitExt;
}
if (!gd.IsTBDR)
{
// Desktop GPUs can transform image barriers into memory barriers.
access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
stages |= PipelineStageFlags.ColorAttachmentOutputBit;
}
return (access, stages);
}
private readonly record struct StageFlags : IEquatable<StageFlags>
{
public readonly PipelineStageFlags Source;
@@ -36,47 +79,130 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private readonly struct BarrierWithStageFlags<T> where T : unmanaged
private readonly struct BarrierWithStageFlags<T, T2> where T : unmanaged
{
public readonly StageFlags Flags;
public readonly T Barrier;
public readonly T2 Resource;
public BarrierWithStageFlags(StageFlags flags, T barrier)
{
Flags = flags;
Barrier = barrier;
Resource = default;
}
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier)
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier, T2 resource)
{
Flags = new StageFlags(srcStageFlags, dstStageFlags);
Barrier = barrier;
Resource = resource;
}
}
private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
private void QueueBarrier<T, T2>(List<BarrierWithStageFlags<T, T2>> list, T barrier, T2 resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
{
list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier));
list.Add(new BarrierWithStageFlags<T, T2>(srcStageFlags, dstStageFlags, barrier, resource));
_queuedBarrierCount++;
}
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags);
QueueBarrier(_memoryBarriers, barrier, default, srcStageFlags, dstStageFlags);
}
public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags);
QueueBarrier(_bufferBarriers, barrier, default, srcStageFlags, dstStageFlags);
}
public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
public void QueueBarrier(ImageMemoryBarrier barrier, TextureStorage resource, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
{
QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags);
QueueBarrier(_imageBarriers, barrier, resource, srcStageFlags, dstStageFlags);
}
public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe void FlushMemoryBarrier(ShaderCollection program, bool inRenderPass)
{
if (_queuedIncoherentBarrier > IncoherentBarrierType.None)
{
// We should emit a memory barrier if there's a write access in the program (current program, or program since last barrier)
bool hasTextureWrite = _incoherentTextureWriteStages != PipelineStageFlags.None;
bool hasBufferWrite = _incoherentBufferWriteStages != PipelineStageFlags.None;
bool hasBufferBarrier = _queuedIncoherentBarrier > IncoherentBarrierType.Texture;
if (hasTextureWrite || (hasBufferBarrier && hasBufferWrite))
{
AccessFlags access = BaseAccess;
PipelineStageFlags stages = inRenderPass ? PipelineStageFlags.AllGraphicsBit : PipelineStageFlags.AllCommandsBit;
if (hasBufferBarrier && hasBufferWrite)
{
access |= BufferAccess;
if (_gd.TransformFeedbackApi != null)
{
access |= AccessFlags.TransformFeedbackWriteBitExt;
stages |= PipelineStageFlags.TransformFeedbackBitExt;
}
}
if (_queuedIncoherentBarrier == IncoherentBarrierType.CommandBuffer)
{
access |= CommandBufferAccess;
stages |= PipelineStageFlags.DrawIndirectBit;
}
MemoryBarrier barrier = new MemoryBarrier()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = access,
DstAccessMask = access
};
QueueBarrier(barrier, stages, stages);
_incoherentTextureWriteStages = program?.IncoherentTextureWriteStages ?? PipelineStageFlags.None;
if (_queuedIncoherentBarrier > IncoherentBarrierType.Texture)
{
if (program != null)
{
_incoherentBufferWriteStages = program.IncoherentBufferWriteStages | _extraStages;
}
else
{
_incoherentBufferWriteStages = PipelineStageFlags.None;
}
}
_queuedIncoherentBarrier = IncoherentBarrierType.None;
}
}
}
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{
Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
}
public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{
if (program != null)
{
_incoherentBufferWriteStages |= program.IncoherentBufferWriteStages | _extraStages;
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
}
FlushMemoryBarrier(program, inRenderPass);
if (!inRenderPass && rpHolder != null)
{
// Render pass is about to begin. Queue any fences that normally interrupt the pass.
rpHolder.InsertForcedFences(cbs);
}
while (_queuedBarrierCount > 0)
{
int memoryCount = 0;
@@ -86,20 +212,20 @@ namespace Ryujinx.Graphics.Vulkan
bool hasBarrier = false;
StageFlags flags = default;
static void AddBarriers<T>(
static void AddBarriers<T, T2>(
Span<T> target,
ref int queuedBarrierCount,
ref bool hasBarrier,
ref StageFlags flags,
ref int count,
List<BarrierWithStageFlags<T>> list) where T : unmanaged
List<BarrierWithStageFlags<T, T2>> list) where T : unmanaged
{
int firstMatch = -1;
int end = list.Count;
for (int i = 0; i < list.Count; i++)
{
BarrierWithStageFlags<T> barrier = list[i];
BarrierWithStageFlags<T, T2> barrier = list[i];
if (!hasBarrier)
{
@@ -162,21 +288,60 @@ namespace Ryujinx.Graphics.Vulkan
}
}
if (insideRenderPass)
if (inRenderPass && _imageBarriers.Count > 0)
{
// Image barriers queued in the batch are meant to be globally scoped,
// but inside a render pass they're scoped to just the range of the render pass.
// On MoltenVK, we just break the rules and always use image barrier.
// On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
// TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done,
// notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future.
// Generally, we want to avoid this from happening in the future, so flag the texture to immediately
// emit a barrier whenever the current render pass is bound again.
bool anyIsNonAttachment = false;
foreach (BarrierWithStageFlags<ImageMemoryBarrier, TextureStorage> barrier in _imageBarriers)
{
// If the binding is an attachment, don't add it as a forced fence.
bool isAttachment = rpHolder.ContainsAttachment(barrier.Resource);
if (!isAttachment)
{
rpHolder.AddForcedFence(barrier.Resource, barrier.Flags.Dest);
anyIsNonAttachment = true;
}
}
if (_gd.IsTBDR)
{
if (!_gd.IsMoltenVk)
{
if (!anyIsNonAttachment)
{
// This case is a feedback loop. To prevent this from causing an absolute performance disaster,
// remove the barriers entirely.
// If this is not here, there will be a lot of single draw render passes.
// TODO: explicit handling for feedback loops, likely outside this class.
_queuedBarrierCount -= _imageBarriers.Count;
_imageBarriers.Clear();
}
else
{
// TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available.
// Metal already has hazard tracking so MVK doesn't need this.
endRenderPass();
inRenderPass = false;
}
}
}
else
{
// Generic pipeline memory barriers will work for desktop GPUs.
// They do require a few more access flags on the subpass dependency, though.
foreach (var barrier in _imageBarriers)
{
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>(
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>(
barrier.Flags,
new MemoryBarrier()
{
@@ -190,6 +355,22 @@ namespace Ryujinx.Graphics.Vulkan
}
}
if (inRenderPass && _memoryBarriers.Count > 0)
{
PipelineStageFlags allFlags = PipelineStageFlags.None;
foreach (var barrier in _memoryBarriers)
{
allFlags |= barrier.Flags.Dest;
}
if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags))
{
endRenderPass();
inRenderPass = false;
}
}
AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
@@ -198,14 +379,14 @@ namespace Ryujinx.Graphics.Vulkan
{
PipelineStageFlags srcStageFlags = flags.Source;
if (insideRenderPass)
if (inRenderPass)
{
// Inside a render pass, barrier stages can only be from rasterization.
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
}
_gd.Api.CmdPipelineBarrier(
cb,
cbs.CommandBuffer,
srcStageFlags,
flags.Dest,
0,
@@ -219,6 +400,41 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private void QueueIncoherentBarrier(IncoherentBarrierType type)
{
if (type > _queuedIncoherentBarrier)
{
_queuedIncoherentBarrier = type;
}
}
public void QueueTextureBarrier()
{
QueueIncoherentBarrier(IncoherentBarrierType.Texture);
}
public void QueueMemoryBarrier()
{
QueueIncoherentBarrier(IncoherentBarrierType.All);
}
public void QueueCommandBufferBarrier()
{
QueueIncoherentBarrier(IncoherentBarrierType.CommandBuffer);
}
public void EnableTfbBarriers(bool enable)
{
if (enable)
{
_extraStages |= PipelineStageFlags.TransformFeedbackBitExt;
}
else
{
_extraStages &= ~PipelineStageFlags.TransformFeedbackBitExt;
}
}
public void Dispose()
{
_memoryBarrierBatch.Dispose();

View File

@@ -59,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var scalingResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var sharpeningResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 3)
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 4)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_sampler = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View File

@@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var resourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View File

@@ -81,20 +81,20 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var edgeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var blendResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 4)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
var neighbourResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 1)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 3)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_samplerLinear = _renderer.CreateSampler(SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));

View File

@@ -286,10 +286,23 @@ namespace Ryujinx.Graphics.Vulkan
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
gd.Barriers.Flush(cbs.CommandBuffer, false, null);
gd.Barriers.Flush(cbs, false, null, null);
}
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
public void AddStoreOpUsage()
{
if (_colors != null)
{
foreach (var color in _colors)
{
color.Storage?.AddStoreOpUsage(false);
}
}
_depthStencil?.Storage?.AddStoreOpUsage(true);
}
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs)

View File

@@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
var strideChangeResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programStrideChange = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
var colorCopyResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.TextureAndSampler, 0)
.Add(ResourceStages.Compute, ResourceType.Image, 0).Build();
.Add(ResourceStages.Compute, ResourceType.Image, 0, true).Build();
_programColorCopyShortening = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -155,7 +155,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertD32S8ToD24S8ResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programConvertD32S8ToD24S8 = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndexBufferResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2).Build();
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true).Build();
_programConvertIndexBuffer = gd.CreateProgramWithMinimalLayout(new[]
{
@@ -175,7 +175,7 @@ namespace Ryujinx.Graphics.Vulkan
var convertIndirectDataResourceLayout = new ResourceLayoutBuilder()
.Add(ResourceStages.Compute, ResourceType.UniformBuffer, 0)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 1)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 2, true)
.Add(ResourceStages.Compute, ResourceType.StorageBuffer, 3).Build();
_programConvertIndirectData = gd.CreateProgramWithMinimalLayout(new[]

View File

@@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Vulkan
protected FramebufferParams FramebufferParams;
private Auto<DisposableFramebuffer> _framebuffer;
private RenderPassHolder _rpHolder;
private Auto<DisposableRenderPass> _renderPass;
private RenderPassHolder _nullRenderPass;
private int _writtenAttachmentCount;
@@ -85,8 +86,6 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfActive;
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
private ulong _drawCountSinceBarrier;
public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; }
@@ -135,48 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void Barrier()
{
if (_drawCountSinceBarrier != DrawCount)
{
_drawCountSinceBarrier = DrawCount;
// Barriers are not supported inside a render pass on Apple GPUs.
// As a workaround, end the render pass.
if (Gd.Vendor == Vendor.Apple)
{
EndRenderPass();
}
}
MemoryBarrier memoryBarrier = new()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
};
PipelineStageFlags pipelineStageFlags = PipelineStageFlags.VertexShaderBit | PipelineStageFlags.FragmentShaderBit;
if (Gd.Capabilities.SupportsGeometryShader)
{
pipelineStageFlags |= PipelineStageFlags.GeometryShaderBit;
}
if (Gd.Capabilities.SupportsTessellationShader)
{
pipelineStageFlags |= PipelineStageFlags.TessellationControlShaderBit | PipelineStageFlags.TessellationEvaluationShaderBit;
}
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
pipelineStageFlags,
pipelineStageFlags,
0,
1,
memoryBarrier,
0,
null,
0,
null);
Gd.Barriers.QueueMemoryBarrier();
}
public void ComputeBarrier()
@@ -203,6 +161,7 @@ namespace Ryujinx.Graphics.Vulkan
public void BeginTransformFeedback(PrimitiveTopology topology)
{
Gd.Barriers.EnableTfbBarriers(true);
_tfEnabled = true;
}
@@ -249,7 +208,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass();
@@ -287,7 +246,7 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass();
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, RenderPassActive, _rpHolder, EndRenderPassDelegate);
BeginRenderPass();
@@ -299,24 +258,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void CommandBufferBarrier()
{
MemoryBarrier memoryBarrier = new()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = BufferHolder.DefaultAccessFlags,
DstAccessMask = AccessFlags.IndirectCommandReadBit,
};
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.DrawIndirectBit,
0,
1,
memoryBarrier,
0,
null,
0,
null);
Gd.Barriers.QueueCommandBufferBarrier();
}
public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size)
@@ -722,6 +664,7 @@ namespace Ryujinx.Graphics.Vulkan
public void EndTransformFeedback()
{
Gd.Barriers.EnableTfbBarriers(false);
PauseTransformFeedbackInternal();
_tfEnabled = false;
}
@@ -1408,24 +1351,7 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe void TextureBarrier()
{
MemoryBarrier memoryBarrier = new()
{
SType = StructureType.MemoryBarrier,
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
};
Gd.Api.CmdPipelineBarrier(
CommandBuffer,
PipelineStageFlags.FragmentShaderBit,
PipelineStageFlags.FragmentShaderBit,
0,
1,
memoryBarrier,
0,
null,
0,
null);
Gd.Barriers.QueueTextureBarrier();
}
public void TextureBarrierTiled()
@@ -1532,12 +1458,15 @@ namespace Ryujinx.Graphics.Vulkan
// Use the null framebuffer.
_nullRenderPass ??= new RenderPassHolder(Gd, Device, new RenderPassCacheKey(), FramebufferParams);
_rpHolder = _nullRenderPass;
_renderPass = _nullRenderPass.GetRenderPass();
_framebuffer = _nullRenderPass.GetFramebuffer(Gd, Cbs, FramebufferParams);
}
else
{
(_renderPass, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
(_rpHolder, _framebuffer) = FramebufferParams.GetPassAndFramebuffer(Gd, Device, Cbs);
_renderPass = _rpHolder.GetRenderPass();
}
}
@@ -1564,7 +1493,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
}
@@ -1629,7 +1558,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
@@ -1708,6 +1637,8 @@ namespace Ryujinx.Graphics.Vulkan
{
if (RenderPassActive)
{
FramebufferParams.AddStoreOpUsage();
PauseTransformFeedbackInternal();
Gd.Api.CmdEndRenderPass(CommandBuffer);
SignalRenderPassEnd();

View File

@@ -9,13 +9,6 @@ namespace Ryujinx.Graphics.Vulkan
{
static class PipelineConverter
{
private const AccessFlags SubpassAccessMask =
AccessFlags.MemoryReadBit |
AccessFlags.MemoryWriteBit |
AccessFlags.ShaderReadBit |
AccessFlags.ColorAttachmentWriteBit |
AccessFlags.DepthStencilAttachmentWriteBit;
public static unsafe DisposableRenderPass ToRenderPass(this ProgramPipelineState state, VulkanRenderer gd, Device device)
{
const int MaxAttachments = Constants.MaxRenderTargets + 1;
@@ -108,7 +101,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
var subpassDependency = CreateSubpassDependency();
var subpassDependency = CreateSubpassDependency(gd);
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
@@ -129,29 +122,33 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public static SubpassDependency CreateSubpassDependency()
public static SubpassDependency CreateSubpassDependency(VulkanRenderer gd)
{
var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
return new SubpassDependency(
0,
0,
PipelineStageFlags.AllGraphicsBit,
PipelineStageFlags.AllGraphicsBit,
SubpassAccessMask,
SubpassAccessMask,
stages,
stages,
access,
access,
0);
}
public unsafe static SubpassDependency2 CreateSubpassDependency2()
public unsafe static SubpassDependency2 CreateSubpassDependency2(VulkanRenderer gd)
{
var (access, stages) = BarrierBatch.GetSubpassAccessSuperset(gd);
return new SubpassDependency2(
StructureType.SubpassDependency2,
null,
0,
0,
PipelineStageFlags.AllGraphicsBit,
PipelineStageFlags.AllGraphicsBit,
SubpassAccessMask,
SubpassAccessMask,
stages,
stages,
access,
access,
0);
}

View File

@@ -257,7 +257,7 @@ namespace Ryujinx.Graphics.Vulkan
PreloadCbs = null;
}
Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
Gd.Barriers.Flush(Cbs, false, null, null);
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush();

View File

@@ -1,5 +1,7 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan
{
@@ -29,10 +31,13 @@ namespace Ryujinx.Graphics.Vulkan
}
}
private readonly record struct ForcedFence(TextureStorage Texture, PipelineStageFlags StageFlags);
private readonly TextureView[] _textures;
private readonly Auto<DisposableRenderPass> _renderPass;
private readonly HashTableSlim<FramebufferCacheKey, Auto<DisposableFramebuffer>> _framebuffers;
private readonly RenderPassCacheKey _key;
private readonly List<ForcedFence> _forcedFences;
public unsafe RenderPassHolder(VulkanRenderer gd, Device device, RenderPassCacheKey key, FramebufferParams fb)
{
@@ -105,7 +110,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
var subpassDependency = PipelineConverter.CreateSubpassDependency();
var subpassDependency = PipelineConverter.CreateSubpassDependency(gd);
fixed (AttachmentDescription* pAttachmentDescs = attachmentDescs)
{
@@ -138,6 +143,8 @@ namespace Ryujinx.Graphics.Vulkan
_textures = textures;
_key = key;
_forcedFences = new List<ForcedFence>();
}
public Auto<DisposableFramebuffer> GetFramebuffer(VulkanRenderer gd, CommandBufferScoped cbs, FramebufferParams fb)
@@ -159,6 +166,37 @@ namespace Ryujinx.Graphics.Vulkan
return _renderPass;
}
public void AddForcedFence(TextureStorage storage, PipelineStageFlags stageFlags)
{
if (!_forcedFences.Any(fence => fence.Texture == storage))
{
_forcedFences.Add(new ForcedFence(storage, stageFlags));
}
}
public void InsertForcedFences(CommandBufferScoped cbs)
{
if (_forcedFences.Count > 0)
{
_forcedFences.RemoveAll((entry) =>
{
if (entry.Texture.Disposed)
{
return true;
}
entry.Texture.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, entry.StageFlags);
return false;
});
}
}
public bool ContainsAttachment(TextureStorage storage)
{
return _textures.Any(view => view.Storage == storage);
}
public void Dispose()
{
// Dispose all framebuffers.

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding)
public ResourceLayoutBuilder Add(ResourceStages stages, ResourceType type, int binding, bool write = false)
{
int setIndex = type switch
{
@@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
};
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, 1, type, stages));
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages));
_resourceUsages[setIndex].Add(new ResourceUsage(binding, 1, type, stages, write));
return this;
}

View File

@@ -27,6 +27,9 @@ namespace Ryujinx.Graphics.Vulkan
public uint Stages { get; }
public PipelineStageFlags IncoherentBufferWriteStages { get; }
public PipelineStageFlags IncoherentTextureWriteStages { get; }
public ResourceBindingSegment[][] ClearSegments { get; }
public ResourceBindingSegment[][] BindingSegments { get; }
public DescriptorSetTemplate[] Templates { get; }
@@ -131,6 +134,7 @@ namespace Ryujinx.Graphics.Vulkan
ClearSegments = BuildClearSegments(sets);
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
Templates = BuildTemplates(usePushDescriptors);
(IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
@@ -377,6 +381,73 @@ namespace Ryujinx.Graphics.Vulkan
return templates;
}
private PipelineStageFlags GetPipelineStages(ResourceStages stages)
{
PipelineStageFlags result = 0;
if ((stages & ResourceStages.Compute) != 0)
{
result |= PipelineStageFlags.ComputeShaderBit;
}
if ((stages & ResourceStages.Vertex) != 0)
{
result |= PipelineStageFlags.VertexShaderBit;
}
if ((stages & ResourceStages.Fragment) != 0)
{
result |= PipelineStageFlags.FragmentShaderBit;
}
if ((stages & ResourceStages.Geometry) != 0)
{
result |= PipelineStageFlags.GeometryShaderBit;
}
if ((stages & ResourceStages.TessellationControl) != 0)
{
result |= PipelineStageFlags.TessellationControlShaderBit;
}
if ((stages & ResourceStages.TessellationEvaluation) != 0)
{
result |= PipelineStageFlags.TessellationEvaluationShaderBit;
}
return result;
}
private (PipelineStageFlags Buffer, PipelineStageFlags Texture) BuildIncoherentStages(ReadOnlyCollection<ResourceUsageCollection> setUsages)
{
PipelineStageFlags buffer = PipelineStageFlags.None;
PipelineStageFlags texture = PipelineStageFlags.None;
foreach (var set in setUsages)
{
foreach (var range in set.Usages)
{
if (range.Write)
{
PipelineStageFlags stages = GetPipelineStages(range.Stages);
switch (range.Type)
{
case ResourceType.Image:
texture |= stages;
break;
case ResourceType.StorageBuffer:
case ResourceType.BufferImage:
buffer |= stages;
break;
}
}
}
}
return (buffer, texture);
}
private async Task BackgroundCompilation()
{
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));

View File

@@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageLayout.General,
ImageLayout.General);
var subpassDependency = PipelineConverter.CreateSubpassDependency2();
var subpassDependency = PipelineConverter.CreateSubpassDependency2(gd);
fixed (AttachmentDescription2* pAttachmentDescs = attachmentDescs)
{

View File

@@ -38,6 +38,8 @@ namespace Ryujinx.Graphics.Vulkan
public TextureCreateInfo Info => _info;
public bool Disposed { get; private set; }
private readonly Image _image;
private readonly Auto<DisposableImage> _imageAuto;
private readonly Auto<MemoryAllocation> _allocationAuto;
@@ -433,6 +435,17 @@ namespace Ryujinx.Graphics.Vulkan
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
}
public void AddStoreOpUsage(bool depthStencil)
{
_lastModificationStage = depthStencil ?
PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
_lastModificationAccess = depthStencil ?
AccessFlags.DepthStencilAttachmentWriteBit :
AccessFlags.ColorAttachmentWriteBit;
}
public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
@@ -458,7 +471,7 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(),
_info.Levels);
_gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
_gd.Barriers.QueueBarrier(barrier, this, srcStageFlags, dstStageFlags);
_lastReadStage = PipelineStageFlags.None;
_lastReadAccess = AccessFlags.None;
@@ -491,7 +504,7 @@ namespace Ryujinx.Graphics.Vulkan
_info.GetLayers(),
_info.Levels);
_gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
_gd.Barriers.QueueBarrier(barrier, this, _lastModificationStage, dstStageFlags);
_lastModificationAccess = AccessFlags.None;
}
@@ -514,6 +527,8 @@ namespace Ryujinx.Graphics.Vulkan
public void Dispose()
{
Disposed = true;
if (_aliasedStorages != null)
{
foreach (var storage in _aliasedStorages.Values)

View File

@@ -667,8 +667,36 @@ namespace Ryujinx.Graphics.Vulkan
if (PrepareOutputBuffer(cbs, hostSize, buffer, out VkBuffer copyToBuffer, out BufferHolder tempCopyHolder))
{
// No barrier necessary, as this is a temporary copy buffer.
offset = 0;
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.TransferWriteBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
offset,
outSize);
}
InsertImageBarrier(
_gd.Api,
cbs.CommandBuffer,
image,
TextureStorage.DefaultAccessMask,
AccessFlags.TransferReadBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
Info.Format.ConvertAspectFlags(),
FirstLayer + layer,
FirstLevel + level,
1,
1);
CopyFromOrToBuffer(cbs.CommandBuffer, copyToBuffer, image, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride);
@@ -677,6 +705,19 @@ namespace Ryujinx.Graphics.Vulkan
CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset);
tempCopyHolder.Dispose();
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
AccessFlags.TransferWriteBit,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.TransferBit,
PipelineStageFlags.AllCommandsBit,
offset,
outSize);
}
}
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)
@@ -993,7 +1034,7 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotImplementedException();
}
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
CommandBufferScoped cbs,
@@ -1006,7 +1047,7 @@ namespace Ryujinx.Graphics.Vulkan
rpHolder = new RenderPassHolder(gd, device, key, fb);
}
return (rpHolder.GetRenderPass(), rpHolder.GetFramebuffer(gd, cbs, fb));
return (rpHolder, rpHolder.GetFramebuffer(gd, cbs, fb));
}
public void AddRenderPass(RenderPassCacheKey key, RenderPassHolder renderPass)

View File

@@ -939,6 +939,11 @@ namespace Ryujinx.Graphics.Vulkan
ScreenCaptured?.Invoke(this, bitmap);
}
public bool SupportsRenderPassBarrier(PipelineStageFlags flags)
{
return !(IsMoltenVk || IsQualcommProprietary);
}
public unsafe void Dispose()
{
if (!_initialized)

View File

@@ -473,7 +473,7 @@ namespace Ryujinx.UI.Widgets
private void ManageDlc_Clicked(object sender, EventArgs args)
{
new DlcWindow(_virtualFileSystem, _applicationData.IdString, _applicationData).Show();
new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show();
}
private void ManageCheats_Clicked(object sender, EventArgs args)

View File

@@ -24,7 +24,7 @@ namespace Ryujinx.UI.Windows
public class DlcWindow : Window
{
private readonly VirtualFileSystem _virtualFileSystem;
private readonly string _applicationId;
private readonly string _applicationIdBase;
private readonly string _dlcJsonPath;
private readonly List<DownloadableContentContainer> _dlcContainerList;
@@ -36,16 +36,16 @@ namespace Ryujinx.UI.Windows
[GUI] TreeSelection _dlcTreeSelection;
#pragma warning restore CS0649, IDE0044
public DlcWindow(VirtualFileSystem virtualFileSystem, string titleId, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, titleId, applicationData) { }
public DlcWindow(VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, applicationIdBase, applicationData) { }
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationId, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow"))
private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow"))
{
builder.Autoconnect(this);
_applicationId = applicationId;
_applicationIdBase = applicationIdBase;
_virtualFileSystem = virtualFileSystem;
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationId, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationId.ToUpper()}]";
_dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationIdBase, "dlc.json");
_baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationIdBase.ToUpper()}]";
try
{
@@ -163,7 +163,7 @@ namespace Ryujinx.UI.Windows
if (nca.Header.ContentType == NcaContentType.PublicData)
{
if (nca.GetProgramIdBase() != (ulong.Parse(_applicationId, NumberStyles.HexNumber) & ~0x1FFFUL))
if (nca.GetProgramIdBase() != ulong.Parse(_applicationIdBase, NumberStyles.HexNumber))
{
continue;
}

View File

@@ -51,7 +51,7 @@ namespace Ryujinx.UI.Windows
_applicationData = applicationData;
_virtualFileSystem = virtualFileSystem;
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "updates.json");
_updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "updates.json");
_radioButtonToPathDictionary = new Dictionary<RadioButton, string>();
try
@@ -67,7 +67,7 @@ namespace Ryujinx.UI.Windows
};
}
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdString}]";
_baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdBaseString}]";
// Try to get updates from PFS first
AddUpdate(_applicationData.Path, true);

View File

@@ -1,10 +0,0 @@
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
enum AddressSpaceType
{
Addr32Bits = 0,
Addr36Bits = 1,
Addr32BitsNoMap = 2,
Addr39Bits = 3,
}
}

View File

@@ -58,11 +58,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public ulong AslrRegionStart { get; private set; }
public ulong AslrRegionEnd { get; private set; }
#pragma warning disable IDE0052 // Remove unread private member
private ulong _heapCapacity;
#pragma warning restore IDE0052
public ulong PhysicalMemoryUsage { get; private set; }
public ulong AliasRegionExtraSize { get; private set; }
private readonly KMemoryBlockManager _blockManager;
@@ -98,30 +97,21 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
_reservedAddressSpaceSize = reservedAddressSpaceSize;
}
private static readonly int[] _addrSpaceSizes = { 32, 36, 32, 39 };
public Result InitializeForProcess(
AddressSpaceType addrSpaceType,
bool aslrEnabled,
ProcessCreationFlags flags,
bool fromBack,
MemoryRegion memRegion,
ulong address,
ulong size,
KMemoryBlockSlabManager slabManager)
{
if ((uint)addrSpaceType > (uint)AddressSpaceType.Addr39Bits)
{
throw new ArgumentException($"AddressSpaceType bigger than {(uint)AddressSpaceType.Addr39Bits}: {(uint)addrSpaceType}", nameof(addrSpaceType));
}
_contextId = Context.ContextIdManager.GetId();
ulong addrSpaceBase = 0;
ulong addrSpaceSize = 1UL << _addrSpaceSizes[(int)addrSpaceType];
ulong addrSpaceSize = 1UL << GetAddressSpaceWidth(flags);
Result result = CreateUserAddressSpace(
addrSpaceType,
aslrEnabled,
flags,
fromBack,
addrSpaceBase,
addrSpaceSize,
@@ -138,6 +128,22 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return result;
}
private static int GetAddressSpaceWidth(ProcessCreationFlags flags)
{
switch (flags & ProcessCreationFlags.AddressSpaceMask)
{
case ProcessCreationFlags.AddressSpace32Bit:
case ProcessCreationFlags.AddressSpace32BitWithoutAlias:
return 32;
case ProcessCreationFlags.AddressSpace64BitDeprecated:
return 36;
case ProcessCreationFlags.AddressSpace64Bit:
return 39;
}
throw new ArgumentException($"Invalid process flags {flags}", nameof(flags));
}
private struct Region
{
public ulong Start;
@@ -147,8 +153,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
}
private Result CreateUserAddressSpace(
AddressSpaceType addrSpaceType,
bool aslrEnabled,
ProcessCreationFlags flags,
bool fromBack,
ulong addrSpaceStart,
ulong addrSpaceEnd,
@@ -168,9 +173,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong stackAndTlsIoStart;
ulong stackAndTlsIoEnd;
switch (addrSpaceType)
AliasRegionExtraSize = 0;
switch (flags & ProcessCreationFlags.AddressSpaceMask)
{
case AddressSpaceType.Addr32Bits:
case ProcessCreationFlags.AddressSpace32Bit:
aliasRegion.Size = 0x40000000;
heapRegion.Size = 0x40000000;
stackRegion.Size = 0;
@@ -183,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
stackAndTlsIoEnd = 0x40000000;
break;
case AddressSpaceType.Addr36Bits:
case ProcessCreationFlags.AddressSpace64BitDeprecated:
aliasRegion.Size = 0x180000000;
heapRegion.Size = 0x180000000;
stackRegion.Size = 0;
@@ -196,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
stackAndTlsIoEnd = 0x80000000;
break;
case AddressSpaceType.Addr32BitsNoMap:
case ProcessCreationFlags.AddressSpace32BitWithoutAlias:
aliasRegion.Size = 0;
heapRegion.Size = 0x80000000;
stackRegion.Size = 0;
@@ -209,7 +216,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
stackAndTlsIoEnd = 0x40000000;
break;
case AddressSpaceType.Addr39Bits:
case ProcessCreationFlags.AddressSpace64Bit:
if (_reservedAddressSpaceSize < addrSpaceEnd)
{
int addressSpaceWidth = (int)ulong.Log2(_reservedAddressSpaceSize);
@@ -218,8 +225,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
heapRegion.Size = 0x180000000;
stackRegion.Size = 1UL << (addressSpaceWidth - 8);
tlsIoRegion.Size = 1UL << (addressSpaceWidth - 3);
CodeRegionStart = BitUtils.AlignDown<ulong>(address, RegionAlignment);
codeRegionSize = BitUtils.AlignUp<ulong>(endAddr, RegionAlignment) - CodeRegionStart;
CodeRegionStart = BitUtils.AlignDown(address, RegionAlignment);
codeRegionSize = BitUtils.AlignUp(endAddr, RegionAlignment) - CodeRegionStart;
stackAndTlsIoStart = 0;
stackAndTlsIoEnd = 0;
AslrRegionStart = 0x8000000;
@@ -239,9 +246,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
stackAndTlsIoStart = 0;
stackAndTlsIoEnd = 0;
}
if (flags.HasFlag(ProcessCreationFlags.EnableAliasRegionExtraSize))
{
AliasRegionExtraSize = addrSpaceEnd / 8;
aliasRegion.Size += AliasRegionExtraSize;
}
break;
default:
throw new ArgumentException($"AddressSpaceType bigger than {(uint)AddressSpaceType.Addr39Bits}: {(uint)addrSpaceType}", nameof(addrSpaceType));
throw new ArgumentException($"Invalid process flags {flags}", nameof(flags));
}
CodeRegionEnd = CodeRegionStart + codeRegionSize;
@@ -266,6 +280,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong aslrMaxOffset = mapAvailableSize - mapTotalSize;
bool aslrEnabled = flags.HasFlag(ProcessCreationFlags.EnableAslr);
_aslrEnabled = aslrEnabled;
AddrSpaceStart = addrSpaceStart;
@@ -725,7 +741,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
address = 0;
if (size > HeapRegionEnd - HeapRegionStart)
if (size > HeapRegionEnd - HeapRegionStart || size > _heapCapacity)
{
return KernelResult.OutOfMemory;
}

View File

@@ -126,8 +126,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
_contextFactory = contextFactory ?? new ProcessContextFactory();
_customThreadStart = customThreadStart;
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
Pid = KernelContext.NewKipId();
if (Pid == 0 || Pid >= KernelConstants.InitialProcessId)
@@ -137,8 +135,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
InitializeMemoryManager(creationInfo.Flags);
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
ulong codeAddress = creationInfo.CodeAddress;
ulong codeSize = (ulong)creationInfo.CodePagesCount * KPageTableBase.PageSize;
@@ -148,9 +144,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
: KernelContext.SmallMemoryBlockSlabManager;
Result result = MemoryManager.InitializeForProcess(
addrSpaceType,
aslrEnabled,
!aslrEnabled,
creationInfo.Flags,
!creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr),
memRegion,
codeAddress,
codeSize,
@@ -234,8 +229,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
: KernelContext.SmallMemoryBlockSlabManager;
}
AddressSpaceType addrSpaceType = (AddressSpaceType)((int)(creationInfo.Flags & ProcessCreationFlags.AddressSpaceMask) >> (int)ProcessCreationFlags.AddressSpaceShift);
Pid = KernelContext.NewProcessId();
if (Pid == ulong.MaxValue || Pid < KernelConstants.InitialProcessId)
@@ -245,16 +238,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
InitializeMemoryManager(creationInfo.Flags);
bool aslrEnabled = creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr);
ulong codeAddress = creationInfo.CodeAddress;
ulong codeSize = codePagesCount * KPageTableBase.PageSize;
Result result = MemoryManager.InitializeForProcess(
addrSpaceType,
aslrEnabled,
!aslrEnabled,
creationInfo.Flags,
!creationInfo.Flags.HasFlag(ProcessCreationFlags.EnableAslr),
memRegion,
codeAddress,
codeSize,
@@ -309,8 +299,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private Result ParseProcessInfo(ProcessCreationInfo creationInfo)
{
// Ensure that the current kernel version is equal or above to the minimum required.
uint requiredKernelVersionMajor = (uint)Capabilities.KernelReleaseVersion >> 19;
uint requiredKernelVersionMinor = ((uint)Capabilities.KernelReleaseVersion >> 15) & 0xf;
uint requiredKernelVersionMajor = Capabilities.KernelReleaseVersion >> 19;
uint requiredKernelVersionMinor = (Capabilities.KernelReleaseVersion >> 15) & 0xf;
if (KernelContext.EnableVersionChecks)
{
@@ -519,12 +509,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return result;
}
#pragma warning disable CA1822 // Mark member as static
private void GenerateRandomEntropy()
{
// TODO.
}
#pragma warning restore CA1822
public Result Start(int mainThreadPriority, ulong stackSize)
{
@@ -1182,5 +1170,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
// TODO
return false;
}
public bool IsSvcPermitted(int svcId)
{
return Capabilities.IsSvcPermitted(svcId);
}
}
}

View File

@@ -8,6 +8,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KProcessCapabilities
{
private const int SvcMaskElementBits = 8;
public byte[] SvcAccessMask { get; }
public byte[] IrqAccessMask { get; }
@@ -22,7 +24,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public KProcessCapabilities()
{
// length / number of bits of the underlying type
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8];
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / SvcMaskElementBits];
IrqAccessMask = new byte[0x80];
}
@@ -208,7 +210,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.MaximumExceeded;
}
SvcAccessMask[svcId / 8] |= (byte)(1 << (svcId & 7));
SvcAccessMask[svcId / SvcMaskElementBits] |= (byte)(1 << (svcId % SvcMaskElementBits));
}
break;
@@ -324,5 +326,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return mask << (int)min;
}
public bool IsSvcPermitted(int svcId)
{
int index = svcId / SvcMaskElementBits;
int mask = 1 << (svcId % SvcMaskElementBits);
return (uint)svcId < KernelConstants.SupervisorCallCount && (SvcAccessMask[index] & mask) != 0;
}
}
}

View File

@@ -29,6 +29,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
PoolPartitionMask = 0xf << PoolPartitionShift,
OptimizeMemoryAllocation = 1 << 11,
DisableDeviceAddressSpaceMerge = 1 << 12,
EnableAliasRegionExtraSize = 1 << 13,
All =
Is64Bit |
@@ -38,6 +40,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
IsApplication |
DeprecatedUseSecureMemory |
PoolPartitionMask |
OptimizeMemoryAllocation,
OptimizeMemoryAllocation |
DisableDeviceAddressSpaceMerge |
EnableAliasRegionExtraSize,
}
}

View File

@@ -21,14 +21,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
SystemResourceSizeTotal,
SystemResourceSizeUsed,
ProgramId,
// NOTE: Added in 4.0.0, removed in 5.0.0.
InitialProcessIdRange,
InitialProcessIdRange, // NOTE: Added in 4.0.0, removed in 5.0.0.
UserExceptionContextAddress,
TotalNonSystemMemorySize,
UsedNonSystemMemorySize,
IsApplication,
FreeThreadCount,
ThreadTickCount,
IsSvcPermitted,
IoRegionHint,
AliasRegionExtraSize,
MesosphereCurrentProcess = 65001,
}
}

View File

@@ -84,6 +84,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidSize;
}
if (info.Flags.HasFlag(ProcessCreationFlags.EnableAliasRegionExtraSize))
{
if ((info.Flags & ProcessCreationFlags.AddressSpaceMask) != ProcessCreationFlags.AddressSpace64Bit ||
info.SystemResourcePagesCount <= 0)
{
return KernelResult.InvalidState;
}
// TODO: Check that we are in debug mode.
}
if (info.Flags.HasFlag(ProcessCreationFlags.OptimizeMemoryAllocation) &&
!info.Flags.HasFlag(ProcessCreationFlags.IsApplication))
{
@@ -139,7 +150,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return handleTable.GenerateHandle(process, out handle);
}
#pragma warning disable CA1822 // Mark member as static
public Result StartProcess(int handle, int priority, int cpuCore, ulong mainThreadStackSize)
{
KProcess process = KernelStatic.GetCurrentProcess().HandleTable.GetObject<KProcess>(handle);
@@ -172,17 +182,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0x5f)]
#pragma warning disable CA1822 // Mark member as static
public Result FlushProcessDataCache(int processHandle, ulong address, ulong size)
{
// FIXME: This needs to be implemented as ARMv7 doesn't have any way to do cache maintenance operations on EL0.
// As we don't support (and don't actually need) to flush the cache, this is stubbed.
return Result.Success;
}
#pragma warning restore CA1822
// IPC
@@ -256,7 +263,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0x22)]
#pragma warning disable CA1822 // Mark member as static
public Result SendSyncRequestWithUserBuffer(
[PointerSized] ulong messagePtr,
[PointerSized] ulong messageSize,
@@ -306,7 +312,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
#pragma warning restore CA1822
[Svc(0x23)]
public Result SendAsyncRequestWithUserBuffer(
@@ -896,7 +901,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(2)]
#pragma warning disable CA1822 // Mark member as static
public Result SetMemoryPermission([PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
{
if (!PageAligned(address))
@@ -928,10 +932,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.MemoryManager.SetMemoryPermission(address, size, permission);
}
#pragma warning restore CA1822
[Svc(3)]
#pragma warning disable CA1822 // Mark member as static
public Result SetMemoryAttribute(
[PointerSized] ulong address,
[PointerSized] ulong size,
@@ -979,10 +981,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
#pragma warning restore CA1822
[Svc(4)]
#pragma warning disable CA1822 // Mark member as static
public Result MapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
{
if (!PageAligned(src | dst))
@@ -1018,10 +1018,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.Map(dst, src, size);
}
#pragma warning restore CA1822
[Svc(5)]
#pragma warning disable CA1822 // Mark member as static
public Result UnmapMemory([PointerSized] ulong dst, [PointerSized] ulong src, [PointerSized] ulong size)
{
if (!PageAligned(src | dst))
@@ -1057,7 +1055,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.Unmap(dst, src, size);
}
#pragma warning restore CA1822
[Svc(6)]
public Result QueryMemory([PointerSized] ulong infoPtr, [PointerSized] out ulong pageInfo, [PointerSized] ulong address)
@@ -1074,7 +1071,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
#pragma warning disable CA1822 // Mark member as static
public Result QueryMemory(out MemoryInfo info, out ulong pageInfo, ulong address)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -1094,10 +1090,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0x13)]
#pragma warning disable CA1822 // Mark member as static
public Result MapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
{
if (!PageAligned(address))
@@ -1143,10 +1137,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
currentProcess,
permission);
}
#pragma warning restore CA1822
[Svc(0x14)]
#pragma warning disable CA1822 // Mark member as static
public Result UnmapSharedMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
@@ -1186,7 +1178,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
size,
currentProcess);
}
#pragma warning restore CA1822
[Svc(0x15)]
public Result CreateTransferMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
@@ -1253,7 +1244,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0x51)]
#pragma warning disable CA1822 // Mark member as static
public Result MapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size, KMemoryPermission permission)
{
if (!PageAligned(address))
@@ -1299,10 +1289,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
currentProcess,
permission);
}
#pragma warning restore CA1822
[Svc(0x52)]
#pragma warning disable CA1822 // Mark member as static
public Result UnmapTransferMemory(int handle, [PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
@@ -1342,10 +1330,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
size,
currentProcess);
}
#pragma warning restore CA1822
[Svc(0x2c)]
#pragma warning disable CA1822 // Mark member as static
public Result MapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
@@ -1380,10 +1366,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.MapPhysicalMemory(address, size);
}
#pragma warning restore CA1822
[Svc(0x2d)]
#pragma warning disable CA1822 // Mark member as static
public Result UnmapPhysicalMemory([PointerSized] ulong address, [PointerSized] ulong size)
{
if (!PageAligned(address))
@@ -1418,7 +1402,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return process.MemoryManager.UnmapPhysicalMemory(address, size);
}
#pragma warning restore CA1822
[Svc(0x4b)]
public Result CreateCodeMemory(out int handle, [PointerSized] ulong address, [PointerSized] ulong size)
@@ -1462,7 +1445,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0x4c)]
#pragma warning disable CA1822 // Mark member as static
public Result ControlCodeMemory(
int handle,
CodeMemoryOperation op,
@@ -1540,10 +1522,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidEnumValue;
}
}
#pragma warning restore CA1822
[Svc(0x73)]
#pragma warning disable CA1822 // Mark member as static
public Result SetProcessMemoryPermission(
int handle,
ulong src,
@@ -1584,10 +1564,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return targetProcess.MemoryManager.SetProcessMemoryPermission(src, size, permission);
}
#pragma warning restore CA1822
[Svc(0x74)]
#pragma warning disable CA1822 // Mark member as static
public Result MapProcessMemory(
[PointerSized] ulong dst,
int handle,
@@ -1643,10 +1621,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return dstProcess.MemoryManager.MapPages(dst, pageList, MemoryState.ProcessMemory, KMemoryPermission.ReadAndWrite);
}
#pragma warning restore CA1822
[Svc(0x75)]
#pragma warning disable CA1822 // Mark member as static
public Result UnmapProcessMemory(
[PointerSized] ulong dst,
int handle,
@@ -1691,10 +1667,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0x77)]
#pragma warning disable CA1822 // Mark member as static
public Result MapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
@@ -1731,10 +1705,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return targetProcess.MemoryManager.MapProcessCodeMemory(dst, src, size);
}
#pragma warning restore CA1822
[Svc(0x78)]
#pragma warning disable CA1822 // Mark member as static
public Result UnmapProcessCodeMemory(int handle, ulong dst, ulong src, ulong size)
{
if (!PageAligned(dst) || !PageAligned(src))
@@ -1771,7 +1743,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return targetProcess.MemoryManager.UnmapProcessCodeMemory(dst, src, size);
}
#pragma warning restore CA1822
private static bool PageAligned(ulong address)
{
@@ -1781,7 +1752,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
// System
[Svc(0x7b)]
#pragma warning disable CA1822 // Mark member as static
public Result TerminateProcess(int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -1810,15 +1780,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
#pragma warning restore CA1822
[Svc(7)]
#pragma warning disable CA1822 // Mark member as static
public void ExitProcess()
{
KernelStatic.GetCurrentProcess().TerminateCurrentProcess();
}
#pragma warning restore CA1822
[Svc(0x11)]
public Result SignalEvent(int handle)
@@ -1911,7 +1878,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0x26)]
#pragma warning disable CA1822 // Mark member as static
public void Break(ulong reason)
{
KThread currentThread = KernelStatic.GetCurrentThread();
@@ -1937,10 +1903,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
Logger.Debug?.Print(LogClass.KernelSvc, "Debugger triggered.");
}
}
#pragma warning restore CA1822
[Svc(0x27)]
#pragma warning disable CA1822 // Mark member as static
public void OutputDebugString([PointerSized] ulong strPtr, [PointerSized] ulong size)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -1949,7 +1913,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
Logger.Warning?.Print(LogClass.KernelSvc, str);
}
#pragma warning restore CA1822
[Svc(0x29)]
public Result GetInfo(out ulong value, InfoType id, int handle, long subId)
@@ -1978,6 +1941,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
case InfoType.UsedNonSystemMemorySize:
case InfoType.IsApplication:
case InfoType.FreeThreadCount:
case InfoType.AliasRegionExtraSize:
{
if (subId != 0)
{
@@ -2006,22 +1970,19 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
value = process.MemoryManager.AliasRegionStart;
break;
case InfoType.AliasRegionSize:
value = (process.MemoryManager.AliasRegionEnd -
process.MemoryManager.AliasRegionStart);
value = process.MemoryManager.AliasRegionEnd - process.MemoryManager.AliasRegionStart;
break;
case InfoType.HeapRegionAddress:
value = process.MemoryManager.HeapRegionStart;
break;
case InfoType.HeapRegionSize:
value = (process.MemoryManager.HeapRegionEnd -
process.MemoryManager.HeapRegionStart);
value = process.MemoryManager.HeapRegionEnd - process.MemoryManager.HeapRegionStart;
break;
case InfoType.TotalMemorySize:
value = process.GetMemoryCapacity();
break;
case InfoType.UsedMemorySize:
value = process.GetMemoryUsage();
break;
@@ -2029,7 +1990,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
case InfoType.AslrRegionAddress:
value = process.MemoryManager.GetAddrSpaceBaseAddr();
break;
case InfoType.AslrRegionSize:
value = process.MemoryManager.GetAddrSpaceSize();
break;
@@ -2038,20 +1998,17 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
value = process.MemoryManager.StackRegionStart;
break;
case InfoType.StackRegionSize:
value = (process.MemoryManager.StackRegionEnd -
process.MemoryManager.StackRegionStart);
value = process.MemoryManager.StackRegionEnd - process.MemoryManager.StackRegionStart;
break;
case InfoType.SystemResourceSizeTotal:
value = process.PersonalMmHeapPagesCount * KPageTableBase.PageSize;
break;
case InfoType.SystemResourceSizeUsed:
if (process.PersonalMmHeapPagesCount != 0)
{
value = process.MemoryManager.GetMmUsedPages() * KPageTableBase.PageSize;
}
break;
case InfoType.ProgramId:
@@ -2065,7 +2022,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
case InfoType.TotalNonSystemMemorySize:
value = process.GetMemoryCapacityWithoutPersonalMmHeap();
break;
case InfoType.UsedNonSystemMemorySize:
value = process.GetMemoryUsageWithoutPersonalMmHeap();
break;
@@ -2084,10 +2040,12 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
{
value = 0;
}
break;
case InfoType.AliasRegionExtraSize:
value = process.MemoryManager.AliasRegionExtraSize;
break;
}
break;
}
@@ -2104,7 +2062,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
value = KernelStatic.GetCurrentProcess().Debug ? 1UL : 0UL;
break;
}
@@ -2136,7 +2093,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
value = (uint)resLimHandle;
}
break;
}
@@ -2155,7 +2111,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
value = (ulong)KTimeManager.ConvertHostTicksToTicks(_context.Schedulers[currentCore].TotalIdleTimeTicks);
break;
}
@@ -2174,7 +2129,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KProcess currentProcess = KernelStatic.GetCurrentProcess();
value = currentProcess.RandomEntropy[subId];
break;
}
@@ -2220,7 +2174,22 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
value = (ulong)KTimeManager.ConvertHostTicksToTicks(totalTimeRunning);
}
break;
}
case InfoType.IsSvcPermitted:
{
if (handle != 0)
{
return KernelResult.InvalidHandle;
}
if (subId != 0x36)
{
return KernelResult.InvalidCombination;
}
value = KernelStatic.GetCurrentProcess().IsSvcPermitted((int)subId) ? 1UL : 0UL;
break;
}
@@ -2231,7 +2200,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
if ((ulong)subId != 0)
if (subId != 0)
{
return KernelResult.InvalidCombination;
}
@@ -2246,8 +2215,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
value = (ulong)outHandle;
value = (uint)outHandle;
break;
}
@@ -2398,7 +2366,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0x30)]
#pragma warning disable CA1822 // Mark member as static
public Result GetResourceLimitLimitValue(out long limitValue, int handle, LimitableResource resource)
{
limitValue = 0;
@@ -2419,10 +2386,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0x31)]
#pragma warning disable CA1822 // Mark member as static
public Result GetResourceLimitCurrentValue(out long limitValue, int handle, LimitableResource resource)
{
limitValue = 0;
@@ -2443,10 +2408,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0x37)]
#pragma warning disable CA1822 // Mark member as static
public Result GetResourceLimitPeakValue(out long peak, int handle, LimitableResource resource)
{
peak = 0;
@@ -2467,7 +2430,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0x7d)]
public Result CreateResourceLimit(out int handle)
@@ -2480,7 +2442,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0x7e)]
#pragma warning disable CA1822 // Mark member as static
public Result SetResourceLimitLimitValue(int handle, LimitableResource resource, long limitValue)
{
if (resource >= LimitableResource.Count)
@@ -2497,7 +2458,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return resourceLimit.SetLimitValue(resource, limitValue);
}
#pragma warning restore CA1822
// Thread
@@ -2577,7 +2537,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(9)]
#pragma warning disable CA1822 // Mark member as static
public Result StartThread(int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -2604,17 +2563,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
}
#pragma warning restore CA1822
[Svc(0xa)]
#pragma warning disable CA1822 // Mark member as static
public void ExitThread()
{
KThread currentThread = KernelStatic.GetCurrentThread();
currentThread.Exit();
}
#pragma warning restore CA1822
[Svc(0xb)]
public void SleepThread(long timeout)
@@ -2641,7 +2597,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0xc)]
#pragma warning disable CA1822 // Mark member as static
public Result GetThreadPriority(out int priority, int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -2661,10 +2616,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
}
#pragma warning restore CA1822
[Svc(0xd)]
#pragma warning disable CA1822 // Mark member as static
public Result SetThreadPriority(int handle, int priority)
{
// TODO: NPDM check.
@@ -2682,10 +2635,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0xe)]
#pragma warning disable CA1822 // Mark member as static
public Result GetThreadCoreMask(out int preferredCore, out ulong affinityMask, int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -2707,10 +2658,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
}
#pragma warning restore CA1822
[Svc(0xf)]
#pragma warning disable CA1822 // Mark member as static
public Result SetThreadCoreMask(int handle, int preferredCore, ulong affinityMask)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@@ -2758,18 +2707,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return thread.SetCoreAndAffinityMask(preferredCore, affinityMask);
}
#pragma warning restore CA1822
[Svc(0x10)]
#pragma warning disable CA1822 // Mark member as static
public int GetCurrentProcessorNumber()
{
return KernelStatic.GetCurrentThread().CurrentCore;
}
#pragma warning restore CA1822
[Svc(0x25)]
#pragma warning disable CA1822 // Mark member as static
public Result GetThreadId(out ulong threadUid, int handle)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -2789,10 +2734,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return KernelResult.InvalidHandle;
}
}
#pragma warning restore CA1822
[Svc(0x32)]
#pragma warning disable CA1822 // Mark member as static
public Result SetThreadActivity(int handle, bool pause)
{
KProcess process = KernelStatic.GetCurrentProcess();
@@ -2816,10 +2759,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return thread.SetActivity(pause);
}
#pragma warning restore CA1822
[Svc(0x33)]
#pragma warning disable CA1822 // Mark member as static
public Result GetThreadContext3([PointerSized] ulong address, int handle)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@@ -2853,7 +2794,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return result;
}
#pragma warning restore CA1822
// Thread synchronization
@@ -2986,7 +2926,6 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
[Svc(0x1a)]
#pragma warning disable CA1822 // Mark member as static
public Result ArbitrateLock(int ownerHandle, [PointerSized] ulong mutexAddress, int requesterHandle)
{
if (IsPointingInsideKernel(mutexAddress))
@@ -3003,10 +2942,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.AddressArbiter.ArbitrateLock(ownerHandle, mutexAddress, requesterHandle);
}
#pragma warning restore CA1822
[Svc(0x1b)]
#pragma warning disable CA1822 // Mark member as static
public Result ArbitrateUnlock([PointerSized] ulong mutexAddress)
{
if (IsPointingInsideKernel(mutexAddress))
@@ -3023,10 +2960,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return currentProcess.AddressArbiter.ArbitrateUnlock(mutexAddress);
}
#pragma warning restore CA1822
[Svc(0x1c)]
#pragma warning disable CA1822 // Mark member as static
public Result WaitProcessWideKeyAtomic(
[PointerSized] ulong mutexAddress,
[PointerSized] ulong condVarAddress,
@@ -3056,10 +2991,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
handle,
timeout);
}
#pragma warning restore CA1822
[Svc(0x1d)]
#pragma warning disable CA1822 // Mark member as static
public Result SignalProcessWideKey([PointerSized] ulong address, int count)
{
KProcess currentProcess = KernelStatic.GetCurrentProcess();
@@ -3068,10 +3001,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
return Result.Success;
}
#pragma warning restore CA1822
[Svc(0x34)]
#pragma warning disable CA1822 // Mark member as static
public Result WaitForAddress([PointerSized] ulong address, ArbitrationType type, int value, long timeout)
{
if (IsPointingInsideKernel(address))
@@ -3102,10 +3033,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
_ => KernelResult.InvalidEnumValue,
};
}
#pragma warning restore CA1822
[Svc(0x35)]
#pragma warning disable CA1822 // Mark member as static
public Result SignalToAddress([PointerSized] ulong address, SignalType type, int value, int count)
{
if (IsPointingInsideKernel(address))
@@ -3131,17 +3060,14 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
_ => KernelResult.InvalidEnumValue,
};
}
#pragma warning restore CA1822
[Svc(0x36)]
#pragma warning disable CA1822 // Mark member as static
public Result SynchronizePreemptionState()
{
KernelStatic.GetCurrentThread().SynchronizePreemptionState();
return Result.Success;
}
#pragma warning restore CA1822
// Not actual syscalls, used by HLE services and such.

View File

@@ -15,6 +15,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
public ServiceAccessControl ServiceAccessControl { get; private set; }
public KernelAccessControl KernelAccessControl { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACI0 data.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
public Aci0(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -19,6 +19,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
public ServiceAccessControl ServiceAccessControl { get; private set; }
public KernelAccessControl KernelAccessControl { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid ACID data.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public Acid(Stream stream, int offset)
{
stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -11,6 +11,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
public int Unknown3 { get; private set; }
public int Unknown4 { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public FsAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -9,6 +9,12 @@ namespace Ryujinx.HLE.Loaders.Npdm
public int Version { get; private set; }
public ulong PermissionsBitmask { get; private set; }
/// <exception cref="InvalidNpdmException">The stream contains invalid data.</exception>
/// <exception cref="NotImplementedException">The ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public FsAccessHeader(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Loaders.Npdm
{
public int[] Capabilities { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public KernelAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -24,6 +24,13 @@ namespace Ryujinx.HLE.Loaders.Npdm
public Aci0 Aci0 { get; private set; }
public Acid Acid { get; private set; }
/// <exception cref="InvalidNpdmException">The stream doesn't contain valid NPDM data.</exception>
/// <exception cref="System.NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public Npdm(Stream stream)
{
BinaryReader reader = new(stream);

View File

@@ -9,6 +9,11 @@ namespace Ryujinx.HLE.Loaders.Npdm
{
public IReadOnlyDictionary<string, bool> Services { get; private set; }
/// <exception cref="System.ArgumentException">The stream does not support reading, is <see langword="null"/>, or is already closed.</exception>
/// <exception cref="System.ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="System.ObjectDisposedException">The stream is closed.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
public ServiceAccessControl(Stream stream, int offset, int size)
{
stream.Seek(offset, SeekOrigin.Begin);

View File

@@ -139,7 +139,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
ulong titleIdBase = mainNca.GetProgramIdBase();
// Load update information if exists.
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "updates.json");
string titleUpdateMetadataPath = Path.Combine(AppDataManager.GamesDirPath, titleIdBase.ToString("x16"), "updates.json");
if (File.Exists(titleUpdateMetadataPath))
{
updatePath = JsonHelper.DeserializeFromFile(titleUpdateMetadataPath, _applicationSerializerContext.TitleUpdateMetadata).Selected;

View File

@@ -118,7 +118,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
device.Configuration.ContentManager.ClearAocData();
// Load DownloadableContents.
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.Header.TitleId.ToString("x16"), "dlc.json");
string addOnContentMetadataPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, mainNca.GetProgramIdBase().ToString("x16"), "dlc.json");
if (File.Exists(addOnContentMetadataPath))
{
List<DownloadableContentContainer> dlcContainerList = JsonHelper.DeserializeFromFile(addOnContentMetadataPath, _contentSerializerContext.ListDownloadableContentContainer);

View File

@@ -26,7 +26,11 @@ namespace Ryujinx.Horizon.Ngc.Ipc
}
[CmifCommand(1)]
public Result Check(out uint checkMask, ReadOnlySpan<byte> text, uint regionMask, ProfanityFilterOption option)
public Result Check(
out uint checkMask,
[Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> text,
uint regionMask,
ProfanityFilterOption option)
{
lock (_profanityFilter)
{

View File

@@ -21,6 +21,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
public long CurrentTime { get; private set; }
public IEnumerable<MultiWaitHolderBase> MultiWaits => _multiWaits;
public MultiWaitImpl()
{
_multiWaits = new List<MultiWaitHolderBase>();

View File

@@ -1,4 +1,5 @@
using Ryujinx.Horizon.Sdk.OsTypes.Impl;
using System.Collections.Generic;
namespace Ryujinx.Horizon.Sdk.OsTypes
{
@@ -6,6 +7,8 @@ namespace Ryujinx.Horizon.Sdk.OsTypes
{
private readonly MultiWaitImpl _impl;
public IEnumerable<MultiWaitHolderBase> MultiWaits => _impl.MultiWaits;
public MultiWait()
{
_impl = new MultiWaitImpl();

View File

@@ -3,6 +3,7 @@ using Ryujinx.Horizon.Sdk.OsTypes;
using Ryujinx.Horizon.Sdk.Sf.Cmif;
using Ryujinx.Horizon.Sdk.Sm;
using System;
using System.Linq;
namespace Ryujinx.Horizon.Sdk.Sf.Hipc
{
@@ -116,6 +117,18 @@ namespace Ryujinx.Horizon.Sdk.Sf.Hipc
while (WaitAndProcessRequestsImpl())
{
}
// Unlink pending sessions, dispose expects them to be already unlinked.
ServerSession[] serverSessions = Enumerable.OfType<ServerSession>(_multiWait.MultiWaits).ToArray();
foreach (ServerSession serverSession in serverSessions)
{
if (serverSession.IsLinked)
{
serverSession.UnlinkFromMultiWaitHolder();
}
}
}
public void WaitAndProcessRequests()

View File

@@ -42,6 +42,8 @@ namespace Ryujinx.UI.App.Common
[JsonIgnore] public ulong IdBase => Id & ~0x1FFFUL;
[JsonIgnore] public string IdBaseString => IdBase.ToString("x16");
public static string GetBuildId(VirtualFileSystem virtualFileSystem, IntegrityCheckLevel checkLevel, string titleFilePath)
{
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);

View File

@@ -72,17 +72,21 @@ namespace Ryujinx.UI.App.Common
return resourceByteArray;
}
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
private ApplicationData GetApplicationFromExeFs(PartitionFileSystem pfs, string filePath)
{
ApplicationData data = new()
{
Icon = _nspIcon,
Path = filePath,
};
using UniqueRef<IFile> npdmFile = new();
try
{
Result result = pfs.OpenFile(ref npdmFile.Ref, "/main.npdm".ToU8Span(), OpenMode.Read);
if (ResultFs.PathNotFound.Includes(result))
@@ -95,14 +99,16 @@ namespace Ryujinx.UI.App.Common
return data;
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception.Message}");
return null;
}
}
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
/// <exception cref="Ryujinx.HLE.Exceptions.InvalidNpdmException">The npdm file doesn't contain valid data.</exception>
/// <exception cref="NotImplementedException">The FsAccessHeader.ContentOwnerId section is not implemented.</exception>
/// <exception cref="ArgumentException">An error occured while reading bytes from the stream.</exception>
/// <exception cref="EndOfStreamException">The end of the stream is reached.</exception>
/// <exception cref="IOException">An I/O error occurred.</exception>
private ApplicationData GetApplicationFromNsp(PartitionFileSystem pfs, string filePath)
{
bool isExeFs = false;
@@ -170,13 +176,15 @@ namespace Ryujinx.UI.App.Common
return null;
}
/// <exception cref="MissingKeyException">The configured key set is missing a key.</exception>
/// <exception cref="InvalidDataException">The NCA header could not be decrypted.</exception>
/// <exception cref="NotSupportedException">The NCA version is not supported.</exception>
/// <exception cref="HorizonResultException">An error occured while reading PFS data.</exception>
private List<ApplicationData> GetApplicationsFromPfs(IFileSystem pfs, string filePath)
{
var applications = new List<ApplicationData>();
string extension = Path.GetExtension(filePath).ToLower();
try
{
foreach ((ulong titleId, ContentMetaData content) in pfs.GetContentData(ContentMetaType.Application, _virtualFileSystem, _checkLevel))
{
ApplicationData applicationData = new()
@@ -251,19 +259,6 @@ namespace Ryujinx.UI.App.Common
applications.Add(applicationData);
}
}
catch (MissingKeyException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
}
catch (InvalidDataException)
{
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {filePath}");
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{filePath}' Error: {exception}");
}
return applications;
}
@@ -319,8 +314,6 @@ namespace Ryujinx.UI.App.Common
BinaryReader reader = new(file);
ApplicationData application = new();
try
{
file.Seek(24, SeekOrigin.Begin);
int assetOffset = reader.ReadInt32();
@@ -358,13 +351,6 @@ namespace Ryujinx.UI.App.Common
application.ControlHolder = controlHolder;
applications.Add(application);
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
return false;
}
break;
@@ -376,8 +362,6 @@ namespace Ryujinx.UI.App.Common
}
}
case ".nca":
{
try
{
ApplicationData application = new();
@@ -393,17 +377,6 @@ namespace Ryujinx.UI.App.Common
application.ControlHolder = controlHolder;
applications.Add(application);
}
catch (InvalidDataException)
{
Logger.Warning?.Print(LogClass.Application, $"The NCA header content type check has failed. This is usually because the header key is incorrect or missing. Errored File: {applicationPath}");
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. Errored File: {applicationPath}");
return false;
}
break;
}
@@ -417,16 +390,35 @@ namespace Ryujinx.UI.App.Common
};
applications.Add(application);
break;
}
}
}
catch (MissingKeyException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Your key set is missing a key with the name: {exception.Name}");
return false;
}
catch (InvalidDataException)
{
Logger.Warning?.Print(LogClass.Application, $"The header key is incorrect or missing and therefore the NCA header content type check has failed. Errored File: {applicationPath}");
return false;
}
catch (IOException exception)
{
Logger.Warning?.Print(LogClass.Application, exception.Message);
return false;
}
catch (Exception exception)
{
Logger.Warning?.Print(LogClass.Application, $"The file encountered was not of a valid type. File: '{applicationPath}' Error: {exception}");
return false;
}
foreach (var data in applications)
{

View File

@@ -367,19 +367,14 @@ namespace Ryujinx.Ava
}
var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
using var bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
using SKBitmap bitmap = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
Marshal.Copy(e.Data, 0, bitmap.GetPixels(), e.Data.Length);
SKBitmap bitmapToSave = null;
using SKBitmap bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
using SKCanvas canvas = new SKCanvas(bitmapToSave);
if (e.FlipX || e.FlipY)
{
bitmapToSave = new SKBitmap(bitmap.Width, bitmap.Height);
using var canvas = new SKCanvas(bitmapToSave);
canvas.Clear(SKColors.Transparent);
canvas.Clear(SKColors.Black);
float scaleX = e.FlipX ? -1 : 1;
float scaleY = e.FlipY ? -1 : 1;
@@ -387,12 +382,9 @@ namespace Ryujinx.Ava
var matrix = SKMatrix.CreateScale(scaleX, scaleY, bitmap.Width / 2f, bitmap.Height / 2f);
canvas.SetMatrix(matrix);
canvas.DrawBitmap(bitmap, SKPoint.Empty);
canvas.DrawBitmap(bitmap, new SKPoint(e.FlipX ? -bitmap.Width : 0, e.FlipY ? -bitmap.Height : 0));
}
SaveBitmapAsPng(bitmapToSave ?? bitmap, path);
bitmapToSave?.Dispose();
SaveBitmapAsPng(bitmapToSave, path);
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot");
}

View File

@@ -1,21 +1,20 @@
using LibHac.Ns;
using Ryujinx.Ava.Common.Locale;
namespace Ryujinx.Ava.UI.Models
{
public class TitleUpdateModel
{
public ApplicationControlProperty Control { get; }
public uint Version { get; }
public string Path { get; }
public string Label { get; }
public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(
System.IO.Path.GetExtension(Path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
Control.DisplayVersionString.ToString()
);
public TitleUpdateModel(ApplicationControlProperty control, string path)
public TitleUpdateModel(uint version, string displayVersion, string path)
{
Control = control;
Version = version;
Label = LocaleManager.Instance.UpdateAndGetDynamicValue(
System.IO.Path.GetExtension(path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
displayVersion
);
Path = path;
}
}

View File

@@ -103,7 +103,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_storageProvider = desktop.MainWindow.StorageProvider;
}
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdString, "dlc.json");
_downloadableContentJsonPath = Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "dlc.json");
if (!File.Exists(_downloadableContentJsonPath))
{
@@ -263,7 +263,7 @@ namespace Ryujinx.Ava.UI.ViewModels
var content = new DownloadableContentModel(nca.Header.TitleId.ToString("X16"), path, fileEntry.FullPath, true);
DownloadableContents.Add(content);
SelectedDownloadableContents.Add(content);
Dispatcher.UIThread.InvokeAsync(() => SelectedDownloadableContents.Add(content));
success = true;
}

View File

@@ -88,7 +88,7 @@ namespace Ryujinx.Ava.UI.ViewModels
StorageProvider = desktop.MainWindow.StorageProvider;
}
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdString, "updates.json");
TitleUpdateJsonPath = Path.Combine(AppDataManager.GamesDirPath, ApplicationData.IdBaseString, "updates.json");
try
{
@@ -96,7 +96,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
catch
{
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdString} at {TitleUpdateJsonPath}");
Logger.Warning?.Print(LogClass.Application, $"Failed to deserialize title update data for {ApplicationData.IdBaseString} at {TitleUpdateJsonPath}");
TitleUpdateWindowData = new TitleUpdateMetadata
{
@@ -131,26 +131,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public void SortUpdates()
{
var list = TitleUpdates.ToList();
list.Sort((first, second) =>
{
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
{
return -1;
}
if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
{
return 1;
}
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
});
var sortedUpdates = TitleUpdates.OrderByDescending(update => update.Version);
Views.Clear();
Views.Add(new BaseModel());
Views.AddRange(list);
Views.AddRange(sortedUpdates);
if (SelectedUpdate == null)
{
@@ -169,7 +154,7 @@ namespace Ryujinx.Ava.UI.ViewModels
}
}
private void AddUpdate(string path, bool ignoreNotFound = false)
private void AddUpdate(string path, bool ignoreNotFound = false, bool selected = false)
{
if (!File.Exists(path) || TitleUpdates.Any(x => x.Path == path))
{
@@ -204,7 +189,15 @@ namespace Ryujinx.Ava.UI.ViewModels
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
TitleUpdates.Add(new TitleUpdateModel(controlData, path));
var displayVersion = controlData.DisplayVersionString.ToString();
var update = new TitleUpdateModel(content.Version.Version, displayVersion, path);
TitleUpdates.Add(update);
if (selected)
{
Dispatcher.UIThread.InvokeAsync(() => SelectedUpdate = update);
}
}
else
{
@@ -245,7 +238,7 @@ namespace Ryujinx.Ava.UI.ViewModels
foreach (var file in result)
{
AddUpdate(file.Path.LocalPath);
AddUpdate(file.Path.LocalPath, selected: true);
}
SortUpdates();

View File

@@ -38,7 +38,7 @@ namespace Ryujinx.Ava.UI.Windows
SecondaryButtonText = "",
CloseButtonText = "",
Content = new DownloadableContentManagerWindow(virtualFileSystem, applicationData),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdString),
Title = string.Format(LocaleManager.Instance[LocaleKeys.DlcWindowTitle], applicationData.Name, applicationData.IdBaseString),
};
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());

View File

@@ -40,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows
SecondaryButtonText = "",
CloseButtonText = "",
Content = new TitleUpdateWindow(virtualFileSystem, applicationData),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdString),
Title = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.GameUpdateWindowHeading, applicationData.Name, applicationData.IdBaseString),
};
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());