Merge branch 'master' into patch-3

This commit is contained in:
greggameplayer 2018-07-13 23:05:33 +02:00 committed by GitHub
commit e24a66967f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 365 additions and 60 deletions

View file

@ -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) */
{

View file

@ -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)));
}
}
}

View file

@ -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**

View file

@ -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()

View file

@ -52,10 +52,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:
@ -64,16 +61,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)

View file

@ -10,24 +10,37 @@ 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.R16_G16: 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.R16_G16: 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());
@ -308,10 +321,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];

View file

@ -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<Process, NvHostCtrlUserCtx> UserCtxs;
private static bool IsProductionMode = true;
static NvHostCtrlIoctl()
{
UserCtxs = new ConcurrentDictionary<Process, NvHostCtrlUserCtx>();
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 _);
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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();

View file

@ -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<float> V0 = Sse.StaticCast<ushort, float>(Sse2.SetAllVector128(Value));
AThreadState ThreadState = SingleOpcode(Opcode, V0: V0);
Assert.Multiple(() =>
{
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)0), Is.EqualTo(Result));
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)1), Is.EqualTo(Result));
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)2), Is.EqualTo(Result));
Assert.That(Sse41.Extract(Sse.StaticCast<float, uint>(ThreadState.V1), (byte)3), Is.EqualTo(Result));
});
}
}
}

View file

@ -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)

View file

@ -67,7 +67,7 @@ namespace Ryujinx
Screen.Exit();
};
Screen.Run(0.0, 60.0);
Screen.MainLoop();
}
Environment.Exit(0);