Compare commits

..

24 Commits

Author SHA1 Message Date
04bd87ed5a Fix shader textureSize with multisample and buffer textures (#3240)
* Fix shader textureSize with multisample and buffer textures

* Replace out param with tuple return value
2022-04-04 14:43:58 -03:00
5158cdb308 infra: Put SDL2 headless release inside a GUI-less block in PR (#3218)
As title say.
2022-03-26 11:38:35 +01:00
1402d8391d Support NVDEC H264 interlaced video decoding and VIC deinterlacing (#3225)
* Support NVDEC H264 interlaced video decoding and VIC deinterlacing

* Remove unused code
2022-03-23 17:09:32 -03:00
e3b36db71c hle: Some cleanup (#3210)
* hle: Some cleanup

This PR cleaned up a bit the HLE folder and the VirtualFileSystem one, since we use LibHac, we can use some class of it directly instead of duplicate things. The "Content" of VFS folder is removed since it should be handled in the NCM service directly.
A larger cleanup should be done later since there is still be duplicated code here and there.

* Fix Headless.SDL2

* Addresses gdkchan feedback
2022-03-22 20:46:16 +01:00
ba0171d054 Memory.Tests: Make Multithreading test explicit (#3220) 2022-03-21 09:21:05 +01:00
d1146a5af2 Don't restore Viewport 0 if it hasn't been set yet. (#3219)
Fixes a driver crash when starting some games caused by #3217
2022-03-20 14:48:43 -03:00
79408b68c3 De-tile GOB when DMA copying from block linear to pitch kind memory regions (#3207)
* De-tile GOB when DMA copying from block linear to pitch kind memory regions

* XML docs + nits

* Remove using

* No flush for regular buffer copies

* Add back ulong casts, fix regression due to oversight
2022-03-20 13:55:07 -03:00
d461d4f68b Fix OpenGL issues with RTSS overlays and OBS Game Capture (#3217)
OpenGL game overlays and hooks tend to make a lot of assumptions about how games present frames to the screen, since presentation in OpenGL kind of sucks and they would like to have info such as the size of the screen, or if the contents are SRGB rather than linear.

There are two ways of getting this. OBS hooks swap buffers to get a frame for video capture, but it actually checks the bound framebuffer at the time. I made sure that this matches the output framebuffer (the window) so that the output matches the size. RTSS checks the viewport size by default, but this was actually set to the last used viewport by the game, causing the OSD to fly all across the screen depending on how it was used (or res scale). The viewport is now manually set to match the output framebuffer size.

In the case of RTSS, it also loads its resources by destructively setting a pixel pack parameter without regard to what it was set to by the guest application. OpenGL state can be set for a long period of time and is not expected to be set before each call to a method, so randomly changing it isn't great practice. To fix this, I've added a line to set the pixel unpack alignment back to 4 after presentation, which should cover RTSS loading its incredibly ugly font.

- RTSS and overlays that use it should no longer cause certain textures to load incorrectly. (mario kart 8, pokemon legends arceus)
- OBS Game Capture should no longer crop the game output incorrectly, flicker randomly, or capture with incorrect gamma.

This doesn't fix issues with how RTSS reports our frame timings.
2022-03-20 13:37:45 -03:00
b45d30acf8 oslc: Fix condition in GetSaveDataBackupSetting (#3208)
* oslc: Fix condition in GetSaveDataBackupSetting

This PR fixes a condition previously implemented in #3190 where ACNH can't be booted without an existing savedata.
Closes #3206

* Addresses gdkchan feedback
2022-03-20 13:25:29 -03:00
df70442c46 InstEmitMemoryEx: Barrier after write on ordered store (#3193)
* InstEmitMemoryEx: Barrier after write on ordered store

* increment ptc version

* 32
2022-03-19 10:32:35 -03:00
e2ffa5a125 ntc: Implement IEnsureNetworkClockAvailabilityService (#3192)
* ntc: Implement IEnsureNetworkClockAvailabilityService

This PR implement a basic `IEnsureNetworkClockAvailabilityService` checked by RE. It's needed by Splatoon 2 with Guest Internet Access enabled. Game is now playable with this setting.

* Update Ryujinx.HLE/HOS/Services/Nim/Ntc/StaticService/IEnsureNetworkClockAvailabilityService.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-03-15 04:07:07 +01:00
73feac5819 Caching local network info and using an event handler to invalidate as needed (improves menu slow down issue in FE3H) (#2761)
* Update IGeneralService.cs

Fix IPV4 local ip related frame drop in fire emblem by rewriting [CommandHipc(12)]

* Fix IPV4 Local IP Slowdown & Style Fixes

fix a missing space

* Remove unnecessary line

* Fix for hardcoding which index to use

* Replace argument with empty string.

By sending an empty string to Dns.GetHostAddresses("") you get back localhost info only.

* Add caching, undo change in GetCurrentIpAddress

Implement caching and revert the GetCurrentIP() function, speed improvements still present.

* Remove unnecessary using

* Syntax fixes and removing extra lines

Requested changes by AcK77

* Properly unsubscribe from event handler

Adds an unsubscribe in the dispose section of IGeneralService
2022-03-15 03:49:35 +01:00
e5ad1dfa48 Implement S8D24 texture format and tweak depth range detection (#2458) 2022-03-15 03:42:08 +01:00
79becc4b78 Dynamically increase buffer size when resizing (#2861)
* Grow buffers by 1.5x of its size when resizing

* Further restrict the cases where the dynamic expansion is done
2022-03-15 03:33:53 +01:00
223172ac0b Ui: Add option to show/hide console window (Windows-only) (#3170)
* Ui: Add option to show/hide console window (Windows-only)

* Ui: Only display Show Console menu item on Windows

* ConsoleHelper: Handle NULL case

This will never happen

* Address nits

* Address comments

* Address comments 2
2022-03-15 02:35:41 +01:00
8c9633d72f Initialize indexed inputs used on next shader stage (#3198) 2022-03-14 20:02:50 -03:00
1f93fd52d9 Do not initialize geometry shader passthrough attributes (#3196) 2022-03-14 19:35:41 -03:00
aac7bbd378 olsc: Implement GetSaveDataBackupSetting (#3190)
* olsc: Implement GetSaveDataBackupSetting

This PR implement GetSaveDataBackupSetting of OLSC service which is now needed by ACNH 2.0.5. The game is playable as usual if you use the same user profile as the original save file (I don't know if it was the case before), everything is checked by RE.

* addresses gdkchan feedback
2022-03-12 18:38:49 +01:00
bed516bfda Implement rotate stick 90 degrees clockwise (#3084)
* Implement swapping sticks

* Rotate 90 degrees clockwise

Co-authored-by: matesic.darko@gmail.com <Darkman1979>
2022-03-12 18:23:48 +01:00
69b05f9918 Fix GetUserDisableCount NRE (#3187) 2022-03-12 18:12:12 +01:00
fb7c80e928 Limit number of events that can be retrieved from GetDisplayVSyncEvent (#3188)
* Limit number of events that can be retrieved from GetDisplayVSyncEvent

* Cleaning

* Rename openDisplayInfos -> openDisplays
2022-03-12 17:56:19 +01:00
bb2f9df0a1 KThread: Fix GetPsr mask (#3180)
* ExecutionContext: GetPstate / SetPstate

* Put it in NativeContext

* KThread: Fix GetPsr mask

* ExecutionContext: Turn methods into Pstate property

* Address nit
2022-03-11 03:16:32 +01:00
54bfaa125d amadeus: Fix wrong Span usage in CopyHistories (#3181)
Fix a copypasta from the original Amadeus PR causing invalid
CopyHistories output.

Also added a missing size check.

This fix a crash in Mononoke Slashdown
2022-03-07 09:49:29 +01:00
7af9fcbc06 T32: Implement Data Processing (Modified Immediate) instructions (#3178)
* T32: Implement Data Processing (Modified Immediate) instructions

* Update tests

* switch -> lookup table
2022-03-06 22:25:01 +01:00
113 changed files with 2560 additions and 608 deletions

View File

@ -36,15 +36,20 @@ jobs:
return core.error(`No artifacts found`);
}
let body = `Download the artifacts for this pull request:\n`;
let hidden_headless_artifacts = `\n\n <details><summary>GUI-less (SDL2)</summary>\n`;
let hidden_debug_artifacts = `\n\n <details><summary>Only for Developers</summary>\n`;
for (const art of artifacts) {
if(art.name.includes('Debug')){
if(art.name.includes('Debug')) {
hidden_debug_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}else{
} else if(art.name.includes('headless-sdl2')) {
hidden_headless_artifacts += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
} else {
body += `\n* [${art.name}](https://nightly.link/${owner}/${repo}/actions/artifacts/${art.id}.zip)`;
}
}
hidden_headless_artifacts += `\n</details>`;
hidden_debug_artifacts += `\n</details>`;
body += hidden_headless_artifacts;
body += hidden_debug_artifacts;
const {data: comments} = await github.issues.listComments({repo, owner, issue_number});

View File

@ -0,0 +1,38 @@
using ARMeilleure.Common;
using System.Runtime.Intrinsics;
namespace ARMeilleure.Decoders
{
class OpCodeT32AluImm : OpCodeT32Alu, IOpCode32AluImm
{
public int Immediate { get; }
public bool IsRotated { get; }
public new static OpCode Create(InstDescriptor inst, ulong address, int opCode) => new OpCodeT32AluImm(inst, address, opCode);
private static readonly Vector128<int> _factor = Vector128.Create(1, 0x00010001, 0x01000100, 0x01010101);
public OpCodeT32AluImm(InstDescriptor inst, ulong address, int opCode) : base(inst, address, opCode)
{
int imm8 = (opCode >> 0) & 0xff;
int imm3 = (opCode >> 12) & 7;
int imm1 = (opCode >> 26) & 1;
int imm12 = imm8 | (imm3 << 8) | (imm1 << 11);
if ((imm12 >> 10) == 0)
{
Immediate = imm8 * _factor.GetElement((imm12 >> 8) & 3);
IsRotated = false;
}
else
{
int shift = imm12 >> 7;
Immediate = BitUtils.RotateRight(0x80 | (imm12 & 0x7f), shift, 32);
IsRotated = shift != 0;
}
}
}
}

View File

@ -1048,25 +1048,41 @@ namespace ARMeilleure.Decoders
#region "OpCode Table (AArch32, T32)"
// Base
SetT32("11101011010xxxxx0xxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT32AluRsImm.Create);
SetT32("11110x01010xxxxx0xxxxxxxxxxxxxxx", InstName.Adc, InstEmit32.Adc, OpCodeT32AluImm.Create);
SetT32("11101011000<xxxx0xxx<<<<xxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT32AluRsImm.Create);
SetT32("11110x01000<xxxx0xxx<<<<xxxxxxxx", InstName.Add, InstEmit32.Add, OpCodeT32AluImm.Create);
SetT32("11101010000<xxxx0xxx<<<<xxxxxxxx", InstName.And, InstEmit32.And, OpCodeT32AluRsImm.Create);
SetT32("11110x00000<xxxx0xxx<<<<xxxxxxxx", InstName.And, InstEmit32.And, OpCodeT32AluImm.Create);
SetT32("11110x<<<xxxxxxx10x0xxxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT32BImm20.Create);
SetT32("11110xxxxxxxxxxx10x1xxxxxxxxxxxx", InstName.B, InstEmit32.B, OpCodeT32BImm24.Create);
SetT32("11101010001xxxxx0xxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT32AluRsImm.Create);
SetT32("11110x00001xxxxx0xxxxxxxxxxxxxxx", InstName.Bic, InstEmit32.Bic, OpCodeT32AluImm.Create);
SetT32("11110xxxxxxxxxxx11x1xxxxxxxxxxxx", InstName.Bl, InstEmit32.Bl, OpCodeT32BImm24.Create);
SetT32("11110xxxxxxxxxxx11x0xxxxxxxxxxx0", InstName.Blx, InstEmit32.Blx, OpCodeT32BImm24.Create);
SetT32("111010110001xxxx0xxx1111xxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT32AluRsImm.Create);
SetT32("11110x010001xxxx0xxx1111xxxxxxxx", InstName.Cmn, InstEmit32.Cmn, OpCodeT32AluImm.Create);
SetT32("111010111011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluRsImm.Create);
SetT32("11110x011011xxxx0xxx1111xxxxxxxx", InstName.Cmp, InstEmit32.Cmp, OpCodeT32AluImm.Create);
SetT32("11101010100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluRsImm.Create);
SetT32("11110x00100<xxxx0xxx<<<<xxxxxxxx", InstName.Eor, InstEmit32.Eor, OpCodeT32AluImm.Create);
SetT32("11101010010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluRsImm.Create);
SetT32("11110x00010x11110xxxxxxxxxxxxxxx", InstName.Mov, InstEmit32.Mov, OpCodeT32AluImm.Create);
SetT32("11101010011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluRsImm.Create);
SetT32("11110x00011x11110xxxxxxxxxxxxxxx", InstName.Mvn, InstEmit32.Mvn, OpCodeT32AluImm.Create);
SetT32("11101010011x<<<<0xxxxxxxxxxxxxxx", InstName.Orn, InstEmit32.Orn, OpCodeT32AluRsImm.Create);
SetT32("11110x00011x<<<<0xxxxxxxxxxxxxxx", InstName.Orn, InstEmit32.Orn, OpCodeT32AluImm.Create);
SetT32("11101010010x<<<<0xxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, OpCodeT32AluRsImm.Create);
SetT32("11110x00010x<<<<0xxxxxxxxxxxxxxx", InstName.Orr, InstEmit32.Orr, OpCodeT32AluImm.Create);
SetT32("11101011110xxxxx0xxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT32AluRsImm.Create);
SetT32("11110x01110xxxxx0xxxxxxxxxxxxxxx", InstName.Rsb, InstEmit32.Rsb, OpCodeT32AluImm.Create);
SetT32("11101011011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluRsImm.Create);
SetT32("11110x01011xxxxx0xxxxxxxxxxxxxxx", InstName.Sbc, InstEmit32.Sbc, OpCodeT32AluImm.Create);
SetT32("11101011101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluRsImm.Create);
SetT32("11110x01101<xxxx0xxx<<<<xxxxxxxx", InstName.Sub, InstEmit32.Sub, OpCodeT32AluImm.Create);
SetT32("111010101001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluRsImm.Create);
SetT32("11110x001001xxxx0xxx1111xxxxxxxx", InstName.Teq, InstEmit32.Teq, OpCodeT32AluImm.Create);
SetT32("111010100001xxxx0xxx1111xxxxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT32AluRsImm.Create);
SetT32("11110x000001xxxx0xxx1111xxxxxxxx", InstName.Tst, InstEmit32.Tst, OpCodeT32AluImm.Create);
#endregion
FillFastLookupTable(InstA32FastLookup, AllInstA32, ToFastLookupIndexA);

View File

@ -130,11 +130,6 @@ namespace ARMeilleure.Instructions
bool ordered = (accType & AccessType.Ordered) != 0;
bool exclusive = (accType & AccessType.Exclusive) != 0;
if (ordered)
{
EmitBarrier(context);
}
Operand address = context.Copy(GetIntOrSP(context, op.Rn));
Operand t = GetIntOrZR(context, op.Rt);
@ -163,6 +158,11 @@ namespace ARMeilleure.Instructions
{
EmitStoreExclusive(context, address, t, exclusive, op.Size, op.Rs, a32: false);
}
if (ordered)
{
EmitBarrier(context);
}
}
private static void EmitBarrier(ArmEmitterContext context)

View File

@ -146,13 +146,13 @@ namespace ARMeilleure.Instructions
var exclusive = (accType & AccessType.Exclusive) != 0;
var ordered = (accType & AccessType.Ordered) != 0;
if (ordered)
{
EmitBarrier(context);
}
if ((accType & AccessType.Load) != 0)
{
if (ordered)
{
EmitBarrier(context);
}
if (size == DWordSizeLog2)
{
// Keep loads atomic - make the call to get the whole region and then decompose it into parts
@ -219,6 +219,11 @@ namespace ARMeilleure.Instructions
Operand value = context.ZeroExtend32(OperandType.I64, GetIntA32(context, op.Rt));
EmitStoreExclusive(context, address, value, exclusive, size, op.Rd, a32: true);
}
if (ordered)
{
EmitBarrier(context);
}
}
}

View File

@ -43,6 +43,12 @@ namespace ARMeilleure.State
public long TpidrEl0 { get; set; }
public long Tpidr { get; set; }
public uint Pstate
{
get => _nativeContext.GetPstate();
set => _nativeContext.SetPstate(value);
}
public FPCR Fpcr { get; set; }
public FPSR Fpsr { get; set; }
public FPCR StandardFpcrValue => (Fpcr & (FPCR.Ahp)) | FPCR.Dn | FPCR.Fz;

View File

@ -95,6 +95,25 @@ namespace ARMeilleure.State
GetStorage().Flags[(int)flag] = value ? 1u : 0u;
}
public unsafe uint GetPstate()
{
uint value = 0;
for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
{
value |= GetStorage().Flags[flag] != 0 ? 1u << flag : 0u;
}
return value;
}
public unsafe void SetPstate(uint value)
{
for (int flag = 0; flag < RegisterConsts.FlagsCount; flag++)
{
uint bit = 1u << flag;
GetStorage().Flags[flag] = (value & bit) == bit ? 1u : 0u;
}
}
public unsafe bool GetFPStateFlag(FPState flag)
{
if ((uint)flag >= RegisterConsts.FpFlagsCount)

View File

@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 3179; //! To be incremented manually for each change to the ARMeilleure project.
private const uint InternalVersion = 3193; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";

View File

@ -149,11 +149,21 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
Span<byte> targetSpan = performanceOutput.Slice(nextOffset);
// NOTE: We check for the space for two headers for the final blank header.
int requiredSpace = Unsafe.SizeOf<THeader>() + Unsafe.SizeOf<TEntry>() * inputHeader.GetEntryCount()
+ Unsafe.SizeOf<TEntryDetail>() * inputHeader.GetEntryDetailCount()
+ Unsafe.SizeOf<THeader>();
if (targetSpan.Length < requiredSpace)
{
break;
}
ref THeader outputHeader = ref MemoryMarshal.Cast<byte, THeader>(targetSpan)[0];
nextOffset += Unsafe.SizeOf<THeader>();
Span<TEntry> outputEntries = MemoryMarshal.Cast<byte, TEntry>(targetSpan.Slice(nextOffset));
Span<TEntry> outputEntries = MemoryMarshal.Cast<byte, TEntry>(performanceOutput.Slice(nextOffset));
int totalProcessingTime = 0;
@ -175,7 +185,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
}
}
Span<TEntryDetail> outputEntriesDetail = MemoryMarshal.Cast<byte, TEntryDetail>(targetSpan.Slice(nextOffset));
Span<TEntryDetail> outputEntriesDetail = MemoryMarshal.Cast<byte, TEntryDetail>(performanceOutput.Slice(nextOffset));
int effectiveEntryDetailCount = 0;

View File

@ -5,6 +5,7 @@
public Stick Joystick { get; set; }
public bool InvertStickX { get; set; }
public bool InvertStickY { get; set; }
public bool Rotate90CW { get; set; }
public Button StickButton { get; set; }
}
}

View File

@ -47,6 +47,7 @@ namespace Ryujinx.Common.Logging
ServiceNim,
ServiceNs,
ServiceNsd,
ServiceNtc,
ServiceNv,
ServiceOlsc,
ServicePctl,

View File

@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.GAL
R32G32B32A32Sint,
S8Uint,
D16Unorm,
D24X8Unorm,
S8UintD24Unorm,
D32Float,
D24UnormS8Uint,
D32FloatS8Uint,
@ -266,7 +266,7 @@ namespace Ryujinx.Graphics.GAL
{
case Format.D16Unorm:
case Format.D24UnormS8Uint:
case Format.D24X8Unorm:
case Format.S8UintD24Unorm:
case Format.D32Float:
case Format.D32FloatS8Uint:
case Format.S8Uint:

View File

@ -1,7 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Device;
using Ryujinx.Graphics.Gpu.Engine.Threed;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using System;
using System.Collections.Generic;
@ -330,11 +330,95 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
{
// TODO: Implement remap functionality.
// Buffer to buffer copy.
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
bool srcIsPitchKind = memoryManager.GetKind(srcGpuVa).IsPitch();
bool dstIsPitchKind = memoryManager.GetKind(dstGpuVa).IsPitch();
if (!srcIsPitchKind && dstIsPitchKind)
{
CopyGobBlockLinearToLinear(memoryManager, srcGpuVa, dstGpuVa, size);
}
else if (srcIsPitchKind && !dstIsPitchKind)
{
CopyGobLinearToBlockLinear(memoryManager, srcGpuVa, dstGpuVa, size);
}
else
{
memoryManager.Physical.BufferCache.CopyBuffer(memoryManager, srcGpuVa, dstGpuVa, size);
}
}
}
}
/// <summary>
/// Copies block linear data with block linear GOBs to a block linear destination with linear GOBs.
/// </summary>
/// <param name="memoryManager">GPU memory manager</param>
/// <param name="srcGpuVa">Source GPU virtual address</param>
/// <param name="dstGpuVa">Destination GPU virtual address</param>
/// <param name="size">Size in bytes of the copy</param>
private static void CopyGobBlockLinearToLinear(MemoryManager memoryManager, ulong srcGpuVa, ulong dstGpuVa, ulong size)
{
if (((srcGpuVa | dstGpuVa | size) & 0xf) == 0)
{
for (ulong offset = 0; offset < size; offset += 16)
{
Vector128<byte> data = memoryManager.Read<Vector128<byte>>(ConvertGobLinearToBlockLinearAddress(srcGpuVa + offset), true);
memoryManager.Write(dstGpuVa + offset, data);
}
}
else
{
for (ulong offset = 0; offset < size; offset++)
{
byte data = memoryManager.Read<byte>(ConvertGobLinearToBlockLinearAddress(srcGpuVa + offset), true);
memoryManager.Write(dstGpuVa + offset, data);
}
}
}
/// <summary>
/// Copies block linear data with linear GOBs to a block linear destination with block linear GOBs.
/// </summary>
/// <param name="memoryManager">GPU memory manager</param>
/// <param name="srcGpuVa">Source GPU virtual address</param>
/// <param name="dstGpuVa">Destination GPU virtual address</param>
/// <param name="size">Size in bytes of the copy</param>
private static void CopyGobLinearToBlockLinear(MemoryManager memoryManager, ulong srcGpuVa, ulong dstGpuVa, ulong size)
{
if (((srcGpuVa | dstGpuVa | size) & 0xf) == 0)
{
for (ulong offset = 0; offset < size; offset += 16)
{
Vector128<byte> data = memoryManager.Read<Vector128<byte>>(srcGpuVa + offset, true);
memoryManager.Write(ConvertGobLinearToBlockLinearAddress(dstGpuVa + offset), data);
}
}
else
{
for (ulong offset = 0; offset < size; offset++)
{
byte data = memoryManager.Read<byte>(srcGpuVa + offset, true);
memoryManager.Write(ConvertGobLinearToBlockLinearAddress(dstGpuVa + offset), data);
}
}
}
/// <summary>
/// Calculates the GOB block linear address from a linear address.
/// </summary>
/// <param name="address">Linear address</param>
/// <returns>Block linear address</returns>
private static ulong ConvertGobLinearToBlockLinearAddress(ulong address)
{
// y2 y1 y0 x5 x4 x3 x2 x1 x0 -> x5 y2 y1 x4 y0 x3 x2 x1 x0
return (address & ~0x1f0UL) |
((address & 0x40) >> 2) |
((address & 0x10) << 1) |
((address & 0x180) >> 1) |
((address & 0x20) << 3);
}
/// <summary>
/// Performs a buffer to buffer, or buffer to texture copy, then optionally releases a semaphore.
/// </summary>

View File

@ -28,13 +28,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
{
return format switch
{
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
_ => FormatInfo.Default
};
}

View File

@ -55,6 +55,7 @@ namespace Ryujinx.Graphics.Gpu.Image
{ 0x24a0e, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
{ 0x48a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
{ 0x4912b, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) },
{ 0x25385, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4) },

View File

@ -7,7 +7,6 @@ using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using System;
using System.Collections.Generic;

View File

@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
lhs.FormatInfo.Format == Format.D24X8Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
{
return TextureMatchQuality.FormatAlias;
}

View File

@ -362,7 +362,7 @@ namespace Ryujinx.Graphics.Gpu.Image
return DepthStencilMode.Depth;
}
if (format == Format.D24X8Unorm || format == Format.D24UnormS8Uint)
if (format == Format.D24UnormS8Uint)
{
return component == SwizzleComponent.Red
? DepthStencilMode.Stencil

View File

@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
private const ulong BufferAlignmentSize = 0x1000;
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
private const ulong MaxDynamicGrowthSize = 0x100000;
private readonly GpuContext _context;
private readonly PhysicalMemory _physicalMemory;
@ -166,10 +168,35 @@ namespace Ryujinx.Graphics.Gpu.Memory
// Otherwise, we must delete the overlapping buffers and create a bigger buffer
// that fits all the data we need. We also need to copy the contents from the
// old buffer(s) to the new buffer.
ulong endAddress = address + size;
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
{
// Check if the following conditions are met:
// - We have a single overlap.
// - The overlap starts at or before the requested range. That is, the overlap happens at the end.
// - The size delta between the new, merged buffer and the old one is of at most 2 pages.
// In this case, we attempt to extend the buffer further than the requested range,
// this can potentially avoid future resizes if the application keeps using overlapping
// sequential memory.
// Allowing for 2 pages (rather than just one) is necessary to catch cases where the
// range crosses a page, and after alignment, ends having a size of 2 pages.
if (overlapsCount == 1 &&
address >= _bufferOverlaps[0].Address &&
endAddress - _bufferOverlaps[0].EndAddress <= BufferAlignmentSize * 2)
{
// Try to grow the buffer by 1.5x of its current size.
// This improves performance in the cases where the buffer is resized often by small amounts.
ulong existingSize = _bufferOverlaps[0].Size;
ulong growthSize = (existingSize + Math.Min(existingSize >> 1, MaxDynamicGrowthSize)) & ~BufferAlignmentMask;
size = Math.Max(size, growthSize);
endAddress = address + size;
overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
}
for (int index = 0; index < overlapsCount; index++)
{
Buffer buffer = _bufferOverlaps[index];
@ -183,7 +210,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, endAddress - address, _bufferOverlaps.Take(overlapsCount));
ulong newSize = endAddress - address;
Buffer newBuffer = new Buffer(_context, _physicalMemory, address, newSize, _bufferOverlaps.Take(overlapsCount));
lock (_buffers)
{
@ -202,7 +231,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
buffer.DisposeData();
}
newBuffer.SynchronizeMemory(address, endAddress - address);
newBuffer.SynchronizeMemory(address, newSize);
// Existing buffers were modified, we need to rebind everything.
NotifyBuffersModified?.Invoke();

View File

@ -255,6 +255,49 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
/// <summary>
/// Writes data to GPU mapped memory, stopping at the first unmapped page at the memory region, if any.
/// </summary>
/// <param name="va">GPU virtual address to write the data into</param>
/// <param name="data">The data to be written</param>
public void WriteMapped(ulong va, ReadOnlySpan<byte> data)
{
if (IsContiguous(va, data.Length))
{
Physical.Write(Translate(va), data);
}
else
{
int offset = 0, size;
if ((va & PageMask) != 0)
{
ulong pa = Translate(va);
size = Math.Min(data.Length, (int)PageSize - (int)(va & PageMask));
if (pa != PteUnmapped && Physical.IsMapped(pa))
{
Physical.Write(pa, data.Slice(0, size));
}
offset += size;
}
for (; offset < data.Length; offset += size)
{
ulong pa = Translate(va + (ulong)offset);
size = Math.Min(data.Length - offset, (int)PageSize);
if (pa != PteUnmapped && Physical.IsMapped(pa))
{
Physical.Write(pa, data.Slice(offset, size));
}
}
}
}
/// <summary>
/// Maps a given range of pages to the specified CPU virtual address.
/// </summary>
@ -264,7 +307,8 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// <param name="pa">CPU virtual address to map into</param>
/// <param name="va">GPU virtual address to be mapped</param>
/// <param name="size">Size in bytes of the mapping</param>
public void Map(ulong pa, ulong va, ulong size)
/// <param name="kind">Kind of the resource located at the mapping</param>
public void Map(ulong pa, ulong va, ulong size, PteKind kind)
{
lock (_pageTable)
{
@ -272,7 +316,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
for (ulong offset = 0; offset < size; offset += PageSize)
{
SetPte(va + offset, pa + offset);
SetPte(va + offset, PackPte(pa + offset, kind));
}
}
}
@ -462,14 +506,37 @@ namespace Ryujinx.Graphics.Gpu.Memory
return PteUnmapped;
}
ulong baseAddress = GetPte(va);
ulong pte = GetPte(va);
if (baseAddress == PteUnmapped)
if (pte == PteUnmapped)
{
return PteUnmapped;
}
return baseAddress + (va & PageMask);
return UnpackPaFromPte(pte) + (va & PageMask);
}
/// <summary>
/// Gets the kind of a given memory page.
/// This might indicate the type of resource that can be allocated on the page, and also texture tiling.
/// </summary>
/// <param name="va">GPU virtual address</param>
/// <returns>Kind of the memory page</returns>
public PteKind GetKind(ulong va)
{
if (!ValidateAddress(va))
{
return PteKind.Invalid;
}
ulong pte = GetPte(va);
if (pte == PteUnmapped)
{
return PteKind.Invalid;
}
return UnpackKindFromPte(pte);
}
/// <summary>
@ -512,5 +579,36 @@ namespace Ryujinx.Graphics.Gpu.Memory
_pageTable[l0][l1] = pte;
}
/// <summary>
/// Creates a page table entry from a physical address and kind.
/// </summary>
/// <param name="pa">Physical address</param>
/// <param name="kind">Kind</param>
/// <returns>Page table entry</returns>
private static ulong PackPte(ulong pa, PteKind kind)
{
return pa | ((ulong)kind << 56);
}
/// <summary>
/// Unpacks kind from a page table entry.
/// </summary>
/// <param name="pte">Page table entry</param>
/// <returns>Kind</returns>
private static PteKind UnpackKindFromPte(ulong pte)
{
return (PteKind)(pte >> 56);
}
/// <summary>
/// Unpacks physical address from a page table entry.
/// </summary>
/// <param name="pte">Page table entry</param>
/// <returns>Physical address</returns>
private static ulong UnpackPaFromPte(ulong pte)
{
return pte & 0xffffffffffffffUL;
}
}
}

View File

@ -340,6 +340,16 @@ namespace Ryujinx.Graphics.Gpu.Memory
return _cpuMemory.BeginSmartGranularTracking(address, size, granularity);
}
/// <summary>
/// Checks if the page at a given address is mapped on CPU memory.
/// </summary>
/// <param name="address">CPU virtual address of the page to check</param>
/// <returns>True if mapped, false otherwise</returns>
public bool IsMapped(ulong address)
{
return _cpuMemory.IsMapped(address);
}
/// <summary>
/// Release our reference to the CPU memory manager.
/// </summary>

View File

@ -0,0 +1,268 @@
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Kind of the resource at the given memory mapping.
/// </summary>
public enum PteKind : byte
{
Invalid = 0xff,
Pitch = 0x00,
Z16 = 0x01,
Z162C = 0x02,
Z16MS22C = 0x03,
Z16MS42C = 0x04,
Z16MS82C = 0x05,
Z16MS162C = 0x06,
Z162Z = 0x07,
Z16MS22Z = 0x08,
Z16MS42Z = 0x09,
Z16MS82Z = 0x0a,
Z16MS162Z = 0x0b,
Z162CZ = 0x36,
Z16MS22CZ = 0x37,
Z16MS42CZ = 0x38,
Z16MS82CZ = 0x39,
Z16MS162CZ = 0x5f,
Z164CZ = 0x0c,
Z16MS24CZ = 0x0d,
Z16MS44CZ = 0x0e,
Z16MS84CZ = 0x0f,
Z16MS164CZ = 0x10,
S8Z24 = 0x11,
S8Z241Z = 0x12,
S8Z24MS21Z = 0x13,
S8Z24MS41Z = 0x14,
S8Z24MS81Z = 0x15,
S8Z24MS161Z = 0x16,
S8Z242CZ = 0x17,
S8Z24MS22CZ = 0x18,
S8Z24MS42CZ = 0x19,
S8Z24MS82CZ = 0x1a,
S8Z24MS162CZ = 0x1b,
S8Z242CS = 0x1c,
S8Z24MS22CS = 0x1d,
S8Z24MS42CS = 0x1e,
S8Z24MS82CS = 0x1f,
S8Z24MS162CS = 0x20,
S8Z244CSZV = 0x21,
S8Z24MS24CSZV = 0x22,
S8Z24MS44CSZV = 0x23,
S8Z24MS84CSZV = 0x24,
S8Z24MS164CSZV = 0x25,
V8Z24MS4VC12 = 0x26,
V8Z24MS4VC4 = 0x27,
V8Z24MS8VC8 = 0x28,
V8Z24MS8VC24 = 0x29,
V8Z24MS4VC121ZV = 0x2e,
V8Z24MS4VC41ZV = 0x2f,
V8Z24MS8VC81ZV = 0x30,
V8Z24MS8VC241ZV = 0x31,
V8Z24MS4VC122CS = 0x32,
V8Z24MS4VC42CS = 0x33,
V8Z24MS8VC82CS = 0x34,
V8Z24MS8VC242CS = 0x35,
V8Z24MS4VC122CZV = 0x3a,
V8Z24MS4VC42CZV = 0x3b,
V8Z24MS8VC82CZV = 0x3c,
V8Z24MS8VC242CZV = 0x3d,
V8Z24MS4VC122ZV = 0x3e,
V8Z24MS4VC42ZV = 0x3f,
V8Z24MS8VC82ZV = 0x40,
V8Z24MS8VC242ZV = 0x41,
V8Z24MS4VC124CSZV = 0x42,
V8Z24MS4VC44CSZV = 0x43,
V8Z24MS8VC84CSZV = 0x44,
V8Z24MS8VC244CSZV = 0x45,
Z24S8 = 0x46,
Z24S81Z = 0x47,
Z24S8MS21Z = 0x48,
Z24S8MS41Z = 0x49,
Z24S8MS81Z = 0x4a,
Z24S8MS161Z = 0x4b,
Z24S82CS = 0x4c,
Z24S8MS22CS = 0x4d,
Z24S8MS42CS = 0x4e,
Z24S8MS82CS = 0x4f,
Z24S8MS162CS = 0x50,
Z24S82CZ = 0x51,
Z24S8MS22CZ = 0x52,
Z24S8MS42CZ = 0x53,
Z24S8MS82CZ = 0x54,
Z24S8MS162CZ = 0x55,
Z24S84CSZV = 0x56,
Z24S8MS24CSZV = 0x57,
Z24S8MS44CSZV = 0x58,
Z24S8MS84CSZV = 0x59,
Z24S8MS164CSZV = 0x5a,
Z24V8MS4VC12 = 0x5b,
Z24V8MS4VC4 = 0x5c,
Z24V8MS8VC8 = 0x5d,
Z24V8MS8VC24 = 0x5e,
YUVB8C12Y = 0x60,
YUVB8C22Y = 0x61,
YUVB10C12Y = 0x62,
YUVB10C22Y = 0x6b,
YUVB12C12Y = 0x6c,
YUVB12C22Y = 0x6d,
Z24V8MS4VC121ZV = 0x63,
Z24V8MS4VC41ZV = 0x64,
Z24V8MS8VC81ZV = 0x65,
Z24V8MS8VC241ZV = 0x66,
Z24V8MS4VC122CS = 0x67,
Z24V8MS4VC42CS = 0x68,
Z24V8MS8VC82CS = 0x69,
Z24V8MS8VC242CS = 0x6a,
Z24V8MS4VC122CZV = 0x6f,
Z24V8MS4VC42CZV = 0x70,
Z24V8MS8VC82CZV = 0x71,
Z24V8MS8VC242CZV = 0x72,
Z24V8MS4VC122ZV = 0x73,
Z24V8MS4VC42ZV = 0x74,
Z24V8MS8VC82ZV = 0x75,
Z24V8MS8VC242ZV = 0x76,
Z24V8MS4VC124CSZV = 0x77,
Z24V8MS4VC44CSZV = 0x78,
Z24V8MS8VC84CSZV = 0x79,
Z24V8MS8VC244CSZV = 0x7a,
ZF32 = 0x7b,
ZF321Z = 0x7c,
ZF32MS21Z = 0x7d,
ZF32MS41Z = 0x7e,
ZF32MS81Z = 0x7f,
ZF32MS161Z = 0x80,
ZF322CS = 0x81,
ZF32MS22CS = 0x82,
ZF32MS42CS = 0x83,
ZF32MS82CS = 0x84,
ZF32MS162CS = 0x85,
ZF322CZ = 0x86,
ZF32MS22CZ = 0x87,
ZF32MS42CZ = 0x88,
ZF32MS82CZ = 0x89,
ZF32MS162CZ = 0x8a,
X8Z24X16V8S8MS4VC12 = 0x8b,
X8Z24X16V8S8MS4VC4 = 0x8c,
X8Z24X16V8S8MS8VC8 = 0x8d,
X8Z24X16V8S8MS8VC24 = 0x8e,
X8Z24X16V8S8MS4VC121CS = 0x8f,
X8Z24X16V8S8MS4VC41CS = 0x90,
X8Z24X16V8S8MS8VC81CS = 0x91,
X8Z24X16V8S8MS8VC241CS = 0x92,
X8Z24X16V8S8MS4VC121ZV = 0x97,
X8Z24X16V8S8MS4VC41ZV = 0x98,
X8Z24X16V8S8MS8VC81ZV = 0x99,
X8Z24X16V8S8MS8VC241ZV = 0x9a,
X8Z24X16V8S8MS4VC121CZV = 0x9b,
X8Z24X16V8S8MS4VC41CZV = 0x9c,
X8Z24X16V8S8MS8VC81CZV = 0x9d,
X8Z24X16V8S8MS8VC241CZV = 0x9e,
X8Z24X16V8S8MS4VC122CS = 0x9f,
X8Z24X16V8S8MS4VC42CS = 0xa0,
X8Z24X16V8S8MS8VC82CS = 0xa1,
X8Z24X16V8S8MS8VC242CS = 0xa2,
X8Z24X16V8S8MS4VC122CSZV = 0xa3,
X8Z24X16V8S8MS4VC42CSZV = 0xa4,
X8Z24X16V8S8MS8VC82CSZV = 0xa5,
X8Z24X16V8S8MS8VC242CSZV = 0xa6,
ZF32X16V8S8MS4VC12 = 0xa7,
ZF32X16V8S8MS4VC4 = 0xa8,
ZF32X16V8S8MS8VC8 = 0xa9,
ZF32X16V8S8MS8VC24 = 0xaa,
ZF32X16V8S8MS4VC121CS = 0xab,
ZF32X16V8S8MS4VC41CS = 0xac,
ZF32X16V8S8MS8VC81CS = 0xad,
ZF32X16V8S8MS8VC241CS = 0xae,
ZF32X16V8S8MS4VC121ZV = 0xb3,
ZF32X16V8S8MS4VC41ZV = 0xb4,
ZF32X16V8S8MS8VC81ZV = 0xb5,
ZF32X16V8S8MS8VC241ZV = 0xb6,
ZF32X16V8S8MS4VC121CZV = 0xb7,
ZF32X16V8S8MS4VC41CZV = 0xb8,
ZF32X16V8S8MS8VC81CZV = 0xb9,
ZF32X16V8S8MS8VC241CZV = 0xba,
ZF32X16V8S8MS4VC122CS = 0xbb,
ZF32X16V8S8MS4VC42CS = 0xbc,
ZF32X16V8S8MS8VC82CS = 0xbd,
ZF32X16V8S8MS8VC242CS = 0xbe,
ZF32X16V8S8MS4VC122CSZV = 0xbf,
ZF32X16V8S8MS4VC42CSZV = 0xc0,
ZF32X16V8S8MS8VC82CSZV = 0xc1,
ZF32X16V8S8MS8VC242CSZV = 0xc2,
ZF32X24S8 = 0xc3,
ZF32X24S81CS = 0xc4,
ZF32X24S8MS21CS = 0xc5,
ZF32X24S8MS41CS = 0xc6,
ZF32X24S8MS81CS = 0xc7,
ZF32X24S8MS161CS = 0xc8,
ZF32X24S82CSZV = 0xce,
ZF32X24S8MS22CSZV = 0xcf,
ZF32X24S8MS42CSZV = 0xd0,
ZF32X24S8MS82CSZV = 0xd1,
ZF32X24S8MS162CSZV = 0xd2,
ZF32X24S82CS = 0xd3,
ZF32X24S8MS22CS = 0xd4,
ZF32X24S8MS42CS = 0xd5,
ZF32X24S8MS82CS = 0xd6,
ZF32X24S8MS162CS = 0xd7,
S8 = 0x2a,
S82S = 0x2b,
Generic16Bx2 = 0xfe,
C322C = 0xd8,
C322CBR = 0xd9,
C322CBA = 0xda,
C322CRA = 0xdb,
C322BRA = 0xdc,
C32MS22C = 0xdd,
C32MS22CBR = 0xde,
C32MS24CBRA = 0xcc,
C32MS42C = 0xdf,
C32MS42CBR = 0xe0,
C32MS42CBA = 0xe1,
C32MS42CRA = 0xe2,
C32MS42BRA = 0xe3,
C32MS44CBRA = 0x2c,
C32MS8MS162C = 0xe4,
C32MS8MS162CRA = 0xe5,
C642C = 0xe6,
C642CBR = 0xe7,
C642CBA = 0xe8,
C642CRA = 0xe9,
C642BRA = 0xea,
C64MS22C = 0xeb,
C64MS22CBR = 0xec,
C64MS24CBRA = 0xcd,
C64MS42C = 0xed,
C64MS42CBR = 0xee,
C64MS42CBA = 0xef,
C64MS42CRA = 0xf0,
C64MS42BRA = 0xf1,
C64MS44CBRA = 0x2d,
C64MS8MS162C = 0xf2,
C64MS8MS162CRA = 0xf3,
C1282C = 0xf4,
C1282CR = 0xf5,
C128MS22C = 0xf6,
C128MS22CR = 0xf7,
C128MS42C = 0xf8,
C128MS42CR = 0xf9,
C128MS8MS162C = 0xfa,
C128MS8MS162CR = 0xfb,
X8C24 = 0xfc,
PitchNoSwizzle = 0xfd,
SmSkedMessage = 0xca,
SmHostMessage = 0xcb
}
static class PteKindExtensions
{
/// <summary>
/// Checks if the kind is pitch.
/// </summary>
/// <param name="kind">Kind to check</param>
/// <returns>True if pitch, false otherwise</returns>
public static bool IsPitch(this PteKind kind)
{
return kind == PteKind.Pitch || kind == PteKind.PitchNoSwizzle;
}
}
}

View File

@ -1,13 +0,0 @@
namespace Ryujinx.Graphics.Gpu.Memory
{
/// <summary>
/// Name of a GPU resource.
/// </summary>
public enum ResourceName
{
Buffer,
Texture,
TexturePool,
SamplerPool
}
}

View File

@ -15,11 +15,13 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg
public Plane UPlane => new Plane((IntPtr)Frame->data[1], UvStride * UvHeight);
public Plane VPlane => new Plane((IntPtr)Frame->data[2], UvStride * UvHeight);
public FrameField Field => Frame->interlaced_frame != 0 ? FrameField.Interlaced : FrameField.Progressive;
public int Width => Frame->width;
public int Height => Frame->height;
public int Stride => Frame->linesize[0];
public int UvWidth => (Frame->width + 1) >> 1;
public int UvHeight => (Frame->height + 1) >> 1;
public int UvWidth => (Width + 1) >> 1;
public int UvHeight => (Height + 1) >> 1;
public int UvStride => Frame->linesize[1];
public Surface(int width, int height)

View File

@ -486,8 +486,7 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
Idct8(tempIn, tempOut);
for (j = 0; j < 8; ++j)
{
dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i],
BitUtils.RoundPowerOfTwo(tempOut[j], 5));
dest[j * stride + i] = ClipPixelAdd(dest[j * stride + i], BitUtils.RoundPowerOfTwo(tempOut[j], 5));
}
}
}

View File

@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Nvdec.Vp9.Types
public unsafe Plane UPlane => new Plane((IntPtr)UBuffer.ToPointer(), UBuffer.Length);
public unsafe Plane VPlane => new Plane((IntPtr)VBuffer.ToPointer(), VBuffer.Length);
public FrameField Field => FrameField.Progressive;
public int Width { get; }
public int Height { get; }
public int AlignedWidth { get; }

View File

@ -31,7 +31,24 @@ namespace Ryujinx.Graphics.Nvdec
if (decoder.Decode(ref info, outputSurface, bitstream))
{
SurfaceWriter.Write(rm.Gmm, outputSurface, lumaOffset, chromaOffset);
if (outputSurface.Field == FrameField.Progressive)
{
SurfaceWriter.Write(
rm.Gmm,
outputSurface,
lumaOffset + pictureInfo.LumaFrameOffset,
chromaOffset + pictureInfo.ChromaFrameOffset);
}
else
{
SurfaceWriter.WriteInterlaced(
rm.Gmm,
outputSurface,
lumaOffset + pictureInfo.LumaTopFieldOffset,
chromaOffset + pictureInfo.ChromaTopFieldOffset,
lumaOffset + pictureInfo.LumaBottomFieldOffset,
chromaOffset + pictureInfo.ChromaBottomFieldOffset);
}
}
rm.Cache.Put(outputSurface);

View File

@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Nvdec.Image
int width = surface.Width;
int height = surface.Height;
int stride = surface.Stride;
ReadOnlySpan<byte> luma = gmm.DeviceGetSpan(lumaOffset, GetBlockLinearSize(width, height, 1));
ReadLuma(surface.YPlane.AsSpan(), luma, stride, width, height);

View File

@ -38,6 +38,55 @@ namespace Ryujinx.Graphics.Nvdec.Image
surface.UvHeight);
}
public static void WriteInterlaced(
MemoryManager gmm,
ISurface surface,
uint lumaTopOffset,
uint chromaTopOffset,
uint lumaBottomOffset,
uint chromaBottomOffset)
{
int lumaSize = GetBlockLinearSize(surface.Width, surface.Height / 2, 1);
using var lumaTop = gmm.GetWritableRegion(ExtendOffset(lumaTopOffset), lumaSize);
using var lumaBottom = gmm.GetWritableRegion(ExtendOffset(lumaBottomOffset), lumaSize);
WriteLuma(
lumaTop.Memory.Span,
surface.YPlane.AsSpan(),
surface.Stride * 2,
surface.Width,
surface.Height / 2);
WriteLuma(
lumaBottom.Memory.Span,
surface.YPlane.AsSpan().Slice(surface.Stride),
surface.Stride * 2,
surface.Width,
surface.Height / 2);
int chromaSize = GetBlockLinearSize(surface.UvWidth, surface.UvHeight / 2, 2);
using var chromaTop = gmm.GetWritableRegion(ExtendOffset(chromaTopOffset), chromaSize);
using var chromaBottom = gmm.GetWritableRegion(ExtendOffset(chromaBottomOffset), chromaSize);
WriteChroma(
chromaTop.Memory.Span,
surface.UPlane.AsSpan(),
surface.VPlane.AsSpan(),
surface.UvStride * 2,
surface.UvWidth,
surface.UvHeight / 2);
WriteChroma(
chromaBottom.Memory.Span,
surface.UPlane.AsSpan().Slice(surface.UvStride),
surface.VPlane.AsSpan().Slice(surface.UvStride),
surface.UvStride * 2,
surface.UvWidth,
surface.UvHeight / 2);
}
private static void WriteLuma(Span<byte> dst, ReadOnlySpan<byte> src, int srcStride, int width, int height)
{
LayoutConverter.ConvertLinearToBlockLinear(dst, width, height, srcStride, 1, 2, src);

View File

@ -26,10 +26,10 @@ namespace Ryujinx.Graphics.Nvdec.Types.H264
public uint Transform8x8ModeFlag;
public uint LumaPitch;
public uint ChromaPitch;
public uint LumaTopOffset;
public uint LumaBottomOffset;
public uint LumaTopFieldOffset;
public uint LumaBottomFieldOffset;
public uint LumaFrameOffset;
public uint ChromaTopOffset;
public uint ChromaTopFieldOffset;
public uint ChromaBottomFieldOffset;
public uint ChromaFrameOffset;
public uint HistBufferSize;

View File

@ -6,15 +6,15 @@ namespace Ryujinx.Graphics.OpenGL
{
struct FormatTable
{
private static FormatInfo[] Table;
private static SizedInternalFormat[] TableImage;
private static FormatInfo[] _table;
private static SizedInternalFormat[] _tableImage;
static FormatTable()
{
int tableSize = Enum.GetNames<Format>().Length;
Table = new FormatInfo[tableSize];
TableImage = new SizedInternalFormat[tableSize];
_table = new FormatInfo[tableSize];
_tableImage = new SizedInternalFormat[tableSize];
Add(Format.R8Unorm, new FormatInfo(1, true, false, All.R8, PixelFormat.Red, PixelType.UnsignedByte));
Add(Format.R8Snorm, new FormatInfo(1, true, false, All.R8Snorm, PixelFormat.Red, PixelType.Byte));
@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.OpenGL
Add(Format.R32G32B32A32Sint, new FormatInfo(4, false, false, All.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int));
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte));
Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort));
Add(Format.D24X8Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt));
Add(Format.S8UintD24Unorm, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float));
Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev));
@ -218,22 +218,22 @@ namespace Ryujinx.Graphics.OpenGL
private static void Add(Format format, FormatInfo info)
{
Table[(int)format] = info;
_table[(int)format] = info;
}
private static void Add(Format format, SizedInternalFormat sif)
{
TableImage[(int)format] = sif;
_tableImage[(int)format] = sif;
}
public static FormatInfo GetFormatInfo(Format format)
{
return Table[(int)format];
return _table[(int)format];
}
public static SizedInternalFormat GetImageFormat(Format format)
{
return TableImage[(int)format];
return _tableImage[(int)format];
}
}
}

View File

@ -127,14 +127,13 @@ namespace Ryujinx.Graphics.OpenGL
private static bool IsPackedDepthStencilFormat(Format format)
{
return format == Format.D24UnormS8Uint ||
format == Format.D32FloatS8Uint;
format == Format.D32FloatS8Uint ||
format == Format.S8UintD24Unorm;
}
private static bool IsDepthOnlyFormat(Format format)
{
return format == Format.D16Unorm ||
format == Format.D24X8Unorm ||
format == Format.D32Float;
return format == Format.D16Unorm || format == Format.D32Float;
}
public void Dispose()

View File

@ -0,0 +1,149 @@
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Ryujinx.Graphics.OpenGL.Image
{
static class FormatConverter
{
public unsafe static byte[] ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
{
byte[] output = new byte[data.Length];
int start = 0;
if (Avx2.IsSupported)
{
var mask = Vector256.Create(
(byte)3, (byte)0, (byte)1, (byte)2,
(byte)7, (byte)4, (byte)5, (byte)6,
(byte)11, (byte)8, (byte)9, (byte)10,
(byte)15, (byte)12, (byte)13, (byte)14,
(byte)19, (byte)16, (byte)17, (byte)18,
(byte)23, (byte)20, (byte)21, (byte)22,
(byte)27, (byte)24, (byte)25, (byte)26,
(byte)31, (byte)28, (byte)29, (byte)30);
int sizeAligned = data.Length & ~31;
fixed (byte* pInput = data, pOutput = output)
{
for (uint i = 0; i < sizeAligned; i += 32)
{
var dataVec = Avx.LoadVector256(pInput + i);
dataVec = Avx2.Shuffle(dataVec, mask);
Avx.Store(pOutput + i, dataVec);
}
}
start = sizeAligned;
}
else if (Ssse3.IsSupported)
{
var mask = Vector128.Create(
(byte)3, (byte)0, (byte)1, (byte)2,
(byte)7, (byte)4, (byte)5, (byte)6,
(byte)11, (byte)8, (byte)9, (byte)10,
(byte)15, (byte)12, (byte)13, (byte)14);
int sizeAligned = data.Length & ~15;
fixed (byte* pInput = data, pOutput = output)
{
for (uint i = 0; i < sizeAligned; i += 16)
{
var dataVec = Sse2.LoadVector128(pInput + i);
dataVec = Ssse3.Shuffle(dataVec, mask);
Sse2.Store(pOutput + i, dataVec);
}
}
start = sizeAligned;
}
var outSpan = MemoryMarshal.Cast<byte, uint>(output);
var dataSpan = MemoryMarshal.Cast<byte, uint>(data);
for (int i = start / sizeof(uint); i < dataSpan.Length; i++)
{
outSpan[i] = BitOperations.RotateLeft(dataSpan[i], 8);
}
return output;
}
public unsafe static byte[] ConvertD24S8ToS8D24(ReadOnlySpan<byte> data)
{
byte[] output = new byte[data.Length];
int start = 0;
if (Avx2.IsSupported)
{
var mask = Vector256.Create(
(byte)1, (byte)2, (byte)3, (byte)0,
(byte)5, (byte)6, (byte)7, (byte)4,
(byte)9, (byte)10, (byte)11, (byte)8,
(byte)13, (byte)14, (byte)15, (byte)12,
(byte)17, (byte)18, (byte)19, (byte)16,
(byte)21, (byte)22, (byte)23, (byte)20,
(byte)25, (byte)26, (byte)27, (byte)24,
(byte)29, (byte)30, (byte)31, (byte)28);
int sizeAligned = data.Length & ~31;
fixed (byte* pInput = data, pOutput = output)
{
for (uint i = 0; i < sizeAligned; i += 32)
{
var dataVec = Avx.LoadVector256(pInput + i);
dataVec = Avx2.Shuffle(dataVec, mask);
Avx.Store(pOutput + i, dataVec);
}
}
start = sizeAligned;
}
else if (Ssse3.IsSupported)
{
var mask = Vector128.Create(
(byte)1, (byte)2, (byte)3, (byte)0,
(byte)5, (byte)6, (byte)7, (byte)4,
(byte)9, (byte)10, (byte)11, (byte)8,
(byte)13, (byte)14, (byte)15, (byte)12);
int sizeAligned = data.Length & ~15;
fixed (byte* pInput = data, pOutput = output)
{
for (uint i = 0; i < sizeAligned; i += 16)
{
var dataVec = Sse2.LoadVector128(pInput + i);
dataVec = Ssse3.Shuffle(dataVec, mask);
Sse2.Store(pOutput + i, dataVec);
}
}
start = sizeAligned;
}
var outSpan = MemoryMarshal.Cast<byte, uint>(output);
var dataSpan = MemoryMarshal.Cast<byte, uint>(data);
for (int i = start / sizeof(uint); i < dataSpan.Length; i++)
{
outSpan[i] = BitOperations.RotateRight(dataSpan[i], 8);
}
return output;
}
}
}

View File

@ -291,7 +291,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
private static ClearBufferMask GetMask(Format format)
{
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint)
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint || format == Format.S8UintD24Unorm)
{
return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
}
@ -311,9 +311,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
private static bool IsDepthOnly(Format format)
{
return format == Format.D16Unorm ||
format == Format.D24X8Unorm ||
format == Format.D32Float;
return format == Format.D16Unorm || format == Format.D32Float;
}
public TextureView BgraSwap(TextureView from)

View File

@ -140,9 +140,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
size += Info.GetMipSize(level);
}
ReadOnlySpan<byte> data;
if (HwCapabilities.UsePersistentBufferForFlush)
{
return _renderer.PersistentBuffers.Default.GetTextureData(this, size);
data = _renderer.PersistentBuffers.Default.GetTextureData(this, size);
}
else
{
@ -150,8 +152,15 @@ namespace Ryujinx.Graphics.OpenGL.Image
WriteTo(target);
return new ReadOnlySpan<byte>(target.ToPointer(), size);
data = new ReadOnlySpan<byte>(target.ToPointer(), size);
}
if (Format == Format.S8UintD24Unorm)
{
data = FormatConverter.ConvertD24S8ToS8D24(data);
}
return data;
}
public unsafe ReadOnlySpan<byte> GetData(int layer, int level)
@ -285,6 +294,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
public void SetData(ReadOnlySpan<byte> data)
{
if (Format == Format.S8UintD24Unorm)
{
data = FormatConverter.ConvertS8D24ToD24S8(data);
}
unsafe
{
fixed (byte* ptr = data)
@ -296,6 +310,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
{
if (Format == Format.S8UintD24Unorm)
{
data = FormatConverter.ConvertS8D24ToD24S8(data);
}
unsafe
{
fixed (byte* ptr = data)

View File

@ -612,7 +612,7 @@ namespace Ryujinx.Graphics.OpenGL
_program?.Bind();
_unit0Sampler?.Bind(0);
GL.ViewportArray(0, 1, _viewportArray);
RestoreViewport0();
Enable(EnableCap.CullFace, _cullEnable);
Enable(EnableCap.StencilTest, _stencilTestEnable);
@ -1478,6 +1478,11 @@ namespace Ryujinx.Graphics.OpenGL
_currentComponentMasks |= componentMaskAtIndex;
}
public void RestoreClipControl()
{
GL.ClipControl(_clipOrigin, _clipDepthMode);
}
public void RestoreScissor0Enable()
{
if ((_scissorEnables & 1u) != 0)
@ -1494,6 +1499,14 @@ namespace Ryujinx.Graphics.OpenGL
}
}
public void RestoreViewport0()
{
if (_viewportArray.Length > 0)
{
GL.ViewportArray(0, 1, _viewportArray);
}
}
public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual)
{
if (value is CounterQueueEvent)

View File

@ -27,11 +27,12 @@ namespace Ryujinx.Graphics.OpenGL
{
GL.Disable(EnableCap.FramebufferSrgb);
CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop);
CopyTextureToFrameBufferRGB(0, GetCopyFramebufferHandleLazy(), (TextureView)texture, crop, swapBuffersCallback);
GL.Enable(EnableCap.FramebufferSrgb);
swapBuffersCallback();
// Restore unpack alignment to 4, as performance overlays such as RTSS may change this to load their resources.
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
}
public void SetSize(int width, int height)
@ -40,7 +41,7 @@ namespace Ryujinx.Graphics.OpenGL
_height = height;
}
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop)
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback)
{
(int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers();
@ -139,11 +140,20 @@ namespace Ryujinx.Graphics.OpenGL
((Pipeline)_renderer.Pipeline).RestoreComponentMask(i);
}
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
GL.Viewport(0, 0, _width, _height);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer);
swapBuffersCallback();
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle);
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle);
((Pipeline)_renderer.Pipeline).RestoreClipControl();
((Pipeline)_renderer.Pipeline).RestoreScissor0Enable();
((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard();
((Pipeline)_renderer.Pipeline).RestoreViewport0();
if (viewConverted != view)
{

View File

@ -70,6 +70,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
AppendLine("}" + suffix);
}
public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp)
{
TextureDescriptor[] descriptors = Config.GetTextureDescriptors();
for (int i = 0; i < descriptors.Length; i++)
{
var descriptor = descriptors[i];
if (descriptor.CbufSlot == texOp.CbufSlot &&
descriptor.HandleIndex == texOp.Handle &&
descriptor.Format == texOp.Format)
{
return (descriptor, i);
}
}
return (default, -1);
}
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
{
for (int i = 0; i < array.Length; i++)

View File

@ -756,27 +756,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
IAstNode lod = operation.GetSource(lodSrcIndex);
string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
if (texOp.Index == 3)
{
return $"textureQueryLevels({samplerName})";
}
else
{
string texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
(TextureDescriptor descriptor, int descriptorIndex) = context.FindTextureDescriptor(texOp);
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer;
string texCall;
if (hasLod)
{
int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
IAstNode lod = operation.GetSource(lodSrcIndex);
string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
texCall = $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
}
else
{
texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}";
}
if (context.Config.Stage.SupportsRenderScale() &&
!isBindless &&
!isIndexed)
{
int index = context.FindTextureDescriptorIndex(texOp);
texCall = "Helper_TextureSizeUnscale(" + texCall + ", " + index + ")";
texCall = $"Helper_TextureSizeUnscale({texCall}, {descriptorIndex})";
}
return texCall;

View File

@ -328,6 +328,7 @@ namespace Ryujinx.Graphics.Shader.Translation
public void SetAllInputUserAttributes()
{
UsedInputAttributes |= Constants.AllAttributesMask;
ThisInputAttributesComponents |= ~UInt128.Zero >> (128 - Constants.MaxAttributes * 4);
}
public void SetAllOutputUserAttributes()

View File

@ -218,10 +218,17 @@ namespace Ryujinx.Graphics.Shader.Translation
while (usedAttributes != UInt128.Zero)
{
int index = usedAttributes.TrailingZeroCount();
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
int vecIndex = index / 4;
usedAttributes &= ~UInt128.Pow2(index);
// We don't need to initialize passthrough attributes.
if ((context.Config.PassthroughAttributes & (1 << vecIndex)) != 0)
{
continue;
}
InitializeOutputComponent(context, AttributeConsts.UserAttributeBase + index * 4, perPatch: false);
}
UInt128 usedAttributesPerPatch = context.Config.NextInputAttributesPerPatchComponents;

View File

@ -10,6 +10,12 @@ namespace Ryujinx.Graphics.Shader.Translation
private ulong _v0;
private ulong _v1;
public UInt128(ulong low, ulong high)
{
_v0 = low;
_v1 = high;
}
public int TrailingZeroCount()
{
int count = BitOperations.TrailingZeroCount(_v0);
@ -25,25 +31,57 @@ namespace Ryujinx.Graphics.Shader.Translation
{
if (x >= 64)
{
return new UInt128() { _v0 = 0, _v1 = 1UL << (x - 64 ) };
return new UInt128(0, 1UL << (x - 64));
}
return new UInt128() { _v0 = 1UL << x, _v1 = 0 };
return new UInt128(1UL << x, 0);
}
public static UInt128 operator ~(UInt128 x)
{
return new UInt128() { _v0 = ~x._v0, _v1 = ~x._v1 };
return new UInt128(~x._v0, ~x._v1);
}
public static UInt128 operator &(UInt128 x, UInt128 y)
{
return new UInt128() { _v0 = x._v0 & y._v0, _v1 = x._v1 & y._v1 };
return new UInt128(x._v0 & y._v0, x._v1 & y._v1);
}
public static UInt128 operator |(UInt128 x, UInt128 y)
{
return new UInt128() { _v0 = x._v0 | y._v0, _v1 = x._v1 | y._v1 };
return new UInt128(x._v0 | y._v0, x._v1 | y._v1);
}
public static UInt128 operator <<(UInt128 x, int shift)
{
if (shift == 0)
{
return new UInt128(x._v0, x._v1);
}
else if (shift >= 64)
{
return new UInt128(0, x._v0 << (shift - 64));
}
ulong shiftOut = x._v0 >> (64 - shift);
return new UInt128(x._v0 << shift, (x._v1 << shift) | shiftOut);
}
public static UInt128 operator >>(UInt128 x, int shift)
{
if (shift == 0)
{
return new UInt128(x._v0, x._v1);
}
else if (shift >= 64)
{
return new UInt128(x._v1 >> (shift - 64), 0);
}
ulong shiftOut = x._v1 & ((1UL << shift) - 1);
return new UInt128((x._v0 >> shift) | (shiftOut << (64 - shift)), x._v1 >> shift);
}
public static bool operator ==(UInt128 x, UInt128 y)

View File

@ -48,38 +48,10 @@ namespace Ryujinx.Graphics.Vic
int one = 1 << (mtx.MatrixRShift + 8);
// NOTE: This is buggy on .NET 5.0.100, we use a workaround for now (see https://github.com/dotnet/runtime/issues/44704)
// TODO: Uncomment this when fixed.
//Vector128<int> col1 = Vector128.Create(mtx.MatrixCoeff00, mtx.MatrixCoeff10, mtx.MatrixCoeff20, 0);
//Vector128<int> col2 = Vector128.Create(mtx.MatrixCoeff01, mtx.MatrixCoeff11, mtx.MatrixCoeff21, 0);
//Vector128<int> col3 = Vector128.Create(mtx.MatrixCoeff02, mtx.MatrixCoeff12, mtx.MatrixCoeff22, one);
//Vector128<int> col4 = Vector128.Create(mtx.MatrixCoeff03, mtx.MatrixCoeff13, mtx.MatrixCoeff23, 0);
Vector128<int> col1 = new Vector128<int>();
Vector128<int> col2 = new Vector128<int>();
Vector128<int> col3 = new Vector128<int>();
Vector128<int> col4 = new Vector128<int>();
col1 = Sse41.Insert(col1, mtx.MatrixCoeff00, 0);
col1 = Sse41.Insert(col1, mtx.MatrixCoeff10, 1);
col1 = Sse41.Insert(col1, mtx.MatrixCoeff20, 2);
col1 = Sse41.Insert(col1, 0, 3);
col2 = Sse41.Insert(col2, mtx.MatrixCoeff01, 0);
col2 = Sse41.Insert(col2, mtx.MatrixCoeff11, 1);
col2 = Sse41.Insert(col2, mtx.MatrixCoeff21, 2);
col2 = Sse41.Insert(col2, 0, 3);
col3 = Sse41.Insert(col3, mtx.MatrixCoeff02, 0);
col3 = Sse41.Insert(col3, mtx.MatrixCoeff12, 1);
col3 = Sse41.Insert(col3, mtx.MatrixCoeff22, 2);
col3 = Sse41.Insert(col3, one, 3);
col4 = Sse41.Insert(col4, mtx.MatrixCoeff03, 0);
col4 = Sse41.Insert(col4, mtx.MatrixCoeff13, 1);
col4 = Sse41.Insert(col4, mtx.MatrixCoeff23, 2);
col4 = Sse41.Insert(col4, 0, 3);
Vector128<int> col1 = Vector128.Create(mtx.MatrixCoeff00, mtx.MatrixCoeff10, mtx.MatrixCoeff20, 0);
Vector128<int> col2 = Vector128.Create(mtx.MatrixCoeff01, mtx.MatrixCoeff11, mtx.MatrixCoeff21, 0);
Vector128<int> col3 = Vector128.Create(mtx.MatrixCoeff02, mtx.MatrixCoeff12, mtx.MatrixCoeff22, one);
Vector128<int> col4 = Vector128.Create(mtx.MatrixCoeff03, mtx.MatrixCoeff13, mtx.MatrixCoeff23, 0);
Vector128<int> rShift = Vector128.CreateScalar(mtx.MatrixRShift);
Vector128<ushort> clMin = Vector128.Create((ushort)slot.SlotConfig.SoftClampLow);

View File

@ -14,7 +14,7 @@ namespace Ryujinx.Graphics.Vic.Image
/// If the required buffer is larger than this, it won't be
/// added to the pool to avoid long term high memory usage.
/// </summary>
private const int MaxBufferSize = 2048 * 1280;
private const int MaxBufferSize = 2048 * 2048;
private struct PoolItem
{

View File

@ -2,16 +2,85 @@
namespace Ryujinx.Graphics.Vic.Image
{
ref struct RentedBuffer
{
public static RentedBuffer Empty => new RentedBuffer(Span<byte>.Empty, -1);
public Span<byte> Data;
public int Index;
public RentedBuffer(Span<byte> data, int index)
{
Data = data;
Index = index;
}
public void Return(BufferPool<byte> pool)
{
if (Index != -1)
{
pool.Return(Index);
}
}
}
ref struct InputSurface
{
public ReadOnlySpan<byte> Buffer0;
public ReadOnlySpan<byte> Buffer1;
public ReadOnlySpan<byte> Buffer2;
public int Buffer0Index;
public int Buffer1Index;
public int Buffer2Index;
public int Width;
public int Height;
public int UvWidth;
public int UvHeight;
public void Initialize()
{
Buffer0Index = -1;
Buffer1Index = -1;
Buffer2Index = -1;
}
public void SetBuffer0(RentedBuffer buffer)
{
Buffer0 = buffer.Data;
Buffer0Index = buffer.Index;
}
public void SetBuffer1(RentedBuffer buffer)
{
Buffer1 = buffer.Data;
Buffer1Index = buffer.Index;
}
public void SetBuffer2(RentedBuffer buffer)
{
Buffer2 = buffer.Data;
Buffer2Index = buffer.Index;
}
public void Return(BufferPool<byte> pool)
{
if (Buffer0Index != -1)
{
pool.Return(Buffer0Index);
}
if (Buffer1Index != -1)
{
pool.Return(Buffer1Index);
}
if (Buffer2Index != -1)
{
pool.Return(Buffer2Index);
}
}
}
}

View File

@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Texture;
using Ryujinx.Graphics.Vic.Types;
using System;
@ -12,24 +12,32 @@ namespace Ryujinx.Graphics.Vic.Image
{
static class SurfaceReader
{
public static Surface Read(ResourceManager rm, ref SlotSurfaceConfig config, ref PlaneOffsets offsets)
public static Surface Read(
ResourceManager rm,
ref SlotConfig config,
ref SlotSurfaceConfig surfaceConfig,
ref Array8<PlaneOffsets> offsets)
{
switch (config.SlotPixelFormat)
switch (surfaceConfig.SlotPixelFormat)
{
case PixelFormat.Y8___V8U8_N420: return ReadNv12(rm, ref config, ref offsets);
case PixelFormat.Y8___V8U8_N420: return ReadNv12(rm, ref config, ref surfaceConfig, ref offsets);
}
Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{config.SlotPixelFormat}\".");
Logger.Error?.Print(LogClass.Vic, $"Unsupported pixel format \"{surfaceConfig.SlotPixelFormat}\".");
int lw = config.SlotLumaWidth + 1;
int lh = config.SlotLumaHeight + 1;
int lw = surfaceConfig.SlotLumaWidth + 1;
int lh = surfaceConfig.SlotLumaHeight + 1;
return new Surface(rm.SurfacePool, lw, lh);
}
private unsafe static Surface ReadNv12(ResourceManager rm, ref SlotSurfaceConfig config, ref PlaneOffsets offsets)
private unsafe static Surface ReadNv12(
ResourceManager rm,
ref SlotConfig config,
ref SlotSurfaceConfig surfaceConfig,
ref Array8<PlaneOffsets> offsets)
{
InputSurface input = ReadSurface(rm.Gmm, ref config, ref offsets, 1, 2);
InputSurface input = ReadSurface(rm, ref config, ref surfaceConfig, ref offsets, 1, 2);
int width = input.Width;
int height = input.Height;
@ -160,6 +168,8 @@ namespace Ryujinx.Graphics.Vic.Image
}
}
input.Return(rm.BufferPool);
return output;
}
@ -170,84 +180,227 @@ namespace Ryujinx.Graphics.Vic.Image
}
private static InputSurface ReadSurface(
MemoryManager gmm,
ref SlotSurfaceConfig config,
ref PlaneOffsets offsets,
ResourceManager rm,
ref SlotConfig config,
ref SlotSurfaceConfig surfaceConfig,
ref Array8<PlaneOffsets> offsets,
int bytesPerPixel,
int planes)
{
InputSurface surface = new InputSurface();
int gobBlocksInY = 1 << config.SlotBlkHeight;
surface.Initialize();
bool linear = config.SlotBlkKind == 0;
int gobBlocksInY = 1 << surfaceConfig.SlotBlkHeight;
int lw = config.SlotLumaWidth + 1;
int lh = config.SlotLumaHeight + 1;
bool linear = surfaceConfig.SlotBlkKind == 0;
int cw = config.SlotChromaWidth + 1;
int ch = config.SlotChromaHeight + 1;
int lw = surfaceConfig.SlotLumaWidth + 1;
int lh = surfaceConfig.SlotLumaHeight + 1;
int cw = surfaceConfig.SlotChromaWidth + 1;
int ch = surfaceConfig.SlotChromaHeight + 1;
// Interlaced inputs have double the height when deinterlaced.
int heightShift = config.FrameFormat.IsField() ? 1 : 0;
surface.Width = lw;
surface.Height = lh;
surface.Height = lh << heightShift;
surface.UvWidth = cw;
surface.UvHeight = ch;
surface.UvHeight = ch << heightShift;
if (planes > 0)
{
surface.Buffer0 = ReadBuffer(gmm, offsets.LumaOffset, linear, lw, lh, bytesPerPixel, gobBlocksInY);
surface.SetBuffer0(ReadBuffer(rm, ref config, ref offsets, linear, 0, lw, lh, bytesPerPixel, gobBlocksInY));
}
if (planes > 1)
{
surface.Buffer1 = ReadBuffer(gmm, offsets.ChromaUOffset, linear, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY);
surface.SetBuffer1(ReadBuffer(rm, ref config, ref offsets, linear, 1, cw, ch, planes == 2 ? 2 : 1, gobBlocksInY));
}
if (planes > 2)
{
surface.Buffer2 = ReadBuffer(gmm, offsets.ChromaVOffset, linear, cw, ch, 1, gobBlocksInY);
surface.SetBuffer2(ReadBuffer(rm, ref config, ref offsets, linear, 2, cw, ch, 1, gobBlocksInY));
}
return surface;
}
private static ReadOnlySpan<byte> ReadBuffer(
MemoryManager gmm,
uint offset,
private static RentedBuffer ReadBuffer(
ResourceManager rm,
ref SlotConfig config,
ref Array8<PlaneOffsets> offsets,
bool linear,
int plane,
int width,
int height,
int bytesPerPixel,
int gobBlocksInY)
{
FrameFormat frameFormat = config.FrameFormat;
bool isLuma = plane == 0;
bool isField = frameFormat.IsField();
bool isTopField = frameFormat.IsTopField(isLuma);
int stride = GetPitch(width, bytesPerPixel);
uint offset = GetOffset(ref offsets[0], plane);
int dstStart = 0;
int dstStride = stride;
if (isField)
{
dstStart = isTopField ? 0 : stride;
dstStride = stride * 2;
}
RentedBuffer buffer;
if (linear)
{
buffer = ReadBufferLinear(rm, offset, width, height, dstStart, dstStride, bytesPerPixel);
}
else
{
buffer = ReadBufferBlockLinear(rm, offset, width, height, dstStart, dstStride, bytesPerPixel, gobBlocksInY);
}
if (isField || frameFormat.IsInterlaced())
{
RentedBuffer prevBuffer = RentedBuffer.Empty;
RentedBuffer nextBuffer = RentedBuffer.Empty;
if (config.PrevFieldEnable)
{
prevBuffer = ReadBufferNoDeinterlace(rm, ref offsets[1], linear, plane, width, height, bytesPerPixel, gobBlocksInY);
}
if (config.NextFieldEnable)
{
nextBuffer = ReadBufferNoDeinterlace(rm, ref offsets[2], linear, plane, width, height, bytesPerPixel, gobBlocksInY);
}
int w = width * bytesPerPixel;
switch (config.DeinterlaceMode)
{
case DeinterlaceMode.Weave:
Scaler.DeinterlaceWeave(buffer.Data, prevBuffer.Data, w, stride, isTopField);
break;
case DeinterlaceMode.BobField:
Scaler.DeinterlaceBob(buffer.Data, w, stride, isTopField);
break;
case DeinterlaceMode.Bob:
bool isCurrentTop = isLuma ? config.IsEven : config.ChromaEven;
Scaler.DeinterlaceBob(buffer.Data, w, stride, isCurrentTop ^ frameFormat.IsInterlacedBottomFirst());
break;
case DeinterlaceMode.NewBob:
case DeinterlaceMode.Disi1:
Scaler.DeinterlaceMotionAdaptive(buffer.Data, prevBuffer.Data, nextBuffer.Data, w, stride, isTopField);
break;
case DeinterlaceMode.WeaveLumaBobFieldChroma:
if (isLuma)
{
Scaler.DeinterlaceWeave(buffer.Data, prevBuffer.Data, w, stride, isTopField);
}
else
{
Scaler.DeinterlaceBob(buffer.Data, w, stride, isTopField);
}
break;
default:
Logger.Error?.Print(LogClass.Vic, $"Unsupported deinterlace mode \"{config.DeinterlaceMode}\".");
break;
}
prevBuffer.Return(rm.BufferPool);
nextBuffer.Return(rm.BufferPool);
}
return buffer;
}
private static uint GetOffset(ref PlaneOffsets offsets, int plane)
{
return plane switch
{
0 => offsets.LumaOffset,
1 => offsets.ChromaUOffset,
2 => offsets.ChromaVOffset,
_ => throw new ArgumentOutOfRangeException(nameof(plane))
};
}
private static RentedBuffer ReadBufferNoDeinterlace(
ResourceManager rm,
ref PlaneOffsets offsets,
bool linear,
int plane,
int width,
int height,
int bytesPerPixel,
int gobBlocksInY)
{
int stride = GetPitch(width, bytesPerPixel);
uint offset = GetOffset(ref offsets, plane);
if (linear)
{
return gmm.GetSpan(ExtendOffset(offset), stride * height);
return ReadBufferLinear(rm, offset, width, height, 0, stride, bytesPerPixel);
}
return ReadBuffer(gmm, offset, width, height, stride, bytesPerPixel, gobBlocksInY);
return ReadBufferBlockLinear(rm, offset, width, height, 0, stride, bytesPerPixel, gobBlocksInY);
}
private static ReadOnlySpan<byte> ReadBuffer(
MemoryManager gmm,
private static RentedBuffer ReadBufferLinear(
ResourceManager rm,
uint offset,
int width,
int height,
int dstStart,
int dstStride,
int bytesPerPixel)
{
int srcStride = GetPitch(width, bytesPerPixel);
int inSize = srcStride * height;
ReadOnlySpan<byte> src = rm.Gmm.GetSpan(ExtendOffset(offset), inSize);
int outSize = dstStride * height;
int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer);
Span<byte> dst = buffer;
dst = dst.Slice(0, outSize);
for (int y = 0; y < height; y++)
{
src.Slice(y * srcStride, srcStride).CopyTo(dst.Slice(dstStart + y * dstStride, srcStride));
}
return new RentedBuffer(dst, bufferIndex);
}
private static RentedBuffer ReadBufferBlockLinear(
ResourceManager rm,
uint offset,
int width,
int height,
int dstStart,
int dstStride,
int bytesPerPixel,
int gobBlocksInY)
{
int inSize = GetBlockLinearSize(width, height, bytesPerPixel, gobBlocksInY);
ReadOnlySpan<byte> src = gmm.GetSpan(ExtendOffset(offset), inSize);
ReadOnlySpan<byte> src = rm.Gmm.GetSpan(ExtendOffset(offset), inSize);
Span<byte> dst = new byte[dstStride * height];
int outSize = dstStride * height;
int bufferIndex = rm.BufferPool.RentMinimum(outSize, out byte[] buffer);
Span<byte> dst = buffer;
dst = dst.Slice(0, outSize);
LayoutConverter.ConvertBlockLinearToLinear(dst, width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
LayoutConverter.ConvertBlockLinearToLinear(dst.Slice(dstStart), width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
return dst;
return new RentedBuffer(dst, bufferIndex);
}
}
}

View File

@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Vic.Image
switch (config.OutPixelFormat)
{
case PixelFormat.A8B8G8R8:
case PixelFormat.X8B8G8R8:
case PixelFormat.X8B8G8R8:
WriteA8B8G8R8(rm, input, ref config, ref offsets);
break;
case PixelFormat.A8R8G8B8:
@ -433,7 +433,7 @@ namespace Ryujinx.Graphics.Vic.Image
{
if (linear)
{
rm.Gmm.Write(ExtendOffset(offset), src);
rm.Gmm.WriteMapped(ExtendOffset(offset), src);
return;
}
@ -456,7 +456,7 @@ namespace Ryujinx.Graphics.Vic.Image
LayoutConverter.ConvertLinearToBlockLinear(dst, width, height, dstStride, bytesPerPixel, gobBlocksInY, src);
rm.Gmm.Write(ExtendOffset(offset), dst);
rm.Gmm.WriteMapped(ExtendOffset(offset), dst);
rm.BufferPool.Return(dstIndex);
}

View File

@ -0,0 +1,124 @@
using System;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
namespace Ryujinx.Graphics.Vic
{
static class Scaler
{
public static void DeinterlaceWeave(Span<byte> data, ReadOnlySpan<byte> prevData, int width, int fieldSize, bool isTopField)
{
// Prev I Curr I Curr P
// TTTTTTTT BBBBBBBB TTTTTTTT
// -------- -------- BBBBBBBB
if (isTopField)
{
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
{
prevData.Slice(offset >> 1, width).CopyTo(data.Slice(offset + fieldSize, width));
}
}
else
{
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
{
prevData.Slice(offset >> 1, width).CopyTo(data.Slice(offset, width));
}
}
}
public static void DeinterlaceBob(Span<byte> data, int width, int fieldSize, bool isTopField)
{
// Curr I Curr P
// TTTTTTTT TTTTTTTT
// -------- TTTTTTTT
if (isTopField)
{
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
{
data.Slice(offset, width).CopyTo(data.Slice(offset + fieldSize, width));
}
}
else
{
for (int offset = 0; offset < data.Length; offset += fieldSize * 2)
{
data.Slice(offset + fieldSize, width).CopyTo(data.Slice(offset, width));
}
}
}
public unsafe static void DeinterlaceMotionAdaptive(
Span<byte> data,
ReadOnlySpan<byte> prevData,
ReadOnlySpan<byte> nextData,
int width,
int fieldSize,
bool isTopField)
{
// Very simple motion adaptive algorithm.
// If the pixel changed between previous and next frame, use Bob, otherwise use Weave.
//
// Example pseudo code:
// C_even = (P_even == N_even) ? P_even : C_odd
// Where: C is current frame, P is previous frame and N is next frame, and even/odd are the fields.
//
// Note: This does not fully match the hardware algorithm.
// The motion adaptive deinterlacing implemented on hardware is considerably more complex,
// and hard to implement accurately without proper documentation as for example, the
// method used for motion estimation is unknown.
int start = isTopField ? fieldSize : 0;
int otherFieldOffset = isTopField ? -fieldSize : fieldSize;
fixed (byte* pData = data, pPrevData = prevData, pNextData = nextData)
{
for (int offset = start; offset < data.Length; offset += fieldSize * 2)
{
int refOffset = (offset - start) >> 1;
int x = 0;
if (Avx2.IsSupported)
{
for (; x < (width & ~0x1f); x += 32)
{
Vector256<byte> prevPixels = Avx.LoadVector256(pPrevData + refOffset + x);
Vector256<byte> nextPixels = Avx.LoadVector256(pNextData + refOffset + x);
Vector256<byte> bob = Avx.LoadVector256(pData + offset + otherFieldOffset + x);
Vector256<byte> diff = Avx2.CompareEqual(prevPixels, nextPixels);
Avx.Store(pData + offset + x, Avx2.BlendVariable(bob, prevPixels, diff));
}
}
else if (Sse41.IsSupported)
{
for (; x < (width & ~0xf); x += 16)
{
Vector128<byte> prevPixels = Sse2.LoadVector128(pPrevData + refOffset + x);
Vector128<byte> nextPixels = Sse2.LoadVector128(pNextData + refOffset + x);
Vector128<byte> bob = Sse2.LoadVector128(pData + offset + otherFieldOffset + x);
Vector128<byte> diff = Sse2.CompareEqual(prevPixels, nextPixels);
Sse2.Store(pData + offset + x, Sse41.BlendVariable(bob, prevPixels, diff));
}
}
for (; x < width; x++)
{
byte prevPixel = prevData[refOffset + x];
byte nextPixel = nextData[refOffset + x];
if (nextPixel != prevPixel)
{
data[offset + x] = data[offset + otherFieldOffset + x];
}
else
{
data[offset + x] = prevPixel;
}
}
}
}
}
}
}

View File

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.Vic.Types
{
enum DeinterlaceMode
{
Weave,
BobField,
Bob,
NewBob,
Disi1,
WeaveLumaBobFieldChroma
}
}

View File

@ -0,0 +1,79 @@
namespace Ryujinx.Graphics.Vic.Types
{
enum FrameFormat
{
Progressive,
InterlacedTopFieldFirst,
InterlacedBottomFieldFirst,
TopField,
BottomField,
SubPicProgressive,
SubPicInterlacedTopFieldFirst,
SubPicInterlacedBottomFieldFirst,
SubPicTopField,
SubPicBottomField,
TopFieldChromaBottom,
BottomFieldChromaTop,
SubPicTopFieldChromaBottom,
SubPicBottomFieldChromaTop
}
static class FrameFormatExtensions
{
public static bool IsField(this FrameFormat frameFormat)
{
switch (frameFormat)
{
case FrameFormat.TopField:
case FrameFormat.BottomField:
case FrameFormat.SubPicTopField:
case FrameFormat.SubPicBottomField:
case FrameFormat.TopFieldChromaBottom:
case FrameFormat.BottomFieldChromaTop:
case FrameFormat.SubPicTopFieldChromaBottom:
case FrameFormat.SubPicBottomFieldChromaTop:
return true;
}
return false;
}
public static bool IsInterlaced(this FrameFormat frameFormat)
{
switch (frameFormat)
{
case FrameFormat.InterlacedTopFieldFirst:
case FrameFormat.InterlacedBottomFieldFirst:
case FrameFormat.SubPicInterlacedTopFieldFirst:
case FrameFormat.SubPicInterlacedBottomFieldFirst:
return true;
}
return false;
}
public static bool IsInterlacedBottomFirst(this FrameFormat frameFormat)
{
return frameFormat == FrameFormat.InterlacedBottomFieldFirst ||
frameFormat == FrameFormat.SubPicInterlacedBottomFieldFirst;
}
public static bool IsTopField(this FrameFormat frameFormat, bool isLuma)
{
switch (frameFormat)
{
case FrameFormat.TopField:
case FrameFormat.SubPicTopField:
return true;
case FrameFormat.TopFieldChromaBottom:
case FrameFormat.SubPicTopFieldChromaBottom:
return isLuma;
case FrameFormat.BottomFieldChromaTop:
case FrameFormat.SubPicBottomFieldChromaTop:
return !isLuma;
}
return false;
}
}
}

View File

@ -27,7 +27,7 @@
public bool PrevMotionFieldEnable => _word0.Extract(13);
public bool PpMotionFieldEnable => _word0.Extract(14);
public bool CombMotionFieldEnable => _word0.Extract(15);
public int FrameFormat => _word0.Extract(16, 4);
public FrameFormat FrameFormat => (FrameFormat)_word0.Extract(16, 4);
public int FilterLengthY => _word0.Extract(20, 2);
public int FilterLengthX => _word0.Extract(22, 2);
public int Panoramic => _word0.Extract(24, 12);
@ -36,7 +36,7 @@
public int FilterDetail => _word1.Extract(74, 10);
public int ChromaNoise => _word1.Extract(84, 10);
public int ChromaDetail => _word1.Extract(94, 10);
public int DeinterlaceMode => _word1.Extract(104, 4);
public DeinterlaceMode DeinterlaceMode => (DeinterlaceMode)_word1.Extract(104, 4);
public int MotionAccumWeight => _word1.Extract(108, 3);
public int NoiseIir => _word1.Extract(111, 11);
public int LightLevel => _word1.Extract(122, 4);

View File

@ -43,9 +43,9 @@ namespace Ryujinx.Graphics.Vic
continue;
}
var offsets = _state.State.SetSurfacexSlotx[i][0];
ref var offsets = ref _state.State.SetSurfacexSlotx[i];
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotSurfaceConfig, ref offsets);
using Surface src = SurfaceReader.Read(_rm, ref slot.SlotConfig, ref slot.SlotSurfaceConfig, ref offsets);
Blender.BlendOne(output, src, ref slot);
}

View File

@ -0,0 +1,8 @@
namespace Ryujinx.Graphics.Video
{
public enum FrameField
{
Progressive,
Interlaced
}
}

View File

@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Video
Plane UPlane { get; }
Plane VPlane { get; }
FrameField Field { get; }
int Width { get; }
int Height { get; }
int Stride { get; }

View File

@ -1,19 +0,0 @@
namespace Ryujinx.HLE.FileSystem.Content
{
static class ContentPath
{
public const string SystemContent = "@SystemContent";
public const string UserContent = "@UserContent";
public const string SdCardContent = "@SdCardContent";
public const string SdCard = "@SdCard";
public const string CalibFile = "@CalibFile";
public const string Safe = "@Safe";
public const string User = "@User";
public const string System = "@System";
public const string Host = "@Host";
public const string GamecardApp = "@GcApp";
public const string GamecardContents = "@GcS00000001";
public const string GamecardUpdate = "@upp";
public const string RegisteredUpdate = "@RegUpdate";
}
}

View File

@ -1,91 +0,0 @@
using System;
using System.IO;
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
namespace Ryujinx.HLE.FileSystem.Content
{
internal static class LocationHelper
{
public static string GetRealPath(VirtualFileSystem fileSystem, string switchContentPath)
{
string basePath = fileSystem.GetBasePath();
switch (switchContentPath)
{
case ContentPath.SystemContent:
return Path.Combine(basePath, SystemNandPath, "Contents");
case ContentPath.UserContent:
return Path.Combine(basePath, UserNandPath, "Contents");
case ContentPath.SdCardContent:
return Path.Combine(fileSystem.GetSdCardPath(), "Nintendo", "Contents");
case ContentPath.System:
return Path.Combine(basePath, SystemNandPath);
case ContentPath.User:
return Path.Combine(basePath, UserNandPath);
default:
throw new NotSupportedException($"Content Path `{switchContentPath}` is not supported.");
}
}
public static string GetContentPath(ContentStorageId contentStorageId)
{
switch (contentStorageId)
{
case ContentStorageId.NandSystem:
return ContentPath.SystemContent;
case ContentStorageId.NandUser:
return ContentPath.UserContent;
case ContentStorageId.SdCard:
return ContentPath.SdCardContent;
default:
throw new NotSupportedException($"Content Storage `{contentStorageId}` is not supported.");
}
}
public static string GetContentRoot(StorageId storageId)
{
switch (storageId)
{
case StorageId.NandSystem:
return ContentPath.SystemContent;
case StorageId.NandUser:
return ContentPath.UserContent;
case StorageId.SdCard:
return ContentPath.SdCardContent;
default:
throw new NotSupportedException($"Storage Id `{storageId}` is not supported.");
}
}
public static StorageId GetStorageId(string contentPathString)
{
string cleanedPath = contentPathString.Split(':')[0];
switch (cleanedPath)
{
case ContentPath.SystemContent:
case ContentPath.System:
return StorageId.NandSystem;
case ContentPath.UserContent:
case ContentPath.User:
return StorageId.NandUser;
case ContentPath.SdCardContent:
return StorageId.SdCard;
case ContentPath.Host:
return StorageId.Host;
case ContentPath.GamecardApp:
case ContentPath.GamecardContents:
case ContentPath.GamecardUpdate:
return StorageId.GameCard;
default:
return StorageId.None;
}
}
}
}

View File

@ -1,9 +0,0 @@
namespace Ryujinx.HLE.FileSystem.Content
{
public enum ContentStorageId
{
NandSystem,
NandUser,
SdCard
}
}

View File

@ -1,15 +0,0 @@
namespace Ryujinx.HLE.FileSystem.Content
{
enum TitleType
{
SystemPrograms = 0x01,
SystemDataArchive = 0x02,
SystemUpdate = 0x03,
FirmwarePackageA = 0x04,
FirmwarePackageB = 0x05,
RegularApplication = 0x80,
Update = 0x81,
AddOnContent = 0x82,
DeltaTitle = 0x83
}
}

View File

@ -20,7 +20,7 @@ using System.IO.Compression;
using System.Linq;
using Path = System.IO.Path;
namespace Ryujinx.HLE.FileSystem.Content
namespace Ryujinx.HLE.FileSystem
{
public class ContentManager
{
@ -110,8 +110,8 @@ namespace Ryujinx.HLE.FileSystem.Content
try
{
contentPathString = LocationHelper.GetContentRoot(storageId);
contentDirectory = LocationHelper.GetRealPath(_virtualFileSystem, contentPathString);
contentPathString = ContentPath.GetContentPath(storageId);
contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString);
registeredDirectory = Path.Combine(contentDirectory, "registered");
}
catch (NotSupportedException)
@ -367,8 +367,7 @@ namespace Ryujinx.HLE.FileSystem.Content
{
LocationEntry locationEntry = GetLocation(titleId, contentType, storageId);
return locationEntry.ContentPath != null ?
LocationHelper.GetStorageId(locationEntry.ContentPath) : StorageId.None;
return locationEntry.ContentPath != null ? ContentPath.GetStorageId(locationEntry.ContentPath) : StorageId.None;
}
}
@ -493,8 +492,8 @@ namespace Ryujinx.HLE.FileSystem.Content
public void InstallFirmware(string firmwareSource)
{
string contentPathString = LocationHelper.GetContentRoot(StorageId.NandSystem);
string contentDirectory = LocationHelper.GetRealPath(_virtualFileSystem, contentPathString);
string contentPathString = ContentPath.GetContentPath(StorageId.BuiltInSystem);
string contentDirectory = ContentPath.GetRealPath(_virtualFileSystem, contentPathString);
string registeredDirectory = Path.Combine(contentDirectory, "registered");
string temporaryDirectory = Path.Combine(contentDirectory, "temp");
@ -998,9 +997,9 @@ namespace Ryujinx.HLE.FileSystem.Content
foreach (var entry in updateNcas)
{
foreach (var nca in entry.Value)
foreach (var (type, path) in entry.Value)
{
extraNcas += nca.path + Environment.NewLine;
extraNcas += path + Environment.NewLine;
}
}
@ -1019,7 +1018,7 @@ namespace Ryujinx.HLE.FileSystem.Content
lock (_lock)
{
var locationEnties = _locationEntries[StorageId.NandSystem];
var locationEnties = _locationEntries[StorageId.BuiltInSystem];
foreach (var entry in locationEnties)
{

View File

@ -0,0 +1,82 @@
using LibHac.Fs;
using LibHac.Ncm;
using Ryujinx.Common.Configuration;
using System;
using static Ryujinx.HLE.FileSystem.VirtualFileSystem;
using Path = System.IO.Path;
namespace Ryujinx.HLE.FileSystem
{
internal static class ContentPath
{
public const string SystemContent = "@SystemContent";
public const string UserContent = "@UserContent";
public const string SdCardContent = "@SdCardContent";
public const string SdCard = "@Sdcard";
public const string CalibFile = "@CalibFile";
public const string Safe = "@Safe";
public const string User = "@User";
public const string System = "@System";
public const string Host = "@Host";
public const string GamecardApp = "@GcApp";
public const string GamecardContents = "@GcS00000001";
public const string GamecardUpdate = "@upp";
public const string RegisteredUpdate = "@RegUpdate";
public const string Nintendo = "Nintendo";
public const string Contents = "Contents";
public static string GetRealPath(VirtualFileSystem fileSystem, string switchContentPath)
{
return switchContentPath switch
{
SystemContent => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath, Contents),
UserContent => Path.Combine(AppDataManager.BaseDirPath, UserNandPath, Contents),
SdCardContent => Path.Combine(fileSystem.GetSdCardPath(), Nintendo, Contents),
System => Path.Combine(AppDataManager.BaseDirPath, SystemNandPath),
User => Path.Combine(AppDataManager.BaseDirPath, UserNandPath),
_ => throw new NotSupportedException($"Content Path \"`{switchContentPath}`\" is not supported.")
};
}
public static string GetContentPath(ContentStorageId contentStorageId)
{
return contentStorageId switch
{
ContentStorageId.System => SystemContent,
ContentStorageId.User => UserContent,
ContentStorageId.SdCard => SdCardContent,
_ => throw new NotSupportedException($"Content Storage Id \"`{contentStorageId}`\" is not supported.")
};
}
public static string GetContentPath(StorageId storageId)
{
return storageId switch
{
StorageId.BuiltInSystem => SystemContent,
StorageId.BuiltInUser => UserContent,
StorageId.SdCard => SdCardContent,
_ => throw new NotSupportedException($"Storage Id \"`{storageId}`\" is not supported.")
};
}
public static StorageId GetStorageId(string contentPathString)
{
return contentPathString.Split(':')[0] switch
{
SystemContent or
System => StorageId.BuiltInSystem,
UserContent or
User => StorageId.BuiltInUser,
SdCardContent => StorageId.SdCard,
Host => StorageId.Host,
GamecardApp or
GamecardContents or
GamecardUpdate => StorageId.GameCard,
_ => StorageId.None
};
}
}
}

View File

@ -8,7 +8,6 @@ namespace Ryujinx.HLE.FileSystem
{
public class EncryptedFileSystemCreator : IEncryptedFileSystemCreator
{
public Result Create(ref SharedRef<IFileSystem> outEncryptedFileSystem,
ref SharedRef<IFileSystem> baseFileSystem, IEncryptedFileSystemCreator.KeyId idIndex,
in EncryptionSeed encryptionSeed)
@ -18,10 +17,10 @@ namespace Ryujinx.HLE.FileSystem
return ResultFs.InvalidArgument.Log();
}
// Todo: Reenable when AesXtsFileSystem is fixed
// TODO: Reenable when AesXtsFileSystem is fixed.
outEncryptedFileSystem = SharedRef<IFileSystem>.CreateMove(ref baseFileSystem);
return Result.Success;
}
}
}
}

View File

@ -1,6 +1,6 @@
using LibHac.FsSystem;
namespace Ryujinx.HLE.FileSystem.Content
namespace Ryujinx.HLE.FileSystem
{
public struct LocationEntry
{

View File

@ -1,12 +0,0 @@
namespace Ryujinx.HLE.FileSystem
{
enum SaveDataType : byte
{
SystemSaveData,
SaveData,
BcatDeliveryCacheStorage,
DeviceSaveData,
TemporaryStorage,
CacheStorage
}
}

View File

@ -1,27 +0,0 @@
using Ryujinx.HLE.HOS.Services.Account.Acc;
namespace Ryujinx.HLE.FileSystem
{
struct SaveInfo
{
public ulong TitleId { get; private set; }
public long SaveId { get; private set; }
public SaveDataType SaveDataType { get; private set; }
public SaveSpaceId SaveSpaceId { get; private set; }
public UserId UserId { get; private set; }
public SaveInfo(
ulong titleId,
long saveId,
SaveDataType saveDataType,
SaveSpaceId saveSpaceId,
UserId userId = new UserId())
{
TitleId = titleId;
SaveId = saveId;
SaveDataType = saveDataType;
SaveSpaceId = saveSpaceId;
UserId = userId;
}
}
}

View File

@ -1,10 +0,0 @@
namespace Ryujinx.HLE.FileSystem
{
enum SaveSpaceId
{
NandSystem,
NandUser,
SdCard,
TemporaryStorage
}
}

View File

@ -1,12 +0,0 @@
namespace Ryujinx.HLE.FileSystem
{
public enum StorageId
{
None,
Host,
GameCard,
NandSystem,
NandUser,
SdCard
}
}

View File

@ -1,8 +1,7 @@
using Ryujinx.HLE.Utilities;
using System.IO;
using System.Text;
namespace Ryujinx.HLE.FileSystem.Content
namespace Ryujinx.HLE.FileSystem
{
public class SystemVersion
{

View File

@ -13,7 +13,6 @@ using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
using System;
using System.Buffers.Text;
@ -28,20 +27,29 @@ namespace Ryujinx.HLE.FileSystem
{
public class VirtualFileSystem : IDisposable
{
public const string NandPath = AppDataManager.DefaultNandDir;
public const string SdCardPath = AppDataManager.DefaultSdcardDir;
public static string SafeNandPath = Path.Combine(AppDataManager.DefaultNandDir, "safe");
public static string SystemNandPath = Path.Combine(AppDataManager.DefaultNandDir, "system");
public static string UserNandPath = Path.Combine(AppDataManager.DefaultNandDir, "user");
public static string SafeNandPath = Path.Combine(NandPath, "safe");
public static string SystemNandPath = Path.Combine(NandPath, "system");
public static string UserNandPath = Path.Combine(NandPath, "user");
public KeySet KeySet { get; private set; }
public EmulatedGameCard GameCard { get; private set; }
public EmulatedSdCard SdCard { get; private set; }
public ModLoader ModLoader { get; private set; }
public Stream RomFs { get; private set; }
private static bool _isInitialized = false;
public KeySet KeySet { get; private set; }
public EmulatedGameCard GameCard { get; private set; }
public EmulatedSdCard SdCard { get; private set; }
public static VirtualFileSystem CreateInstance()
{
if (_isInitialized)
{
throw new InvalidOperationException("VirtualFileSystem can only be instantiated once!");
}
public ModLoader ModLoader { get; private set; }
_isInitialized = true;
return new VirtualFileSystem();
}
private VirtualFileSystem()
{
@ -49,8 +57,6 @@ namespace Ryujinx.HLE.FileSystem
ModLoader = new ModLoader(); // Should only be created once
}
public Stream RomFs { get; private set; }
public void LoadRomFs(string fileName)
{
RomFs = new FileStream(fileName, FileMode.Open, FileAccess.Read);
@ -79,7 +85,7 @@ namespace Ryujinx.HLE.FileSystem
string fullPath = Path.GetFullPath(Path.Combine(basePath, fileName));
if (!fullPath.StartsWith(GetBasePath()))
if (!fullPath.StartsWith(AppDataManager.BaseDirPath))
{
return null;
}
@ -87,14 +93,8 @@ namespace Ryujinx.HLE.FileSystem
return fullPath;
}
internal string GetBasePath() => AppDataManager.BaseDirPath;
internal string GetSdCardPath() => MakeFullPath(SdCardPath);
public string GetNandPath() => MakeFullPath(NandPath);
public string GetFullPartitionPath(string partitionPath)
{
return MakeFullPath(partitionPath);
}
internal string GetSdCardPath() => MakeFullPath(AppDataManager.DefaultSdcardDir);
public string GetNandPath() => MakeFullPath(AppDataManager.DefaultNandDir);
public string SwitchPathToSystemPath(string switchPath)
{
@ -110,7 +110,7 @@ namespace Ryujinx.HLE.FileSystem
public string SystemPathToSwitchPath(string systemPath)
{
string baseSystemPath = GetBasePath() + Path.DirectorySeparatorChar;
string baseSystemPath = AppDataManager.BaseDirPath + Path.DirectorySeparatorChar;
if (systemPath.StartsWith(baseSystemPath))
{
@ -136,8 +136,7 @@ namespace Ryujinx.HLE.FileSystem
switch (path)
{
case ContentPath.SdCard:
case "@Sdcard":
path = SdCardPath;
path = AppDataManager.DefaultSdcardDir;
break;
case ContentPath.User:
path = UserNandPath;
@ -146,7 +145,7 @@ namespace Ryujinx.HLE.FileSystem
path = SystemNandPath;
break;
case ContentPath.SdCardContent:
path = Path.Combine(SdCardPath, "Nintendo", "Contents");
path = Path.Combine(AppDataManager.DefaultSdcardDir, "Nintendo", "Contents");
break;
case ContentPath.UserContent:
path = Path.Combine(UserNandPath, "Contents");
@ -156,27 +155,19 @@ namespace Ryujinx.HLE.FileSystem
break;
}
string fullPath = Path.Combine(GetBasePath(), path);
string fullPath = Path.Combine(AppDataManager.BaseDirPath, path);
if (isDirectory)
if (isDirectory && !Directory.Exists(fullPath))
{
if (!Directory.Exists(fullPath))
{
Directory.CreateDirectory(fullPath);
}
Directory.CreateDirectory(fullPath);
}
return fullPath;
}
public DriveInfo GetDrive()
{
return new DriveInfo(Path.GetPathRoot(GetBasePath()));
}
public void InitializeFsServer(LibHac.Horizon horizon, out HorizonClient fsServerClient)
{
LocalFileSystem serverBaseFs = new LocalFileSystem(GetBasePath());
LocalFileSystem serverBaseFs = new LocalFileSystem(AppDataManager.BaseDirPath);
fsServerClient = horizon.CreatePrivilegedHorizonClient();
var fsServer = new FileSystemServer(fsServerClient);
@ -505,7 +496,7 @@ namespace Ryujinx.HLE.FileSystem
bool canFixBySaveDataId = extraData.Attribute.StaticSaveDataId == 0 && info.StaticSaveDataId != 0;
bool hasEmptyOwnerId = extraData.OwnerId == 0 && info.Type != LibHac.Fs.SaveDataType.System;
bool hasEmptyOwnerId = extraData.OwnerId == 0 && info.Type != SaveDataType.System;
if (!canFixByProgramId && !canFixBySaveDataId && !hasEmptyOwnerId)
{
@ -523,7 +514,7 @@ namespace Ryujinx.HLE.FileSystem
// The rest of the extra data can't be created from the save data info.
// On user saves the owner ID will almost certainly be the same as the program ID.
if (info.Type != LibHac.Fs.SaveDataType.System)
if (info.Type != SaveDataType.System)
{
extraData.OwnerId = info.ProgramId.Value;
}
@ -580,11 +571,6 @@ namespace Ryujinx.HLE.FileSystem
}
};
public void Unload()
{
RomFs?.Dispose();
}
public void Dispose()
{
Dispose(true);
@ -594,20 +580,8 @@ namespace Ryujinx.HLE.FileSystem
{
if (disposing)
{
Unload();
RomFs?.Dispose();
}
}
public static VirtualFileSystem CreateInstance()
{
if (_isInitialized)
{
throw new InvalidOperationException("VirtualFileSystem can only be instantiated once!");
}
_isInitialized = true;
return new VirtualFileSystem();
}
}
}

View File

@ -3,7 +3,6 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Common.Configuration;
using Ryujinx.Graphics.GAL;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState;

View File

@ -2,10 +2,10 @@
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.SystemState;
using System;
@ -106,7 +106,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
private string GetMessageText(uint module, uint description, string key)
{
string binaryTitleContentPath = _horizon.ContentManager.GetInstalledContentPath(ErrorMessageBinaryTitleId, StorageId.NandSystem, NcaContentType.Data);
string binaryTitleContentPath = _horizon.ContentManager.GetInstalledContentPath(ErrorMessageBinaryTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
using (LibHac.Fs.IStorage ncaFileStream = new LocalStorage(_horizon.Device.FileSystem.SwitchPathToSystemPath(binaryTitleContentPath), FileAccess.Read, FileMode.Open))
{

View File

@ -10,7 +10,7 @@ using Ryujinx.Audio.Integration;
using Ryujinx.Audio.Output;
using Ryujinx.Audio.Renderer.Device;
using Ryujinx.Audio.Renderer.Server;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Process;
@ -238,6 +238,7 @@ namespace Ryujinx.HLE.HOS
SurfaceFlinger = new SurfaceFlinger(device);
InitializeAudioRenderer();
InitializeServices();
}
private void InitializeAudioRenderer()
@ -288,7 +289,7 @@ namespace Ryujinx.HLE.HOS
AudioManager.Start();
}
public void InitializeServices()
private void InitializeServices()
{
SmServer = new ServerBase(KernelContext, "SmServer", () => new IUserInterface(KernelContext));

View File

@ -751,7 +751,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
KThread currentThread = KernelStatic.GetCurrentThread();
if (currentThread.Owner != null &&
if (currentThread.Context.Running &&
currentThread.Owner != null &&
currentThread.GetUserDisableCount() != 0 &&
currentThread.Owner.PinnedThreads[currentThread.CurrentCore] == null)
{

View File

@ -658,10 +658,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private static uint GetPsr(ARMeilleure.State.ExecutionContext context)
{
return (context.GetPstateFlag(ARMeilleure.State.PState.NFlag) ? (1U << (int)ARMeilleure.State.PState.NFlag) : 0U) |
(context.GetPstateFlag(ARMeilleure.State.PState.ZFlag) ? (1U << (int)ARMeilleure.State.PState.ZFlag) : 0U) |
(context.GetPstateFlag(ARMeilleure.State.PState.CFlag) ? (1U << (int)ARMeilleure.State.PState.CFlag) : 0U) |
(context.GetPstateFlag(ARMeilleure.State.PState.VFlag) ? (1U << (int)ARMeilleure.State.PState.VFlag) : 0U);
return context.Pstate & 0xFF0FFE20;
}
private ThreadContext GetCurrentContext()
@ -1371,7 +1368,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
PreferredCore = _originalPreferredCore;
AffinityMask = _originalAffinityMask;
if (AffinityMask != affinityMask)
{
if ((AffinityMask & 1UL << ActiveCore) != 0)

View File

@ -1,4 +1,4 @@
using Ryujinx.HLE.FileSystem;
using LibHac.Ncm;
namespace Ryujinx.HLE.HOS.Services.Arp
{
@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp
{
TitleId = 0x00,
Version = 0x00,
BaseGameStorageId = (byte)StorageId.NandSystem,
BaseGameStorageId = (byte)StorageId.BuiltInSystem,
UpdateGameStorageId = (byte)StorageId.None
};
}
@ -35,7 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.Arp
{
TitleId = context.Device.Application.TitleId,
Version = 0x00,
BaseGameStorageId = (byte)StorageId.NandSystem,
BaseGameStorageId = (byte)StorageId.BuiltInSystem,
UpdateGameStorageId = (byte)StorageId.None
};
}

View File

@ -2,7 +2,6 @@ using LibHac;
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Shim;
using LibHac.FsSrv;
using LibHac.FsSrv.Impl;
using LibHac.FsSystem;
using LibHac.Ncm;
@ -19,7 +18,6 @@ using static Ryujinx.HLE.Utilities.StringUtils;
using IFileSystem = LibHac.FsSrv.Sf.IFileSystem;
using IStorage = LibHac.FsSrv.Sf.IStorage;
using RightsId = LibHac.Fs.RightsId;
using StorageId = Ryujinx.HLE.FileSystem.StorageId;
namespace Ryujinx.HLE.HOS.Services.Fs
{

View File

@ -55,9 +55,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
_storage = storage;
SharedMemory = SharedMemory.Create();
InitDevices();
}
public void InitDevices()
private void InitDevices()
{
DebugPad = new DebugPadDevice(_device, true);
Touchscreen = new TouchDevice(_device, true);

View File

@ -1,4 +1,4 @@
using Ryujinx.HLE.FileSystem;
using LibHac.Ncm;
using Ryujinx.HLE.HOS.Services.Ncm.Lr.LocationResolverManager;
namespace Ryujinx.HLE.HOS.Services.Ncm.Lr

View File

@ -1,6 +1,6 @@
using LibHac.FsSystem;
using LibHac.Ncm;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using System.Text;
using static Ryujinx.HLE.Utilities.StringUtils;

View File

@ -14,6 +14,9 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
{
private GeneralServiceDetail _generalServiceDetail;
private IPInterfaceProperties _targetPropertiesCache = null;
private UnicastIPAddressInformation _targetAddressInfoCache = null;
public IGeneralService()
{
_generalServiceDetail = new GeneralServiceDetail
@ -22,6 +25,8 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
IsAnyInternetRequestAccepted = true // NOTE: Why not accept any internet request?
};
NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(LocalInterfaceCacheHandler);
GeneralServiceManager.Add(_generalServiceDetail);
}
@ -165,6 +170,11 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
return (null, null);
}
if (_targetPropertiesCache != null && _targetAddressInfoCache != null)
{
return (_targetPropertiesCache, _targetAddressInfoCache);
}
IPInterfaceProperties targetProperties = null;
UnicastIPAddressInformation targetAddressInfo = null;
@ -194,13 +204,26 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService
}
}
_targetPropertiesCache = targetProperties;
_targetAddressInfoCache = targetAddressInfo;
return (targetProperties, targetAddressInfo);
}
private void LocalInterfaceCacheHandler(object sender, EventArgs e)
{
Logger.Info?.Print(LogClass.ServiceNifm, $"NetworkAddress changed, invalidating cached data.");
_targetPropertiesCache = null;
_targetAddressInfoCache = null;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
NetworkChange.NetworkAddressChanged -= LocalInterfaceCacheHandler;
GeneralServiceManager.Remove(_generalServiceDetail.ClientId);
}
}

View File

@ -1,5 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using LibHac.Ncm;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Arp;
using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface;

View File

@ -1,8 +1,24 @@
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService;
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc
{
[Service("ntc")]
class IStaticService : IpcService
{
public IStaticService(ServiceCtx context) { }
[CommandHipc(0)]
// OpenEnsureNetworkClockAvailabilityService(u64) -> object<nn::ntc::detail::service::IEnsureNetworkClockAvailabilityService>
public ResultCode CreateAsyncInterface(ServiceCtx context)
{
ulong unknown = context.RequestData.ReadUInt64();
MakeObject(context, new IEnsureNetworkClockAvailabilityService(context));
Logger.Stub?.PrintStub(LogClass.ServiceNtc, new { unknown });
return ResultCode.Success;
}
}
}

View File

@ -0,0 +1,77 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Threading;
using System;
namespace Ryujinx.HLE.HOS.Services.Nim.Ntc.StaticService
{
class IEnsureNetworkClockAvailabilityService : IpcService
{
private KEvent _finishNotificationEvent;
private ResultCode _taskResultCode;
public IEnsureNetworkClockAvailabilityService(ServiceCtx context)
{
_finishNotificationEvent = new KEvent(context.Device.System.KernelContext);
_taskResultCode = ResultCode.Success;
// NOTE: The service starts a thread that polls Nintendo NTP server and syncs the time with it.
// Additionnally it gets and uses some settings too:
// autonomic_correction_interval_seconds, autonomic_correction_failed_retry_interval_seconds,
// autonomic_correction_immediate_try_count_max, autonomic_correction_immediate_try_interval_milliseconds
}
[CommandHipc(0)]
// StartTask()
public ResultCode StartTask(ServiceCtx context)
{
if (!context.Device.Configuration.EnableInternetAccess)
{
return (ResultCode)Time.ResultCode.NetworkTimeNotAvailable;
}
// NOTE: Since we don't support the Nintendo NTP server, we can signal the event now to confirm the update task is done.
_finishNotificationEvent.ReadableEvent.Signal();
Logger.Stub?.PrintStub(LogClass.ServiceNtc);
return ResultCode.Success;
}
[CommandHipc(1)]
// GetFinishNotificationEvent() -> handle<copy>
public ResultCode GetFinishNotificationEvent(ServiceCtx context)
{
if (context.Process.HandleTable.GenerateHandle(_finishNotificationEvent.ReadableEvent, out int finishNotificationEventHandle) != KernelResult.Success)
{
throw new InvalidOperationException("Out of handles!");
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(finishNotificationEventHandle);
return ResultCode.Success;
}
[CommandHipc(2)]
// GetResult()
public ResultCode GetResult(ServiceCtx context)
{
return _taskResultCode;
}
[CommandHipc(3)]
// Cancel()
public ResultCode Cancel(ServiceCtx context)
{
// NOTE: The update task should be canceled here.
_finishNotificationEvent.ReadableEvent.Signal();
_taskResultCode = (ResultCode)Time.ResultCode.NetworkTimeTaskCanceled;
Logger.Stub?.PrintStub(LogClass.ServiceNtc);
return ResultCode.Success;
}
}
}

View File

@ -232,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
ulong virtualAddress = arguments.Offset + arguments.BufferOffset;
physicalAddress += arguments.BufferOffset;
_asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize);
_asContext.Gmm.Map(physicalAddress, virtualAddress, arguments.MappingSize, (PteKind)arguments.Kind);
return NvInternalResult.Success;
}
@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
{
if (_asContext.ValidateFixedBuffer(arguments.Offset, size, pageSize))
{
_asContext.Gmm.Map(physicalAddress, arguments.Offset, size);
_asContext.Gmm.Map(physicalAddress, arguments.Offset, size, (PteKind)arguments.Kind);
}
else
{
@ -301,7 +301,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
_memoryAllocator.AllocateRange(va, size, freeAddressStartPosition);
}
_asContext.Gmm.Map(physicalAddress, va, size);
_asContext.Gmm.Map(physicalAddress, va, size, (PteKind)arguments.Kind);
arguments.Offset = va;
}
@ -366,26 +366,30 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu
for (int index = 0; index < arguments.Length; index++)
{
ulong mapOffs = (ulong)arguments[index].MapOffset << 16;
ulong gpuVa = (ulong)arguments[index].GpuOffset << 16;
ulong size = (ulong)arguments[index].Pages << 16;
ref RemapArguments argument = ref arguments[index];
ulong gpuVa = (ulong)argument.GpuOffset << 16;
ulong size = (ulong)argument.Pages << 16;
int nvmapHandle = argument.NvMapHandle;
if (arguments[index].NvMapHandle == 0)
if (nvmapHandle == 0)
{
gmm.Unmap(gpuVa, size);
}
else
{
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments[index].NvMapHandle);
ulong mapOffs = (ulong)argument.MapOffset << 16;
PteKind kind = (PteKind)argument.Kind;
NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, nvmapHandle);
if (map == null)
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{arguments[index].NvMapHandle:x8}!");
Logger.Warning?.Print(LogClass.ServiceNv, $"Invalid NvMap handle 0x{nvmapHandle:x8}!");
return NvInternalResult.InvalidInput;
}
gmm.Map(mapOffs + map.Address, gpuVa, size);
gmm.Map(mapOffs + map.Address, gpuVa, size, kind);
}
}

View File

@ -251,7 +251,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel
if (va != NvMemoryAllocator.PteUnmapped && va <= uint.MaxValue && (va + (uint)map.Size) <= uint.MaxValue)
{
_host1xContext.MemoryAllocator.AllocateRange(va, (uint)map.Size, freeAddressStartPosition);
_host1xContext.Smmu.Map(map.Address, va, (uint)map.Size);
_host1xContext.Smmu.Map(map.Address, va, (uint)map.Size, PteKind.Pitch); // FIXME: This should not use the GMMU.
map.DmaMapAddress = va;
}
else

View File

@ -1,13 +1,15 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Olsc
{
[Service("olsc:u")] // 10.0.0+
class IOlscServiceForApplication : IpcService
{
private bool _initialized;
private bool _initialized;
private Dictionary<UserId, bool> _saveDataBackupSettingDatabase;
public IOlscServiceForApplication(ServiceCtx context) { }
@ -16,7 +18,9 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
public ResultCode Initialize(ServiceCtx context)
{
// NOTE: Service call arp:r GetApplicationInstanceUnregistrationNotifier with the pid and initialize some internal struct.
// Since we will not support online savedata backup. It's fine to stub it for now.
// Since we will not support online savedata backup, it's fine to stub it for now.
_saveDataBackupSettingDatabase = new Dictionary<UserId, bool>();
_initialized = true;
@ -25,12 +29,11 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
return ResultCode.Success;
}
[CommandHipc(14)]
// SetSaveDataBackupSettingEnabled(nn::account::Uid, bool)
public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context)
[CommandHipc(13)]
// GetSaveDataBackupSetting(nn::account::Uid) -> u8
public ResultCode GetSaveDataBackupSetting(ServiceCtx context)
{
UserId userId = context.RequestData.ReadStruct<UserId>();
ulong saveDataBackupSettingEnabled = context.RequestData.ReadUInt64();
UserId userId = context.RequestData.ReadStruct<UserId>();
if (!_initialized)
{
@ -42,8 +45,42 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
return ResultCode.NullArgument;
}
// NOTE: Service store the UserId and the boolean in an internal SaveDataBackupSettingDatabase object.
// Since we will not support online savedata backup. It's fine to stub it for now.
if (_saveDataBackupSettingDatabase.TryGetValue(userId, out bool enabled) && enabled)
{
context.ResponseData.Write((byte)1); // TODO: Determine value.
}
else
{
context.ResponseData.Write((byte)2); // TODO: Determine value.
}
// NOTE: Since we will not support online savedata backup, it's fine to stub it for now.
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId });
return ResultCode.Success;
}
[CommandHipc(14)]
// SetSaveDataBackupSettingEnabled(nn::account::Uid, bool)
public ResultCode SetSaveDataBackupSettingEnabled(ServiceCtx context)
{
bool saveDataBackupSettingEnabled = context.RequestData.ReadUInt64() != 0;
UserId userId = context.RequestData.ReadStruct<UserId>();
if (!_initialized)
{
return ResultCode.NotInitialized;
}
if (userId.IsNull)
{
return ResultCode.NullArgument;
}
_saveDataBackupSettingDatabase[userId] = saveDataBackupSettingEnabled;
// NOTE: Since we will not support online savedata backup, it's fine to stub it for now.
Logger.Stub?.PrintStub(LogClass.ServiceOlsc, new { userId, saveDataBackupSettingEnabled });

View File

@ -2,11 +2,11 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Services.Sdb.Pl.Types;
using System;
@ -63,7 +63,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
{
if (contentManager.TryGetFontTitle(name, out ulong fontTitle) && contentManager.TryGetFontFilename(name, out string fontFilename))
{
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.NandSystem, NcaContentType.Data);
string contentPath = contentManager.GetInstalledContentPath(fontTitle, StorageId.BuiltInSystem, NcaContentType.Data);
string fontPath = _device.FileSystem.SwitchPathToSystemPath(contentPath);
if (!string.IsNullOrWhiteSpace(fontPath))

View File

@ -3,9 +3,9 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Utilities;
using System;
@ -290,7 +290,7 @@ namespace Ryujinx.HLE.HOS.Services.Settings
{
const ulong SystemVersionTitleId = 0x0100000000000809;
string contentPath = device.System.ContentManager.GetInstalledContentPath(SystemVersionTitleId, StorageId.NandSystem, NcaContentType.Data);
string contentPath = device.System.ContentManager.GetInstalledContentPath(SystemVersionTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
if (string.IsNullOrWhiteSpace(contentPath))
{

View File

@ -1,5 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Services.Spl.Types;

View File

@ -3,13 +3,13 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS.Services.Ssl.Types;
using System;
using System.Collections.Generic;
@ -82,7 +82,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl
public string GetCertStoreTitleContentPath()
{
return _contentManager.GetInstalledContentPath(CertStoreTitleId, StorageId.NandSystem, NcaContentType.Data);
return _contentManager.GetInstalledContentPath(CertStoreTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
}
public bool HasCertStoreTitle()

View File

@ -7,16 +7,18 @@
Success = 0,
TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId,
PermissionDenied = (1 << ErrorCodeShift) | ModuleId,
TimeMismatch = (102 << ErrorCodeShift) | ModuleId,
UninitializedClock = (103 << ErrorCodeShift) | ModuleId,
TimeNotFound = (200 << ErrorCodeShift) | ModuleId,
Overflow = (201 << ErrorCodeShift) | ModuleId,
LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId,
OutOfRange = (902 << ErrorCodeShift) | ModuleId,
TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId,
TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId,
NotImplemented = (990 << ErrorCodeShift) | ModuleId,
TimeServiceNotInitialized = (0 << ErrorCodeShift) | ModuleId,
PermissionDenied = (1 << ErrorCodeShift) | ModuleId,
TimeMismatch = (102 << ErrorCodeShift) | ModuleId,
UninitializedClock = (103 << ErrorCodeShift) | ModuleId,
TimeNotFound = (200 << ErrorCodeShift) | ModuleId,
Overflow = (201 << ErrorCodeShift) | ModuleId,
LocationNameTooLong = (801 << ErrorCodeShift) | ModuleId,
OutOfRange = (902 << ErrorCodeShift) | ModuleId,
TimeZoneConversionFailed = (903 << ErrorCodeShift) | ModuleId,
TimeZoneNotFound = (989 << ErrorCodeShift) | ModuleId,
NotImplemented = (990 << ErrorCodeShift) | ModuleId,
NetworkTimeNotAvailable = (1000 << ErrorCodeShift) | ModuleId,
NetworkTimeTaskCanceled = (1003 << ErrorCodeShift) | ModuleId,
}
}

View File

@ -3,12 +3,12 @@ using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS.Services.Time.Clock;
using Ryujinx.HLE.Utilities;
using System;
@ -241,7 +241,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone
public string GetTimeZoneBinaryTitleContentPath()
{
return _contentManager.GetInstalledContentPath(TimeZoneBinaryTitleId, StorageId.NandSystem, NcaContentType.Data);
return _contentManager.GetInstalledContentPath(TimeZoneBinaryTitleId, StorageId.BuiltInSystem, NcaContentType.Data);
}
public bool HasTimeZoneBinaryTitle()

View File

@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
if (serviceType != ViServiceType.Application)
{
return ResultCode.InvalidRange;
return ResultCode.PermissionDenied;
}
MakeObject(context, new IApplicationDisplayService(serviceType));

View File

@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
if (serviceType != ViServiceType.Manager)
{
return ResultCode.InvalidRange;
return ResultCode.PermissionDenied;
}
MakeObject(context, new IApplicationDisplayService(serviceType));

View File

@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
if (serviceType != ViServiceType.System)
{
return ResultCode.InvalidRange;
return ResultCode.PermissionDenied;
}
MakeObject(context, new IApplicationDisplayService(serviceType));

View File

@ -9,7 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi
InvalidArguments = (1 << ErrorCodeShift) | ModuleId,
InvalidLayerSize = (4 << ErrorCodeShift) | ModuleId,
InvalidRange = (5 << ErrorCodeShift) | ModuleId,
PermissionDenied = (5 << ErrorCodeShift) | ModuleId,
InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId,
InvalidValue = (7 << ErrorCodeShift) | ModuleId,
AlreadyOpened = (9 << ErrorCodeShift) | ModuleId

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Cpu;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Common;
@ -22,16 +21,21 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
{
private readonly ViServiceType _serviceType;
private readonly List<DisplayInfo> _displayInfo;
private readonly Dictionary<ulong, DisplayInfo> _openDisplayInfo;
private class DisplayState
{
public int RetrievedEventsCount;
}
private readonly List<DisplayInfo> _displayInfo;
private readonly Dictionary<ulong, DisplayState> _openDisplays;
private int _vsyncEventHandle;
public IApplicationDisplayService(ViServiceType serviceType)
{
_serviceType = serviceType;
_displayInfo = new List<DisplayInfo>();
_openDisplayInfo = new Dictionary<ulong, DisplayInfo>();
_serviceType = serviceType;
_displayInfo = new List<DisplayInfo>();
_openDisplays = new Dictionary<ulong, DisplayState>();
void AddDisplayInfo(string name, bool layerLimitEnabled, ulong layerLimitMax, ulong width, ulong height)
{
@ -64,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
// FIXME: Should be _serviceType != ViServiceType.Application but guests crashes if we do this check.
if (_serviceType > ViServiceType.System)
{
return ResultCode.InvalidRange;
return ResultCode.PermissionDenied;
}
MakeObject(context, new HOSBinderDriverServer());
@ -79,7 +83,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
// FIXME: Should be _serviceType == ViServiceType.System but guests crashes if we do this check.
if (_serviceType > ViServiceType.System)
{
return ResultCode.InvalidRange;
return ResultCode.PermissionDenied;
}
MakeObject(context, new ISystemDisplayService(this));
@ -93,7 +97,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
{
if (_serviceType > ViServiceType.System)
{
return ResultCode.InvalidRange;
return ResultCode.PermissionDenied;
}
MakeObject(context, new IManagerDisplayService(this));
@ -107,7 +111,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
{
if (_serviceType > ViServiceType.System)
{
return ResultCode.InvalidRange;
return ResultCode.PermissionDenied;
}
MakeObject(context, new HOSBinderDriverServer());
@ -174,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
return ResultCode.InvalidValue;
}
if (!_openDisplayInfo.TryAdd((ulong)displayId, _displayInfo[displayId]))
if (!_openDisplays.TryAdd((ulong)displayId, new DisplayState()))
{
return ResultCode.AlreadyOpened;
}
@ -190,7 +194,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
{
ulong displayId = context.RequestData.ReadUInt64();
if (!_openDisplayInfo.Remove(displayId))
if (!_openDisplays.Remove(displayId))
{
return ResultCode.InvalidValue;
}
@ -454,11 +458,16 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
{
ulong displayId = context.RequestData.ReadUInt64();
if (!_openDisplayInfo.ContainsKey(displayId))
if (!_openDisplays.TryGetValue(displayId, out DisplayState displayState))
{
return ResultCode.InvalidValue;
}
if (displayState.RetrievedEventsCount > 0)
{
return ResultCode.PermissionDenied;
}
if (_vsyncEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != KernelResult.Success)
@ -467,6 +476,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
}
}
displayState.RetrievedEventsCount++;
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_vsyncEventHandle);
return ResultCode.Success;

View File

@ -13,27 +13,17 @@ namespace Ryujinx.HLE
{
public class Switch : IDisposable
{
public HLEConfiguration Configuration { get; }
public HLEConfiguration Configuration { get; }
public IHardwareDeviceDriver AudioDeviceDriver { get; }
internal MemoryBlock Memory { get; }
public GpuContext Gpu { get; }
public VirtualFileSystem FileSystem => Configuration.VirtualFileSystem;
public Horizon System { get; }
public ApplicationLoader Application { get; }
public PerformanceStatistics Statistics { get; }
public Hid Hid { get; }
public TamperMachine TamperMachine { get; }
public IHostUiHandler UiHandler { get; }
public MemoryBlock Memory { get; }
public GpuContext Gpu { get; }
public VirtualFileSystem FileSystem { get; }
public Horizon System { get; }
public ApplicationLoader Application { get; }
public PerformanceStatistics Statistics { get; }
public Hid Hid { get; }
public TamperMachine TamperMachine { get; }
public IHostUiHandler UiHandler { get; }
public bool EnableDeviceVsync { get; set; } = true;
@ -55,47 +45,27 @@ namespace Ryujinx.HLE
}
Configuration = configuration;
FileSystem = Configuration.VirtualFileSystem;
UiHandler = Configuration.HostUiHandler;
UiHandler = configuration.HostUiHandler;
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver);
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), MemoryAllocationFlags.Reserve);
Gpu = new GpuContext(Configuration.GpuRenderer);
System = new Horizon(this);
Statistics = new PerformanceStatistics();
Hid = new Hid(this, System.HidStorage);
Application = new ApplicationLoader(this);
TamperMachine = new TamperMachine();
AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(configuration.AudioDeviceDriver);
Memory = new MemoryBlock(configuration.MemoryConfiguration.ToDramSize(), MemoryAllocationFlags.Reserve);
Gpu = new GpuContext(configuration.GpuRenderer);
System = new Horizon(this);
System.InitializeServices();
Statistics = new PerformanceStatistics();
Hid = new Hid(this, System.HidStorage);
Hid.InitDevices();
Application = new ApplicationLoader(this);
TamperMachine = new TamperMachine();
Initialize();
}
private void Initialize()
{
System.State.SetLanguage(Configuration.SystemLanguage);
System.State.SetRegion(Configuration.Region);
EnableDeviceVsync = Configuration.EnableVsync;
System.State.DockedMode = Configuration.EnableDockedMode;
EnableDeviceVsync = Configuration.EnableVsync;
System.State.DockedMode = Configuration.EnableDockedMode;
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
System.EnablePtc = Configuration.EnablePtc;
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
System.EnablePtc = Configuration.EnablePtc;
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
}
public void LoadCart(string exeFsDir, string romFsFile = null)
@ -132,7 +102,6 @@ namespace Ryujinx.HLE
{
Gpu.ProcessShaderCacheQueue();
Gpu.Renderer.PreFrame();
Gpu.GPFifo.DispatchCalls();
}
@ -182,9 +151,9 @@ namespace Ryujinx.HLE
{
System.Dispose();
AudioDeviceDriver.Dispose();
FileSystem.Unload();
FileSystem.Dispose();
Memory.Dispose();
}
}
}
}
}

View File

@ -20,7 +20,6 @@ using Ryujinx.Graphics.OpenGL;
using Ryujinx.Headless.SDL2.OpenGL;
using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.FileSystem.Content;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input;
@ -29,7 +28,6 @@ using Ryujinx.Input.SDL2;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text.Json;
using System.Threading;
@ -220,6 +218,7 @@ namespace Ryujinx.Headless.SDL2
StickButton = ConfigGamepadInputId.LeftStick,
InvertStickX = false,
InvertStickY = false,
Rotate90CW = false,
},
RightJoycon = new RightJoyconCommonConfig<ConfigGamepadInputId>
@ -241,6 +240,7 @@ namespace Ryujinx.Headless.SDL2
StickButton = ConfigGamepadInputId.RightStick,
InvertStickX = false,
InvertStickY = false,
Rotate90CW = false,
},
Motion = new StandardMotionConfigController

View File

@ -350,6 +350,14 @@ namespace Ryujinx.Input.SDL2
{
resultY = -resultY;
}
if ((inputId == StickInputId.Left && _configuration.LeftJoyconStick.Rotate90CW) ||
(inputId == StickInputId.Right && _configuration.RightJoyconStick.Rotate90CW))
{
float temp = resultX;
resultX = resultY;
resultY = -temp;
}
}
return (resultX, resultY);

View File

@ -188,7 +188,7 @@ namespace Ryujinx.Memory.Tests
Assert.False(alignedAfterTriggers);
}
[Test, Timeout(1000)]
[Test, Explicit, Timeout(1000)]
public void Multithreading()
{
// Multithreading sanity test

Some files were not shown because too many files have changed in this diff Show More