Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
@ -355,12 +355,9 @@ namespace ARMeilleure.Instructions
|
||||
}
|
||||
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));
|
||||
|
||||
|
@ -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 bool HasWriteWatchSupport => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
public static IntPtr Allocate(ulong size)
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
@ -88,27 +86,5 @@ namespace ARMeilleure.Memory
|
||||
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
|
||||
}
|
||||
|
||||
private enum WriteWatchFlags : uint
|
||||
{
|
||||
None = 0,
|
||||
Reset = 1
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr VirtualAlloc(
|
||||
IntPtr lpAddress,
|
||||
@ -62,15 +56,6 @@ namespace ARMeilleure.Memory
|
||||
IntPtr dwSize,
|
||||
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)
|
||||
{
|
||||
const AllocationType flags =
|
||||
@ -130,27 +115,5 @@ namespace ARMeilleure.Memory
|
||||
{
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
|
||||
@ -29,8 +30,6 @@ namespace ARMeilleure.Memory
|
||||
internal int PtLevelSize { get; }
|
||||
internal int PtLevelMask { get; }
|
||||
|
||||
public bool HasWriteWatchSupport => MemoryManagement.HasWriteWatchSupport;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
public long AddressSpaceSize { get; }
|
||||
|
||||
@ -254,119 +253,57 @@ namespace ARMeilleure.Memory
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
if (IsValidPosition((long)address))
|
||||
{
|
||||
return count != 0;
|
||||
}
|
||||
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];
|
||||
byte* ptr = ((byte**)_pageTable)[address >> PageBits];
|
||||
|
||||
ulong ptrUlong = (ulong)ptr;
|
||||
|
||||
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
|
||||
{
|
||||
modified = true;
|
||||
currSize += PageSize;
|
||||
}
|
||||
|
||||
position += PageSize;
|
||||
address += PageSize;
|
||||
}
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
public bool TryGetHostAddress(long position, long size, out IntPtr ptr)
|
||||
{
|
||||
if (IsContiguous(position, size))
|
||||
if (currSize != 0)
|
||||
{
|
||||
ptr = (IntPtr)Translate(position);
|
||||
|
||||
return true;
|
||||
ranges.Add((currAddr, currSize));
|
||||
}
|
||||
|
||||
ptr = IntPtr.Zero;
|
||||
|
||||
return false;
|
||||
return ranges.ToArray();
|
||||
}
|
||||
|
||||
private bool IsContiguous(long position, long size)
|
||||
@ -612,41 +549,6 @@ namespace ARMeilleure.Memory
|
||||
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)
|
||||
{
|
||||
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()
|
||||
{
|
||||
Dispose(true);
|
||||
|
@ -1,9 +1,11 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Ryujinx.Common
|
||||
{
|
||||
public static class PerformanceCounter
|
||||
{
|
||||
private static double _ticksToNs;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the number of ticks in 1 day.
|
||||
/// </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()
|
||||
{
|
||||
TicksPerMillisecond = Stopwatch.Frequency / 1000;
|
||||
@ -60,6 +75,8 @@ namespace Ryujinx.Common
|
||||
TicksPerMinute = TicksPerSecond * 60;
|
||||
TicksPerHour = TicksPerMinute * 60;
|
||||
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,6 +1,6 @@
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum GalCullFace
|
||||
public enum Face
|
||||
{
|
||||
Front = 0x404,
|
||||
Back = 0x405,
|
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.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
public class DmaPusher
|
||||
{
|
||||
private ConcurrentQueue<(NvGpuVmm, long)> _ibBuffer;
|
||||
private ConcurrentQueue<ulong> _ibBuffer;
|
||||
|
||||
private long _dmaPut;
|
||||
private long _dmaGet;
|
||||
private ulong _dmaPut;
|
||||
private ulong _dmaGet;
|
||||
|
||||
private struct DmaState
|
||||
{
|
||||
@ -29,28 +28,26 @@ namespace Ryujinx.Graphics
|
||||
private bool _ibEnable;
|
||||
private bool _nonMain;
|
||||
|
||||
private long _dmaMGet;
|
||||
private ulong _dmaMGet;
|
||||
|
||||
private NvGpuVmm _vmm;
|
||||
|
||||
private NvGpu _gpu;
|
||||
private GpuContext _context;
|
||||
|
||||
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;
|
||||
|
||||
_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();
|
||||
}
|
||||
@ -69,7 +66,7 @@ namespace Ryujinx.Graphics
|
||||
{
|
||||
if (_dmaGet != _dmaPut)
|
||||
{
|
||||
int word = _vmm.ReadInt32(_dmaGet);
|
||||
int word = _context.MemoryAccessor.ReadInt32(_dmaGet);
|
||||
|
||||
_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;
|
||||
|
||||
long entry = tuple.Entry;
|
||||
|
||||
int length = (int)(entry >> 42) & 0x1fffff;
|
||||
ulong length = (entry >> 42) & 0x1fffff;
|
||||
|
||||
_dmaGet = entry & 0xfffffffffc;
|
||||
_dmaPut = _dmaGet + length * 4;
|
||||
|
||||
_nonMain = (entry & (1L << 41)) != 0;
|
||||
|
||||
_gpu.ResourceManager.ClearPbCache();
|
||||
_nonMain = (entry & (1UL << 41)) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -180,7 +171,7 @@ namespace Ryujinx.Graphics
|
||||
|
||||
private void CallMethod(int argument)
|
||||
{
|
||||
_gpu.Fifo.CallMethod(_vmm, new GpuMethodCall(
|
||||
_context.Fifo.CallMethod(new MethodParams(
|
||||
_state.Method,
|
||||
argument,
|
||||
_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +1,43 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Graphics.Gal
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
static class ShaderDumper
|
||||
class ShaderDumper
|
||||
{
|
||||
private static string _runtimeDir;
|
||||
private const int ShaderHeaderSize = 0x50;
|
||||
|
||||
public static int DumpIndex { get; private set; } = 1;
|
||||
private GpuContext _context;
|
||||
|
||||
public static void Dump(IGalMemory memory, long position, GalShaderType type, string extSuffix = "")
|
||||
private string _runtimeDir;
|
||||
private string _dumpPath;
|
||||
private int _dumpIndex;
|
||||
|
||||
public int CurrentDumpIndex => _dumpIndex;
|
||||
|
||||
public ShaderDumper(GpuContext context)
|
||||
{
|
||||
if (!IsDumpEnabled())
|
||||
_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") + "." + ShaderExtension(type) + extSuffix + ".bin";
|
||||
string fileName = "Shader" + _dumpIndex.ToString("d4") + ".bin";
|
||||
|
||||
string fullPath = Path.Combine(FullDir(), fileName);
|
||||
string codePath = Path.Combine(CodeDir(), fileName);
|
||||
|
||||
DumpIndex++;
|
||||
_dumpIndex++;
|
||||
|
||||
ulong headerSize = compute ? 0UL : ShaderHeaderSize;
|
||||
|
||||
using (FileStream fullFile = File.Create(fullPath))
|
||||
using (FileStream codeFile = File.Create(codePath))
|
||||
@ -29,25 +45,25 @@ namespace Ryujinx.Graphics.Gal
|
||||
BinaryWriter fullWriter = new BinaryWriter(fullFile);
|
||||
BinaryWriter codeWriter = new BinaryWriter(codeFile);
|
||||
|
||||
for (long i = 0; i < 0x50; i += 4)
|
||||
for (ulong i = 0; i < headerSize; i += 4)
|
||||
{
|
||||
fullWriter.Write(memory.ReadInt32(position + i));
|
||||
fullWriter.Write(_context.MemoryAccessor.ReadInt32(gpuVa + i));
|
||||
}
|
||||
|
||||
long offset = 0;
|
||||
ulong offset = 0;
|
||||
|
||||
ulong instruction = 0;
|
||||
|
||||
// Dump until a NOP instruction is found
|
||||
// Dump until a NOP instruction is found.
|
||||
while ((instruction >> 48 & 0xfff8) != 0x50b0)
|
||||
{
|
||||
uint word0 = (uint)memory.ReadInt32(position + 0x50 + offset + 0);
|
||||
uint word1 = (uint)memory.ReadInt32(position + 0x50 + offset + 4);
|
||||
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
|
||||
// this is to avoid two rows of zeroes.
|
||||
if (instruction == 0)
|
||||
{
|
||||
break;
|
||||
@ -59,7 +75,7 @@ namespace Ryujinx.Graphics.Gal
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
// Align to meet nvdisasm requirements
|
||||
// Align to meet nvdisasm requirements.
|
||||
while (offset % 0x20 != 0)
|
||||
{
|
||||
fullWriter.Write(0);
|
||||
@ -70,22 +86,17 @@ namespace Ryujinx.Graphics.Gal
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsDumpEnabled()
|
||||
{
|
||||
return !string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath);
|
||||
}
|
||||
|
||||
private static string FullDir()
|
||||
private string FullDir()
|
||||
{
|
||||
return CreateAndReturn(Path.Combine(DumpDir(), "Full"));
|
||||
}
|
||||
|
||||
private static string CodeDir()
|
||||
private string CodeDir()
|
||||
{
|
||||
return CreateAndReturn(Path.Combine(DumpDir(), "Code"));
|
||||
}
|
||||
|
||||
private static string DumpDir()
|
||||
private string DumpDir()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_runtimeDir))
|
||||
{
|
||||
@ -93,7 +104,7 @@ namespace Ryujinx.Graphics.Gal
|
||||
|
||||
do
|
||||
{
|
||||
_runtimeDir = Path.Combine(GraphicsConfig.ShadersDumpPath, "Dumps" + index.ToString("d2"));
|
||||
_runtimeDir = Path.Combine(_dumpPath, "Dumps" + index.ToString("d2"));
|
||||
|
||||
index++;
|
||||
}
|
||||
@ -107,26 +118,9 @@ namespace Ryujinx.Graphics.Gal
|
||||
|
||||
private static string CreateAndReturn(string dir)
|
||||
{
|
||||
if (!Directory.Exists(dir))
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
}
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
private static string ShaderExtension(GalShaderType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case GalShaderType.Vertex: return "vert";
|
||||
case GalShaderType.TessControl: return "tesc";
|
||||
case GalShaderType.TessEvaluation: return "tese";
|
||||
case GalShaderType.Geometry: return "geom";
|
||||
case GalShaderType.Fragment: return "frag";
|
||||
|
||||
default: throw new ArgumentException(nameof(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
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.Graphics.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
class MacroInterpreter
|
||||
{
|
||||
@ -42,8 +41,9 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
BitwiseNotAnd = 12
|
||||
}
|
||||
|
||||
private NvGpuFifo _pFifo;
|
||||
private INvGpuEngine _engine;
|
||||
private GpuContext _context;
|
||||
|
||||
private NvGpuFifo _pFifo;
|
||||
|
||||
public Queue<int> Fifo { get; private set; }
|
||||
|
||||
@ -60,17 +60,17 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
|
||||
private int _pc;
|
||||
|
||||
public MacroInterpreter(NvGpuFifo pFifo, INvGpuEngine engine)
|
||||
public MacroInterpreter(GpuContext context, NvGpuFifo pFifo)
|
||||
{
|
||||
_pFifo = pFifo;
|
||||
_engine = engine;
|
||||
_context = context;
|
||||
_pFifo = pFifo;
|
||||
|
||||
Fifo = new Queue<int>();
|
||||
|
||||
_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();
|
||||
|
||||
@ -80,11 +80,11 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
|
||||
FetchOpCode(mme);
|
||||
|
||||
while (Step(vmm, mme));
|
||||
while (Step(mme));
|
||||
|
||||
// Due to the delay slot, we still need to execute
|
||||
// one more instruction before we actually exit.
|
||||
Step(vmm, mme);
|
||||
Step(mme);
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
_carry = false;
|
||||
}
|
||||
|
||||
private bool Step(NvGpuVmm vmm, int[] mme)
|
||||
private bool Step(int[] mme)
|
||||
{
|
||||
int baseAddr = _pc - 1;
|
||||
|
||||
@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
Send(vmm, result);
|
||||
Send(result);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
{
|
||||
SetDstGpr(result);
|
||||
|
||||
Send(vmm, result);
|
||||
Send(result);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
|
||||
SetMethAddr(result);
|
||||
|
||||
Send(vmm, FetchParam());
|
||||
Send(FetchParam());
|
||||
|
||||
break;
|
||||
}
|
||||
@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
|
||||
SetMethAddr(result);
|
||||
|
||||
Send(vmm, (result >> 12) & 0x3f);
|
||||
Send((result >> 12) & 0x3f);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -228,7 +228,6 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
private void FetchOpCode(int[] mme)
|
||||
{
|
||||
_opCode = _pipeOp;
|
||||
|
||||
_pipeOp = mme[_pc++];
|
||||
}
|
||||
|
||||
@ -401,14 +400,14 @@ namespace Ryujinx.Graphics.Graphics3d
|
||||
|
||||
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;
|
||||
}
|
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 New Issue
Block a user