mirror of
https://github.com/GreemDev/Ryujinx
synced 2024-11-24 10:46:54 +01:00
Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
519 changed files with 15258 additions and 12574 deletions
|
@ -355,12 +355,9 @@ namespace ARMeilleure.Instructions
|
||||||
}
|
}
|
||||||
while (bit < context.Memory.AddressSpaceBits);
|
while (bit < context.Memory.AddressSpaceBits);
|
||||||
|
|
||||||
if (!context.Memory.HasWriteWatchSupport)
|
Operand hasFlagSet = context.BitwiseAnd(pte, Const((long)MemoryManager.PteFlagsMask));
|
||||||
{
|
|
||||||
Operand hasFlagSet = context.BitwiseAnd(pte, Const((long)MemoryManager.PteFlagsMask));
|
|
||||||
|
|
||||||
context.BranchIfTrue(lblFallbackPath, hasFlagSet);
|
context.BranchIfTrue(lblFallbackPath, hasFlagSet);
|
||||||
}
|
|
||||||
|
|
||||||
Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, MemoryManager.PageMask));
|
Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, MemoryManager.PageMask));
|
||||||
|
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
namespace ARMeilleure.Memory
|
|
||||||
{
|
|
||||||
public interface IMemory
|
|
||||||
{
|
|
||||||
sbyte ReadSByte(long position);
|
|
||||||
|
|
||||||
short ReadInt16(long position);
|
|
||||||
|
|
||||||
int ReadInt32(long position);
|
|
||||||
|
|
||||||
long ReadInt64(long position);
|
|
||||||
|
|
||||||
byte ReadByte(long position);
|
|
||||||
|
|
||||||
ushort ReadUInt16(long position);
|
|
||||||
|
|
||||||
uint ReadUInt32(long position);
|
|
||||||
|
|
||||||
ulong ReadUInt64(long position);
|
|
||||||
|
|
||||||
void WriteSByte(long position, sbyte value);
|
|
||||||
|
|
||||||
void WriteInt16(long position, short value);
|
|
||||||
|
|
||||||
void WriteInt32(long position, int value);
|
|
||||||
|
|
||||||
void WriteInt64(long position, long value);
|
|
||||||
|
|
||||||
void WriteByte(long position, byte value);
|
|
||||||
|
|
||||||
void WriteUInt16(long position, ushort value);
|
|
||||||
|
|
||||||
void WriteUInt32(long position, uint value);
|
|
||||||
|
|
||||||
void WriteUInt64(long position, ulong value);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,8 +6,6 @@ namespace ARMeilleure.Memory
|
||||||
{
|
{
|
||||||
public static class MemoryManagement
|
public static class MemoryManagement
|
||||||
{
|
{
|
||||||
public static bool HasWriteWatchSupport => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
|
||||||
|
|
||||||
public static IntPtr Allocate(ulong size)
|
public static IntPtr Allocate(ulong size)
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
@ -88,27 +86,5 @@ namespace ARMeilleure.Memory
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool GetModifiedPages(
|
|
||||||
IntPtr address,
|
|
||||||
IntPtr size,
|
|
||||||
IntPtr[] addresses,
|
|
||||||
out ulong count)
|
|
||||||
{
|
|
||||||
// This is only supported on windows, but returning
|
|
||||||
// false (failed) is also valid for platforms without
|
|
||||||
// write tracking support on the OS.
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
return MemoryManagementWindows.GetModifiedPages(address, size, addresses, out count);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
count = 0;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -36,12 +36,6 @@ namespace ARMeilleure.Memory
|
||||||
WriteCombineModifierflag = 0x400
|
WriteCombineModifierflag = 0x400
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum WriteWatchFlags : uint
|
|
||||||
{
|
|
||||||
None = 0,
|
|
||||||
Reset = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
[DllImport("kernel32.dll")]
|
||||||
private static extern IntPtr VirtualAlloc(
|
private static extern IntPtr VirtualAlloc(
|
||||||
IntPtr lpAddress,
|
IntPtr lpAddress,
|
||||||
|
@ -62,15 +56,6 @@ namespace ARMeilleure.Memory
|
||||||
IntPtr dwSize,
|
IntPtr dwSize,
|
||||||
AllocationType dwFreeType);
|
AllocationType dwFreeType);
|
||||||
|
|
||||||
[DllImport("kernel32.dll")]
|
|
||||||
private static extern int GetWriteWatch(
|
|
||||||
WriteWatchFlags dwFlags,
|
|
||||||
IntPtr lpBaseAddress,
|
|
||||||
IntPtr dwRegionSize,
|
|
||||||
IntPtr[] lpAddresses,
|
|
||||||
ref ulong lpdwCount,
|
|
||||||
out uint lpdwGranularity);
|
|
||||||
|
|
||||||
public static IntPtr Allocate(IntPtr size)
|
public static IntPtr Allocate(IntPtr size)
|
||||||
{
|
{
|
||||||
const AllocationType flags =
|
const AllocationType flags =
|
||||||
|
@ -130,27 +115,5 @@ namespace ARMeilleure.Memory
|
||||||
{
|
{
|
||||||
return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
|
return VirtualFree(address, IntPtr.Zero, AllocationType.Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool GetModifiedPages(
|
|
||||||
IntPtr address,
|
|
||||||
IntPtr size,
|
|
||||||
IntPtr[] addresses,
|
|
||||||
out ulong count)
|
|
||||||
{
|
|
||||||
ulong pagesCount = (ulong)addresses.Length;
|
|
||||||
|
|
||||||
int result = GetWriteWatch(
|
|
||||||
WriteWatchFlags.Reset,
|
|
||||||
address,
|
|
||||||
size,
|
|
||||||
addresses,
|
|
||||||
ref pagesCount,
|
|
||||||
out uint granularity);
|
|
||||||
|
|
||||||
count = pagesCount;
|
|
||||||
|
|
||||||
return result == 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -29,8 +30,6 @@ namespace ARMeilleure.Memory
|
||||||
internal int PtLevelSize { get; }
|
internal int PtLevelSize { get; }
|
||||||
internal int PtLevelMask { get; }
|
internal int PtLevelMask { get; }
|
||||||
|
|
||||||
public bool HasWriteWatchSupport => MemoryManagement.HasWriteWatchSupport;
|
|
||||||
|
|
||||||
public int AddressSpaceBits { get; }
|
public int AddressSpaceBits { get; }
|
||||||
public long AddressSpaceSize { get; }
|
public long AddressSpaceSize { get; }
|
||||||
|
|
||||||
|
@ -254,119 +253,57 @@ namespace ARMeilleure.Memory
|
||||||
return ptePtr;
|
return ptePtr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRegionModified(long position, long size)
|
public unsafe (ulong, ulong)[] GetModifiedRanges(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
if (!HasWriteWatchSupport)
|
List<(ulong, ulong)> ranges = new List<(ulong, ulong)>();
|
||||||
|
|
||||||
|
ulong endAddress = (address + size + PageMask) & ~(ulong)PageMask;
|
||||||
|
|
||||||
|
address &= ~(ulong)PageMask;
|
||||||
|
|
||||||
|
ulong currAddr = address;
|
||||||
|
ulong currSize = 0;
|
||||||
|
|
||||||
|
while (address < endAddress)
|
||||||
{
|
{
|
||||||
return IsRegionModifiedFallback(position, size);
|
if (IsValidPosition((long)address))
|
||||||
}
|
|
||||||
|
|
||||||
IntPtr address = Translate(position);
|
|
||||||
|
|
||||||
IntPtr baseAddr = address;
|
|
||||||
IntPtr expectedAddr = address;
|
|
||||||
|
|
||||||
long pendingPages = 0;
|
|
||||||
|
|
||||||
long pages = size / PageSize;
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
|
|
||||||
bool IsAnyPageModified()
|
|
||||||
{
|
|
||||||
IntPtr pendingSize = new IntPtr(pendingPages * PageSize);
|
|
||||||
|
|
||||||
IntPtr[] addresses = new IntPtr[pendingPages];
|
|
||||||
|
|
||||||
bool result = GetModifiedPages(baseAddr, pendingSize, addresses, out ulong count);
|
|
||||||
|
|
||||||
if (result)
|
|
||||||
{
|
{
|
||||||
return count != 0;
|
byte* ptr = ((byte**)_pageTable)[address >> PageBits];
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (pages-- > 0)
|
|
||||||
{
|
|
||||||
if (address != expectedAddr)
|
|
||||||
{
|
|
||||||
modified |= IsAnyPageModified();
|
|
||||||
|
|
||||||
baseAddr = address;
|
|
||||||
|
|
||||||
pendingPages = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedAddr = address + PageSize;
|
|
||||||
|
|
||||||
pendingPages++;
|
|
||||||
|
|
||||||
if (pages == 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
position += PageSize;
|
|
||||||
|
|
||||||
address = Translate(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pendingPages != 0)
|
|
||||||
{
|
|
||||||
modified |= IsAnyPageModified();
|
|
||||||
}
|
|
||||||
|
|
||||||
return modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
private unsafe bool IsRegionModifiedFallback(long position, long size)
|
|
||||||
{
|
|
||||||
long endAddr = (position + size + PageMask) & ~PageMask;
|
|
||||||
|
|
||||||
bool modified = false;
|
|
||||||
|
|
||||||
while ((ulong)position < (ulong)endAddr)
|
|
||||||
{
|
|
||||||
if (IsValidPosition(position))
|
|
||||||
{
|
|
||||||
byte* ptr = ((byte**)_pageTable)[position >> PageBits];
|
|
||||||
|
|
||||||
ulong ptrUlong = (ulong)ptr;
|
ulong ptrUlong = (ulong)ptr;
|
||||||
|
|
||||||
if ((ptrUlong & PteFlagNotModified) == 0)
|
if ((ptrUlong & PteFlagNotModified) == 0)
|
||||||
{
|
{
|
||||||
modified = true;
|
// Modified.
|
||||||
|
currSize += PageSize;
|
||||||
|
|
||||||
SetPtEntryFlag(position, PteFlagNotModified);
|
SetPtEntryFlag((long)address, PteFlagNotModified);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (currSize != 0)
|
||||||
|
{
|
||||||
|
ranges.Add((currAddr, currSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
currAddr = address + PageSize;
|
||||||
|
currSize = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
modified = true;
|
currSize += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
position += PageSize;
|
address += PageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
return modified;
|
if (currSize != 0)
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetHostAddress(long position, long size, out IntPtr ptr)
|
|
||||||
{
|
|
||||||
if (IsContiguous(position, size))
|
|
||||||
{
|
{
|
||||||
ptr = (IntPtr)Translate(position);
|
ranges.Add((currAddr, currSize));
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ptr = IntPtr.Zero;
|
return ranges.ToArray();
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsContiguous(long position, long size)
|
private bool IsContiguous(long position, long size)
|
||||||
|
@ -612,41 +549,6 @@ namespace ARMeilleure.Memory
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadBytes(long position, byte[] data, int startIndex, int size)
|
|
||||||
{
|
|
||||||
// Note: This will be moved later.
|
|
||||||
long endAddr = position + size;
|
|
||||||
|
|
||||||
if ((ulong)size > int.MaxValue)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(size));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((ulong)endAddr < (ulong)position)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = startIndex;
|
|
||||||
|
|
||||||
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(Translate(position), data, offset, copySize);
|
|
||||||
|
|
||||||
position += copySize;
|
|
||||||
offset += copySize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WriteSByte(long position, sbyte value)
|
public void WriteSByte(long position, sbyte value)
|
||||||
{
|
{
|
||||||
WriteByte(position, (byte)value);
|
WriteByte(position, (byte)value);
|
||||||
|
@ -746,53 +648,6 @@ namespace ARMeilleure.Memory
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void WriteBytes(long position, byte[] data, int startIndex, int size)
|
|
||||||
{
|
|
||||||
// Note: This will be moved later.
|
|
||||||
long endAddr = position + size;
|
|
||||||
|
|
||||||
if ((ulong)endAddr < (ulong)position)
|
|
||||||
{
|
|
||||||
throw new ArgumentOutOfRangeException(nameof(position));
|
|
||||||
}
|
|
||||||
|
|
||||||
int offset = startIndex;
|
|
||||||
|
|
||||||
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, Translate(position), copySize);
|
|
||||||
|
|
||||||
position += copySize;
|
|
||||||
offset += copySize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CopyBytes(long src, long dst, long size)
|
|
||||||
{
|
|
||||||
// Note: This will be moved later.
|
|
||||||
if (IsContiguous(src, size) &&
|
|
||||||
IsContiguous(dst, size))
|
|
||||||
{
|
|
||||||
byte* srcPtr = (byte*)Translate(src);
|
|
||||||
byte* dstPtr = (byte*)Translate(dst);
|
|
||||||
|
|
||||||
Buffer.MemoryCopy(srcPtr, dstPtr, size, size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
WriteBytes(dst, ReadBytes(src, size));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Ryujinx.Common
|
namespace Ryujinx.Common
|
||||||
{
|
{
|
||||||
public static class PerformanceCounter
|
public static class PerformanceCounter
|
||||||
{
|
{
|
||||||
|
private static double _ticksToNs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents the number of ticks in 1 day.
|
/// Represents the number of ticks in 1 day.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -53,6 +55,19 @@ namespace Ryujinx.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the number of nanoseconds elapsed since the system started.
|
||||||
|
/// </summary>
|
||||||
|
public static long ElapsedNanoseconds
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
long timestamp = Stopwatch.GetTimestamp();
|
||||||
|
|
||||||
|
return (long)(timestamp * _ticksToNs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static PerformanceCounter()
|
static PerformanceCounter()
|
||||||
{
|
{
|
||||||
TicksPerMillisecond = Stopwatch.Frequency / 1000;
|
TicksPerMillisecond = Stopwatch.Frequency / 1000;
|
||||||
|
@ -60,6 +75,8 @@ namespace Ryujinx.Common
|
||||||
TicksPerMinute = TicksPerSecond * 60;
|
TicksPerMinute = TicksPerSecond * 60;
|
||||||
TicksPerHour = TicksPerMinute * 60;
|
TicksPerHour = TicksPerMinute * 60;
|
||||||
TicksPerDay = TicksPerHour * 24;
|
TicksPerDay = TicksPerHour * 24;
|
||||||
|
|
||||||
|
_ticksToNs = 1000000000.0 / (double)Stopwatch.Frequency;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
32
Ryujinx.Graphics.GAL/Blend/BlendDescriptor.cs
Normal file
32
Ryujinx.Graphics.GAL/Blend/BlendDescriptor.cs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Blend
|
||||||
|
{
|
||||||
|
public struct BlendDescriptor
|
||||||
|
{
|
||||||
|
public bool Enable { get; }
|
||||||
|
|
||||||
|
public BlendOp ColorOp { get; }
|
||||||
|
public BlendFactor ColorSrcFactor { get; }
|
||||||
|
public BlendFactor ColorDstFactor { get; }
|
||||||
|
public BlendOp AlphaOp { get; }
|
||||||
|
public BlendFactor AlphaSrcFactor { get; }
|
||||||
|
public BlendFactor AlphaDstFactor { get; }
|
||||||
|
|
||||||
|
public BlendDescriptor(
|
||||||
|
bool enable,
|
||||||
|
BlendOp colorOp,
|
||||||
|
BlendFactor colorSrcFactor,
|
||||||
|
BlendFactor colorDstFactor,
|
||||||
|
BlendOp alphaOp,
|
||||||
|
BlendFactor alphaSrcFactor,
|
||||||
|
BlendFactor alphaDstFactor)
|
||||||
|
{
|
||||||
|
Enable = enable;
|
||||||
|
ColorOp = colorOp;
|
||||||
|
ColorSrcFactor = colorSrcFactor;
|
||||||
|
ColorDstFactor = colorDstFactor;
|
||||||
|
AlphaOp = alphaOp;
|
||||||
|
AlphaSrcFactor = alphaSrcFactor;
|
||||||
|
AlphaDstFactor = alphaDstFactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
Ryujinx.Graphics.GAL/Blend/BlendFactor.cs
Normal file
25
Ryujinx.Graphics.GAL/Blend/BlendFactor.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Blend
|
||||||
|
{
|
||||||
|
public enum BlendFactor
|
||||||
|
{
|
||||||
|
Zero = 1,
|
||||||
|
One,
|
||||||
|
SrcColor,
|
||||||
|
OneMinusSrcColor,
|
||||||
|
SrcAlpha,
|
||||||
|
OneMinusSrcAlpha,
|
||||||
|
DstAlpha,
|
||||||
|
OneMinusDstAlpha,
|
||||||
|
DstColor,
|
||||||
|
OneMinusDstColor,
|
||||||
|
SrcAlphaSaturate,
|
||||||
|
Src1Color = 0x10,
|
||||||
|
OneMinusSrc1Color,
|
||||||
|
Src1Alpha,
|
||||||
|
OneMinusSrc1Alpha,
|
||||||
|
ConstantColor = 0xc001,
|
||||||
|
OneMinusConstantColor,
|
||||||
|
ConstantAlpha,
|
||||||
|
OneMinusConstantAlpha
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics.GAL/Blend/BlendOp.cs
Normal file
11
Ryujinx.Graphics.GAL/Blend/BlendOp.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Blend
|
||||||
|
{
|
||||||
|
public enum BlendOp
|
||||||
|
{
|
||||||
|
Add = 1,
|
||||||
|
Subtract,
|
||||||
|
ReverseSubtract,
|
||||||
|
Minimum,
|
||||||
|
Maximum
|
||||||
|
}
|
||||||
|
}
|
21
Ryujinx.Graphics.GAL/BufferRange.cs
Normal file
21
Ryujinx.Graphics.GAL/BufferRange.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public struct BufferRange
|
||||||
|
{
|
||||||
|
private static BufferRange _empty = new BufferRange(null, 0, 0);
|
||||||
|
|
||||||
|
public BufferRange Empty => _empty;
|
||||||
|
|
||||||
|
public IBuffer Buffer { get; }
|
||||||
|
|
||||||
|
public int Offset { get; }
|
||||||
|
public int Size { get; }
|
||||||
|
|
||||||
|
public BufferRange(IBuffer buffer, int offset, int size)
|
||||||
|
{
|
||||||
|
Buffer = buffer;
|
||||||
|
Offset = offset;
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/Capabilities.cs
Normal file
12
Ryujinx.Graphics.GAL/Capabilities.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public struct Capabilities
|
||||||
|
{
|
||||||
|
public bool SupportsAstcCompression { get; }
|
||||||
|
|
||||||
|
public Capabilities(bool supportsAstcCompression)
|
||||||
|
{
|
||||||
|
SupportsAstcCompression = supportsAstcCompression;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.GAL/Color/ColorF.cs
Normal file
18
Ryujinx.Graphics.GAL/Color/ColorF.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Color
|
||||||
|
{
|
||||||
|
public struct ColorF
|
||||||
|
{
|
||||||
|
public float Red { get; }
|
||||||
|
public float Green { get; }
|
||||||
|
public float Blue { get; }
|
||||||
|
public float Alpha { get; }
|
||||||
|
|
||||||
|
public ColorF(float red, float green, float blue, float alpha)
|
||||||
|
{
|
||||||
|
Red = red;
|
||||||
|
Green = green;
|
||||||
|
Blue = blue;
|
||||||
|
Alpha = alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.GAL/Color/ColorSI.cs
Normal file
18
Ryujinx.Graphics.GAL/Color/ColorSI.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Color
|
||||||
|
{
|
||||||
|
public struct ColorSI
|
||||||
|
{
|
||||||
|
public int Red { get; }
|
||||||
|
public int Green { get; }
|
||||||
|
public int Blue { get; }
|
||||||
|
public int Alpha { get; }
|
||||||
|
|
||||||
|
public ColorSI(int red, int green, int blue, int alpha)
|
||||||
|
{
|
||||||
|
Red = red;
|
||||||
|
Green = green;
|
||||||
|
Blue = blue;
|
||||||
|
Alpha = alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.GAL/Color/ColorUI.cs
Normal file
18
Ryujinx.Graphics.GAL/Color/ColorUI.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Color
|
||||||
|
{
|
||||||
|
public struct ColorUI
|
||||||
|
{
|
||||||
|
public uint Red { get; }
|
||||||
|
public uint Green { get; }
|
||||||
|
public uint Blue { get; }
|
||||||
|
public uint Alpha { get; }
|
||||||
|
|
||||||
|
public ColorUI(uint red, uint green, uint blue, uint alpha)
|
||||||
|
{
|
||||||
|
Red = red;
|
||||||
|
Green = green;
|
||||||
|
Blue = blue;
|
||||||
|
Alpha = alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics.GAL/CompareOp.cs
Normal file
14
Ryujinx.Graphics.GAL/CompareOp.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum CompareOp
|
||||||
|
{
|
||||||
|
Never = 1,
|
||||||
|
Less,
|
||||||
|
Equal,
|
||||||
|
LessOrEqual,
|
||||||
|
Greater,
|
||||||
|
NotEqual,
|
||||||
|
GreaterOrEqual,
|
||||||
|
Always
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Graphics.GAL/CounterType.cs
Normal file
9
Ryujinx.Graphics.GAL/CounterType.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum CounterType
|
||||||
|
{
|
||||||
|
SamplesPassed,
|
||||||
|
PrimitivesGenerated,
|
||||||
|
TransformFeedbackPrimitivesWritten
|
||||||
|
}
|
||||||
|
}
|
47
Ryujinx.Graphics.GAL/DepthStencil/DepthStencilState.cs
Normal file
47
Ryujinx.Graphics.GAL/DepthStencil/DepthStencilState.cs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.DepthStencil
|
||||||
|
{
|
||||||
|
public struct DepthStencilState
|
||||||
|
{
|
||||||
|
public bool DepthTestEnable { get; }
|
||||||
|
public bool DepthWriteEnable { get; }
|
||||||
|
public bool StencilTestEnable { get; }
|
||||||
|
|
||||||
|
public CompareOp DepthFunc { get; }
|
||||||
|
public CompareOp StencilFrontFunc { get; }
|
||||||
|
public StencilOp StencilFrontSFail { get; }
|
||||||
|
public StencilOp StencilFrontDpPass { get; }
|
||||||
|
public StencilOp StencilFrontDpFail { get; }
|
||||||
|
public CompareOp StencilBackFunc { get; }
|
||||||
|
public StencilOp StencilBackSFail { get; }
|
||||||
|
public StencilOp StencilBackDpPass { get; }
|
||||||
|
public StencilOp StencilBackDpFail { get; }
|
||||||
|
|
||||||
|
public DepthStencilState(
|
||||||
|
bool depthTestEnable,
|
||||||
|
bool depthWriteEnable,
|
||||||
|
bool stencilTestEnable,
|
||||||
|
CompareOp depthFunc,
|
||||||
|
CompareOp stencilFrontFunc,
|
||||||
|
StencilOp stencilFrontSFail,
|
||||||
|
StencilOp stencilFrontDpPass,
|
||||||
|
StencilOp stencilFrontDpFail,
|
||||||
|
CompareOp stencilBackFunc,
|
||||||
|
StencilOp stencilBackSFail,
|
||||||
|
StencilOp stencilBackDpPass,
|
||||||
|
StencilOp stencilBackDpFail)
|
||||||
|
{
|
||||||
|
DepthTestEnable = depthTestEnable;
|
||||||
|
DepthWriteEnable = depthWriteEnable;
|
||||||
|
StencilTestEnable = stencilTestEnable;
|
||||||
|
DepthFunc = depthFunc;
|
||||||
|
StencilFrontFunc = stencilFrontFunc;
|
||||||
|
StencilFrontSFail = stencilFrontSFail;
|
||||||
|
StencilFrontDpPass = stencilFrontDpPass;
|
||||||
|
StencilFrontDpFail = stencilFrontDpFail;
|
||||||
|
StencilBackFunc = stencilBackFunc;
|
||||||
|
StencilBackSFail = stencilBackSFail;
|
||||||
|
StencilBackDpPass = stencilBackDpPass;
|
||||||
|
StencilBackDpFail = stencilBackDpFail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.Graphics.GAL/DepthStencil/DepthTestDescriptor.cs
Normal file
20
Ryujinx.Graphics.GAL/DepthStencil/DepthTestDescriptor.cs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.DepthStencil
|
||||||
|
{
|
||||||
|
public struct DepthTestDescriptor
|
||||||
|
{
|
||||||
|
public bool TestEnable { get; }
|
||||||
|
public bool WriteEnable { get; }
|
||||||
|
|
||||||
|
public CompareOp Func { get; }
|
||||||
|
|
||||||
|
public DepthTestDescriptor(
|
||||||
|
bool testEnable,
|
||||||
|
bool writeEnable,
|
||||||
|
CompareOp func)
|
||||||
|
{
|
||||||
|
TestEnable = testEnable;
|
||||||
|
WriteEnable = writeEnable;
|
||||||
|
Func = func;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics.GAL/DepthStencil/StencilOp.cs
Normal file
14
Ryujinx.Graphics.GAL/DepthStencil/StencilOp.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.DepthStencil
|
||||||
|
{
|
||||||
|
public enum StencilOp
|
||||||
|
{
|
||||||
|
Keep = 1,
|
||||||
|
Zero,
|
||||||
|
Replace,
|
||||||
|
IncrementAndClamp,
|
||||||
|
DecrementAndClamp,
|
||||||
|
Invert,
|
||||||
|
IncrementAndWrap,
|
||||||
|
DecrementAndWrap
|
||||||
|
}
|
||||||
|
}
|
56
Ryujinx.Graphics.GAL/DepthStencil/StencilTestDescriptor.cs
Normal file
56
Ryujinx.Graphics.GAL/DepthStencil/StencilTestDescriptor.cs
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.DepthStencil
|
||||||
|
{
|
||||||
|
public struct StencilTestDescriptor
|
||||||
|
{
|
||||||
|
public bool TestEnable { get; }
|
||||||
|
|
||||||
|
public CompareOp FrontFunc { get; }
|
||||||
|
public StencilOp FrontSFail { get; }
|
||||||
|
public StencilOp FrontDpPass { get; }
|
||||||
|
public StencilOp FrontDpFail { get; }
|
||||||
|
public int FrontFuncRef { get; }
|
||||||
|
public int FrontFuncMask { get; }
|
||||||
|
public int FrontMask { get; }
|
||||||
|
public CompareOp BackFunc { get; }
|
||||||
|
public StencilOp BackSFail { get; }
|
||||||
|
public StencilOp BackDpPass { get; }
|
||||||
|
public StencilOp BackDpFail { get; }
|
||||||
|
public int BackFuncRef { get; }
|
||||||
|
public int BackFuncMask { get; }
|
||||||
|
public int BackMask { get; }
|
||||||
|
|
||||||
|
public StencilTestDescriptor(
|
||||||
|
bool testEnable,
|
||||||
|
CompareOp frontFunc,
|
||||||
|
StencilOp frontSFail,
|
||||||
|
StencilOp frontDpPass,
|
||||||
|
StencilOp frontDpFail,
|
||||||
|
int frontFuncRef,
|
||||||
|
int frontFuncMask,
|
||||||
|
int frontMask,
|
||||||
|
CompareOp backFunc,
|
||||||
|
StencilOp backSFail,
|
||||||
|
StencilOp backDpPass,
|
||||||
|
StencilOp backDpFail,
|
||||||
|
int backFuncRef,
|
||||||
|
int backFuncMask,
|
||||||
|
int backMask)
|
||||||
|
{
|
||||||
|
TestEnable = testEnable;
|
||||||
|
FrontFunc = frontFunc;
|
||||||
|
FrontSFail = frontSFail;
|
||||||
|
FrontDpPass = frontDpPass;
|
||||||
|
FrontDpFail = frontDpFail;
|
||||||
|
FrontFuncRef = frontFuncRef;
|
||||||
|
FrontFuncMask = frontFuncMask;
|
||||||
|
FrontMask = frontMask;
|
||||||
|
BackFunc = backFunc;
|
||||||
|
BackSFail = backSFail;
|
||||||
|
BackDpPass = backDpPass;
|
||||||
|
BackDpFail = backDpFail;
|
||||||
|
BackFuncRef = backFuncRef;
|
||||||
|
BackFuncMask = backFuncMask;
|
||||||
|
BackMask = backMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.GAL/Extents2D.cs
Normal file
18
Ryujinx.Graphics.GAL/Extents2D.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public struct Extents2D
|
||||||
|
{
|
||||||
|
public int X1 { get; }
|
||||||
|
public int Y1 { get; }
|
||||||
|
public int X2 { get; }
|
||||||
|
public int Y2 { get; }
|
||||||
|
|
||||||
|
public Extents2D(int x1, int y1, int x2, int y2)
|
||||||
|
{
|
||||||
|
X1 = x1;
|
||||||
|
Y1 = y1;
|
||||||
|
X2 = x2;
|
||||||
|
Y2 = y2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
namespace Ryujinx.Graphics.Gal
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
public enum GalCullFace
|
public enum Face
|
||||||
{
|
{
|
||||||
Front = 0x404,
|
Front = 0x404,
|
||||||
Back = 0x405,
|
Back = 0x405,
|
||||||
FrontAndBack = 0x408
|
FrontAndBack = 0x408
|
||||||
}
|
}
|
||||||
}
|
}
|
218
Ryujinx.Graphics.GAL/Format.cs
Normal file
218
Ryujinx.Graphics.GAL/Format.cs
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum Format
|
||||||
|
{
|
||||||
|
R8Unorm,
|
||||||
|
R8Snorm,
|
||||||
|
R8Uint,
|
||||||
|
R8Sint,
|
||||||
|
R16Float,
|
||||||
|
R16Unorm,
|
||||||
|
R16Snorm,
|
||||||
|
R16Uint,
|
||||||
|
R16Sint,
|
||||||
|
R32Float,
|
||||||
|
R32Uint,
|
||||||
|
R32Sint,
|
||||||
|
R8G8Unorm,
|
||||||
|
R8G8Snorm,
|
||||||
|
R8G8Uint,
|
||||||
|
R8G8Sint,
|
||||||
|
R16G16Float,
|
||||||
|
R16G16Unorm,
|
||||||
|
R16G16Snorm,
|
||||||
|
R16G16Uint,
|
||||||
|
R16G16Sint,
|
||||||
|
R32G32Float,
|
||||||
|
R32G32Uint,
|
||||||
|
R32G32Sint,
|
||||||
|
R8G8B8Unorm,
|
||||||
|
R8G8B8Snorm,
|
||||||
|
R8G8B8Uint,
|
||||||
|
R8G8B8Sint,
|
||||||
|
R16G16B16Float,
|
||||||
|
R16G16B16Unorm,
|
||||||
|
R16G16B16Snorm,
|
||||||
|
R16G16B16Uint,
|
||||||
|
R16G16B16Sint,
|
||||||
|
R32G32B32Float,
|
||||||
|
R32G32B32Uint,
|
||||||
|
R32G32B32Sint,
|
||||||
|
R8G8B8A8Unorm,
|
||||||
|
R8G8B8A8Snorm,
|
||||||
|
R8G8B8A8Uint,
|
||||||
|
R8G8B8A8Sint,
|
||||||
|
R16G16B16A16Float,
|
||||||
|
R16G16B16A16Unorm,
|
||||||
|
R16G16B16A16Snorm,
|
||||||
|
R16G16B16A16Uint,
|
||||||
|
R16G16B16A16Sint,
|
||||||
|
R32G32B32A32Float,
|
||||||
|
R32G32B32A32Uint,
|
||||||
|
R32G32B32A32Sint,
|
||||||
|
S8Uint,
|
||||||
|
D16Unorm,
|
||||||
|
D24X8Unorm,
|
||||||
|
D32Float,
|
||||||
|
D24UnormS8Uint,
|
||||||
|
D32FloatS8Uint,
|
||||||
|
R8G8B8X8Srgb,
|
||||||
|
R8G8B8A8Srgb,
|
||||||
|
R4G4B4A4Unorm,
|
||||||
|
R5G5B5X1Unorm,
|
||||||
|
R5G5B5A1Unorm,
|
||||||
|
R5G6B5Unorm,
|
||||||
|
R10G10B10A2Unorm,
|
||||||
|
R10G10B10A2Uint,
|
||||||
|
R11G11B10Float,
|
||||||
|
R9G9B9E5Float,
|
||||||
|
Bc1RgbUnorm,
|
||||||
|
Bc1RgbaUnorm,
|
||||||
|
Bc2Unorm,
|
||||||
|
Bc3Unorm,
|
||||||
|
Bc1RgbSrgb,
|
||||||
|
Bc1RgbaSrgb,
|
||||||
|
Bc2Srgb,
|
||||||
|
Bc3Srgb,
|
||||||
|
Bc4Unorm,
|
||||||
|
Bc4Snorm,
|
||||||
|
Bc5Unorm,
|
||||||
|
Bc5Snorm,
|
||||||
|
Bc7Unorm,
|
||||||
|
Bc7Srgb,
|
||||||
|
Bc6HUfloat,
|
||||||
|
Bc6HSfloat,
|
||||||
|
R8Uscaled,
|
||||||
|
R8Sscaled,
|
||||||
|
R16Uscaled,
|
||||||
|
R16Sscaled,
|
||||||
|
R32Uscaled,
|
||||||
|
R32Sscaled,
|
||||||
|
R8G8Uscaled,
|
||||||
|
R8G8Sscaled,
|
||||||
|
R16G16Uscaled,
|
||||||
|
R16G16Sscaled,
|
||||||
|
R32G32Uscaled,
|
||||||
|
R32G32Sscaled,
|
||||||
|
R8G8B8Uscaled,
|
||||||
|
R8G8B8Sscaled,
|
||||||
|
R16G16B16Uscaled,
|
||||||
|
R16G16B16Sscaled,
|
||||||
|
R32G32B32Uscaled,
|
||||||
|
R32G32B32Sscaled,
|
||||||
|
R8G8B8A8Uscaled,
|
||||||
|
R8G8B8A8Sscaled,
|
||||||
|
R16G16B16A16Uscaled,
|
||||||
|
R16G16B16A16Sscaled,
|
||||||
|
R32G32B32A32Uscaled,
|
||||||
|
R32G32B32A32Sscaled,
|
||||||
|
R10G10B10A2Snorm,
|
||||||
|
R10G10B10A2Sint,
|
||||||
|
R10G10B10A2Uscaled,
|
||||||
|
R10G10B10A2Sscaled,
|
||||||
|
R8G8B8X8Unorm,
|
||||||
|
R8G8B8X8Snorm,
|
||||||
|
R8G8B8X8Uint,
|
||||||
|
R8G8B8X8Sint,
|
||||||
|
R16G16B16X16Float,
|
||||||
|
R16G16B16X16Unorm,
|
||||||
|
R16G16B16X16Snorm,
|
||||||
|
R16G16B16X16Uint,
|
||||||
|
R16G16B16X16Sint,
|
||||||
|
R32G32B32X32Float,
|
||||||
|
R32G32B32X32Uint,
|
||||||
|
R32G32B32X32Sint,
|
||||||
|
Astc4x4Unorm,
|
||||||
|
Astc5x4Unorm,
|
||||||
|
Astc5x5Unorm,
|
||||||
|
Astc6x5Unorm,
|
||||||
|
Astc6x6Unorm,
|
||||||
|
Astc8x5Unorm,
|
||||||
|
Astc8x6Unorm,
|
||||||
|
Astc8x8Unorm,
|
||||||
|
Astc10x5Unorm,
|
||||||
|
Astc10x6Unorm,
|
||||||
|
Astc10x8Unorm,
|
||||||
|
Astc10x10Unorm,
|
||||||
|
Astc12x10Unorm,
|
||||||
|
Astc12x12Unorm,
|
||||||
|
Astc4x4Srgb,
|
||||||
|
Astc5x4Srgb,
|
||||||
|
Astc5x5Srgb,
|
||||||
|
Astc6x5Srgb,
|
||||||
|
Astc6x6Srgb,
|
||||||
|
Astc8x5Srgb,
|
||||||
|
Astc8x6Srgb,
|
||||||
|
Astc8x8Srgb,
|
||||||
|
Astc10x5Srgb,
|
||||||
|
Astc10x6Srgb,
|
||||||
|
Astc10x8Srgb,
|
||||||
|
Astc10x10Srgb,
|
||||||
|
Astc12x10Srgb,
|
||||||
|
Astc12x12Srgb,
|
||||||
|
B5G6R5Unorm,
|
||||||
|
B5G5R5X1Unorm,
|
||||||
|
B5G5R5A1Unorm,
|
||||||
|
A1B5G5R5Unorm,
|
||||||
|
B8G8R8X8Unorm,
|
||||||
|
B8G8R8A8Unorm,
|
||||||
|
B8G8R8X8Srgb,
|
||||||
|
B8G8R8A8Srgb
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class FormatExtensions
|
||||||
|
{
|
||||||
|
public static bool IsAstc(this Format format)
|
||||||
|
{
|
||||||
|
return format.IsAstcUnorm() || format.IsAstcSrgb();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsAstcUnorm(this Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Format.Astc4x4Unorm:
|
||||||
|
case Format.Astc5x4Unorm:
|
||||||
|
case Format.Astc5x5Unorm:
|
||||||
|
case Format.Astc6x5Unorm:
|
||||||
|
case Format.Astc6x6Unorm:
|
||||||
|
case Format.Astc8x5Unorm:
|
||||||
|
case Format.Astc8x6Unorm:
|
||||||
|
case Format.Astc8x8Unorm:
|
||||||
|
case Format.Astc10x5Unorm:
|
||||||
|
case Format.Astc10x6Unorm:
|
||||||
|
case Format.Astc10x8Unorm:
|
||||||
|
case Format.Astc10x10Unorm:
|
||||||
|
case Format.Astc12x10Unorm:
|
||||||
|
case Format.Astc12x12Unorm:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsAstcSrgb(this Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Format.Astc4x4Srgb:
|
||||||
|
case Format.Astc5x4Srgb:
|
||||||
|
case Format.Astc5x5Srgb:
|
||||||
|
case Format.Astc6x5Srgb:
|
||||||
|
case Format.Astc6x6Srgb:
|
||||||
|
case Format.Astc8x5Srgb:
|
||||||
|
case Format.Astc8x6Srgb:
|
||||||
|
case Format.Astc8x8Srgb:
|
||||||
|
case Format.Astc10x5Srgb:
|
||||||
|
case Format.Astc10x6Srgb:
|
||||||
|
case Format.Astc10x8Srgb:
|
||||||
|
case Format.Astc10x10Srgb:
|
||||||
|
case Format.Astc12x10Srgb:
|
||||||
|
case Format.Astc12x12Srgb:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Graphics.GAL/FrontFace.cs
Normal file
8
Ryujinx.Graphics.GAL/FrontFace.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum FrontFace
|
||||||
|
{
|
||||||
|
Clockwise = 0x900,
|
||||||
|
CounterClockwise = 0x901
|
||||||
|
}
|
||||||
|
}
|
15
Ryujinx.Graphics.GAL/IBuffer.cs
Normal file
15
Ryujinx.Graphics.GAL/IBuffer.cs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface IBuffer : IDisposable
|
||||||
|
{
|
||||||
|
void CopyTo(IBuffer destination, int srcOffset, int dstOffset, int size);
|
||||||
|
|
||||||
|
byte[] GetData(int offset, int size);
|
||||||
|
|
||||||
|
void SetData(Span<byte> data);
|
||||||
|
|
||||||
|
void SetData(int offset, Span<byte> data);
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/IComputePipeline.cs
Normal file
12
Ryujinx.Graphics.GAL/IComputePipeline.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface IComputePipeline
|
||||||
|
{
|
||||||
|
void Dispatch(int groupsX, int groupsY, int groupsZ);
|
||||||
|
|
||||||
|
void SetProgram(IProgram program);
|
||||||
|
|
||||||
|
void SetStorageBuffer(int index, BufferRange buffer);
|
||||||
|
void SetUniformBuffer(int index, BufferRange buffer);
|
||||||
|
}
|
||||||
|
}
|
69
Ryujinx.Graphics.GAL/IGraphicsPipeline.cs
Normal file
69
Ryujinx.Graphics.GAL/IGraphicsPipeline.cs
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Blend;
|
||||||
|
using Ryujinx.Graphics.GAL.Color;
|
||||||
|
using Ryujinx.Graphics.GAL.DepthStencil;
|
||||||
|
using Ryujinx.Graphics.GAL.InputAssembler;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface IGraphicsPipeline
|
||||||
|
{
|
||||||
|
void BindBlendState(int index, BlendDescriptor blend);
|
||||||
|
|
||||||
|
void BindIndexBuffer(BufferRange buffer, IndexType type);
|
||||||
|
|
||||||
|
void BindProgram(IProgram program);
|
||||||
|
|
||||||
|
void BindSampler(int index, ShaderStage stage, ISampler sampler);
|
||||||
|
void BindTexture(int index, ShaderStage stage, ITexture texture);
|
||||||
|
|
||||||
|
void BindStorageBuffers(int index, ShaderStage stage, BufferRange[] buffers);
|
||||||
|
void BindUniformBuffers(int index, ShaderStage stage, BufferRange[] buffers);
|
||||||
|
|
||||||
|
void BindVertexAttribs(VertexAttribDescriptor[] vertexAttribs);
|
||||||
|
void BindVertexBuffers(VertexBufferDescriptor[] vertexBuffers);
|
||||||
|
|
||||||
|
void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
|
||||||
|
void ClearRenderTargetColor(int index, uint componentMask, ColorSI color);
|
||||||
|
void ClearRenderTargetColor(int index, uint componentMask, ColorUI color);
|
||||||
|
|
||||||
|
void ClearRenderTargetDepthStencil(
|
||||||
|
float depthValue,
|
||||||
|
bool depthMask,
|
||||||
|
int stencilValue,
|
||||||
|
int stencilMask);
|
||||||
|
|
||||||
|
void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance);
|
||||||
|
void DrawIndexed(
|
||||||
|
int indexCount,
|
||||||
|
int instanceCount,
|
||||||
|
int firstIndex,
|
||||||
|
int firstVertex,
|
||||||
|
int firstInstance);
|
||||||
|
void DrawIndirect (BufferRange buffer, ulong offset, int drawCount, int stride);
|
||||||
|
void DrawIndexedIndirect(BufferRange buffer, ulong offset, int drawCount, int stride);
|
||||||
|
|
||||||
|
void SetBlendColor(ColorF color);
|
||||||
|
|
||||||
|
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
|
||||||
|
|
||||||
|
void SetDepthTest(DepthTestDescriptor depthTest);
|
||||||
|
|
||||||
|
void SetFaceCulling(bool enable, Face face);
|
||||||
|
|
||||||
|
void SetFrontFace(FrontFace frontFace);
|
||||||
|
|
||||||
|
void SetPrimitiveRestart(bool enable, int index);
|
||||||
|
|
||||||
|
void SetPrimitiveTopology(PrimitiveTopology topology);
|
||||||
|
|
||||||
|
void SetRenderTargetColorMasks(uint[] componentMask);
|
||||||
|
|
||||||
|
void SetRenderTargets(ITexture color3D, ITexture depthStencil);
|
||||||
|
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
|
||||||
|
|
||||||
|
void SetStencilTest(StencilTestDescriptor stencilTest);
|
||||||
|
|
||||||
|
void SetViewports(int first, Viewport[] viewports);
|
||||||
|
}
|
||||||
|
}
|
6
Ryujinx.Graphics.GAL/IProgram.cs
Normal file
6
Ryujinx.Graphics.GAL/IProgram.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface IProgram : IDisposable { }
|
||||||
|
}
|
33
Ryujinx.Graphics.GAL/IRenderer.cs
Normal file
33
Ryujinx.Graphics.GAL/IRenderer.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Sampler;
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface IRenderer
|
||||||
|
{
|
||||||
|
IComputePipeline ComputePipeline { get; }
|
||||||
|
IGraphicsPipeline GraphicsPipeline { get; }
|
||||||
|
|
||||||
|
IWindow Window { get; }
|
||||||
|
|
||||||
|
IShader CompileShader(ShaderProgram shader);
|
||||||
|
|
||||||
|
IBuffer CreateBuffer(int size);
|
||||||
|
|
||||||
|
IProgram CreateProgram(IShader[] shaders);
|
||||||
|
|
||||||
|
ISampler CreateSampler(SamplerCreateInfo info);
|
||||||
|
ITexture CreateTexture(TextureCreateInfo info);
|
||||||
|
|
||||||
|
void FlushPipelines();
|
||||||
|
|
||||||
|
Capabilities GetCapabilities();
|
||||||
|
|
||||||
|
ulong GetCounter(CounterType type);
|
||||||
|
|
||||||
|
void InitializeCounters();
|
||||||
|
|
||||||
|
void ResetCounter(CounterType type);
|
||||||
|
}
|
||||||
|
}
|
6
Ryujinx.Graphics.GAL/ISampler.cs
Normal file
6
Ryujinx.Graphics.GAL/ISampler.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface ISampler : IDisposable { }
|
||||||
|
}
|
6
Ryujinx.Graphics.GAL/IShader.cs
Normal file
6
Ryujinx.Graphics.GAL/IShader.cs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface IShader : IDisposable { }
|
||||||
|
}
|
21
Ryujinx.Graphics.GAL/ITexture.cs
Normal file
21
Ryujinx.Graphics.GAL/ITexture.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface ITexture : IDisposable
|
||||||
|
{
|
||||||
|
int Handle { get; }
|
||||||
|
|
||||||
|
void CopyTo(ITexture destination);
|
||||||
|
void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter);
|
||||||
|
|
||||||
|
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);
|
||||||
|
|
||||||
|
int GetStorageDebugId();
|
||||||
|
|
||||||
|
byte[] GetData(int face);
|
||||||
|
|
||||||
|
void SetData(Span<byte> data);
|
||||||
|
}
|
||||||
|
}
|
13
Ryujinx.Graphics.GAL/IWindow.cs
Normal file
13
Ryujinx.Graphics.GAL/IWindow.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public interface IWindow
|
||||||
|
{
|
||||||
|
void Present();
|
||||||
|
|
||||||
|
void QueueTexture(ITexture texture, ImageCrop crop, object context);
|
||||||
|
|
||||||
|
void RegisterTextureReleaseCallback(TextureReleaseCallback callback);
|
||||||
|
}
|
||||||
|
}
|
28
Ryujinx.Graphics.GAL/ImageCrop.cs
Normal file
28
Ryujinx.Graphics.GAL/ImageCrop.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public struct ImageCrop
|
||||||
|
{
|
||||||
|
public int Left { get; }
|
||||||
|
public int Right { get; }
|
||||||
|
public int Top { get; }
|
||||||
|
public int Bottom { get; }
|
||||||
|
public bool FlipX { get; }
|
||||||
|
public bool FlipY { get; }
|
||||||
|
|
||||||
|
public ImageCrop(
|
||||||
|
int left,
|
||||||
|
int right,
|
||||||
|
int top,
|
||||||
|
int bottom,
|
||||||
|
bool flipX,
|
||||||
|
bool flipY)
|
||||||
|
{
|
||||||
|
Left = left;
|
||||||
|
Right = right;
|
||||||
|
Top = top;
|
||||||
|
Bottom = bottom;
|
||||||
|
FlipX = flipX;
|
||||||
|
FlipY = flipY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Graphics.GAL/IndexType.cs
Normal file
9
Ryujinx.Graphics.GAL/IndexType.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum IndexType
|
||||||
|
{
|
||||||
|
UByte,
|
||||||
|
UShort,
|
||||||
|
UInt
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.InputAssembler
|
||||||
|
{
|
||||||
|
public struct VertexAttribDescriptor
|
||||||
|
{
|
||||||
|
public int BufferIndex { get; }
|
||||||
|
public int Offset { get; }
|
||||||
|
|
||||||
|
public Format Format { get; }
|
||||||
|
|
||||||
|
public VertexAttribDescriptor(int bufferIndex, int offset, Format format)
|
||||||
|
{
|
||||||
|
BufferIndex = bufferIndex;
|
||||||
|
Offset = offset;
|
||||||
|
Format = format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.InputAssembler
|
||||||
|
{
|
||||||
|
public struct VertexBufferDescriptor
|
||||||
|
{
|
||||||
|
public BufferRange Buffer { get; }
|
||||||
|
|
||||||
|
public int Stride { get; }
|
||||||
|
public int Divisor { get; }
|
||||||
|
|
||||||
|
public VertexBufferDescriptor(BufferRange buffer, int stride, int divisor)
|
||||||
|
{
|
||||||
|
Buffer = buffer;
|
||||||
|
Stride = stride;
|
||||||
|
Divisor = divisor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/PolygonModeMask.cs
Normal file
12
Ryujinx.Graphics.GAL/PolygonModeMask.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
public enum PolygonModeMask
|
||||||
|
{
|
||||||
|
Point = 1 << 0,
|
||||||
|
Line = 1 << 1,
|
||||||
|
Fill = 1 << 2
|
||||||
|
}
|
||||||
|
}
|
21
Ryujinx.Graphics.GAL/PrimitiveTopology.cs
Normal file
21
Ryujinx.Graphics.GAL/PrimitiveTopology.cs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum PrimitiveTopology
|
||||||
|
{
|
||||||
|
Points,
|
||||||
|
Lines,
|
||||||
|
LineLoop,
|
||||||
|
LineStrip,
|
||||||
|
Triangles,
|
||||||
|
TriangleStrip,
|
||||||
|
TriangleFan,
|
||||||
|
Quads,
|
||||||
|
QuadStrip,
|
||||||
|
Polygon,
|
||||||
|
LinesAdjacency,
|
||||||
|
LineStripAdjacency,
|
||||||
|
TrianglesAdjacency,
|
||||||
|
TriangleStripAdjacency,
|
||||||
|
Patches
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.GAL/RectangleF.cs
Normal file
18
Ryujinx.Graphics.GAL/RectangleF.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public struct RectangleF
|
||||||
|
{
|
||||||
|
public float X { get; }
|
||||||
|
public float Y { get; }
|
||||||
|
public float Width { get; }
|
||||||
|
public float Height { get; }
|
||||||
|
|
||||||
|
public RectangleF(float x, float y, float width, float height)
|
||||||
|
{
|
||||||
|
X = x;
|
||||||
|
Y = y;
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj
Normal file
12
Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||||
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
14
Ryujinx.Graphics.GAL/Sampler/AddressMode.cs
Normal file
14
Ryujinx.Graphics.GAL/Sampler/AddressMode.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Sampler
|
||||||
|
{
|
||||||
|
public enum AddressMode
|
||||||
|
{
|
||||||
|
Repeat,
|
||||||
|
MirroredRepeat,
|
||||||
|
ClampToEdge,
|
||||||
|
ClampToBorder,
|
||||||
|
Clamp,
|
||||||
|
MirrorClampToEdge,
|
||||||
|
MirrorClampToBorder,
|
||||||
|
MirrorClamp
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Graphics.GAL/Sampler/CompareMode.cs
Normal file
8
Ryujinx.Graphics.GAL/Sampler/CompareMode.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Sampler
|
||||||
|
{
|
||||||
|
public enum CompareMode
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
CompareRToTexture
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Graphics.GAL/Sampler/MagFilter.cs
Normal file
8
Ryujinx.Graphics.GAL/Sampler/MagFilter.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Sampler
|
||||||
|
{
|
||||||
|
public enum MagFilter
|
||||||
|
{
|
||||||
|
Nearest = 1,
|
||||||
|
Linear
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/Sampler/MinFilter.cs
Normal file
12
Ryujinx.Graphics.GAL/Sampler/MinFilter.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Sampler
|
||||||
|
{
|
||||||
|
public enum MinFilter
|
||||||
|
{
|
||||||
|
Nearest = 1,
|
||||||
|
Linear,
|
||||||
|
NearestMipmapNearest,
|
||||||
|
LinearMipmapNearest,
|
||||||
|
NearestMipmapLinear,
|
||||||
|
LinearMipmapLinear
|
||||||
|
}
|
||||||
|
}
|
52
Ryujinx.Graphics.GAL/Sampler/SamplerCreateInfo.cs
Normal file
52
Ryujinx.Graphics.GAL/Sampler/SamplerCreateInfo.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Color;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL.Sampler
|
||||||
|
{
|
||||||
|
public struct SamplerCreateInfo
|
||||||
|
{
|
||||||
|
public MinFilter MinFilter { get; }
|
||||||
|
public MagFilter MagFilter { get; }
|
||||||
|
|
||||||
|
public AddressMode AddressU { get; }
|
||||||
|
public AddressMode AddressV { get; }
|
||||||
|
public AddressMode AddressP { get; }
|
||||||
|
|
||||||
|
public CompareMode CompareMode { get; }
|
||||||
|
public CompareOp CompareOp { get; }
|
||||||
|
|
||||||
|
public ColorF BorderColor { get; }
|
||||||
|
|
||||||
|
public float MinLod { get; }
|
||||||
|
public float MaxLod { get; }
|
||||||
|
public float MipLodBias { get; }
|
||||||
|
public float MaxAnisotropy { get; }
|
||||||
|
|
||||||
|
public SamplerCreateInfo(
|
||||||
|
MinFilter minFilter,
|
||||||
|
MagFilter magFilter,
|
||||||
|
AddressMode addressU,
|
||||||
|
AddressMode addressV,
|
||||||
|
AddressMode addressP,
|
||||||
|
CompareMode compareMode,
|
||||||
|
CompareOp compareOp,
|
||||||
|
ColorF borderColor,
|
||||||
|
float minLod,
|
||||||
|
float maxLod,
|
||||||
|
float mipLodBias,
|
||||||
|
float maxAnisotropy)
|
||||||
|
{
|
||||||
|
MinFilter = minFilter;
|
||||||
|
MagFilter = magFilter;
|
||||||
|
AddressU = addressU;
|
||||||
|
AddressV = addressV;
|
||||||
|
AddressP = addressP;
|
||||||
|
CompareMode = compareMode;
|
||||||
|
CompareOp = compareOp;
|
||||||
|
BorderColor = borderColor;
|
||||||
|
MinLod = minLod;
|
||||||
|
MaxLod = maxLod;
|
||||||
|
MipLodBias = mipLodBias;
|
||||||
|
MaxAnisotropy = maxAnisotropy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Graphics.GAL/Texture/DepthStencilMode.cs
Normal file
8
Ryujinx.Graphics.GAL/Texture/DepthStencilMode.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Texture
|
||||||
|
{
|
||||||
|
public enum DepthStencilMode
|
||||||
|
{
|
||||||
|
Depth,
|
||||||
|
Stencil
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/Texture/SwizzleComponent.cs
Normal file
12
Ryujinx.Graphics.GAL/Texture/SwizzleComponent.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Texture
|
||||||
|
{
|
||||||
|
public enum SwizzleComponent
|
||||||
|
{
|
||||||
|
Zero,
|
||||||
|
One,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
Alpha
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.Graphics.GAL/Texture/Target.cs
Normal file
17
Ryujinx.Graphics.GAL/Texture/Target.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL.Texture
|
||||||
|
{
|
||||||
|
public enum Target
|
||||||
|
{
|
||||||
|
Texture1D,
|
||||||
|
Texture2D,
|
||||||
|
Texture3D,
|
||||||
|
Texture1DArray,
|
||||||
|
Texture2DArray,
|
||||||
|
Texture2DMultisample,
|
||||||
|
Texture2DMultisampleArray,
|
||||||
|
Rectangle,
|
||||||
|
Cubemap,
|
||||||
|
CubemapArray,
|
||||||
|
TextureBuffer
|
||||||
|
}
|
||||||
|
}
|
115
Ryujinx.Graphics.GAL/Texture/TextureCreateInfo.cs
Normal file
115
Ryujinx.Graphics.GAL/Texture/TextureCreateInfo.cs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.GAL.Texture
|
||||||
|
{
|
||||||
|
public struct TextureCreateInfo
|
||||||
|
{
|
||||||
|
public int Width { get; }
|
||||||
|
public int Height { get; }
|
||||||
|
public int Depth { get; }
|
||||||
|
public int Levels { get; }
|
||||||
|
public int Samples { get; }
|
||||||
|
public int BlockWidth { get; }
|
||||||
|
public int BlockHeight { get; }
|
||||||
|
public int BytesPerPixel { get; }
|
||||||
|
|
||||||
|
public bool IsCompressed => (BlockWidth | BlockHeight) != 1;
|
||||||
|
|
||||||
|
public Format Format { get; }
|
||||||
|
|
||||||
|
public DepthStencilMode DepthStencilMode { get; }
|
||||||
|
|
||||||
|
public Target Target { get; }
|
||||||
|
|
||||||
|
public SwizzleComponent SwizzleR { get; }
|
||||||
|
public SwizzleComponent SwizzleG { get; }
|
||||||
|
public SwizzleComponent SwizzleB { get; }
|
||||||
|
public SwizzleComponent SwizzleA { get; }
|
||||||
|
|
||||||
|
public TextureCreateInfo(
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int depth,
|
||||||
|
int levels,
|
||||||
|
int samples,
|
||||||
|
int blockWidth,
|
||||||
|
int blockHeight,
|
||||||
|
int bytesPerPixel,
|
||||||
|
Format format,
|
||||||
|
DepthStencilMode depthStencilMode,
|
||||||
|
Target target,
|
||||||
|
SwizzleComponent swizzleR,
|
||||||
|
SwizzleComponent swizzleG,
|
||||||
|
SwizzleComponent swizzleB,
|
||||||
|
SwizzleComponent swizzleA)
|
||||||
|
{
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
Depth = depth;
|
||||||
|
Levels = levels;
|
||||||
|
Samples = samples;
|
||||||
|
BlockWidth = blockWidth;
|
||||||
|
BlockHeight = blockHeight;
|
||||||
|
BytesPerPixel = bytesPerPixel;
|
||||||
|
Format = format;
|
||||||
|
DepthStencilMode = depthStencilMode;
|
||||||
|
Target = target;
|
||||||
|
SwizzleR = swizzleR;
|
||||||
|
SwizzleG = swizzleG;
|
||||||
|
SwizzleB = swizzleB;
|
||||||
|
SwizzleA = swizzleA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMipSize(int level)
|
||||||
|
{
|
||||||
|
return GetMipStride(level) * GetLevelHeight(level) * GetLevelDepth(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetMipStride(int level)
|
||||||
|
{
|
||||||
|
return BitUtils.AlignUp(GetLevelWidth(level) * BytesPerPixel, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetLevelWidth(int level)
|
||||||
|
{
|
||||||
|
return BitUtils.DivRoundUp(GetLevelSize(Width, level), BlockWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetLevelHeight(int level)
|
||||||
|
{
|
||||||
|
return BitUtils.DivRoundUp(GetLevelSize(Height, level), BlockHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetLevelDepth(int level)
|
||||||
|
{
|
||||||
|
return Target == Target.Texture3D ? GetLevelSize(Depth, level) : GetLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetDepthOrLayers()
|
||||||
|
{
|
||||||
|
return Target == Target.Texture3D ? Depth : GetLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetLayers()
|
||||||
|
{
|
||||||
|
if (Target == Target.Texture2DArray ||
|
||||||
|
Target == Target.Texture2DMultisampleArray ||
|
||||||
|
Target == Target.CubemapArray)
|
||||||
|
{
|
||||||
|
return Depth;
|
||||||
|
}
|
||||||
|
else if (Target == Target.Cubemap)
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int GetLevelSize(int size, int level)
|
||||||
|
{
|
||||||
|
return Math.Max(1, size >> level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
4
Ryujinx.Graphics.GAL/TextureReleaseCallback.cs
Normal file
4
Ryujinx.Graphics.GAL/TextureReleaseCallback.cs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public delegate void TextureReleaseCallback(object context);
|
||||||
|
}
|
33
Ryujinx.Graphics.GAL/Viewport.cs
Normal file
33
Ryujinx.Graphics.GAL/Viewport.cs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public struct Viewport
|
||||||
|
{
|
||||||
|
public RectangleF Region { get; }
|
||||||
|
|
||||||
|
public ViewportSwizzle SwizzleX { get; }
|
||||||
|
public ViewportSwizzle SwizzleY { get; }
|
||||||
|
public ViewportSwizzle SwizzleZ { get; }
|
||||||
|
public ViewportSwizzle SwizzleW { get; }
|
||||||
|
|
||||||
|
public float DepthNear { get; }
|
||||||
|
public float DepthFar { get; }
|
||||||
|
|
||||||
|
public Viewport(
|
||||||
|
RectangleF region,
|
||||||
|
ViewportSwizzle swizzleX,
|
||||||
|
ViewportSwizzle swizzleY,
|
||||||
|
ViewportSwizzle swizzleZ,
|
||||||
|
ViewportSwizzle swizzleW,
|
||||||
|
float depthNear,
|
||||||
|
float depthFar)
|
||||||
|
{
|
||||||
|
Region = region;
|
||||||
|
SwizzleX = swizzleX;
|
||||||
|
SwizzleY = swizzleY;
|
||||||
|
SwizzleZ = swizzleZ;
|
||||||
|
SwizzleW = swizzleW;
|
||||||
|
DepthNear = depthNear;
|
||||||
|
DepthFar = depthFar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics.GAL/ViewportSwizzle.cs
Normal file
14
Ryujinx.Graphics.GAL/ViewportSwizzle.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum ViewportSwizzle
|
||||||
|
{
|
||||||
|
PositiveX,
|
||||||
|
NegativeX,
|
||||||
|
PositiveY,
|
||||||
|
NegativeY,
|
||||||
|
PositiveZ,
|
||||||
|
NegativeZ,
|
||||||
|
PositiveW,
|
||||||
|
NegativeW
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics.Gpu/ClassId.cs
Normal file
11
Ryujinx.Graphics.Gpu/ClassId.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
enum ClassId
|
||||||
|
{
|
||||||
|
Engine2D = 0x902d,
|
||||||
|
Engine3D = 0xb197,
|
||||||
|
EngineCompute = 0xb1c0,
|
||||||
|
EngineInline2Memory = 0xa140,
|
||||||
|
EngineDma = 0xb0b5
|
||||||
|
}
|
||||||
|
}
|
14
Ryujinx.Graphics.Gpu/Constants.cs
Normal file
14
Ryujinx.Graphics.Gpu/Constants.cs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
static class Constants
|
||||||
|
{
|
||||||
|
public const int TotalCpUniformBuffers = 8;
|
||||||
|
public const int TotalCpStorageBuffers = 16;
|
||||||
|
public const int TotalGpUniformBuffers = 18;
|
||||||
|
public const int TotalGpStorageBuffers = 16;
|
||||||
|
public const int TotalRenderTargets = 8;
|
||||||
|
public const int TotalShaderStages = 5;
|
||||||
|
public const int TotalVertexBuffers = 16;
|
||||||
|
public const int TotalViewports = 8;
|
||||||
|
}
|
||||||
|
}
|
25
Ryujinx.Graphics.Gpu/Debugging.cs
Normal file
25
Ryujinx.Graphics.Gpu/Debugging.cs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
static class Debugging
|
||||||
|
{
|
||||||
|
public static void PrintTexInfo(string prefix, Image.Texture tex)
|
||||||
|
{
|
||||||
|
if (tex == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(prefix + " null");
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string range = $"{tex.Address:X}..{(tex.Address + tex.Size):X}";
|
||||||
|
|
||||||
|
int debugId = tex.HostTexture.GetStorageDebugId();
|
||||||
|
|
||||||
|
string str = $"{prefix} p {debugId:X8} {tex.Info.Target} {tex.Info.FormatInfo.Format} {tex.Info.Width}x{tex.Info.Height}x{tex.Info.DepthOrLayers} mips {tex.Info.Levels} addr {range}";
|
||||||
|
|
||||||
|
Console.WriteLine(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,14 @@
|
||||||
using Ryujinx.Graphics.Memory;
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
public class DmaPusher
|
public class DmaPusher
|
||||||
{
|
{
|
||||||
private ConcurrentQueue<(NvGpuVmm, long)> _ibBuffer;
|
private ConcurrentQueue<ulong> _ibBuffer;
|
||||||
|
|
||||||
private long _dmaPut;
|
private ulong _dmaPut;
|
||||||
private long _dmaGet;
|
private ulong _dmaGet;
|
||||||
|
|
||||||
private struct DmaState
|
private struct DmaState
|
||||||
{
|
{
|
||||||
|
@ -29,28 +28,26 @@ namespace Ryujinx.Graphics
|
||||||
private bool _ibEnable;
|
private bool _ibEnable;
|
||||||
private bool _nonMain;
|
private bool _nonMain;
|
||||||
|
|
||||||
private long _dmaMGet;
|
private ulong _dmaMGet;
|
||||||
|
|
||||||
private NvGpuVmm _vmm;
|
private GpuContext _context;
|
||||||
|
|
||||||
private NvGpu _gpu;
|
|
||||||
|
|
||||||
private AutoResetEvent _event;
|
private AutoResetEvent _event;
|
||||||
|
|
||||||
public DmaPusher(NvGpu gpu)
|
internal DmaPusher(GpuContext context)
|
||||||
{
|
{
|
||||||
_gpu = gpu;
|
_context = context;
|
||||||
|
|
||||||
_ibBuffer = new ConcurrentQueue<(NvGpuVmm, long)>();
|
_ibBuffer = new ConcurrentQueue<ulong>();
|
||||||
|
|
||||||
_ibEnable = true;
|
_ibEnable = true;
|
||||||
|
|
||||||
_event = new AutoResetEvent(false);
|
_event = new AutoResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Push(NvGpuVmm vmm, long entry)
|
public void Push(ulong entry)
|
||||||
{
|
{
|
||||||
_ibBuffer.Enqueue((vmm, entry));
|
_ibBuffer.Enqueue(entry);
|
||||||
|
|
||||||
_event.Set();
|
_event.Set();
|
||||||
}
|
}
|
||||||
|
@ -69,7 +66,7 @@ namespace Ryujinx.Graphics
|
||||||
{
|
{
|
||||||
if (_dmaGet != _dmaPut)
|
if (_dmaGet != _dmaPut)
|
||||||
{
|
{
|
||||||
int word = _vmm.ReadInt32(_dmaGet);
|
int word = _context.MemoryAccessor.ReadInt32(_dmaGet);
|
||||||
|
|
||||||
_dmaGet += 4;
|
_dmaGet += 4;
|
||||||
|
|
||||||
|
@ -148,20 +145,14 @@ namespace Ryujinx.Graphics
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_ibEnable && _ibBuffer.TryDequeue(out (NvGpuVmm Vmm, long Entry) tuple))
|
else if (_ibEnable && _ibBuffer.TryDequeue(out ulong entry))
|
||||||
{
|
{
|
||||||
_vmm = tuple.Vmm;
|
ulong length = (entry >> 42) & 0x1fffff;
|
||||||
|
|
||||||
long entry = tuple.Entry;
|
|
||||||
|
|
||||||
int length = (int)(entry >> 42) & 0x1fffff;
|
|
||||||
|
|
||||||
_dmaGet = entry & 0xfffffffffc;
|
_dmaGet = entry & 0xfffffffffc;
|
||||||
_dmaPut = _dmaGet + length * 4;
|
_dmaPut = _dmaGet + length * 4;
|
||||||
|
|
||||||
_nonMain = (entry & (1L << 41)) != 0;
|
_nonMain = (entry & (1UL << 41)) != 0;
|
||||||
|
|
||||||
_gpu.ResourceManager.ClearPbCache();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -180,7 +171,7 @@ namespace Ryujinx.Graphics
|
||||||
|
|
||||||
private void CallMethod(int argument)
|
private void CallMethod(int argument)
|
||||||
{
|
{
|
||||||
_gpu.Fifo.CallMethod(_vmm, new GpuMethodCall(
|
_context.Fifo.CallMethod(new MethodParams(
|
||||||
_state.Method,
|
_state.Method,
|
||||||
argument,
|
argument,
|
||||||
_state.SubChannel,
|
_state.SubChannel,
|
83
Ryujinx.Graphics.Gpu/Engine/Compute.cs
Normal file
83
Ryujinx.Graphics.Gpu/Engine/Compute.cs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
public void Dispatch(int argument)
|
||||||
|
{
|
||||||
|
uint dispatchParamsAddress = (uint)_context.State.Get<int>(MethodOffset.DispatchParamsAddress);
|
||||||
|
|
||||||
|
var dispatchParams = _context.MemoryAccessor.Read<ComputeParams>((ulong)dispatchParamsAddress << 8);
|
||||||
|
|
||||||
|
GpuVa shaderBaseAddress = _context.State.Get<GpuVa>(MethodOffset.ShaderBaseAddress);
|
||||||
|
|
||||||
|
ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)dispatchParams.ShaderOffset;
|
||||||
|
|
||||||
|
ComputeShader cs = _shaderCache.GetComputeShader(
|
||||||
|
shaderGpuVa,
|
||||||
|
dispatchParams.UnpackBlockSizeX(),
|
||||||
|
dispatchParams.UnpackBlockSizeY(),
|
||||||
|
dispatchParams.UnpackBlockSizeZ());
|
||||||
|
|
||||||
|
_context.Renderer.ComputePipeline.SetProgram(cs.Interface);
|
||||||
|
|
||||||
|
ShaderProgramInfo info = cs.Shader.Info;
|
||||||
|
|
||||||
|
uint sbEnableMask = 0;
|
||||||
|
uint ubEnableMask = dispatchParams.UnpackUniformBuffersEnableMask();
|
||||||
|
|
||||||
|
for (int index = 0; index < dispatchParams.UniformBuffers.Length; index++)
|
||||||
|
{
|
||||||
|
if ((ubEnableMask & (1 << index)) == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong gpuVa = dispatchParams.UniformBuffers[index].PackAddress();
|
||||||
|
ulong size = dispatchParams.UniformBuffers[index].UnpackSize();
|
||||||
|
|
||||||
|
_bufferManager.SetComputeUniformBuffer(index, gpuVa, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||||
|
{
|
||||||
|
BufferDescriptor sb = info.SBuffers[index];
|
||||||
|
|
||||||
|
sbEnableMask |= 1u << sb.Slot;
|
||||||
|
|
||||||
|
ulong sbDescAddress = _bufferManager.GetComputeUniformBufferAddress(0);
|
||||||
|
|
||||||
|
int sbDescOffset = 0x310 + sb.Slot * 0x10;
|
||||||
|
|
||||||
|
sbDescAddress += (ulong)sbDescOffset;
|
||||||
|
|
||||||
|
Span<byte> sbDescriptorData = _context.PhysicalMemory.Read(sbDescAddress, 0x10);
|
||||||
|
|
||||||
|
SbDescriptor sbDescriptor = MemoryMarshal.Cast<byte, SbDescriptor>(sbDescriptorData)[0];
|
||||||
|
|
||||||
|
_bufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ubEnableMask = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < info.CBuffers.Count; index++)
|
||||||
|
{
|
||||||
|
ubEnableMask |= 1u << info.CBuffers[index].Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bufferManager.SetComputeStorageBufferEnableMask(sbEnableMask);
|
||||||
|
_bufferManager.SetComputeUniformBufferEnableMask(ubEnableMask);
|
||||||
|
|
||||||
|
_bufferManager.CommitComputeBindings();
|
||||||
|
|
||||||
|
_context.Renderer.ComputePipeline.Dispatch(
|
||||||
|
dispatchParams.UnpackGridSizeX(),
|
||||||
|
dispatchParams.UnpackGridSizeY(),
|
||||||
|
dispatchParams.UnpackGridSizeZ());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs
Normal file
126
Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
struct UniformBufferParams
|
||||||
|
{
|
||||||
|
public int AddressLow;
|
||||||
|
public int AddressHighAndSize;
|
||||||
|
|
||||||
|
public ulong PackAddress()
|
||||||
|
{
|
||||||
|
return (uint)AddressLow | ((ulong)(AddressHighAndSize & 0xff) << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong UnpackSize()
|
||||||
|
{
|
||||||
|
return (ulong)((AddressHighAndSize >> 15) & 0x1ffff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ComputeParams
|
||||||
|
{
|
||||||
|
public int Unknown0;
|
||||||
|
public int Unknown1;
|
||||||
|
public int Unknown2;
|
||||||
|
public int Unknown3;
|
||||||
|
public int Unknown4;
|
||||||
|
public int Unknown5;
|
||||||
|
public int Unknown6;
|
||||||
|
public int Unknown7;
|
||||||
|
public int ShaderOffset;
|
||||||
|
public int Unknown9;
|
||||||
|
public int Unknown10;
|
||||||
|
public int Unknown11;
|
||||||
|
public int GridSizeX;
|
||||||
|
public int GridSizeYZ;
|
||||||
|
public int Unknown14;
|
||||||
|
public int Unknown15;
|
||||||
|
public int Unknown16;
|
||||||
|
public int Unknown17;
|
||||||
|
public int BlockSizeX;
|
||||||
|
public int BlockSizeYZ;
|
||||||
|
public int UniformBuffersConfig;
|
||||||
|
public int Unknown21;
|
||||||
|
public int Unknown22;
|
||||||
|
public int Unknown23;
|
||||||
|
public int Unknown24;
|
||||||
|
public int Unknown25;
|
||||||
|
public int Unknown26;
|
||||||
|
public int Unknown27;
|
||||||
|
public int Unknown28;
|
||||||
|
|
||||||
|
private UniformBufferParams _uniformBuffer0;
|
||||||
|
private UniformBufferParams _uniformBuffer1;
|
||||||
|
private UniformBufferParams _uniformBuffer2;
|
||||||
|
private UniformBufferParams _uniformBuffer3;
|
||||||
|
private UniformBufferParams _uniformBuffer4;
|
||||||
|
private UniformBufferParams _uniformBuffer5;
|
||||||
|
private UniformBufferParams _uniformBuffer6;
|
||||||
|
private UniformBufferParams _uniformBuffer7;
|
||||||
|
|
||||||
|
public Span<UniformBufferParams> UniformBuffers
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return MemoryMarshal.CreateSpan(ref _uniformBuffer0, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Unknown45;
|
||||||
|
public int Unknown46;
|
||||||
|
public int Unknown47;
|
||||||
|
public int Unknown48;
|
||||||
|
public int Unknown49;
|
||||||
|
public int Unknown50;
|
||||||
|
public int Unknown51;
|
||||||
|
public int Unknown52;
|
||||||
|
public int Unknown53;
|
||||||
|
public int Unknown54;
|
||||||
|
public int Unknown55;
|
||||||
|
public int Unknown56;
|
||||||
|
public int Unknown57;
|
||||||
|
public int Unknown58;
|
||||||
|
public int Unknown59;
|
||||||
|
public int Unknown60;
|
||||||
|
public int Unknown61;
|
||||||
|
public int Unknown62;
|
||||||
|
public int Unknown63;
|
||||||
|
|
||||||
|
public int UnpackGridSizeX()
|
||||||
|
{
|
||||||
|
return GridSizeX & 0x7fffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackGridSizeY()
|
||||||
|
{
|
||||||
|
return GridSizeYZ & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackGridSizeZ()
|
||||||
|
{
|
||||||
|
return (GridSizeYZ >> 16) & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackBlockSizeX()
|
||||||
|
{
|
||||||
|
return (BlockSizeX >> 16) & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackBlockSizeY()
|
||||||
|
{
|
||||||
|
return BlockSizeYZ & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackBlockSizeZ()
|
||||||
|
{
|
||||||
|
return (BlockSizeYZ >> 16) & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public uint UnpackUniformBuffersEnableMask()
|
||||||
|
{
|
||||||
|
return (uint)UniformBuffersConfig & 0xff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs
Normal file
18
Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
class ComputeShader
|
||||||
|
{
|
||||||
|
public IProgram Interface { get; set; }
|
||||||
|
|
||||||
|
public ShaderProgram Shader { get; }
|
||||||
|
|
||||||
|
public ComputeShader(IProgram program, ShaderProgram shader)
|
||||||
|
{
|
||||||
|
Interface = program;
|
||||||
|
Shader = shader;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs
Normal file
17
Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
class GraphicsShader
|
||||||
|
{
|
||||||
|
public IProgram Interface { get; set; }
|
||||||
|
|
||||||
|
public ShaderProgram[] Shader { get; }
|
||||||
|
|
||||||
|
public GraphicsShader()
|
||||||
|
{
|
||||||
|
Shader = new ShaderProgram[5];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
42
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
Normal file
42
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private Inline2MemoryParams _params;
|
||||||
|
|
||||||
|
private bool _isLinear;
|
||||||
|
|
||||||
|
private int _offset;
|
||||||
|
private int _size;
|
||||||
|
|
||||||
|
public void Execute(int argument)
|
||||||
|
{
|
||||||
|
_params = _context.State.Get<Inline2MemoryParams>(MethodOffset.Inline2MemoryParams);
|
||||||
|
|
||||||
|
_isLinear = (argument & 1) != 0;
|
||||||
|
|
||||||
|
_offset = 0;
|
||||||
|
_size = _params.LineLengthIn * _params.LineCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PushData(int argument)
|
||||||
|
{
|
||||||
|
if (_isLinear)
|
||||||
|
{
|
||||||
|
for (int shift = 0; shift < 32 && _offset < _size; shift += 8, _offset++)
|
||||||
|
{
|
||||||
|
ulong gpuVa = _params.DstAddress.Pack() + (ulong)_offset;
|
||||||
|
|
||||||
|
_context.MemoryAccessor.Write(gpuVa, new byte[] { (byte)(argument >> shift) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
55
Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
Normal file
55
Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Color;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private void Clear(int argument)
|
||||||
|
{
|
||||||
|
UpdateState();
|
||||||
|
|
||||||
|
bool clearDepth = (argument & 1) != 0;
|
||||||
|
bool clearStencil = (argument & 2) != 0;
|
||||||
|
|
||||||
|
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||||
|
|
||||||
|
int index = (argument >> 6) & 0xf;
|
||||||
|
|
||||||
|
if (componentMask != 0)
|
||||||
|
{
|
||||||
|
ClearColors clearColor = _context.State.GetClearColors();
|
||||||
|
|
||||||
|
ColorF color = new ColorF(
|
||||||
|
clearColor.Red,
|
||||||
|
clearColor.Green,
|
||||||
|
clearColor.Blue,
|
||||||
|
clearColor.Alpha);
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.ClearRenderTargetColor(
|
||||||
|
index,
|
||||||
|
componentMask,
|
||||||
|
color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearDepth || clearStencil)
|
||||||
|
{
|
||||||
|
float depthValue = _context.State.GetClearDepthValue();
|
||||||
|
int stencilValue = _context.State.GetClearStencilValue();
|
||||||
|
|
||||||
|
int stencilMask = 0;
|
||||||
|
|
||||||
|
if (clearStencil)
|
||||||
|
{
|
||||||
|
stencilMask = _context.State.GetStencilTestState().FrontMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.ClearRenderTargetDepthStencil(
|
||||||
|
depthValue,
|
||||||
|
clearDepth,
|
||||||
|
stencilValue,
|
||||||
|
stencilMask);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
79
Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
Normal file
79
Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using Ryujinx.Graphics.Texture;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private void CopyBuffer(int argument)
|
||||||
|
{
|
||||||
|
var cbp = _context.State.Get<CopyBufferParams>(MethodOffset.CopyBufferParams);
|
||||||
|
|
||||||
|
var swizzle = _context.State.Get<CopyBufferSwizzle>(MethodOffset.CopyBufferSwizzle);
|
||||||
|
|
||||||
|
bool srcLinear = (argument & (1 << 7)) != 0;
|
||||||
|
bool dstLinear = (argument & (1 << 8)) != 0;
|
||||||
|
bool copy2D = (argument & (1 << 9)) != 0;
|
||||||
|
|
||||||
|
int size = cbp.XCount;
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy2D)
|
||||||
|
{
|
||||||
|
// Buffer to texture copy.
|
||||||
|
int srcBpp = swizzle.UnpackSrcComponentsCount() * swizzle.UnpackComponentSize();
|
||||||
|
int dstBpp = swizzle.UnpackDstComponentsCount() * swizzle.UnpackComponentSize();
|
||||||
|
|
||||||
|
var dst = _context.State.Get<CopyBufferTexture>(MethodOffset.CopyBufferDstTexture);
|
||||||
|
var src = _context.State.Get<CopyBufferTexture>(MethodOffset.CopyBufferSrcTexture);
|
||||||
|
|
||||||
|
var srcCalculator = new OffsetCalculator(
|
||||||
|
src.Width,
|
||||||
|
src.Height,
|
||||||
|
cbp.SrcStride,
|
||||||
|
srcLinear,
|
||||||
|
src.MemoryLayout.UnpackGobBlocksInY(),
|
||||||
|
srcBpp);
|
||||||
|
|
||||||
|
var dstCalculator = new OffsetCalculator(
|
||||||
|
dst.Width,
|
||||||
|
dst.Height,
|
||||||
|
cbp.DstStride,
|
||||||
|
dstLinear,
|
||||||
|
dst.MemoryLayout.UnpackGobBlocksInY(),
|
||||||
|
dstBpp);
|
||||||
|
|
||||||
|
ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
|
||||||
|
ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
|
||||||
|
|
||||||
|
for (int y = 0; y < cbp.YCount; y++)
|
||||||
|
for (int x = 0; x < cbp.XCount; x++)
|
||||||
|
{
|
||||||
|
int srcOffset = srcCalculator.GetOffset(src.RegionX + x, src.RegionY + y);
|
||||||
|
int dstOffset = dstCalculator.GetOffset(dst.RegionX + x, dst.RegionY + y);
|
||||||
|
|
||||||
|
ulong srcAddress = srcBaseAddress + (ulong)srcOffset;
|
||||||
|
ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
|
||||||
|
|
||||||
|
Span<byte> pixel = _context.PhysicalMemory.Read(srcAddress, (ulong)srcBpp);
|
||||||
|
|
||||||
|
_context.PhysicalMemory.Write(dstAddress, pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Buffer to buffer copy.
|
||||||
|
_bufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
|
||||||
|
|
||||||
|
Span<byte> data = _context.MemoryAccessor.Read(cbp.SrcAddress.Pack(), (uint)size);
|
||||||
|
|
||||||
|
_context.MemoryAccessor.Write(cbp.DstAddress.Pack(), data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
70
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs
Normal file
70
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private void CopyTexture(int argument)
|
||||||
|
{
|
||||||
|
CopyTexture dstCopyTexture = _context.State.GetCopyDstTexture();
|
||||||
|
CopyTexture srcCopyTexture = _context.State.GetCopySrcTexture();
|
||||||
|
|
||||||
|
Image.Texture srcTexture = _textureManager.FindOrCreateTexture(srcCopyTexture);
|
||||||
|
|
||||||
|
if (srcTexture == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the source texture that was found has a depth format,
|
||||||
|
// we must enforce the target texture also has a depth format,
|
||||||
|
// as copies between depth and color formats are not allowed.
|
||||||
|
if (srcTexture.Format == Format.D32Float)
|
||||||
|
{
|
||||||
|
dstCopyTexture.Format = RtFormat.D32Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image.Texture dstTexture = _textureManager.FindOrCreateTexture(dstCopyTexture);
|
||||||
|
|
||||||
|
if (dstTexture == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyTextureControl control = _context.State.GetCopyTextureControl();
|
||||||
|
|
||||||
|
CopyRegion region = _context.State.GetCopyRegion();
|
||||||
|
|
||||||
|
int srcX1 = (int)(region.SrcXF >> 32);
|
||||||
|
int srcY1 = (int)(region.SrcYF >> 32);
|
||||||
|
|
||||||
|
int srcX2 = (int)((region.SrcXF + region.SrcWidthRF * region.DstWidth) >> 32);
|
||||||
|
int srcY2 = (int)((region.SrcYF + region.SrcHeightRF * region.DstHeight) >> 32);
|
||||||
|
|
||||||
|
int dstX1 = region.DstX;
|
||||||
|
int dstY1 = region.DstY;
|
||||||
|
|
||||||
|
int dstX2 = region.DstX + region.DstWidth;
|
||||||
|
int dstY2 = region.DstY + region.DstHeight;
|
||||||
|
|
||||||
|
Extents2D srcRegion = new Extents2D(
|
||||||
|
srcX1 / srcTexture.Info.SamplesInX,
|
||||||
|
srcY1 / srcTexture.Info.SamplesInY,
|
||||||
|
srcX2 / srcTexture.Info.SamplesInX,
|
||||||
|
srcY2 / srcTexture.Info.SamplesInY);
|
||||||
|
|
||||||
|
Extents2D dstRegion = new Extents2D(
|
||||||
|
dstX1 / dstTexture.Info.SamplesInX,
|
||||||
|
dstY1 / dstTexture.Info.SamplesInY,
|
||||||
|
dstX2 / dstTexture.Info.SamplesInX,
|
||||||
|
dstY2 / dstTexture.Info.SamplesInY);
|
||||||
|
|
||||||
|
bool linearFilter = control.UnpackLinearFilter();
|
||||||
|
|
||||||
|
srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);
|
||||||
|
|
||||||
|
dstTexture.Modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
133
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
Normal file
133
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private bool _drawIndexed;
|
||||||
|
|
||||||
|
private int _firstIndex;
|
||||||
|
private int _indexCount;
|
||||||
|
|
||||||
|
private bool _instancedHasState;
|
||||||
|
private bool _instancedIndexed;
|
||||||
|
|
||||||
|
private int _instancedFirstIndex;
|
||||||
|
private int _instancedFirstVertex;
|
||||||
|
private int _instancedFirstInstance;
|
||||||
|
private int _instancedIndexCount;
|
||||||
|
private int _instancedDrawStateFirst;
|
||||||
|
private int _instancedDrawStateCount;
|
||||||
|
|
||||||
|
private int _instanceIndex;
|
||||||
|
|
||||||
|
public PrimitiveType PrimitiveType { get; private set; }
|
||||||
|
|
||||||
|
private void DrawEnd(int argument)
|
||||||
|
{
|
||||||
|
UpdateState();
|
||||||
|
|
||||||
|
bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;
|
||||||
|
|
||||||
|
if (instanced)
|
||||||
|
{
|
||||||
|
if (!_instancedHasState)
|
||||||
|
{
|
||||||
|
_instancedHasState = true;
|
||||||
|
|
||||||
|
_instancedIndexed = _drawIndexed;
|
||||||
|
|
||||||
|
_instancedFirstIndex = _firstIndex;
|
||||||
|
_instancedFirstVertex = _context.State.GetBaseVertex();
|
||||||
|
_instancedFirstInstance = _context.State.GetBaseInstance();
|
||||||
|
|
||||||
|
_instancedIndexCount = _indexCount;
|
||||||
|
|
||||||
|
VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState();
|
||||||
|
|
||||||
|
_instancedDrawStateFirst = drawState.First;
|
||||||
|
_instancedDrawStateCount = drawState.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int firstInstance = _context.State.GetBaseInstance();
|
||||||
|
|
||||||
|
if (_drawIndexed)
|
||||||
|
{
|
||||||
|
_drawIndexed = false;
|
||||||
|
|
||||||
|
int firstVertex = _context.State.GetBaseVertex();
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.DrawIndexed(
|
||||||
|
_indexCount,
|
||||||
|
1,
|
||||||
|
_firstIndex,
|
||||||
|
firstVertex,
|
||||||
|
firstInstance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState();
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.Draw(
|
||||||
|
drawState.Count,
|
||||||
|
1,
|
||||||
|
drawState.First,
|
||||||
|
firstInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawBegin(int argument)
|
||||||
|
{
|
||||||
|
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetPrimitiveTopology(type.Convert());
|
||||||
|
|
||||||
|
PrimitiveType = type;
|
||||||
|
|
||||||
|
if ((argument & (1 << 26)) != 0)
|
||||||
|
{
|
||||||
|
_instanceIndex++;
|
||||||
|
}
|
||||||
|
else if ((argument & (1 << 27)) == 0)
|
||||||
|
{
|
||||||
|
_instanceIndex = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetIndexCount(int argument)
|
||||||
|
{
|
||||||
|
_drawIndexed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PerformDeferredDraws()
|
||||||
|
{
|
||||||
|
// Perform any pending instanced draw.
|
||||||
|
if (_instancedHasState)
|
||||||
|
{
|
||||||
|
_instancedHasState = false;
|
||||||
|
|
||||||
|
if (_instancedIndexed)
|
||||||
|
{
|
||||||
|
_context.Renderer.GraphicsPipeline.DrawIndexed(
|
||||||
|
_instancedIndexCount,
|
||||||
|
_instanceIndex + 1,
|
||||||
|
_instancedFirstIndex,
|
||||||
|
_instancedFirstVertex,
|
||||||
|
_instancedFirstInstance);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_context.Renderer.GraphicsPipeline.Draw(
|
||||||
|
_instancedDrawStateCount,
|
||||||
|
_instanceIndex + 1,
|
||||||
|
_instancedDrawStateFirst,
|
||||||
|
_instancedFirstInstance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
Normal file
100
Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private ulong _runningCounter;
|
||||||
|
|
||||||
|
private void Report(int argument)
|
||||||
|
{
|
||||||
|
ReportMode mode = (ReportMode)(argument & 3);
|
||||||
|
|
||||||
|
ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case ReportMode.Semaphore: ReportSemaphore(); break;
|
||||||
|
case ReportMode.Counter: ReportCounter(type); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportSemaphore()
|
||||||
|
{
|
||||||
|
ReportState state = _context.State.GetReportState();
|
||||||
|
|
||||||
|
_context.MemoryAccessor.Write(state.Address.Pack(), state.Payload);
|
||||||
|
|
||||||
|
_context.AdvanceSequence();
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct CounterData
|
||||||
|
{
|
||||||
|
public ulong Counter;
|
||||||
|
public ulong Timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReportCounter(ReportCounterType type)
|
||||||
|
{
|
||||||
|
CounterData counterData = new CounterData();
|
||||||
|
|
||||||
|
ulong counter = 0;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ReportCounterType.Zero:
|
||||||
|
counter = 0;
|
||||||
|
break;
|
||||||
|
case ReportCounterType.SamplesPassed:
|
||||||
|
counter = _context.Renderer.GetCounter(CounterType.SamplesPassed);
|
||||||
|
break;
|
||||||
|
case ReportCounterType.PrimitivesGenerated:
|
||||||
|
counter = _context.Renderer.GetCounter(CounterType.PrimitivesGenerated);
|
||||||
|
break;
|
||||||
|
case ReportCounterType.TransformFeedbackPrimitivesWritten:
|
||||||
|
counter = _context.Renderer.GetCounter(CounterType.TransformFeedbackPrimitivesWritten);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong ticks;
|
||||||
|
|
||||||
|
if (GraphicsConfig.FastGpuTime)
|
||||||
|
{
|
||||||
|
ticks = _runningCounter++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
counterData.Counter = counter;
|
||||||
|
counterData.Timestamp = ticks;
|
||||||
|
|
||||||
|
Span<CounterData> counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1);
|
||||||
|
|
||||||
|
Span<byte> data = MemoryMarshal.Cast<CounterData, byte>(counterDataSpan);
|
||||||
|
|
||||||
|
ReportState state = _context.State.GetReportState();
|
||||||
|
|
||||||
|
_context.MemoryAccessor.Write(state.Address.Pack(), data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ulong ConvertNanosecondsToTicks(ulong nanoseconds)
|
||||||
|
{
|
||||||
|
// We need to divide first to avoid overflows.
|
||||||
|
// We fix up the result later by calculating the difference and adding
|
||||||
|
// that to the result.
|
||||||
|
ulong divided = nanoseconds / 625;
|
||||||
|
|
||||||
|
ulong rounded = divided * 625;
|
||||||
|
|
||||||
|
ulong errorBias = ((nanoseconds - rounded) * 384) / 625;
|
||||||
|
|
||||||
|
return divided * 384 + errorBias;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
Normal file
26
Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private void ResetCounter(int argument)
|
||||||
|
{
|
||||||
|
ResetCounterType type = (ResetCounterType)argument;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ResetCounterType.SamplesPassed:
|
||||||
|
_context.Renderer.ResetCounter(CounterType.SamplesPassed);
|
||||||
|
break;
|
||||||
|
case ResetCounterType.PrimitivesGenerated:
|
||||||
|
_context.Renderer.ResetCounter(CounterType.PrimitivesGenerated);
|
||||||
|
break;
|
||||||
|
case ResetCounterType.TransformFeedbackPrimitivesWritten:
|
||||||
|
_context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
52
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
Normal file
52
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private void UniformBufferBind0(int argument)
|
||||||
|
{
|
||||||
|
UniformBufferBind(argument, ShaderType.Vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UniformBufferBind1(int argument)
|
||||||
|
{
|
||||||
|
UniformBufferBind(argument, ShaderType.TessellationControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UniformBufferBind2(int argument)
|
||||||
|
{
|
||||||
|
UniformBufferBind(argument, ShaderType.TessellationEvaluation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UniformBufferBind3(int argument)
|
||||||
|
{
|
||||||
|
UniformBufferBind(argument, ShaderType.Geometry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UniformBufferBind4(int argument)
|
||||||
|
{
|
||||||
|
UniformBufferBind(argument, ShaderType.Fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UniformBufferBind(int argument, ShaderType type)
|
||||||
|
{
|
||||||
|
bool enable = (argument & 1) != 0;
|
||||||
|
|
||||||
|
int index = (argument >> 4) & 0x1f;
|
||||||
|
|
||||||
|
if (enable)
|
||||||
|
{
|
||||||
|
UniformBufferState uniformBuffer = _context.State.GetUniformBufferState();
|
||||||
|
|
||||||
|
ulong address = uniformBuffer.Address.Pack();
|
||||||
|
|
||||||
|
_bufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_bufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
Normal file
18
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private void UniformBufferUpdate(int argument)
|
||||||
|
{
|
||||||
|
UniformBufferState uniformBuffer = _context.State.GetUniformBufferState();
|
||||||
|
|
||||||
|
_context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument);
|
||||||
|
|
||||||
|
_context.State.SetUniformBufferOffset(uniformBuffer.Offset + 4);
|
||||||
|
|
||||||
|
_context.AdvanceSequence();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
784
Ryujinx.Graphics.Gpu/Engine/Methods.cs
Normal file
784
Ryujinx.Graphics.Gpu/Engine/Methods.cs
Normal file
|
@ -0,0 +1,784 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.GAL.Blend;
|
||||||
|
using Ryujinx.Graphics.GAL.DepthStencil;
|
||||||
|
using Ryujinx.Graphics.GAL.InputAssembler;
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
partial class Methods
|
||||||
|
{
|
||||||
|
private GpuContext _context;
|
||||||
|
|
||||||
|
private ShaderCache _shaderCache;
|
||||||
|
|
||||||
|
private BufferManager _bufferManager;
|
||||||
|
private TextureManager _textureManager;
|
||||||
|
|
||||||
|
public TextureManager TextureManager => _textureManager;
|
||||||
|
|
||||||
|
private bool _isAnyVbInstanced;
|
||||||
|
private bool _vsUsesInstanceId;
|
||||||
|
|
||||||
|
public Methods(GpuContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
|
||||||
|
_shaderCache = new ShaderCache(_context);
|
||||||
|
|
||||||
|
_bufferManager = new BufferManager(context);
|
||||||
|
_textureManager = new TextureManager(context, _bufferManager);
|
||||||
|
|
||||||
|
RegisterCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RegisterCallbacks()
|
||||||
|
{
|
||||||
|
_context.State.RegisterCopyBufferCallback(CopyBuffer);
|
||||||
|
_context.State.RegisterCopyTextureCallback(CopyTexture);
|
||||||
|
|
||||||
|
_context.State.RegisterDrawEndCallback(DrawEnd);
|
||||||
|
|
||||||
|
_context.State.RegisterDrawBeginCallback(DrawBegin);
|
||||||
|
|
||||||
|
_context.State.RegisterSetIndexCountCallback(SetIndexCount);
|
||||||
|
|
||||||
|
_context.State.RegisterClearCallback(Clear);
|
||||||
|
|
||||||
|
_context.State.RegisterReportCallback(Report);
|
||||||
|
|
||||||
|
_context.State.RegisterUniformBufferUpdateCallback(UniformBufferUpdate);
|
||||||
|
|
||||||
|
_context.State.RegisterUniformBufferBind0Callback(UniformBufferBind0);
|
||||||
|
_context.State.RegisterUniformBufferBind1Callback(UniformBufferBind1);
|
||||||
|
_context.State.RegisterUniformBufferBind2Callback(UniformBufferBind2);
|
||||||
|
_context.State.RegisterUniformBufferBind3Callback(UniformBufferBind3);
|
||||||
|
_context.State.RegisterUniformBufferBind4Callback(UniformBufferBind4);
|
||||||
|
|
||||||
|
_context.State.RegisterCallback(MethodOffset.InvalidateTextures, InvalidateTextures);
|
||||||
|
|
||||||
|
_context.State.RegisterCallback(MethodOffset.ResetCounter, ResetCounter);
|
||||||
|
|
||||||
|
_context.State.RegisterCallback(MethodOffset.Inline2MemoryExecute, Execute);
|
||||||
|
_context.State.RegisterCallback(MethodOffset.Inline2MemoryPushData, PushData);
|
||||||
|
|
||||||
|
_context.State.RegisterCallback(MethodOffset.Dispatch, Dispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Image.Texture GetTexture(ulong address) => _textureManager.Find2(address);
|
||||||
|
|
||||||
|
private void UpdateState()
|
||||||
|
{
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.Any) == 0)
|
||||||
|
{
|
||||||
|
CommitBindings();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shaders must be the first one to be updated if modified, because
|
||||||
|
// some of the other state depends on information from the currently
|
||||||
|
// bound shaders.
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.ShaderState) != 0)
|
||||||
|
{
|
||||||
|
UpdateShaderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.RenderTargetGroup) != 0)
|
||||||
|
{
|
||||||
|
UpdateRenderTargetGroupState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.DepthTestState) != 0)
|
||||||
|
{
|
||||||
|
UpdateDepthTestState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.ViewportTransform) != 0)
|
||||||
|
{
|
||||||
|
UpdateViewportTransform();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.DepthBiasState) != 0)
|
||||||
|
{
|
||||||
|
UpdateDepthBiasState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.StencilTestState) != 0)
|
||||||
|
{
|
||||||
|
UpdateStencilTestState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.SamplerPoolState) != 0)
|
||||||
|
{
|
||||||
|
UpdateSamplerPoolState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.TexturePoolState) != 0)
|
||||||
|
{
|
||||||
|
UpdateTexturePoolState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.InputAssemblerGroup) != 0)
|
||||||
|
{
|
||||||
|
UpdateInputAssemblerGroupState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.FaceState) != 0)
|
||||||
|
{
|
||||||
|
UpdateFaceState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.RtColorMask) != 0)
|
||||||
|
{
|
||||||
|
UpdateRtColorMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.BlendState) != 0)
|
||||||
|
{
|
||||||
|
UpdateBlendState();
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.State.StateWriteFlags &= ~StateWriteFlags.Any;
|
||||||
|
|
||||||
|
CommitBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CommitBindings()
|
||||||
|
{
|
||||||
|
_bufferManager.CommitBindings();
|
||||||
|
_textureManager.CommitBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvalidateRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
_bufferManager.InvalidateRange(address, size);
|
||||||
|
_textureManager.InvalidateRange(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvalidateTextureRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
_textureManager.InvalidateRange(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRenderTargetGroupState()
|
||||||
|
{
|
||||||
|
TextureMsaaMode msaaMode = _context.State.GetRtMsaaMode();
|
||||||
|
|
||||||
|
int samplesInX = msaaMode.SamplesInX();
|
||||||
|
int samplesInY = msaaMode.SamplesInY();
|
||||||
|
|
||||||
|
Image.Texture color3D = Get3DRenderTarget(samplesInX, samplesInY);
|
||||||
|
|
||||||
|
if (color3D == null)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
|
{
|
||||||
|
RtColorState colorState = _context.State.GetRtColorState(index);
|
||||||
|
|
||||||
|
if (!IsRtEnabled(colorState))
|
||||||
|
{
|
||||||
|
_textureManager.SetRenderTargetColor(index, null);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Image.Texture color = _textureManager.FindOrCreateTexture(
|
||||||
|
colorState,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY);
|
||||||
|
|
||||||
|
_textureManager.SetRenderTargetColor(index, color);
|
||||||
|
|
||||||
|
color.Modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_textureManager.SetRenderTargetColor3D(color3D);
|
||||||
|
|
||||||
|
color3D.Modified = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dsEnable = _context.State.Get<bool>(MethodOffset.RtDepthStencilEnable);
|
||||||
|
|
||||||
|
Image.Texture depthStencil = null;
|
||||||
|
|
||||||
|
if (dsEnable)
|
||||||
|
{
|
||||||
|
var dsState = _context.State.GetRtDepthStencilState();
|
||||||
|
var dsSize = _context.State.GetRtDepthStencilSize();
|
||||||
|
|
||||||
|
depthStencil = _textureManager.FindOrCreateTexture(
|
||||||
|
dsState,
|
||||||
|
dsSize,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY);
|
||||||
|
}
|
||||||
|
|
||||||
|
_textureManager.SetRenderTargetDepthStencil(depthStencil);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Image.Texture Get3DRenderTarget(int samplesInX, int samplesInY)
|
||||||
|
{
|
||||||
|
RtColorState colorState0 = _context.State.GetRtColorState(0);
|
||||||
|
|
||||||
|
if (!IsRtEnabled(colorState0) || !colorState0.MemoryLayout.UnpackIsTarget3D() || colorState0.Depth != 1)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int slices = 1;
|
||||||
|
int unused = 0;
|
||||||
|
|
||||||
|
for (int index = 1; index < Constants.TotalRenderTargets; index++)
|
||||||
|
{
|
||||||
|
RtColorState colorState = _context.State.GetRtColorState(index);
|
||||||
|
|
||||||
|
if (!IsRtEnabled(colorState))
|
||||||
|
{
|
||||||
|
unused++;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (colorState.MemoryLayout.UnpackIsTarget3D() && colorState.Depth == 1)
|
||||||
|
{
|
||||||
|
slices++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slices + unused == Constants.TotalRenderTargets)
|
||||||
|
{
|
||||||
|
colorState0.Depth = slices;
|
||||||
|
|
||||||
|
return _textureManager.FindOrCreateTexture(colorState0, samplesInX, samplesInY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRtEnabled(RtColorState colorState)
|
||||||
|
{
|
||||||
|
// Colors are disabled by writing 0 to the format.
|
||||||
|
return colorState.Format != 0 && colorState.WidthOrStride != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDepthTestState()
|
||||||
|
{
|
||||||
|
_context.Renderer.GraphicsPipeline.SetDepthTest(new DepthTestDescriptor(
|
||||||
|
_context.State.GetDepthTestEnable().IsTrue(),
|
||||||
|
_context.State.GetDepthWriteEnable().IsTrue(),
|
||||||
|
_context.State.GetDepthTestFunc()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateViewportTransform()
|
||||||
|
{
|
||||||
|
Viewport[] viewports = new Viewport[Constants.TotalViewports];
|
||||||
|
|
||||||
|
for (int index = 0; index < Constants.TotalViewports; index++)
|
||||||
|
{
|
||||||
|
var transform = _context.State.Get<ViewportTransform>(MethodOffset.ViewportTransform + index * 8);
|
||||||
|
var extents = _context.State.Get<ViewportExtents> (MethodOffset.ViewportExtents + index * 4);
|
||||||
|
|
||||||
|
float x = transform.TranslateX - MathF.Abs(transform.ScaleX);
|
||||||
|
float y = transform.TranslateY - MathF.Abs(transform.ScaleY);
|
||||||
|
|
||||||
|
float width = transform.ScaleX * 2;
|
||||||
|
float height = transform.ScaleY * 2;
|
||||||
|
|
||||||
|
RectangleF region = new RectangleF(x, y, width, height);
|
||||||
|
|
||||||
|
viewports[index] = new Viewport(
|
||||||
|
region,
|
||||||
|
transform.UnpackSwizzleX(),
|
||||||
|
transform.UnpackSwizzleY(),
|
||||||
|
transform.UnpackSwizzleZ(),
|
||||||
|
transform.UnpackSwizzleW(),
|
||||||
|
extents.DepthNear,
|
||||||
|
extents.DepthFar);
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetViewports(0, viewports);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateDepthBiasState()
|
||||||
|
{
|
||||||
|
var polygonOffset = _context.State.Get<DepthBiasState>(MethodOffset.DepthBiasState);
|
||||||
|
|
||||||
|
float factor = _context.State.Get<float>(MethodOffset.DepthBiasFactor);
|
||||||
|
float units = _context.State.Get<float>(MethodOffset.DepthBiasUnits);
|
||||||
|
float clamp = _context.State.Get<float>(MethodOffset.DepthBiasClamp);
|
||||||
|
|
||||||
|
PolygonModeMask enables = 0;
|
||||||
|
|
||||||
|
enables = (polygonOffset.PointEnable.IsTrue() ? PolygonModeMask.Point : 0);
|
||||||
|
enables |= (polygonOffset.LineEnable.IsTrue() ? PolygonModeMask.Line : 0);
|
||||||
|
enables |= (polygonOffset.FillEnable.IsTrue() ? PolygonModeMask.Fill : 0);
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetDepthBias(enables, factor, units, clamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStencilTestState()
|
||||||
|
{
|
||||||
|
StencilBackMasks backMasks = _context.State.GetStencilBackMasks();
|
||||||
|
StencilTestState test = _context.State.GetStencilTestState();
|
||||||
|
StencilBackTestState backTest = _context.State.GetStencilBackTestState();
|
||||||
|
|
||||||
|
CompareOp backFunc;
|
||||||
|
StencilOp backSFail;
|
||||||
|
StencilOp backDpPass;
|
||||||
|
StencilOp backDpFail;
|
||||||
|
int backFuncRef;
|
||||||
|
int backFuncMask;
|
||||||
|
int backMask;
|
||||||
|
|
||||||
|
if (backTest.TwoSided.IsTrue())
|
||||||
|
{
|
||||||
|
backFunc = backTest.BackFunc;
|
||||||
|
backSFail = backTest.BackSFail;
|
||||||
|
backDpPass = backTest.BackDpPass;
|
||||||
|
backDpFail = backTest.BackDpFail;
|
||||||
|
backFuncRef = backMasks.FuncRef;
|
||||||
|
backFuncMask = backMasks.FuncMask;
|
||||||
|
backMask = backMasks.Mask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
backFunc = test.FrontFunc;
|
||||||
|
backSFail = test.FrontSFail;
|
||||||
|
backDpPass = test.FrontDpPass;
|
||||||
|
backDpFail = test.FrontDpFail;
|
||||||
|
backFuncRef = test.FrontFuncRef;
|
||||||
|
backFuncMask = test.FrontFuncMask;
|
||||||
|
backMask = test.FrontMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetStencilTest(new StencilTestDescriptor(
|
||||||
|
test.Enable.IsTrue(),
|
||||||
|
test.FrontFunc,
|
||||||
|
test.FrontSFail,
|
||||||
|
test.FrontDpPass,
|
||||||
|
test.FrontDpFail,
|
||||||
|
test.FrontFuncRef,
|
||||||
|
test.FrontFuncMask,
|
||||||
|
test.FrontMask,
|
||||||
|
backFunc,
|
||||||
|
backSFail,
|
||||||
|
backDpPass,
|
||||||
|
backDpFail,
|
||||||
|
backFuncRef,
|
||||||
|
backFuncMask,
|
||||||
|
backMask));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateSamplerPoolState()
|
||||||
|
{
|
||||||
|
PoolState samplerPool = _context.State.GetSamplerPoolState();
|
||||||
|
|
||||||
|
_textureManager.SetSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTexturePoolState()
|
||||||
|
{
|
||||||
|
PoolState texturePool = _context.State.GetTexturePoolState();
|
||||||
|
|
||||||
|
_textureManager.SetTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
|
||||||
|
|
||||||
|
_textureManager.SetTextureBufferIndex(_context.State.GetTextureBufferIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateInputAssemblerGroupState()
|
||||||
|
{
|
||||||
|
// Must be updated before the vertex buffer.
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.VertexAttribState) != 0)
|
||||||
|
{
|
||||||
|
UpdateVertexAttribState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.PrimitiveRestartState) != 0)
|
||||||
|
{
|
||||||
|
UpdatePrimitiveRestartState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.IndexBufferState) != 0)
|
||||||
|
{
|
||||||
|
UpdateIndexBufferState();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((_context.State.StateWriteFlags & StateWriteFlags.VertexBufferState) != 0)
|
||||||
|
{
|
||||||
|
UpdateVertexBufferState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateVertexAttribState()
|
||||||
|
{
|
||||||
|
VertexAttribDescriptor[] vertexAttribs = new VertexAttribDescriptor[16];
|
||||||
|
|
||||||
|
for (int index = 0; index < 16; index++)
|
||||||
|
{
|
||||||
|
VertexAttribState vertexAttrib = _context.State.GetVertexAttribState(index);
|
||||||
|
|
||||||
|
if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
|
||||||
|
{
|
||||||
|
// TODO: warning.
|
||||||
|
|
||||||
|
format = Format.R32G32B32A32Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
vertexAttribs[index] = new VertexAttribDescriptor(
|
||||||
|
vertexAttrib.UnpackBufferIndex(),
|
||||||
|
vertexAttrib.UnpackOffset(),
|
||||||
|
format);
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.BindVertexAttribs(vertexAttribs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePrimitiveRestartState()
|
||||||
|
{
|
||||||
|
PrimitiveRestartState primitiveRestart = _context.State.Get<PrimitiveRestartState>(MethodOffset.PrimitiveRestartState);
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetPrimitiveRestart(
|
||||||
|
primitiveRestart.Enable,
|
||||||
|
primitiveRestart.Index);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateIndexBufferState()
|
||||||
|
{
|
||||||
|
IndexBufferState indexBuffer = _context.State.GetIndexBufferState();
|
||||||
|
|
||||||
|
_firstIndex = indexBuffer.First;
|
||||||
|
_indexCount = indexBuffer.Count;
|
||||||
|
|
||||||
|
if (_indexCount == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong gpuVa = indexBuffer.Address.Pack();
|
||||||
|
|
||||||
|
// Do not use the end address to calculate the size, because
|
||||||
|
// the result may be much larger than the real size of the index buffer.
|
||||||
|
ulong size = (ulong)(_firstIndex + _indexCount);
|
||||||
|
|
||||||
|
switch (indexBuffer.Type)
|
||||||
|
{
|
||||||
|
case IndexType.UShort: size *= 2; break;
|
||||||
|
case IndexType.UInt: size *= 4; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type);
|
||||||
|
|
||||||
|
// The index buffer affects the vertex buffer size calculation, we
|
||||||
|
// need to ensure that they are updated.
|
||||||
|
UpdateVertexBufferState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private uint GetIndexBufferMaxIndex(ulong gpuVa, ulong size, IndexType type)
|
||||||
|
{
|
||||||
|
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||||
|
|
||||||
|
Span<byte> data = _context.PhysicalMemory.Read(address, size);
|
||||||
|
|
||||||
|
uint maxIndex = 0;
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case IndexType.UByte:
|
||||||
|
{
|
||||||
|
for (int index = 0; index < data.Length; index++)
|
||||||
|
{
|
||||||
|
if (maxIndex < data[index])
|
||||||
|
{
|
||||||
|
maxIndex = data[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IndexType.UShort:
|
||||||
|
{
|
||||||
|
Span<ushort> indices = MemoryMarshal.Cast<byte, ushort>(data);
|
||||||
|
|
||||||
|
for (int index = 0; index < indices.Length; index++)
|
||||||
|
{
|
||||||
|
if (maxIndex < indices[index])
|
||||||
|
{
|
||||||
|
maxIndex = indices[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IndexType.UInt:
|
||||||
|
{
|
||||||
|
Span<uint> indices = MemoryMarshal.Cast<byte, uint>(data);
|
||||||
|
|
||||||
|
for (int index = 0; index < indices.Length; index++)
|
||||||
|
{
|
||||||
|
if (maxIndex < indices[index])
|
||||||
|
{
|
||||||
|
maxIndex = indices[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateVertexBufferState()
|
||||||
|
{
|
||||||
|
_isAnyVbInstanced = false;
|
||||||
|
|
||||||
|
for (int index = 0; index < 16; index++)
|
||||||
|
{
|
||||||
|
VertexBufferState vertexBuffer = _context.State.GetVertexBufferState(index);
|
||||||
|
|
||||||
|
if (!vertexBuffer.UnpackEnable())
|
||||||
|
{
|
||||||
|
_bufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
GpuVa endAddress = _context.State.GetVertexBufferEndAddress(index);
|
||||||
|
|
||||||
|
ulong address = vertexBuffer.Address.Pack();
|
||||||
|
|
||||||
|
int stride = vertexBuffer.UnpackStride();
|
||||||
|
|
||||||
|
bool instanced = _context.State.Get<bool>(MethodOffset.VertexBufferInstanced + index);
|
||||||
|
|
||||||
|
int divisor = instanced ? vertexBuffer.Divisor : 0;
|
||||||
|
|
||||||
|
_isAnyVbInstanced |= divisor != 0;
|
||||||
|
|
||||||
|
ulong size;
|
||||||
|
|
||||||
|
if (_drawIndexed || stride == 0 || instanced)
|
||||||
|
{
|
||||||
|
// This size may be (much) larger than the real vertex buffer size.
|
||||||
|
// Avoid calculating it this way, unless we don't have any other option.
|
||||||
|
size = endAddress.Pack() - address + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// For non-indexed draws, we can guess the size from the vertex count
|
||||||
|
// and stride.
|
||||||
|
int firstInstance = _context.State.GetBaseInstance();
|
||||||
|
|
||||||
|
VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState();
|
||||||
|
|
||||||
|
size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride);
|
||||||
|
}
|
||||||
|
|
||||||
|
_bufferManager.SetVertexBuffer(index, address, size, stride, divisor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFaceState()
|
||||||
|
{
|
||||||
|
FaceState face = _context.State.GetFaceState();
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetFaceCulling(face.CullEnable.IsTrue(), face.CullFace);
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetFrontFace(face.FrontFace);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRtColorMask()
|
||||||
|
{
|
||||||
|
uint[] componentMasks = new uint[Constants.TotalRenderTargets];
|
||||||
|
|
||||||
|
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||||
|
{
|
||||||
|
RtColorMask colorMask = _context.State.Get<RtColorMask>(MethodOffset.RtColorMask + index);
|
||||||
|
|
||||||
|
uint componentMask = 0;
|
||||||
|
|
||||||
|
componentMask = (colorMask.UnpackRed() ? 1u : 0u);
|
||||||
|
componentMask |= (colorMask.UnpackGreen() ? 2u : 0u);
|
||||||
|
componentMask |= (colorMask.UnpackBlue() ? 4u : 0u);
|
||||||
|
componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
|
||||||
|
|
||||||
|
componentMasks[index] = componentMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.SetRenderTargetColorMasks(componentMasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateBlendState()
|
||||||
|
{
|
||||||
|
BlendState[] blends = new BlendState[8];
|
||||||
|
|
||||||
|
for (int index = 0; index < 8; index++)
|
||||||
|
{
|
||||||
|
bool blendEnable = _context.State.GetBlendEnable(index).IsTrue();
|
||||||
|
|
||||||
|
BlendState blend = _context.State.GetBlendState(index);
|
||||||
|
|
||||||
|
BlendDescriptor descriptor = new BlendDescriptor(
|
||||||
|
blendEnable,
|
||||||
|
blend.ColorOp,
|
||||||
|
blend.ColorSrcFactor,
|
||||||
|
blend.ColorDstFactor,
|
||||||
|
blend.AlphaOp,
|
||||||
|
blend.AlphaSrcFactor,
|
||||||
|
blend.AlphaDstFactor);
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.BindBlendState(index, descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct SbDescriptor
|
||||||
|
{
|
||||||
|
public uint AddressLow;
|
||||||
|
public uint AddressHigh;
|
||||||
|
public int Size;
|
||||||
|
public int Padding;
|
||||||
|
|
||||||
|
public ulong PackAddress()
|
||||||
|
{
|
||||||
|
return AddressLow | ((ulong)AddressHigh << 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateShaderState()
|
||||||
|
{
|
||||||
|
ShaderAddresses addresses = new ShaderAddresses();
|
||||||
|
|
||||||
|
Span<ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);
|
||||||
|
|
||||||
|
Span<ulong> addressesArray = MemoryMarshal.Cast<ShaderAddresses, ulong>(addressesSpan);
|
||||||
|
|
||||||
|
ulong baseAddress = _context.State.GetShaderBaseAddress().Pack();
|
||||||
|
|
||||||
|
for (int index = 0; index < 6; index++)
|
||||||
|
{
|
||||||
|
ShaderState shader = _context.State.GetShaderState(index);
|
||||||
|
|
||||||
|
if (!shader.UnpackEnable() && index != 1)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
addressesArray[index] = baseAddress + shader.Offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphicsShader gs = _shaderCache.GetGraphicsShader(addresses);
|
||||||
|
|
||||||
|
_vsUsesInstanceId = gs.Shader[0].Info.UsesInstanceId;
|
||||||
|
|
||||||
|
for (int stage = 0; stage < Constants.TotalShaderStages; stage++)
|
||||||
|
{
|
||||||
|
ShaderProgramInfo info = gs.Shader[stage]?.Info;
|
||||||
|
|
||||||
|
if (info == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureBindings = new TextureBindingInfo[info.Textures.Count];
|
||||||
|
|
||||||
|
for (int index = 0; index < info.Textures.Count; index++)
|
||||||
|
{
|
||||||
|
var descriptor = info.Textures[index];
|
||||||
|
|
||||||
|
Target target = GetTarget(descriptor.Target);
|
||||||
|
|
||||||
|
textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
_textureManager.BindTextures(stage, textureBindings);
|
||||||
|
|
||||||
|
uint sbEnableMask = 0;
|
||||||
|
uint ubEnableMask = 0;
|
||||||
|
|
||||||
|
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||||
|
{
|
||||||
|
BufferDescriptor sb = info.SBuffers[index];
|
||||||
|
|
||||||
|
sbEnableMask |= 1u << sb.Slot;
|
||||||
|
|
||||||
|
ulong sbDescAddress = _bufferManager.GetGraphicsUniformBufferAddress(stage, 0);
|
||||||
|
|
||||||
|
int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
|
||||||
|
|
||||||
|
sbDescAddress += (ulong)sbDescOffset;
|
||||||
|
|
||||||
|
Span<byte> sbDescriptorData = _context.PhysicalMemory.Read(sbDescAddress, 0x10);
|
||||||
|
|
||||||
|
SbDescriptor sbDescriptor = MemoryMarshal.Cast<byte, SbDescriptor>(sbDescriptorData)[0];
|
||||||
|
|
||||||
|
_bufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < info.CBuffers.Count; index++)
|
||||||
|
{
|
||||||
|
ubEnableMask |= 1u << info.CBuffers[index].Slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask);
|
||||||
|
_bufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.BindProgram(gs.Interface);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Target GetTarget(Shader.TextureTarget target)
|
||||||
|
{
|
||||||
|
target &= ~Shader.TextureTarget.Shadow;
|
||||||
|
|
||||||
|
switch (target)
|
||||||
|
{
|
||||||
|
case Shader.TextureTarget.Texture1D:
|
||||||
|
return Target.Texture1D;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.Texture1D | Shader.TextureTarget.Array:
|
||||||
|
return Target.Texture1DArray;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.Texture2D:
|
||||||
|
return Target.Texture2D;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Array:
|
||||||
|
return Target.Texture2DArray;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Multisample:
|
||||||
|
return Target.Texture2DMultisample;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Multisample | Shader.TextureTarget.Array:
|
||||||
|
return Target.Texture2DMultisampleArray;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.Texture3D:
|
||||||
|
return Target.Texture3D;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.TextureCube:
|
||||||
|
return Target.Cubemap;
|
||||||
|
|
||||||
|
case Shader.TextureTarget.TextureCube | Shader.TextureTarget.Array:
|
||||||
|
return Target.CubemapArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Warning.
|
||||||
|
|
||||||
|
return Target.Texture2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InvalidateTextures(int argument)
|
||||||
|
{
|
||||||
|
_textureManager.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs
Normal file
34
Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
struct ShaderAddresses : IEquatable<ShaderAddresses>
|
||||||
|
{
|
||||||
|
public ulong VertexA;
|
||||||
|
public ulong Vertex;
|
||||||
|
public ulong TessControl;
|
||||||
|
public ulong TessEvaluation;
|
||||||
|
public ulong Geometry;
|
||||||
|
public ulong Fragment;
|
||||||
|
|
||||||
|
public override bool Equals(object other)
|
||||||
|
{
|
||||||
|
return other is ShaderAddresses addresses && Equals(addresses);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Equals(ShaderAddresses other)
|
||||||
|
{
|
||||||
|
return VertexA == other.VertexA &&
|
||||||
|
Vertex == other.Vertex &&
|
||||||
|
TessControl == other.TessControl &&
|
||||||
|
TessEvaluation == other.TessEvaluation &&
|
||||||
|
Geometry == other.Geometry &&
|
||||||
|
Fragment == other.Fragment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int GetHashCode()
|
||||||
|
{
|
||||||
|
return HashCode.Combine(VertexA, Vertex, TessControl, TessEvaluation, Geometry, Fragment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
228
Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs
Normal file
228
Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
class ShaderCache
|
||||||
|
{
|
||||||
|
private const int MaxProgramSize = 0x100000;
|
||||||
|
|
||||||
|
private GpuContext _context;
|
||||||
|
|
||||||
|
private ShaderDumper _dumper;
|
||||||
|
|
||||||
|
private Dictionary<ulong, ComputeShader> _cpPrograms;
|
||||||
|
|
||||||
|
private Dictionary<ShaderAddresses, GraphicsShader> _gpPrograms;
|
||||||
|
|
||||||
|
public ShaderCache(GpuContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
|
||||||
|
_dumper = new ShaderDumper(context);
|
||||||
|
|
||||||
|
_cpPrograms = new Dictionary<ulong, ComputeShader>();
|
||||||
|
|
||||||
|
_gpPrograms = new Dictionary<ShaderAddresses, GraphicsShader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComputeShader GetComputeShader(ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ)
|
||||||
|
{
|
||||||
|
if (!_cpPrograms.TryGetValue(gpuVa, out ComputeShader cpShader))
|
||||||
|
{
|
||||||
|
ShaderProgram shader = TranslateComputeShader(gpuVa);
|
||||||
|
|
||||||
|
shader.Replace(DefineNames.LocalSizeX, localSizeX.ToString(CultureInfo.InvariantCulture));
|
||||||
|
shader.Replace(DefineNames.LocalSizeY, localSizeY.ToString(CultureInfo.InvariantCulture));
|
||||||
|
shader.Replace(DefineNames.LocalSizeZ, localSizeZ.ToString(CultureInfo.InvariantCulture));
|
||||||
|
|
||||||
|
IShader hostShader = _context.Renderer.CompileShader(shader);
|
||||||
|
|
||||||
|
IProgram program = _context.Renderer.CreateProgram(new IShader[] { hostShader });
|
||||||
|
|
||||||
|
cpShader = new ComputeShader(program, shader);
|
||||||
|
|
||||||
|
_cpPrograms.Add(gpuVa, cpShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GraphicsShader GetGraphicsShader(ShaderAddresses addresses)
|
||||||
|
{
|
||||||
|
if (!_gpPrograms.TryGetValue(addresses, out GraphicsShader gpShader))
|
||||||
|
{
|
||||||
|
gpShader = new GraphicsShader();
|
||||||
|
|
||||||
|
if (addresses.VertexA != 0)
|
||||||
|
{
|
||||||
|
gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex, addresses.VertexA);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpShader.Shader[1] = TranslateGraphicsShader(addresses.TessControl);
|
||||||
|
gpShader.Shader[2] = TranslateGraphicsShader(addresses.TessEvaluation);
|
||||||
|
gpShader.Shader[3] = TranslateGraphicsShader(addresses.Geometry);
|
||||||
|
gpShader.Shader[4] = TranslateGraphicsShader(addresses.Fragment);
|
||||||
|
|
||||||
|
BackpropQualifiers(gpShader);
|
||||||
|
|
||||||
|
List<IShader> shaders = new List<IShader>();
|
||||||
|
|
||||||
|
for (int stage = 0; stage < gpShader.Shader.Length; stage++)
|
||||||
|
{
|
||||||
|
if (gpShader.Shader[stage] == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
IShader shader = _context.Renderer.CompileShader(gpShader.Shader[stage]);
|
||||||
|
|
||||||
|
shaders.Add(shader);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpShader.Interface = _context.Renderer.CreateProgram(shaders.ToArray());
|
||||||
|
|
||||||
|
_gpPrograms.Add(addresses, gpShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpShader;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderProgram TranslateComputeShader(ulong gpuVa)
|
||||||
|
{
|
||||||
|
if (gpuVa == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderProgram program;
|
||||||
|
|
||||||
|
const TranslationFlags flags =
|
||||||
|
TranslationFlags.Compute |
|
||||||
|
TranslationFlags.Unspecialized;
|
||||||
|
|
||||||
|
TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags);
|
||||||
|
|
||||||
|
Span<byte> code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize);
|
||||||
|
|
||||||
|
program = Translator.Translate(code, translationConfig);
|
||||||
|
|
||||||
|
_dumper.Dump(gpuVa, compute : true);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ShaderProgram TranslateGraphicsShader(ulong gpuVa, ulong gpuVaA = 0)
|
||||||
|
{
|
||||||
|
if (gpuVa == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderProgram program;
|
||||||
|
|
||||||
|
const TranslationFlags flags =
|
||||||
|
TranslationFlags.DebugMode |
|
||||||
|
TranslationFlags.Unspecialized;
|
||||||
|
|
||||||
|
TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags);
|
||||||
|
|
||||||
|
if (gpuVaA != 0)
|
||||||
|
{
|
||||||
|
Span<byte> codeA = _context.MemoryAccessor.Read(gpuVaA, MaxProgramSize);
|
||||||
|
Span<byte> codeB = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize);
|
||||||
|
|
||||||
|
program = Translator.Translate(codeA, codeB, translationConfig);
|
||||||
|
|
||||||
|
_dumper.Dump(gpuVaA, compute: false);
|
||||||
|
_dumper.Dump(gpuVa, compute: false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Span<byte> code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize);
|
||||||
|
|
||||||
|
program = Translator.Translate(code, translationConfig);
|
||||||
|
|
||||||
|
_dumper.Dump(gpuVa, compute: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (program.Stage == ShaderStage.Geometry)
|
||||||
|
{
|
||||||
|
PrimitiveType primitiveType = _context.Methods.PrimitiveType;
|
||||||
|
|
||||||
|
string inPrimitive = "points";
|
||||||
|
|
||||||
|
switch (primitiveType)
|
||||||
|
{
|
||||||
|
case PrimitiveType.Points:
|
||||||
|
inPrimitive = "points";
|
||||||
|
break;
|
||||||
|
case PrimitiveType.Lines:
|
||||||
|
case PrimitiveType.LineLoop:
|
||||||
|
case PrimitiveType.LineStrip:
|
||||||
|
inPrimitive = "lines";
|
||||||
|
break;
|
||||||
|
case PrimitiveType.LinesAdjacency:
|
||||||
|
case PrimitiveType.LineStripAdjacency:
|
||||||
|
inPrimitive = "lines_adjacency";
|
||||||
|
break;
|
||||||
|
case PrimitiveType.Triangles:
|
||||||
|
case PrimitiveType.TriangleStrip:
|
||||||
|
case PrimitiveType.TriangleFan:
|
||||||
|
inPrimitive = "triangles";
|
||||||
|
break;
|
||||||
|
case PrimitiveType.TrianglesAdjacency:
|
||||||
|
case PrimitiveType.TriangleStripAdjacency:
|
||||||
|
inPrimitive = "triangles_adjacency";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
program.Replace(DefineNames.InputTopologyName, inPrimitive);
|
||||||
|
}
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BackpropQualifiers(GraphicsShader program)
|
||||||
|
{
|
||||||
|
ShaderProgram fragmentShader = program.Shader[4];
|
||||||
|
|
||||||
|
bool isFirst = true;
|
||||||
|
|
||||||
|
for (int stage = 3; stage >= 0; stage--)
|
||||||
|
{
|
||||||
|
if (program.Shader[stage] == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to iterate backwards, since we do name replacement,
|
||||||
|
// and it would otherwise replace a subset of the longer names.
|
||||||
|
for (int attr = 31; attr >= 0; attr--)
|
||||||
|
{
|
||||||
|
string iq = fragmentShader?.Info.InterpolationQualifiers[attr].ToGlslQualifier() ?? string.Empty;
|
||||||
|
|
||||||
|
if (isFirst && iq != string.Empty)
|
||||||
|
{
|
||||||
|
program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirst = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
126
Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs
Normal file
126
Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
using System.IO;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Engine
|
||||||
|
{
|
||||||
|
class ShaderDumper
|
||||||
|
{
|
||||||
|
private const int ShaderHeaderSize = 0x50;
|
||||||
|
|
||||||
|
private GpuContext _context;
|
||||||
|
|
||||||
|
private string _runtimeDir;
|
||||||
|
private string _dumpPath;
|
||||||
|
private int _dumpIndex;
|
||||||
|
|
||||||
|
public int CurrentDumpIndex => _dumpIndex;
|
||||||
|
|
||||||
|
public ShaderDumper(GpuContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
|
||||||
|
_dumpIndex = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dump(ulong gpuVa, bool compute)
|
||||||
|
{
|
||||||
|
_dumpPath = GraphicsConfig.ShadersDumpPath;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_dumpPath))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string fileName = "Shader" + _dumpIndex.ToString("d4") + ".bin";
|
||||||
|
|
||||||
|
string fullPath = Path.Combine(FullDir(), fileName);
|
||||||
|
string codePath = Path.Combine(CodeDir(), fileName);
|
||||||
|
|
||||||
|
_dumpIndex++;
|
||||||
|
|
||||||
|
ulong headerSize = compute ? 0UL : ShaderHeaderSize;
|
||||||
|
|
||||||
|
using (FileStream fullFile = File.Create(fullPath))
|
||||||
|
using (FileStream codeFile = File.Create(codePath))
|
||||||
|
{
|
||||||
|
BinaryWriter fullWriter = new BinaryWriter(fullFile);
|
||||||
|
BinaryWriter codeWriter = new BinaryWriter(codeFile);
|
||||||
|
|
||||||
|
for (ulong i = 0; i < headerSize; i += 4)
|
||||||
|
{
|
||||||
|
fullWriter.Write(_context.MemoryAccessor.ReadInt32(gpuVa + i));
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong offset = 0;
|
||||||
|
|
||||||
|
ulong instruction = 0;
|
||||||
|
|
||||||
|
// Dump until a NOP instruction is found.
|
||||||
|
while ((instruction >> 48 & 0xfff8) != 0x50b0)
|
||||||
|
{
|
||||||
|
uint word0 = (uint)_context.MemoryAccessor.ReadInt32(gpuVa + headerSize + offset + 0);
|
||||||
|
uint word1 = (uint)_context.MemoryAccessor.ReadInt32(gpuVa + headerSize + offset + 4);
|
||||||
|
|
||||||
|
instruction = word0 | (ulong)word1 << 32;
|
||||||
|
|
||||||
|
// Zero instructions (other kind of NOP) stop immediately,
|
||||||
|
// this is to avoid two rows of zeroes.
|
||||||
|
if (instruction == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fullWriter.Write(instruction);
|
||||||
|
codeWriter.Write(instruction);
|
||||||
|
|
||||||
|
offset += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align to meet nvdisasm requirements.
|
||||||
|
while (offset % 0x20 != 0)
|
||||||
|
{
|
||||||
|
fullWriter.Write(0);
|
||||||
|
codeWriter.Write(0);
|
||||||
|
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FullDir()
|
||||||
|
{
|
||||||
|
return CreateAndReturn(Path.Combine(DumpDir(), "Full"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CodeDir()
|
||||||
|
{
|
||||||
|
return CreateAndReturn(Path.Combine(DumpDir(), "Code"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string DumpDir()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_runtimeDir))
|
||||||
|
{
|
||||||
|
int index = 1;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
_runtimeDir = Path.Combine(_dumpPath, "Dumps" + index.ToString("d2"));
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
while (Directory.Exists(_runtimeDir));
|
||||||
|
|
||||||
|
Directory.CreateDirectory(_runtimeDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _runtimeDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateAndReturn(string dir)
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
Ryujinx.Graphics.Gpu/GpuContext.cs
Normal file
100
Ryujinx.Graphics.Gpu/GpuContext.cs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
using Ryujinx.Graphics.Gpu.Engine;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
public class GpuContext
|
||||||
|
{
|
||||||
|
public IRenderer Renderer { get; }
|
||||||
|
|
||||||
|
internal GpuState State { get; }
|
||||||
|
|
||||||
|
internal IPhysicalMemory PhysicalMemory { get; private set; }
|
||||||
|
|
||||||
|
public MemoryManager MemoryManager { get; }
|
||||||
|
|
||||||
|
internal MemoryAccessor MemoryAccessor { get; }
|
||||||
|
|
||||||
|
internal Methods Methods { get; }
|
||||||
|
|
||||||
|
internal NvGpuFifo Fifo { get; }
|
||||||
|
|
||||||
|
public DmaPusher DmaPusher { get; }
|
||||||
|
|
||||||
|
internal int SequenceNumber { get; private set; }
|
||||||
|
|
||||||
|
private Lazy<Capabilities> _caps;
|
||||||
|
|
||||||
|
internal Capabilities Capabilities => _caps.Value;
|
||||||
|
|
||||||
|
public GpuContext(IRenderer renderer)
|
||||||
|
{
|
||||||
|
Renderer = renderer;
|
||||||
|
|
||||||
|
State = new GpuState();
|
||||||
|
|
||||||
|
MemoryManager = new MemoryManager();
|
||||||
|
|
||||||
|
MemoryAccessor = new MemoryAccessor(this);
|
||||||
|
|
||||||
|
Methods = new Methods(this);
|
||||||
|
|
||||||
|
Fifo = new NvGpuFifo(this);
|
||||||
|
|
||||||
|
DmaPusher = new DmaPusher(this);
|
||||||
|
|
||||||
|
_caps = new Lazy<Capabilities>(GetCapabilities);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void AdvanceSequence()
|
||||||
|
{
|
||||||
|
SequenceNumber++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITexture GetTexture(
|
||||||
|
ulong address,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int stride,
|
||||||
|
bool isLinear,
|
||||||
|
int gobBlocksInY,
|
||||||
|
Format format,
|
||||||
|
int bytesPerPixel)
|
||||||
|
{
|
||||||
|
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel);
|
||||||
|
|
||||||
|
TextureInfo info = new TextureInfo(
|
||||||
|
address,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
stride,
|
||||||
|
isLinear,
|
||||||
|
gobBlocksInY,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
Target.Texture2D,
|
||||||
|
formatInfo);
|
||||||
|
|
||||||
|
return Methods.GetTexture(address)?.HostTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Capabilities GetCapabilities()
|
||||||
|
{
|
||||||
|
return Renderer.GetCapabilities();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVmm(IPhysicalMemory mm)
|
||||||
|
{
|
||||||
|
PhysicalMemory = mm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.Gpu/GraphicsConfig.cs
Normal file
12
Ryujinx.Graphics.Gpu/GraphicsConfig.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu
|
||||||
|
{
|
||||||
|
public static class GraphicsConfig
|
||||||
|
{
|
||||||
|
public static string ShadersDumpPath;
|
||||||
|
|
||||||
|
public static bool FastGpuTime = true;
|
||||||
|
|
||||||
|
public static bool DisableTUpdate;
|
||||||
|
public static bool DisableBUpdate;
|
||||||
|
}
|
||||||
|
}
|
62
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal file
62
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
class AutoDeleteCache : IEnumerable<Texture>
|
||||||
|
{
|
||||||
|
private const int MaxCapacity = 2048;
|
||||||
|
|
||||||
|
private LinkedList<Texture> _textures;
|
||||||
|
|
||||||
|
public AutoDeleteCache()
|
||||||
|
{
|
||||||
|
_textures = new LinkedList<Texture>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(Texture texture)
|
||||||
|
{
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
|
|
||||||
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
|
||||||
|
if (_textures.Count > MaxCapacity)
|
||||||
|
{
|
||||||
|
Texture oldestTexture = _textures.First.Value;
|
||||||
|
|
||||||
|
_textures.RemoveFirst();
|
||||||
|
|
||||||
|
oldestTexture.DecrementReferenceCount();
|
||||||
|
|
||||||
|
oldestTexture.CacheNode = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Lift(Texture texture)
|
||||||
|
{
|
||||||
|
if (texture.CacheNode != null)
|
||||||
|
{
|
||||||
|
if (texture.CacheNode != _textures.Last)
|
||||||
|
{
|
||||||
|
_textures.Remove(texture.CacheNode);
|
||||||
|
|
||||||
|
texture.CacheNode = _textures.AddLast(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Add(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<Texture> GetEnumerator()
|
||||||
|
{
|
||||||
|
return _textures.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return _textures.GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal file
31
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
struct FormatInfo
|
||||||
|
{
|
||||||
|
private static FormatInfo _rgba8 = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
|
||||||
|
|
||||||
|
public static FormatInfo Default => _rgba8;
|
||||||
|
|
||||||
|
public Format Format { get; }
|
||||||
|
|
||||||
|
public int BlockWidth { get; }
|
||||||
|
public int BlockHeight { get; }
|
||||||
|
public int BytesPerPixel { get; }
|
||||||
|
|
||||||
|
public bool IsCompressed => (BlockWidth | BlockHeight) != 1;
|
||||||
|
|
||||||
|
public FormatInfo(
|
||||||
|
Format format,
|
||||||
|
int blockWidth,
|
||||||
|
int blockHeight,
|
||||||
|
int bytesPerPixel)
|
||||||
|
{
|
||||||
|
Format = format;
|
||||||
|
BlockWidth = blockWidth;
|
||||||
|
BlockHeight = blockHeight;
|
||||||
|
BytesPerPixel = bytesPerPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
201
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal file
201
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
static class FormatTable
|
||||||
|
{
|
||||||
|
private static Dictionary<uint, FormatInfo> _textureFormats = new Dictionary<uint, FormatInfo>()
|
||||||
|
{
|
||||||
|
{ 0x2491d, new FormatInfo(Format.R8Unorm, 1, 1, 1) },
|
||||||
|
{ 0x1249d, new FormatInfo(Format.R8Snorm, 1, 1, 1) },
|
||||||
|
{ 0x4921d, new FormatInfo(Format.R8Uint, 1, 1, 1) },
|
||||||
|
{ 0x36d9d, new FormatInfo(Format.R8Sint, 1, 1, 1) },
|
||||||
|
{ 0x7ff9b, new FormatInfo(Format.R16Float, 1, 1, 2) },
|
||||||
|
{ 0x2491b, new FormatInfo(Format.R16Unorm, 1, 1, 2) },
|
||||||
|
{ 0x1249b, new FormatInfo(Format.R16Snorm, 1, 1, 2) },
|
||||||
|
{ 0x4921b, new FormatInfo(Format.R16Uint, 1, 1, 2) },
|
||||||
|
{ 0x36d9b, new FormatInfo(Format.R16Sint, 1, 1, 2) },
|
||||||
|
{ 0x7ff8f, new FormatInfo(Format.R32Float, 1, 1, 4) },
|
||||||
|
{ 0x4920f, new FormatInfo(Format.R32Uint, 1, 1, 4) },
|
||||||
|
{ 0x36d8f, new FormatInfo(Format.R32Sint, 1, 1, 4) },
|
||||||
|
{ 0x24918, new FormatInfo(Format.R8G8Unorm, 1, 1, 2) },
|
||||||
|
{ 0x12498, new FormatInfo(Format.R8G8Snorm, 1, 1, 2) },
|
||||||
|
{ 0x49218, new FormatInfo(Format.R8G8Uint, 1, 1, 2) },
|
||||||
|
{ 0x36d98, new FormatInfo(Format.R8G8Sint, 1, 1, 2) },
|
||||||
|
{ 0x7ff8c, new FormatInfo(Format.R16G16Float, 1, 1, 4) },
|
||||||
|
{ 0x2490c, new FormatInfo(Format.R16G16Unorm, 1, 1, 4) },
|
||||||
|
{ 0x1248c, new FormatInfo(Format.R16G16Snorm, 1, 1, 4) },
|
||||||
|
{ 0x4920c, new FormatInfo(Format.R16G16Uint, 1, 1, 4) },
|
||||||
|
{ 0x36d8c, new FormatInfo(Format.R16G16Sint, 1, 1, 4) },
|
||||||
|
{ 0x7ff84, new FormatInfo(Format.R32G32Float, 1, 1, 8) },
|
||||||
|
{ 0x49204, new FormatInfo(Format.R32G32Uint, 1, 1, 8) },
|
||||||
|
{ 0x36d84, new FormatInfo(Format.R32G32Sint, 1, 1, 8) },
|
||||||
|
{ 0x7ff82, new FormatInfo(Format.R32G32B32Float, 1, 1, 12) },
|
||||||
|
{ 0x49202, new FormatInfo(Format.R32G32B32Uint, 1, 1, 12) },
|
||||||
|
{ 0x36d82, new FormatInfo(Format.R32G32B32Sint, 1, 1, 12) },
|
||||||
|
{ 0x24908, new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4) },
|
||||||
|
{ 0x12488, new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4) },
|
||||||
|
{ 0x49208, new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4) },
|
||||||
|
{ 0x36d88, new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4) },
|
||||||
|
{ 0x7ff83, new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8) },
|
||||||
|
{ 0x24903, new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8) },
|
||||||
|
{ 0x12483, new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8) },
|
||||||
|
{ 0x49203, new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8) },
|
||||||
|
{ 0x36d83, new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8) },
|
||||||
|
{ 0x7ff81, new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16) },
|
||||||
|
{ 0x49201, new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16) },
|
||||||
|
{ 0x36d81, new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16) },
|
||||||
|
{ 0x2493a, new FormatInfo(Format.D16Unorm, 1, 1, 2) },
|
||||||
|
{ 0x7ffaf, new FormatInfo(Format.D32Float, 1, 1, 4) },
|
||||||
|
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4) },
|
||||||
|
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8) },
|
||||||
|
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4) },
|
||||||
|
{ 0x24912, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2) },
|
||||||
|
{ 0x24914, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2) },
|
||||||
|
{ 0x24915, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2) },
|
||||||
|
{ 0x24909, new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4) },
|
||||||
|
{ 0x49209, new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4) },
|
||||||
|
{ 0x7ffa1, new FormatInfo(Format.R11G11B10Float, 1, 1, 4) },
|
||||||
|
{ 0x7ffa0, new FormatInfo(Format.R9G9B9E5Float, 1, 1, 4) },
|
||||||
|
{ 0x24924, new FormatInfo(Format.Bc1RgbaUnorm, 4, 4, 8) },
|
||||||
|
{ 0x24925, new FormatInfo(Format.Bc2Unorm, 4, 4, 16) },
|
||||||
|
{ 0x24926, new FormatInfo(Format.Bc3Unorm, 4, 4, 16) },
|
||||||
|
{ 0xa4924, new FormatInfo(Format.Bc1RgbaSrgb, 4, 4, 8) },
|
||||||
|
{ 0xa4925, new FormatInfo(Format.Bc2Srgb, 4, 4, 16) },
|
||||||
|
{ 0xa4926, new FormatInfo(Format.Bc3Srgb, 4, 4, 16) },
|
||||||
|
{ 0x24927, new FormatInfo(Format.Bc4Unorm, 4, 4, 8) },
|
||||||
|
{ 0x124a7, new FormatInfo(Format.Bc4Snorm, 4, 4, 8) },
|
||||||
|
{ 0x24928, new FormatInfo(Format.Bc5Unorm, 4, 4, 16) },
|
||||||
|
{ 0x124a8, new FormatInfo(Format.Bc5Snorm, 4, 4, 16) },
|
||||||
|
{ 0x24917, new FormatInfo(Format.Bc7Unorm, 4, 4, 16) },
|
||||||
|
{ 0xa4917, new FormatInfo(Format.Bc7Srgb, 4, 4, 16) },
|
||||||
|
{ 0x7ff90, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16) },
|
||||||
|
{ 0x7ff91, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16) },
|
||||||
|
{ 0x24940, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16) },
|
||||||
|
{ 0x24950, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16) },
|
||||||
|
{ 0x24941, new FormatInfo(Format.Astc5x5Unorm, 5, 5, 16) },
|
||||||
|
{ 0x24951, new FormatInfo(Format.Astc6x5Unorm, 6, 5, 16) },
|
||||||
|
{ 0x24942, new FormatInfo(Format.Astc6x6Unorm, 6, 6, 16) },
|
||||||
|
{ 0x24955, new FormatInfo(Format.Astc8x5Unorm, 8, 5, 16) },
|
||||||
|
{ 0x24952, new FormatInfo(Format.Astc8x6Unorm, 8, 6, 16) },
|
||||||
|
{ 0x24944, new FormatInfo(Format.Astc8x8Unorm, 8, 8, 16) },
|
||||||
|
{ 0x24956, new FormatInfo(Format.Astc10x5Unorm, 10, 5, 16) },
|
||||||
|
{ 0x24957, new FormatInfo(Format.Astc10x6Unorm, 10, 6, 16) },
|
||||||
|
{ 0x24953, new FormatInfo(Format.Astc10x8Unorm, 10, 8, 16) },
|
||||||
|
{ 0x24945, new FormatInfo(Format.Astc10x10Unorm, 10, 10, 16) },
|
||||||
|
{ 0x24954, new FormatInfo(Format.Astc12x10Unorm, 12, 10, 16) },
|
||||||
|
{ 0x24946, new FormatInfo(Format.Astc12x12Unorm, 12, 12, 16) },
|
||||||
|
{ 0xa4940, new FormatInfo(Format.Astc4x4Srgb, 4, 4, 16) },
|
||||||
|
{ 0xa4950, new FormatInfo(Format.Astc5x4Srgb, 5, 4, 16) },
|
||||||
|
{ 0xa4941, new FormatInfo(Format.Astc5x5Srgb, 5, 5, 16) },
|
||||||
|
{ 0xa4951, new FormatInfo(Format.Astc6x5Srgb, 6, 5, 16) },
|
||||||
|
{ 0xa4942, new FormatInfo(Format.Astc6x6Srgb, 6, 6, 16) },
|
||||||
|
{ 0xa4955, new FormatInfo(Format.Astc8x5Srgb, 8, 5, 16) },
|
||||||
|
{ 0xa4952, new FormatInfo(Format.Astc8x6Srgb, 8, 6, 16) },
|
||||||
|
{ 0xa4944, new FormatInfo(Format.Astc8x8Srgb, 8, 8, 16) },
|
||||||
|
{ 0xa4956, new FormatInfo(Format.Astc10x5Srgb, 10, 5, 16) },
|
||||||
|
{ 0xa4957, new FormatInfo(Format.Astc10x6Srgb, 10, 6, 16) },
|
||||||
|
{ 0xa4953, new FormatInfo(Format.Astc10x8Srgb, 10, 8, 16) },
|
||||||
|
{ 0xa4945, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16) },
|
||||||
|
{ 0xa4954, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16) },
|
||||||
|
{ 0xa4946, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16) },
|
||||||
|
{ 0x24913, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2) }
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Dictionary<ulong, Format> _attribFormats = new Dictionary<ulong, Format>()
|
||||||
|
{
|
||||||
|
{ 0x13a00000, Format.R8Unorm },
|
||||||
|
{ 0x0ba00000, Format.R8Snorm },
|
||||||
|
{ 0x23a00000, Format.R8Uint },
|
||||||
|
{ 0x1ba00000, Format.R8Sint },
|
||||||
|
{ 0x3b600000, Format.R16Float },
|
||||||
|
{ 0x13600000, Format.R16Unorm },
|
||||||
|
{ 0x0b600000, Format.R16Snorm },
|
||||||
|
{ 0x23600000, Format.R16Uint },
|
||||||
|
{ 0x1b600000, Format.R16Sint },
|
||||||
|
{ 0x3a400000, Format.R32Float },
|
||||||
|
{ 0x22400000, Format.R32Uint },
|
||||||
|
{ 0x1a400000, Format.R32Sint },
|
||||||
|
{ 0x13000000, Format.R8G8Unorm },
|
||||||
|
{ 0x0b000000, Format.R8G8Snorm },
|
||||||
|
{ 0x23000000, Format.R8G8Uint },
|
||||||
|
{ 0x1b000000, Format.R8G8Sint },
|
||||||
|
{ 0x39e00000, Format.R16G16Float },
|
||||||
|
{ 0x11e00000, Format.R16G16Unorm },
|
||||||
|
{ 0x09e00000, Format.R16G16Snorm },
|
||||||
|
{ 0x21e00000, Format.R16G16Uint },
|
||||||
|
{ 0x19e00000, Format.R16G16Sint },
|
||||||
|
{ 0x38800000, Format.R32G32Float },
|
||||||
|
{ 0x20800000, Format.R32G32Uint },
|
||||||
|
{ 0x18800000, Format.R32G32Sint },
|
||||||
|
{ 0x12600000, Format.R8G8B8Unorm },
|
||||||
|
{ 0x0a600000, Format.R8G8B8Snorm },
|
||||||
|
{ 0x22600000, Format.R8G8B8Uint },
|
||||||
|
{ 0x1a600000, Format.R8G8B8Sint },
|
||||||
|
{ 0x38a00000, Format.R16G16B16Float },
|
||||||
|
{ 0x10a00000, Format.R16G16B16Unorm },
|
||||||
|
{ 0x08a00000, Format.R16G16B16Snorm },
|
||||||
|
{ 0x20a00000, Format.R16G16B16Uint },
|
||||||
|
{ 0x18a00000, Format.R16G16B16Sint },
|
||||||
|
{ 0x38400000, Format.R32G32B32Float },
|
||||||
|
{ 0x20400000, Format.R32G32B32Uint },
|
||||||
|
{ 0x18400000, Format.R32G32B32Sint },
|
||||||
|
{ 0x11400000, Format.R8G8B8A8Unorm },
|
||||||
|
{ 0x09400000, Format.R8G8B8A8Snorm },
|
||||||
|
{ 0x21400000, Format.R8G8B8A8Uint },
|
||||||
|
{ 0x19400000, Format.R8G8B8A8Sint },
|
||||||
|
{ 0x38600000, Format.R16G16B16A16Float },
|
||||||
|
{ 0x10600000, Format.R16G16B16A16Unorm },
|
||||||
|
{ 0x08600000, Format.R16G16B16A16Snorm },
|
||||||
|
{ 0x20600000, Format.R16G16B16A16Uint },
|
||||||
|
{ 0x18600000, Format.R16G16B16A16Sint },
|
||||||
|
{ 0x38200000, Format.R32G32B32A32Float },
|
||||||
|
{ 0x20200000, Format.R32G32B32A32Uint },
|
||||||
|
{ 0x18200000, Format.R32G32B32A32Sint },
|
||||||
|
{ 0x16000000, Format.R10G10B10A2Unorm },
|
||||||
|
{ 0x26000000, Format.R10G10B10A2Uint },
|
||||||
|
{ 0x3e200000, Format.R11G11B10Float },
|
||||||
|
{ 0x2ba00000, Format.R8Uscaled },
|
||||||
|
{ 0x33a00000, Format.R8Sscaled },
|
||||||
|
{ 0x2b600000, Format.R16Uscaled },
|
||||||
|
{ 0x33600000, Format.R16Sscaled },
|
||||||
|
{ 0x2a400000, Format.R32Uscaled },
|
||||||
|
{ 0x32400000, Format.R32Sscaled },
|
||||||
|
{ 0x2b000000, Format.R8G8Uscaled },
|
||||||
|
{ 0x33000000, Format.R8G8Sscaled },
|
||||||
|
{ 0x29e00000, Format.R16G16Uscaled },
|
||||||
|
{ 0x31e00000, Format.R16G16Sscaled },
|
||||||
|
{ 0x28800000, Format.R32G32Uscaled },
|
||||||
|
{ 0x30800000, Format.R32G32Sscaled },
|
||||||
|
{ 0x2a600000, Format.R8G8B8Uscaled },
|
||||||
|
{ 0x32600000, Format.R8G8B8Sscaled },
|
||||||
|
{ 0x28a00000, Format.R16G16B16Uscaled },
|
||||||
|
{ 0x30a00000, Format.R16G16B16Sscaled },
|
||||||
|
{ 0x28400000, Format.R32G32B32Uscaled },
|
||||||
|
{ 0x30400000, Format.R32G32B32Sscaled },
|
||||||
|
{ 0x29400000, Format.R8G8B8A8Uscaled },
|
||||||
|
{ 0x31400000, Format.R8G8B8A8Sscaled },
|
||||||
|
{ 0x28600000, Format.R16G16B16A16Uscaled },
|
||||||
|
{ 0x30600000, Format.R16G16B16A16Sscaled },
|
||||||
|
{ 0x28200000, Format.R32G32B32A32Uscaled },
|
||||||
|
{ 0x30200000, Format.R32G32B32A32Sscaled },
|
||||||
|
{ 0x0e000000, Format.R10G10B10A2Snorm },
|
||||||
|
{ 0x1e000000, Format.R10G10B10A2Sint },
|
||||||
|
{ 0x2e000000, Format.R10G10B10A2Uscaled },
|
||||||
|
{ 0x36000000, Format.R10G10B10A2Sscaled }
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
|
||||||
|
{
|
||||||
|
encoded |= (isSrgb ? 1u << 19 : 0u);
|
||||||
|
|
||||||
|
return _textureFormats.TryGetValue(encoded, out format);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetAttribFormat(uint encoded, out Format format)
|
||||||
|
{
|
||||||
|
return _attribFormats.TryGetValue(encoded, out format);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
99
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal file
99
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
abstract class Pool<T> : IDisposable
|
||||||
|
{
|
||||||
|
protected const int DescriptorSize = 0x20;
|
||||||
|
|
||||||
|
protected GpuContext Context;
|
||||||
|
|
||||||
|
protected T[] Items;
|
||||||
|
|
||||||
|
public ulong Address { get; }
|
||||||
|
public ulong Size { get; }
|
||||||
|
|
||||||
|
public Pool(GpuContext context, ulong address, int maximumId)
|
||||||
|
{
|
||||||
|
Context = context;
|
||||||
|
|
||||||
|
int count = maximumId + 1;
|
||||||
|
|
||||||
|
ulong size = (ulong)(uint)count * DescriptorSize;;
|
||||||
|
|
||||||
|
Items = new T[count];
|
||||||
|
|
||||||
|
Address = address;
|
||||||
|
Size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract T Get(int id);
|
||||||
|
|
||||||
|
public void SynchronizeMemory()
|
||||||
|
{
|
||||||
|
(ulong, ulong)[] modifiedRanges = Context.PhysicalMemory.GetModifiedRanges(Address, Size);
|
||||||
|
|
||||||
|
for (int index = 0; index < modifiedRanges.Length; index++)
|
||||||
|
{
|
||||||
|
(ulong mAddress, ulong mSize) = modifiedRanges[index];
|
||||||
|
|
||||||
|
if (mAddress < Address)
|
||||||
|
{
|
||||||
|
mAddress = Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong maxSize = Address + Size - mAddress;
|
||||||
|
|
||||||
|
if (mSize > maxSize)
|
||||||
|
{
|
||||||
|
mSize = maxSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidateRangeImpl(mAddress, mSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvalidateRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
|
ulong texturePoolEndAddress = Address + Size;
|
||||||
|
|
||||||
|
// If the range being invalidated is not overlapping the texture pool range,
|
||||||
|
// then we don't have anything to do, exit early.
|
||||||
|
if (address >= texturePoolEndAddress || endAddress <= Address)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address < Address)
|
||||||
|
{
|
||||||
|
address = Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (endAddress > texturePoolEndAddress)
|
||||||
|
{
|
||||||
|
endAddress = texturePoolEndAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidateRangeImpl(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void InvalidateRangeImpl(ulong address, ulong size);
|
||||||
|
|
||||||
|
protected abstract void Delete(T item);
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Items != null)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < Items.Length; index++)
|
||||||
|
{
|
||||||
|
Delete(Items[index]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Items = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
9
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
enum ReductionFilter
|
||||||
|
{
|
||||||
|
Average,
|
||||||
|
Minimum,
|
||||||
|
Maximum
|
||||||
|
}
|
||||||
|
}
|
52
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal file
52
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.GAL.Color;
|
||||||
|
using Ryujinx.Graphics.GAL.Sampler;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
class Sampler : IDisposable
|
||||||
|
{
|
||||||
|
public ISampler HostSampler { get; }
|
||||||
|
|
||||||
|
public Sampler(GpuContext context, SamplerDescriptor descriptor)
|
||||||
|
{
|
||||||
|
MinFilter minFilter = descriptor.UnpackMinFilter();
|
||||||
|
MagFilter magFilter = descriptor.UnpackMagFilter();
|
||||||
|
|
||||||
|
AddressMode addressU = descriptor.UnpackAddressU();
|
||||||
|
AddressMode addressV = descriptor.UnpackAddressV();
|
||||||
|
AddressMode addressP = descriptor.UnpackAddressP();
|
||||||
|
|
||||||
|
CompareMode compareMode = descriptor.UnpackCompareMode();
|
||||||
|
CompareOp compareOp = descriptor.UnpackCompareOp();
|
||||||
|
|
||||||
|
ColorF color = new ColorF(0, 0, 0, 0);
|
||||||
|
|
||||||
|
float minLod = descriptor.UnpackMinLod();
|
||||||
|
float maxLod = descriptor.UnpackMaxLod();
|
||||||
|
float mipLodBias = descriptor.UnpackMipLodBias();
|
||||||
|
|
||||||
|
float maxAnisotropy = descriptor.UnpackMaxAnisotropy();
|
||||||
|
|
||||||
|
HostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo(
|
||||||
|
minFilter,
|
||||||
|
magFilter,
|
||||||
|
addressU,
|
||||||
|
addressV,
|
||||||
|
addressP,
|
||||||
|
compareMode,
|
||||||
|
compareOp,
|
||||||
|
color,
|
||||||
|
minLod,
|
||||||
|
maxLod,
|
||||||
|
mipLodBias,
|
||||||
|
maxAnisotropy));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
HostSampler.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
132
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal file
132
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.GAL.Sampler;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
struct SamplerDescriptor
|
||||||
|
{
|
||||||
|
private static readonly float[] _f5ToF32ConversionLut = new float[]
|
||||||
|
{
|
||||||
|
0.0f,
|
||||||
|
0.055555556f,
|
||||||
|
0.1f,
|
||||||
|
0.13636364f,
|
||||||
|
0.16666667f,
|
||||||
|
0.1923077f,
|
||||||
|
0.21428572f,
|
||||||
|
0.23333333f,
|
||||||
|
0.25f,
|
||||||
|
0.2777778f,
|
||||||
|
0.3f,
|
||||||
|
0.3181818f,
|
||||||
|
0.33333334f,
|
||||||
|
0.34615386f,
|
||||||
|
0.35714287f,
|
||||||
|
0.36666667f,
|
||||||
|
0.375f,
|
||||||
|
0.3888889f,
|
||||||
|
0.4f,
|
||||||
|
0.4090909f,
|
||||||
|
0.41666666f,
|
||||||
|
0.42307693f,
|
||||||
|
0.42857143f,
|
||||||
|
0.43333334f,
|
||||||
|
0.4375f,
|
||||||
|
0.44444445f,
|
||||||
|
0.45f,
|
||||||
|
0.45454547f,
|
||||||
|
0.45833334f,
|
||||||
|
0.46153846f,
|
||||||
|
0.4642857f,
|
||||||
|
0.46666667f
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly float[] _maxAnisotropyLut = new float[]
|
||||||
|
{
|
||||||
|
1, 2, 4, 6, 8, 10, 12, 16
|
||||||
|
};
|
||||||
|
|
||||||
|
private const float Frac8ToF32 = 1.0f / 256.0f;
|
||||||
|
|
||||||
|
public uint Word0;
|
||||||
|
public uint Word1;
|
||||||
|
public uint Word2;
|
||||||
|
public uint Word3;
|
||||||
|
public uint BorderColorR;
|
||||||
|
public uint BorderColorG;
|
||||||
|
public uint BorderColorB;
|
||||||
|
public uint BorderColorA;
|
||||||
|
|
||||||
|
public AddressMode UnpackAddressU()
|
||||||
|
{
|
||||||
|
return (AddressMode)(Word0 & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressMode UnpackAddressV()
|
||||||
|
{
|
||||||
|
return (AddressMode)((Word0 >> 3) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AddressMode UnpackAddressP()
|
||||||
|
{
|
||||||
|
return (AddressMode)((Word0 >> 6) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompareMode UnpackCompareMode()
|
||||||
|
{
|
||||||
|
return (CompareMode)((Word0 >> 9) & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompareOp UnpackCompareOp()
|
||||||
|
{
|
||||||
|
return (CompareOp)(((Word0 >> 10) & 7) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float UnpackMaxAnisotropy()
|
||||||
|
{
|
||||||
|
return _maxAnisotropyLut[(Word0 >> 20) & 7];
|
||||||
|
}
|
||||||
|
|
||||||
|
public MagFilter UnpackMagFilter()
|
||||||
|
{
|
||||||
|
return (MagFilter)(Word1 & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public MinFilter UnpackMinFilter()
|
||||||
|
{
|
||||||
|
int minFilter = (int)(Word1 >> 4) & 3;
|
||||||
|
int mipFilter = (int)(Word1 >> 6) & 3;
|
||||||
|
|
||||||
|
return (MinFilter)(minFilter + (mipFilter - 1) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReductionFilter UnpackReductionFilter()
|
||||||
|
{
|
||||||
|
return (ReductionFilter)((Word1 >> 10) & 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float UnpackMipLodBias()
|
||||||
|
{
|
||||||
|
int fixedValue = (int)(Word1 >> 12) & 0x1fff;
|
||||||
|
|
||||||
|
fixedValue = (fixedValue << 19) >> 19;
|
||||||
|
|
||||||
|
return fixedValue * Frac8ToF32;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float UnpackLodSnap()
|
||||||
|
{
|
||||||
|
return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f];
|
||||||
|
}
|
||||||
|
|
||||||
|
public float UnpackMinLod()
|
||||||
|
{
|
||||||
|
return (Word2 & 0xfff) * Frac8ToF32;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float UnpackMaxLod()
|
||||||
|
{
|
||||||
|
return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
61
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal file
61
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
class SamplerPool : Pool<Sampler>
|
||||||
|
{
|
||||||
|
public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
|
||||||
|
|
||||||
|
public override Sampler Get(int id)
|
||||||
|
{
|
||||||
|
if ((uint)id >= Items.Length)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizeMemory();
|
||||||
|
|
||||||
|
Sampler sampler = Items[id];
|
||||||
|
|
||||||
|
if (sampler == null)
|
||||||
|
{
|
||||||
|
ulong address = Address + (ulong)(uint)id * DescriptorSize;
|
||||||
|
|
||||||
|
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
|
||||||
|
|
||||||
|
SamplerDescriptor descriptor = MemoryMarshal.Cast<byte, SamplerDescriptor>(data)[0];
|
||||||
|
|
||||||
|
sampler = new Sampler(Context, descriptor);
|
||||||
|
|
||||||
|
Items[id] = sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
|
for (; address < endAddress; address += DescriptorSize)
|
||||||
|
{
|
||||||
|
int id = (int)((address - Address) / DescriptorSize);
|
||||||
|
|
||||||
|
Sampler sampler = Items[id];
|
||||||
|
|
||||||
|
if (sampler != null)
|
||||||
|
{
|
||||||
|
sampler.Dispose();
|
||||||
|
|
||||||
|
Items[id] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Delete(Sampler item)
|
||||||
|
{
|
||||||
|
item?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
719
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal file
719
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal file
|
@ -0,0 +1,719 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Texture;
|
||||||
|
using Ryujinx.Graphics.Texture.Astc;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
class Texture : IRange<Texture>
|
||||||
|
{
|
||||||
|
private GpuContext _context;
|
||||||
|
|
||||||
|
private TextureInfo _info;
|
||||||
|
|
||||||
|
private SizeInfo _sizeInfo;
|
||||||
|
|
||||||
|
public Format Format => _info.FormatInfo.Format;
|
||||||
|
|
||||||
|
public TextureInfo Info => _info;
|
||||||
|
|
||||||
|
private int _depth;
|
||||||
|
private int _layers;
|
||||||
|
private int _firstLayer;
|
||||||
|
private int _firstLevel;
|
||||||
|
|
||||||
|
private bool _hasData;
|
||||||
|
|
||||||
|
private ITexture _arrayViewTexture;
|
||||||
|
private Target _arrayViewTarget;
|
||||||
|
|
||||||
|
private Texture _viewStorage;
|
||||||
|
|
||||||
|
private List<Texture> _views;
|
||||||
|
|
||||||
|
public ITexture HostTexture { get; private set; }
|
||||||
|
|
||||||
|
public LinkedListNode<Texture> CacheNode { get; set; }
|
||||||
|
|
||||||
|
public bool Modified { get; set; }
|
||||||
|
|
||||||
|
public ulong Address => _info.Address;
|
||||||
|
public ulong EndAddress => _info.Address + Size;
|
||||||
|
|
||||||
|
public ulong Size => (ulong)_sizeInfo.TotalSize;
|
||||||
|
|
||||||
|
private int _referenceCount;
|
||||||
|
|
||||||
|
private int _sequenceNumber;
|
||||||
|
|
||||||
|
private Texture(
|
||||||
|
GpuContext context,
|
||||||
|
TextureInfo info,
|
||||||
|
SizeInfo sizeInfo,
|
||||||
|
int firstLayer,
|
||||||
|
int firstLevel)
|
||||||
|
{
|
||||||
|
InitializeTexture(context, info, sizeInfo);
|
||||||
|
|
||||||
|
_firstLayer = firstLayer;
|
||||||
|
_firstLevel = firstLevel;
|
||||||
|
|
||||||
|
_hasData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
|
||||||
|
{
|
||||||
|
InitializeTexture(context, info, sizeInfo);
|
||||||
|
|
||||||
|
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, context.Capabilities);
|
||||||
|
|
||||||
|
HostTexture = _context.Renderer.CreateTexture(createInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_sizeInfo = sizeInfo;
|
||||||
|
|
||||||
|
SetInfo(info);
|
||||||
|
|
||||||
|
_viewStorage = this;
|
||||||
|
|
||||||
|
_views = new List<Texture>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, int firstLayer, int firstLevel)
|
||||||
|
{
|
||||||
|
Texture texture = new Texture(
|
||||||
|
_context,
|
||||||
|
info,
|
||||||
|
sizeInfo,
|
||||||
|
_firstLayer + firstLayer,
|
||||||
|
_firstLevel + firstLevel);
|
||||||
|
|
||||||
|
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, _context.Capabilities);
|
||||||
|
|
||||||
|
texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel);
|
||||||
|
|
||||||
|
_viewStorage.AddView(texture);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddView(Texture texture)
|
||||||
|
{
|
||||||
|
_views.Add(texture);
|
||||||
|
|
||||||
|
texture._viewStorage = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveView(Texture texture)
|
||||||
|
{
|
||||||
|
_views.Remove(texture);
|
||||||
|
|
||||||
|
texture._viewStorage = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ChangeSize(int width, int height, int depthOrLayers)
|
||||||
|
{
|
||||||
|
width <<= _firstLevel;
|
||||||
|
height <<= _firstLevel;
|
||||||
|
|
||||||
|
if (_info.Target == Target.Texture3D)
|
||||||
|
{
|
||||||
|
depthOrLayers <<= _firstLevel;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
depthOrLayers = _viewStorage._info.DepthOrLayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
_viewStorage.RecreateStorageOrView(width, height, depthOrLayers);
|
||||||
|
|
||||||
|
foreach (Texture view in _viewStorage._views)
|
||||||
|
{
|
||||||
|
int viewWidth = Math.Max(1, width >> view._firstLevel);
|
||||||
|
int viewHeight = Math.Max(1, height >> view._firstLevel);
|
||||||
|
|
||||||
|
int viewDepthOrLayers;
|
||||||
|
|
||||||
|
if (view._info.Target == Target.Texture3D)
|
||||||
|
{
|
||||||
|
viewDepthOrLayers = Math.Max(1, depthOrLayers >> view._firstLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
viewDepthOrLayers = view._info.DepthOrLayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
view.RecreateStorageOrView(viewWidth, viewHeight, viewDepthOrLayers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecreateStorageOrView(int width, int height, int depthOrLayers)
|
||||||
|
{
|
||||||
|
SetInfo(new TextureInfo(
|
||||||
|
_info.Address,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depthOrLayers,
|
||||||
|
_info.Levels,
|
||||||
|
_info.SamplesInX,
|
||||||
|
_info.SamplesInY,
|
||||||
|
_info.Stride,
|
||||||
|
_info.IsLinear,
|
||||||
|
_info.GobBlocksInY,
|
||||||
|
_info.GobBlocksInZ,
|
||||||
|
_info.GobBlocksInTileX,
|
||||||
|
_info.Target,
|
||||||
|
_info.FormatInfo,
|
||||||
|
_info.DepthStencilMode,
|
||||||
|
_info.SwizzleR,
|
||||||
|
_info.SwizzleG,
|
||||||
|
_info.SwizzleB,
|
||||||
|
_info.SwizzleA));
|
||||||
|
|
||||||
|
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(_info, _context.Capabilities);
|
||||||
|
|
||||||
|
if (_viewStorage != this)
|
||||||
|
{
|
||||||
|
ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, _firstLayer, _firstLevel));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ITexture newStorage = _context.Renderer.CreateTexture(createInfo);
|
||||||
|
|
||||||
|
HostTexture.CopyTo(newStorage);
|
||||||
|
|
||||||
|
ReplaceStorage(newStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SynchronizeMemory()
|
||||||
|
{
|
||||||
|
if (_sequenceNumber == _context.SequenceNumber && _hasData)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_sequenceNumber = _context.SequenceNumber;
|
||||||
|
|
||||||
|
bool modified = _context.PhysicalMemory.GetModifiedRanges(Address, Size).Length != 0;
|
||||||
|
|
||||||
|
if (!modified && _hasData)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong pageSize = (uint)_context.PhysicalMemory.GetPageSize();
|
||||||
|
|
||||||
|
ulong pageMask = pageSize - 1;
|
||||||
|
|
||||||
|
ulong rangeAddress = Address & ~pageMask;
|
||||||
|
|
||||||
|
ulong rangeSize = (EndAddress - Address + pageMask) & ~pageMask;
|
||||||
|
|
||||||
|
_context.Methods.InvalidateRange(rangeAddress, rangeSize);
|
||||||
|
|
||||||
|
Span<byte> data = _context.PhysicalMemory.Read(Address, Size);
|
||||||
|
|
||||||
|
if (_info.IsLinear)
|
||||||
|
{
|
||||||
|
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||||
|
_info.Width,
|
||||||
|
_info.Height,
|
||||||
|
_info.FormatInfo.BlockWidth,
|
||||||
|
_info.FormatInfo.BlockHeight,
|
||||||
|
_info.Stride,
|
||||||
|
_info.FormatInfo.BytesPerPixel,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data = LayoutConverter.ConvertBlockLinearToLinear(
|
||||||
|
_info.Width,
|
||||||
|
_info.Height,
|
||||||
|
_depth,
|
||||||
|
_info.Levels,
|
||||||
|
_layers,
|
||||||
|
_info.FormatInfo.BlockWidth,
|
||||||
|
_info.FormatInfo.BlockHeight,
|
||||||
|
_info.FormatInfo.BytesPerPixel,
|
||||||
|
_info.GobBlocksInY,
|
||||||
|
_info.GobBlocksInZ,
|
||||||
|
_info.GobBlocksInTileX,
|
||||||
|
_sizeInfo,
|
||||||
|
data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_context.Capabilities.SupportsAstcCompression && _info.FormatInfo.Format.IsAstc())
|
||||||
|
{
|
||||||
|
int blockWidth = _info.FormatInfo.BlockWidth;
|
||||||
|
int blockHeight = _info.FormatInfo.BlockHeight;
|
||||||
|
|
||||||
|
data = AstcDecoder.DecodeToRgba8(
|
||||||
|
data,
|
||||||
|
blockWidth,
|
||||||
|
blockHeight,
|
||||||
|
1,
|
||||||
|
_info.Width,
|
||||||
|
_info.Height,
|
||||||
|
_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
HostTexture.SetData(data);
|
||||||
|
|
||||||
|
_hasData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
byte[] data = HostTexture.GetData(0);
|
||||||
|
|
||||||
|
_context.PhysicalMemory.Write(Address, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsPerfectMatch(TextureInfo info, TextureSearchFlags flags)
|
||||||
|
{
|
||||||
|
if (!FormatMatches(info, (flags & TextureSearchFlags.Strict) != 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!LayoutMatches(info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SizeMatches(info, (flags & TextureSearchFlags.Strict) == 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & TextureSearchFlags.Sampler) != 0)
|
||||||
|
{
|
||||||
|
if (!SamplerParamsMatches(info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & TextureSearchFlags.IgnoreMs) != 0)
|
||||||
|
{
|
||||||
|
bool msTargetCompatible = _info.Target == Target.Texture2DMultisample &&
|
||||||
|
info.Target == Target.Texture2D;
|
||||||
|
|
||||||
|
if (!msTargetCompatible && !TargetAndSamplesCompatible(info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!TargetAndSamplesCompatible(info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _info.Address == info.Address && _info.Levels == info.Levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FormatMatches(TextureInfo info, bool strict)
|
||||||
|
{
|
||||||
|
// D32F and R32F texture have the same representation internally,
|
||||||
|
// however the R32F format is used to sample from depth textures.
|
||||||
|
if (_info.FormatInfo.Format == Format.D32Float &&
|
||||||
|
info.FormatInfo.Format == Format.R32Float && !strict)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_info.FormatInfo.Format == Format.R8G8B8A8Srgb &&
|
||||||
|
info.FormatInfo.Format == Format.R8G8B8A8Unorm && !strict)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_info.FormatInfo.Format == Format.R8G8B8A8Unorm &&
|
||||||
|
info.FormatInfo.Format == Format.R8G8B8A8Srgb && !strict)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _info.FormatInfo.Format == info.FormatInfo.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool LayoutMatches(TextureInfo info)
|
||||||
|
{
|
||||||
|
if (_info.IsLinear != info.IsLinear)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For linear textures, gob block sizes are ignored.
|
||||||
|
// For block linear textures, the stride is ignored.
|
||||||
|
if (info.IsLinear)
|
||||||
|
{
|
||||||
|
return _info.Stride == info.Stride;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _info.GobBlocksInY == info.GobBlocksInY &&
|
||||||
|
_info.GobBlocksInZ == info.GobBlocksInZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SizeMatches(TextureInfo info)
|
||||||
|
{
|
||||||
|
return SizeMatches(info, alignSizes: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SizeMatches(TextureInfo info, int level)
|
||||||
|
{
|
||||||
|
return Math.Max(1, _info.Width >> level) == info.Width &&
|
||||||
|
Math.Max(1, _info.Height >> level) == info.Height &&
|
||||||
|
Math.Max(1, _info.GetDepth() >> level) == info.GetDepth();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SizeMatches(TextureInfo info, bool alignSizes)
|
||||||
|
{
|
||||||
|
if (_info.GetLayers() != info.GetLayers())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alignSizes)
|
||||||
|
{
|
||||||
|
Size size0 = GetAlignedSize(_info);
|
||||||
|
Size size1 = GetAlignedSize(info);
|
||||||
|
|
||||||
|
return size0.Width == size1.Width &&
|
||||||
|
size0.Height == size1.Height &&
|
||||||
|
size0.Depth == size1.Depth;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return _info.Width == info.Width &&
|
||||||
|
_info.Height == info.Height &&
|
||||||
|
_info.GetDepth() == info.GetDepth();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SamplerParamsMatches(TextureInfo info)
|
||||||
|
{
|
||||||
|
return _info.DepthStencilMode == info.DepthStencilMode &&
|
||||||
|
_info.SwizzleR == info.SwizzleR &&
|
||||||
|
_info.SwizzleG == info.SwizzleG &&
|
||||||
|
_info.SwizzleB == info.SwizzleB &&
|
||||||
|
_info.SwizzleA == info.SwizzleA;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TargetAndSamplesCompatible(TextureInfo info)
|
||||||
|
{
|
||||||
|
return _info.Target == info.Target &&
|
||||||
|
_info.SamplesInX == info.SamplesInX &&
|
||||||
|
_info.SamplesInY == info.SamplesInY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsViewCompatible(TextureInfo info, ulong size, out int firstLayer, out int firstLevel)
|
||||||
|
{
|
||||||
|
// Out of range.
|
||||||
|
if (info.Address < Address || info.Address + size > EndAddress)
|
||||||
|
{
|
||||||
|
firstLayer = 0;
|
||||||
|
firstLevel = 0;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int offset = (int)(info.Address - Address);
|
||||||
|
|
||||||
|
if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ViewLayoutCompatible(info, firstLevel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ViewFormatCompatible(info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ViewSizeMatches(info, firstLevel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ViewTargetCompatible(info))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _info.SamplesInX == info.SamplesInX &&
|
||||||
|
_info.SamplesInY == info.SamplesInY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ViewLayoutCompatible(TextureInfo info, int level)
|
||||||
|
{
|
||||||
|
if (_info.IsLinear != info.IsLinear)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For linear textures, gob block sizes are ignored.
|
||||||
|
// For block linear textures, the stride is ignored.
|
||||||
|
if (info.IsLinear)
|
||||||
|
{
|
||||||
|
int width = Math.Max(1, _info.Width >> level);
|
||||||
|
|
||||||
|
int stride = width * _info.FormatInfo.BytesPerPixel;
|
||||||
|
|
||||||
|
stride = BitUtils.AlignUp(stride, 32);
|
||||||
|
|
||||||
|
return stride == info.Stride;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int height = Math.Max(1, _info.Height >> level);
|
||||||
|
int depth = Math.Max(1, _info.GetDepth() >> level);
|
||||||
|
|
||||||
|
(int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
_info.FormatInfo.BlockHeight,
|
||||||
|
_info.GobBlocksInY,
|
||||||
|
_info.GobBlocksInZ);
|
||||||
|
|
||||||
|
return gobBlocksInY == info.GobBlocksInY &&
|
||||||
|
gobBlocksInZ == info.GobBlocksInZ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ViewFormatCompatible(TextureInfo info)
|
||||||
|
{
|
||||||
|
return TextureCompatibility.FormatCompatible(_info.FormatInfo, info.FormatInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ViewSizeMatches(TextureInfo info, int level)
|
||||||
|
{
|
||||||
|
Size size = GetAlignedSize(_info, level);
|
||||||
|
|
||||||
|
Size otherSize = GetAlignedSize(info);
|
||||||
|
|
||||||
|
return size.Width == otherSize.Width &&
|
||||||
|
size.Height == otherSize.Height &&
|
||||||
|
size.Depth == otherSize.Depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ViewTargetCompatible(TextureInfo info)
|
||||||
|
{
|
||||||
|
switch (_info.Target)
|
||||||
|
{
|
||||||
|
case Target.Texture1D:
|
||||||
|
case Target.Texture1DArray:
|
||||||
|
return info.Target == Target.Texture1D ||
|
||||||
|
info.Target == Target.Texture1DArray;
|
||||||
|
|
||||||
|
case Target.Texture2D:
|
||||||
|
return info.Target == Target.Texture2D ||
|
||||||
|
info.Target == Target.Texture2DArray;
|
||||||
|
|
||||||
|
case Target.Texture2DArray:
|
||||||
|
case Target.Cubemap:
|
||||||
|
case Target.CubemapArray:
|
||||||
|
return info.Target == Target.Texture2D ||
|
||||||
|
info.Target == Target.Texture2DArray ||
|
||||||
|
info.Target == Target.Cubemap ||
|
||||||
|
info.Target == Target.CubemapArray;
|
||||||
|
|
||||||
|
case Target.Texture2DMultisample:
|
||||||
|
case Target.Texture2DMultisampleArray:
|
||||||
|
return info.Target == Target.Texture2DMultisample ||
|
||||||
|
info.Target == Target.Texture2DMultisampleArray;
|
||||||
|
|
||||||
|
case Target.Texture3D:
|
||||||
|
return info.Target == Target.Texture3D;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Size GetAlignedSize(TextureInfo info, int level = 0)
|
||||||
|
{
|
||||||
|
int width = Math.Max(1, info.Width >> level);
|
||||||
|
int height = Math.Max(1, info.Height >> level);
|
||||||
|
|
||||||
|
if (info.IsLinear)
|
||||||
|
{
|
||||||
|
return SizeCalculator.GetLinearAlignedSize(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
info.FormatInfo.BlockWidth,
|
||||||
|
info.FormatInfo.BlockHeight,
|
||||||
|
info.FormatInfo.BytesPerPixel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int depth = Math.Max(1, info.GetDepth() >> level);
|
||||||
|
|
||||||
|
(int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
info.FormatInfo.BlockHeight,
|
||||||
|
info.GobBlocksInY,
|
||||||
|
info.GobBlocksInZ);
|
||||||
|
|
||||||
|
return SizeCalculator.GetBlockLinearAlignedSize(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
info.FormatInfo.BlockWidth,
|
||||||
|
info.FormatInfo.BlockHeight,
|
||||||
|
info.FormatInfo.BytesPerPixel,
|
||||||
|
gobBlocksInY,
|
||||||
|
gobBlocksInZ,
|
||||||
|
info.GobBlocksInTileX);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITexture GetTargetTexture(Target target)
|
||||||
|
{
|
||||||
|
if (target == _info.Target)
|
||||||
|
{
|
||||||
|
return HostTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
|
||||||
|
{
|
||||||
|
TextureCreateInfo createInfo = new TextureCreateInfo(
|
||||||
|
_info.Width,
|
||||||
|
_info.Height,
|
||||||
|
target == Target.CubemapArray ? 6 : 1,
|
||||||
|
_info.Levels,
|
||||||
|
_info.Samples,
|
||||||
|
_info.FormatInfo.BlockWidth,
|
||||||
|
_info.FormatInfo.BlockHeight,
|
||||||
|
_info.FormatInfo.BytesPerPixel,
|
||||||
|
_info.FormatInfo.Format,
|
||||||
|
_info.DepthStencilMode,
|
||||||
|
target,
|
||||||
|
_info.SwizzleR,
|
||||||
|
_info.SwizzleG,
|
||||||
|
_info.SwizzleB,
|
||||||
|
_info.SwizzleA);
|
||||||
|
|
||||||
|
ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0);
|
||||||
|
|
||||||
|
_arrayViewTexture = viewTexture;
|
||||||
|
_arrayViewTarget = target;
|
||||||
|
|
||||||
|
return viewTexture;
|
||||||
|
}
|
||||||
|
else if (_arrayViewTarget == target)
|
||||||
|
{
|
||||||
|
return _arrayViewTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsSameDimensionsTarget(Target target)
|
||||||
|
{
|
||||||
|
switch (_info.Target)
|
||||||
|
{
|
||||||
|
case Target.Texture1D:
|
||||||
|
case Target.Texture1DArray:
|
||||||
|
return target == Target.Texture1D ||
|
||||||
|
target == Target.Texture1DArray;
|
||||||
|
|
||||||
|
case Target.Texture2D:
|
||||||
|
case Target.Texture2DArray:
|
||||||
|
return target == Target.Texture2D ||
|
||||||
|
target == Target.Texture2DArray;
|
||||||
|
|
||||||
|
case Target.Cubemap:
|
||||||
|
case Target.CubemapArray:
|
||||||
|
return target == Target.Cubemap ||
|
||||||
|
target == Target.CubemapArray;
|
||||||
|
|
||||||
|
case Target.Texture2DMultisample:
|
||||||
|
case Target.Texture2DMultisampleArray:
|
||||||
|
return target == Target.Texture2DMultisample ||
|
||||||
|
target == Target.Texture2DMultisampleArray;
|
||||||
|
|
||||||
|
case Target.Texture3D:
|
||||||
|
return target == Target.Texture3D;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture)
|
||||||
|
{
|
||||||
|
ReplaceStorage(hostTexture);
|
||||||
|
|
||||||
|
parent._viewStorage.AddView(this);
|
||||||
|
|
||||||
|
SetInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetInfo(TextureInfo info)
|
||||||
|
{
|
||||||
|
_info = info;
|
||||||
|
|
||||||
|
_depth = info.GetDepth();
|
||||||
|
_layers = info.GetLayers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReplaceStorage(ITexture hostTexture)
|
||||||
|
{
|
||||||
|
DisposeTextures();
|
||||||
|
|
||||||
|
HostTexture = hostTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OverlapsWith(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
return Address < address + size && address < EndAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Invalidate()
|
||||||
|
{
|
||||||
|
// _hasData = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void IncrementReferenceCount()
|
||||||
|
{
|
||||||
|
_referenceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecrementReferenceCount()
|
||||||
|
{
|
||||||
|
if (--_referenceCount == 0)
|
||||||
|
{
|
||||||
|
if (_viewStorage != this)
|
||||||
|
{
|
||||||
|
_viewStorage.RemoveView(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_context.Methods.TextureManager.RemoveTextureFromCache(this);
|
||||||
|
|
||||||
|
DisposeTextures();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DisposeTextures()
|
||||||
|
{
|
||||||
|
HostTexture.Dispose();
|
||||||
|
|
||||||
|
_arrayViewTexture?.Dispose();
|
||||||
|
_arrayViewTexture = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal file
17
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
struct TextureBindingInfo
|
||||||
|
{
|
||||||
|
public Target Target { get; }
|
||||||
|
|
||||||
|
public int Handle { get; }
|
||||||
|
|
||||||
|
public TextureBindingInfo(Target target, int handle)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
|
Handle = handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
95
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal file
95
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
static class TextureCompatibility
|
||||||
|
{
|
||||||
|
private enum FormatClass
|
||||||
|
{
|
||||||
|
Unclassified,
|
||||||
|
BCn64,
|
||||||
|
BCn128,
|
||||||
|
Bc1Rgb,
|
||||||
|
Bc1Rgba,
|
||||||
|
Bc2,
|
||||||
|
Bc3,
|
||||||
|
Bc4,
|
||||||
|
Bc5,
|
||||||
|
Bc6,
|
||||||
|
Bc7
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool FormatCompatible(FormatInfo lhs, FormatInfo rhs)
|
||||||
|
{
|
||||||
|
if (IsDsFormat(lhs.Format) || IsDsFormat(rhs.Format))
|
||||||
|
{
|
||||||
|
return lhs.Format == rhs.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs.Format.IsAstc() || rhs.Format.IsAstc())
|
||||||
|
{
|
||||||
|
return lhs.Format == rhs.Format;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs.IsCompressed && rhs.IsCompressed)
|
||||||
|
{
|
||||||
|
FormatClass lhsClass = GetFormatClass(lhs.Format);
|
||||||
|
FormatClass rhsClass = GetFormatClass(rhs.Format);
|
||||||
|
|
||||||
|
return lhsClass == rhsClass;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return lhs.BytesPerPixel == rhs.BytesPerPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FormatClass GetFormatClass(Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Format.Bc1RgbSrgb:
|
||||||
|
case Format.Bc1RgbUnorm:
|
||||||
|
return FormatClass.Bc1Rgb;
|
||||||
|
case Format.Bc1RgbaSrgb:
|
||||||
|
case Format.Bc1RgbaUnorm:
|
||||||
|
return FormatClass.Bc1Rgba;
|
||||||
|
case Format.Bc2Srgb:
|
||||||
|
case Format.Bc2Unorm:
|
||||||
|
return FormatClass.Bc2;
|
||||||
|
case Format.Bc3Srgb:
|
||||||
|
case Format.Bc3Unorm:
|
||||||
|
return FormatClass.Bc3;
|
||||||
|
case Format.Bc4Snorm:
|
||||||
|
case Format.Bc4Unorm:
|
||||||
|
return FormatClass.Bc4;
|
||||||
|
case Format.Bc5Snorm:
|
||||||
|
case Format.Bc5Unorm:
|
||||||
|
return FormatClass.Bc5;
|
||||||
|
case Format.Bc6HSfloat:
|
||||||
|
case Format.Bc6HUfloat:
|
||||||
|
return FormatClass.Bc6;
|
||||||
|
case Format.Bc7Srgb:
|
||||||
|
case Format.Bc7Unorm:
|
||||||
|
return FormatClass.Bc7;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FormatClass.Unclassified;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsDsFormat(Format format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case Format.D16Unorm:
|
||||||
|
case Format.D24X8Unorm:
|
||||||
|
case Format.D24UnormS8Uint:
|
||||||
|
case Format.D32Float:
|
||||||
|
case Format.D32FloatS8Uint:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal file
35
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
enum TextureComponent
|
||||||
|
{
|
||||||
|
Zero = 0,
|
||||||
|
Red = 2,
|
||||||
|
Green = 3,
|
||||||
|
Blue = 4,
|
||||||
|
Alpha = 5,
|
||||||
|
OneSI = 6,
|
||||||
|
OneF = 7
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TextureComponentConverter
|
||||||
|
{
|
||||||
|
public static SwizzleComponent Convert(this TextureComponent component)
|
||||||
|
{
|
||||||
|
switch (component)
|
||||||
|
{
|
||||||
|
case TextureComponent.Zero: return SwizzleComponent.Zero;
|
||||||
|
case TextureComponent.Red: return SwizzleComponent.Red;
|
||||||
|
case TextureComponent.Green: return SwizzleComponent.Green;
|
||||||
|
case TextureComponent.Blue: return SwizzleComponent.Blue;
|
||||||
|
case TextureComponent.Alpha: return SwizzleComponent.Alpha;
|
||||||
|
case TextureComponent.OneSI:
|
||||||
|
case TextureComponent.OneF:
|
||||||
|
return SwizzleComponent.One;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SwizzleComponent.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
119
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal file
119
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
struct TextureDescriptor
|
||||||
|
{
|
||||||
|
public uint Word0;
|
||||||
|
public uint Word1;
|
||||||
|
public uint Word2;
|
||||||
|
public uint Word3;
|
||||||
|
public uint Word4;
|
||||||
|
public uint Word5;
|
||||||
|
public uint Word6;
|
||||||
|
public uint Word7;
|
||||||
|
|
||||||
|
public uint UnpackFormat()
|
||||||
|
{
|
||||||
|
return Word0 & 0x8007ffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureComponent UnpackSwizzleR()
|
||||||
|
{
|
||||||
|
return(TextureComponent)((Word0 >> 19) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureComponent UnpackSwizzleG()
|
||||||
|
{
|
||||||
|
return(TextureComponent)((Word0 >> 22) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureComponent UnpackSwizzleB()
|
||||||
|
{
|
||||||
|
return(TextureComponent)((Word0 >> 25) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureComponent UnpackSwizzleA()
|
||||||
|
{
|
||||||
|
return(TextureComponent)((Word0 >> 28) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ulong UnpackAddress()
|
||||||
|
{
|
||||||
|
return Word1 | ((ulong)(Word2 & 0xffff) << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureDescriptorType UnpackTextureDescriptorType()
|
||||||
|
{
|
||||||
|
return (TextureDescriptorType)((Word2 >> 21) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackStride()
|
||||||
|
{
|
||||||
|
return (int)(Word3 & 0xffff) << 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackGobBlocksInX()
|
||||||
|
{
|
||||||
|
return 1 << (int)(Word3 & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackGobBlocksInY()
|
||||||
|
{
|
||||||
|
return 1 << (int)((Word3 >> 3) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackGobBlocksInZ()
|
||||||
|
{
|
||||||
|
return 1 << (int)((Word3 >> 6) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackGobBlocksInTileX()
|
||||||
|
{
|
||||||
|
return 1 << (int)((Word3 >> 10) & 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackLevels()
|
||||||
|
{
|
||||||
|
return (int)(Word3 >> 28) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackWidth()
|
||||||
|
{
|
||||||
|
return (int)(Word4 & 0xffff) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool UnpackSrgb()
|
||||||
|
{
|
||||||
|
return (Word4 & (1 << 22)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureTarget UnpackTextureTarget()
|
||||||
|
{
|
||||||
|
return (TextureTarget)((Word4 >> 23) & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackHeight()
|
||||||
|
{
|
||||||
|
return (int)(Word5 & 0xffff) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackDepth()
|
||||||
|
{
|
||||||
|
return (int)((Word5 >> 16) & 0x3fff) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackBaseLevel()
|
||||||
|
{
|
||||||
|
return (int)(Word7 & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int UnpackMaxLevelInclusive()
|
||||||
|
{
|
||||||
|
return (int)((Word7 >> 4) & 0xf);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureMsaaMode UnpackTextureMsaaMode()
|
||||||
|
{
|
||||||
|
return (TextureMsaaMode)((Word7 >> 8) & 0xf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
11
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
enum TextureDescriptorType
|
||||||
|
{
|
||||||
|
Buffer,
|
||||||
|
LinearColorKey,
|
||||||
|
Linear,
|
||||||
|
BlockLinear,
|
||||||
|
BlockLinearColorKey
|
||||||
|
}
|
||||||
|
}
|
101
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal file
101
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
struct TextureInfo
|
||||||
|
{
|
||||||
|
public ulong Address { get; }
|
||||||
|
|
||||||
|
public int Width { get; }
|
||||||
|
public int Height { get; }
|
||||||
|
public int DepthOrLayers { get; }
|
||||||
|
public int Levels { get; }
|
||||||
|
public int SamplesInX { get; }
|
||||||
|
public int SamplesInY { get; }
|
||||||
|
public int Stride { get; }
|
||||||
|
public bool IsLinear { get; }
|
||||||
|
public int GobBlocksInY { get; }
|
||||||
|
public int GobBlocksInZ { get; }
|
||||||
|
public int GobBlocksInTileX { get; }
|
||||||
|
|
||||||
|
public int Samples => SamplesInX * SamplesInY;
|
||||||
|
|
||||||
|
public Target Target { get; }
|
||||||
|
|
||||||
|
public FormatInfo FormatInfo { get; }
|
||||||
|
|
||||||
|
public DepthStencilMode DepthStencilMode { get; }
|
||||||
|
|
||||||
|
public SwizzleComponent SwizzleR { get; }
|
||||||
|
public SwizzleComponent SwizzleG { get; }
|
||||||
|
public SwizzleComponent SwizzleB { get; }
|
||||||
|
public SwizzleComponent SwizzleA { get; }
|
||||||
|
|
||||||
|
public TextureInfo(
|
||||||
|
ulong address,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int depthOrLayers,
|
||||||
|
int levels,
|
||||||
|
int samplesInX,
|
||||||
|
int samplesInY,
|
||||||
|
int stride,
|
||||||
|
bool isLinear,
|
||||||
|
int gobBlocksInY,
|
||||||
|
int gobBlocksInZ,
|
||||||
|
int gobBlocksInTileX,
|
||||||
|
Target target,
|
||||||
|
FormatInfo formatInfo,
|
||||||
|
DepthStencilMode depthStencilMode = DepthStencilMode.Depth,
|
||||||
|
SwizzleComponent swizzleR = SwizzleComponent.Red,
|
||||||
|
SwizzleComponent swizzleG = SwizzleComponent.Green,
|
||||||
|
SwizzleComponent swizzleB = SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent swizzleA = SwizzleComponent.Alpha)
|
||||||
|
{
|
||||||
|
Address = address;
|
||||||
|
Width = width;
|
||||||
|
Height = height;
|
||||||
|
DepthOrLayers = depthOrLayers;
|
||||||
|
Levels = levels;
|
||||||
|
SamplesInX = samplesInX;
|
||||||
|
SamplesInY = samplesInY;
|
||||||
|
Stride = stride;
|
||||||
|
IsLinear = isLinear;
|
||||||
|
GobBlocksInY = gobBlocksInY;
|
||||||
|
GobBlocksInZ = gobBlocksInZ;
|
||||||
|
GobBlocksInTileX = gobBlocksInTileX;
|
||||||
|
Target = target;
|
||||||
|
FormatInfo = formatInfo;
|
||||||
|
DepthStencilMode = depthStencilMode;
|
||||||
|
SwizzleR = swizzleR;
|
||||||
|
SwizzleG = swizzleG;
|
||||||
|
SwizzleB = swizzleB;
|
||||||
|
SwizzleA = swizzleA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetDepth()
|
||||||
|
{
|
||||||
|
return Target == Target.Texture3D ? DepthOrLayers : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetLayers()
|
||||||
|
{
|
||||||
|
if (Target == Target.Texture2DArray || Target == Target.Texture2DMultisampleArray)
|
||||||
|
{
|
||||||
|
return DepthOrLayers;
|
||||||
|
}
|
||||||
|
else if (Target == Target.CubemapArray)
|
||||||
|
{
|
||||||
|
return DepthOrLayers * 6;
|
||||||
|
}
|
||||||
|
else if (Target == Target.Cubemap)
|
||||||
|
{
|
||||||
|
return 6;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
669
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal file
669
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal file
|
@ -0,0 +1,669 @@
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
using Ryujinx.Graphics.Gpu.Image;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using Ryujinx.Graphics.Gpu.State;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Texture;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
class TextureManager
|
||||||
|
{
|
||||||
|
private GpuContext _context;
|
||||||
|
private BufferManager _bufferManager;
|
||||||
|
|
||||||
|
private SamplerPool _samplerPool;
|
||||||
|
|
||||||
|
private ulong _texturePoolAddress;
|
||||||
|
private int _texturePoolMaximumId;
|
||||||
|
|
||||||
|
private TexturePoolCache _texturePoolCache;
|
||||||
|
|
||||||
|
private Texture[] _rtColors;
|
||||||
|
private Texture _rtColor3D;
|
||||||
|
|
||||||
|
private Texture _rtDepthStencil;
|
||||||
|
|
||||||
|
private ITexture[] _rtHostColors;
|
||||||
|
|
||||||
|
private ITexture _rtHostDs;
|
||||||
|
|
||||||
|
private RangeList<Texture> _textures;
|
||||||
|
|
||||||
|
private AutoDeleteCache _cache;
|
||||||
|
|
||||||
|
private TextureBindingInfo[][] _bindings;
|
||||||
|
|
||||||
|
private struct TextureStatePerStage
|
||||||
|
{
|
||||||
|
public ITexture Texture;
|
||||||
|
public ISampler Sampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextureStatePerStage[][] _textureState;
|
||||||
|
|
||||||
|
private int _textureBufferIndex;
|
||||||
|
|
||||||
|
public TextureManager(GpuContext context, BufferManager bufferManager)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_bufferManager = bufferManager;
|
||||||
|
|
||||||
|
_texturePoolCache = new TexturePoolCache(context, this);
|
||||||
|
|
||||||
|
_rtColors = new Texture[Constants.TotalRenderTargets];
|
||||||
|
|
||||||
|
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
||||||
|
|
||||||
|
_textures = new RangeList<Texture>();
|
||||||
|
|
||||||
|
_cache = new AutoDeleteCache();
|
||||||
|
|
||||||
|
_bindings = new TextureBindingInfo[Constants.TotalShaderStages][];
|
||||||
|
|
||||||
|
_textureState = new TextureStatePerStage[Constants.TotalShaderStages][];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BindTextures(int stage, TextureBindingInfo[] bindings)
|
||||||
|
{
|
||||||
|
_bindings[stage] = bindings;
|
||||||
|
|
||||||
|
_textureState[stage] = new TextureStatePerStage[bindings.Length];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTextureBufferIndex(int index)
|
||||||
|
{
|
||||||
|
_textureBufferIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetSamplerPool(ulong gpuVa, int maximumId)
|
||||||
|
{
|
||||||
|
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||||
|
|
||||||
|
if (_samplerPool != null)
|
||||||
|
{
|
||||||
|
if (_samplerPool.Address == address)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_samplerPool.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_samplerPool = new SamplerPool(_context, address, maximumId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTexturePool(ulong gpuVa, int maximumId)
|
||||||
|
{
|
||||||
|
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||||
|
|
||||||
|
_texturePoolAddress = address;
|
||||||
|
_texturePoolMaximumId = maximumId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRenderTargetColor(int index, Texture color)
|
||||||
|
{
|
||||||
|
_rtColors[index] = color;
|
||||||
|
|
||||||
|
_rtColor3D = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRenderTargetColor3D(Texture color)
|
||||||
|
{
|
||||||
|
_rtColor3D = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetRenderTargetDepthStencil(Texture depthStencil)
|
||||||
|
{
|
||||||
|
_rtDepthStencil = depthStencil;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CommitBindings()
|
||||||
|
{
|
||||||
|
UpdateTextures();
|
||||||
|
UpdateRenderTargets();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateTextures()
|
||||||
|
{
|
||||||
|
TexturePool texturePool = _texturePoolCache.FindOrCreate(
|
||||||
|
_texturePoolAddress,
|
||||||
|
_texturePoolMaximumId);
|
||||||
|
|
||||||
|
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
|
||||||
|
{
|
||||||
|
int stageIndex = (int)stage - 1;
|
||||||
|
|
||||||
|
if (_bindings[stageIndex] == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < _bindings[stageIndex].Length; index++)
|
||||||
|
{
|
||||||
|
TextureBindingInfo binding = _bindings[stageIndex][index];
|
||||||
|
|
||||||
|
int packedId = ReadPackedId(stageIndex, binding.Handle);
|
||||||
|
|
||||||
|
int textureId = (packedId >> 0) & 0xfffff;
|
||||||
|
int samplerId = (packedId >> 20) & 0xfff;
|
||||||
|
|
||||||
|
Texture texture = texturePool.Get(textureId);
|
||||||
|
|
||||||
|
ITexture hostTexture = texture?.GetTargetTexture(binding.Target);
|
||||||
|
|
||||||
|
if (_textureState[stageIndex][index].Texture != hostTexture)
|
||||||
|
{
|
||||||
|
_textureState[stageIndex][index].Texture = hostTexture;
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.BindTexture(index, stage, hostTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sampler sampler = _samplerPool.Get(samplerId);
|
||||||
|
|
||||||
|
ISampler hostSampler = sampler?.HostSampler;
|
||||||
|
|
||||||
|
if (_textureState[stageIndex][index].Sampler != hostSampler)
|
||||||
|
{
|
||||||
|
_textureState[stageIndex][index].Sampler = hostSampler;
|
||||||
|
|
||||||
|
_context.Renderer.GraphicsPipeline.BindSampler(index, stage, hostSampler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateRenderTargets()
|
||||||
|
{
|
||||||
|
bool anyChanged = false;
|
||||||
|
|
||||||
|
if (_rtHostDs != _rtDepthStencil?.HostTexture)
|
||||||
|
{
|
||||||
|
_rtHostDs = _rtDepthStencil?.HostTexture;
|
||||||
|
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_rtColor3D == null)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < _rtColors.Length; index++)
|
||||||
|
{
|
||||||
|
ITexture hostTexture = _rtColors[index]?.HostTexture;
|
||||||
|
|
||||||
|
if (_rtHostColors[index] != hostTexture)
|
||||||
|
{
|
||||||
|
_rtHostColors[index] = hostTexture;
|
||||||
|
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anyChanged)
|
||||||
|
{
|
||||||
|
_context.Renderer.GraphicsPipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_rtHostColors[0] != _rtColor3D.HostTexture)
|
||||||
|
{
|
||||||
|
_rtHostColors[0] = _rtColor3D.HostTexture;
|
||||||
|
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (anyChanged)
|
||||||
|
{
|
||||||
|
_context.Renderer.GraphicsPipeline.SetRenderTargets(_rtColor3D.HostTexture, _rtHostDs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int ReadPackedId(int stage, int wordOffset)
|
||||||
|
{
|
||||||
|
ulong address = _bufferManager.GetGraphicsUniformBufferAddress(stage, _textureBufferIndex);
|
||||||
|
|
||||||
|
address += (uint)wordOffset * 4;
|
||||||
|
|
||||||
|
return BitConverter.ToInt32(_context.PhysicalMemory.Read(address, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture FindOrCreateTexture(CopyTexture copyTexture)
|
||||||
|
{
|
||||||
|
ulong address = _context.MemoryManager.Translate(copyTexture.Address.Pack());
|
||||||
|
|
||||||
|
if (address == MemoryManager.BadAddress)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
||||||
|
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
|
|
||||||
|
FormatInfo formatInfo = copyTexture.Format.Convert();
|
||||||
|
|
||||||
|
TextureInfo info = new TextureInfo(
|
||||||
|
address,
|
||||||
|
copyTexture.Width,
|
||||||
|
copyTexture.Height,
|
||||||
|
copyTexture.Depth,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
copyTexture.Stride,
|
||||||
|
copyTexture.LinearLayout,
|
||||||
|
gobBlocksInY,
|
||||||
|
gobBlocksInZ,
|
||||||
|
1,
|
||||||
|
Target.Texture2D,
|
||||||
|
formatInfo);
|
||||||
|
|
||||||
|
Texture texture = FindOrCreateTexture(info, TextureSearchFlags.IgnoreMs);
|
||||||
|
|
||||||
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY)
|
||||||
|
{
|
||||||
|
ulong address = _context.MemoryManager.Translate(colorState.Address.Pack());
|
||||||
|
|
||||||
|
if (address == MemoryManager.BadAddress)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
|
||||||
|
|
||||||
|
int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY();
|
||||||
|
int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
|
|
||||||
|
Target target;
|
||||||
|
|
||||||
|
if (colorState.MemoryLayout.UnpackIsTarget3D())
|
||||||
|
{
|
||||||
|
target = Target.Texture3D;
|
||||||
|
}
|
||||||
|
else if ((samplesInX | samplesInY) != 1)
|
||||||
|
{
|
||||||
|
target = colorState.Depth > 1
|
||||||
|
? Target.Texture2DMultisampleArray
|
||||||
|
: Target.Texture2DMultisample;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
target = colorState.Depth > 1
|
||||||
|
? Target.Texture2DArray
|
||||||
|
: Target.Texture2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
FormatInfo formatInfo = colorState.Format.Convert();
|
||||||
|
|
||||||
|
int width, stride;
|
||||||
|
|
||||||
|
// For linear textures, the width value is actually the stride.
|
||||||
|
// We can easily get the width by dividing the stride by the bpp,
|
||||||
|
// since the stride is the total number of bytes occupied by a
|
||||||
|
// line. The stride should also meet alignment constraints however,
|
||||||
|
// so the width we get here is the aligned width.
|
||||||
|
if (isLinear)
|
||||||
|
{
|
||||||
|
width = colorState.WidthOrStride / formatInfo.BytesPerPixel;
|
||||||
|
stride = colorState.WidthOrStride;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
width = colorState.WidthOrStride;
|
||||||
|
stride = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureInfo info = new TextureInfo(
|
||||||
|
address,
|
||||||
|
width,
|
||||||
|
colorState.Height,
|
||||||
|
colorState.Depth,
|
||||||
|
1,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY,
|
||||||
|
stride,
|
||||||
|
isLinear,
|
||||||
|
gobBlocksInY,
|
||||||
|
gobBlocksInZ,
|
||||||
|
1,
|
||||||
|
target,
|
||||||
|
formatInfo);
|
||||||
|
|
||||||
|
Texture texture = FindOrCreateTexture(info);
|
||||||
|
|
||||||
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture FindOrCreateTexture(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY)
|
||||||
|
{
|
||||||
|
ulong address = _context.MemoryManager.Translate(dsState.Address.Pack());
|
||||||
|
|
||||||
|
if (address == MemoryManager.BadAddress)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
||||||
|
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
||||||
|
|
||||||
|
Target target = (samplesInX | samplesInY) != 1
|
||||||
|
? Target.Texture2DMultisample
|
||||||
|
: Target.Texture2D;
|
||||||
|
|
||||||
|
FormatInfo formatInfo = dsState.Format.Convert();
|
||||||
|
|
||||||
|
TextureInfo info = new TextureInfo(
|
||||||
|
address,
|
||||||
|
size.Width,
|
||||||
|
size.Height,
|
||||||
|
size.Depth,
|
||||||
|
1,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY,
|
||||||
|
0,
|
||||||
|
false,
|
||||||
|
gobBlocksInY,
|
||||||
|
gobBlocksInZ,
|
||||||
|
1,
|
||||||
|
target,
|
||||||
|
formatInfo);
|
||||||
|
|
||||||
|
Texture texture = FindOrCreateTexture(info);
|
||||||
|
|
||||||
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None)
|
||||||
|
{
|
||||||
|
bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0;
|
||||||
|
|
||||||
|
// Try to find a perfect texture match, with the same address and parameters.
|
||||||
|
Texture[] sameAddressOverlaps = _textures.FindOverlaps(info.Address);
|
||||||
|
|
||||||
|
foreach (Texture overlap in sameAddressOverlaps)
|
||||||
|
{
|
||||||
|
if (overlap.IsPerfectMatch(info, flags))
|
||||||
|
{
|
||||||
|
if (!isSamplerTexture)
|
||||||
|
{
|
||||||
|
// If not a sampler texture, it is managed by the auto delete
|
||||||
|
// cache, ensure that it is on the "top" of the list to avoid
|
||||||
|
// deletion.
|
||||||
|
_cache.Lift(overlap);
|
||||||
|
}
|
||||||
|
else if (!overlap.SizeMatches(info))
|
||||||
|
{
|
||||||
|
// If this is used for sampling, the size must match,
|
||||||
|
// otherwise the shader would sample garbage data.
|
||||||
|
// To fix that, we create a new texture with the correct
|
||||||
|
// size, and copy the data from the old one to the new one.
|
||||||
|
overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate texture sizes, used to find all overlapping textures.
|
||||||
|
SizeInfo sizeInfo;
|
||||||
|
|
||||||
|
if (info.IsLinear)
|
||||||
|
{
|
||||||
|
sizeInfo = SizeCalculator.GetLinearTextureSize(
|
||||||
|
info.Stride,
|
||||||
|
info.Height,
|
||||||
|
info.FormatInfo.BlockHeight);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
|
||||||
|
info.Width,
|
||||||
|
info.Height,
|
||||||
|
info.GetDepth(),
|
||||||
|
info.Levels,
|
||||||
|
info.GetLayers(),
|
||||||
|
info.FormatInfo.BlockWidth,
|
||||||
|
info.FormatInfo.BlockHeight,
|
||||||
|
info.FormatInfo.BytesPerPixel,
|
||||||
|
info.GobBlocksInY,
|
||||||
|
info.GobBlocksInZ,
|
||||||
|
info.GobBlocksInTileX);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find view compatible matches.
|
||||||
|
ulong size = (ulong)sizeInfo.TotalSize;
|
||||||
|
|
||||||
|
Texture[] overlaps = _textures.FindOverlaps(info.Address, size);
|
||||||
|
|
||||||
|
Texture texture = null;
|
||||||
|
|
||||||
|
foreach (Texture overlap in overlaps)
|
||||||
|
{
|
||||||
|
if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel))
|
||||||
|
{
|
||||||
|
if (!isSamplerTexture)
|
||||||
|
{
|
||||||
|
info = AdjustSizes(overlap, info, firstLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);
|
||||||
|
|
||||||
|
// The size only matters (and is only really reliable) when the
|
||||||
|
// texture is used on a sampler, because otherwise the size will be
|
||||||
|
// aligned.
|
||||||
|
if (!overlap.SizeMatches(info, firstLevel) && isSamplerTexture)
|
||||||
|
{
|
||||||
|
texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No match, create a new texture.
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
texture = new Texture(_context, info, sizeInfo);
|
||||||
|
|
||||||
|
// We need to synchronize before copying the old view data to the texture,
|
||||||
|
// otherwise the copied data would be overwritten by a future synchronization.
|
||||||
|
texture.SynchronizeMemory();
|
||||||
|
|
||||||
|
foreach (Texture overlap in overlaps)
|
||||||
|
{
|
||||||
|
if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel))
|
||||||
|
{
|
||||||
|
TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel);
|
||||||
|
|
||||||
|
TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);
|
||||||
|
|
||||||
|
ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel);
|
||||||
|
|
||||||
|
overlap.HostTexture.CopyTo(newView);
|
||||||
|
|
||||||
|
overlap.ReplaceView(texture, overlapInfo, newView);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sampler textures are managed by the texture pool, all other textures
|
||||||
|
// are managed by the auto delete cache.
|
||||||
|
if (!isSamplerTexture)
|
||||||
|
{
|
||||||
|
_cache.Add(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
_textures.Add(texture);
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
|
||||||
|
{
|
||||||
|
// When the texture is used as view of another texture, we must
|
||||||
|
// ensure that the sizes are valid, otherwise data uploads would fail
|
||||||
|
// (and the size wouldn't match the real size used on the host API).
|
||||||
|
// Given a parent texture from where the view is created, we have the
|
||||||
|
// following rules:
|
||||||
|
// - The view size must be equal to the parent size, divided by (2 ^ l),
|
||||||
|
// where l is the first mipmap level of the view. The division result must
|
||||||
|
// be rounded down, and the result must be clamped to 1.
|
||||||
|
// - If the parent format is compressed, and the view format isn't, the
|
||||||
|
// view size is calculated as above, but the width and height of the
|
||||||
|
// view must be also divided by the compressed format block width and height.
|
||||||
|
// - If the parent format is not compressed, and the view is, the view
|
||||||
|
// size is calculated as described on the first point, but the width and height
|
||||||
|
// of the view must be also multiplied by the block width and height.
|
||||||
|
int width = Math.Max(1, parent.Info.Width >> firstLevel);
|
||||||
|
int height = Math.Max(1, parent.Info.Height >> firstLevel);
|
||||||
|
|
||||||
|
if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
|
||||||
|
{
|
||||||
|
width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
|
||||||
|
height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
|
||||||
|
}
|
||||||
|
else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
|
||||||
|
{
|
||||||
|
width *= info.FormatInfo.BlockWidth;
|
||||||
|
height *= info.FormatInfo.BlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
int depthOrLayers;
|
||||||
|
|
||||||
|
if (info.Target == Target.Texture3D)
|
||||||
|
{
|
||||||
|
depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
depthOrLayers = info.DepthOrLayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new TextureInfo(
|
||||||
|
info.Address,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depthOrLayers,
|
||||||
|
info.Levels,
|
||||||
|
info.SamplesInX,
|
||||||
|
info.SamplesInY,
|
||||||
|
info.Stride,
|
||||||
|
info.IsLinear,
|
||||||
|
info.GobBlocksInY,
|
||||||
|
info.GobBlocksInZ,
|
||||||
|
info.GobBlocksInTileX,
|
||||||
|
info.Target,
|
||||||
|
info.FormatInfo,
|
||||||
|
info.DepthStencilMode,
|
||||||
|
info.SwizzleR,
|
||||||
|
info.SwizzleG,
|
||||||
|
info.SwizzleB,
|
||||||
|
info.SwizzleA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps)
|
||||||
|
{
|
||||||
|
FormatInfo formatInfo = info.FormatInfo;
|
||||||
|
|
||||||
|
if (!caps.SupportsAstcCompression)
|
||||||
|
{
|
||||||
|
if (formatInfo.Format.IsAstcUnorm())
|
||||||
|
{
|
||||||
|
formatInfo = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
|
||||||
|
}
|
||||||
|
else if (formatInfo.Format.IsAstcSrgb())
|
||||||
|
{
|
||||||
|
formatInfo = new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = info.Width / info.SamplesInX;
|
||||||
|
int height = info.Height / info.SamplesInY;
|
||||||
|
|
||||||
|
int depth = info.GetDepth() * info.GetLayers();
|
||||||
|
|
||||||
|
return new TextureCreateInfo(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth,
|
||||||
|
info.Levels,
|
||||||
|
info.Samples,
|
||||||
|
formatInfo.BlockWidth,
|
||||||
|
formatInfo.BlockHeight,
|
||||||
|
formatInfo.BytesPerPixel,
|
||||||
|
formatInfo.Format,
|
||||||
|
info.DepthStencilMode,
|
||||||
|
info.Target,
|
||||||
|
info.SwizzleR,
|
||||||
|
info.SwizzleG,
|
||||||
|
info.SwizzleB,
|
||||||
|
info.SwizzleA);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Texture Find2(ulong address)
|
||||||
|
{
|
||||||
|
Texture[] ts = _textures.FindOverlaps(address, 1);
|
||||||
|
|
||||||
|
if (ts.Length == 2)
|
||||||
|
{
|
||||||
|
return ts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts.Length == 0)
|
||||||
|
{
|
||||||
|
ts = _textures.FindOverlaps(address - 1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts.Length == 0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvalidateRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
Texture[] overlaps = _textures.FindOverlaps(address, size);
|
||||||
|
|
||||||
|
foreach (Texture overlap in overlaps)
|
||||||
|
{
|
||||||
|
overlap.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
_samplerPool?.InvalidateRange(address, size);
|
||||||
|
|
||||||
|
_texturePoolCache.InvalidateRange(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Flush()
|
||||||
|
{
|
||||||
|
foreach (Texture texture in _cache)
|
||||||
|
{
|
||||||
|
if (texture.Info.IsLinear && texture.Modified)
|
||||||
|
{
|
||||||
|
texture.Flush();
|
||||||
|
|
||||||
|
texture.Modified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveTextureFromCache(Texture texture)
|
||||||
|
{
|
||||||
|
_textures.Remove(texture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
53
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal file
53
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
enum TextureMsaaMode
|
||||||
|
{
|
||||||
|
Ms1x1 = 0,
|
||||||
|
Ms2x2 = 2,
|
||||||
|
Ms4x2 = 4,
|
||||||
|
Ms2x1 = 5,
|
||||||
|
Ms4x4 = 6
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TextureMsaaModeConverter
|
||||||
|
{
|
||||||
|
public static int SamplesCount(this TextureMsaaMode msaaMode)
|
||||||
|
{
|
||||||
|
switch (msaaMode)
|
||||||
|
{
|
||||||
|
case TextureMsaaMode.Ms2x1: return 2;
|
||||||
|
case TextureMsaaMode.Ms2x2: return 4;
|
||||||
|
case TextureMsaaMode.Ms4x2: return 8;
|
||||||
|
case TextureMsaaMode.Ms4x4: return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int SamplesInX(this TextureMsaaMode msaaMode)
|
||||||
|
{
|
||||||
|
switch (msaaMode)
|
||||||
|
{
|
||||||
|
case TextureMsaaMode.Ms2x1: return 2;
|
||||||
|
case TextureMsaaMode.Ms2x2: return 2;
|
||||||
|
case TextureMsaaMode.Ms4x2: return 4;
|
||||||
|
case TextureMsaaMode.Ms4x4: return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int SamplesInY(this TextureMsaaMode msaaMode)
|
||||||
|
{
|
||||||
|
switch (msaaMode)
|
||||||
|
{
|
||||||
|
case TextureMsaaMode.Ms2x1: return 1;
|
||||||
|
case TextureMsaaMode.Ms2x2: return 2;
|
||||||
|
case TextureMsaaMode.Ms4x2: return 2;
|
||||||
|
case TextureMsaaMode.Ms4x4: return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
219
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal file
219
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
using Ryujinx.Graphics.Gpu.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
class TexturePool : Pool<Texture>
|
||||||
|
{
|
||||||
|
private TextureManager _textureManager;
|
||||||
|
|
||||||
|
public LinkedListNode<TexturePool> CacheNode { get; set; }
|
||||||
|
|
||||||
|
private struct TextureContainer
|
||||||
|
{
|
||||||
|
public Texture Texture0 { get; set; }
|
||||||
|
public Texture Texture1 { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public TexturePool(
|
||||||
|
GpuContext context,
|
||||||
|
TextureManager textureManager,
|
||||||
|
ulong address,
|
||||||
|
int maximumId) : base(context, address, maximumId)
|
||||||
|
{
|
||||||
|
_textureManager = textureManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Texture Get(int id)
|
||||||
|
{
|
||||||
|
if ((uint)id >= Items.Length)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SynchronizeMemory();
|
||||||
|
|
||||||
|
Texture texture = Items[id];
|
||||||
|
|
||||||
|
if (texture == null)
|
||||||
|
{
|
||||||
|
ulong address = Address + (ulong)(uint)id * DescriptorSize;
|
||||||
|
|
||||||
|
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
|
||||||
|
|
||||||
|
TextureDescriptor descriptor = MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0];
|
||||||
|
|
||||||
|
TextureInfo info = GetInfo(descriptor);
|
||||||
|
|
||||||
|
// Bad address. We can't add a texture with a invalid address
|
||||||
|
// to the cache.
|
||||||
|
if (info.Address == MemoryManager.BadAddress)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture = _textureManager.FindOrCreateTexture(info, TextureSearchFlags.Sampler);
|
||||||
|
|
||||||
|
texture.IncrementReferenceCount();
|
||||||
|
|
||||||
|
Items[id] = texture;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Memory is automatically synchronized on texture creation.
|
||||||
|
texture.SynchronizeMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
ulong endAddress = address + size;
|
||||||
|
|
||||||
|
for (; address < endAddress; address += DescriptorSize)
|
||||||
|
{
|
||||||
|
int id = (int)((address - Address) / DescriptorSize);
|
||||||
|
|
||||||
|
Texture texture = Items[id];
|
||||||
|
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
|
||||||
|
|
||||||
|
TextureDescriptor descriptor = MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0];
|
||||||
|
|
||||||
|
// If the descriptors are the same, the texture is the same,
|
||||||
|
// we don't need to remove as it was not modified. Just continue.
|
||||||
|
if (texture.IsPerfectMatch(GetInfo(descriptor), TextureSearchFlags.Strict))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
texture.DecrementReferenceCount();
|
||||||
|
|
||||||
|
Items[id] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextureInfo GetInfo(TextureDescriptor descriptor)
|
||||||
|
{
|
||||||
|
ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress());
|
||||||
|
|
||||||
|
int width = descriptor.UnpackWidth();
|
||||||
|
int height = descriptor.UnpackHeight();
|
||||||
|
int depthOrLayers = descriptor.UnpackDepth();
|
||||||
|
int levels = descriptor.UnpackLevels();
|
||||||
|
|
||||||
|
TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode();
|
||||||
|
|
||||||
|
int samplesInX = msaaMode.SamplesInX();
|
||||||
|
int samplesInY = msaaMode.SamplesInY();
|
||||||
|
|
||||||
|
int stride = descriptor.UnpackStride();
|
||||||
|
|
||||||
|
TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType();
|
||||||
|
|
||||||
|
bool isLinear = descriptorType == TextureDescriptorType.Linear;
|
||||||
|
|
||||||
|
Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1);
|
||||||
|
|
||||||
|
uint format = descriptor.UnpackFormat();
|
||||||
|
bool srgb = descriptor.UnpackSrgb();
|
||||||
|
|
||||||
|
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
|
||||||
|
{
|
||||||
|
// TODO: Warning.
|
||||||
|
|
||||||
|
formatInfo = FormatInfo.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
int gobBlocksInY = descriptor.UnpackGobBlocksInY();
|
||||||
|
int gobBlocksInZ = descriptor.UnpackGobBlocksInZ();
|
||||||
|
|
||||||
|
int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();
|
||||||
|
|
||||||
|
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
|
||||||
|
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
|
||||||
|
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
|
||||||
|
SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert();
|
||||||
|
|
||||||
|
DepthStencilMode depthStencilMode = GetDepthStencilMode(
|
||||||
|
formatInfo.Format,
|
||||||
|
swizzleR,
|
||||||
|
swizzleG,
|
||||||
|
swizzleB,
|
||||||
|
swizzleA);
|
||||||
|
|
||||||
|
return new TextureInfo(
|
||||||
|
address,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depthOrLayers,
|
||||||
|
levels,
|
||||||
|
samplesInX,
|
||||||
|
samplesInY,
|
||||||
|
stride,
|
||||||
|
isLinear,
|
||||||
|
gobBlocksInY,
|
||||||
|
gobBlocksInZ,
|
||||||
|
gobBlocksInTileX,
|
||||||
|
target,
|
||||||
|
formatInfo,
|
||||||
|
depthStencilMode,
|
||||||
|
swizzleR,
|
||||||
|
swizzleG,
|
||||||
|
swizzleB,
|
||||||
|
swizzleA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components)
|
||||||
|
{
|
||||||
|
// R = Depth, G = Stencil.
|
||||||
|
// On 24-bits depth formats, this is inverted (Stencil is R etc).
|
||||||
|
// NVN setup:
|
||||||
|
// For depth, A is set to 1.0f, the other components are set to Depth.
|
||||||
|
// For stencil, all components are set to Stencil.
|
||||||
|
SwizzleComponent component = components[0];
|
||||||
|
|
||||||
|
for (int index = 1; index < 4 && !IsRG(component); index++)
|
||||||
|
{
|
||||||
|
component = components[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsRG(component))
|
||||||
|
{
|
||||||
|
return DepthStencilMode.Depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format == Format.D24X8Unorm || format == Format.D24UnormS8Uint)
|
||||||
|
{
|
||||||
|
return component == SwizzleComponent.Red
|
||||||
|
? DepthStencilMode.Stencil
|
||||||
|
: DepthStencilMode.Depth;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return component == SwizzleComponent.Red
|
||||||
|
? DepthStencilMode.Depth
|
||||||
|
: DepthStencilMode.Stencil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsRG(SwizzleComponent component)
|
||||||
|
{
|
||||||
|
return component == SwizzleComponent.Red ||
|
||||||
|
component == SwizzleComponent.Green;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Delete(Texture item)
|
||||||
|
{
|
||||||
|
item?.DecrementReferenceCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
73
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal file
73
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
class TexturePoolCache
|
||||||
|
{
|
||||||
|
private const int MaxCapacity = 4;
|
||||||
|
|
||||||
|
private GpuContext _context;
|
||||||
|
private TextureManager _textureManager;
|
||||||
|
|
||||||
|
private LinkedList<TexturePool> _pools;
|
||||||
|
|
||||||
|
public TexturePoolCache(GpuContext context, TextureManager textureManager)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
_textureManager = textureManager;
|
||||||
|
|
||||||
|
_pools = new LinkedList<TexturePool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TexturePool FindOrCreate(ulong address, int maximumId)
|
||||||
|
{
|
||||||
|
TexturePool pool;
|
||||||
|
|
||||||
|
// First we try to find the pool.
|
||||||
|
for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next)
|
||||||
|
{
|
||||||
|
pool = node.Value;
|
||||||
|
|
||||||
|
if (pool.Address == address)
|
||||||
|
{
|
||||||
|
if (pool.CacheNode != _pools.Last)
|
||||||
|
{
|
||||||
|
_pools.Remove(pool.CacheNode);
|
||||||
|
|
||||||
|
pool.CacheNode = _pools.AddLast(pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not found, create a new one.
|
||||||
|
pool = new TexturePool(_context, _textureManager, address, maximumId);
|
||||||
|
|
||||||
|
pool.CacheNode = _pools.AddLast(pool);
|
||||||
|
|
||||||
|
if (_pools.Count > MaxCapacity)
|
||||||
|
{
|
||||||
|
TexturePool oldestPool = _pools.First.Value;
|
||||||
|
|
||||||
|
_pools.RemoveFirst();
|
||||||
|
|
||||||
|
oldestPool.Dispose();
|
||||||
|
|
||||||
|
oldestPool.CacheNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InvalidateRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next)
|
||||||
|
{
|
||||||
|
TexturePool pool = node.Value;
|
||||||
|
|
||||||
|
pool.InvalidateRange(address, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal file
13
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
enum TextureSearchFlags
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
IgnoreMs = 1 << 0,
|
||||||
|
Strict = 1 << 1 | Sampler,
|
||||||
|
Sampler = 1 << 2
|
||||||
|
}
|
||||||
|
}
|
49
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal file
49
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
using Ryujinx.Graphics.GAL.Texture;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Image
|
||||||
|
{
|
||||||
|
enum TextureTarget
|
||||||
|
{
|
||||||
|
Texture1D,
|
||||||
|
Texture2D,
|
||||||
|
Texture3D,
|
||||||
|
Cubemap,
|
||||||
|
Texture1DArray,
|
||||||
|
Texture2DArray,
|
||||||
|
TextureBuffer,
|
||||||
|
Texture2DLinear,
|
||||||
|
CubemapArray
|
||||||
|
}
|
||||||
|
|
||||||
|
static class TextureTargetConverter
|
||||||
|
{
|
||||||
|
public static Target Convert(this TextureTarget target, bool isMultisample)
|
||||||
|
{
|
||||||
|
if (isMultisample)
|
||||||
|
{
|
||||||
|
switch (target)
|
||||||
|
{
|
||||||
|
case TextureTarget.Texture2D: return Target.Texture2DMultisample;
|
||||||
|
case TextureTarget.Texture2DArray: return Target.Texture2DMultisampleArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (target)
|
||||||
|
{
|
||||||
|
case TextureTarget.Texture1D: return Target.Texture1D;
|
||||||
|
case TextureTarget.Texture2D: return Target.Texture2D;
|
||||||
|
case TextureTarget.Texture2DLinear: return Target.Texture2D;
|
||||||
|
case TextureTarget.Texture3D: return Target.Texture3D;
|
||||||
|
case TextureTarget.Texture1DArray: return Target.Texture1DArray;
|
||||||
|
case TextureTarget.Texture2DArray: return Target.Texture2DArray;
|
||||||
|
case TextureTarget.Cubemap: return Target.Cubemap;
|
||||||
|
case TextureTarget.CubemapArray: return Target.CubemapArray;
|
||||||
|
case TextureTarget.TextureBuffer: return Target.TextureBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Target.Texture1D;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,8 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Graphics.Memory;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Graphics3d
|
namespace Ryujinx.Graphics.Gpu
|
||||||
{
|
{
|
||||||
class MacroInterpreter
|
class MacroInterpreter
|
||||||
{
|
{
|
||||||
|
@ -42,8 +41,9 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
BitwiseNotAnd = 12
|
BitwiseNotAnd = 12
|
||||||
}
|
}
|
||||||
|
|
||||||
private NvGpuFifo _pFifo;
|
private GpuContext _context;
|
||||||
private INvGpuEngine _engine;
|
|
||||||
|
private NvGpuFifo _pFifo;
|
||||||
|
|
||||||
public Queue<int> Fifo { get; private set; }
|
public Queue<int> Fifo { get; private set; }
|
||||||
|
|
||||||
|
@ -60,17 +60,17 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
private int _pc;
|
private int _pc;
|
||||||
|
|
||||||
public MacroInterpreter(NvGpuFifo pFifo, INvGpuEngine engine)
|
public MacroInterpreter(GpuContext context, NvGpuFifo pFifo)
|
||||||
{
|
{
|
||||||
_pFifo = pFifo;
|
_context = context;
|
||||||
_engine = engine;
|
_pFifo = pFifo;
|
||||||
|
|
||||||
Fifo = new Queue<int>();
|
Fifo = new Queue<int>();
|
||||||
|
|
||||||
_gprs = new int[8];
|
_gprs = new int[8];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Execute(NvGpuVmm vmm, int[] mme, int position, int param)
|
public void Execute(int[] mme, int position, int param)
|
||||||
{
|
{
|
||||||
Reset();
|
Reset();
|
||||||
|
|
||||||
|
@ -80,11 +80,11 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
FetchOpCode(mme);
|
FetchOpCode(mme);
|
||||||
|
|
||||||
while (Step(vmm, mme));
|
while (Step(mme));
|
||||||
|
|
||||||
// Due to the delay slot, we still need to execute
|
// Due to the delay slot, we still need to execute
|
||||||
// one more instruction before we actually exit.
|
// one more instruction before we actually exit.
|
||||||
Step(vmm, mme);
|
Step(mme);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Reset()
|
private void Reset()
|
||||||
|
@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
_carry = false;
|
_carry = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool Step(NvGpuVmm vmm, int[] mme)
|
private bool Step(int[] mme)
|
||||||
{
|
{
|
||||||
int baseAddr = _pc - 1;
|
int baseAddr = _pc - 1;
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
{
|
{
|
||||||
SetDstGpr(FetchParam());
|
SetDstGpr(FetchParam());
|
||||||
|
|
||||||
Send(vmm, result);
|
Send(result);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
{
|
{
|
||||||
SetDstGpr(result);
|
SetDstGpr(result);
|
||||||
|
|
||||||
Send(vmm, result);
|
Send(result);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
SetMethAddr(result);
|
SetMethAddr(result);
|
||||||
|
|
||||||
Send(vmm, FetchParam());
|
Send(FetchParam());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
SetMethAddr(result);
|
SetMethAddr(result);
|
||||||
|
|
||||||
Send(vmm, (result >> 12) & 0x3f);
|
Send((result >> 12) & 0x3f);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -228,7 +228,6 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
private void FetchOpCode(int[] mme)
|
private void FetchOpCode(int[] mme)
|
||||||
{
|
{
|
||||||
_opCode = _pipeOp;
|
_opCode = _pipeOp;
|
||||||
|
|
||||||
_pipeOp = mme[_pc++];
|
_pipeOp = mme[_pc++];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -401,14 +400,14 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||||
|
|
||||||
private int Read(int reg)
|
private int Read(int reg)
|
||||||
{
|
{
|
||||||
return _engine.Registers[reg];
|
return _context.State.Read(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Send(NvGpuVmm vmm, int value)
|
private void Send(int value)
|
||||||
{
|
{
|
||||||
GpuMethodCall methCall = new GpuMethodCall(_methAddr, value);
|
MethodParams meth = new MethodParams(_methAddr, value);
|
||||||
|
|
||||||
_engine.CallMethod(vmm, methCall);
|
_context.State.CallMethod(meth);
|
||||||
|
|
||||||
_methAddr += _methIncr;
|
_methAddr += _methIncr;
|
||||||
}
|
}
|
99
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
99
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
{
|
||||||
|
class Buffer : IRange<Buffer>, IDisposable
|
||||||
|
{
|
||||||
|
private GpuContext _context;
|
||||||
|
|
||||||
|
private IBuffer _buffer;
|
||||||
|
|
||||||
|
public ulong Address { get; }
|
||||||
|
public ulong Size { get; }
|
||||||
|
|
||||||
|
public ulong EndAddress => Address + Size;
|
||||||
|
|
||||||
|
private int[] _sequenceNumbers;
|
||||||
|
|
||||||
|
public Buffer(GpuContext context, ulong address, ulong size)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
Address = address;
|
||||||
|
Size = size;
|
||||||
|
|
||||||
|
_buffer = context.Renderer.CreateBuffer((int)size);
|
||||||
|
|
||||||
|
_sequenceNumbers = new int[size / MemoryManager.PageSize];
|
||||||
|
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferRange GetRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
int offset = (int)(address - Address);
|
||||||
|
|
||||||
|
return new BufferRange(_buffer, offset, (int)size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OverlapsWith(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
return Address < address + size && address < EndAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SynchronizeMemory(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
int currentSequenceNumber = _context.SequenceNumber;
|
||||||
|
|
||||||
|
bool needsSync = false;
|
||||||
|
|
||||||
|
ulong buffOffset = address - Address;
|
||||||
|
|
||||||
|
ulong buffEndOffset = (buffOffset + size + MemoryManager.PageMask) & ~MemoryManager.PageMask;
|
||||||
|
|
||||||
|
int startIndex = (int)(buffOffset / MemoryManager.PageSize);
|
||||||
|
int endIndex = (int)(buffEndOffset / MemoryManager.PageSize);
|
||||||
|
|
||||||
|
for (int index = startIndex; index < endIndex; index++)
|
||||||
|
{
|
||||||
|
if (_sequenceNumbers[index] != currentSequenceNumber)
|
||||||
|
{
|
||||||
|
_sequenceNumbers[index] = currentSequenceNumber;
|
||||||
|
|
||||||
|
needsSync = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsSync)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(ulong, ulong)[] modifiedRanges = _context.PhysicalMemory.GetModifiedRanges(address, size);
|
||||||
|
|
||||||
|
for (int index = 0; index < modifiedRanges.Length; index++)
|
||||||
|
{
|
||||||
|
(ulong mAddress, ulong mSize) = modifiedRanges[index];
|
||||||
|
|
||||||
|
int offset = (int)(mAddress - Address);
|
||||||
|
|
||||||
|
_buffer.SetData(offset, _context.PhysicalMemory.Read(mAddress, mSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CopyTo(Buffer destination, int dstOffset)
|
||||||
|
{
|
||||||
|
_buffer.CopyTo(destination._buffer, 0, dstOffset, (int)Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Invalidate()
|
||||||
|
{
|
||||||
|
_buffer.SetData(0, _context.PhysicalMemory.Read(Address, Size));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_buffer.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
8
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
8
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Ryujinx.Graphics.Gpu.Memory
|
||||||
|
{
|
||||||
|
struct BufferBounds
|
||||||
|
{
|
||||||
|
public ulong Address;
|
||||||
|
public ulong Size;
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue