From 09c53fe06fd9dd6ec6a9e585771f73d7e45f7148 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 12 Jul 2018 00:08:20 +0200 Subject: [PATCH 1/5] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 71dad9ce2..f6bac98c8 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,8 @@ If you have some homebrew that currently doesn't work within the emulator, you c For help, support, suggestions, or if you just want to get in touch with the team; join our Discord server! https://discord.gg/VkQYXAZ +For donation support, please take a look at our Patreon: https://www.patreon.com/ryujinx + **Running** To run this emulator, you need the .NET Core 2.1 (or higher) SDK *and* the OpenAL 11 Core SDK. @@ -92,6 +94,7 @@ Run `dotnet run -c Release -- path\to\homebrew.nro` inside the Ryujinx solution Run `dotnet run -c Release -- path\to\game_exefs_and_romfs_folder` to run official games (they need to be decrypted and extracted first!) **Compatibility** + You can check out the compatibility list within the Wiki. Only a handful of games actually work. **Latest build** From 37071285bcf14ef96b16b55cb6ed7c8c003489e0 Mon Sep 17 00:00:00 2001 From: David <25727384+ogniK5377@users.noreply.github.com> Date: Thu, 12 Jul 2018 11:41:35 +1000 Subject: [PATCH 2/5] NvGetConfig with production/non production swapping (#243) * GetConfig should return 0x30006 in production mode * GetConfig will now check settings only if nv!rmos_set_production_mode is set to "0" * Code formatting, TryGetValue * Slight fixup * dont forget the setting * Implemented non production mode setting grabbing * format issue * style changes --- .../Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs | 59 ++++++++++++++++--- Ryujinx.HLE/OsHle/Services/Nv/NvResult.cs | 17 +++--- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs index a9fd9d3ab..7705a1f78 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -2,6 +2,7 @@ using ChocolArm64.Memory; using Ryujinx.HLE.Logging; using System; using System.Collections.Concurrent; +using System.Text; using System.Threading; namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl @@ -10,9 +11,16 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl { private static ConcurrentDictionary UserCtxs; + private static bool IsProductionMode = true; + static NvHostCtrlIoctl() { UserCtxs = new ConcurrentDictionary(); + + if (Set.NxSettings.Settings.TryGetValue("nv!rmos_set_production_mode", out object ProductionModeSetting)) + { + IsProductionMode = ((string)ProductionModeSetting) != "0"; // Default value is "" + } } public static int ProcessIoctl(ServiceCtx Context, int Cmd) @@ -71,17 +79,52 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl private static int GetConfig(ServiceCtx Context) { - long InputPosition = Context.Request.GetBufferType0x21().Position; - long OutputPosition = Context.Request.GetBufferType0x22().Position; + if (!IsProductionMode) + { + long InputPosition = Context.Request.GetBufferType0x21().Position; + long OutputPosition = Context.Request.GetBufferType0x22().Position; - string Nv = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0, 0x41); - string Name = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0x41, 0x41); + string Domain = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0, 0x41); + string Name = AMemoryHelper.ReadAsciiString(Context.Memory, InputPosition + 0x41, 0x41); - Context.Memory.WriteByte(OutputPosition + 0x82, 0); + if (Set.NxSettings.Settings.TryGetValue($"{Domain}!{Name}", out object NvSetting)) + { + byte[] SettingBuffer = new byte[0x101]; - Context.Ns.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + if (NvSetting is string StringValue) + { + if (StringValue.Length > 0x100) + { + Context.Ns.Log.PrintError(Logging.LogClass.ServiceNv, $"{Domain}!{Name} String value size is too big!"); + } + else + { + SettingBuffer = Encoding.ASCII.GetBytes(StringValue + "\0"); + } + } - return NvResult.Success; + if (NvSetting is int IntValue) + { + SettingBuffer = BitConverter.GetBytes(IntValue); + } + else if (NvSetting is bool BoolValue) + { + SettingBuffer[0] = BoolValue ? (byte)1 : (byte)0; + } + else + { + throw new NotImplementedException(NvSetting.GetType().Name); + } + + Context.Memory.WriteBytes(OutputPosition + 0x82, SettingBuffer); + + Context.Ns.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}"); + } + + return NvResult.Success; + } + + return NvResult.NotAvailableInProduction; } private static int EventWait(ServiceCtx Context) @@ -352,4 +395,4 @@ namespace Ryujinx.HLE.OsHle.Services.Nv.NvHostCtrl UserCtxs.TryRemove(Process, out _); } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/OsHle/Services/Nv/NvResult.cs b/Ryujinx.HLE/OsHle/Services/Nv/NvResult.cs index 720f5ccf2..78ae5ae33 100644 --- a/Ryujinx.HLE/OsHle/Services/Nv/NvResult.cs +++ b/Ryujinx.HLE/OsHle/Services/Nv/NvResult.cs @@ -2,12 +2,13 @@ namespace Ryujinx.HLE.OsHle.Services.Nv { static class NvResult { - public const int Success = 0; - public const int TryAgain = -11; - public const int OutOfMemory = -12; - public const int InvalidInput = -22; - public const int NotSupported = -25; - public const int Restart = -85; - public const int TimedOut = -110; + public const int NotAvailableInProduction = 196614; + public const int Success = 0; + public const int TryAgain = -11; + public const int OutOfMemory = -12; + public const int InvalidInput = -22; + public const int NotSupported = -25; + public const int Restart = -85; + public const int TimedOut = -110; } -} \ No newline at end of file +} From cd18ab29dfacd1f7a3218d4ec73ce664bccc3887 Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Thu, 12 Jul 2018 14:03:52 -0300 Subject: [PATCH 3/5] Loop GLScreen with custom method (#244) * Loop GLScreen with custom method * Fix deadlocks * Fix screen resizing * Change event to bool * Try to fix quitting error * Set title from main thread * Queue max 1 vsync, fix high FPS after a slowdown --- Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs | 7 ++ Ryujinx.HLE/Switch.cs | 5 ++ Ryujinx/Ui/GLScreen.cs | 110 ++++++++++++++++++++++++--- Ryujinx/Ui/Program.cs | 2 +- 4 files changed, 113 insertions(+), 11 deletions(-) diff --git a/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs b/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs index 0bc682a70..7b999eaed 100644 --- a/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs +++ b/Ryujinx.HLE/Gpu/Engines/NvGpuFifo.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.Gpu.Memory; using System.Collections.Concurrent; +using System.Threading; namespace Ryujinx.HLE.Gpu.Engines { @@ -18,6 +19,8 @@ namespace Ryujinx.HLE.Gpu.Engines private NvGpuEngine[] SubChannels; + public AutoResetEvent Event { get; private set; } + private struct CachedMacro { public int Position { get; private set; } @@ -60,6 +63,8 @@ namespace Ryujinx.HLE.Gpu.Engines Macros = new CachedMacro[MacrosCount]; Mme = new int[MmeWords]; + + Event = new AutoResetEvent(false); } public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer) @@ -68,6 +73,8 @@ namespace Ryujinx.HLE.Gpu.Engines { BufferQueue.Enqueue((Vmm, PBEntry)); } + + Event.Set(); } public void DispatchCalls() diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index f7b263cd0..1946b187b 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -71,6 +71,11 @@ namespace Ryujinx.HLE Os.LoadProgram(FileName); } + public bool WaitFifo() + { + return Gpu.Fifo.Event.WaitOne(8); + } + public void ProcessFrame() { Gpu.Fifo.DispatchCalls(); diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index 7a4e42e9e..9b5dda4f0 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -5,6 +5,9 @@ using Ryujinx.Graphics.Gal; using Ryujinx.HLE; using Ryujinx.HLE.Input; using System; +using System.Threading; + +using Stopwatch = System.Diagnostics.Stopwatch; namespace Ryujinx { @@ -16,6 +19,8 @@ namespace Ryujinx private const float TouchScreenRatioX = (float)TouchScreenWidth / TouchScreenHeight; private const float TouchScreenRatioY = (float)TouchScreenHeight / TouchScreenWidth; + private const int TargetFPS = 60; + private Switch Ns; private IGalRenderer Renderer; @@ -24,6 +29,14 @@ namespace Ryujinx private MouseState? Mouse = null; + private Thread RenderThread; + + private bool ResizeEvent; + + private bool TitleEvent; + + private string NewTitle; + public GLScreen(Switch Ns, IGalRenderer Renderer) : base(1280, 720, new GraphicsMode(), "Ryujinx", 0, @@ -36,13 +49,85 @@ namespace Ryujinx Location = new Point( (DisplayDevice.Default.Width / 2) - (Width / 2), (DisplayDevice.Default.Height / 2) - (Height / 2)); + + ResizeEvent = false; + + TitleEvent = false; } - protected override void OnLoad(EventArgs e) + private void RenderLoop() { - VSync = VSyncMode.On; + MakeCurrent(); + + Stopwatch Chrono = new Stopwatch(); + + Chrono.Start(); + + long TicksPerFrame = Stopwatch.Frequency / TargetFPS; + + long Ticks = 0; + + while (Exists && !IsExiting) + { + if (Ns.WaitFifo()) + { + Ns.ProcessFrame(); + } + + Renderer.RunActions(); + + if (ResizeEvent) + { + ResizeEvent = false; + + Renderer.FrameBuffer.SetWindowSize(Width, Height); + } + + Ticks += Chrono.ElapsedTicks; + + Chrono.Restart(); + + if (Ticks >= TicksPerFrame) + { + RenderFrame(); + + //Queue max. 1 vsync + Ticks = Math.Min(Ticks - TicksPerFrame, TicksPerFrame); + } + } + } + + public void MainLoop() + { + VSync = VSyncMode.Off; + + Visible = true; Renderer.FrameBuffer.SetWindowSize(Width, Height); + + Context.MakeCurrent(null); + + //OpenTK doesn't like sleeps in its thread, to avoid this a renderer thread is created + RenderThread = new Thread(RenderLoop); + + RenderThread.Start(); + + while (Exists && !IsExiting) + { + ProcessEvents(); + + if (!IsExiting) + { + UpdateFrame(); + + if (TitleEvent) + { + TitleEvent = false; + + Title = NewTitle; + } + } + } } private bool IsGamePadButtonPressedFromString(GamePadState GamePad, string Button) @@ -99,7 +184,7 @@ namespace Ryujinx } } - protected override void OnUpdateFrame(FrameEventArgs e) + private new void UpdateFrame() { HidControllerButtons CurrentButton = 0; HidJoystickPosition LeftJoystick; @@ -278,13 +363,9 @@ namespace Ryujinx CurrentButton, LeftJoystick, RightJoystick); - - Ns.ProcessFrame(); - - Renderer.RunActions(); } - protected override void OnRenderFrame(FrameEventArgs e) + private new void RenderFrame() { Renderer.FrameBuffer.Render(); @@ -293,16 +374,25 @@ namespace Ryujinx double HostFps = Ns.Statistics.GetSystemFrameRate(); double GameFps = Ns.Statistics.GetGameFrameRate(); - Title = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}"; + NewTitle = $"Ryujinx | Host FPS: {HostFps:0.0} | Game FPS: {GameFps:0.0}"; + + TitleEvent = true; SwapBuffers(); Ns.Os.SignalVsync(); } + protected override void OnUnload(EventArgs e) + { + RenderThread.Join(); + + base.OnUnload(e); + } + protected override void OnResize(EventArgs e) { - Renderer.FrameBuffer.SetWindowSize(Width, Height); + ResizeEvent = true; } protected override void OnKeyDown(KeyboardKeyEventArgs e) diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index b14897695..5cacc6228 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -67,7 +67,7 @@ namespace Ryujinx Screen.Exit(); }; - Screen.Run(0.0, 60.0); + Screen.MainLoop(); } Environment.Exit(0); From b233ae964fcaae900cdefa6ce51b0edb2892dfaf Mon Sep 17 00:00:00 2001 From: Merry Date: Thu, 12 Jul 2018 19:51:02 +0100 Subject: [PATCH 4/5] AInstEmitSimdCvt: Half-precision to single-precision conversion (#235) --- ChocolArm64/Instruction/AInstEmitSimdCvt.cs | 8 ++--- ChocolArm64/Instruction/ASoftFloat.cs | 36 +++++++++++++++++++ Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs | 40 +++++++++++++++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs index 98bb972a2..da584743c 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -45,10 +45,10 @@ namespace ChocolArm64.Instruction { if (SizeF == 0) { - //TODO: This need the half precision floating point type, - //that is not yet supported on .NET. We should probably - //do our own implementation on the meantime. - throw new NotImplementedException(); + EmitVectorExtractZx(Context, Op.Rn, Part + Index, 1); + Context.Emit(OpCodes.Conv_U2); + + Context.EmitCall(typeof(ASoftFloat), nameof(ASoftFloat.ConvertHalfToSingle)); } else /* if (SizeF == 1) */ { diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs index e63c82bee..27f4f7fb4 100644 --- a/ChocolArm64/Instruction/ASoftFloat.cs +++ b/ChocolArm64/Instruction/ASoftFloat.cs @@ -225,5 +225,41 @@ namespace ChocolArm64.Instruction return 2.0 + op1 * op2; } + + public static float ConvertHalfToSingle(ushort x) + { + uint x_sign = (uint)(x >> 15) & 0x0001; + uint x_exp = (uint)(x >> 10) & 0x001F; + uint x_mantissa = (uint)x & 0x03FF; + + if (x_exp == 0 && x_mantissa == 0) + { + // Zero + return BitConverter.Int32BitsToSingle((int)(x_sign << 31)); + } + + if (x_exp == 0x1F) + { + // NaN or Infinity + return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | 0x7F800000 | (x_mantissa << 13))); + } + + int exponent = (int)x_exp - 15; + + if (x_exp == 0) + { + // Denormal + x_mantissa <<= 1; + while ((x_mantissa & 0x0400) == 0) + { + x_mantissa <<= 1; + exponent--; + } + x_mantissa &= 0x03FF; + } + + uint new_exp = (uint)((exponent + 127) & 0xFF) << 23; + return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13))); + } } } \ No newline at end of file diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs new file mode 100644 index 000000000..2d021616c --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdCvt.cs @@ -0,0 +1,40 @@ +using ChocolArm64.State; + +using NUnit.Framework; + +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; + +namespace Ryujinx.Tests.Cpu +{ + public class CpuTestSimdCvt : CpuTest + { + [TestCase((ushort)0x0000, 0x00000000u)] // Positive Zero + [TestCase((ushort)0x8000, 0x80000000u)] // Negative Zero + [TestCase((ushort)0x3E00, 0x3FC00000u)] // +1.5 + [TestCase((ushort)0xBE00, 0xBFC00000u)] // -1.5 + [TestCase((ushort)0xFFFF, 0xFFFFE000u)] // -QNaN + [TestCase((ushort)0x7C00, 0x7F800000u)] // +Inf + [TestCase((ushort)0x3C00, 0x3F800000u)] // 1.0 + [TestCase((ushort)0x3C01, 0x3F802000u)] // 1.0009765625 + [TestCase((ushort)0xC000, 0xC0000000u)] // -2.0 + [TestCase((ushort)0x7BFF, 0x477FE000u)] // 65504.0 (Largest Normal) + [TestCase((ushort)0x03FF, 0x387FC000u)] // 0.00006097555 (Largest Subnormal) + [TestCase((ushort)0x0001, 0x33800000u)] // 5.96046448e-8 (Smallest Subnormal) + public void Fcvtl_V_f16(ushort Value, uint Result) + { + uint Opcode = 0x0E217801; + Vector128 V0 = Sse.StaticCast(Sse2.SetAllVector128(Value)); + + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0); + + Assert.Multiple(() => + { + Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)0), Is.EqualTo(Result)); + Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)1), Is.EqualTo(Result)); + Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)2), Is.EqualTo(Result)); + Assert.That(Sse41.Extract(Sse.StaticCast(ThreadState.V1), (byte)3), Is.EqualTo(Result)); + }); + } + } +} From 3b00333b0ce21538e5ff361e3e41d89bee586d36 Mon Sep 17 00:00:00 2001 From: greggameplayer <33609333+greggameplayer@users.noreply.github.com> Date: Fri, 13 Jul 2018 02:27:59 +0200 Subject: [PATCH 5/5] Add return of Texture Size and Bytes Per Pixel of ASTC2D 5x5, 6x6, 8x8, 10x10 and 12x12 (#249) * return correct size of ASTC 5x5, 6x6, 8x8, 10x10 and 12x12 * return correct Bytes Per Pixel * Use method in order to get CompressedTextureSize * Add Read16BptCompressedTexture method * add Bpb integer argument --- Ryujinx.HLE/Gpu/Texture/TextureHelper.cs | 83 +++++++++++++++++++++--- Ryujinx.HLE/Gpu/Texture/TextureReader.cs | 53 +++++++++------ 2 files changed, 108 insertions(+), 28 deletions(-) diff --git a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs index 3c633b692..6b9a30635 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureHelper.cs @@ -51,10 +51,7 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.BC1: case GalTextureFormat.BC4: { - int W = (Texture.Width + 3) / 4; - int H = (Texture.Height + 3) / 4; - - return W * H * 8; + return CompressedTextureSize(Texture.Width, Texture.Height, 4, 4, 8); } case GalTextureFormat.BC7U: @@ -63,16 +60,86 @@ namespace Ryujinx.HLE.Gpu.Texture case GalTextureFormat.BC5: case GalTextureFormat.Astc2D4x4: { - int W = (Texture.Width + 3) / 4; - int H = (Texture.Height + 3) / 4; - - return W * H * 16; + return CompressedTextureSize(Texture.Width, Texture.Height, 4, 4, 16); + } + + case GalTextureFormat.Astc2D5x5: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 5, 5, 16); + } + + case GalTextureFormat.Astc2D6x6: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 6, 6, 16); + } + + case GalTextureFormat.Astc2D8x8: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 8, 8, 16); + } + + case GalTextureFormat.Astc2D10x10: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 10, 10, 16); + } + + case GalTextureFormat.Astc2D12x12: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 12, 12, 16); + } + + case GalTextureFormat.Astc2D5x4: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 5, 4, 16); + } + + case GalTextureFormat.Astc2D6x5: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 6, 5, 16); + } + + case GalTextureFormat.Astc2D8x6: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 8, 6, 16); + } + + case GalTextureFormat.Astc2D10x8: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 10, 8, 16); + } + + case GalTextureFormat.Astc2D12x10: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 12, 10, 16); + } + + case GalTextureFormat.Astc2D8x5: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 8, 5, 16); + } + + case GalTextureFormat.Astc2D10x5: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 10, 5, 16); + } + + case GalTextureFormat.Astc2D10x6: + { + return CompressedTextureSize(Texture.Width, Texture.Height, 10, 6, 16); } } throw new NotImplementedException(Texture.Format.ToString()); } + public static int CompressedTextureSize(int TextureWidth, int TextureHeight, int BlockWidth, int BlockHeight, int Bpb) + { + int W = (TextureWidth + (BlockWidth - 1)) / BlockWidth; + int H = (TextureHeight + (BlockHeight - 1)) / BlockHeight; + + return W * H * Bpb; + } + public static (AMemory Memory, long Position) GetMemoryAndPosition( IAMemory Memory, long Position) diff --git a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs index 24bceffb1..8bd4dbcba 100644 --- a/Ryujinx.HLE/Gpu/Texture/TextureReader.cs +++ b/Ryujinx.HLE/Gpu/Texture/TextureReader.cs @@ -10,23 +10,36 @@ namespace Ryujinx.HLE.Gpu.Texture { switch (Texture.Format) { - case GalTextureFormat.R32G32B32A32: return Read16Bpp (Memory, Texture); - case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture); - case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); - case GalTextureFormat.R32: return Read4Bpp (Memory, Texture); - case GalTextureFormat.A1B5G5R5: return Read5551 (Memory, Texture); - case GalTextureFormat.B5G6R5: return Read565 (Memory, Texture); - case GalTextureFormat.G8R8: return Read2Bpp (Memory, Texture); - case GalTextureFormat.R16: return Read2Bpp (Memory, Texture); - case GalTextureFormat.R8: return Read1Bpp (Memory, Texture); - case GalTextureFormat.BC7U: return Read16Bpt4x4(Memory, Texture); - case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); - case GalTextureFormat.BC2: return Read16Bpt4x4(Memory, Texture); - case GalTextureFormat.BC3: return Read16Bpt4x4(Memory, Texture); - case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture); - case GalTextureFormat.BC5: return Read16Bpt4x4(Memory, Texture); - case GalTextureFormat.ZF32: return Read4Bpp (Memory, Texture); - case GalTextureFormat.Astc2D4x4: return Read16Bpt4x4(Memory, Texture); + case GalTextureFormat.R32G32B32A32: return Read16Bpp (Memory, Texture); + case GalTextureFormat.R16G16B16A16: return Read8Bpp (Memory, Texture); + case GalTextureFormat.A8B8G8R8: return Read4Bpp (Memory, Texture); + case GalTextureFormat.R32: return Read4Bpp (Memory, Texture); + case GalTextureFormat.A1B5G5R5: return Read5551 (Memory, Texture); + case GalTextureFormat.B5G6R5: return Read565 (Memory, Texture); + case GalTextureFormat.G8R8: return Read2Bpp (Memory, Texture); + case GalTextureFormat.R16: return Read2Bpp (Memory, Texture); + case GalTextureFormat.R8: return Read1Bpp (Memory, Texture); + case GalTextureFormat.BC7U: return Read16BptCompressedTexture(Memory, Texture, 4, 4); + case GalTextureFormat.BC1: return Read8Bpt4x4 (Memory, Texture); + case GalTextureFormat.BC2: return Read16BptCompressedTexture(Memory, Texture, 4, 4); + case GalTextureFormat.BC3: return Read16BptCompressedTexture(Memory, Texture, 4, 4); + case GalTextureFormat.BC4: return Read8Bpt4x4 (Memory, Texture); + case GalTextureFormat.BC5: return Read16BptCompressedTexture(Memory, Texture, 4, 4); + case GalTextureFormat.ZF32: return Read4Bpp (Memory, Texture); + case GalTextureFormat.Astc2D4x4: return Read16BptCompressedTexture(Memory, Texture, 4, 4); + case GalTextureFormat.Astc2D5x5: return Read16BptCompressedTexture(Memory, Texture, 5, 5); + case GalTextureFormat.Astc2D6x6: return Read16BptCompressedTexture(Memory, Texture, 6, 6); + case GalTextureFormat.Astc2D8x8: return Read16BptCompressedTexture(Memory, Texture, 8, 8); + case GalTextureFormat.Astc2D10x10: return Read16BptCompressedTexture(Memory, Texture, 10, 10); + case GalTextureFormat.Astc2D12x12: return Read16BptCompressedTexture(Memory, Texture, 12, 12); + case GalTextureFormat.Astc2D5x4: return Read16BptCompressedTexture(Memory, Texture, 5, 4); + case GalTextureFormat.Astc2D6x5: return Read16BptCompressedTexture(Memory, Texture, 6, 5); + case GalTextureFormat.Astc2D8x6: return Read16BptCompressedTexture(Memory, Texture, 8, 6); + case GalTextureFormat.Astc2D10x8: return Read16BptCompressedTexture(Memory, Texture, 10, 8); + case GalTextureFormat.Astc2D12x10: return Read16BptCompressedTexture(Memory, Texture, 12, 10); + case GalTextureFormat.Astc2D8x5: return Read16BptCompressedTexture(Memory, Texture, 8, 5); + case GalTextureFormat.Astc2D10x5: return Read16BptCompressedTexture(Memory, Texture, 10, 5); + case GalTextureFormat.Astc2D10x6: return Read16BptCompressedTexture(Memory, Texture, 10, 6); } throw new NotImplementedException(Texture.Format.ToString()); @@ -307,10 +320,10 @@ namespace Ryujinx.HLE.Gpu.Texture return Output; } - private unsafe static byte[] Read16Bpt4x4(IAMemory Memory, TextureInfo Texture) + private unsafe static byte[] Read16BptCompressedTexture(IAMemory Memory, TextureInfo Texture, int BlockWidth, int BlockHeight) { - int Width = (Texture.Width + 3) / 4; - int Height = (Texture.Height + 3) / 4; + int Width = (Texture.Width + (BlockWidth - 1)) / BlockWidth; + int Height = (Texture.Height + (BlockHeight - 1)) / BlockHeight; byte[] Output = new byte[Width * Height * 16];