Merge branch 'master' into aot
This commit is contained in:
commit
fd4a05fe5a
569 changed files with 24753 additions and 12023 deletions
|
@ -320,12 +320,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;
|
||||
|
||||
|
@ -13,8 +14,6 @@ namespace ARMeilleure.Memory
|
|||
public const int PageSize = 1 << PageBits;
|
||||
public const int PageMask = PageSize - 1;
|
||||
|
||||
private const long PteFlagNotModified = 1;
|
||||
|
||||
internal const long PteFlagsMask = 7;
|
||||
|
||||
public IntPtr Ram { get; private set; }
|
||||
|
@ -29,8 +28,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; }
|
||||
|
||||
|
@ -107,6 +104,11 @@ namespace ARMeilleure.Memory
|
|||
ptr = (byte*)ptrUlong;
|
||||
}
|
||||
|
||||
if (ptr == null)
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
|
||||
return new IntPtr(ptr + (position & PageMask));
|
||||
}
|
||||
|
||||
|
@ -123,10 +125,7 @@ namespace ARMeilleure.Memory
|
|||
|
||||
if ((ptrUlong & PteFlagsMask) != 0)
|
||||
{
|
||||
if ((ptrUlong & PteFlagNotModified) != 0)
|
||||
{
|
||||
ClearPtEntryFlag(position, PteFlagNotModified);
|
||||
}
|
||||
ClearPtEntryFlag(position, PteFlagsMask);
|
||||
|
||||
ptrUlong &= ~(ulong)PteFlagsMask;
|
||||
|
||||
|
@ -254,119 +253,60 @@ namespace ARMeilleure.Memory
|
|||
return ptePtr;
|
||||
}
|
||||
|
||||
public bool IsRegionModified(long position, long size)
|
||||
public unsafe (ulong, ulong)[] GetModifiedRanges(ulong address, ulong size, int id)
|
||||
{
|
||||
if (!HasWriteWatchSupport)
|
||||
ulong idMask = 1UL << id;
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
// If the address is invalid, we stop and consider all the remaining memory
|
||||
// as not modified (since the address is invalid, we can't check, and technically
|
||||
// the memory doesn't exist).
|
||||
if (!IsValidPosition((long)address))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
position += PageSize;
|
||||
byte* ptr = ((byte**)_pageTable)[address >> PageBits];
|
||||
|
||||
address = Translate(position);
|
||||
}
|
||||
ulong ptrUlong = (ulong)ptr;
|
||||
|
||||
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))
|
||||
if ((ptrUlong & idMask) == 0)
|
||||
{
|
||||
byte* ptr = ((byte**)_pageTable)[position >> PageBits];
|
||||
// Modified.
|
||||
currSize += PageSize;
|
||||
|
||||
ulong ptrUlong = (ulong)ptr;
|
||||
|
||||
if ((ptrUlong & PteFlagNotModified) == 0)
|
||||
{
|
||||
modified = true;
|
||||
|
||||
SetPtEntryFlag(position, PteFlagNotModified);
|
||||
}
|
||||
SetPtEntryFlag((long)address, (long)idMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
modified = true;
|
||||
if (currSize != 0)
|
||||
{
|
||||
ranges.Add((currAddr, currSize));
|
||||
}
|
||||
|
||||
currAddr = address + PageSize;
|
||||
currSize = 0;
|
||||
}
|
||||
|
||||
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 +552,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 +651,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);
|
||||
|
|
|
@ -43,10 +43,6 @@ The latest automatic build for Windows, macOS, and Linux can be found on the [Of
|
|||
|
||||
Everything on the Switch is encrypted, so if you want to run anything other than homebrew, you have to dump encryption keys from your console. To get more information please take a look at our [Keys Documentation](KEYS.md).
|
||||
|
||||
- **FFmpeg Dependencies**
|
||||
|
||||
Ryujinx has a basic implementation of `NVDEC`, a video decoder used by the Switch's GPU. Many games include videos that use it, so you need to download [Zeranoe's FFmpeg Builds](http://ffmpeg.zeranoe.com/builds/) for **Shared** linking and your computer's operating system. When it's done, extract the contents of the `bin` folder directly into your Ryujinx folder.
|
||||
|
||||
- **System Titles**
|
||||
|
||||
Some of our System Module implementations, like `time`, require [System Data Archives](https://switchbrew.org/wiki/Title_list#System_Data_Archives). You can install them by mounting your nand partition using [HacDiskMount](https://switchtools.sshnuke.net/) and copying the content to `Ryujinx/nand/system`.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
59
Ryujinx.Common/Utilities/Buffers.cs
Normal file
59
Ryujinx.Common/Utilities/Buffers.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
[DebuggerDisplay("{ToString()}")]
|
||||
[StructLayout(LayoutKind.Sequential, Size = 16)]
|
||||
public struct Buffer16
|
||||
{
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy0;
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)] private ulong _dummy1;
|
||||
|
||||
public byte this[int i]
|
||||
{
|
||||
get => Bytes[i];
|
||||
set => Bytes[i] = value;
|
||||
}
|
||||
|
||||
public Span<byte> Bytes => SpanHelpers.AsByteSpan(ref this);
|
||||
|
||||
// Prevent a defensive copy by changing the read-only in reference to a reference with Unsafe.AsRef()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Span<byte>(in Buffer16 value)
|
||||
{
|
||||
return SpanHelpers.AsByteSpan(ref Unsafe.AsRef(in value));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator ReadOnlySpan<byte>(in Buffer16 value)
|
||||
{
|
||||
return SpanHelpers.AsReadOnlyByteSpan(ref Unsafe.AsRef(in value));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T As<T>() where T : unmanaged
|
||||
{
|
||||
if (Unsafe.SizeOf<T>() > (uint)Unsafe.SizeOf<Buffer16>())
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
return ref MemoryMarshal.GetReference(AsSpan<T>());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<T> AsSpan<T>() where T : unmanaged
|
||||
{
|
||||
return SpanHelpers.AsSpan<Buffer16, T>(ref this);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public readonly ReadOnlySpan<T> AsReadOnlySpan<T>() where T : unmanaged
|
||||
{
|
||||
return SpanHelpers.AsReadOnlySpan<Buffer16, T>(ref Unsafe.AsRef(in this));
|
||||
}
|
||||
}
|
||||
}
|
148
Ryujinx.Common/Utilities/EmbeddedResources.cs
Normal file
148
Ryujinx.Common/Utilities/EmbeddedResources.cs
Normal file
|
@ -0,0 +1,148 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Common
|
||||
{
|
||||
public static class EmbeddedResources
|
||||
{
|
||||
private readonly static Assembly ResourceAssembly;
|
||||
|
||||
static EmbeddedResources()
|
||||
{
|
||||
ResourceAssembly = Assembly.GetAssembly(typeof(EmbeddedResources));
|
||||
}
|
||||
|
||||
public static byte[] Read(string filename)
|
||||
{
|
||||
var (assembly, path) = ResolveManifestPath(filename);
|
||||
|
||||
return Read(assembly, path);
|
||||
}
|
||||
|
||||
public static Task<byte[]> ReadAsync(string filename)
|
||||
{
|
||||
var (assembly, path) = ResolveManifestPath(filename);
|
||||
|
||||
return ReadAsync(assembly, path);
|
||||
}
|
||||
|
||||
public static byte[] Read(Assembly assembly, string filename)
|
||||
{
|
||||
using (var stream = GetStream(assembly, filename))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var mem = new MemoryStream())
|
||||
{
|
||||
stream.CopyTo(mem);
|
||||
|
||||
return mem.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async static Task<byte[]> ReadAsync(Assembly assembly, string filename)
|
||||
{
|
||||
using (var stream = GetStream(assembly, filename))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var mem = new MemoryStream())
|
||||
{
|
||||
await stream.CopyToAsync(mem);
|
||||
|
||||
return mem.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string ReadAllText(string filename)
|
||||
{
|
||||
var (assembly, path) = ResolveManifestPath(filename);
|
||||
|
||||
return ReadAllText(assembly, path);
|
||||
}
|
||||
|
||||
public static Task<string> ReadAllTextAsync(string filename)
|
||||
{
|
||||
var (assembly, path) = ResolveManifestPath(filename);
|
||||
|
||||
return ReadAllTextAsync(assembly, path);
|
||||
}
|
||||
|
||||
public static string ReadAllText(Assembly assembly, string filename)
|
||||
{
|
||||
using (var stream = GetStream(assembly, filename))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async static Task<string> ReadAllTextAsync(Assembly assembly, string filename)
|
||||
{
|
||||
using (var stream = GetStream(assembly, filename))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
return await reader.ReadToEndAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Stream GetStream(string filename)
|
||||
{
|
||||
var (assembly, _) = ResolveManifestPath(filename);
|
||||
|
||||
return GetStream(assembly, filename);
|
||||
}
|
||||
|
||||
public static Stream GetStream(Assembly assembly, string filename)
|
||||
{
|
||||
var namespace_ = assembly.GetName().Name;
|
||||
var manifestUri = namespace_ + "." + filename.Replace('/', '.');
|
||||
|
||||
var stream = assembly.GetManifestResourceStream(manifestUri);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
private static (Assembly, string) ResolveManifestPath(string filename)
|
||||
{
|
||||
var segments = filename.Split(new[] { '/' }, 2, StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (segments.Length >= 2)
|
||||
{
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
if (assembly.GetName().Name == segments[0])
|
||||
{
|
||||
return (assembly, segments[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (ResourceAssembly, filename);
|
||||
}
|
||||
}
|
||||
}
|
61
Ryujinx.Common/Utilities/SpanHelpers.cs
Normal file
61
Ryujinx.Common/Utilities/SpanHelpers.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Common.Utilities
|
||||
{
|
||||
public static class SpanHelpers
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<T> CreateSpan<T>(ref T reference, int length)
|
||||
{
|
||||
return MemoryMarshal.CreateSpan(ref reference, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<T> AsSpan<T>(ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateSpan(ref reference, 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<TSpan> AsSpan<TStruct, TSpan>(ref TStruct reference)
|
||||
where TStruct : unmanaged where TSpan : unmanaged
|
||||
{
|
||||
return CreateSpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
||||
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Span<byte> AsByteSpan<T>(ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateSpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<T> CreateReadOnlySpan<T>(ref T reference, int length)
|
||||
{
|
||||
return MemoryMarshal.CreateReadOnlySpan(ref reference, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<T> AsReadOnlySpan<T>(ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateReadOnlySpan(ref reference, 1);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<TSpan> AsReadOnlySpan<TStruct, TSpan>(ref TStruct reference)
|
||||
where TStruct : unmanaged where TSpan : unmanaged
|
||||
{
|
||||
return CreateReadOnlySpan(ref Unsafe.As<TStruct, TSpan>(ref reference),
|
||||
Unsafe.SizeOf<TStruct>() / Unsafe.SizeOf<TSpan>());
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ReadOnlySpan<byte> AsReadOnlyByteSpan<T>(ref T reference) where T : unmanaged
|
||||
{
|
||||
return CreateReadOnlySpan(ref Unsafe.As<T, byte>(ref reference), Unsafe.SizeOf<T>());
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics.GAL/AddressMode.cs
Normal file
14
Ryujinx.Graphics.GAL/AddressMode.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum AddressMode
|
||||
{
|
||||
Repeat,
|
||||
MirroredRepeat,
|
||||
ClampToEdge,
|
||||
ClampToBorder,
|
||||
Clamp,
|
||||
MirrorClampToEdge,
|
||||
MirrorClampToBorder,
|
||||
MirrorClamp
|
||||
}
|
||||
}
|
32
Ryujinx.Graphics.GAL/BlendDescriptor.cs
Normal file
32
Ryujinx.Graphics.GAL/BlendDescriptor.cs
Normal file
|
@ -0,0 +1,32 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
41
Ryujinx.Graphics.GAL/BlendFactor.cs
Normal file
41
Ryujinx.Graphics.GAL/BlendFactor.cs
Normal file
|
@ -0,0 +1,41 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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,
|
||||
|
||||
ZeroGl = 0x4000,
|
||||
OneGl = 0x4001,
|
||||
SrcColorGl = 0x4300,
|
||||
OneMinusSrcColorGl = 0x4301,
|
||||
SrcAlphaGl = 0x4302,
|
||||
OneMinusSrcAlphaGl = 0x4303,
|
||||
DstAlphaGl = 0x4304,
|
||||
OneMinusDstAlphaGl = 0x4305,
|
||||
DstColorGl = 0x4306,
|
||||
OneMinusDstColorGl = 0x4307,
|
||||
SrcAlphaSaturateGl = 0x4308,
|
||||
Src1ColorGl = 0xc900,
|
||||
OneMinusSrc1ColorGl = 0xc901,
|
||||
Src1AlphaGl = 0xc902,
|
||||
OneMinusSrc1AlphaGl = 0xc903
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.GAL/BlendOp.cs
Normal file
17
Ryujinx.Graphics.GAL/BlendOp.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum BlendOp
|
||||
{
|
||||
Add = 1,
|
||||
Subtract,
|
||||
ReverseSubtract,
|
||||
Minimum,
|
||||
Maximum,
|
||||
|
||||
AddGl = 0x8006,
|
||||
SubtractGl = 0x8007,
|
||||
ReverseSubtractGl = 0x8008,
|
||||
MinimumGl = 0x800a,
|
||||
MaximumGl = 0x800b
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
23
Ryujinx.Graphics.GAL/Capabilities.cs
Normal file
23
Ryujinx.Graphics.GAL/Capabilities.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public struct Capabilities
|
||||
{
|
||||
public bool SupportsAstcCompression { get; }
|
||||
public bool SupportsNonConstantTextureOffset { get; }
|
||||
|
||||
public int MaximumComputeSharedMemorySize { get; }
|
||||
public int StorageBufferOffsetAlignment { get; }
|
||||
|
||||
public Capabilities(
|
||||
bool supportsAstcCompression,
|
||||
bool supportsNonConstantTextureOffset,
|
||||
int maximumComputeSharedMemorySize,
|
||||
int storageBufferOffsetAlignment)
|
||||
{
|
||||
SupportsAstcCompression = supportsAstcCompression;
|
||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
|
||||
StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx.Graphics.GAL/ColorF.cs
Normal file
18
Ryujinx.Graphics.GAL/ColorF.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.GAL/CompareMode.cs
Normal file
8
Ryujinx.Graphics.GAL/CompareMode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum CompareMode
|
||||
{
|
||||
None,
|
||||
CompareRToTexture
|
||||
}
|
||||
}
|
23
Ryujinx.Graphics.GAL/CompareOp.cs
Normal file
23
Ryujinx.Graphics.GAL/CompareOp.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum CompareOp
|
||||
{
|
||||
Never = 1,
|
||||
Less,
|
||||
Equal,
|
||||
LessOrEqual,
|
||||
Greater,
|
||||
NotEqual,
|
||||
GreaterOrEqual,
|
||||
Always,
|
||||
|
||||
NeverGl = 0x200,
|
||||
LessGl = 0x201,
|
||||
EqualGl = 0x202,
|
||||
LessOrEqualGl = 0x203,
|
||||
GreaterGl = 0x204,
|
||||
NotEqualGl = 0x205,
|
||||
GreaterOrEqualGl = 0x206,
|
||||
AlwaysGl = 0x207,
|
||||
}
|
||||
}
|
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
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.GAL/DepthMode.cs
Normal file
8
Ryujinx.Graphics.GAL/DepthMode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum DepthMode
|
||||
{
|
||||
MinusOneToOne,
|
||||
ZeroToOne
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.GAL/DepthStencilMode.cs
Normal file
8
Ryujinx.Graphics.GAL/DepthStencilMode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum DepthStencilMode
|
||||
{
|
||||
Depth,
|
||||
Stencil
|
||||
}
|
||||
}
|
47
Ryujinx.Graphics.GAL/DepthStencilState.cs
Normal file
47
Ryujinx.Graphics.GAL/DepthStencilState.cs
Normal file
|
@ -0,0 +1,47 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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/DepthTestDescriptor.cs
Normal file
20
Ryujinx.Graphics.GAL/DepthTestDescriptor.cs
Normal file
|
@ -0,0 +1,20 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx.Graphics.GAL/Extents2D.cs
Normal file
18
Ryujinx.Graphics.GAL/Extents2D.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public struct Extents2D
|
||||
{
|
||||
public int X1 { get; }
|
||||
public int Y1 { get; }
|
||||
public int X2 { get; }
|
||||
public int Y2 { get; }
|
||||
|
||||
public Extents2D(int x1, int y1, int x2, int y2)
|
||||
{
|
||||
X1 = x1;
|
||||
Y1 = y1;
|
||||
X2 = x2;
|
||||
Y2 = y2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gal
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum GalCullFace
|
||||
public enum Face
|
||||
{
|
||||
Front = 0x404,
|
||||
Back = 0x405,
|
||||
FrontAndBack = 0x408
|
||||
}
|
||||
}
|
||||
}
|
218
Ryujinx.Graphics.GAL/Format.cs
Normal file
218
Ryujinx.Graphics.GAL/Format.cs
Normal file
|
@ -0,0 +1,218 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum Format
|
||||
{
|
||||
R8Unorm,
|
||||
R8Snorm,
|
||||
R8Uint,
|
||||
R8Sint,
|
||||
R16Float,
|
||||
R16Unorm,
|
||||
R16Snorm,
|
||||
R16Uint,
|
||||
R16Sint,
|
||||
R32Float,
|
||||
R32Uint,
|
||||
R32Sint,
|
||||
R8G8Unorm,
|
||||
R8G8Snorm,
|
||||
R8G8Uint,
|
||||
R8G8Sint,
|
||||
R16G16Float,
|
||||
R16G16Unorm,
|
||||
R16G16Snorm,
|
||||
R16G16Uint,
|
||||
R16G16Sint,
|
||||
R32G32Float,
|
||||
R32G32Uint,
|
||||
R32G32Sint,
|
||||
R8G8B8Unorm,
|
||||
R8G8B8Snorm,
|
||||
R8G8B8Uint,
|
||||
R8G8B8Sint,
|
||||
R16G16B16Float,
|
||||
R16G16B16Unorm,
|
||||
R16G16B16Snorm,
|
||||
R16G16B16Uint,
|
||||
R16G16B16Sint,
|
||||
R32G32B32Float,
|
||||
R32G32B32Uint,
|
||||
R32G32B32Sint,
|
||||
R8G8B8A8Unorm,
|
||||
R8G8B8A8Snorm,
|
||||
R8G8B8A8Uint,
|
||||
R8G8B8A8Sint,
|
||||
R16G16B16A16Float,
|
||||
R16G16B16A16Unorm,
|
||||
R16G16B16A16Snorm,
|
||||
R16G16B16A16Uint,
|
||||
R16G16B16A16Sint,
|
||||
R32G32B32A32Float,
|
||||
R32G32B32A32Uint,
|
||||
R32G32B32A32Sint,
|
||||
S8Uint,
|
||||
D16Unorm,
|
||||
D24X8Unorm,
|
||||
D32Float,
|
||||
D24UnormS8Uint,
|
||||
D32FloatS8Uint,
|
||||
R8G8B8X8Srgb,
|
||||
R8G8B8A8Srgb,
|
||||
R4G4B4A4Unorm,
|
||||
R5G5B5X1Unorm,
|
||||
R5G5B5A1Unorm,
|
||||
R5G6B5Unorm,
|
||||
R10G10B10A2Unorm,
|
||||
R10G10B10A2Uint,
|
||||
R11G11B10Float,
|
||||
R9G9B9E5Float,
|
||||
Bc1RgbUnorm,
|
||||
Bc1RgbaUnorm,
|
||||
Bc2Unorm,
|
||||
Bc3Unorm,
|
||||
Bc1RgbSrgb,
|
||||
Bc1RgbaSrgb,
|
||||
Bc2Srgb,
|
||||
Bc3Srgb,
|
||||
Bc4Unorm,
|
||||
Bc4Snorm,
|
||||
Bc5Unorm,
|
||||
Bc5Snorm,
|
||||
Bc7Unorm,
|
||||
Bc7Srgb,
|
||||
Bc6HSfloat,
|
||||
Bc6HUfloat,
|
||||
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);
|
||||
}
|
||||
}
|
71
Ryujinx.Graphics.GAL/IPipeline.cs
Normal file
71
Ryujinx.Graphics.GAL/IPipeline.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public interface IPipeline
|
||||
{
|
||||
void ClearRenderTargetColor(int index, uint componentMask, ColorF color);
|
||||
|
||||
void ClearRenderTargetDepthStencil(
|
||||
float depthValue,
|
||||
bool depthMask,
|
||||
int stencilValue,
|
||||
int stencilMask);
|
||||
|
||||
void DispatchCompute(int groupsX, int groupsY, int groupsZ);
|
||||
|
||||
void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance);
|
||||
void DrawIndexed(
|
||||
int indexCount,
|
||||
int instanceCount,
|
||||
int firstIndex,
|
||||
int firstVertex,
|
||||
int firstInstance);
|
||||
|
||||
void SetBlendState(int index, BlendDescriptor blend);
|
||||
|
||||
void SetBlendColor(ColorF color);
|
||||
|
||||
void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp);
|
||||
|
||||
void SetDepthMode(DepthMode mode);
|
||||
|
||||
void SetDepthTest(DepthTestDescriptor depthTest);
|
||||
|
||||
void SetFaceCulling(bool enable, Face face);
|
||||
|
||||
void SetFrontFace(FrontFace frontFace);
|
||||
|
||||
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
||||
|
||||
void SetImage(int index, ShaderStage stage, ITexture texture);
|
||||
|
||||
void SetPrimitiveRestart(bool enable, int index);
|
||||
|
||||
void SetPrimitiveTopology(PrimitiveTopology topology);
|
||||
|
||||
void SetProgram(IProgram program);
|
||||
|
||||
void SetRenderTargetColorMasks(uint[] componentMask);
|
||||
|
||||
void SetRenderTargets(ITexture[] colors, ITexture depthStencil);
|
||||
|
||||
void SetSampler(int index, ShaderStage stage, ISampler sampler);
|
||||
|
||||
void SetStencilTest(StencilTestDescriptor stencilTest);
|
||||
|
||||
void SetStorageBuffer(int index, ShaderStage stage, BufferRange buffer);
|
||||
|
||||
void SetTexture(int index, ShaderStage stage, ITexture texture);
|
||||
|
||||
void SetUniformBuffer(int index, ShaderStage stage, BufferRange buffer);
|
||||
|
||||
void SetVertexAttribs(VertexAttribDescriptor[] vertexAttribs);
|
||||
void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers);
|
||||
|
||||
void SetViewports(int first, Viewport[] viewports);
|
||||
|
||||
void TextureBarrier();
|
||||
void TextureBarrierTiled();
|
||||
}
|
||||
}
|
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 { }
|
||||
}
|
31
Ryujinx.Graphics.GAL/IRenderer.cs
Normal file
31
Ryujinx.Graphics.GAL/IRenderer.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public interface IRenderer : IDisposable
|
||||
{
|
||||
IPipeline Pipeline { 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 Initialize();
|
||||
|
||||
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 { }
|
||||
}
|
16
Ryujinx.Graphics.GAL/ITexture.cs
Normal file
16
Ryujinx.Graphics.GAL/ITexture.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public interface ITexture : IDisposable
|
||||
{
|
||||
void CopyTo(ITexture destination, int firstLayer, int firstLevel);
|
||||
void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter);
|
||||
|
||||
ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel);
|
||||
|
||||
byte[] GetData();
|
||||
|
||||
void SetData(Span<byte> data);
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.GAL/IWindow.cs
Normal file
9
Ryujinx.Graphics.GAL/IWindow.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public interface IWindow
|
||||
{
|
||||
void Present(ITexture texture, ImageCrop crop);
|
||||
|
||||
void SetSize(int width, int height);
|
||||
}
|
||||
}
|
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
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.GAL/MagFilter.cs
Normal file
8
Ryujinx.Graphics.GAL/MagFilter.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum MagFilter
|
||||
{
|
||||
Nearest = 1,
|
||||
Linear
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.GAL/MinFilter.cs
Normal file
12
Ryujinx.Graphics.GAL/MinFilter.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum MinFilter
|
||||
{
|
||||
Nearest = 1,
|
||||
Linear,
|
||||
NearestMipmapNearest,
|
||||
LinearMipmapNearest,
|
||||
NearestMipmapLinear,
|
||||
LinearMipmapLinear
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj
Normal file
13
Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj
Normal file
|
@ -0,0 +1,13 @@
|
|||
<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>
|
||||
<RuntimeIdentifiers>win-x64;osx-x64;linux-x64</RuntimeIdentifiers>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
50
Ryujinx.Graphics.GAL/SamplerCreateInfo.cs
Normal file
50
Ryujinx.Graphics.GAL/SamplerCreateInfo.cs
Normal file
|
@ -0,0 +1,50 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics.GAL/StencilOp.cs
Normal file
14
Ryujinx.Graphics.GAL/StencilOp.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum StencilOp
|
||||
{
|
||||
Keep = 1,
|
||||
Zero,
|
||||
Replace,
|
||||
IncrementAndClamp,
|
||||
DecrementAndClamp,
|
||||
Invert,
|
||||
IncrementAndWrap,
|
||||
DecrementAndWrap
|
||||
}
|
||||
}
|
56
Ryujinx.Graphics.GAL/StencilTestDescriptor.cs
Normal file
56
Ryujinx.Graphics.GAL/StencilTestDescriptor.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.GAL/SwizzleComponent.cs
Normal file
12
Ryujinx.Graphics.GAL/SwizzleComponent.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum SwizzleComponent
|
||||
{
|
||||
Zero,
|
||||
One,
|
||||
Red,
|
||||
Green,
|
||||
Blue,
|
||||
Alpha
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.GAL/Target.cs
Normal file
17
Ryujinx.Graphics.GAL/Target.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
public enum Target
|
||||
{
|
||||
Texture1D,
|
||||
Texture2D,
|
||||
Texture3D,
|
||||
Texture1DArray,
|
||||
Texture2DArray,
|
||||
Texture2DMultisample,
|
||||
Texture2DMultisampleArray,
|
||||
Rectangle,
|
||||
Cubemap,
|
||||
CubemapArray,
|
||||
TextureBuffer
|
||||
}
|
||||
}
|
120
Ryujinx.Graphics.GAL/TextureCreateInfo.cs
Normal file
120
Ryujinx.Graphics.GAL/TextureCreateInfo.cs
Normal file
|
@ -0,0 +1,120 @@
|
|||
using Ryujinx.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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 readonly int GetMipSize(int level)
|
||||
{
|
||||
return GetMipStride(level) * GetLevelHeight(level) * GetLevelDepth(level);
|
||||
}
|
||||
|
||||
public readonly int GetMipSize2D(int level)
|
||||
{
|
||||
return GetMipStride(level) * GetLevelHeight(level);
|
||||
}
|
||||
|
||||
public readonly int GetMipStride(int level)
|
||||
{
|
||||
return BitUtils.AlignUp(GetLevelWidth(level) * BytesPerPixel, 4);
|
||||
}
|
||||
|
||||
private readonly int GetLevelWidth(int level)
|
||||
{
|
||||
return BitUtils.DivRoundUp(GetLevelSize(Width, level), BlockWidth);
|
||||
}
|
||||
|
||||
private readonly int GetLevelHeight(int level)
|
||||
{
|
||||
return BitUtils.DivRoundUp(GetLevelSize(Height, level), BlockHeight);
|
||||
}
|
||||
|
||||
private readonly int GetLevelDepth(int level)
|
||||
{
|
||||
return Target == Target.Texture3D ? GetLevelSize(Depth, level) : GetLayers();
|
||||
}
|
||||
|
||||
public readonly int GetDepthOrLayers()
|
||||
{
|
||||
return Target == Target.Texture3D ? Depth : GetLayers();
|
||||
}
|
||||
|
||||
public readonly 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);
|
||||
}
|
17
Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs
Normal file
17
Ryujinx.Graphics.GAL/VertexAttribDescriptor.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs
Normal file
17
Ryujinx.Graphics.GAL/VertexBufferDescriptor.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
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
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics.Gpu/ClassId.cs
Normal file
14
Ryujinx.Graphics.Gpu/ClassId.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
/// <summary>
|
||||
/// GPU engine class ID.
|
||||
/// </summary>
|
||||
enum ClassId
|
||||
{
|
||||
Engine2D = 0x902d,
|
||||
Engine3D = 0xb197,
|
||||
EngineCompute = 0xb1c0,
|
||||
EngineInline2Memory = 0xa140,
|
||||
EngineDma = 0xb0b5
|
||||
}
|
||||
}
|
48
Ryujinx.Graphics.Gpu/Constants.cs
Normal file
48
Ryujinx.Graphics.Gpu/Constants.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
/// <summary>
|
||||
/// Common Maxwell GPU constants.
|
||||
/// </summary>
|
||||
static class Constants
|
||||
{
|
||||
/// <summary>
|
||||
/// Maximum number of compute uniform buffers.
|
||||
/// </summary>
|
||||
public const int TotalCpUniformBuffers = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of compute storage buffers (this is an API limitation).
|
||||
/// </summary>
|
||||
public const int TotalCpStorageBuffers = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of graphics uniform buffers.
|
||||
/// </summary>
|
||||
public const int TotalGpUniformBuffers = 18;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of graphics storage buffers (this is an API limitation).
|
||||
/// </summary>
|
||||
public const int TotalGpStorageBuffers = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of render target color buffers.
|
||||
/// </summary>
|
||||
public const int TotalRenderTargets = 8;
|
||||
|
||||
/// <summary>
|
||||
/// Number of shader stages.
|
||||
/// </summary>
|
||||
public const int ShaderStages = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of vertex buffers.
|
||||
/// </summary>
|
||||
public const int TotalVertexBuffers = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum number of viewports.
|
||||
/// </summary>
|
||||
public const int TotalViewports = 8;
|
||||
}
|
||||
}
|
|
@ -1,16 +1,21 @@
|
|||
using Ryujinx.Graphics.Memory;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
/// <summary>
|
||||
/// GPU DMA pusher, used to push commands to the GPU.
|
||||
/// </summary>
|
||||
public class DmaPusher
|
||||
{
|
||||
private ConcurrentQueue<(NvGpuVmm, long)> _ibBuffer;
|
||||
private ConcurrentQueue<ulong> _ibBuffer;
|
||||
|
||||
private long _dmaPut;
|
||||
private long _dmaGet;
|
||||
private ulong _dmaPut;
|
||||
private ulong _dmaGet;
|
||||
|
||||
/// <summary>
|
||||
/// Internal GPFIFO state.
|
||||
/// </summary>
|
||||
private struct DmaState
|
||||
{
|
||||
public int Method;
|
||||
|
@ -29,47 +34,64 @@ 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)
|
||||
/// <summary>
|
||||
/// Creates a new instance of the GPU DMA pusher.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that the pusher belongs to</param>
|
||||
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)
|
||||
/// <summary>
|
||||
/// Pushes a GPFIFO entry.
|
||||
/// </summary>
|
||||
/// <param name="entry">GPFIFO entry</param>
|
||||
public void Push(ulong entry)
|
||||
{
|
||||
_ibBuffer.Enqueue((vmm, entry));
|
||||
_ibBuffer.Enqueue(entry);
|
||||
|
||||
_event.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits until commands are pushed to the FIFO.
|
||||
/// </summary>
|
||||
/// <returns>True if commands were received, false if wait timed out</returns>
|
||||
public bool WaitForCommands()
|
||||
{
|
||||
return _event.WaitOne(8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes commands pushed to the FIFO.
|
||||
/// </summary>
|
||||
public void DispatchCalls()
|
||||
{
|
||||
while (Step());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes a single command on the FIFO.
|
||||
/// </summary>
|
||||
/// <returns>True if the FIFO still has commands to be processed, false otherwise</returns>
|
||||
private bool Step()
|
||||
{
|
||||
if (_dmaGet != _dmaPut)
|
||||
{
|
||||
int word = _vmm.ReadInt32(_dmaGet);
|
||||
int word = _context.MemoryAccessor.ReadInt32(_dmaGet);
|
||||
|
||||
_dmaGet += 4;
|
||||
|
||||
|
@ -148,20 +170,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
|
||||
{
|
||||
|
@ -171,6 +187,10 @@ namespace Ryujinx.Graphics
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets current non-immediate method call state.
|
||||
/// </summary>
|
||||
/// <param name="word">Compressed method word</param>
|
||||
private void SetNonImmediateState(int word)
|
||||
{
|
||||
_state.Method = (word >> 0) & 0x1fff;
|
||||
|
@ -178,9 +198,13 @@ namespace Ryujinx.Graphics
|
|||
_state.MethodCount = (word >> 16) & 0x1fff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forwards the method call to GPU engines.
|
||||
/// </summary>
|
||||
/// <param name="argument">Call argument</param>
|
||||
private void CallMethod(int argument)
|
||||
{
|
||||
_gpu.Fifo.CallMethod(_vmm, new GpuMethodCall(
|
||||
_context.Fifo.CallMethod(new MethodParams(
|
||||
_state.Method,
|
||||
argument,
|
||||
_state.SubChannel,
|
141
Ryujinx.Graphics.Gpu/Engine/Compute.cs
Normal file
141
Ryujinx.Graphics.Gpu/Engine/Compute.cs
Normal file
|
@ -0,0 +1,141 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
/// <summary>
|
||||
/// Dispatches compute work.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void Dispatch(GpuState state, int argument)
|
||||
{
|
||||
uint dispatchParamsAddress = (uint)state.Get<int>(MethodOffset.DispatchParamsAddress);
|
||||
|
||||
var dispatchParams = _context.MemoryAccessor.Read<ComputeParams>((ulong)dispatchParamsAddress << 8);
|
||||
|
||||
GpuVa shaderBaseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress);
|
||||
|
||||
ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)dispatchParams.ShaderOffset;
|
||||
|
||||
// Note: A size of 0 is also invalid, the size must be at least 1.
|
||||
int sharedMemorySize = Math.Clamp(dispatchParams.SharedMemorySize & 0xffff, 1, _context.Capabilities.MaximumComputeSharedMemorySize);
|
||||
|
||||
ComputeShader cs = ShaderCache.GetComputeShader(
|
||||
shaderGpuVa,
|
||||
sharedMemorySize,
|
||||
dispatchParams.UnpackBlockSizeX(),
|
||||
dispatchParams.UnpackBlockSizeY(),
|
||||
dispatchParams.UnpackBlockSizeZ());
|
||||
|
||||
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||
|
||||
var samplerPool = state.Get<PoolState>(MethodOffset.SamplerPoolState);
|
||||
|
||||
TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, dispatchParams.SamplerIndex);
|
||||
|
||||
var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
|
||||
|
||||
TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
|
||||
|
||||
TextureManager.SetComputeTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
|
||||
|
||||
ShaderProgramInfo info = cs.Shader.Program.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);
|
||||
|
||||
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.Type);
|
||||
|
||||
if (descriptor.IsBindless)
|
||||
{
|
||||
textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufOffset, descriptor.CbufSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
|
||||
}
|
||||
}
|
||||
|
||||
TextureManager.SetComputeTextures(textureBindings);
|
||||
|
||||
var imageBindings = new TextureBindingInfo[info.Images.Count];
|
||||
|
||||
for (int index = 0; index < info.Images.Count; index++)
|
||||
{
|
||||
var descriptor = info.Images[index];
|
||||
|
||||
Target target = GetTarget(descriptor.Type);
|
||||
|
||||
imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
|
||||
}
|
||||
|
||||
TextureManager.SetComputeImages(imageBindings);
|
||||
|
||||
BufferManager.CommitComputeBindings();
|
||||
TextureManager.CommitComputeBindings();
|
||||
|
||||
_context.Renderer.Pipeline.DispatchCompute(
|
||||
dispatchParams.UnpackGridSizeX(),
|
||||
dispatchParams.UnpackGridSizeY(),
|
||||
dispatchParams.UnpackGridSizeZ());
|
||||
|
||||
UpdateShaderState(state);
|
||||
}
|
||||
}
|
||||
}
|
173
Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs
Normal file
173
Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs
Normal file
|
@ -0,0 +1,173 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
/// <summary>
|
||||
/// Compute uniform buffer parameters.
|
||||
/// </summary>
|
||||
struct UniformBufferParams
|
||||
{
|
||||
public int AddressLow;
|
||||
public int AddressHighAndSize;
|
||||
|
||||
/// <summary>
|
||||
/// Packs the split address to a 64-bits integer.
|
||||
/// </summary>
|
||||
/// <returns>Uniform buffer GPU virtual address</returns>
|
||||
public ulong PackAddress()
|
||||
{
|
||||
return (uint)AddressLow | ((ulong)(AddressHighAndSize & 0xff) << 32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the uniform buffer size in bytes.
|
||||
/// </summary>
|
||||
/// <returns>Uniform buffer size in bytes</returns>
|
||||
public ulong UnpackSize()
|
||||
{
|
||||
return (ulong)((AddressHighAndSize >> 15) & 0x1ffff);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compute dispatch parameters.
|
||||
/// </summary>
|
||||
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 SamplerIndex SamplerIndex;
|
||||
public int GridSizeX;
|
||||
public int GridSizeYZ;
|
||||
public int Unknown14;
|
||||
public int Unknown15;
|
||||
public int Unknown16;
|
||||
public int SharedMemorySize;
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Uniform buffer parameters.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the work group X size.
|
||||
/// </summary>
|
||||
/// <returns>Work group X size</returns>
|
||||
public int UnpackGridSizeX()
|
||||
{
|
||||
return GridSizeX & 0x7fffffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the work group Y size.
|
||||
/// </summary>
|
||||
/// <returns>Work group Y size</returns>
|
||||
public int UnpackGridSizeY()
|
||||
{
|
||||
return GridSizeYZ & 0xffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the work group Z size.
|
||||
/// </summary>
|
||||
/// <returns>Work group Z size</returns>
|
||||
public int UnpackGridSizeZ()
|
||||
{
|
||||
return (GridSizeYZ >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the local group X size.
|
||||
/// </summary>
|
||||
/// <returns>Local group X size</returns>
|
||||
public int UnpackBlockSizeX()
|
||||
{
|
||||
return (BlockSizeX >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the local group Y size.
|
||||
/// </summary>
|
||||
/// <returns>Local group Y size</returns>
|
||||
public int UnpackBlockSizeY()
|
||||
{
|
||||
return BlockSizeYZ & 0xffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the local group Z size.
|
||||
/// </summary>
|
||||
/// <returns>Local group Z size</returns>
|
||||
public int UnpackBlockSizeZ()
|
||||
{
|
||||
return (BlockSizeYZ >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the uniform buffers enable mask.
|
||||
/// Each bit set on the mask indicates that the respective buffer index is enabled.
|
||||
/// </summary>
|
||||
/// <returns>Uniform buffers enable mask</returns>
|
||||
public uint UnpackUniformBuffersEnableMask()
|
||||
{
|
||||
return (uint)UniformBuffersConfig & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
130
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
Normal file
130
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
Normal file
|
@ -0,0 +1,130 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private Inline2MemoryParams _params;
|
||||
|
||||
private bool _isLinear;
|
||||
|
||||
private int _offset;
|
||||
private int _size;
|
||||
|
||||
private bool _finished;
|
||||
|
||||
private int[] _buffer;
|
||||
|
||||
/// <summary>
|
||||
/// Launches Inline-to-Memory engine DMA copy.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void LaunchDma(GpuState state, int argument)
|
||||
{
|
||||
_params = state.Get<Inline2MemoryParams>(MethodOffset.I2mParams);
|
||||
|
||||
_isLinear = (argument & 1) != 0;
|
||||
|
||||
_offset = 0;
|
||||
_size = _params.LineLengthIn * _params.LineCount;
|
||||
|
||||
int count = BitUtils.DivRoundUp(_size, 4);
|
||||
|
||||
if (_buffer == null || _buffer.Length < count)
|
||||
{
|
||||
_buffer = new int[count];
|
||||
}
|
||||
|
||||
ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack());
|
||||
|
||||
_context.Methods.TextureManager.Flush(dstBaseAddress, (ulong)_size);
|
||||
|
||||
_finished = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pushes a word of data to the Inline-to-Memory engine.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
public void LoadInlineData(GpuState state, int argument)
|
||||
{
|
||||
if (!_finished)
|
||||
{
|
||||
_buffer[_offset++] = argument;
|
||||
|
||||
if (_offset * 4 >= _size)
|
||||
{
|
||||
FinishTransfer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs actual copy of the inline data after the transfer is finished.
|
||||
/// </summary>
|
||||
private void FinishTransfer()
|
||||
{
|
||||
Span<byte> data = MemoryMarshal.Cast<int, byte>(_buffer).Slice(0, _size);
|
||||
|
||||
if (_isLinear && _params.LineCount == 1)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate( _params.DstAddress.Pack());
|
||||
|
||||
_context.PhysicalMemory.Write(address, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
var dstCalculator = new OffsetCalculator(
|
||||
_params.DstWidth,
|
||||
_params.DstHeight,
|
||||
_params.DstStride,
|
||||
_isLinear,
|
||||
_params.DstMemoryLayout.UnpackGobBlocksInY(),
|
||||
1);
|
||||
|
||||
int srcOffset = 0;
|
||||
|
||||
ulong dstBaseAddress = _context.MemoryManager.Translate(_params.DstAddress.Pack());
|
||||
|
||||
for (int y = _params.DstY; y < _params.DstY + _params.LineCount; y++)
|
||||
{
|
||||
int x1 = _params.DstX;
|
||||
int x2 = _params.DstX + _params.LineLengthIn;
|
||||
int x2Trunc = _params.DstX + BitUtils.AlignDown(_params.LineLengthIn, 16);
|
||||
|
||||
int x;
|
||||
|
||||
for (x = x1; x < x2Trunc; x += 16, srcOffset += 16)
|
||||
{
|
||||
int dstOffset = dstCalculator.GetOffset(x, y);
|
||||
|
||||
ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
|
||||
|
||||
Span<byte> pixel = data.Slice(srcOffset, 16);
|
||||
|
||||
_context.PhysicalMemory.Write(dstAddress, pixel);
|
||||
}
|
||||
|
||||
for (; x < x2; x++, srcOffset++)
|
||||
{
|
||||
int dstOffset = dstCalculator.GetOffset(x, y);
|
||||
|
||||
ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
|
||||
|
||||
Span<byte> pixel = data.Slice(srcOffset, 1);
|
||||
|
||||
_context.PhysicalMemory.Write(dstAddress, pixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_finished = true;
|
||||
}
|
||||
}
|
||||
}
|
62
Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
Normal file
62
Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
/// <summary>
|
||||
/// Clears the current color and depth-stencil buffers.
|
||||
/// Which buffers should be cleared is also specified on the argument.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void Clear(GpuState state, int argument)
|
||||
{
|
||||
UpdateRenderTargetState(state, useControl: false);
|
||||
|
||||
TextureManager.CommitGraphicsBindings();
|
||||
|
||||
bool clearDepth = (argument & 1) != 0;
|
||||
bool clearStencil = (argument & 2) != 0;
|
||||
|
||||
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||
|
||||
int index = (argument >> 6) & 0xf;
|
||||
|
||||
if (componentMask != 0)
|
||||
{
|
||||
var clearColor = state.Get<ClearColors>(MethodOffset.ClearColors);
|
||||
|
||||
ColorF color = new ColorF(
|
||||
clearColor.Red,
|
||||
clearColor.Green,
|
||||
clearColor.Blue,
|
||||
clearColor.Alpha);
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color);
|
||||
}
|
||||
|
||||
if (clearDepth || clearStencil)
|
||||
{
|
||||
float depthValue = state.Get<float>(MethodOffset.ClearDepthValue);
|
||||
int stencilValue = state.Get<int> (MethodOffset.ClearStencilValue);
|
||||
|
||||
int stencilMask = 0;
|
||||
|
||||
if (clearStencil)
|
||||
{
|
||||
stencilMask = state.Get<StencilTestState>(MethodOffset.StencilTestState).FrontMask;
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.ClearRenderTargetDepthStencil(
|
||||
depthValue,
|
||||
clearDepth,
|
||||
stencilValue,
|
||||
stencilMask);
|
||||
}
|
||||
|
||||
UpdateRenderTargetState(state, useControl: true);
|
||||
}
|
||||
}
|
||||
}
|
80
Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
Normal file
80
Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
Normal file
|
@ -0,0 +1,80 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs a buffer to buffer, or buffer to texture copy.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void CopyBuffer(GpuState state, int argument)
|
||||
{
|
||||
var cbp = state.Get<CopyBufferParams>(MethodOffset.CopyBufferParams);
|
||||
|
||||
var swizzle = 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 = state.Get<CopyBufferTexture>(MethodOffset.CopyBufferDstTexture);
|
||||
var src = 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs
Normal file
100
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
using Texture = Image.Texture;
|
||||
|
||||
partial class Methods
|
||||
{
|
||||
/// <summary>
|
||||
/// Performs a texture to texture copy.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void CopyTexture(GpuState state, int argument)
|
||||
{
|
||||
var dstCopyTexture = state.Get<CopyTexture>(MethodOffset.CopyDstTexture);
|
||||
var srcCopyTexture = state.Get<CopyTexture>(MethodOffset.CopySrcTexture);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Texture dstTexture = TextureManager.FindOrCreateTexture(dstCopyTexture);
|
||||
|
||||
if (dstTexture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var control = state.Get<CopyTextureControl>(MethodOffset.CopyTextureControl);
|
||||
|
||||
var region = state.Get<CopyRegion>(MethodOffset.CopyRegion);
|
||||
|
||||
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);
|
||||
|
||||
// For an out of bounds copy, we must ensure that the copy wraps to the next line,
|
||||
// so for a copy from a 64x64 texture, in the region [32, 96[, there are 32 pixels that are
|
||||
// outside the bounds of the texture. We fill the destination with the first 32 pixels
|
||||
// of the next line on the source texture.
|
||||
// This can be emulated with 2 copies (the first copy handles the region inside the bounds,
|
||||
// the second handles the region outside of the bounds).
|
||||
// We must also extend the source texture by one line to ensure we can wrap on the last line.
|
||||
// This is required by the (guest) OpenGL driver.
|
||||
if (srcRegion.X2 > srcTexture.Info.Width)
|
||||
{
|
||||
srcCopyTexture.Height++;
|
||||
|
||||
srcTexture = TextureManager.FindOrCreateTexture(srcCopyTexture);
|
||||
|
||||
srcRegion = new Extents2D(
|
||||
srcRegion.X1 - srcTexture.Info.Width,
|
||||
srcRegion.Y1 + 1,
|
||||
srcRegion.X2 - srcTexture.Info.Width,
|
||||
srcRegion.Y2 + 1);
|
||||
|
||||
srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);
|
||||
}
|
||||
|
||||
dstTexture.Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
169
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
Normal file
169
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
Normal file
|
@ -0,0 +1,169 @@
|
|||
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 _instancedDrawPending;
|
||||
private bool _instancedIndexed;
|
||||
|
||||
private int _instancedFirstIndex;
|
||||
private int _instancedFirstVertex;
|
||||
private int _instancedFirstInstance;
|
||||
private int _instancedIndexCount;
|
||||
private int _instancedDrawStateFirst;
|
||||
private int _instancedDrawStateCount;
|
||||
|
||||
private int _instanceIndex;
|
||||
|
||||
/// <summary>
|
||||
/// Primitive type of the current draw.
|
||||
/// </summary>
|
||||
public PrimitiveType PrimitiveType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Finishes draw call.
|
||||
/// This draws geometry on the bound buffers based on the current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawEnd(GpuState state, int argument)
|
||||
{
|
||||
if (_instancedDrawPending)
|
||||
{
|
||||
_drawIndexed = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateState(state);
|
||||
|
||||
bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;
|
||||
|
||||
if (instanced)
|
||||
{
|
||||
_instancedDrawPending = true;
|
||||
|
||||
_instancedIndexed = _drawIndexed;
|
||||
|
||||
_instancedFirstIndex = _firstIndex;
|
||||
_instancedFirstVertex = state.Get<int>(MethodOffset.FirstVertex);
|
||||
_instancedFirstInstance = state.Get<int>(MethodOffset.FirstInstance);
|
||||
|
||||
_instancedIndexCount = _indexCount;
|
||||
|
||||
var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
|
||||
|
||||
_instancedDrawStateFirst = drawState.First;
|
||||
_instancedDrawStateCount = drawState.Count;
|
||||
|
||||
_drawIndexed = false;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int firstInstance = state.Get<int>(MethodOffset.FirstInstance);
|
||||
|
||||
if (_drawIndexed)
|
||||
{
|
||||
_drawIndexed = false;
|
||||
|
||||
int firstVertex = state.Get<int>(MethodOffset.FirstVertex);
|
||||
|
||||
_context.Renderer.Pipeline.DrawIndexed(
|
||||
_indexCount,
|
||||
1,
|
||||
_firstIndex,
|
||||
firstVertex,
|
||||
firstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
|
||||
|
||||
_context.Renderer.Pipeline.Draw(
|
||||
drawState.Count,
|
||||
1,
|
||||
drawState.First,
|
||||
firstInstance);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts draw.
|
||||
/// This sets primitive type and instanced draw parameters.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void DrawBegin(GpuState state, int argument)
|
||||
{
|
||||
if ((argument & (1 << 26)) != 0)
|
||||
{
|
||||
_instanceIndex++;
|
||||
}
|
||||
else if ((argument & (1 << 27)) == 0)
|
||||
{
|
||||
PerformDeferredDraws();
|
||||
|
||||
_instanceIndex = 0;
|
||||
}
|
||||
|
||||
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
|
||||
|
||||
_context.Renderer.Pipeline.SetPrimitiveTopology(type.Convert());
|
||||
|
||||
PrimitiveType = type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the index buffer count.
|
||||
/// This also sets internal state that indicates that the next draw is an indexed draw.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void SetIndexBufferCount(GpuState state, int argument)
|
||||
{
|
||||
_drawIndexed = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Perform any deferred draws.
|
||||
/// This is used for instanced draws.
|
||||
/// Since each instance is a separate draw, we defer the draw and accumulate the instance count.
|
||||
/// Once we detect the last instanced draw, then we perform the host instanced draw,
|
||||
/// with the accumulated instance count.
|
||||
/// </summary>
|
||||
public void PerformDeferredDraws()
|
||||
{
|
||||
// Perform any pending instanced draw.
|
||||
if (_instancedDrawPending)
|
||||
{
|
||||
_instancedDrawPending = false;
|
||||
|
||||
if (_instancedIndexed)
|
||||
{
|
||||
_context.Renderer.Pipeline.DrawIndexed(
|
||||
_instancedIndexCount,
|
||||
_instanceIndex + 1,
|
||||
_instancedFirstIndex,
|
||||
_instancedFirstVertex,
|
||||
_instancedFirstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.Draw(
|
||||
_instancedDrawStateCount,
|
||||
_instanceIndex + 1,
|
||||
_instancedDrawStateFirst,
|
||||
_instancedFirstInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
129
Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
Normal file
129
Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
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 const int NsToTicksFractionNumerator = 384;
|
||||
private const int NsToTicksFractionDenominator = 625;
|
||||
|
||||
private ulong _runningCounter;
|
||||
|
||||
/// <summary>
|
||||
/// Writes a GPU counter to guest memory.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void Report(GpuState state, int argument)
|
||||
{
|
||||
ReportMode mode = (ReportMode)(argument & 3);
|
||||
|
||||
ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case ReportMode.Semaphore: ReportSemaphore(state); break;
|
||||
case ReportMode.Counter: ReportCounter(state, type); break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a GPU semaphore value to guest memory.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void ReportSemaphore(GpuState state)
|
||||
{
|
||||
var rs = state.Get<ReportState>(MethodOffset.ReportState);
|
||||
|
||||
_context.MemoryAccessor.Write(rs.Address.Pack(), rs.Payload);
|
||||
|
||||
_context.AdvanceSequence();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Packed GPU counter data (including GPU timestamp) in memory.
|
||||
/// </summary>
|
||||
private struct CounterData
|
||||
{
|
||||
public ulong Counter;
|
||||
public ulong Timestamp;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes a GPU counter to guest memory.
|
||||
/// This also writes the current timestamp value.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="type">Counter to be written to memory</param>
|
||||
private void ReportCounter(GpuState state, 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);
|
||||
|
||||
var rs = state.Get<ReportState>(MethodOffset.ReportState);
|
||||
|
||||
_context.MemoryAccessor.Write(rs.Address.Pack(), data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a nanoseconds timestamp value to Maxwell time ticks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The frequency is 614400000 Hz.
|
||||
/// </remarks>
|
||||
/// <param name="nanoseconds">Timestamp in nanoseconds</param>
|
||||
/// <returns>Maxwell ticks</returns>
|
||||
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 / NsToTicksFractionDenominator;
|
||||
|
||||
ulong rounded = divided * NsToTicksFractionDenominator;
|
||||
|
||||
ulong errorBias = (nanoseconds - rounded) * NsToTicksFractionNumerator / NsToTicksFractionDenominator;
|
||||
|
||||
return divided * NsToTicksFractionNumerator + errorBias;
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
Normal file
31
Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
/// <summary>
|
||||
/// Resets the value of an internal GPU counter back to zero.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void ResetCounter(GpuState state, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
Normal file
83
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the vertex shader stage.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void UniformBufferBindVertex(GpuState state, int argument)
|
||||
{
|
||||
UniformBufferBind(state, argument, ShaderType.Vertex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the tessellation control shader stage.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void UniformBufferBindTessControl(GpuState state, int argument)
|
||||
{
|
||||
UniformBufferBind(state, argument, ShaderType.TessellationControl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the tessellation evaluation shader stage.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void UniformBufferBindTessEvaluation(GpuState state, int argument)
|
||||
{
|
||||
UniformBufferBind(state, argument, ShaderType.TessellationEvaluation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the geometry shader stage.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void UniformBufferBindGeometry(GpuState state, int argument)
|
||||
{
|
||||
UniformBufferBind(state, argument, ShaderType.Geometry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a uniform buffer for the fragment shader stage.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
private void UniformBufferBindFragment(GpuState state, int argument)
|
||||
{
|
||||
UniformBufferBind(state, argument, ShaderType.Fragment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///Binds a uniform buffer for the specified shader stage.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">Method call argument</param>
|
||||
/// <param name="type">Shader stage that will access the uniform buffer</param>
|
||||
private void UniformBufferBind(GpuState state, int argument, ShaderType type)
|
||||
{
|
||||
bool enable = (argument & 1) != 0;
|
||||
|
||||
int index = (argument >> 4) & 0x1f;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
|
||||
|
||||
ulong address = uniformBuffer.Address.Pack();
|
||||
|
||||
BufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
Normal file
23
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
/// <summary>
|
||||
/// Updates the uniform buffer data with inline data.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="argument">New uniform buffer data word</param>
|
||||
private void UniformBufferUpdate(GpuState state, int argument)
|
||||
{
|
||||
var uniformBuffer = state.Get<UniformBufferState>(MethodOffset.UniformBufferState);
|
||||
|
||||
_context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument);
|
||||
|
||||
state.SetUniformBufferOffset(uniformBuffer.Offset + 4);
|
||||
|
||||
_context.AdvanceSequence();
|
||||
}
|
||||
}
|
||||
}
|
890
Ryujinx.Graphics.Gpu/Engine/Methods.cs
Normal file
890
Ryujinx.Graphics.Gpu/Engine/Methods.cs
Normal file
|
@ -0,0 +1,890 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.Shader;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
using Texture = Image.Texture;
|
||||
|
||||
/// <summary>
|
||||
/// GPU method implementations.
|
||||
/// </summary>
|
||||
partial class Methods
|
||||
{
|
||||
private readonly GpuContext _context;
|
||||
private readonly ShaderProgramInfo[] _currentProgramInfo;
|
||||
|
||||
/// <summary>
|
||||
/// In-memory shader cache.
|
||||
/// </summary>
|
||||
public ShaderCache ShaderCache { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GPU buffer manager.
|
||||
/// </summary>
|
||||
public BufferManager BufferManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GPU texture manager.
|
||||
/// </summary>
|
||||
public TextureManager TextureManager { get; }
|
||||
|
||||
private bool _isAnyVbInstanced;
|
||||
private bool _vsUsesInstanceId;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the GPU methods class.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
public Methods(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
ShaderCache = new ShaderCache(_context);
|
||||
|
||||
_currentProgramInfo = new ShaderProgramInfo[Constants.ShaderStages];
|
||||
|
||||
BufferManager = new BufferManager(context);
|
||||
TextureManager = new TextureManager(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register callback for GPU method calls that triggers an action on the GPU.
|
||||
/// </summary>
|
||||
/// <param name="state">GPU state where the triggers will be registered</param>
|
||||
public void RegisterCallbacks(GpuState state)
|
||||
{
|
||||
state.RegisterCallback(MethodOffset.LaunchDma, LaunchDma);
|
||||
state.RegisterCallback(MethodOffset.LoadInlineData, LoadInlineData);
|
||||
|
||||
state.RegisterCallback(MethodOffset.Dispatch, Dispatch);
|
||||
|
||||
state.RegisterCallback(MethodOffset.CopyBuffer, CopyBuffer);
|
||||
state.RegisterCallback(MethodOffset.CopyTexture, CopyTexture);
|
||||
|
||||
state.RegisterCallback(MethodOffset.TextureBarrier, TextureBarrier);
|
||||
state.RegisterCallback(MethodOffset.InvalidateTextures, InvalidateTextures);
|
||||
state.RegisterCallback(MethodOffset.TextureBarrierTiled, TextureBarrierTiled);
|
||||
|
||||
state.RegisterCallback(MethodOffset.ResetCounter, ResetCounter);
|
||||
|
||||
state.RegisterCallback(MethodOffset.DrawEnd, DrawEnd);
|
||||
state.RegisterCallback(MethodOffset.DrawBegin, DrawBegin);
|
||||
|
||||
state.RegisterCallback(MethodOffset.IndexBufferCount, SetIndexBufferCount);
|
||||
|
||||
state.RegisterCallback(MethodOffset.Clear, Clear);
|
||||
|
||||
state.RegisterCallback(MethodOffset.Report, Report);
|
||||
|
||||
state.RegisterCallback(MethodOffset.UniformBufferUpdateData, 16, UniformBufferUpdate);
|
||||
|
||||
state.RegisterCallback(MethodOffset.UniformBufferBindVertex, UniformBufferBindVertex);
|
||||
state.RegisterCallback(MethodOffset.UniformBufferBindTessControl, UniformBufferBindTessControl);
|
||||
state.RegisterCallback(MethodOffset.UniformBufferBindTessEvaluation, UniformBufferBindTessEvaluation);
|
||||
state.RegisterCallback(MethodOffset.UniformBufferBindGeometry, UniformBufferBindGeometry);
|
||||
state.RegisterCallback(MethodOffset.UniformBufferBindFragment, UniformBufferBindFragment);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host state based on the current guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Guest GPU state</param>
|
||||
private void UpdateState(GpuState state)
|
||||
{
|
||||
// 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 (state.QueryModified(MethodOffset.ShaderBaseAddress, MethodOffset.ShaderState))
|
||||
{
|
||||
UpdateShaderState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.RtColorState,
|
||||
MethodOffset.RtDepthStencilState,
|
||||
MethodOffset.RtControl,
|
||||
MethodOffset.RtDepthStencilSize,
|
||||
MethodOffset.RtDepthStencilEnable))
|
||||
{
|
||||
UpdateRenderTargetState(state, useControl: true);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.DepthTestEnable,
|
||||
MethodOffset.DepthWriteEnable,
|
||||
MethodOffset.DepthTestFunc))
|
||||
{
|
||||
UpdateDepthTestState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.DepthMode, MethodOffset.ViewportTransform, MethodOffset.ViewportExtents))
|
||||
{
|
||||
UpdateViewportTransform(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.DepthBiasState,
|
||||
MethodOffset.DepthBiasFactor,
|
||||
MethodOffset.DepthBiasUnits,
|
||||
MethodOffset.DepthBiasClamp))
|
||||
{
|
||||
UpdateDepthBiasState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.StencilBackMasks,
|
||||
MethodOffset.StencilTestState,
|
||||
MethodOffset.StencilBackTestState))
|
||||
{
|
||||
UpdateStencilTestState(state);
|
||||
}
|
||||
|
||||
// Pools.
|
||||
if (state.QueryModified(MethodOffset.SamplerPoolState, MethodOffset.SamplerIndex))
|
||||
{
|
||||
UpdateSamplerPoolState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.TexturePoolState))
|
||||
{
|
||||
UpdateTexturePoolState(state);
|
||||
}
|
||||
|
||||
// Input assembler state.
|
||||
if (state.QueryModified(MethodOffset.VertexAttribState))
|
||||
{
|
||||
UpdateVertexAttribState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.PrimitiveRestartState))
|
||||
{
|
||||
UpdatePrimitiveRestartState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.IndexBufferState))
|
||||
{
|
||||
UpdateIndexBufferState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.VertexBufferDrawState,
|
||||
MethodOffset.VertexBufferInstanced,
|
||||
MethodOffset.VertexBufferState,
|
||||
MethodOffset.VertexBufferEndAddress))
|
||||
{
|
||||
UpdateVertexBufferState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.FaceState))
|
||||
{
|
||||
UpdateFaceState(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.RtColorMaskShared, MethodOffset.RtColorMask))
|
||||
{
|
||||
UpdateRtColorMask(state);
|
||||
}
|
||||
|
||||
if (state.QueryModified(MethodOffset.BlendIndependent,
|
||||
MethodOffset.BlendStateCommon,
|
||||
MethodOffset.BlendEnableCommon,
|
||||
MethodOffset.BlendEnable,
|
||||
MethodOffset.BlendState))
|
||||
{
|
||||
UpdateBlendState(state);
|
||||
}
|
||||
|
||||
CommitBindings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
/// </summary>
|
||||
private void CommitBindings()
|
||||
{
|
||||
UpdateStorageBuffers();
|
||||
|
||||
BufferManager.CommitBindings();
|
||||
TextureManager.CommitGraphicsBindings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates storage buffer bindings.
|
||||
/// </summary>
|
||||
private void UpdateStorageBuffers()
|
||||
{
|
||||
for (int stage = 0; stage < _currentProgramInfo.Length; stage++)
|
||||
{
|
||||
ShaderProgramInfo info = _currentProgramInfo[stage];
|
||||
|
||||
if (info == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||
{
|
||||
BufferDescriptor sb = info.SBuffers[index];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates render targets (color and depth-stencil buffers) based on current render target state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="useControl">Use draw buffers information from render target control register</param>
|
||||
private void UpdateRenderTargetState(GpuState state, bool useControl)
|
||||
{
|
||||
var rtControl = state.Get<RtControl>(MethodOffset.RtControl);
|
||||
|
||||
int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets;
|
||||
|
||||
var msaaMode = state.Get<TextureMsaaMode>(MethodOffset.RtMsaaMode);
|
||||
|
||||
int samplesInX = msaaMode.SamplesInX();
|
||||
int samplesInY = msaaMode.SamplesInY();
|
||||
|
||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||
{
|
||||
int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index;
|
||||
|
||||
var colorState = state.Get<RtColorState>(MethodOffset.RtColorState, rtIndex);
|
||||
|
||||
if (index >= count || !IsRtEnabled(colorState))
|
||||
{
|
||||
TextureManager.SetRenderTargetColor(index, null);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Texture color = TextureManager.FindOrCreateTexture(colorState, samplesInX, samplesInY);
|
||||
|
||||
TextureManager.SetRenderTargetColor(index, color);
|
||||
|
||||
if (color != null)
|
||||
{
|
||||
color.Modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool dsEnable = state.Get<Boolean32>(MethodOffset.RtDepthStencilEnable);
|
||||
|
||||
Texture depthStencil = null;
|
||||
|
||||
if (dsEnable)
|
||||
{
|
||||
var dsState = state.Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
|
||||
var dsSize = state.Get<Size3D> (MethodOffset.RtDepthStencilSize);
|
||||
|
||||
depthStencil = TextureManager.FindOrCreateTexture(dsState, dsSize, samplesInX, samplesInY);
|
||||
}
|
||||
|
||||
TextureManager.SetRenderTargetDepthStencil(depthStencil);
|
||||
|
||||
if (depthStencil != null)
|
||||
{
|
||||
depthStencil.Modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a render target color buffer is used.
|
||||
/// </summary>
|
||||
/// <param name="colorState">Color buffer information</param>
|
||||
/// <returns>True if the specified buffer is enabled/used, false otherwise</returns>
|
||||
private static bool IsRtEnabled(RtColorState colorState)
|
||||
{
|
||||
// Colors are disabled by writing 0 to the format.
|
||||
return colorState.Format != 0 && colorState.WidthOrStride != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host depth test state based on current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateDepthTestState(GpuState state)
|
||||
{
|
||||
_context.Renderer.Pipeline.SetDepthTest(new DepthTestDescriptor(
|
||||
state.Get<Boolean32>(MethodOffset.DepthTestEnable),
|
||||
state.Get<Boolean32>(MethodOffset.DepthWriteEnable),
|
||||
state.Get<CompareOp>(MethodOffset.DepthTestFunc)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host viewport transform and clipping state based on current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateViewportTransform(GpuState state)
|
||||
{
|
||||
DepthMode depthMode = state.Get<DepthMode>(MethodOffset.DepthMode);
|
||||
|
||||
_context.Renderer.Pipeline.SetDepthMode(depthMode);
|
||||
|
||||
bool flipY = (state.Get<int>(MethodOffset.YControl) & 1) != 0;
|
||||
|
||||
float yFlip = flipY ? -1 : 1;
|
||||
|
||||
Viewport[] viewports = new Viewport[Constants.TotalViewports];
|
||||
|
||||
for (int index = 0; index < Constants.TotalViewports; index++)
|
||||
{
|
||||
var transform = state.Get<ViewportTransform>(MethodOffset.ViewportTransform, index);
|
||||
var extents = state.Get<ViewportExtents> (MethodOffset.ViewportExtents, index);
|
||||
|
||||
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 * yFlip;
|
||||
|
||||
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.Pipeline.SetViewports(0, viewports);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host depth bias (also called polygon offset) state based on current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateDepthBiasState(GpuState state)
|
||||
{
|
||||
var depthBias = state.Get<DepthBiasState>(MethodOffset.DepthBiasState);
|
||||
|
||||
float factor = state.Get<float>(MethodOffset.DepthBiasFactor);
|
||||
float units = state.Get<float>(MethodOffset.DepthBiasUnits);
|
||||
float clamp = state.Get<float>(MethodOffset.DepthBiasClamp);
|
||||
|
||||
PolygonModeMask enables;
|
||||
|
||||
enables = (depthBias.PointEnable ? PolygonModeMask.Point : 0);
|
||||
enables |= (depthBias.LineEnable ? PolygonModeMask.Line : 0);
|
||||
enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0);
|
||||
|
||||
_context.Renderer.Pipeline.SetDepthBias(enables, factor, units, clamp);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host stencil test state based on current GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateStencilTestState(GpuState state)
|
||||
{
|
||||
var backMasks = state.Get<StencilBackMasks> (MethodOffset.StencilBackMasks);
|
||||
var test = state.Get<StencilTestState> (MethodOffset.StencilTestState);
|
||||
var backTest = state.Get<StencilBackTestState>(MethodOffset.StencilBackTestState);
|
||||
|
||||
CompareOp backFunc;
|
||||
StencilOp backSFail;
|
||||
StencilOp backDpPass;
|
||||
StencilOp backDpFail;
|
||||
int backFuncRef;
|
||||
int backFuncMask;
|
||||
int backMask;
|
||||
|
||||
if (backTest.TwoSided)
|
||||
{
|
||||
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.Pipeline.SetStencilTest(new StencilTestDescriptor(
|
||||
test.Enable,
|
||||
test.FrontFunc,
|
||||
test.FrontSFail,
|
||||
test.FrontDpPass,
|
||||
test.FrontDpFail,
|
||||
test.FrontFuncRef,
|
||||
test.FrontFuncMask,
|
||||
test.FrontMask,
|
||||
backFunc,
|
||||
backSFail,
|
||||
backDpPass,
|
||||
backDpFail,
|
||||
backFuncRef,
|
||||
backFuncMask,
|
||||
backMask));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates current sampler pool address and size based on guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateSamplerPoolState(GpuState state)
|
||||
{
|
||||
var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
|
||||
var samplerPool = state.Get<PoolState>(MethodOffset.SamplerPoolState);
|
||||
|
||||
var samplerIndex = state.Get<SamplerIndex>(MethodOffset.SamplerIndex);
|
||||
|
||||
int maximumId = samplerIndex == SamplerIndex.ViaHeaderIndex
|
||||
? texturePool.MaximumId
|
||||
: samplerPool.MaximumId;
|
||||
|
||||
TextureManager.SetGraphicsSamplerPool(samplerPool.Address.Pack(), maximumId, samplerIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates current texture pool address and size based on guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateTexturePoolState(GpuState state)
|
||||
{
|
||||
var texturePool = state.Get<PoolState>(MethodOffset.TexturePoolState);
|
||||
|
||||
TextureManager.SetGraphicsTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
|
||||
|
||||
TextureManager.SetGraphicsTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host vertex attributes based on guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateVertexAttribState(GpuState state)
|
||||
{
|
||||
VertexAttribDescriptor[] vertexAttribs = new VertexAttribDescriptor[16];
|
||||
|
||||
for (int index = 0; index < 16; index++)
|
||||
{
|
||||
var vertexAttrib = state.Get<VertexAttribState>(MethodOffset.VertexAttribState, index);
|
||||
|
||||
if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
|
||||
{
|
||||
Logger.PrintDebug(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
|
||||
|
||||
format = Format.R32G32B32A32Float;
|
||||
}
|
||||
|
||||
vertexAttribs[index] = new VertexAttribDescriptor(
|
||||
vertexAttrib.UnpackBufferIndex(),
|
||||
vertexAttrib.UnpackOffset(),
|
||||
format);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetVertexAttribs(vertexAttribs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host primitive restart based on guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdatePrimitiveRestartState(GpuState state)
|
||||
{
|
||||
PrimitiveRestartState primitiveRestart = state.Get<PrimitiveRestartState>(MethodOffset.PrimitiveRestartState);
|
||||
|
||||
_context.Renderer.Pipeline.SetPrimitiveRestart(
|
||||
primitiveRestart.Enable,
|
||||
primitiveRestart.Index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host index buffer binding based on guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateIndexBufferState(GpuState state)
|
||||
{
|
||||
var indexBuffer = state.Get<IndexBufferState>(MethodOffset.IndexBufferState);
|
||||
|
||||
_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(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host vertex buffer bindings based on guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateVertexBufferState(GpuState state)
|
||||
{
|
||||
_isAnyVbInstanced = false;
|
||||
|
||||
for (int index = 0; index < 16; index++)
|
||||
{
|
||||
var vertexBuffer = state.Get<VertexBufferState>(MethodOffset.VertexBufferState, index);
|
||||
|
||||
if (!vertexBuffer.UnpackEnable())
|
||||
{
|
||||
BufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
GpuVa endAddress = state.Get<GpuVa>(MethodOffset.VertexBufferEndAddress, index);
|
||||
|
||||
ulong address = vertexBuffer.Address.Pack();
|
||||
|
||||
int stride = vertexBuffer.UnpackStride();
|
||||
|
||||
bool instanced = state.Get<Boolean32>(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 = state.Get<int>(MethodOffset.FirstInstance);
|
||||
|
||||
var drawState = state.Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
|
||||
|
||||
size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride);
|
||||
}
|
||||
|
||||
BufferManager.SetVertexBuffer(index, address, size, stride, divisor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host face culling and orientation based on guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateFaceState(GpuState state)
|
||||
{
|
||||
var face = state.Get<FaceState>(MethodOffset.FaceState);
|
||||
|
||||
_context.Renderer.Pipeline.SetFaceCulling(face.CullEnable, face.CullFace);
|
||||
|
||||
_context.Renderer.Pipeline.SetFrontFace(face.FrontFace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host render target color masks, based on guest GPU state.
|
||||
/// This defines which color channels are written to each color buffer.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateRtColorMask(GpuState state)
|
||||
{
|
||||
bool rtColorMaskShared = state.Get<Boolean32>(MethodOffset.RtColorMaskShared);
|
||||
|
||||
uint[] componentMasks = new uint[Constants.TotalRenderTargets];
|
||||
|
||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||
{
|
||||
var colorMask = state.Get<RtColorMask>(MethodOffset.RtColorMask, rtColorMaskShared ? 0 : index);
|
||||
|
||||
uint componentMask;
|
||||
|
||||
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.Pipeline.SetRenderTargetColorMasks(componentMasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host render target color buffer blending state, based on guest state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateBlendState(GpuState state)
|
||||
{
|
||||
bool blendIndependent = state.Get<Boolean32>(MethodOffset.BlendIndependent);
|
||||
|
||||
for (int index = 0; index < 8; index++)
|
||||
{
|
||||
BlendDescriptor descriptor;
|
||||
|
||||
if (blendIndependent)
|
||||
{
|
||||
bool enable = state.Get<Boolean32> (MethodOffset.BlendEnable, index);
|
||||
var blend = state.Get<BlendState>(MethodOffset.BlendState, index);
|
||||
|
||||
descriptor = new BlendDescriptor(
|
||||
enable,
|
||||
blend.ColorOp,
|
||||
blend.ColorSrcFactor,
|
||||
blend.ColorDstFactor,
|
||||
blend.AlphaOp,
|
||||
blend.AlphaSrcFactor,
|
||||
blend.AlphaDstFactor);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool enable = state.Get<Boolean32> (MethodOffset.BlendEnable, 0);
|
||||
var blend = state.Get<BlendStateCommon>(MethodOffset.BlendStateCommon);
|
||||
|
||||
descriptor = new BlendDescriptor(
|
||||
enable,
|
||||
blend.ColorOp,
|
||||
blend.ColorSrcFactor,
|
||||
blend.ColorDstFactor,
|
||||
blend.AlphaOp,
|
||||
blend.AlphaSrcFactor,
|
||||
blend.AlphaDstFactor);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetBlendState(index, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Storage buffer address and size information.
|
||||
/// </summary>
|
||||
private struct SbDescriptor
|
||||
{
|
||||
public uint AddressLow;
|
||||
public uint AddressHigh;
|
||||
public int Size;
|
||||
public int Padding;
|
||||
|
||||
public ulong PackAddress()
|
||||
{
|
||||
return AddressLow | ((ulong)AddressHigh << 32);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates host shaders based on the guest GPU state.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
private void UpdateShaderState(GpuState state)
|
||||
{
|
||||
ShaderAddresses addresses = new ShaderAddresses();
|
||||
|
||||
Span<ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);
|
||||
|
||||
Span<ulong> addressesArray = MemoryMarshal.Cast<ShaderAddresses, ulong>(addressesSpan);
|
||||
|
||||
ulong baseAddress = state.Get<GpuVa>(MethodOffset.ShaderBaseAddress).Pack();
|
||||
|
||||
for (int index = 0; index < 6; index++)
|
||||
{
|
||||
var shader = state.Get<ShaderState>(MethodOffset.ShaderState, index);
|
||||
|
||||
if (!shader.UnpackEnable() && index != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
addressesArray[index] = baseAddress + shader.Offset;
|
||||
}
|
||||
|
||||
GraphicsShader gs = ShaderCache.GetGraphicsShader(state, addresses);
|
||||
|
||||
_vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false;
|
||||
|
||||
for (int stage = 0; stage < Constants.ShaderStages; stage++)
|
||||
{
|
||||
ShaderProgramInfo info = gs.Shaders[stage]?.Program.Info;
|
||||
|
||||
_currentProgramInfo[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.Type);
|
||||
|
||||
if (descriptor.IsBindless)
|
||||
{
|
||||
textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufSlot, descriptor.CbufOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
|
||||
}
|
||||
}
|
||||
|
||||
TextureManager.SetGraphicsTextures(stage, textureBindings);
|
||||
|
||||
var imageBindings = new TextureBindingInfo[info.Images.Count];
|
||||
|
||||
for (int index = 0; index < info.Images.Count; index++)
|
||||
{
|
||||
var descriptor = info.Images[index];
|
||||
|
||||
Target target = GetTarget(descriptor.Type);
|
||||
|
||||
imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
|
||||
}
|
||||
|
||||
TextureManager.SetGraphicsImages(stage, imageBindings);
|
||||
|
||||
uint sbEnableMask = 0;
|
||||
uint ubEnableMask = 0;
|
||||
|
||||
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||
{
|
||||
sbEnableMask |= 1u << info.SBuffers[index].Slot;
|
||||
}
|
||||
|
||||
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.Pipeline.SetProgram(gs.HostProgram);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets texture target from a sampler type.
|
||||
/// </summary>
|
||||
/// <param name="type">Sampler type</param>
|
||||
/// <returns>Texture target value</returns>
|
||||
private static Target GetTarget(SamplerType type)
|
||||
{
|
||||
type &= ~(SamplerType.Indexed | SamplerType.Shadow);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case SamplerType.Texture1D:
|
||||
return Target.Texture1D;
|
||||
|
||||
case SamplerType.TextureBuffer:
|
||||
return Target.TextureBuffer;
|
||||
|
||||
case SamplerType.Texture1D | SamplerType.Array:
|
||||
return Target.Texture1DArray;
|
||||
|
||||
case SamplerType.Texture2D:
|
||||
return Target.Texture2D;
|
||||
|
||||
case SamplerType.Texture2D | SamplerType.Array:
|
||||
return Target.Texture2DArray;
|
||||
|
||||
case SamplerType.Texture2D | SamplerType.Multisample:
|
||||
return Target.Texture2DMultisample;
|
||||
|
||||
case SamplerType.Texture2D | SamplerType.Multisample | SamplerType.Array:
|
||||
return Target.Texture2DMultisampleArray;
|
||||
|
||||
case SamplerType.Texture3D:
|
||||
return Target.Texture3D;
|
||||
|
||||
case SamplerType.TextureCube:
|
||||
return Target.Cubemap;
|
||||
|
||||
case SamplerType.TextureCube | SamplerType.Array:
|
||||
return Target.CubemapArray;
|
||||
}
|
||||
|
||||
Logger.PrintWarning(LogClass.Gpu, $"Invalid sampler type \"{type}\".");
|
||||
|
||||
return Target.Texture2D;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issues a texture barrier.
|
||||
/// This waits until previous texture writes from the GPU to finish, before
|
||||
/// performing new operations with said textures.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state (unused)</param>
|
||||
/// <param name="argument">Method call argument (unused)</param>
|
||||
private void TextureBarrier(GpuState state, int argument)
|
||||
{
|
||||
_context.Renderer.Pipeline.TextureBarrier();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates all modified textures on the cache.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state (unused)</param>
|
||||
/// <param name="argument">Method call argument (unused)</param>
|
||||
private void InvalidateTextures(GpuState state, int argument)
|
||||
{
|
||||
TextureManager.Flush();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Issues a texture barrier.
|
||||
/// This waits until previous texture writes from the GPU to finish, before
|
||||
/// performing new operations with said textures.
|
||||
/// This performs a per-tile wait, it is only valid if both the previous write
|
||||
/// and current access has the same access patterns.
|
||||
/// This may be faster than the regular barrier on tile-based rasterizers.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state (unused)</param>
|
||||
/// <param name="argument">Method call argument (unused)</param>
|
||||
private void TextureBarrierTiled(GpuState state, int argument)
|
||||
{
|
||||
_context.Renderer.Pipeline.TextureBarrierTiled();
|
||||
}
|
||||
}
|
||||
}
|
121
Ryujinx.Graphics.Gpu/GpuContext.cs
Normal file
121
Ryujinx.Graphics.Gpu/GpuContext.cs
Normal file
|
@ -0,0 +1,121 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Engine;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
/// <summary>
|
||||
/// GPU emulation context.
|
||||
/// </summary>
|
||||
public sealed class GpuContext : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Host renderer.
|
||||
/// </summary>
|
||||
public IRenderer Renderer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Physical memory access (it actually accesses the process memory, not actual physical memory).
|
||||
/// </summary>
|
||||
internal PhysicalMemory PhysicalMemory { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// GPU memory manager.
|
||||
/// </summary>
|
||||
public MemoryManager MemoryManager { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GPU memory accessor.
|
||||
/// </summary>
|
||||
public MemoryAccessor MemoryAccessor { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GPU engine methods processing.
|
||||
/// </summary>
|
||||
internal Methods Methods { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GPU commands FIFO.
|
||||
/// </summary>
|
||||
internal NvGpuFifo Fifo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// DMA pusher.
|
||||
/// </summary>
|
||||
public DmaPusher DmaPusher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Presentation window.
|
||||
/// </summary>
|
||||
public Window Window { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal sequence number, used to avoid needless resource data updates
|
||||
/// in the middle of a command buffer before synchronizations.
|
||||
/// </summary>
|
||||
internal int SequenceNumber { get; private set; }
|
||||
|
||||
private readonly Lazy<Capabilities> _caps;
|
||||
|
||||
/// <summary>
|
||||
/// Host hardware capabilities.
|
||||
/// </summary>
|
||||
internal Capabilities Capabilities => _caps.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the GPU emulation context.
|
||||
/// </summary>
|
||||
/// <param name="renderer">Host renderer</param>
|
||||
public GpuContext(IRenderer renderer)
|
||||
{
|
||||
Renderer = renderer;
|
||||
|
||||
MemoryManager = new MemoryManager();
|
||||
|
||||
MemoryAccessor = new MemoryAccessor(this);
|
||||
|
||||
Methods = new Methods(this);
|
||||
|
||||
Fifo = new NvGpuFifo(this);
|
||||
|
||||
DmaPusher = new DmaPusher(this);
|
||||
|
||||
Window = new Window(this);
|
||||
|
||||
_caps = new Lazy<Capabilities>(Renderer.GetCapabilities);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances internal sequence number.
|
||||
/// This forces the update of any modified GPU resource.
|
||||
/// </summary>
|
||||
internal void AdvanceSequence()
|
||||
{
|
||||
SequenceNumber++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the process memory manager, after the application process is initialized.
|
||||
/// This is required for any GPU memory access.
|
||||
/// </summary>
|
||||
/// <param name="cpuMemory">CPU memory manager</param>
|
||||
public void SetVmm(ARMeilleure.Memory.MemoryManager cpuMemory)
|
||||
{
|
||||
PhysicalMemory = new PhysicalMemory(cpuMemory);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all GPU resources currently cached.
|
||||
/// It's an error to push any GPU commands after disposal.
|
||||
/// Additionally, the GPU commands FIFO must be empty for disposal,
|
||||
/// and processing of all commands must have finished.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Methods.ShaderCache.Dispose();
|
||||
Methods.BufferManager.Dispose();
|
||||
Methods.TextureManager.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
21
Ryujinx.Graphics.Gpu/GraphicsConfig.cs
Normal file
21
Ryujinx.Graphics.Gpu/GraphicsConfig.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
/// <summary>
|
||||
/// General GPU and graphics configuration.
|
||||
/// </summary>
|
||||
public static class GraphicsConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// Base directory used to write shader code dumps.
|
||||
/// Set to null to disable code dumping.
|
||||
/// </summary>
|
||||
public static string ShadersDumpPath;
|
||||
|
||||
/// <summary>
|
||||
/// Fast GPU time calculates the internal GPU time ticks as if the GPU was capable of
|
||||
/// processing commands almost instantly, instead of using the host timer.
|
||||
/// This can avoid lower resolution on some games when GPU performance is poor.
|
||||
/// </summary>
|
||||
public static bool FastGpuTime = true;
|
||||
}
|
||||
}
|
87
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal file
87
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// A texture cache that automatically removes older textures that are not used for some time.
|
||||
/// The cache works with a rotated list with a fixed size. When new textures are added, the
|
||||
/// old ones at the bottom of the list are deleted.
|
||||
/// </summary>
|
||||
class AutoDeleteCache : IEnumerable<Texture>
|
||||
{
|
||||
private const int MaxCapacity = 2048;
|
||||
|
||||
private readonly LinkedList<Texture> _textures;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the automatic deletion cache.
|
||||
/// </summary>
|
||||
public AutoDeleteCache()
|
||||
{
|
||||
_textures = new LinkedList<Texture>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new texture to the cache, even if the texture added is already on the cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Using this method is only recommended if you know that the texture is not yet on the cache,
|
||||
/// otherwise it would store the same texture more than once.
|
||||
/// </remarks>
|
||||
/// <param name="texture">The texture to be added to the cache</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new texture to the cache, or just moves it to the top of the list if the
|
||||
/// texture is already on the cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Moving the texture to the top of the list prevents it from being deleted,
|
||||
/// as the textures on the bottom of the list are deleted when new ones are added.
|
||||
/// </remarks>
|
||||
/// <param name="texture">The texture to be added, or moved to the top</param>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
65
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal file
65
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal file
|
@ -0,0 +1,65 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents texture format information.
|
||||
/// </summary>
|
||||
struct FormatInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// A default, generic RGBA8 texture format.
|
||||
/// </summary>
|
||||
public static FormatInfo Default { get; } = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
|
||||
|
||||
/// <summary>
|
||||
/// The format of the texture data.
|
||||
/// </summary>
|
||||
public Format Format { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The block width for compressed formats.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Must be 1 for non-compressed formats.
|
||||
/// </remarks>
|
||||
public int BlockWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The block height for compressed formats.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Must be 1 for non-compressed formats.
|
||||
/// </remarks>
|
||||
public int BlockHeight { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes occupied by a single pixel in memory of the texture data.
|
||||
/// </summary>
|
||||
public int BytesPerPixel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whenever or not the texture format is a compressed format. Determined from block size.
|
||||
/// </summary>
|
||||
public bool IsCompressed => (BlockWidth | BlockHeight) != 1;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the texture format info structure.
|
||||
/// </summary>
|
||||
/// <param name="format">The format of the texture data</param>
|
||||
/// <param name="blockWidth">The block width for compressed formats. Must be 1 for non-compressed formats</param>
|
||||
/// <param name="blockHeight">The block height for compressed formats. Must be 1 for non-compressed formats</param>
|
||||
/// <param name="bytesPerPixel">The number of bytes occupied by a single pixel in memory of the texture data</param>
|
||||
public FormatInfo(
|
||||
Format format,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int bytesPerPixel)
|
||||
{
|
||||
Format = format;
|
||||
BlockWidth = blockWidth;
|
||||
BlockHeight = blockHeight;
|
||||
BytesPerPixel = bytesPerPixel;
|
||||
}
|
||||
}
|
||||
}
|
217
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal file
217
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal file
|
@ -0,0 +1,217 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains format tables, for texture and vertex attribute formats.
|
||||
/// </summary>
|
||||
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.Bc6HSfloat, 4, 4, 16) },
|
||||
{ 0x7ff91, new FormatInfo(Format.Bc6HUfloat, 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 }
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Try getting the texture format from an encoded format integer from the Maxwell texture descriptor.
|
||||
/// </summary>
|
||||
/// <param name="encoded">The encoded format integer from the texture descriptor</param>
|
||||
/// <param name="isSrgb">Indicates if the format is a sRGB format</param>
|
||||
/// <param name="format">The output texture format</param>
|
||||
/// <returns>True if the format is valid, false otherwise</returns>
|
||||
public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
|
||||
{
|
||||
encoded |= (isSrgb ? 1u << 19 : 0u);
|
||||
|
||||
return _textureFormats.TryGetValue(encoded, out format);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try getting the vertex attribute format from an encoded format integer from Maxwell attribute registers.
|
||||
/// </summary>
|
||||
/// <param name="encoded">The encoded format integer from the attribute registers</param>
|
||||
/// <param name="format">The output vertex attribute format</param>
|
||||
/// <returns>True if the format is valid, false otherwise</returns>
|
||||
public static bool TryGetAttribFormat(uint encoded, out Format format)
|
||||
{
|
||||
return _attribFormats.TryGetValue(encoded, out format);
|
||||
}
|
||||
}
|
||||
}
|
143
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal file
143
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal file
|
@ -0,0 +1,143 @@
|
|||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a pool of GPU resources, such as samplers or textures.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Type of the GPU resource</typeparam>
|
||||
abstract class Pool<T> : IDisposable
|
||||
{
|
||||
protected const int DescriptorSize = 0x20;
|
||||
|
||||
protected GpuContext Context;
|
||||
|
||||
protected T[] Items;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum ID value of resources on the pool (inclusive).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The maximum amount of resources on the pool is equal to this value plus one.
|
||||
/// </remarks>
|
||||
public int MaximumId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The address of the pool in guest memory.
|
||||
/// </summary>
|
||||
public ulong Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The size of the pool in bytes.
|
||||
/// </summary>
|
||||
public ulong Size { get; }
|
||||
|
||||
public Pool(GpuContext context, ulong address, int maximumId)
|
||||
{
|
||||
Context = context;
|
||||
MaximumId = maximumId;
|
||||
|
||||
int count = maximumId + 1;
|
||||
|
||||
ulong size = (ulong)(uint)count * DescriptorSize;;
|
||||
|
||||
Items = new T[count];
|
||||
|
||||
Address = address;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the GPU resource with the given ID.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the resource. This is effectively a zero-based index</param>
|
||||
/// <returns>The GPU resource with the given ID</returns>
|
||||
public abstract T Get(int id);
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes host memory with guest memory.
|
||||
/// This causes invalidation of pool entries,
|
||||
/// if a modification of entries by the CPU is detected.
|
||||
/// </summary>
|
||||
public void SynchronizeMemory()
|
||||
{
|
||||
(ulong, ulong)[] modifiedRanges = Context.PhysicalMemory.GetModifiedRanges(Address, Size, ResourceName.TexturePool);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates a range of memory of the GPU resource pool.
|
||||
/// Entries that falls inside the speicified range will be invalidated,
|
||||
/// causing all the data to be reloaded from guest memory.
|
||||
/// </summary>
|
||||
/// <param name="address">The start address of the range to invalidate</param>
|
||||
/// <param name="size">The size of the range to invalidate</param>
|
||||
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;
|
||||
}
|
||||
|
||||
size = endAddress - address;
|
||||
|
||||
InvalidateRangeImpl(address, size);
|
||||
}
|
||||
|
||||
protected abstract void InvalidateRangeImpl(ulong address, ulong size);
|
||||
|
||||
protected abstract void Delete(T item);
|
||||
|
||||
/// <summary>
|
||||
/// Performs the disposal of all resources stored on the pool.
|
||||
/// It's an error to try using the pool after disposal.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (Items != null)
|
||||
{
|
||||
for (int index = 0; index < Items.Length; index++)
|
||||
{
|
||||
Delete(Items[index]);
|
||||
}
|
||||
|
||||
Items = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
15
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a filter used with texture minification linear filtering.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This feature is only supported on NVIDIA GPUs.
|
||||
/// </remarks>
|
||||
enum ReductionFilter
|
||||
{
|
||||
Average,
|
||||
Minimum,
|
||||
Maximum
|
||||
}
|
||||
}
|
64
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal file
64
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Cached sampler entry for sampler pools.
|
||||
/// </summary>
|
||||
class Sampler : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Host sampler object.
|
||||
/// </summary>
|
||||
public ISampler HostSampler { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the cached sampler.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context the sampler belongs to</param>
|
||||
/// <param name="descriptor">The Maxwell sampler descriptor</param>
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the host sampler object.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
HostSampler.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
237
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal file
237
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal file
|
@ -0,0 +1,237 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Maxwell sampler descriptor structure.
|
||||
/// This structure defines the sampler descriptor as it is packed on the GPU sampler pool region.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture wrap mode along the X axis.
|
||||
/// </summary>
|
||||
/// <returns>The texture wrap mode enum</returns>
|
||||
public AddressMode UnpackAddressU()
|
||||
{
|
||||
return (AddressMode)(Word0 & 7);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Unpacks the texture wrap mode along the Y axis.
|
||||
/// </summary>
|
||||
/// <returns>The texture wrap mode enum</returns>
|
||||
public AddressMode UnpackAddressV()
|
||||
{
|
||||
return (AddressMode)((Word0 >> 3) & 7);
|
||||
}
|
||||
|
||||
// <summary>
|
||||
/// Unpacks the texture wrap mode along the Z axis.
|
||||
/// </summary>
|
||||
/// <returns>The texture wrap mode enum</returns>
|
||||
public AddressMode UnpackAddressP()
|
||||
{
|
||||
return (AddressMode)((Word0 >> 6) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the compare mode used for depth comparison on the shader, for
|
||||
/// depth buffer texture.
|
||||
/// This is only relevant for shaders with shadow samplers.
|
||||
/// </summary>
|
||||
/// <returns>The depth comparison mode enum</returns>
|
||||
public CompareMode UnpackCompareMode()
|
||||
{
|
||||
return (CompareMode)((Word0 >> 9) & 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the compare operation used for depth comparison on the shader, for
|
||||
/// depth buffer texture.
|
||||
/// This is only relevant for shaders with shadow samplers.
|
||||
/// </summary>
|
||||
/// <returns>The depth comparison operation enum</returns>
|
||||
public CompareOp UnpackCompareOp()
|
||||
{
|
||||
return (CompareOp)(((Word0 >> 10) & 7) + 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks and converts the maximum anisotropy value used for texture anisotropic filtering.
|
||||
/// </summary>
|
||||
/// <returns>The maximum anisotropy</returns>
|
||||
public float UnpackMaxAnisotropy()
|
||||
{
|
||||
return _maxAnisotropyLut[(Word0 >> 20) & 7];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture magnification filter.
|
||||
/// This defines the filtering used when the texture covers an area on the screen
|
||||
/// that is larger than the texture size.
|
||||
/// </summary>
|
||||
/// <returns>The magnification filter</returns>
|
||||
public MagFilter UnpackMagFilter()
|
||||
{
|
||||
return (MagFilter)(Word1 & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture minification filter.
|
||||
/// This defines the filtering used when the texture covers an area on the screen
|
||||
/// that is smaller than the texture size.
|
||||
/// </summary>
|
||||
/// <returns>The minification filter</returns>
|
||||
public MinFilter UnpackMinFilter()
|
||||
{
|
||||
SamplerMinFilter minFilter = (SamplerMinFilter)((Word1 >> 4) & 3);
|
||||
SamplerMipFilter mipFilter = (SamplerMipFilter)((Word1 >> 6) & 3);
|
||||
|
||||
return ConvertFilter(minFilter, mipFilter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts two minification and filter enum, to a single minification enum,
|
||||
/// including mipmap filtering information, as expected from the host API.
|
||||
/// </summary>
|
||||
/// <param name="minFilter">The minification filter</param>
|
||||
/// <param name="mipFilter">The mipmap level filter</param>
|
||||
/// <returns>The combined, host API compatible filter enum</returns>
|
||||
private static MinFilter ConvertFilter(SamplerMinFilter minFilter, SamplerMipFilter mipFilter)
|
||||
{
|
||||
switch (mipFilter)
|
||||
{
|
||||
case SamplerMipFilter.None:
|
||||
switch (minFilter)
|
||||
{
|
||||
case SamplerMinFilter.Nearest: return MinFilter.Nearest;
|
||||
case SamplerMinFilter.Linear: return MinFilter.Linear;
|
||||
}
|
||||
break;
|
||||
|
||||
case SamplerMipFilter.Nearest:
|
||||
switch (minFilter)
|
||||
{
|
||||
case SamplerMinFilter.Nearest: return MinFilter.NearestMipmapNearest;
|
||||
case SamplerMinFilter.Linear: return MinFilter.LinearMipmapNearest;
|
||||
}
|
||||
break;
|
||||
|
||||
case SamplerMipFilter.Linear:
|
||||
switch (minFilter)
|
||||
{
|
||||
case SamplerMinFilter.Nearest: return MinFilter.NearestMipmapLinear;
|
||||
case SamplerMinFilter.Linear: return MinFilter.LinearMipmapLinear;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return MinFilter.Nearest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the reduction filter, used with texture minification linear filtering.
|
||||
/// This describes how the final value will be computed from neighbouring pixels.
|
||||
/// </summary>
|
||||
/// <returns>The reduction filter</returns>
|
||||
public ReductionFilter UnpackReductionFilter()
|
||||
{
|
||||
return (ReductionFilter)((Word1 >> 10) & 3);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the level-of-detail bias value.
|
||||
/// This is a bias added to the level-of-detail value as computed by the GPU, used to select
|
||||
/// which mipmap level to use from a given texture.
|
||||
/// </summary>
|
||||
/// <returns>The level-of-detail bias value</returns>
|
||||
public float UnpackMipLodBias()
|
||||
{
|
||||
int fixedValue = (int)(Word1 >> 12) & 0x1fff;
|
||||
|
||||
fixedValue = (fixedValue << 19) >> 19;
|
||||
|
||||
return fixedValue * Frac8ToF32;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the level-of-detail snap value.
|
||||
/// </summary>
|
||||
/// <returns>The level-of-detail snap value</returns>
|
||||
public float UnpackLodSnap()
|
||||
{
|
||||
return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the minimum level-of-detail value.
|
||||
/// </summary>
|
||||
/// <returns>The minimum level-of-detail value</returns>
|
||||
public float UnpackMinLod()
|
||||
{
|
||||
return (Word2 & 0xfff) * Frac8ToF32;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the maximum level-of-detail value.
|
||||
/// </summary>
|
||||
/// <returns>The maximum level-of-detail value</returns>
|
||||
public float UnpackMaxLod()
|
||||
{
|
||||
return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs
Normal file
11
Ryujinx.Graphics.Gpu/Image/SamplerMinFilter.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Sampler texture minification filter.
|
||||
/// </summary>
|
||||
enum SamplerMinFilter
|
||||
{
|
||||
Nearest = 1,
|
||||
Linear
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs
Normal file
12
Ryujinx.Graphics.Gpu/Image/SamplerMipFilter.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Sampler texture mipmap level filter.
|
||||
/// </summary>
|
||||
enum SamplerMipFilter
|
||||
{
|
||||
None = 1,
|
||||
Nearest,
|
||||
Linear
|
||||
}
|
||||
}
|
92
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal file
92
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal file
|
@ -0,0 +1,92 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Sampler pool.
|
||||
/// </summary>
|
||||
class SamplerPool : Pool<Sampler>
|
||||
{
|
||||
private int _sequenceNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the sampler pool.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that the sampler pool belongs to</param>
|
||||
/// <param name="address">Address of the sampler pool in guest memory</param>
|
||||
/// <param name="maximumId">Maximum sampler ID of the sampler pool (equal to maximum samplers minus one)</param>
|
||||
public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sampler with the given ID.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the sampler. This is effectively a zero-based index</param>
|
||||
/// <returns>The sampler with the given ID</returns>
|
||||
public override Sampler Get(int id)
|
||||
{
|
||||
if ((uint)id >= Items.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_sequenceNumber != Context.SequenceNumber)
|
||||
{
|
||||
_sequenceNumber = Context.SequenceNumber;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the sampler pool range invalidation.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the range of the sampler pool</param>
|
||||
/// <param name="size">Size of the range being invalidated</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a given sampler pool entry.
|
||||
/// The host memory used by the sampler is released by the driver.
|
||||
/// </summary>
|
||||
/// <param name="item">The entry to be deleted</param>
|
||||
protected override void Delete(Sampler item)
|
||||
{
|
||||
item?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
1025
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal file
1025
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal file
File diff suppressed because it is too large
Load diff
73
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal file
73
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture binding information.
|
||||
/// This is used for textures that needs to be accessed from shaders.
|
||||
/// </summary>
|
||||
struct TextureBindingInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Shader sampler target type.
|
||||
/// </summary>
|
||||
public Target Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Shader texture handle.
|
||||
/// This is an index into the texture constant buffer.
|
||||
/// </summary>
|
||||
public int Handle { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates if the texture is a bindless texture.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// For those textures, Handle is ignored.
|
||||
/// </remarks>
|
||||
public bool IsBindless { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constant buffer slot with the bindless texture handle, for bindless texture.
|
||||
/// </summary>
|
||||
public int CbufSlot { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constant buffer offset of the bindless texture handle, for bindless texture.
|
||||
/// </summary>
|
||||
public int CbufOffset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the texture binding information structure.
|
||||
/// </summary>
|
||||
/// <param name="target">The shader sampler target type</param>
|
||||
/// <param name="handle">The shader texture handle (read index into the texture constant buffer)</param>
|
||||
public TextureBindingInfo(Target target, int handle)
|
||||
{
|
||||
Target = target;
|
||||
Handle = handle;
|
||||
|
||||
IsBindless = false;
|
||||
|
||||
CbufSlot = 0;
|
||||
CbufOffset = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the bindless texture binding information structure.
|
||||
/// </summary>
|
||||
/// <param name="target">The shader sampler target type</param>
|
||||
/// <param name="cbufSlot">Constant buffer slot where the bindless texture handle is located</param>
|
||||
/// <param name="cbufOffset">Constant buffer offset of the bindless texture handle</param>
|
||||
public TextureBindingInfo(Target target, int cbufSlot, int cbufOffset)
|
||||
{
|
||||
Target = target;
|
||||
Handle = 0;
|
||||
|
||||
IsBindless = true;
|
||||
|
||||
CbufSlot = cbufSlot;
|
||||
CbufOffset = cbufOffset;
|
||||
}
|
||||
}
|
||||
}
|
367
Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
Normal file
367
Ryujinx.Graphics.Gpu/Image/TextureBindingsManager.cs
Normal file
|
@ -0,0 +1,367 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture bindings manager.
|
||||
/// </summary>
|
||||
class TextureBindingsManager
|
||||
{
|
||||
private GpuContext _context;
|
||||
|
||||
private bool _isCompute;
|
||||
|
||||
private SamplerPool _samplerPool;
|
||||
|
||||
private SamplerIndex _samplerIndex;
|
||||
|
||||
private ulong _texturePoolAddress;
|
||||
private int _texturePoolMaximumId;
|
||||
|
||||
private TexturePoolCache _texturePoolCache;
|
||||
|
||||
private TextureBindingInfo[][] _textureBindings;
|
||||
private TextureBindingInfo[][] _imageBindings;
|
||||
|
||||
private struct TextureStatePerStage
|
||||
{
|
||||
public ITexture Texture;
|
||||
public ISampler Sampler;
|
||||
}
|
||||
|
||||
private TextureStatePerStage[][] _textureState;
|
||||
private TextureStatePerStage[][] _imageState;
|
||||
|
||||
private int _textureBufferIndex;
|
||||
|
||||
private bool _rebind;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the texture bindings manager.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context that the texture bindings manager belongs to</param>
|
||||
/// <param name="texturePoolCache">Texture pools cache used to get texture pools from</param>
|
||||
/// <param name="isCompute">True if the bindings manager is used for the compute engine</param>
|
||||
public TextureBindingsManager(GpuContext context, TexturePoolCache texturePoolCache, bool isCompute)
|
||||
{
|
||||
_context = context;
|
||||
_texturePoolCache = texturePoolCache;
|
||||
_isCompute = isCompute;
|
||||
|
||||
int stages = isCompute ? 1 : Constants.ShaderStages;
|
||||
|
||||
_textureBindings = new TextureBindingInfo[stages][];
|
||||
_imageBindings = new TextureBindingInfo[stages][];
|
||||
|
||||
_textureState = new TextureStatePerStage[stages][];
|
||||
_imageState = new TextureStatePerStage[stages][];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds textures for a given shader stage.
|
||||
/// </summary>
|
||||
/// <param name="stage">Shader stage number, or 0 for compute shaders</param>
|
||||
/// <param name="bindings">Texture bindings</param>
|
||||
public void SetTextures(int stage, TextureBindingInfo[] bindings)
|
||||
{
|
||||
_textureBindings[stage] = bindings;
|
||||
|
||||
_textureState[stage] = new TextureStatePerStage[bindings.Length];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds images for a given shader stage.
|
||||
/// </summary>
|
||||
/// <param name="stage">Shader stage number, or 0 for compute shaders</param>
|
||||
/// <param name="bindings">Image bindings</param>
|
||||
public void SetImages(int stage, TextureBindingInfo[] bindings)
|
||||
{
|
||||
_imageBindings[stage] = bindings;
|
||||
|
||||
_imageState[stage] = new TextureStatePerStage[bindings.Length];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the textures constant buffer index.
|
||||
/// The constant buffer specified holds the texture handles.
|
||||
/// </summary>
|
||||
/// <param name="index">Constant buffer index</param>
|
||||
public void SetTextureBufferIndex(int index)
|
||||
{
|
||||
_textureBufferIndex = index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current texture sampler pool to be used.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the pool</param>
|
||||
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
|
||||
/// <param name="samplerIndex">Type of the sampler pool indexing used for bound samplers</param>
|
||||
public void SetSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
if (_samplerPool != null)
|
||||
{
|
||||
if (_samplerPool.Address == address && _samplerPool.MaximumId >= maximumId)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_samplerPool.Dispose();
|
||||
}
|
||||
|
||||
_samplerPool = new SamplerPool(_context, address, maximumId);
|
||||
|
||||
_samplerIndex = samplerIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current texture pool to be used.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the pool</param>
|
||||
/// <param name="maximumId">Maximum ID of the pool (total count minus one)</param>
|
||||
public void SetTexturePool(ulong gpuVa, int maximumId)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
_texturePoolAddress = address;
|
||||
_texturePoolMaximumId = maximumId;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
/// </summary>
|
||||
public void CommitBindings()
|
||||
{
|
||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(
|
||||
_texturePoolAddress,
|
||||
_texturePoolMaximumId);
|
||||
|
||||
if (_isCompute)
|
||||
{
|
||||
CommitTextureBindings(texturePool, ShaderStage.Compute, 0);
|
||||
CommitImageBindings (texturePool, ShaderStage.Compute, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
|
||||
{
|
||||
int stageIndex = (int)stage - 1;
|
||||
|
||||
CommitTextureBindings(texturePool, stage, stageIndex);
|
||||
CommitImageBindings (texturePool, stage, stageIndex);
|
||||
}
|
||||
}
|
||||
|
||||
_rebind = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the texture bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
/// </summary>
|
||||
/// <param name="pool">The current texture pool</param>
|
||||
/// <param name="stage">The shader stage using the textures to be bound</param>
|
||||
/// <param name="stageIndex">The stage number of the specified shader stage</param>
|
||||
private void CommitTextureBindings(TexturePool pool, ShaderStage stage, int stageIndex)
|
||||
{
|
||||
if (_textureBindings[stageIndex] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < _textureBindings[stageIndex].Length; index++)
|
||||
{
|
||||
TextureBindingInfo binding = _textureBindings[stageIndex][index];
|
||||
|
||||
int packedId;
|
||||
|
||||
if (binding.IsBindless)
|
||||
{
|
||||
ulong address;
|
||||
|
||||
var bufferManager = _context.Methods.BufferManager;
|
||||
|
||||
if (_isCompute)
|
||||
{
|
||||
address = bufferManager.GetComputeUniformBufferAddress(binding.CbufSlot);
|
||||
}
|
||||
else
|
||||
{
|
||||
address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, binding.CbufSlot);
|
||||
}
|
||||
|
||||
packedId = MemoryMarshal.Cast<byte, int>(_context.PhysicalMemory.Read(address + (ulong)binding.CbufOffset * 4, 4))[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
packedId = ReadPackedId(stageIndex, binding.Handle);
|
||||
}
|
||||
|
||||
int textureId = UnpackTextureId(packedId);
|
||||
int samplerId;
|
||||
|
||||
if (_samplerIndex == SamplerIndex.ViaHeaderIndex)
|
||||
{
|
||||
samplerId = textureId;
|
||||
}
|
||||
else
|
||||
{
|
||||
samplerId = UnpackSamplerId(packedId);
|
||||
}
|
||||
|
||||
Texture texture = pool.Get(textureId);
|
||||
|
||||
ITexture hostTexture = texture?.GetTargetTexture(binding.Target);
|
||||
|
||||
if (_textureState[stageIndex][index].Texture != hostTexture || _rebind)
|
||||
{
|
||||
_textureState[stageIndex][index].Texture = hostTexture;
|
||||
|
||||
_context.Renderer.Pipeline.SetTexture(index, stage, hostTexture);
|
||||
}
|
||||
|
||||
Sampler sampler = _samplerPool.Get(samplerId);
|
||||
|
||||
ISampler hostSampler = sampler?.HostSampler;
|
||||
|
||||
if (_textureState[stageIndex][index].Sampler != hostSampler || _rebind)
|
||||
{
|
||||
_textureState[stageIndex][index].Sampler = hostSampler;
|
||||
|
||||
_context.Renderer.Pipeline.SetSampler(index, stage, hostSampler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the image bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
/// </summary>
|
||||
/// <param name="pool">The current texture pool</param>
|
||||
/// <param name="stage">The shader stage using the textures to be bound</param>
|
||||
/// <param name="stageIndex">The stage number of the specified shader stage</param>
|
||||
private void CommitImageBindings(TexturePool pool, ShaderStage stage, int stageIndex)
|
||||
{
|
||||
if (_imageBindings[stageIndex] == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < _imageBindings[stageIndex].Length; index++)
|
||||
{
|
||||
TextureBindingInfo binding = _imageBindings[stageIndex][index];
|
||||
|
||||
int packedId = ReadPackedId(stageIndex, binding.Handle);
|
||||
|
||||
int textureId = UnpackTextureId(packedId);
|
||||
|
||||
Texture texture = pool.Get(textureId);
|
||||
|
||||
ITexture hostTexture = texture?.GetTargetTexture(binding.Target);
|
||||
|
||||
if (_imageState[stageIndex][index].Texture != hostTexture || _rebind)
|
||||
{
|
||||
_imageState[stageIndex][index].Texture = hostTexture;
|
||||
|
||||
_context.Renderer.Pipeline.SetImage(index, stage, hostTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor for a given texture handle.
|
||||
/// </summary>
|
||||
/// <param name="state">The current GPU state</param>
|
||||
/// <param name="stageIndex">The stage number where the texture is bound</param>
|
||||
/// <param name="handle">The texture handle</param>
|
||||
/// <returns>The texture descriptor for the specified texture</returns>
|
||||
public TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int handle)
|
||||
{
|
||||
int packedId = ReadPackedId(stageIndex, handle);
|
||||
|
||||
int textureId = UnpackTextureId(packedId);
|
||||
|
||||
var poolState = state.Get<PoolState>(MethodOffset.TexturePoolState);
|
||||
|
||||
ulong poolAddress = _context.MemoryManager.Translate(poolState.Address.Pack());
|
||||
|
||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(poolAddress, poolState.MaximumId);
|
||||
|
||||
return texturePool.GetDescriptor(textureId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads a packed texture and sampler ID (basically, the real texture handle)
|
||||
/// from the texture constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="stageIndex">The number of the shader stage where the texture is bound</param>
|
||||
/// <param name="wordOffset">A word offset of the handle on the buffer (the "fake" shader handle)</param>
|
||||
/// <returns>The packed texture and sampler ID (the real texture handle)</returns>
|
||||
private int ReadPackedId(int stageIndex, int wordOffset)
|
||||
{
|
||||
ulong address;
|
||||
|
||||
var bufferManager = _context.Methods.BufferManager;
|
||||
|
||||
if (_isCompute)
|
||||
{
|
||||
address = bufferManager.GetComputeUniformBufferAddress(_textureBufferIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
address = bufferManager.GetGraphicsUniformBufferAddress(stageIndex, _textureBufferIndex);
|
||||
}
|
||||
|
||||
address += (uint)wordOffset * 4;
|
||||
|
||||
return BitConverter.ToInt32(_context.PhysicalMemory.Read(address, 4));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture ID from the real texture handle.
|
||||
/// </summary>
|
||||
/// <param name="packedId">The real texture handle</param>
|
||||
/// <returns>The texture ID</returns>
|
||||
private static int UnpackTextureId(int packedId)
|
||||
{
|
||||
return (packedId >> 0) & 0xfffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the sampler ID from the real texture handle.
|
||||
/// </summary>
|
||||
/// <param name="packedId">The real texture handle</param>
|
||||
/// <returns>The sampler ID</returns>
|
||||
private static int UnpackSamplerId(int packedId)
|
||||
{
|
||||
return (packedId >> 20) & 0xfff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates a range of memory on all GPU resource pools (both texture and sampler pools).
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the range to invalidate</param>
|
||||
/// <param name="size">Size of the range to invalidate</param>
|
||||
public void InvalidatePoolRange(ulong address, ulong size)
|
||||
{
|
||||
_samplerPool?.InvalidateRange(address, size);
|
||||
|
||||
_texturePoolCache.InvalidateRange(address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Force all bound textures and images to be rebound the next time CommitBindings is called.
|
||||
/// </summary>
|
||||
public void Rebind()
|
||||
{
|
||||
_rebind = true;
|
||||
}
|
||||
}
|
||||
}
|
115
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal file
115
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal file
|
@ -0,0 +1,115 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture format compatibility checks.
|
||||
/// </summary>
|
||||
static class TextureCompatibility
|
||||
{
|
||||
private enum FormatClass
|
||||
{
|
||||
Unclassified,
|
||||
BCn64,
|
||||
BCn128,
|
||||
Bc1Rgb,
|
||||
Bc1Rgba,
|
||||
Bc2,
|
||||
Bc3,
|
||||
Bc4,
|
||||
Bc5,
|
||||
Bc6,
|
||||
Bc7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if two formats are compatible, according to the host API copy format compatibility rules.
|
||||
/// </summary>
|
||||
/// <param name="lhs">First comparand</param>
|
||||
/// <param name="rhs">Second comparand</param>
|
||||
/// <returns>True if the formats are compatible, false otherwise</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture format class, for compressed textures, or Unclassified otherwise.
|
||||
/// </summary>
|
||||
/// <param name="format">The format</param>
|
||||
/// <returns>Format class</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the format is a depth-stencil texture format.
|
||||
/// </summary>
|
||||
/// <param name="format">Format to check</param>
|
||||
/// <returns>True if the format is a depth-stencil format (including depth only), false otherwise</returns>
|
||||
private static bool IsDsFormat(Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Format.D16Unorm:
|
||||
case Format.D24X8Unorm:
|
||||
case Format.D24UnormS8Uint:
|
||||
case Format.D32Float:
|
||||
case Format.D32FloatS8Uint:
|
||||
case Format.S8Uint:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
43
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal file
43
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal file
|
@ -0,0 +1,43 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture swizzle color component.
|
||||
/// </summary>
|
||||
enum TextureComponent
|
||||
{
|
||||
Zero = 0,
|
||||
Red = 2,
|
||||
Green = 3,
|
||||
Blue = 4,
|
||||
Alpha = 5,
|
||||
OneSI = 6,
|
||||
OneF = 7
|
||||
}
|
||||
|
||||
static class TextureComponentConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the texture swizzle color component enum to the respective Graphics Abstraction Layer enum.
|
||||
/// </summary>
|
||||
/// <param name="component">Texture swizzle color component</param>
|
||||
/// <returns>Converted enum</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
229
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal file
229
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal file
|
@ -0,0 +1,229 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Maxwell texture descriptor, as stored on the GPU texture pool memory region.
|
||||
/// </summary>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks Maxwell texture format integer.
|
||||
/// </summary>
|
||||
/// <returns>The texture format integer</returns>
|
||||
public uint UnpackFormat()
|
||||
{
|
||||
return Word0 & 0x8007ffff;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the swizzle component for the texture red color channel.
|
||||
/// </summary>
|
||||
/// <returns>The swizzle component</returns>
|
||||
public TextureComponent UnpackSwizzleR()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 19) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the swizzle component for the texture green color channel.
|
||||
/// </summary>
|
||||
/// <returns>The swizzle component</returns>
|
||||
public TextureComponent UnpackSwizzleG()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 22) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the swizzle component for the texture blue color channel.
|
||||
/// </summary>
|
||||
/// <returns>The swizzle component</returns>
|
||||
public TextureComponent UnpackSwizzleB()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 25) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the swizzle component for the texture alpha color channel.
|
||||
/// </summary>
|
||||
/// <returns>The swizzle component</returns>
|
||||
public TextureComponent UnpackSwizzleA()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 28) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the 40-bits texture GPU virtual address.
|
||||
/// </summary>
|
||||
/// <returns>The GPU virtual address</returns>
|
||||
public ulong UnpackAddress()
|
||||
{
|
||||
return Word1 | ((ulong)(Word2 & 0xffff) << 32);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks texture descriptor type for this texture descriptor.
|
||||
/// This defines the texture layout, among other things.
|
||||
/// </summary>
|
||||
/// <returns>The texture descriptor type</returns>
|
||||
public TextureDescriptorType UnpackTextureDescriptorType()
|
||||
{
|
||||
return (TextureDescriptorType)((Word2 >> 21) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture stride (bytes per line) for linear textures only.
|
||||
/// Always 32-bytes aligned.
|
||||
/// </summary>
|
||||
/// <returns>The linear texture stride</returns>
|
||||
public int UnpackStride()
|
||||
{
|
||||
return (int)(Word3 & 0xffff) << 5;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the GOB block size in X (width) for block linear textures.
|
||||
/// Must be always 1, ignored by the GPU.
|
||||
/// </summary>
|
||||
/// <returns>THe GOB block X size</returns>
|
||||
public int UnpackGobBlocksInX()
|
||||
{
|
||||
return 1 << (int)(Word3 & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the GOB block size in Y (height) for block linear textures.
|
||||
/// Must be always a power of 2, with a maximum value of 32.
|
||||
/// </summary>
|
||||
/// <returns>THe GOB block Y size</returns>
|
||||
public int UnpackGobBlocksInY()
|
||||
{
|
||||
return 1 << (int)((Word3 >> 3) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the GOB block size in Z (depth) for block linear textures.
|
||||
/// Must be always a power of 2, with a maximum value of 32.
|
||||
/// Must be 1 for any texture target other than 3D textures.
|
||||
/// </summary>
|
||||
/// <returns>The GOB block Z size</returns>
|
||||
public int UnpackGobBlocksInZ()
|
||||
{
|
||||
return 1 << (int)((Word3 >> 6) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of GOB blocks per tile in the X direction.
|
||||
/// This is only used for sparse textures, should be 1 otherwise.
|
||||
/// </summary>
|
||||
/// <returns>The number of GOB blocks per tile</returns>
|
||||
public int UnpackGobBlocksInTileX()
|
||||
{
|
||||
return 1 << (int)((Word3 >> 10) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the number of mipmap levels of the texture.
|
||||
/// </summary>
|
||||
/// <returns>The number of mipmap levels</returns>
|
||||
public int UnpackLevels()
|
||||
{
|
||||
return (int)(Word3 >> 28) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpack the base level texture width size.
|
||||
/// </summary>
|
||||
/// <returns>The texture width</returns>
|
||||
public int UnpackWidth()
|
||||
{
|
||||
return (int)(Word4 & 0xffff) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture sRGB format flag.
|
||||
/// </summary>
|
||||
/// <returns>True if the texture is sRGB, false otherwise</returns>
|
||||
public bool UnpackSrgb()
|
||||
{
|
||||
return (Word4 & (1 << 22)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture target.
|
||||
/// </summary>
|
||||
/// <returns>The texture target</returns>
|
||||
public TextureTarget UnpackTextureTarget()
|
||||
{
|
||||
return (TextureTarget)((Word4 >> 23) & 0xf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpack the base level texture height size, or array layers for 1D array textures.
|
||||
/// Should be ignored for 1D or buffer textures.
|
||||
/// </summary>
|
||||
/// <returns>The texture height or layers count</returns>
|
||||
public int UnpackHeight()
|
||||
{
|
||||
return (int)(Word5 & 0xffff) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpack the base level texture depth size, number of array layers or cubemap faces.
|
||||
/// The meaning of this value depends on the texture target.
|
||||
/// </summary>
|
||||
/// <returns>The texture depth, layer or faces count</returns>
|
||||
public int UnpackDepth()
|
||||
{
|
||||
return (int)((Word5 >> 16) & 0x3fff) + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the texture coordinates normalized flag.
|
||||
/// When this is true, texture coordinates are expected to be in the [0, 1] range on the shader.
|
||||
/// When this is false, texture coordinates are expected to be in the [0, W], [0, H] and [0, D] range.
|
||||
/// It must be set to false (by the guest driver) for rectangle textures.
|
||||
/// </summary>
|
||||
/// <returns>The texture coordinates normalized flag</returns>
|
||||
public bool UnpackTextureCoordNormalized()
|
||||
{
|
||||
return (Word5 & (1 << 31)) != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the base mipmap level of the texture.
|
||||
/// </summary>
|
||||
/// <returns>The base mipmap level of the texture</returns>
|
||||
public int UnpackBaseLevel()
|
||||
{
|
||||
return (int)(Word7 & 0xf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the maximum mipmap level (inclusive) of the texture.
|
||||
/// Usually equal to Levels minus 1.
|
||||
/// </summary>
|
||||
/// <returns>The maximum mipmap level (inclusive) of the texture</returns>
|
||||
public int UnpackMaxLevelInclusive()
|
||||
{
|
||||
return (int)((Word7 >> 4) & 0xf);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unpacks the multisampled texture samples count in each direction.
|
||||
/// Must be ignored for non-multisample textures.
|
||||
/// </summary>
|
||||
/// <returns>The multisample counts enum</returns>
|
||||
public TextureMsaaMode UnpackTextureMsaaMode()
|
||||
{
|
||||
return (TextureMsaaMode)((Word7 >> 8) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
16
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// The texture descriptor type.
|
||||
/// This specifies the texture memory layout.
|
||||
/// The texture descriptor structure depends on the type.
|
||||
/// </summary>
|
||||
enum TextureDescriptorType
|
||||
{
|
||||
Buffer,
|
||||
LinearColorKey,
|
||||
Linear,
|
||||
BlockLinear,
|
||||
BlockLinearColorKey
|
||||
}
|
||||
}
|
207
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal file
207
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal file
|
@ -0,0 +1,207 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture information.
|
||||
/// </summary>
|
||||
struct TextureInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Address of the texture in guest memory.
|
||||
/// </summary>
|
||||
public ulong Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The width of the texture.
|
||||
/// </summary>
|
||||
public int Width { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The height of the texture, or layers count for 1D array textures.
|
||||
/// </summary>
|
||||
public int Height { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The depth of the texture (for 3D textures), or layers count for array textures.
|
||||
/// </summary>
|
||||
public int DepthOrLayers { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of mipmap levels of the texture.
|
||||
/// </summary>
|
||||
public int Levels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of samples in the X direction for multisampled textures.
|
||||
/// </summary>
|
||||
public int SamplesInX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of samples in the Y direction for multisampled textures.
|
||||
/// </summary>
|
||||
public int SamplesInY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of bytes per line for linear textures.
|
||||
/// </summary>
|
||||
public int Stride { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whenever or not the texture is a linear texture.
|
||||
/// </summary>
|
||||
public bool IsLinear { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GOB blocks in the Y direction, for block linear textures.
|
||||
/// </summary>
|
||||
public int GobBlocksInY { get; }
|
||||
|
||||
/// <summary>
|
||||
/// GOB blocks in the Z direction, for block linear textures.
|
||||
/// </summary>
|
||||
public int GobBlocksInZ { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Number of GOB blocks per tile in the X direction, for block linear textures.
|
||||
/// </summary>
|
||||
public int GobBlocksInTileX { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Total number of samples for multisampled textures.
|
||||
/// </summary>
|
||||
public int Samples => SamplesInX * SamplesInY;
|
||||
|
||||
/// <summary>
|
||||
/// Texture target type.
|
||||
/// </summary>
|
||||
public Target Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture format information.
|
||||
/// </summary>
|
||||
public FormatInfo FormatInfo { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Depth-stencil mode of the texture. This defines whenever the depth or stencil value is read from shaders,
|
||||
/// for depth-stencil texture formats.
|
||||
/// </summary>
|
||||
public DepthStencilMode DepthStencilMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Texture swizzle for the red color channel.
|
||||
/// </summary>
|
||||
public SwizzleComponent SwizzleR { get; }
|
||||
/// <summary>
|
||||
/// Texture swizzle for the green color channel.
|
||||
/// </summary>
|
||||
public SwizzleComponent SwizzleG { get; }
|
||||
/// <summary>
|
||||
/// Texture swizzle for the blue color channel.
|
||||
/// </summary>
|
||||
public SwizzleComponent SwizzleB { get; }
|
||||
/// <summary>
|
||||
/// Texture swizzle for the alpha color channel.
|
||||
/// </summary>
|
||||
public SwizzleComponent SwizzleA { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs the texture information structure.
|
||||
/// </summary>
|
||||
/// <param name="address">The address of the texture</param>
|
||||
/// <param name="width">The width of the texture</param>
|
||||
/// <param name="height">The height or the texture</param>
|
||||
/// <param name="depthOrLayers">The depth or layers count of the texture</param>
|
||||
/// <param name="levels">The amount of mipmap levels of the texture</param>
|
||||
/// <param name="samplesInX">The number of samples in the X direction for multisample textures, should be 1 otherwise</param>
|
||||
/// <param name="samplesInY">The number of samples in the Y direction for multisample textures, should be 1 otherwise</param>
|
||||
/// <param name="stride">The stride for linear textures</param>
|
||||
/// <param name="isLinear">Whenever the texture is linear or block linear</param>
|
||||
/// <param name="gobBlocksInY">Number of GOB blocks in the Y direction</param>
|
||||
/// <param name="gobBlocksInZ">Number of GOB blocks in the Z direction</param>
|
||||
/// <param name="gobBlocksInTileX">Number of GOB blocks per tile in the X direction</param>
|
||||
/// <param name="target">Texture target type</param>
|
||||
/// <param name="formatInfo">Texture format information</param>
|
||||
/// <param name="depthStencilMode">Depth-stencil mode</param>
|
||||
/// <param name="swizzleR">Swizzle for the red color channel</param>
|
||||
/// <param name="swizzleG">Swizzle for the green color channel</param>
|
||||
/// <param name="swizzleB">Swizzle for the blue color channel</param>
|
||||
/// <param name="swizzleA">Swizzle for the alpha color channel</param>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the real texture depth.
|
||||
/// Returns 1 for any target other than 3D textures.
|
||||
/// </summary>
|
||||
/// <returns>Texture depth</returns>
|
||||
public int GetDepth()
|
||||
{
|
||||
return Target == Target.Texture3D ? DepthOrLayers : 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of layers of the texture.
|
||||
/// Returns 1 for non-array textures, 6 for cubemap textures, and layer faces for cubemap array textures.
|
||||
/// </summary>
|
||||
/// <returns>The number of texture layers</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
779
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal file
779
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal file
|
@ -0,0 +1,779 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture manager.
|
||||
/// </summary>
|
||||
class TextureManager : IDisposable
|
||||
{
|
||||
private const int OverlapsBufferInitialCapacity = 10;
|
||||
private const int OverlapsBufferMaxCapacity = 10000;
|
||||
|
||||
private readonly GpuContext _context;
|
||||
|
||||
private readonly TextureBindingsManager _cpBindingsManager;
|
||||
private readonly TextureBindingsManager _gpBindingsManager;
|
||||
|
||||
private readonly Texture[] _rtColors;
|
||||
|
||||
private Texture _rtDepthStencil;
|
||||
|
||||
private readonly ITexture[] _rtHostColors;
|
||||
|
||||
private ITexture _rtHostDs;
|
||||
|
||||
private readonly RangeList<Texture> _textures;
|
||||
|
||||
private Texture[] _textureOverlaps;
|
||||
|
||||
private readonly AutoDeleteCache _cache;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the texture manager.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context that the texture manager belongs to</param>
|
||||
public TextureManager(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
TexturePoolCache texturePoolCache = new TexturePoolCache(context);
|
||||
|
||||
_cpBindingsManager = new TextureBindingsManager(context, texturePoolCache, isCompute: true);
|
||||
_gpBindingsManager = new TextureBindingsManager(context, texturePoolCache, isCompute: false);
|
||||
|
||||
_rtColors = new Texture[Constants.TotalRenderTargets];
|
||||
|
||||
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
||||
|
||||
_textures = new RangeList<Texture>();
|
||||
|
||||
_textureOverlaps = new Texture[OverlapsBufferInitialCapacity];
|
||||
|
||||
_cache = new AutoDeleteCache();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets texture bindings on the compute pipeline.
|
||||
/// </summary>
|
||||
/// <param name="bindings">The texture bindings</param>
|
||||
public void SetComputeTextures(TextureBindingInfo[] bindings)
|
||||
{
|
||||
_cpBindingsManager.SetTextures(0, bindings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets texture bindings on the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="stage">The index of the shader stage to bind the textures</param>
|
||||
/// <param name="bindings">The texture bindings</param>
|
||||
public void SetGraphicsTextures(int stage, TextureBindingInfo[] bindings)
|
||||
{
|
||||
_gpBindingsManager.SetTextures(stage, bindings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets image bindings on the compute pipeline.
|
||||
/// </summary>
|
||||
/// <param name="bindings">The image bindings</param>
|
||||
public void SetComputeImages(TextureBindingInfo[] bindings)
|
||||
{
|
||||
_cpBindingsManager.SetImages(0, bindings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets image bindings on the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="stage">The index of the shader stage to bind the images</param>
|
||||
/// <param name="bindings">The image bindings</param>
|
||||
public void SetGraphicsImages(int stage, TextureBindingInfo[] bindings)
|
||||
{
|
||||
_gpBindingsManager.SetImages(stage, bindings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the texture constant buffer index on the compute pipeline.
|
||||
/// </summary>
|
||||
/// <param name="index">The texture constant buffer index</param>
|
||||
public void SetComputeTextureBufferIndex(int index)
|
||||
{
|
||||
_cpBindingsManager.SetTextureBufferIndex(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the texture constant buffer index on the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="index">The texture constant buffer index</param>
|
||||
public void SetGraphicsTextureBufferIndex(int index)
|
||||
{
|
||||
_gpBindingsManager.SetTextureBufferIndex(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current sampler pool on the compute pipeline.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">The start GPU virtual address of the sampler pool</param>
|
||||
/// <param name="maximumId">The maximum ID of the sampler pool</param>
|
||||
/// <param name="samplerIndex">The indexing type of the sampler pool</param>
|
||||
public void SetComputeSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
|
||||
{
|
||||
_cpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current sampler pool on the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">The start GPU virtual address of the sampler pool</param>
|
||||
/// <param name="maximumId">The maximum ID of the sampler pool</param>
|
||||
/// <param name="samplerIndex">The indexing type of the sampler pool</param>
|
||||
public void SetGraphicsSamplerPool(ulong gpuVa, int maximumId, SamplerIndex samplerIndex)
|
||||
{
|
||||
_gpBindingsManager.SetSamplerPool(gpuVa, maximumId, samplerIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current texture pool on the compute pipeline.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">The start GPU virtual address of the texture pool</param>
|
||||
/// <param name="maximumId">The maximum ID of the texture pool</param>
|
||||
public void SetComputeTexturePool(ulong gpuVa, int maximumId)
|
||||
{
|
||||
_cpBindingsManager.SetTexturePool(gpuVa, maximumId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current texture pool on the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">The start GPU virtual address of the texture pool</param>
|
||||
/// <param name="maximumId">The maximum ID of the texture pool</param>
|
||||
public void SetGraphicsTexturePool(ulong gpuVa, int maximumId)
|
||||
{
|
||||
_gpBindingsManager.SetTexturePool(gpuVa, maximumId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the render target color buffer.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the color buffer to set (up to 8)</param>
|
||||
/// <param name="color">The color buffer texture</param>
|
||||
public void SetRenderTargetColor(int index, Texture color)
|
||||
{
|
||||
_rtColors[index] = color;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the render target depth-stencil buffer.
|
||||
/// </summary>
|
||||
/// <param name="depthStencil">The depth-stencil buffer texture</param>
|
||||
public void SetRenderTargetDepthStencil(Texture depthStencil)
|
||||
{
|
||||
_rtDepthStencil = depthStencil;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits bindings on the compute pipeline.
|
||||
/// </summary>
|
||||
public void CommitComputeBindings()
|
||||
{
|
||||
// Every time we switch between graphics and compute work,
|
||||
// we must rebind everything.
|
||||
// Since compute work happens less often, we always do that
|
||||
// before and after the compute dispatch.
|
||||
_cpBindingsManager.Rebind();
|
||||
_cpBindingsManager.CommitBindings();
|
||||
_gpBindingsManager.Rebind();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Commits bindings on the graphics pipeline.
|
||||
/// </summary>
|
||||
public void CommitGraphicsBindings()
|
||||
{
|
||||
_gpBindingsManager.CommitBindings();
|
||||
|
||||
UpdateRenderTargets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a texture descriptor used on the graphics pipeline.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="stageIndex">Index of the shader stage where the texture is bound</param>
|
||||
/// <param name="handle">Shader "fake" handle of the texture</param>
|
||||
/// <returns>The texture descriptor</returns>
|
||||
public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle)
|
||||
{
|
||||
return _gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update host framebuffer attachments based on currently bound render target buffers.
|
||||
/// </summary>
|
||||
private void UpdateRenderTargets()
|
||||
{
|
||||
bool anyChanged = false;
|
||||
|
||||
if (_rtHostDs != _rtDepthStencil?.HostTexture)
|
||||
{
|
||||
_rtHostDs = _rtDepthStencil?.HostTexture;
|
||||
|
||||
anyChanged = true;
|
||||
}
|
||||
|
||||
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.Pipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find an existing texture, or create a new one if not found.
|
||||
/// </summary>
|
||||
/// <param name="copyTexture">Copy texture to find or create</param>
|
||||
/// <returns>The texture</returns>
|
||||
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();
|
||||
|
||||
int width;
|
||||
|
||||
if (copyTexture.LinearLayout)
|
||||
{
|
||||
width = copyTexture.Stride / formatInfo.BytesPerPixel;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = copyTexture.Width;
|
||||
}
|
||||
|
||||
TextureInfo info = new TextureInfo(
|
||||
address,
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find an existing texture, or create a new one if not found.
|
||||
/// </summary>
|
||||
/// <param name="colorState">Color buffer texture to find or create</param>
|
||||
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
||||
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
||||
/// <returns>The texture</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find an existing texture, or create a new one if not found.
|
||||
/// </summary>
|
||||
/// <param name="dsState">Depth-stencil buffer texture to find or create</param>
|
||||
/// <param name="size">Size of the depth-stencil texture</param>
|
||||
/// <param name="samplesInX">Number of samples in the X direction, for MSAA</param>
|
||||
/// <param name="samplesInY">Number of samples in the Y direction, for MSAA</param>
|
||||
/// <returns>The texture</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to find an existing texture, or create a new one if not found.
|
||||
/// </summary>
|
||||
/// <param name="info">Texture information of the texture to be found or created</param>
|
||||
/// <param name="flags">The texture search flags, defines texture comparison rules</param>
|
||||
/// <returns>The texture</returns>
|
||||
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.
|
||||
int sameAddressOverlapsCount = _textures.FindOverlaps(info.Address, ref _textureOverlaps);
|
||||
|
||||
for (int index = 0; index < sameAddressOverlapsCount; index++)
|
||||
{
|
||||
Texture overlap = _textureOverlaps[index];
|
||||
|
||||
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;
|
||||
|
||||
int overlapsCount = _textures.FindOverlaps(info.Address, size, ref _textureOverlaps);
|
||||
|
||||
Texture texture = null;
|
||||
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
Texture overlap = _textureOverlaps[index];
|
||||
|
||||
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();
|
||||
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
Texture overlap = _textureOverlaps[index];
|
||||
|
||||
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, 0, 0);
|
||||
|
||||
overlap.ReplaceView(texture, overlapInfo, newView);
|
||||
}
|
||||
}
|
||||
|
||||
// If the texture is a 3D texture, we need to additionally copy any slice
|
||||
// of the 3D texture to the newly created 3D texture.
|
||||
if (info.Target == Target.Texture3D)
|
||||
{
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
Texture overlap = _textureOverlaps[index];
|
||||
|
||||
if (texture.IsViewCompatible(
|
||||
overlap.Info,
|
||||
overlap.Size,
|
||||
isCopy: true,
|
||||
out int firstLayer,
|
||||
out int firstLevel))
|
||||
{
|
||||
overlap.HostTexture.CopyTo(texture.HostTexture, firstLayer, firstLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
ShrinkOverlapsBufferIfNeeded();
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
|
||||
/// </summary>
|
||||
private void ShrinkOverlapsBufferIfNeeded()
|
||||
{
|
||||
if (_textureOverlaps.Length > OverlapsBufferMaxCapacity)
|
||||
{
|
||||
Array.Resize(ref _textureOverlaps, OverlapsBufferMaxCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts the size of the texture information for a given mipmap level,
|
||||
/// based on the size of a parent texture.
|
||||
/// </summary>
|
||||
/// <param name="parent">The parent texture</param>
|
||||
/// <param name="info">The texture information to be adjusted</param>
|
||||
/// <param name="firstLevel">The first level of the texture view</param>
|
||||
/// <returns>The adjusted texture information with the new size</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets a texture creation information from texture information.
|
||||
/// This can be used to create new host textures.
|
||||
/// </summary>
|
||||
/// <param name="info">Texture information</param>
|
||||
/// <param name="caps">GPU capabilities</param>
|
||||
/// <returns>The texture creation information</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes all the textures in the cache that have been modified since the last call.
|
||||
/// </summary>
|
||||
public void Flush()
|
||||
{
|
||||
foreach (Texture texture in _cache)
|
||||
{
|
||||
if (texture.Info.IsLinear && texture.Modified)
|
||||
{
|
||||
texture.Flush();
|
||||
|
||||
texture.Modified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes the textures in the cache inside a given range that have been modified since the last call.
|
||||
/// </summary>
|
||||
/// <param name="address">The range start address</param>
|
||||
/// <param name="size">The range size</param>
|
||||
public void Flush(ulong address, ulong size)
|
||||
{
|
||||
foreach (Texture texture in _cache)
|
||||
{
|
||||
if (texture.OverlapsWith(address, size) && texture.Modified)
|
||||
{
|
||||
texture.Flush();
|
||||
|
||||
texture.Modified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a texture from the cache.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This only removes the texture from the internal list, not from the auto-deletion cache.
|
||||
/// It may still have live references after the removal.
|
||||
/// </remarks>
|
||||
/// <param name="texture">The texture to be removed</param>
|
||||
public void RemoveTextureFromCache(Texture texture)
|
||||
{
|
||||
_textures.Remove(texture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all textures in the cache.
|
||||
/// It's an error to use the texture manager after disposal.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (Texture texture in _textures)
|
||||
{
|
||||
texture.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
68
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal file
68
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal file
|
@ -0,0 +1,68 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Multisampled texture samples count.
|
||||
/// </summary>
|
||||
enum TextureMsaaMode
|
||||
{
|
||||
Ms1x1 = 0,
|
||||
Ms2x2 = 2,
|
||||
Ms4x2 = 4,
|
||||
Ms2x1 = 5,
|
||||
Ms4x4 = 6
|
||||
}
|
||||
|
||||
static class TextureMsaaModeConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the total number of samples from the MSAA mode.
|
||||
/// </summary>
|
||||
/// <param name="msaaMode">The MSAA mode</param>
|
||||
/// <returns>The total number of samples</returns>
|
||||
public static int SamplesCount(this TextureMsaaMode msaaMode)
|
||||
{
|
||||
return msaaMode switch
|
||||
{
|
||||
TextureMsaaMode.Ms2x1 => 2,
|
||||
TextureMsaaMode.Ms2x2 => 4,
|
||||
TextureMsaaMode.Ms4x2 => 8,
|
||||
TextureMsaaMode.Ms4x4 => 16,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of samples in the X direction from the MSAA mode.
|
||||
/// </summary>
|
||||
/// <param name="msaaMode">The MSAA mode</param>
|
||||
/// <returns>The number of samples in the X direction</returns>
|
||||
public static int SamplesInX(this TextureMsaaMode msaaMode)
|
||||
{
|
||||
return msaaMode switch
|
||||
{
|
||||
TextureMsaaMode.Ms2x1 => 2,
|
||||
TextureMsaaMode.Ms2x2 => 2,
|
||||
TextureMsaaMode.Ms4x2 => 4,
|
||||
TextureMsaaMode.Ms4x4 => 4,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of samples in the Y direction from the MSAA mode.
|
||||
/// </summary>
|
||||
/// <param name="msaaMode">The MSAA mode</param>
|
||||
/// <returns>The number of samples in the Y direction</returns>
|
||||
public static int SamplesInY(this TextureMsaaMode msaaMode)
|
||||
{
|
||||
return msaaMode switch
|
||||
{
|
||||
TextureMsaaMode.Ms2x1 => 1,
|
||||
TextureMsaaMode.Ms2x2 => 2,
|
||||
TextureMsaaMode.Ms4x2 => 2,
|
||||
TextureMsaaMode.Ms4x4 => 4,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
265
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal file
265
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal file
|
@ -0,0 +1,265 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture pool.
|
||||
/// </summary>
|
||||
class TexturePool : Pool<Texture>
|
||||
{
|
||||
private int _sequenceNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Intrusive linked list node used on the texture pool cache.
|
||||
/// </summary>
|
||||
public LinkedListNode<TexturePool> CacheNode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the texture pool.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||
/// <param name="address">Address of the texture pool in guest memory</param>
|
||||
/// <param name="maximumId">Maximum texture ID of the texture pool (equal to maximum textures minus one)</param>
|
||||
public TexturePool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture with the given ID.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||
/// <returns>The texture with the given ID</returns>
|
||||
public override Texture Get(int id)
|
||||
{
|
||||
if ((uint)id >= Items.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_sequenceNumber != Context.SequenceNumber)
|
||||
{
|
||||
_sequenceNumber = Context.SequenceNumber;
|
||||
|
||||
SynchronizeMemory();
|
||||
}
|
||||
|
||||
Texture texture = Items[id];
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
TextureDescriptor descriptor = GetDescriptor(id);
|
||||
|
||||
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 = Context.Methods.TextureManager.FindOrCreateTexture(info, TextureSearchFlags.Sampler);
|
||||
|
||||
texture.IncrementReferenceCount();
|
||||
|
||||
Items[id] = texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Memory is automatically synchronized on texture creation.
|
||||
texture.SynchronizeMemory();
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor from a given texture ID.
|
||||
/// </summary>
|
||||
/// <param name="id">ID of the texture. This is effectively a zero-based index</param>
|
||||
/// <returns>The texture descriptor</returns>
|
||||
public TextureDescriptor GetDescriptor(int id)
|
||||
{
|
||||
ulong address = Address + (ulong)(uint)id * DescriptorSize;
|
||||
|
||||
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
|
||||
|
||||
return MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of the texture pool range invalidation.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the range of the texture pool</param>
|
||||
/// <param name="size">Size of the range being invalidated</param>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets texture information from a texture descriptor.
|
||||
/// </summary>
|
||||
/// <param name="descriptor">The texture descriptor</param>
|
||||
/// <returns>The texture information</returns>
|
||||
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))
|
||||
{
|
||||
Logger.PrintDebug(LogClass.Gpu, $"Invalid texture format 0x{format:X} (sRGB: {srgb}).");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
|
||||
/// The depth-stencil mode is determined based on how the driver sets those parameters.
|
||||
/// </summary>
|
||||
/// <param name="format">The format of the texture</param>
|
||||
/// <param name="components">The texture swizzle components</param>
|
||||
/// <returns>The depth-stencil mode</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the swizzle component is equal to the red or green channels.
|
||||
/// </summary>
|
||||
/// <param name="component">The swizzle component to check</param>
|
||||
/// <returns>True if the swizzle component is equal to the red or green, false otherwise</returns>
|
||||
private static bool IsRG(SwizzleComponent component)
|
||||
{
|
||||
return component == SwizzleComponent.Red ||
|
||||
component == SwizzleComponent.Green;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decrements the reference count of the texture.
|
||||
/// This indicates that the texture pool is not using it anymore.
|
||||
/// </summary>
|
||||
/// <param name="item">The texture to be deleted</param>
|
||||
protected override void Delete(Texture item)
|
||||
{
|
||||
item?.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
}
|
91
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal file
91
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal file
|
@ -0,0 +1,91 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture pool cache.
|
||||
/// This can keep multiple texture pools, and return the current one as needed.
|
||||
/// It is useful for applications that uses multiple texture pools.
|
||||
/// </summary>
|
||||
class TexturePoolCache
|
||||
{
|
||||
private const int MaxCapacity = 4;
|
||||
|
||||
private GpuContext _context;
|
||||
|
||||
private LinkedList<TexturePool> _pools;
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new instance of the texture pool.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that the texture pool belongs to</param>
|
||||
public TexturePoolCache(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_pools = new LinkedList<TexturePool>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds a cache texture pool, or creates a new one if not found.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the texture pool</param>
|
||||
/// <param name="maximumId">Maximum ID of the texture pool</param>
|
||||
/// <returns>The found or newly created texture pool</returns>
|
||||
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, 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates a memory range of all intersecting texture pools on the cache.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the range to invalidate</param>
|
||||
/// <param name="size">Size of the range to invalidate</param>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal file
16
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture search flags, defines texture information comparison rules.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
enum TextureSearchFlags
|
||||
{
|
||||
None = 0,
|
||||
IgnoreMs = 1 << 0,
|
||||
Strict = 1 << 1 | Sampler,
|
||||
Sampler = 1 << 2
|
||||
}
|
||||
}
|
58
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal file
58
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal file
|
@ -0,0 +1,58 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
/// <summary>
|
||||
/// Texture target.
|
||||
/// </summary>
|
||||
enum TextureTarget
|
||||
{
|
||||
Texture1D,
|
||||
Texture2D,
|
||||
Texture3D,
|
||||
Cubemap,
|
||||
Texture1DArray,
|
||||
Texture2DArray,
|
||||
TextureBuffer,
|
||||
Texture2DRect,
|
||||
CubemapArray
|
||||
}
|
||||
|
||||
static class TextureTargetConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts the texture target enum to a host compatible, Graphics Abstraction Layer enum.
|
||||
/// </summary>
|
||||
/// <param name="target">The target enum to convert</param>
|
||||
/// <param name="isMultisample">True if the texture is a multisampled texture</param>
|
||||
/// <returns>The host compatible texture target</returns>
|
||||
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.Texture2DRect: 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,10 +1,13 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Memory;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Graphics3d
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
/// <summary>
|
||||
/// Macro code interpreter.
|
||||
/// </summary>
|
||||
class MacroInterpreter
|
||||
{
|
||||
private enum AssignmentOperation
|
||||
|
@ -42,9 +45,6 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
BitwiseNotAnd = 12
|
||||
}
|
||||
|
||||
private NvGpuFifo _pFifo;
|
||||
private INvGpuEngine _engine;
|
||||
|
||||
public Queue<int> Fifo { get; private set; }
|
||||
|
||||
private int[] _gprs;
|
||||
|
@ -60,17 +60,24 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
private int _pc;
|
||||
|
||||
public MacroInterpreter(NvGpuFifo pFifo, INvGpuEngine engine)
|
||||
/// <summary>
|
||||
/// Creates a new instance of the macro code interpreter.
|
||||
/// </summary>
|
||||
public MacroInterpreter()
|
||||
{
|
||||
_pFifo = pFifo;
|
||||
_engine = engine;
|
||||
|
||||
Fifo = new Queue<int>();
|
||||
|
||||
_gprs = new int[8];
|
||||
}
|
||||
|
||||
public void Execute(NvGpuVmm vmm, int[] mme, int position, int param)
|
||||
/// <summary>
|
||||
/// Executes a macro program until it exits.
|
||||
/// </summary>
|
||||
/// <param name="mme">Code of the program to execute</param>
|
||||
/// <param name="position">Start position to execute</param>
|
||||
/// <param name="param">Optional argument passed to the program, 0 if not used</param>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
public void Execute(int[] mme, int position, int param, GpuState state)
|
||||
{
|
||||
Reset();
|
||||
|
||||
|
@ -80,13 +87,17 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
FetchOpCode(mme);
|
||||
|
||||
while (Step(vmm, mme));
|
||||
while (Step(mme, state));
|
||||
|
||||
// Due to the delay slot, we still need to execute
|
||||
// one more instruction before we actually exit.
|
||||
Step(vmm, mme);
|
||||
Step(mme, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the internal interpreter state.
|
||||
/// Call each time you run a new program.
|
||||
/// </summary>
|
||||
private void Reset()
|
||||
{
|
||||
for (int index = 0; index < _gprs.Length; index++)
|
||||
|
@ -100,7 +111,13 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
_carry = false;
|
||||
}
|
||||
|
||||
private bool Step(NvGpuVmm vmm, int[] mme)
|
||||
/// <summary>
|
||||
/// Executes a single instruction of the program.
|
||||
/// </summary>
|
||||
/// <param name="mme">Program code to execute</param>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <returns>True to continue execution, false if the program exited</returns>
|
||||
private bool Step(int[] mme, GpuState state)
|
||||
{
|
||||
int baseAddr = _pc - 1;
|
||||
|
||||
|
@ -111,7 +128,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
// Operation produces a value.
|
||||
AssignmentOperation asgOp = (AssignmentOperation)((_opCode >> 4) & 7);
|
||||
|
||||
int result = GetAluResult();
|
||||
int result = GetAluResult(state);
|
||||
|
||||
switch (asgOp)
|
||||
{
|
||||
|
@ -146,7 +163,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
Send(vmm, result);
|
||||
Send(state, result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -156,7 +173,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
{
|
||||
SetDstGpr(result);
|
||||
|
||||
Send(vmm, result);
|
||||
Send(state, result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -178,7 +195,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
SetMethAddr(result);
|
||||
|
||||
Send(vmm, FetchParam());
|
||||
Send(state, FetchParam());
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -190,7 +207,7 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
SetMethAddr(result);
|
||||
|
||||
Send(vmm, (result >> 12) & 0x3f);
|
||||
Send(state, (result >> 12) & 0x3f);
|
||||
|
||||
break;
|
||||
}
|
||||
|
@ -225,14 +242,22 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
return !exit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a single operation code from the program code.
|
||||
/// </summary>
|
||||
/// <param name="mme">Program code</param>
|
||||
private void FetchOpCode(int[] mme)
|
||||
{
|
||||
_opCode = _pipeOp;
|
||||
|
||||
_pipeOp = mme[_pc++];
|
||||
}
|
||||
|
||||
private int GetAluResult()
|
||||
/// <summary>
|
||||
/// Gets the result of the current Arithmetic and Logic unit operation.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <returns>Operation result</returns>
|
||||
private int GetAluResult(GpuState state)
|
||||
{
|
||||
AluOperation op = (AluOperation)(_opCode & 7);
|
||||
|
||||
|
@ -296,13 +321,20 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
|
||||
case AluOperation.ReadImmediate:
|
||||
{
|
||||
return Read(GetGprA() + GetImm());
|
||||
return Read(state, GetGprA() + GetImm());
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(_opCode));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the result of an Arithmetic and Logic operation using registers.
|
||||
/// </summary>
|
||||
/// <param name="aluOp">Arithmetic and Logic unit operation with registers</param>
|
||||
/// <param name="a">First operand value</param>
|
||||
/// <param name="b">Second operand value</param>
|
||||
/// <returns>Operation result</returns>
|
||||
private int GetAluResult(AluRegOperation aluOp, int a, int b)
|
||||
{
|
||||
switch (aluOp)
|
||||
|
@ -353,43 +385,70 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
throw new ArgumentOutOfRangeException(nameof(aluOp));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts a 32-bits signed integer constant from the current operation code.
|
||||
/// </summary>
|
||||
/// <returns>The 32-bits immediate value encoded at the current operation code</returns>
|
||||
private int GetImm()
|
||||
{
|
||||
// Note: The immediate is signed, the sign-extension is intended here.
|
||||
return _opCode >> 14;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current method address, for method calls.
|
||||
/// </summary>
|
||||
/// <param name="value">Packed address and increment value</param>
|
||||
private void SetMethAddr(int value)
|
||||
{
|
||||
_methAddr = (value >> 0) & 0xfff;
|
||||
_methIncr = (value >> 12) & 0x3f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the destination register value.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to set (usually the operation result)</param>
|
||||
private void SetDstGpr(int value)
|
||||
{
|
||||
_gprs[(_opCode >> 8) & 7] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets first operand value from the respective register.
|
||||
/// </summary>
|
||||
/// <returns>Operand value</returns>
|
||||
private int GetGprA()
|
||||
{
|
||||
return GetGprValue((_opCode >> 11) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets second operand value from the respective register.
|
||||
/// </summary>
|
||||
/// <returns>Operand value</returns>
|
||||
private int GetGprB()
|
||||
{
|
||||
return GetGprValue((_opCode >> 14) & 7);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value from a register, or 0 if the R0 register is specified.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the register</param>
|
||||
/// <returns>Register value</returns>
|
||||
private int GetGprValue(int index)
|
||||
{
|
||||
return index != 0 ? _gprs[index] : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a call argument from the call argument FIFO.
|
||||
/// </summary>
|
||||
/// <returns>The call argument, or 0 if the FIFO is empty</returns>
|
||||
private int FetchParam()
|
||||
{
|
||||
int value;
|
||||
|
||||
if (!Fifo.TryDequeue(out value))
|
||||
if (!Fifo.TryDequeue(out int value))
|
||||
{
|
||||
Logger.PrintWarning(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
|
||||
|
||||
|
@ -399,16 +458,27 @@ namespace Ryujinx.Graphics.Graphics3d
|
|||
return value;
|
||||
}
|
||||
|
||||
private int Read(int reg)
|
||||
/// <summary>
|
||||
/// Reads data from a GPU register.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="reg">Register offset to read</param>
|
||||
/// <returns>GPU register value</returns>
|
||||
private int Read(GpuState state, int reg)
|
||||
{
|
||||
return _engine.Registers[reg];
|
||||
return state.Read(reg);
|
||||
}
|
||||
|
||||
private void Send(NvGpuVmm vmm, int value)
|
||||
/// <summary>
|
||||
/// Performs a GPU method call.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="value">Call argument</param>
|
||||
private void Send(GpuState state, int value)
|
||||
{
|
||||
GpuMethodCall methCall = new GpuMethodCall(_methAddr, value);
|
||||
MethodParams meth = new MethodParams(_methAddr, value);
|
||||
|
||||
_engine.CallMethod(vmm, methCall);
|
||||
state.CallMethod(meth);
|
||||
|
||||
_methAddr += _methIncr;
|
||||
}
|
171
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
171
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
|
@ -0,0 +1,171 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// Buffer, used to store vertex and index data, uniform and storage buffers, and others.
|
||||
/// </summary>
|
||||
class Buffer : IRange, IDisposable
|
||||
{
|
||||
private readonly GpuContext _context;
|
||||
|
||||
/// <summary>
|
||||
/// Host buffer object.
|
||||
/// </summary>
|
||||
public IBuffer HostBuffer { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Start address of the buffer in guest memory.
|
||||
/// </summary>
|
||||
public ulong Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Size of the buffer in bytes.
|
||||
/// </summary>
|
||||
public ulong Size { get; }
|
||||
|
||||
/// <summary>
|
||||
/// End address of the buffer in guest memory.
|
||||
/// </summary>
|
||||
public ulong EndAddress => Address + Size;
|
||||
|
||||
private int[] _sequenceNumbers;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the buffer.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that the buffer belongs to</param>
|
||||
/// <param name="address">Start address of the buffer</param>
|
||||
/// <param name="size">Size of the buffer in bytes</param>
|
||||
public Buffer(GpuContext context, ulong address, ulong size)
|
||||
{
|
||||
_context = context;
|
||||
Address = address;
|
||||
Size = size;
|
||||
|
||||
HostBuffer = context.Renderer.CreateBuffer((int)size);
|
||||
|
||||
_sequenceNumbers = new int[size / MemoryManager.PageSize];
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a sub-range from the buffer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This can be used to bind and use sub-ranges of the buffer on the host API.
|
||||
/// </remarks>
|
||||
/// <param name="address">Start address of the sub-range, must be greater than or equal to the buffer address</param>
|
||||
/// <param name="size">Size in bytes of the sub-range, must be less than or equal to the buffer size</param>
|
||||
/// <returns>The buffer sub-range</returns>
|
||||
public BufferRange GetRange(ulong address, ulong size)
|
||||
{
|
||||
int offset = (int)(address - Address);
|
||||
|
||||
return new BufferRange(HostBuffer, offset, (int)size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a given range overlaps with the buffer.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the range</param>
|
||||
/// <param name="size">Size in bytes of the range</param>
|
||||
/// <returns>True if the range overlaps, false otherwise</returns>
|
||||
public bool OverlapsWith(ulong address, ulong size)
|
||||
{
|
||||
return Address < address + size && address < EndAddress;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs guest to host memory synchronization of the buffer data.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This causes the buffer data to be overwritten if a write was detected from the CPU,
|
||||
/// since the last call to this method.
|
||||
/// </remarks>
|
||||
/// <param name="address">Start address of the range to synchronize</param>
|
||||
/// <param name="size">Size in bytes of the range to synchronize</param>
|
||||
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, ResourceName.Buffer);
|
||||
|
||||
for (int index = 0; index < modifiedRanges.Length; index++)
|
||||
{
|
||||
(ulong mAddress, ulong mSize) = modifiedRanges[index];
|
||||
|
||||
int offset = (int)(mAddress - Address);
|
||||
|
||||
HostBuffer.SetData(offset, _context.PhysicalMemory.Read(mAddress, mSize));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs copy of all the buffer data from one buffer to another.
|
||||
/// </summary>
|
||||
/// <param name="destination">The destination buffer to copy the data into</param>
|
||||
/// <param name="dstOffset">The offset of the destination buffer to copy into</param>
|
||||
public void CopyTo(Buffer destination, int dstOffset)
|
||||
{
|
||||
HostBuffer.CopyTo(destination.HostBuffer, 0, dstOffset, (int)Size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flushes a range of the buffer.
|
||||
/// This writes the range data back into guest memory.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the range</param>
|
||||
/// <param name="size">Size in bytes of the range</param>
|
||||
public void Flush(ulong address, ulong size)
|
||||
{
|
||||
int offset = (int)(address - Address);
|
||||
|
||||
byte[] data = HostBuffer.GetData(offset, (int)size);
|
||||
|
||||
_context.PhysicalMemory.Write(address, data);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidates all the buffer data, causing it to be read from guest memory.
|
||||
/// </summary>
|
||||
public void Invalidate()
|
||||
{
|
||||
HostBuffer.SetData(0, _context.PhysicalMemory.Read(Address, Size));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the host buffer.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
HostBuffer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
11
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// Memory range used for buffers.
|
||||
/// </summary>
|
||||
struct BufferBounds
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong Size;
|
||||
}
|
||||
}
|
701
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
Normal file
701
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
Normal file
|
@ -0,0 +1,701 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// Buffer manager.
|
||||
/// </summary>
|
||||
class BufferManager
|
||||
{
|
||||
private const int OverlapsBufferInitialCapacity = 10;
|
||||
private const int OverlapsBufferMaxCapacity = 10000;
|
||||
|
||||
private const ulong BufferAlignmentSize = 0x1000;
|
||||
private const ulong BufferAlignmentMask = BufferAlignmentSize - 1;
|
||||
|
||||
private GpuContext _context;
|
||||
|
||||
private RangeList<Buffer> _buffers;
|
||||
|
||||
private Buffer[] _bufferOverlaps;
|
||||
|
||||
private IndexBuffer _indexBuffer;
|
||||
|
||||
private VertexBuffer[] _vertexBuffers;
|
||||
|
||||
private class BuffersPerStage
|
||||
{
|
||||
public uint EnableMask { get; set; }
|
||||
|
||||
public BufferBounds[] Buffers { get; }
|
||||
|
||||
public BuffersPerStage(int count)
|
||||
{
|
||||
Buffers = new BufferBounds[count];
|
||||
}
|
||||
|
||||
public void Bind(int index, ulong address, ulong size)
|
||||
{
|
||||
Buffers[index].Address = address;
|
||||
Buffers[index].Size = size;
|
||||
}
|
||||
}
|
||||
|
||||
private BuffersPerStage _cpStorageBuffers;
|
||||
private BuffersPerStage _cpUniformBuffers;
|
||||
private BuffersPerStage[] _gpStorageBuffers;
|
||||
private BuffersPerStage[] _gpUniformBuffers;
|
||||
|
||||
private bool _gpStorageBuffersDirty;
|
||||
private bool _gpUniformBuffersDirty;
|
||||
|
||||
private bool _indexBufferDirty;
|
||||
private bool _vertexBuffersDirty;
|
||||
private uint _vertexBuffersEnableMask;
|
||||
|
||||
private bool _rebind;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the buffer manager.
|
||||
/// </summary>
|
||||
/// <param name="context">The GPU context that the buffer manager belongs to</param>
|
||||
public BufferManager(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_buffers = new RangeList<Buffer>();
|
||||
|
||||
_bufferOverlaps = new Buffer[OverlapsBufferInitialCapacity];
|
||||
|
||||
_vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers];
|
||||
|
||||
_cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers);
|
||||
_cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers);
|
||||
|
||||
_gpStorageBuffers = new BuffersPerStage[Constants.ShaderStages];
|
||||
_gpUniformBuffers = new BuffersPerStage[Constants.ShaderStages];
|
||||
|
||||
for (int index = 0; index < Constants.ShaderStages; index++)
|
||||
{
|
||||
_gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers);
|
||||
_gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the memory range with the index buffer data, to be used for subsequent draw calls.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the index buffer</param>
|
||||
/// <param name="size">Size, in bytes, of the index buffer</param>
|
||||
/// <param name="type">Type of each index buffer element</param>
|
||||
public void SetIndexBuffer(ulong gpuVa, ulong size, IndexType type)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_indexBuffer.Address = address;
|
||||
_indexBuffer.Size = size;
|
||||
_indexBuffer.Type = type;
|
||||
|
||||
_indexBufferDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the memory range with vertex buffer data, to be used for subsequent draw calls.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the vertex buffer (up to 16)</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
/// <param name="stride">Stride of the buffer, defined as the number of bytes of each vertex</param>
|
||||
/// <param name="divisor">Vertex divisor of the buffer, for instanced draws</param>
|
||||
public void SetVertexBuffer(int index, ulong gpuVa, ulong size, int stride, int divisor)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_vertexBuffers[index].Address = address;
|
||||
_vertexBuffers[index].Size = size;
|
||||
_vertexBuffers[index].Stride = stride;
|
||||
_vertexBuffers[index].Divisor = divisor;
|
||||
|
||||
_vertexBuffersDirty = true;
|
||||
|
||||
if (address != 0)
|
||||
{
|
||||
_vertexBuffersEnableMask |= 1u << index;
|
||||
}
|
||||
else
|
||||
{
|
||||
_vertexBuffersEnableMask &= ~(1u << index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a storage buffer on the compute pipeline.
|
||||
/// Storage buffers can be read and written to on shaders.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the storage buffer</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||
public void SetComputeStorageBuffer(int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
|
||||
|
||||
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
||||
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_cpStorageBuffers.Bind(index, address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a storage buffer on the graphics pipeline.
|
||||
/// Storage buffers can be read and written to on shaders.
|
||||
/// </summary>
|
||||
/// <param name="stage">Index of the shader stage</param>
|
||||
/// <param name="index">Index of the storage buffer</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||
public void SetGraphicsStorageBuffer(int stage, int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
size += gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1);
|
||||
|
||||
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
|
||||
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
if (_gpStorageBuffers[stage].Buffers[index].Address != address ||
|
||||
_gpStorageBuffers[stage].Buffers[index].Size != size)
|
||||
{
|
||||
_gpStorageBuffersDirty = true;
|
||||
}
|
||||
|
||||
_gpStorageBuffers[stage].Bind(index, address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uniform buffer on the compute pipeline.
|
||||
/// Uniform buffers are read-only from shaders, and have a small capacity.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the uniform buffer</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||
public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_cpUniformBuffers.Bind(index, address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets a uniform buffer on the graphics pipeline.
|
||||
/// Uniform buffers are read-only from shaders, and have a small capacity.
|
||||
/// </summary>
|
||||
/// <param name="stage">Index of the shader stage</param>
|
||||
/// <param name="index">Index of the uniform buffer</param>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the storage buffer</param>
|
||||
public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(gpuVa, size);
|
||||
|
||||
_gpUniformBuffers[stage].Bind(index, address, size);
|
||||
|
||||
_gpUniformBuffersDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the enabled storage buffers mask on the compute pipeline.
|
||||
/// Each bit set on the mask indicates that the respective buffer index is enabled.
|
||||
/// </summary>
|
||||
/// <param name="mask">Buffer enable mask</param>
|
||||
public void SetComputeStorageBufferEnableMask(uint mask)
|
||||
{
|
||||
_cpStorageBuffers.EnableMask = mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the enabled storage buffers mask on the graphics pipeline.
|
||||
/// Each bit set on the mask indicates that the respective buffer index is enabled.
|
||||
/// </summary>
|
||||
/// <param name="stage">Index of the shader stage</param>
|
||||
/// <param name="mask">Buffer enable mask</param>
|
||||
public void SetGraphicsStorageBufferEnableMask(int stage, uint mask)
|
||||
{
|
||||
_gpStorageBuffers[stage].EnableMask = mask;
|
||||
|
||||
_gpStorageBuffersDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the enabled uniform buffers mask on the compute pipeline.
|
||||
/// Each bit set on the mask indicates that the respective buffer index is enabled.
|
||||
/// </summary>
|
||||
/// <param name="mask">Buffer enable mask</param>
|
||||
public void SetComputeUniformBufferEnableMask(uint mask)
|
||||
{
|
||||
_cpUniformBuffers.EnableMask = mask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the enabled uniform buffers mask on the graphics pipeline.
|
||||
/// Each bit set on the mask indicates that the respective buffer index is enabled.
|
||||
/// </summary>
|
||||
/// <param name="stage">Index of the shader stage</param>
|
||||
/// <param name="mask">Buffer enable mask</param>
|
||||
public void SetGraphicsUniformBufferEnableMask(int stage, uint mask)
|
||||
{
|
||||
_gpUniformBuffers[stage].EnableMask = mask;
|
||||
|
||||
_gpUniformBuffersDirty = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs address translation of the GPU virtual address, and creates a
|
||||
/// new buffer, if needed, for the specified range.
|
||||
/// </summary>
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
/// <returns>CPU virtual address of the buffer, after address translation</returns>
|
||||
private ulong TranslateAndCreateBuffer(ulong gpuVa, ulong size)
|
||||
{
|
||||
if (gpuVa == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
if (address == MemoryManager.BadAddress)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ulong endAddress = address + size;
|
||||
|
||||
ulong alignedAddress = address & ~BufferAlignmentMask;
|
||||
|
||||
ulong alignedEndAddress = (endAddress + BufferAlignmentMask) & ~BufferAlignmentMask;
|
||||
|
||||
// The buffer must have the size of at least one page.
|
||||
if (alignedEndAddress == alignedAddress)
|
||||
{
|
||||
alignedEndAddress += BufferAlignmentSize;
|
||||
}
|
||||
|
||||
CreateBuffer(alignedAddress, alignedEndAddress - alignedAddress);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new buffer for the specified range, if needed.
|
||||
/// If a buffer where this range can be fully contained already exists,
|
||||
/// then the creation of a new buffer is not necessary.
|
||||
/// </summary>
|
||||
/// <param name="address">Address of the buffer in guest memory</param>
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
private void CreateBuffer(ulong address, ulong size)
|
||||
{
|
||||
int overlapsCount = _buffers.FindOverlapsNonOverlapping(address, size, ref _bufferOverlaps);
|
||||
|
||||
if (overlapsCount != 0)
|
||||
{
|
||||
// The buffer already exists. We can just return the existing buffer
|
||||
// if the buffer we need is fully contained inside the overlapping buffer.
|
||||
// Otherwise, we must delete the overlapping buffers and create a bigger buffer
|
||||
// that fits all the data we need. We also need to copy the contents from the
|
||||
// old buffer(s) to the new buffer.
|
||||
ulong endAddress = address + size;
|
||||
|
||||
if (_bufferOverlaps[0].Address > address || _bufferOverlaps[0].EndAddress < endAddress)
|
||||
{
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
Buffer buffer = _bufferOverlaps[index];
|
||||
|
||||
address = Math.Min(address, buffer.Address);
|
||||
endAddress = Math.Max(endAddress, buffer.EndAddress);
|
||||
|
||||
buffer.SynchronizeMemory(buffer.Address, buffer.Size);
|
||||
|
||||
_buffers.Remove(buffer);
|
||||
}
|
||||
|
||||
Buffer newBuffer = new Buffer(_context, address, endAddress - address);
|
||||
|
||||
_buffers.Add(newBuffer);
|
||||
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
Buffer buffer = _bufferOverlaps[index];
|
||||
|
||||
int dstOffset = (int)(buffer.Address - newBuffer.Address);
|
||||
|
||||
buffer.CopyTo(newBuffer, dstOffset);
|
||||
|
||||
buffer.Dispose();
|
||||
}
|
||||
|
||||
_rebind = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No overlap, just create a new buffer.
|
||||
Buffer buffer = new Buffer(_context, address, size);
|
||||
|
||||
_buffers.Add(buffer);
|
||||
}
|
||||
|
||||
ShrinkOverlapsBufferIfNeeded();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resizes the temporary buffer used for range list intersection results, if it has grown too much.
|
||||
/// </summary>
|
||||
private void ShrinkOverlapsBufferIfNeeded()
|
||||
{
|
||||
if (_bufferOverlaps.Length > OverlapsBufferMaxCapacity)
|
||||
{
|
||||
Array.Resize(ref _bufferOverlaps, OverlapsBufferMaxCapacity);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the compute uniform buffer currently bound at the given index.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the uniform buffer binding</param>
|
||||
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
||||
public ulong GetComputeUniformBufferAddress(int index)
|
||||
{
|
||||
return _cpUniformBuffers.Buffers[index].Address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the address of the graphics uniform buffer currently bound at the given index.
|
||||
/// </summary>
|
||||
/// <param name="stage">Index of the shader stage</param>
|
||||
/// <param name="index">Index of the uniform buffer binding</param>
|
||||
/// <returns>The uniform buffer address, or an undefined value if the buffer is not currently bound</returns>
|
||||
public ulong GetGraphicsUniformBufferAddress(int stage, int index)
|
||||
{
|
||||
return _gpUniformBuffers[stage].Buffers[index].Address;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the compute engine bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
/// </summary>
|
||||
public void CommitComputeBindings()
|
||||
{
|
||||
uint enableMask = _cpStorageBuffers.EnableMask;
|
||||
|
||||
for (int index = 0; (enableMask >> index) != 0; index++)
|
||||
{
|
||||
if ((enableMask & (1u << index)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferBounds bounds = _cpStorageBuffers.Buffers[index];
|
||||
|
||||
if (bounds.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
|
||||
|
||||
_context.Renderer.Pipeline.SetStorageBuffer(index, ShaderStage.Compute, buffer);
|
||||
}
|
||||
|
||||
enableMask = _cpUniformBuffers.EnableMask;
|
||||
|
||||
for (int index = 0; (enableMask >> index) != 0; index++)
|
||||
{
|
||||
if ((enableMask & (1u << index)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferBounds bounds = _cpUniformBuffers.Buffers[index];
|
||||
|
||||
if (bounds.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
|
||||
|
||||
_context.Renderer.Pipeline.SetUniformBuffer(index, ShaderStage.Compute, buffer);
|
||||
}
|
||||
|
||||
// Force rebind after doing compute work.
|
||||
_rebind = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensures that the graphics engine bindings are visible to the host GPU.
|
||||
/// Note: this actually performs the binding using the host graphics API.
|
||||
/// </summary>
|
||||
public void CommitBindings()
|
||||
{
|
||||
if (_indexBufferDirty || _rebind)
|
||||
{
|
||||
_indexBufferDirty = false;
|
||||
|
||||
if (_indexBuffer.Address != 0)
|
||||
{
|
||||
BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
||||
|
||||
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
|
||||
}
|
||||
}
|
||||
else if (_indexBuffer.Address != 0)
|
||||
{
|
||||
SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
|
||||
}
|
||||
|
||||
uint vbEnableMask = _vertexBuffersEnableMask;
|
||||
|
||||
if (_vertexBuffersDirty || _rebind)
|
||||
{
|
||||
_vertexBuffersDirty = false;
|
||||
|
||||
VertexBufferDescriptor[] vertexBuffers = new VertexBufferDescriptor[Constants.TotalVertexBuffers];
|
||||
|
||||
for (int index = 0; (vbEnableMask >> index) != 0; index++)
|
||||
{
|
||||
VertexBuffer vb = _vertexBuffers[index];
|
||||
|
||||
if (vb.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferRange buffer = GetBufferRange(vb.Address, vb.Size);
|
||||
|
||||
vertexBuffers[index] = new VertexBufferDescriptor(buffer, vb.Stride, vb.Divisor);
|
||||
}
|
||||
|
||||
_context.Renderer.Pipeline.SetVertexBuffers(vertexBuffers);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int index = 0; (vbEnableMask >> index) != 0; index++)
|
||||
{
|
||||
VertexBuffer vb = _vertexBuffers[index];
|
||||
|
||||
if (vb.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SynchronizeBufferRange(vb.Address, vb.Size);
|
||||
}
|
||||
}
|
||||
|
||||
if (_gpStorageBuffersDirty || _rebind)
|
||||
{
|
||||
_gpStorageBuffersDirty = false;
|
||||
|
||||
BindBuffers(_gpStorageBuffers, isStorage: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBuffers(_gpStorageBuffers);
|
||||
}
|
||||
|
||||
if (_gpUniformBuffersDirty || _rebind)
|
||||
{
|
||||
_gpUniformBuffersDirty = false;
|
||||
|
||||
BindBuffers(_gpUniformBuffers, isStorage: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateBuffers(_gpUniformBuffers);
|
||||
}
|
||||
|
||||
_rebind = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bind respective buffer bindings on the host API.
|
||||
/// </summary>
|
||||
/// <param name="bindings">Bindings to bind</param>
|
||||
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffers</param>
|
||||
private void BindBuffers(BuffersPerStage[] bindings, bool isStorage)
|
||||
{
|
||||
BindOrUpdateBuffers(bindings, bind: true, isStorage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates data for the already bound buffer bindings.
|
||||
/// </summary>
|
||||
/// <param name="bindings">Bindings to update</param>
|
||||
private void UpdateBuffers(BuffersPerStage[] bindings)
|
||||
{
|
||||
BindOrUpdateBuffers(bindings, bind: false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This binds buffers into the host API, or updates data for already bound buffers.
|
||||
/// </summary>
|
||||
/// <param name="bindings">Bindings to bind or update</param>
|
||||
/// <param name="bind">True to bind, false to update</param>
|
||||
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
||||
private void BindOrUpdateBuffers(BuffersPerStage[] bindings, bool bind, bool isStorage = false)
|
||||
{
|
||||
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
|
||||
{
|
||||
uint enableMask = bindings[(int)stage - 1].EnableMask;
|
||||
|
||||
if (enableMask == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int index = 0; (enableMask >> index) != 0; index++)
|
||||
{
|
||||
if ((enableMask & (1u << index)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BufferBounds bounds = bindings[(int)stage - 1].Buffers[index];
|
||||
|
||||
if (bounds.Address == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bind)
|
||||
{
|
||||
BindBuffer(index, stage, bounds, isStorage);
|
||||
}
|
||||
else
|
||||
{
|
||||
SynchronizeBufferRange(bounds.Address, bounds.Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a buffer on the host API.
|
||||
/// </summary>
|
||||
/// <param name="index">Index to bind the buffer into</param>
|
||||
/// <param name="stage">Shader stage to bind the buffer into</param>
|
||||
/// <param name="bounds">Buffer address and size</param>
|
||||
/// <param name="isStorage">True to bind as storage buffer, false to bind as uniform buffer</param>
|
||||
private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage)
|
||||
{
|
||||
BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size);
|
||||
|
||||
if (isStorage)
|
||||
{
|
||||
_context.Renderer.Pipeline.SetStorageBuffer(index, stage, buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.Pipeline.SetUniformBuffer(index, stage, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy a buffer data from a given address to another.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This does a GPU side copy.
|
||||
/// </remarks>
|
||||
/// <param name="srcVa">GPU virtual address of the copy source</param>
|
||||
/// <param name="dstVa">GPU virtual address of the copy destination</param>
|
||||
/// <param name="size">Size in bytes of the copy</param>
|
||||
public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size)
|
||||
{
|
||||
ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size);
|
||||
ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size);
|
||||
|
||||
Buffer srcBuffer = GetBuffer(srcAddress, size);
|
||||
Buffer dstBuffer = GetBuffer(dstAddress, size);
|
||||
|
||||
int srcOffset = (int)(srcAddress - srcBuffer.Address);
|
||||
int dstOffset = (int)(dstAddress - dstBuffer.Address);
|
||||
|
||||
srcBuffer.HostBuffer.CopyTo(
|
||||
dstBuffer.HostBuffer,
|
||||
srcOffset,
|
||||
dstOffset,
|
||||
(int)size);
|
||||
|
||||
dstBuffer.Flush(dstAddress, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a buffer sub-range for a given memory range.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the memory range</param>
|
||||
/// <param name="size">Size in bytes of the memory range</param>
|
||||
/// <returns>The buffer sub-range for the given range</returns>
|
||||
private BufferRange GetBufferRange(ulong address, ulong size)
|
||||
{
|
||||
return GetBuffer(address, size).GetRange(address, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a buffer for a given memory range.
|
||||
/// A buffer overlapping with the specified range is assumed to already exist on the cache.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the memory range</param>
|
||||
/// <param name="size">Size in bytes of the memory range</param>
|
||||
/// <returns>The buffer where the range is fully contained</returns>
|
||||
private Buffer GetBuffer(ulong address, ulong size)
|
||||
{
|
||||
Buffer buffer;
|
||||
|
||||
if (size != 0)
|
||||
{
|
||||
buffer = _buffers.FindFirstOverlap(address, size);
|
||||
|
||||
buffer.SynchronizeMemory(address, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = _buffers.FindFirstOverlap(address, 1);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs guest to host memory synchronization of a given memory range.
|
||||
/// </summary>
|
||||
/// <param name="address">Start address of the memory range</param>
|
||||
/// <param name="size">Size in bytes of the memory range</param>
|
||||
private void SynchronizeBufferRange(ulong address, ulong size)
|
||||
{
|
||||
if (size != 0)
|
||||
{
|
||||
Buffer buffer = _buffers.FindFirstOverlap(address, size);
|
||||
|
||||
buffer.SynchronizeMemory(address, size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all buffers in the cache.
|
||||
/// It's an error to use the buffer manager after disposal.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (Buffer buffer in _buffers)
|
||||
{
|
||||
buffer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue