Improved GPU command lists decoding (#499)

* Better implementation of the DMA pusher, misc fixes

* Remove some debug code

* Correct RGBX8 format

* Add support for linked Texture Sampler Control

* Attempt to fix upside down screen issue
This commit is contained in:
gdkchan 2018-11-17 02:01:31 -02:00 committed by Ac_K
parent b833183ef6
commit d2bb458b51
25 changed files with 616 additions and 395 deletions

View file

@ -409,9 +409,31 @@ namespace ChocolArm64.Memory
public void WriteBytes(long position, byte[] data) public void WriteBytes(long position, byte[] data)
{ {
EnsureRangeIsValid(position, (uint)data.Length); long endAddr = position + data.Length;
Marshal.Copy(data, 0, (IntPtr)TranslateWrite(position), data.Length); if ((ulong)endAddr < (ulong)position)
{
throw new ArgumentOutOfRangeException(nameof(position));
}
int offset = 0;
while ((ulong)position < (ulong)endAddr)
{
long pageLimit = (position + PageSize) & ~(long)PageMask;
if ((ulong)pageLimit > (ulong)endAddr)
{
pageLimit = endAddr;
}
int copySize = (int)(pageLimit - position);
Marshal.Copy(data, offset, (IntPtr)TranslateWrite(position), copySize);
position += copySize;
offset += copySize;
}
} }
public void WriteBytes(long position, byte[] data, int startIndex, int size) public void WriteBytes(long position, byte[] data, int startIndex, int size)

View file

@ -0,0 +1,190 @@
using Ryujinx.Graphics.Memory;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Graphics
{
public class DmaPusher
{
private ConcurrentQueue<(NvGpuVmm, long)> IbBuffer;
private long DmaPut;
private long DmaGet;
private struct DmaState
{
public int Method;
public int SubChannel;
public int MethodCount;
public bool NonIncrementing;
public bool IncrementOnce;
public int LengthPending;
}
private DmaState State;
private bool SliEnable;
private bool SliActive;
private bool IbEnable;
private bool NonMain;
private long DmaMGet;
private NvGpuVmm Vmm;
private NvGpu Gpu;
private AutoResetEvent Event;
public DmaPusher(NvGpu Gpu)
{
this.Gpu = Gpu;
IbBuffer = new ConcurrentQueue<(NvGpuVmm, long)>();
IbEnable = true;
Event = new AutoResetEvent(false);
}
public void Push(NvGpuVmm Vmm, long Entry)
{
IbBuffer.Enqueue((Vmm, Entry));
Event.Set();
}
public bool WaitForCommands()
{
return Event.WaitOne(8);
}
public void DispatchCalls()
{
while (Step());
}
private bool Step()
{
if (DmaGet != DmaPut)
{
int Word = Vmm.ReadInt32(DmaGet);
DmaGet += 4;
if (!NonMain)
{
DmaMGet = DmaGet;
}
if (State.LengthPending != 0)
{
State.LengthPending = 0;
State.MethodCount = Word & 0xffffff;
}
else if (State.MethodCount != 0)
{
if (!SliEnable || SliActive)
{
CallMethod(Word);
}
if (!State.NonIncrementing)
{
State.Method++;
}
if (State.IncrementOnce)
{
State.NonIncrementing = true;
}
State.MethodCount--;
}
else
{
int SumissionMode = (Word >> 29) & 7;
switch (SumissionMode)
{
case 1:
//Incrementing.
SetNonImmediateState(Word);
State.NonIncrementing = false;
State.IncrementOnce = false;
break;
case 3:
//Non-incrementing.
SetNonImmediateState(Word);
State.NonIncrementing = true;
State.IncrementOnce = false;
break;
case 4:
//Immediate.
State.Method = (Word >> 0) & 0x1fff;
State.SubChannel = (Word >> 13) & 7;
State.NonIncrementing = true;
State.IncrementOnce = false;
CallMethod((Word >> 16) & 0x1fff);
break;
case 5:
//Increment-once.
SetNonImmediateState(Word);
State.NonIncrementing = false;
State.IncrementOnce = true;
break;
}
}
}
else if (IbEnable && IbBuffer.TryDequeue(out (NvGpuVmm Vmm, long Entry) Tuple))
{
this.Vmm = Tuple.Vmm;
long Entry = Tuple.Entry;
int Length = (int)(Entry >> 42) & 0x1fffff;
DmaGet = Entry & 0xfffffffffc;
DmaPut = DmaGet + Length * 4;
NonMain = (Entry & (1L << 41)) != 0;
Gpu.ResourceManager.ClearPbCache();
}
else
{
return false;
}
return true;
}
private void SetNonImmediateState(int Word)
{
State.Method = (Word >> 0) & 0x1fff;
State.SubChannel = (Word >> 13) & 7;
State.MethodCount = (Word >> 16) & 0x1fff;
}
private void CallMethod(int Argument)
{
Gpu.Fifo.CallMethod(Vmm, new GpuMethodCall(
State.Method,
Argument,
State.SubChannel,
State.MethodCount));
}
}
}

View file

@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gal
RGB5A1, RGB5A1,
R8, R8,
RG8, RG8,
RGBX8,
RGBA8, RGBA8,
BGRA8, BGRA8,
RGB10A2, RGB10A2,
@ -39,6 +40,7 @@ namespace Ryujinx.Graphics.Gal
RGBA32, RGBA32,
R11G11B10, R11G11B10,
D16, D16,
D24,
D32, D32,
D24S8, D24S8,
D32S8, D32S8,

View file

@ -139,6 +139,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalImageFormat.RG32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float); case GalImageFormat.RG32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float);
case GalImageFormat.RG32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int); case GalImageFormat.RG32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int);
case GalImageFormat.RG32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt); case GalImageFormat.RG32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt);
case GalImageFormat.RGBX8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte); case GalImageFormat.RGBA8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte);
case GalImageFormat.RGBA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte); case GalImageFormat.RGBA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte);
case GalImageFormat.RGBA8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte); case GalImageFormat.RGBA8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte);
@ -174,10 +175,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL
case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte); case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte);
case GalImageFormat.R11G11B10 | GalImageFormat.Float: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); case GalImageFormat.R11G11B10 | GalImageFormat.Float: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev);
case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort);
case GalImageFormat.D24 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt);
case GalImageFormat.D24S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); case GalImageFormat.D24S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
case GalImageFormat.D24S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); case GalImageFormat.D24S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248);
case GalImageFormat.D32 | GalImageFormat.Float: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float); case GalImageFormat.D32 | GalImageFormat.Float: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float);
case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort);
case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev); case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev);
} }

View file

@ -421,8 +421,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL
ClearBufferMask Mask = GetClearMask(SrcTex); ClearBufferMask Mask = GetClearMask(SrcTex);
GL.Clear(Mask);
GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter); GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter);
} }
} }

View file

@ -0,0 +1,24 @@
namespace Ryujinx.Graphics
{
struct GpuMethodCall
{
public int Method { get; private set; }
public int Argument { get; private set; }
public int SubChannel { get; private set; }
public int MethodCount { get; private set; }
public bool IsLastCall => MethodCount <= 1;
public GpuMethodCall(
int Method,
int Argument,
int SubChannel = 0,
int MethodCount = 0)
{
this.Method = Method;
this.Argument = Argument;
this.SubChannel = SubChannel;
this.MethodCount = MethodCount;
}
}
}

View file

@ -117,7 +117,7 @@ namespace Ryujinx.Graphics
return false; return false;
} }
private bool MemoryRegionModified(NvGpuVmm Vmm, long Position, long Size, NvGpuBufferType Type) public bool MemoryRegionModified(NvGpuVmm Vmm, long Position, long Size, NvGpuBufferType Type)
{ {
HashSet<long> Uploaded = UploadedKeys[(int)Type]; HashSet<long> Uploaded = UploadedKeys[(int)Type];
@ -136,5 +136,10 @@ namespace Ryujinx.Graphics
UploadedKeys[Index].Clear(); UploadedKeys[Index].Clear();
} }
} }
public void ClearPbCache(NvGpuBufferType Type)
{
UploadedKeys[(int)Type].Clear();
}
} }
} }

View file

@ -6,6 +6,6 @@ namespace Ryujinx.Graphics
{ {
int[] Registers { get; } int[] Registers { get; }
void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry); void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall);
} }
} }

View file

@ -1,3 +1,4 @@
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -388,14 +389,11 @@ namespace Ryujinx.Graphics
{ {
int Value; int Value;
//If we don't have any parameters in the FIFO, if (!Fifo.TryDequeue(out Value))
//keep running the PFIFO engine until it writes the parameters.
while (!Fifo.TryDequeue(out Value))
{ {
if (!PFifo.Step()) Logger.PrintWarning(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
{
return 0; return 0;
}
} }
return Value; return Value;
@ -408,9 +406,9 @@ namespace Ryujinx.Graphics
private void Send(NvGpuVmm Vmm, int Value) private void Send(NvGpuVmm Vmm, int Value)
{ {
NvGpuPBEntry PBEntry = new NvGpuPBEntry(MethAddr, 0, Value); GpuMethodCall MethCall = new GpuMethodCall(MethAddr, Value);
Engine.CallMethod(Vmm, PBEntry); Engine.CallMethod(Vmm, MethCall);
MethAddr += MethIncr; MethAddr += MethIncr;
} }

View file

@ -1,23 +0,0 @@
using System;
using System.Collections.ObjectModel;
namespace Ryujinx.Graphics.Memory
{
public struct NvGpuPBEntry
{
public int Method { get; private set; }
public int SubChannel { get; private set; }
private int[] m_Arguments;
public ReadOnlyCollection<int> Arguments => Array.AsReadOnly(m_Arguments);
public NvGpuPBEntry(int Method, int SubChannel, params int[] Arguments)
{
this.Method = Method;
this.SubChannel = SubChannel;
this.m_Arguments = Arguments;
}
}
}

View file

@ -1,101 +0,0 @@
using System.Collections.Generic;
using System.IO;
namespace Ryujinx.Graphics.Memory
{
public static class NvGpuPushBuffer
{
private enum SubmissionMode
{
Incrementing = 1,
NonIncrementing = 3,
Immediate = 4,
IncrementOnce = 5
}
public static NvGpuPBEntry[] Decode(byte[] Data)
{
using (MemoryStream MS = new MemoryStream(Data))
{
BinaryReader Reader = new BinaryReader(MS);
List<NvGpuPBEntry> PushBuffer = new List<NvGpuPBEntry>();
bool CanRead() => MS.Position + 4 <= MS.Length;
while (CanRead())
{
int Packed = Reader.ReadInt32();
int Meth = (Packed >> 0) & 0x1fff;
int SubC = (Packed >> 13) & 7;
int Args = (Packed >> 16) & 0x1fff;
int Mode = (Packed >> 29) & 7;
switch ((SubmissionMode)Mode)
{
case SubmissionMode.Incrementing:
{
for (int Index = 0; Index < Args && CanRead(); Index++, Meth++)
{
PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
}
break;
}
case SubmissionMode.NonIncrementing:
{
int[] Arguments = new int[Args];
for (int Index = 0; Index < Arguments.Length; Index++)
{
if (!CanRead())
{
break;
}
Arguments[Index] = Reader.ReadInt32();
}
PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Arguments));
break;
}
case SubmissionMode.Immediate:
{
PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Args));
break;
}
case SubmissionMode.IncrementOnce:
{
if (CanRead())
{
PushBuffer.Add(new NvGpuPBEntry(Meth, SubC, Reader.ReadInt32()));
}
if (CanRead() && Args > 1)
{
int[] Arguments = new int[Args - 1];
for (int Index = 0; Index < Arguments.Length && CanRead(); Index++)
{
Arguments[Index] = Reader.ReadInt32();
}
PushBuffer.Add(new NvGpuPBEntry(Meth + 1, SubC, Arguments));
}
break;
}
}
}
return PushBuffer.ToArray();
}
}
}
}

View file

@ -5,27 +5,54 @@ namespace Ryujinx.Graphics.Memory
{ {
class NvGpuVmmCache class NvGpuVmmCache
{ {
private ValueRangeSet<int> CachedRanges; private struct CachedResource
{
public long Key;
public int Mask;
public CachedResource(long Key, int Mask)
{
this.Key = Key;
this.Mask = Mask;
}
public override int GetHashCode()
{
return (int)(Key * 23 + Mask);
}
public override bool Equals(object obj)
{
return obj is CachedResource Cached && Equals(Cached);
}
public bool Equals(CachedResource other)
{
return Key == other.Key && Mask == other.Mask;
}
}
private ValueRangeSet<CachedResource> CachedRanges;
public NvGpuVmmCache() public NvGpuVmmCache()
{ {
CachedRanges = new ValueRangeSet<int>(); CachedRanges = new ValueRangeSet<CachedResource>();
} }
public bool IsRegionModified(MemoryManager Memory, NvGpuBufferType BufferType, long PA, long Size) public bool IsRegionModified(MemoryManager Memory, NvGpuBufferType BufferType, long Start, long Size)
{ {
(bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(PA, Size); (bool[] Modified, long ModifiedCount) = Memory.IsRegionModified(Start, Size);
//Remove all modified ranges. //Remove all modified ranges.
int Index = 0; int Index = 0;
long Position = PA & ~NvGpuVmm.PageMask; long Position = Start & ~NvGpuVmm.PageMask;
while (ModifiedCount > 0) while (ModifiedCount > 0)
{ {
if (Modified[Index++]) if (Modified[Index++])
{ {
CachedRanges.Remove(new ValueRange<int>(Position, Position + NvGpuVmm.PageSize)); CachedRanges.Remove(new ValueRange<CachedResource>(Position, Position + NvGpuVmm.PageSize));
ModifiedCount--; ModifiedCount--;
} }
@ -37,11 +64,19 @@ namespace Ryujinx.Graphics.Memory
//If the region is not yet present on the list, then a new ValueRange //If the region is not yet present on the list, then a new ValueRange
//is directly added with the current resource type as the only bit set. //is directly added with the current resource type as the only bit set.
//Otherwise, it just sets the bit for this new resource type on the current mask. //Otherwise, it just sets the bit for this new resource type on the current mask.
//The physical address of the resource is used as key, those keys are used to keep
//track of resources that are already on the cache. A resource may be inside another
//resource, and in this case we should return true if the "sub-resource" was not
//yet cached.
int Mask = 1 << (int)BufferType; int Mask = 1 << (int)BufferType;
ValueRange<int> NewCached = new ValueRange<int>(PA, PA + Size); CachedResource NewCachedValue = new CachedResource(Start, Mask);
ValueRange<int>[] Ranges = CachedRanges.GetAllIntersections(NewCached); ValueRange<CachedResource> NewCached = new ValueRange<CachedResource>(Start, Start + Size);
ValueRange<CachedResource>[] Ranges = CachedRanges.GetAllIntersections(NewCached);
bool IsKeyCached = Ranges.Length > 0 && Ranges[0].Value.Key == Start;
long LastEnd = NewCached.Start; long LastEnd = NewCached.Start;
@ -49,23 +84,36 @@ namespace Ryujinx.Graphics.Memory
for (Index = 0; Index < Ranges.Length; Index++) for (Index = 0; Index < Ranges.Length; Index++)
{ {
ValueRange<int> Current = Ranges[Index]; ValueRange<CachedResource> Current = Ranges[Index];
CachedResource Cached = Current.Value;
long RgStart = Math.Max(Current.Start, NewCached.Start); long RgStart = Math.Max(Current.Start, NewCached.Start);
long RgEnd = Math.Min(Current.End, NewCached.End); long RgEnd = Math.Min(Current.End, NewCached.End);
if ((Current.Value & Mask) == 0) if ((Cached.Mask & Mask) != 0)
{
CachedRanges.Add(new ValueRange<int>(RgStart, RgEnd, Current.Value | Mask));
}
else
{ {
Coverage += RgEnd - RgStart; Coverage += RgEnd - RgStart;
} }
//Highest key value has priority, this prevents larger resources
//for completely invalidating smaller ones on the cache. For example,
//consider that a resource in the range [100, 200) was added, and then
//another one in the range [50, 200). We prevent the new resource from
//completely replacing the old one by spliting it like this:
//New resource key is added at [50, 100), old key is still present at [100, 200).
if (Cached.Key < Start)
{
Cached.Key = Start;
}
Cached.Mask |= Mask;
CachedRanges.Add(new ValueRange<CachedResource>(RgStart, RgEnd, Cached));
if (RgStart > LastEnd) if (RgStart > LastEnd)
{ {
CachedRanges.Add(new ValueRange<int>(LastEnd, RgStart, Mask)); CachedRanges.Add(new ValueRange<CachedResource>(LastEnd, RgStart, NewCachedValue));
} }
LastEnd = RgEnd; LastEnd = RgEnd;
@ -73,10 +121,10 @@ namespace Ryujinx.Graphics.Memory
if (LastEnd < NewCached.End) if (LastEnd < NewCached.End)
{ {
CachedRanges.Add(new ValueRange<int>(LastEnd, NewCached.End, Mask)); CachedRanges.Add(new ValueRange<CachedResource>(LastEnd, NewCached.End, NewCachedValue));
} }
return Coverage != Size; return !IsKeyCached || Coverage != Size;
} }
} }
} }

View file

@ -8,8 +8,9 @@ namespace Ryujinx.Graphics
public GpuResourceManager ResourceManager { get; private set; } public GpuResourceManager ResourceManager { get; private set; }
public NvGpuFifo Fifo { get; private set; } public DmaPusher Pusher { get; private set; }
internal NvGpuFifo Fifo { get; private set; }
internal NvGpuEngine2d Engine2d { get; private set; } internal NvGpuEngine2d Engine2d { get; private set; }
internal NvGpuEngine3d Engine3d { get; private set; } internal NvGpuEngine3d Engine3d { get; private set; }
internal NvGpuEngineM2mf EngineM2mf { get; private set; } internal NvGpuEngineM2mf EngineM2mf { get; private set; }
@ -21,8 +22,9 @@ namespace Ryujinx.Graphics
ResourceManager = new GpuResourceManager(this); ResourceManager = new GpuResourceManager(this);
Fifo = new NvGpuFifo(this); Pusher = new DmaPusher(this);
Fifo = new NvGpuFifo(this);
Engine2d = new NvGpuEngine2d(this); Engine2d = new NvGpuEngine2d(this);
Engine3d = new NvGpuEngine3d(this); Engine3d = new NvGpuEngine3d(this);
EngineM2mf = new NvGpuEngineM2mf(this); EngineM2mf = new NvGpuEngineM2mf(this);

View file

@ -1,11 +1,10 @@
using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal;
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
using System;
namespace Ryujinx.Graphics namespace Ryujinx.Graphics
{ {
public class NvGpuEngine2d : INvGpuEngine class NvGpuEngine2d : INvGpuEngine
{ {
private enum CopyOperation private enum CopyOperation
{ {
@ -29,11 +28,11 @@ namespace Ryujinx.Graphics
Registers = new int[0x238]; Registers = new int[0x238];
} }
public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
WriteRegister(PBEntry); WriteRegister(MethCall);
if ((NvGpuEngine2dReg)PBEntry.Method == NvGpuEngine2dReg.BlitSrcYInt) if ((NvGpuEngine2dReg)MethCall.Method == NvGpuEngine2dReg.BlitSrcYInt)
{ {
TextureCopy(Vmm); TextureCopy(Vmm);
} }
@ -43,6 +42,13 @@ namespace Ryujinx.Graphics
{ {
CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); CopyOperation Operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation);
int DstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat);
bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0;
int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth);
int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight);
int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch);
int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions);
int SrcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat); int SrcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat);
bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; bool SrcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0;
int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); int SrcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth);
@ -50,12 +56,13 @@ namespace Ryujinx.Graphics
int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); int SrcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch);
int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); int SrcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions);
int DstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat); int DstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX);
bool DstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; int DstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY);
int DstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); int DstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW);
int DstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); int DstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH);
int DstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch);
int DstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); int SrcBlitX = ReadRegister(NvGpuEngine2dReg.BlitSrcXInt);
int SrcBlitY = ReadRegister(NvGpuEngine2dReg.BlitSrcYInt);
GalImageFormat SrcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)SrcFormat); GalImageFormat SrcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)SrcFormat);
GalImageFormat DstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)DstFormat); GalImageFormat DstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)DstFormat);
@ -86,23 +93,42 @@ namespace Ryujinx.Graphics
DstLayout, DstLayout,
DstImgFormat); DstImgFormat);
SrcTexture.Pitch = SrcPitch;
DstTexture.Pitch = DstPitch;
Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture); Gpu.ResourceManager.SendTexture(Vmm, SrcKey, SrcTexture);
Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture); Gpu.ResourceManager.SendTexture(Vmm, DstKey, DstTexture);
int Width = Math.Min(SrcWidth, DstWidth);
int Height = Math.Min(SrcHeight, DstHeight);
Gpu.Renderer.RenderTarget.Copy( Gpu.Renderer.RenderTarget.Copy(
SrcKey, SrcKey,
DstKey, DstKey,
0, SrcBlitX,
0, SrcBlitY,
Width, SrcBlitX + DstBlitW,
Height, SrcBlitY + DstBlitH,
0, DstBlitX,
0, DstBlitY,
Width, DstBlitX + DstBlitW,
Height); DstBlitY + DstBlitH);
//Do a guest side copy aswell. This is necessary when
//the texture is modified by the guest, however it doesn't
//work when resources that the gpu can write to are copied,
//like framebuffers.
ImageUtils.CopyTexture(
Vmm,
SrcTexture,
DstTexture,
SrcAddress,
DstAddress,
SrcBlitX,
SrcBlitY,
DstBlitX,
DstBlitY,
DstBlitW,
DstBlitH);
Vmm.IsRegionModified(DstKey, ImageUtils.GetSize(DstTexture), NvGpuBufferType.Texture);
} }
private static GalMemoryLayout GetLayout(bool Linear) private static GalMemoryLayout GetLayout(bool Linear)
@ -119,14 +145,9 @@ namespace Ryujinx.Graphics
(uint)Registers[(int)Reg + 1]; (uint)Registers[(int)Reg + 1];
} }
private void WriteRegister(NvGpuPBEntry PBEntry) private void WriteRegister(GpuMethodCall MethCall)
{ {
int ArgsCount = PBEntry.Arguments.Count; Registers[MethCall.Method] = MethCall.Argument;
if (ArgsCount > 0)
{
Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
}
} }
private int ReadRegister(NvGpuEngine2dReg Reg) private int ReadRegister(NvGpuEngine2dReg Reg)

View file

@ -7,7 +7,7 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics namespace Ryujinx.Graphics
{ {
public class NvGpuEngine3d : INvGpuEngine class NvGpuEngine3d : INvGpuEngine
{ {
public int[] Registers { get; private set; } public int[] Registers { get; private set; }
@ -24,8 +24,6 @@ namespace Ryujinx.Graphics
private ConstBuffer[][] ConstBuffers; private ConstBuffer[][] ConstBuffers;
private List<long>[] UploadedKeys;
private int CurrentInstance = 0; private int CurrentInstance = 0;
public NvGpuEngine3d(NvGpu Gpu) public NvGpuEngine3d(NvGpu Gpu)
@ -59,13 +57,6 @@ namespace Ryujinx.Graphics
ConstBuffers[Index] = new ConstBuffer[18]; ConstBuffers[Index] = new ConstBuffer[18];
} }
UploadedKeys = new List<long>[(int)NvGpuBufferType.Count];
for (int i = 0; i < UploadedKeys.Length; i++)
{
UploadedKeys[i] = new List<long>();
}
//Ensure that all components are enabled by default. //Ensure that all components are enabled by default.
//FIXME: Is this correct? //FIXME: Is this correct?
WriteRegister(NvGpuEngine3dReg.ColorMaskN, 0x1111); WriteRegister(NvGpuEngine3dReg.ColorMaskN, 0x1111);
@ -81,27 +72,19 @@ namespace Ryujinx.Graphics
} }
} }
public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method))
{ {
Method(Vmm, PBEntry); Method(Vmm, MethCall);
} }
else else
{ {
WriteRegister(PBEntry); WriteRegister(MethCall);
} }
} }
public void ResetCache() private void VertexEndGl(NvGpuVmm Vmm, GpuMethodCall MethCall)
{
foreach (List<long> Uploaded in UploadedKeys)
{
Uploaded.Clear();
}
}
private void VertexEndGl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{ {
LockCaches(); LockCaches();
@ -152,13 +135,11 @@ namespace Ryujinx.Graphics
Gpu.Renderer.Texture.UnlockCache(); Gpu.Renderer.Texture.UnlockCache();
} }
private void ClearBuffers(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void ClearBuffers(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
int Arg0 = PBEntry.Arguments[0]; int Attachment = (MethCall.Argument >> 6) & 0xf;
int Attachment = (Arg0 >> 6) & 0xf; GalClearBufferFlags Flags = (GalClearBufferFlags)(MethCall.Argument & 0x3f);
GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f);
float Red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0); float Red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0);
float Green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1); float Green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1);
@ -234,6 +215,15 @@ namespace Ryujinx.Graphics
State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); State.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX);
State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); State.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY);
int ScreenYControl = ReadRegister(NvGpuEngine3dReg.ScreenYControl);
bool NegateY = (ScreenYControl & 1) != 0;
if (NegateY)
{
State.FlipY = -State.FlipY;
}
} }
private void SetZeta(NvGpuVmm Vmm) private void SetZeta(NvGpuVmm Vmm)
@ -566,8 +556,11 @@ namespace Ryujinx.Graphics
return; return;
} }
bool LinkedTsc = ReadRegisterBool(NvGpuEngine3dReg.LinkedTsc);
int TicIndex = (TextureHandle >> 0) & 0xfffff; int TicIndex = (TextureHandle >> 0) & 0xfffff;
int TscIndex = (TextureHandle >> 20) & 0xfff;
int TscIndex = LinkedTsc ? TicIndex : (TextureHandle >> 20) & 0xfff;
long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); long TicPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset);
long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); long TscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset);
@ -618,7 +611,7 @@ namespace Ryujinx.Graphics
long Key = Vmm.GetPhysicalAddress(Cb.Position); long Key = Vmm.GetPhysicalAddress(Cb.Position);
if (QueryKeyUpload(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer)) if (Gpu.ResourceManager.MemoryRegionModified(Vmm, Key, Cb.Size, NvGpuBufferType.ConstBuffer))
{ {
IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size); IntPtr Source = Vmm.GetHostAddress(Cb.Position, Cb.Size);
@ -661,7 +654,7 @@ namespace Ryujinx.Graphics
PrimType == GalPrimitiveType.Quads || PrimType == GalPrimitiveType.Quads ||
PrimType == GalPrimitiveType.QuadStrip; PrimType == GalPrimitiveType.QuadStrip;
if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) if (!IboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index))
{ {
if (!UsesLegacyQuads) if (!UsesLegacyQuads)
{ {
@ -778,7 +771,7 @@ namespace Ryujinx.Graphics
bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize); bool VboCached = Gpu.Renderer.Rasterizer.IsVboCached(VboKey, VbSize);
if (!VboCached || QueryKeyUpload(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex)) if (!VboCached || Gpu.ResourceManager.MemoryRegionModified(Vmm, VboKey, VbSize, NvGpuBufferType.Vertex))
{ {
IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize); IntPtr DataAddress = Vmm.GetHostAddress(VertexPosition, VbSize);
@ -877,9 +870,9 @@ namespace Ryujinx.Graphics
WriteCounterAndTimestamp WriteCounterAndTimestamp
} }
private void QueryControl(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void QueryControl(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
WriteRegister(PBEntry); WriteRegister(MethCall);
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress);
@ -909,29 +902,24 @@ namespace Ryujinx.Graphics
} }
} }
private void CbData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void CbData(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); long Position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress);
int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); int Offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset);
foreach (int Arg in PBEntry.Arguments) Vmm.WriteInt32(Position + Offset, MethCall.Argument);
{
Vmm.WriteInt32(Position + Offset, Arg);
Offset += 4; WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset + 4);
}
WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, Offset); Gpu.ResourceManager.ClearPbCache(NvGpuBufferType.ConstBuffer);
UploadedKeys[(int)NvGpuBufferType.ConstBuffer].Clear();
} }
private void CbBind(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void CbBind(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
int Stage = (PBEntry.Method - 0x904) >> 3; int Stage = (MethCall.Method - 0x904) >> 3;
int Index = PBEntry.Arguments[0]; int Index = MethCall.Argument;
bool Enabled = (Index & 1) != 0; bool Enabled = (Index & 1) != 0;
@ -970,14 +958,9 @@ namespace Ryujinx.Graphics
(uint)Registers[(int)Reg + 1]; (uint)Registers[(int)Reg + 1];
} }
private void WriteRegister(NvGpuPBEntry PBEntry) private void WriteRegister(GpuMethodCall MethCall)
{ {
int ArgsCount = PBEntry.Arguments.Count; Registers[MethCall.Method] = MethCall.Argument;
if (ArgsCount > 0)
{
Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
}
} }
private int ReadRegister(NvGpuEngine3dReg Reg) private int ReadRegister(NvGpuEngine3dReg Reg)
@ -999,19 +982,5 @@ namespace Ryujinx.Graphics
{ {
Registers[(int)Reg] = Value; Registers[(int)Reg] = Value;
} }
private bool QueryKeyUpload(NvGpuVmm Vmm, long Key, long Size, NvGpuBufferType Type)
{
List<long> Uploaded = UploadedKeys[(int)Type];
if (Uploaded.Contains(Key))
{
return false;
}
Uploaded.Add(Key);
return Vmm.IsRegionModified(Key, Size, Type);
}
} }
} }

View file

@ -36,6 +36,7 @@ namespace Ryujinx.Graphics
ZetaHoriz = 0x48a, ZetaHoriz = 0x48a,
ZetaVert = 0x48b, ZetaVert = 0x48b,
ZetaArrayMode = 0x48c, ZetaArrayMode = 0x48c,
LinkedTsc = 0x48d,
DepthTestEnable = 0x4b3, DepthTestEnable = 0x4b3,
BlendIndependent = 0x4b9, BlendIndependent = 0x4b9,
DepthWriteEnable = 0x4ba, DepthWriteEnable = 0x4ba,
@ -57,6 +58,7 @@ namespace Ryujinx.Graphics
StencilFrontFuncRef = 0x4e5, StencilFrontFuncRef = 0x4e5,
StencilFrontFuncMask = 0x4e6, StencilFrontFuncMask = 0x4e6,
StencilFrontMask = 0x4e7, StencilFrontMask = 0x4e7,
ScreenYControl = 0x4eb,
VertexArrayElemBase = 0x50d, VertexArrayElemBase = 0x50d,
VertexArrayInstBase = 0x50e, VertexArrayInstBase = 0x50e,
ZetaEnable = 0x54e, ZetaEnable = 0x54e,

View file

@ -4,7 +4,7 @@ using System.Collections.Generic;
namespace Ryujinx.Graphics namespace Ryujinx.Graphics
{ {
public class NvGpuEngineM2mf : INvGpuEngine class NvGpuEngineM2mf : INvGpuEngine
{ {
public int[] Registers { get; private set; } public int[] Registers { get; private set; }
@ -33,22 +33,22 @@ namespace Ryujinx.Graphics
AddMethod(0xc0, 1, 1, Execute); AddMethod(0xc0, 1, 1, Execute);
} }
public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method))
{ {
Method(Vmm, PBEntry); Method(Vmm, MethCall);
} }
else else
{ {
WriteRegister(PBEntry); WriteRegister(MethCall);
} }
} }
private void Execute(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
//TODO: Some registers and copy modes are still not implemented. //TODO: Some registers and copy modes are still not implemented.
int Control = PBEntry.Arguments[0]; int Control = MethCall.Argument;
bool SrcLinear = ((Control >> 7) & 1) != 0; bool SrcLinear = ((Control >> 7) & 1) != 0;
bool DstLinear = ((Control >> 8) & 1) != 0; bool DstLinear = ((Control >> 8) & 1) != 0;
@ -169,14 +169,9 @@ namespace Ryujinx.Graphics
(uint)Registers[(int)Reg + 1]; (uint)Registers[(int)Reg + 1];
} }
private void WriteRegister(NvGpuPBEntry PBEntry) private void WriteRegister(GpuMethodCall MethCall)
{ {
int ArgsCount = PBEntry.Arguments.Count; Registers[MethCall.Method] = MethCall.Argument;
if (ArgsCount > 0)
{
Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
}
} }
private int ReadRegister(NvGpuEngineM2mfReg Reg) private int ReadRegister(NvGpuEngineM2mfReg Reg)

View file

@ -1,10 +1,10 @@
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using Ryujinx.Graphics.Texture;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Ryujinx.Graphics namespace Ryujinx.Graphics
{ {
public class NvGpuEngineP2mf : INvGpuEngine class NvGpuEngineP2mf : INvGpuEngine
{ {
public int[] Registers { get; private set; } public int[] Registers { get; private set; }
@ -12,7 +12,21 @@ namespace Ryujinx.Graphics
private Dictionary<int, NvGpuMethod> Methods; private Dictionary<int, NvGpuMethod> Methods;
private ReadOnlyCollection<int> DataBuffer; private int CopyStartX;
private int CopyStartY;
private int CopyWidth;
private int CopyHeight;
private int CopyGobBlockHeight;
private long CopyAddress;
private int CopyOffset;
private int CopySize;
private bool CopyLinear;
private byte[] Buffer;
public NvGpuEngineP2mf(NvGpu Gpu) public NvGpuEngineP2mf(NvGpu Gpu)
{ {
@ -36,40 +50,90 @@ namespace Ryujinx.Graphics
AddMethod(0x6d, 1, 1, PushData); AddMethod(0x6d, 1, 1, PushData);
} }
public void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
if (Methods.TryGetValue(PBEntry.Method, out NvGpuMethod Method)) if (Methods.TryGetValue(MethCall.Method, out NvGpuMethod Method))
{ {
Method(Vmm, PBEntry); Method(Vmm, MethCall);
} }
else else
{ {
WriteRegister(PBEntry); WriteRegister(MethCall);
} }
} }
private void Execute(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void Execute(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
//TODO: Some registers and copy modes are still not implemented. //TODO: Some registers and copy modes are still not implemented.
int Control = PBEntry.Arguments[0]; int Control = MethCall.Argument;
long DstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress); long DstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress);
int DstPitch = ReadRegister(NvGpuEngineP2mfReg.DstPitch);
int DstBlkDim = ReadRegister(NvGpuEngineP2mfReg.DstBlockDim);
int DstX = ReadRegister(NvGpuEngineP2mfReg.DstX);
int DstY = ReadRegister(NvGpuEngineP2mfReg.DstY);
int DstWidth = ReadRegister(NvGpuEngineP2mfReg.DstWidth);
int DstHeight = ReadRegister(NvGpuEngineP2mfReg.DstHeight);
int LineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn); int LineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn);
int LineCount = ReadRegister(NvGpuEngineP2mfReg.LineCount);
DataBuffer = null; CopyLinear = (Control & 1) != 0;
Gpu.Fifo.Step(); CopyGobBlockHeight = 1 << ((DstBlkDim >> 4) & 0xf);
for (int Offset = 0; Offset < LineLengthIn; Offset += 4) CopyStartX = DstX;
{ CopyStartY = DstY;
Vmm.WriteInt32(DstAddress + Offset, DataBuffer[Offset >> 2]);
} CopyWidth = DstWidth;
CopyHeight = DstHeight;
CopyAddress = DstAddress;
CopyOffset = 0;
CopySize = LineLengthIn * LineCount;
Buffer = new byte[CopySize];
} }
private void PushData(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void PushData(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
DataBuffer = PBEntry.Arguments; if (Buffer == null)
{
return;
}
for (int Shift = 0; Shift < 32 && CopyOffset < CopySize; Shift += 8, CopyOffset++)
{
Buffer[CopyOffset] = (byte)(MethCall.Argument >> Shift);
}
if (MethCall.IsLastCall)
{
if (CopyLinear)
{
Vmm.WriteBytes(CopyAddress, Buffer);
}
else
{
BlockLinearSwizzle Swizzle = new BlockLinearSwizzle(CopyWidth, 1, CopyGobBlockHeight);
int SrcOffset = 0;
for (int Y = CopyStartY; Y < CopyHeight && SrcOffset < CopySize; Y++)
for (int X = CopyStartX; X < CopyWidth && SrcOffset < CopySize; X++)
{
int DstOffset = Swizzle.GetSwizzleOffset(X, Y);
Vmm.WriteByte(CopyAddress + DstOffset, Buffer[SrcOffset++]);
}
}
Buffer = null;
}
} }
private long MakeInt64From2xInt32(NvGpuEngineP2mfReg Reg) private long MakeInt64From2xInt32(NvGpuEngineP2mfReg Reg)
@ -79,14 +143,9 @@ namespace Ryujinx.Graphics
(uint)Registers[(int)Reg + 1]; (uint)Registers[(int)Reg + 1];
} }
private void WriteRegister(NvGpuPBEntry PBEntry) private void WriteRegister(GpuMethodCall MethCall)
{ {
int ArgsCount = PBEntry.Arguments.Count; Registers[MethCall.Method] = MethCall.Argument;
if (ArgsCount > 0)
{
Registers[PBEntry.Method] = PBEntry.Arguments[ArgsCount - 1];
}
} }
private int ReadRegister(NvGpuEngineP2mfReg Reg) private int ReadRegister(NvGpuEngineP2mfReg Reg)

View file

@ -1,10 +1,8 @@
using Ryujinx.Graphics.Memory; using Ryujinx.Graphics.Memory;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Graphics namespace Ryujinx.Graphics
{ {
public class NvGpuFifo class NvGpuFifo
{ {
private const int MacrosCount = 0x80; private const int MacrosCount = 0x80;
private const int MacroIndexMask = MacrosCount - 1; private const int MacroIndexMask = MacrosCount - 1;
@ -15,33 +13,47 @@ namespace Ryujinx.Graphics
private NvGpu Gpu; private NvGpu Gpu;
private ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry[])> BufferQueue;
private NvGpuEngine[] SubChannels; private NvGpuEngine[] SubChannels;
public AutoResetEvent Event { get; private set; }
private struct CachedMacro private struct CachedMacro
{ {
public int Position { get; private set; } public int Position { get; private set; }
private bool ExecutionPending;
private int Argument;
private MacroInterpreter Interpreter; private MacroInterpreter Interpreter;
public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, int Position) public CachedMacro(NvGpuFifo PFifo, INvGpuEngine Engine, int Position)
{ {
this.Position = Position; this.Position = Position;
ExecutionPending = false;
Argument = 0;
Interpreter = new MacroInterpreter(PFifo, Engine); Interpreter = new MacroInterpreter(PFifo, Engine);
} }
public void PushParam(int Param) public void StartExecution(int Argument)
{ {
Interpreter?.Fifo.Enqueue(Param); this.Argument = Argument;
ExecutionPending = true;
} }
public void Execute(NvGpuVmm Vmm, int[] Mme, int Param) public void Execute(NvGpuVmm Vmm, int[] Mme)
{ {
Interpreter?.Execute(Vmm, Mme, Position, Param); if (ExecutionPending)
{
ExecutionPending = false;
Interpreter?.Execute(Vmm, Mme, Position, Argument);
}
}
public void PushArgument(int Argument)
{
Interpreter?.Fifo.Enqueue(Argument);
} }
} }
@ -56,148 +68,109 @@ namespace Ryujinx.Graphics
{ {
this.Gpu = Gpu; this.Gpu = Gpu;
BufferQueue = new ConcurrentQueue<(NvGpuVmm, NvGpuPBEntry[])>();
SubChannels = new NvGpuEngine[8]; SubChannels = new NvGpuEngine[8];
Macros = new CachedMacro[MacrosCount]; Macros = new CachedMacro[MacrosCount];
Mme = new int[MmeWords]; Mme = new int[MmeWords];
Event = new AutoResetEvent(false);
} }
public void PushBuffer(NvGpuVmm Vmm, NvGpuPBEntry[] Buffer) public void CallMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
BufferQueue.Enqueue((Vmm, Buffer)); if ((NvGpuFifoMeth)MethCall.Method == NvGpuFifoMeth.BindChannel)
Event.Set();
}
public void DispatchCalls()
{
while (Step());
}
private (NvGpuVmm Vmm, NvGpuPBEntry[] Pb) Curr;
private int CurrPbEntryIndex;
public bool Step()
{
while (Curr.Pb == null || Curr.Pb.Length <= CurrPbEntryIndex)
{ {
if (!BufferQueue.TryDequeue(out Curr)) NvGpuEngine Engine = (NvGpuEngine)MethCall.Argument;
{
return false;
}
Gpu.Engine3d.ResetCache(); SubChannels[MethCall.SubChannel] = Engine;
Gpu.ResourceManager.ClearPbCache();
CurrPbEntryIndex = 0;
}
CallMethod(Curr.Vmm, Curr.Pb[CurrPbEntryIndex++]);
return true;
}
private void CallMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry)
{
if ((NvGpuFifoMeth)PBEntry.Method == NvGpuFifoMeth.BindChannel)
{
NvGpuEngine Engine = (NvGpuEngine)PBEntry.Arguments[0];
SubChannels[PBEntry.SubChannel] = Engine;
} }
else else
{ {
switch (SubChannels[PBEntry.SubChannel]) switch (SubChannels[MethCall.SubChannel])
{ {
case NvGpuEngine._2d: Call2dMethod (Vmm, PBEntry); break; case NvGpuEngine._2d: Call2dMethod (Vmm, MethCall); break;
case NvGpuEngine._3d: Call3dMethod (Vmm, PBEntry); break; case NvGpuEngine._3d: Call3dMethod (Vmm, MethCall); break;
case NvGpuEngine.P2mf: CallP2mfMethod(Vmm, PBEntry); break; case NvGpuEngine.P2mf: CallP2mfMethod(Vmm, MethCall); break;
case NvGpuEngine.M2mf: CallM2mfMethod(Vmm, PBEntry); break; case NvGpuEngine.M2mf: CallM2mfMethod(Vmm, MethCall); break;
} }
} }
} }
private void Call2dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void Call2dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
Gpu.Engine2d.CallMethod(Vmm, PBEntry); Gpu.Engine2d.CallMethod(Vmm, MethCall);
} }
private void Call3dMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void Call3dMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
if (PBEntry.Method < 0x80) if (MethCall.Method < 0x80)
{ {
switch ((NvGpuFifoMeth)PBEntry.Method) switch ((NvGpuFifoMeth)MethCall.Method)
{ {
case NvGpuFifoMeth.SetMacroUploadAddress: case NvGpuFifoMeth.SetMacroUploadAddress:
{ {
CurrMacroPosition = PBEntry.Arguments[0]; CurrMacroPosition = MethCall.Argument;
break; break;
} }
case NvGpuFifoMeth.SendMacroCodeData: case NvGpuFifoMeth.SendMacroCodeData:
{ {
foreach (int Arg in PBEntry.Arguments) Mme[CurrMacroPosition++] = MethCall.Argument;
{
Mme[CurrMacroPosition++] = Arg;
}
break; break;
} }
case NvGpuFifoMeth.SetMacroBindingIndex: case NvGpuFifoMeth.SetMacroBindingIndex:
{ {
CurrMacroBindIndex = PBEntry.Arguments[0]; CurrMacroBindIndex = MethCall.Argument;
break; break;
} }
case NvGpuFifoMeth.BindMacro: case NvGpuFifoMeth.BindMacro:
{ {
int Position = PBEntry.Arguments[0]; int Position = MethCall.Argument;
Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position); Macros[CurrMacroBindIndex] = new CachedMacro(this, Gpu.Engine3d, Position);
break; break;
} }
default: CallP2mfMethod(Vmm, MethCall); break;
} }
} }
else if (PBEntry.Method < 0xe00) else if (MethCall.Method < 0xe00)
{ {
Gpu.Engine3d.CallMethod(Vmm, PBEntry); Gpu.Engine3d.CallMethod(Vmm, MethCall);
} }
else else
{ {
int MacroIndex = (PBEntry.Method >> 1) & MacroIndexMask; int MacroIndex = (MethCall.Method >> 1) & MacroIndexMask;
if ((PBEntry.Method & 1) != 0) if ((MethCall.Method & 1) != 0)
{ {
foreach (int Arg in PBEntry.Arguments) Macros[MacroIndex].PushArgument(MethCall.Argument);
{
Macros[MacroIndex].PushParam(Arg);
}
} }
else else
{ {
Macros[MacroIndex].Execute(Vmm, Mme, PBEntry.Arguments[0]); Macros[MacroIndex].StartExecution(MethCall.Argument);
}
if (MethCall.IsLastCall)
{
Macros[MacroIndex].Execute(Vmm, Mme);
} }
} }
} }
private void CallP2mfMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void CallP2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
Gpu.EngineP2mf.CallMethod(Vmm, PBEntry); Gpu.EngineP2mf.CallMethod(Vmm, MethCall);
} }
private void CallM2mfMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry) private void CallM2mfMethod(NvGpuVmm Vmm, GpuMethodCall MethCall)
{ {
Gpu.EngineM2mf.CallMethod(Vmm, PBEntry); Gpu.EngineM2mf.CallMethod(Vmm, MethCall);
} }
} }
} }

View file

@ -2,5 +2,5 @@ using Ryujinx.Graphics.Memory;
namespace Ryujinx.Graphics namespace Ryujinx.Graphics
{ {
delegate void NvGpuMethod(NvGpuVmm Vmm, NvGpuPBEntry PBEntry); delegate void NvGpuMethod(NvGpuVmm Vmm, GpuMethodCall MethCall);
} }

View file

@ -95,6 +95,7 @@ namespace Ryujinx.Graphics.Texture
{ GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBX8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
{ GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) },
@ -131,9 +132,10 @@ namespace Ryujinx.Graphics.Texture
{ GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) }, { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) },
{ GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) }, { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) },
{ GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D24, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) }, { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) },
{ GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) },
{ GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) } { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) }
}; };
@ -198,6 +200,7 @@ namespace Ryujinx.Graphics.Texture
case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint; case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint;
case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm; case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm;
case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm; case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm;
case GalSurfaceFormat.RGBX8Unorm: return GalImageFormat.RGBX8 | Unorm;
} }
throw new NotImplementedException(Format.ToString()); throw new NotImplementedException(Format.ToString());
@ -210,6 +213,7 @@ namespace Ryujinx.Graphics.Texture
case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float; case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float;
case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm; case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm;
case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm; case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm;
case GalZetaFormat.D24X8Unorm: return GalImageFormat.D24 | Unorm;
case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm; case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm;
case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float; case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float;
} }
@ -247,7 +251,7 @@ namespace Ryujinx.Graphics.Texture
{ {
int OutOffs = Y * Pitch; int OutOffs = Y * Pitch;
for (int X = 0; X < Width; X++) for (int X = 0; X < Width; X++)
{ {
long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y);
@ -283,6 +287,45 @@ namespace Ryujinx.Graphics.Texture
} }
} }
public static bool CopyTexture(
NvGpuVmm Vmm,
GalImage SrcImage,
GalImage DstImage,
long SrcAddress,
long DstAddress,
int SrcX,
int SrcY,
int DstX,
int DstY,
int Width,
int Height)
{
ISwizzle SrcSwizzle = TextureHelper.GetSwizzle(SrcImage);
ISwizzle DstSwizzle = TextureHelper.GetSwizzle(DstImage);
ImageDescriptor Desc = GetImageDescriptor(SrcImage.Format);
if (GetImageDescriptor(DstImage.Format).BytesPerPixel != Desc.BytesPerPixel)
{
return false;
}
int BytesPerPixel = Desc.BytesPerPixel;
for (int Y = 0; Y < Height; Y++)
for (int X = 0; X < Width; X++)
{
long SrcOffset = (uint)SrcSwizzle.GetSwizzleOffset(SrcX + X, SrcY + Y);
long DstOffset = (uint)DstSwizzle.GetSwizzleOffset(DstX + X, DstY + Y);
byte[] Texel = Vmm.ReadBytes(SrcAddress + SrcOffset, BytesPerPixel);
Vmm.WriteBytes(DstAddress + DstOffset, Texel);
}
return true;
}
public static int GetSize(GalImage Image) public static int GetSize(GalImage Image)
{ {
ImageDescriptor Desc = GetImageDescriptor(Image.Format); ImageDescriptor Desc = GetImageDescriptor(Image.Format);

View file

@ -76,7 +76,7 @@ namespace Ryujinx.Graphics
{ {
Ranges.RemoveAt(NewIndex + 1); Ranges.RemoveAt(NewIndex + 1);
Ranges[NewIndex] = new ValueRange<T>(Range.Start, Next.End, Range.Value); Ranges[NewIndex] = new ValueRange<T>(Ranges[NewIndex].Start, Next.End, Range.Value);
} }
} }
} }

View file

@ -181,15 +181,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel
private static void PushGpfifo(ServiceCtx Context, NvGpuVmm Vmm, long Gpfifo) private static void PushGpfifo(ServiceCtx Context, NvGpuVmm Vmm, long Gpfifo)
{ {
long VA = Gpfifo & 0xff_ffff_ffff; Context.Device.Gpu.Pusher.Push(Vmm, Gpfifo);
int Size = (int)(Gpfifo >> 40) & 0x7ffffc;
byte[] Data = Vmm.ReadBytes(VA, Size);
NvGpuPBEntry[] PushBuffer = NvGpuPushBuffer.Decode(Data);
Context.Device.Gpu.Fifo.PushBuffer(Vmm, PushBuffer);
} }
public static NvChannel GetChannel(ServiceCtx Context, NvChannelName Channel) public static NvChannel GetChannel(ServiceCtx Context, NvChannelName Channel)

View file

@ -88,12 +88,12 @@ namespace Ryujinx.HLE
public bool WaitFifo() public bool WaitFifo()
{ {
return Gpu.Fifo.Event.WaitOne(8); return Gpu.Pusher.WaitForCommands();
} }
public void ProcessFrame() public void ProcessFrame()
{ {
Gpu.Fifo.DispatchCalls(); Gpu.Pusher.DispatchCalls();
} }
internal void Unload() internal void Unload()