diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs index 0ae5e3f26..e0b44353b 100644 --- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs +++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs @@ -355,12 +355,9 @@ namespace ARMeilleure.Instructions } while (bit < context.Memory.AddressSpaceBits); - if (!context.Memory.HasWriteWatchSupport) - { - Operand hasFlagSet = context.BitwiseAnd(pte, Const((long)MemoryManager.PteFlagsMask)); + Operand hasFlagSet = context.BitwiseAnd(pte, Const((long)MemoryManager.PteFlagsMask)); - context.BranchIfTrue(lblFallbackPath, hasFlagSet); - } + context.BranchIfTrue(lblFallbackPath, hasFlagSet); Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, MemoryManager.PageMask)); diff --git a/ARMeilleure/Memory/IMemory.cs b/ARMeilleure/Memory/IMemory.cs deleted file mode 100644 index 0c3849c07..000000000 --- a/ARMeilleure/Memory/IMemory.cs +++ /dev/null @@ -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); - } -} \ No newline at end of file diff --git a/ARMeilleure/Memory/MemoryManagement.cs b/ARMeilleure/Memory/MemoryManagement.cs index bf0bd02ce..e299ae49d 100644 --- a/ARMeilleure/Memory/MemoryManagement.cs +++ b/ARMeilleure/Memory/MemoryManagement.cs @@ -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; - } - } } } \ No newline at end of file diff --git a/ARMeilleure/Memory/MemoryManagementWindows.cs b/ARMeilleure/Memory/MemoryManagementWindows.cs index c1a84c95b..ae64b5c62 100644 --- a/ARMeilleure/Memory/MemoryManagementWindows.cs +++ b/ARMeilleure/Memory/MemoryManagementWindows.cs @@ -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; - } } } \ No newline at end of file diff --git a/ARMeilleure/Memory/MemoryManager.cs b/ARMeilleure/Memory/MemoryManager.cs index c62249881..2bdbc3094 100644 --- a/ARMeilleure/Memory/MemoryManager.cs +++ b/ARMeilleure/Memory/MemoryManager.cs @@ -1,5 +1,6 @@ using ARMeilleure.State; using System; +using System.Collections.Generic; using System.Runtime.InteropServices; using System.Threading; @@ -29,8 +30,6 @@ namespace ARMeilleure.Memory internal int PtLevelSize { get; } internal int PtLevelMask { get; } - public bool HasWriteWatchSupport => MemoryManagement.HasWriteWatchSupport; - public int AddressSpaceBits { get; } public long AddressSpaceSize { get; } @@ -254,119 +253,57 @@ namespace ARMeilleure.Memory return ptePtr; } - public bool IsRegionModified(long position, long size) + public unsafe (ulong, ulong)[] GetModifiedRanges(ulong address, ulong size) { - if (!HasWriteWatchSupport) + List<(ulong, ulong)> ranges = new List<(ulong, ulong)>(); + + ulong endAddress = (address + size + PageMask) & ~(ulong)PageMask; + + address &= ~(ulong)PageMask; + + ulong currAddr = address; + ulong currSize = 0; + + while (address < endAddress) { - return IsRegionModifiedFallback(position, size); - } - - IntPtr address = Translate(position); - - IntPtr baseAddr = address; - IntPtr expectedAddr = address; - - long pendingPages = 0; - - long pages = size / PageSize; - - bool modified = false; - - bool IsAnyPageModified() - { - IntPtr pendingSize = new IntPtr(pendingPages * PageSize); - - IntPtr[] addresses = new IntPtr[pendingPages]; - - bool result = GetModifiedPages(baseAddr, pendingSize, addresses, out ulong count); - - if (result) + if (IsValidPosition((long)address)) { - return count != 0; - } - else - { - return true; - } - } - - while (pages-- > 0) - { - if (address != expectedAddr) - { - modified |= IsAnyPageModified(); - - baseAddr = address; - - pendingPages = 0; - } - - expectedAddr = address + PageSize; - - pendingPages++; - - if (pages == 0) - { - break; - } - - position += PageSize; - - address = Translate(position); - } - - if (pendingPages != 0) - { - modified |= IsAnyPageModified(); - } - - return modified; - } - - private unsafe bool IsRegionModifiedFallback(long position, long size) - { - long endAddr = (position + size + PageMask) & ~PageMask; - - bool modified = false; - - while ((ulong)position < (ulong)endAddr) - { - if (IsValidPosition(position)) - { - byte* ptr = ((byte**)_pageTable)[position >> PageBits]; + byte* ptr = ((byte**)_pageTable)[address >> PageBits]; ulong ptrUlong = (ulong)ptr; if ((ptrUlong & PteFlagNotModified) == 0) { - modified = true; + // Modified. + currSize += PageSize; - SetPtEntryFlag(position, PteFlagNotModified); + SetPtEntryFlag((long)address, PteFlagNotModified); + } + else + { + if (currSize != 0) + { + ranges.Add((currAddr, currSize)); + } + + currAddr = address + PageSize; + currSize = 0; } } else { - modified = true; + currSize += PageSize; } - position += PageSize; + address += PageSize; } - return modified; - } - - public bool TryGetHostAddress(long position, long size, out IntPtr ptr) - { - if (IsContiguous(position, size)) + if (currSize != 0) { - ptr = (IntPtr)Translate(position); - - return true; + ranges.Add((currAddr, currSize)); } - ptr = IntPtr.Zero; - - return false; + return ranges.ToArray(); } private bool IsContiguous(long position, long size) @@ -612,41 +549,6 @@ namespace ARMeilleure.Memory return data; } - public void ReadBytes(long position, byte[] data, int startIndex, int size) - { - // Note: This will be moved later. - long endAddr = position + size; - - if ((ulong)size > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(size)); - } - - if ((ulong)endAddr < (ulong)position) - { - throw new ArgumentOutOfRangeException(nameof(position)); - } - - int offset = startIndex; - - while ((ulong)position < (ulong)endAddr) - { - long pageLimit = (position + PageSize) & ~(long)PageMask; - - if ((ulong)pageLimit > (ulong)endAddr) - { - pageLimit = endAddr; - } - - int copySize = (int)(pageLimit - position); - - Marshal.Copy(Translate(position), data, offset, copySize); - - position += copySize; - offset += copySize; - } - } - public void WriteSByte(long position, sbyte value) { WriteByte(position, (byte)value); @@ -746,53 +648,6 @@ namespace ARMeilleure.Memory } } - public void WriteBytes(long position, byte[] data, int startIndex, int size) - { - // Note: This will be moved later. - long endAddr = position + size; - - if ((ulong)endAddr < (ulong)position) - { - throw new ArgumentOutOfRangeException(nameof(position)); - } - - int offset = startIndex; - - while ((ulong)position < (ulong)endAddr) - { - long pageLimit = (position + PageSize) & ~(long)PageMask; - - if ((ulong)pageLimit > (ulong)endAddr) - { - pageLimit = endAddr; - } - - int copySize = (int)(pageLimit - position); - - Marshal.Copy(data, offset, Translate(position), copySize); - - position += copySize; - offset += copySize; - } - } - - public void CopyBytes(long src, long dst, long size) - { - // Note: This will be moved later. - if (IsContiguous(src, size) && - IsContiguous(dst, size)) - { - byte* srcPtr = (byte*)Translate(src); - byte* dstPtr = (byte*)Translate(dst); - - Buffer.MemoryCopy(srcPtr, dstPtr, size, size); - } - else - { - WriteBytes(dst, ReadBytes(src, size)); - } - } - public void Dispose() { Dispose(true); diff --git a/Ryujinx.Common/PerformanceCounter.cs b/Ryujinx.Common/PerformanceCounter.cs index 4c8ae6a75..1c2782e34 100644 --- a/Ryujinx.Common/PerformanceCounter.cs +++ b/Ryujinx.Common/PerformanceCounter.cs @@ -1,9 +1,11 @@ -using System.Diagnostics; +using System.Diagnostics; namespace Ryujinx.Common { public static class PerformanceCounter { + private static double _ticksToNs; + /// /// Represents the number of ticks in 1 day. /// @@ -53,6 +55,19 @@ namespace Ryujinx.Common } } + /// + /// Gets the number of nanoseconds elapsed since the system started. + /// + 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; } } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Blend/BlendDescriptor.cs b/Ryujinx.Graphics.GAL/Blend/BlendDescriptor.cs new file mode 100644 index 000000000..d7ef80044 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Blend/BlendDescriptor.cs @@ -0,0 +1,32 @@ +namespace Ryujinx.Graphics.GAL.Blend +{ + public struct BlendDescriptor + { + public bool Enable { get; } + + public BlendOp ColorOp { get; } + public BlendFactor ColorSrcFactor { get; } + public BlendFactor ColorDstFactor { get; } + public BlendOp AlphaOp { get; } + public BlendFactor AlphaSrcFactor { get; } + public BlendFactor AlphaDstFactor { get; } + + public BlendDescriptor( + bool enable, + BlendOp colorOp, + BlendFactor colorSrcFactor, + BlendFactor colorDstFactor, + BlendOp alphaOp, + BlendFactor alphaSrcFactor, + BlendFactor alphaDstFactor) + { + Enable = enable; + ColorOp = colorOp; + ColorSrcFactor = colorSrcFactor; + ColorDstFactor = colorDstFactor; + AlphaOp = alphaOp; + AlphaSrcFactor = alphaSrcFactor; + AlphaDstFactor = alphaDstFactor; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Blend/BlendFactor.cs b/Ryujinx.Graphics.GAL/Blend/BlendFactor.cs new file mode 100644 index 000000000..0eda08a7f --- /dev/null +++ b/Ryujinx.Graphics.GAL/Blend/BlendFactor.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Graphics.GAL.Blend +{ + public enum BlendFactor + { + Zero = 1, + One, + SrcColor, + OneMinusSrcColor, + SrcAlpha, + OneMinusSrcAlpha, + DstAlpha, + OneMinusDstAlpha, + DstColor, + OneMinusDstColor, + SrcAlphaSaturate, + Src1Color = 0x10, + OneMinusSrc1Color, + Src1Alpha, + OneMinusSrc1Alpha, + ConstantColor = 0xc001, + OneMinusConstantColor, + ConstantAlpha, + OneMinusConstantAlpha + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Blend/BlendOp.cs b/Ryujinx.Graphics.GAL/Blend/BlendOp.cs new file mode 100644 index 000000000..51a0062d7 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Blend/BlendOp.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.GAL.Blend +{ + public enum BlendOp + { + Add = 1, + Subtract, + ReverseSubtract, + Minimum, + Maximum + } +} diff --git a/Ryujinx.Graphics.GAL/BufferRange.cs b/Ryujinx.Graphics.GAL/BufferRange.cs new file mode 100644 index 000000000..a35636aa7 --- /dev/null +++ b/Ryujinx.Graphics.GAL/BufferRange.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Capabilities.cs b/Ryujinx.Graphics.GAL/Capabilities.cs new file mode 100644 index 000000000..9640447be --- /dev/null +++ b/Ryujinx.Graphics.GAL/Capabilities.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL +{ + public struct Capabilities + { + public bool SupportsAstcCompression { get; } + + public Capabilities(bool supportsAstcCompression) + { + SupportsAstcCompression = supportsAstcCompression; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Color/ColorF.cs b/Ryujinx.Graphics.GAL/Color/ColorF.cs new file mode 100644 index 000000000..bc4b32b87 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Color/ColorF.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Color +{ + public struct ColorF + { + public float Red { get; } + public float Green { get; } + public float Blue { get; } + public float Alpha { get; } + + public ColorF(float red, float green, float blue, float alpha) + { + Red = red; + Green = green; + Blue = blue; + Alpha = alpha; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Color/ColorSI.cs b/Ryujinx.Graphics.GAL/Color/ColorSI.cs new file mode 100644 index 000000000..9cde0406b --- /dev/null +++ b/Ryujinx.Graphics.GAL/Color/ColorSI.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Color +{ + public struct ColorSI + { + public int Red { get; } + public int Green { get; } + public int Blue { get; } + public int Alpha { get; } + + public ColorSI(int red, int green, int blue, int alpha) + { + Red = red; + Green = green; + Blue = blue; + Alpha = alpha; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Color/ColorUI.cs b/Ryujinx.Graphics.GAL/Color/ColorUI.cs new file mode 100644 index 000000000..3bac84ef9 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Color/ColorUI.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.GAL.Color +{ + public struct ColorUI + { + public uint Red { get; } + public uint Green { get; } + public uint Blue { get; } + public uint Alpha { get; } + + public ColorUI(uint red, uint green, uint blue, uint alpha) + { + Red = red; + Green = green; + Blue = blue; + Alpha = alpha; + } + } +} diff --git a/Ryujinx.Graphics.GAL/CompareOp.cs b/Ryujinx.Graphics.GAL/CompareOp.cs new file mode 100644 index 000000000..da5d5067c --- /dev/null +++ b/Ryujinx.Graphics.GAL/CompareOp.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum CompareOp + { + Never = 1, + Less, + Equal, + LessOrEqual, + Greater, + NotEqual, + GreaterOrEqual, + Always + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/CounterType.cs b/Ryujinx.Graphics.GAL/CounterType.cs new file mode 100644 index 000000000..9d7b3b981 --- /dev/null +++ b/Ryujinx.Graphics.GAL/CounterType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum CounterType + { + SamplesPassed, + PrimitivesGenerated, + TransformFeedbackPrimitivesWritten + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/DepthStencil/DepthStencilState.cs b/Ryujinx.Graphics.GAL/DepthStencil/DepthStencilState.cs new file mode 100644 index 000000000..93b8d50ed --- /dev/null +++ b/Ryujinx.Graphics.GAL/DepthStencil/DepthStencilState.cs @@ -0,0 +1,47 @@ +namespace Ryujinx.Graphics.GAL.DepthStencil +{ + public struct DepthStencilState + { + public bool DepthTestEnable { get; } + public bool DepthWriteEnable { get; } + public bool StencilTestEnable { get; } + + public CompareOp DepthFunc { get; } + public CompareOp StencilFrontFunc { get; } + public StencilOp StencilFrontSFail { get; } + public StencilOp StencilFrontDpPass { get; } + public StencilOp StencilFrontDpFail { get; } + public CompareOp StencilBackFunc { get; } + public StencilOp StencilBackSFail { get; } + public StencilOp StencilBackDpPass { get; } + public StencilOp StencilBackDpFail { get; } + + public DepthStencilState( + bool depthTestEnable, + bool depthWriteEnable, + bool stencilTestEnable, + CompareOp depthFunc, + CompareOp stencilFrontFunc, + StencilOp stencilFrontSFail, + StencilOp stencilFrontDpPass, + StencilOp stencilFrontDpFail, + CompareOp stencilBackFunc, + StencilOp stencilBackSFail, + StencilOp stencilBackDpPass, + StencilOp stencilBackDpFail) + { + DepthTestEnable = depthTestEnable; + DepthWriteEnable = depthWriteEnable; + StencilTestEnable = stencilTestEnable; + DepthFunc = depthFunc; + StencilFrontFunc = stencilFrontFunc; + StencilFrontSFail = stencilFrontSFail; + StencilFrontDpPass = stencilFrontDpPass; + StencilFrontDpFail = stencilFrontDpFail; + StencilBackFunc = stencilBackFunc; + StencilBackSFail = stencilBackSFail; + StencilBackDpPass = stencilBackDpPass; + StencilBackDpFail = stencilBackDpFail; + } + } +} diff --git a/Ryujinx.Graphics.GAL/DepthStencil/DepthTestDescriptor.cs b/Ryujinx.Graphics.GAL/DepthStencil/DepthTestDescriptor.cs new file mode 100644 index 000000000..98b1429bc --- /dev/null +++ b/Ryujinx.Graphics.GAL/DepthStencil/DepthTestDescriptor.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.Graphics.GAL.DepthStencil +{ + public struct DepthTestDescriptor + { + public bool TestEnable { get; } + public bool WriteEnable { get; } + + public CompareOp Func { get; } + + public DepthTestDescriptor( + bool testEnable, + bool writeEnable, + CompareOp func) + { + TestEnable = testEnable; + WriteEnable = writeEnable; + Func = func; + } + } +} diff --git a/Ryujinx.Graphics.GAL/DepthStencil/StencilOp.cs b/Ryujinx.Graphics.GAL/DepthStencil/StencilOp.cs new file mode 100644 index 000000000..3818c6a52 --- /dev/null +++ b/Ryujinx.Graphics.GAL/DepthStencil/StencilOp.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL.DepthStencil +{ + public enum StencilOp + { + Keep = 1, + Zero, + Replace, + IncrementAndClamp, + DecrementAndClamp, + Invert, + IncrementAndWrap, + DecrementAndWrap + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/DepthStencil/StencilTestDescriptor.cs b/Ryujinx.Graphics.GAL/DepthStencil/StencilTestDescriptor.cs new file mode 100644 index 000000000..2db53172d --- /dev/null +++ b/Ryujinx.Graphics.GAL/DepthStencil/StencilTestDescriptor.cs @@ -0,0 +1,56 @@ +namespace Ryujinx.Graphics.GAL.DepthStencil +{ + public struct StencilTestDescriptor + { + public bool TestEnable { get; } + + public CompareOp FrontFunc { get; } + public StencilOp FrontSFail { get; } + public StencilOp FrontDpPass { get; } + public StencilOp FrontDpFail { get; } + public int FrontFuncRef { get; } + public int FrontFuncMask { get; } + public int FrontMask { get; } + public CompareOp BackFunc { get; } + public StencilOp BackSFail { get; } + public StencilOp BackDpPass { get; } + public StencilOp BackDpFail { get; } + public int BackFuncRef { get; } + public int BackFuncMask { get; } + public int BackMask { get; } + + public StencilTestDescriptor( + bool testEnable, + CompareOp frontFunc, + StencilOp frontSFail, + StencilOp frontDpPass, + StencilOp frontDpFail, + int frontFuncRef, + int frontFuncMask, + int frontMask, + CompareOp backFunc, + StencilOp backSFail, + StencilOp backDpPass, + StencilOp backDpFail, + int backFuncRef, + int backFuncMask, + int backMask) + { + TestEnable = testEnable; + FrontFunc = frontFunc; + FrontSFail = frontSFail; + FrontDpPass = frontDpPass; + FrontDpFail = frontDpFail; + FrontFuncRef = frontFuncRef; + FrontFuncMask = frontFuncMask; + FrontMask = frontMask; + BackFunc = backFunc; + BackSFail = backSFail; + BackDpPass = backDpPass; + BackDpFail = backDpFail; + BackFuncRef = backFuncRef; + BackFuncMask = backFuncMask; + BackMask = backMask; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Extents2D.cs b/Ryujinx.Graphics.GAL/Extents2D.cs new file mode 100644 index 000000000..e9e26af41 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Extents2D.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalCullFace.cs b/Ryujinx.Graphics.GAL/Face.cs similarity index 61% rename from Ryujinx.Graphics/Gal/GalCullFace.cs rename to Ryujinx.Graphics.GAL/Face.cs index 4ab3e1742..0587641fa 100644 --- a/Ryujinx.Graphics/Gal/GalCullFace.cs +++ b/Ryujinx.Graphics.GAL/Face.cs @@ -1,9 +1,9 @@ -namespace Ryujinx.Graphics.Gal +namespace Ryujinx.Graphics.GAL { - public enum GalCullFace + public enum Face { Front = 0x404, Back = 0x405, FrontAndBack = 0x408 } -} +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Format.cs b/Ryujinx.Graphics.GAL/Format.cs new file mode 100644 index 000000000..9f28cdfb8 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Format.cs @@ -0,0 +1,218 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum Format + { + R8Unorm, + R8Snorm, + R8Uint, + R8Sint, + R16Float, + R16Unorm, + R16Snorm, + R16Uint, + R16Sint, + R32Float, + R32Uint, + R32Sint, + R8G8Unorm, + R8G8Snorm, + R8G8Uint, + R8G8Sint, + R16G16Float, + R16G16Unorm, + R16G16Snorm, + R16G16Uint, + R16G16Sint, + R32G32Float, + R32G32Uint, + R32G32Sint, + R8G8B8Unorm, + R8G8B8Snorm, + R8G8B8Uint, + R8G8B8Sint, + R16G16B16Float, + R16G16B16Unorm, + R16G16B16Snorm, + R16G16B16Uint, + R16G16B16Sint, + R32G32B32Float, + R32G32B32Uint, + R32G32B32Sint, + R8G8B8A8Unorm, + R8G8B8A8Snorm, + R8G8B8A8Uint, + R8G8B8A8Sint, + R16G16B16A16Float, + R16G16B16A16Unorm, + R16G16B16A16Snorm, + R16G16B16A16Uint, + R16G16B16A16Sint, + R32G32B32A32Float, + R32G32B32A32Uint, + R32G32B32A32Sint, + S8Uint, + D16Unorm, + D24X8Unorm, + D32Float, + D24UnormS8Uint, + D32FloatS8Uint, + R8G8B8X8Srgb, + R8G8B8A8Srgb, + R4G4B4A4Unorm, + R5G5B5X1Unorm, + R5G5B5A1Unorm, + R5G6B5Unorm, + R10G10B10A2Unorm, + R10G10B10A2Uint, + R11G11B10Float, + R9G9B9E5Float, + Bc1RgbUnorm, + Bc1RgbaUnorm, + Bc2Unorm, + Bc3Unorm, + Bc1RgbSrgb, + Bc1RgbaSrgb, + Bc2Srgb, + Bc3Srgb, + Bc4Unorm, + Bc4Snorm, + Bc5Unorm, + Bc5Snorm, + Bc7Unorm, + Bc7Srgb, + Bc6HUfloat, + Bc6HSfloat, + R8Uscaled, + R8Sscaled, + R16Uscaled, + R16Sscaled, + R32Uscaled, + R32Sscaled, + R8G8Uscaled, + R8G8Sscaled, + R16G16Uscaled, + R16G16Sscaled, + R32G32Uscaled, + R32G32Sscaled, + R8G8B8Uscaled, + R8G8B8Sscaled, + R16G16B16Uscaled, + R16G16B16Sscaled, + R32G32B32Uscaled, + R32G32B32Sscaled, + R8G8B8A8Uscaled, + R8G8B8A8Sscaled, + R16G16B16A16Uscaled, + R16G16B16A16Sscaled, + R32G32B32A32Uscaled, + R32G32B32A32Sscaled, + R10G10B10A2Snorm, + R10G10B10A2Sint, + R10G10B10A2Uscaled, + R10G10B10A2Sscaled, + R8G8B8X8Unorm, + R8G8B8X8Snorm, + R8G8B8X8Uint, + R8G8B8X8Sint, + R16G16B16X16Float, + R16G16B16X16Unorm, + R16G16B16X16Snorm, + R16G16B16X16Uint, + R16G16B16X16Sint, + R32G32B32X32Float, + R32G32B32X32Uint, + R32G32B32X32Sint, + Astc4x4Unorm, + Astc5x4Unorm, + Astc5x5Unorm, + Astc6x5Unorm, + Astc6x6Unorm, + Astc8x5Unorm, + Astc8x6Unorm, + Astc8x8Unorm, + Astc10x5Unorm, + Astc10x6Unorm, + Astc10x8Unorm, + Astc10x10Unorm, + Astc12x10Unorm, + Astc12x12Unorm, + Astc4x4Srgb, + Astc5x4Srgb, + Astc5x5Srgb, + Astc6x5Srgb, + Astc6x6Srgb, + Astc8x5Srgb, + Astc8x6Srgb, + Astc8x8Srgb, + Astc10x5Srgb, + Astc10x6Srgb, + Astc10x8Srgb, + Astc10x10Srgb, + Astc12x10Srgb, + Astc12x12Srgb, + B5G6R5Unorm, + B5G5R5X1Unorm, + B5G5R5A1Unorm, + A1B5G5R5Unorm, + B8G8R8X8Unorm, + B8G8R8A8Unorm, + B8G8R8X8Srgb, + B8G8R8A8Srgb + } + + public static class FormatExtensions + { + public static bool IsAstc(this Format format) + { + return format.IsAstcUnorm() || format.IsAstcSrgb(); + } + + public static bool IsAstcUnorm(this Format format) + { + switch (format) + { + case Format.Astc4x4Unorm: + case Format.Astc5x4Unorm: + case Format.Astc5x5Unorm: + case Format.Astc6x5Unorm: + case Format.Astc6x6Unorm: + case Format.Astc8x5Unorm: + case Format.Astc8x6Unorm: + case Format.Astc8x8Unorm: + case Format.Astc10x5Unorm: + case Format.Astc10x6Unorm: + case Format.Astc10x8Unorm: + case Format.Astc10x10Unorm: + case Format.Astc12x10Unorm: + case Format.Astc12x12Unorm: + return true; + } + + return false; + } + + public static bool IsAstcSrgb(this Format format) + { + switch (format) + { + case Format.Astc4x4Srgb: + case Format.Astc5x4Srgb: + case Format.Astc5x5Srgb: + case Format.Astc6x5Srgb: + case Format.Astc6x6Srgb: + case Format.Astc8x5Srgb: + case Format.Astc8x6Srgb: + case Format.Astc8x8Srgb: + case Format.Astc10x5Srgb: + case Format.Astc10x6Srgb: + case Format.Astc10x8Srgb: + case Format.Astc10x10Srgb: + case Format.Astc12x10Srgb: + case Format.Astc12x12Srgb: + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/FrontFace.cs b/Ryujinx.Graphics.GAL/FrontFace.cs new file mode 100644 index 000000000..aa6bfdc5b --- /dev/null +++ b/Ryujinx.Graphics.GAL/FrontFace.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum FrontFace + { + Clockwise = 0x900, + CounterClockwise = 0x901 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IBuffer.cs b/Ryujinx.Graphics.GAL/IBuffer.cs new file mode 100644 index 000000000..000efd67a --- /dev/null +++ b/Ryujinx.Graphics.GAL/IBuffer.cs @@ -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 data); + + void SetData(int offset, Span data); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IComputePipeline.cs b/Ryujinx.Graphics.GAL/IComputePipeline.cs new file mode 100644 index 000000000..e2d0b06ea --- /dev/null +++ b/Ryujinx.Graphics.GAL/IComputePipeline.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL +{ + public interface IComputePipeline + { + void Dispatch(int groupsX, int groupsY, int groupsZ); + + void SetProgram(IProgram program); + + void SetStorageBuffer(int index, BufferRange buffer); + void SetUniformBuffer(int index, BufferRange buffer); + } +} diff --git a/Ryujinx.Graphics.GAL/IGraphicsPipeline.cs b/Ryujinx.Graphics.GAL/IGraphicsPipeline.cs new file mode 100644 index 000000000..13e6ab1a6 --- /dev/null +++ b/Ryujinx.Graphics.GAL/IGraphicsPipeline.cs @@ -0,0 +1,69 @@ +using Ryujinx.Graphics.GAL.Blend; +using Ryujinx.Graphics.GAL.Color; +using Ryujinx.Graphics.GAL.DepthStencil; +using Ryujinx.Graphics.GAL.InputAssembler; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.GAL +{ + public interface IGraphicsPipeline + { + void BindBlendState(int index, BlendDescriptor blend); + + void BindIndexBuffer(BufferRange buffer, IndexType type); + + void BindProgram(IProgram program); + + void BindSampler(int index, ShaderStage stage, ISampler sampler); + void BindTexture(int index, ShaderStage stage, ITexture texture); + + void BindStorageBuffers(int index, ShaderStage stage, BufferRange[] buffers); + void BindUniformBuffers(int index, ShaderStage stage, BufferRange[] buffers); + + void BindVertexAttribs(VertexAttribDescriptor[] vertexAttribs); + void BindVertexBuffers(VertexBufferDescriptor[] vertexBuffers); + + void ClearRenderTargetColor(int index, uint componentMask, ColorF color); + void ClearRenderTargetColor(int index, uint componentMask, ColorSI color); + void ClearRenderTargetColor(int index, uint componentMask, ColorUI color); + + void ClearRenderTargetDepthStencil( + float depthValue, + bool depthMask, + int stencilValue, + int stencilMask); + + void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance); + void DrawIndexed( + int indexCount, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance); + void DrawIndirect (BufferRange buffer, ulong offset, int drawCount, int stride); + void DrawIndexedIndirect(BufferRange buffer, ulong offset, int drawCount, int stride); + + void SetBlendColor(ColorF color); + + void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp); + + void SetDepthTest(DepthTestDescriptor depthTest); + + void SetFaceCulling(bool enable, Face face); + + void SetFrontFace(FrontFace frontFace); + + void SetPrimitiveRestart(bool enable, int index); + + void SetPrimitiveTopology(PrimitiveTopology topology); + + void SetRenderTargetColorMasks(uint[] componentMask); + + void SetRenderTargets(ITexture color3D, ITexture depthStencil); + void SetRenderTargets(ITexture[] colors, ITexture depthStencil); + + void SetStencilTest(StencilTestDescriptor stencilTest); + + void SetViewports(int first, Viewport[] viewports); + } +} diff --git a/Ryujinx.Graphics.GAL/IProgram.cs b/Ryujinx.Graphics.GAL/IProgram.cs new file mode 100644 index 000000000..ef44fc47e --- /dev/null +++ b/Ryujinx.Graphics.GAL/IProgram.cs @@ -0,0 +1,6 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface IProgram : IDisposable { } +} diff --git a/Ryujinx.Graphics.GAL/IRenderer.cs b/Ryujinx.Graphics.GAL/IRenderer.cs new file mode 100644 index 000000000..609f05f58 --- /dev/null +++ b/Ryujinx.Graphics.GAL/IRenderer.cs @@ -0,0 +1,33 @@ +using Ryujinx.Graphics.GAL.Sampler; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.GAL +{ + public interface IRenderer + { + IComputePipeline ComputePipeline { get; } + IGraphicsPipeline GraphicsPipeline { get; } + + IWindow Window { get; } + + IShader CompileShader(ShaderProgram shader); + + IBuffer CreateBuffer(int size); + + IProgram CreateProgram(IShader[] shaders); + + ISampler CreateSampler(SamplerCreateInfo info); + ITexture CreateTexture(TextureCreateInfo info); + + void FlushPipelines(); + + Capabilities GetCapabilities(); + + ulong GetCounter(CounterType type); + + void InitializeCounters(); + + void ResetCounter(CounterType type); + } +} diff --git a/Ryujinx.Graphics.GAL/ISampler.cs b/Ryujinx.Graphics.GAL/ISampler.cs new file mode 100644 index 000000000..3aefc6a78 --- /dev/null +++ b/Ryujinx.Graphics.GAL/ISampler.cs @@ -0,0 +1,6 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface ISampler : IDisposable { } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IShader.cs b/Ryujinx.Graphics.GAL/IShader.cs new file mode 100644 index 000000000..be24adcda --- /dev/null +++ b/Ryujinx.Graphics.GAL/IShader.cs @@ -0,0 +1,6 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface IShader : IDisposable { } +} diff --git a/Ryujinx.Graphics.GAL/ITexture.cs b/Ryujinx.Graphics.GAL/ITexture.cs new file mode 100644 index 000000000..6b3f5c59e --- /dev/null +++ b/Ryujinx.Graphics.GAL/ITexture.cs @@ -0,0 +1,21 @@ +using Ryujinx.Graphics.GAL.Texture; +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface ITexture : IDisposable + { + int Handle { get; } + + void CopyTo(ITexture destination); + void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter); + + ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel); + + int GetStorageDebugId(); + + byte[] GetData(int face); + + void SetData(Span data); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IWindow.cs b/Ryujinx.Graphics.GAL/IWindow.cs new file mode 100644 index 000000000..a0e5f196c --- /dev/null +++ b/Ryujinx.Graphics.GAL/IWindow.cs @@ -0,0 +1,13 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + public interface IWindow + { + void Present(); + + void QueueTexture(ITexture texture, ImageCrop crop, object context); + + void RegisterTextureReleaseCallback(TextureReleaseCallback callback); + } +} diff --git a/Ryujinx.Graphics.GAL/ImageCrop.cs b/Ryujinx.Graphics.GAL/ImageCrop.cs new file mode 100644 index 000000000..4428eee97 --- /dev/null +++ b/Ryujinx.Graphics.GAL/ImageCrop.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/IndexType.cs b/Ryujinx.Graphics.GAL/IndexType.cs new file mode 100644 index 000000000..4abf28d9c --- /dev/null +++ b/Ryujinx.Graphics.GAL/IndexType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum IndexType + { + UByte, + UShort, + UInt + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/InputAssembler/VertexAttribDescriptor.cs b/Ryujinx.Graphics.GAL/InputAssembler/VertexAttribDescriptor.cs new file mode 100644 index 000000000..cba3a9a41 --- /dev/null +++ b/Ryujinx.Graphics.GAL/InputAssembler/VertexAttribDescriptor.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.GAL.InputAssembler +{ + public struct VertexAttribDescriptor + { + public int BufferIndex { get; } + public int Offset { get; } + + public Format Format { get; } + + public VertexAttribDescriptor(int bufferIndex, int offset, Format format) + { + BufferIndex = bufferIndex; + Offset = offset; + Format = format; + } + } +} diff --git a/Ryujinx.Graphics.GAL/InputAssembler/VertexBufferDescriptor.cs b/Ryujinx.Graphics.GAL/InputAssembler/VertexBufferDescriptor.cs new file mode 100644 index 000000000..eb428f8ec --- /dev/null +++ b/Ryujinx.Graphics.GAL/InputAssembler/VertexBufferDescriptor.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.GAL.InputAssembler +{ + public struct VertexBufferDescriptor + { + public BufferRange Buffer { get; } + + public int Stride { get; } + public int Divisor { get; } + + public VertexBufferDescriptor(BufferRange buffer, int stride, int divisor) + { + Buffer = buffer; + Stride = stride; + Divisor = divisor; + } + } +} diff --git a/Ryujinx.Graphics.GAL/PolygonModeMask.cs b/Ryujinx.Graphics.GAL/PolygonModeMask.cs new file mode 100644 index 000000000..514b42317 --- /dev/null +++ b/Ryujinx.Graphics.GAL/PolygonModeMask.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Graphics.GAL +{ + [Flags] + public enum PolygonModeMask + { + Point = 1 << 0, + Line = 1 << 1, + Fill = 1 << 2 + } +} diff --git a/Ryujinx.Graphics.GAL/PrimitiveTopology.cs b/Ryujinx.Graphics.GAL/PrimitiveTopology.cs new file mode 100644 index 000000000..ed567a68a --- /dev/null +++ b/Ryujinx.Graphics.GAL/PrimitiveTopology.cs @@ -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 + } +} diff --git a/Ryujinx.Graphics.GAL/RectangleF.cs b/Ryujinx.Graphics.GAL/RectangleF.cs new file mode 100644 index 000000000..c58aabf0e --- /dev/null +++ b/Ryujinx.Graphics.GAL/RectangleF.cs @@ -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; + } + } +} diff --git a/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj b/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj new file mode 100644 index 000000000..584ebe975 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Ryujinx.Graphics.GAL.csproj @@ -0,0 +1,12 @@ + + + + + + + + + netcoreapp3.0 + + + diff --git a/Ryujinx.Graphics.GAL/Sampler/AddressMode.cs b/Ryujinx.Graphics.GAL/Sampler/AddressMode.cs new file mode 100644 index 000000000..4f56d892f --- /dev/null +++ b/Ryujinx.Graphics.GAL/Sampler/AddressMode.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL.Sampler +{ + public enum AddressMode + { + Repeat, + MirroredRepeat, + ClampToEdge, + ClampToBorder, + Clamp, + MirrorClampToEdge, + MirrorClampToBorder, + MirrorClamp + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Sampler/CompareMode.cs b/Ryujinx.Graphics.GAL/Sampler/CompareMode.cs new file mode 100644 index 000000000..ca4b09a06 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Sampler/CompareMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL.Sampler +{ + public enum CompareMode + { + None, + CompareRToTexture + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Sampler/MagFilter.cs b/Ryujinx.Graphics.GAL/Sampler/MagFilter.cs new file mode 100644 index 000000000..3c9c9de6d --- /dev/null +++ b/Ryujinx.Graphics.GAL/Sampler/MagFilter.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL.Sampler +{ + public enum MagFilter + { + Nearest = 1, + Linear + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Sampler/MinFilter.cs b/Ryujinx.Graphics.GAL/Sampler/MinFilter.cs new file mode 100644 index 000000000..d012f695b --- /dev/null +++ b/Ryujinx.Graphics.GAL/Sampler/MinFilter.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Sampler +{ + public enum MinFilter + { + Nearest = 1, + Linear, + NearestMipmapNearest, + LinearMipmapNearest, + NearestMipmapLinear, + LinearMipmapLinear + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Sampler/SamplerCreateInfo.cs b/Ryujinx.Graphics.GAL/Sampler/SamplerCreateInfo.cs new file mode 100644 index 000000000..3f42742b9 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Sampler/SamplerCreateInfo.cs @@ -0,0 +1,52 @@ +using Ryujinx.Graphics.GAL.Color; + +namespace Ryujinx.Graphics.GAL.Sampler +{ + public struct SamplerCreateInfo + { + public MinFilter MinFilter { get; } + public MagFilter MagFilter { get; } + + public AddressMode AddressU { get; } + public AddressMode AddressV { get; } + public AddressMode AddressP { get; } + + public CompareMode CompareMode { get; } + public CompareOp CompareOp { get; } + + public ColorF BorderColor { get; } + + public float MinLod { get; } + public float MaxLod { get; } + public float MipLodBias { get; } + public float MaxAnisotropy { get; } + + public SamplerCreateInfo( + MinFilter minFilter, + MagFilter magFilter, + AddressMode addressU, + AddressMode addressV, + AddressMode addressP, + CompareMode compareMode, + CompareOp compareOp, + ColorF borderColor, + float minLod, + float maxLod, + float mipLodBias, + float maxAnisotropy) + { + MinFilter = minFilter; + MagFilter = magFilter; + AddressU = addressU; + AddressV = addressV; + AddressP = addressP; + CompareMode = compareMode; + CompareOp = compareOp; + BorderColor = borderColor; + MinLod = minLod; + MaxLod = maxLod; + MipLodBias = mipLodBias; + MaxAnisotropy = maxAnisotropy; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Texture/DepthStencilMode.cs b/Ryujinx.Graphics.GAL/Texture/DepthStencilMode.cs new file mode 100644 index 000000000..d7a379d81 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Texture/DepthStencilMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.GAL.Texture +{ + public enum DepthStencilMode + { + Depth, + Stencil + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Texture/SwizzleComponent.cs b/Ryujinx.Graphics.GAL/Texture/SwizzleComponent.cs new file mode 100644 index 000000000..fd7d50e3b --- /dev/null +++ b/Ryujinx.Graphics.GAL/Texture/SwizzleComponent.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.GAL.Texture +{ + public enum SwizzleComponent + { + Zero, + One, + Red, + Green, + Blue, + Alpha + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Texture/Target.cs b/Ryujinx.Graphics.GAL/Texture/Target.cs new file mode 100644 index 000000000..b9cc11054 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Texture/Target.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.GAL.Texture +{ + public enum Target + { + Texture1D, + Texture2D, + Texture3D, + Texture1DArray, + Texture2DArray, + Texture2DMultisample, + Texture2DMultisampleArray, + Rectangle, + Cubemap, + CubemapArray, + TextureBuffer + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.GAL/Texture/TextureCreateInfo.cs b/Ryujinx.Graphics.GAL/Texture/TextureCreateInfo.cs new file mode 100644 index 000000000..ad365f6b1 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Texture/TextureCreateInfo.cs @@ -0,0 +1,115 @@ +using Ryujinx.Common; +using System; + +namespace Ryujinx.Graphics.GAL.Texture +{ + public struct TextureCreateInfo + { + public int Width { get; } + public int Height { get; } + public int Depth { get; } + public int Levels { get; } + public int Samples { get; } + public int BlockWidth { get; } + public int BlockHeight { get; } + public int BytesPerPixel { get; } + + public bool IsCompressed => (BlockWidth | BlockHeight) != 1; + + public Format Format { get; } + + public DepthStencilMode DepthStencilMode { get; } + + public Target Target { get; } + + public SwizzleComponent SwizzleR { get; } + public SwizzleComponent SwizzleG { get; } + public SwizzleComponent SwizzleB { get; } + public SwizzleComponent SwizzleA { get; } + + public TextureCreateInfo( + int width, + int height, + int depth, + int levels, + int samples, + int blockWidth, + int blockHeight, + int bytesPerPixel, + Format format, + DepthStencilMode depthStencilMode, + Target target, + SwizzleComponent swizzleR, + SwizzleComponent swizzleG, + SwizzleComponent swizzleB, + SwizzleComponent swizzleA) + { + Width = width; + Height = height; + Depth = depth; + Levels = levels; + Samples = samples; + BlockWidth = blockWidth; + BlockHeight = blockHeight; + BytesPerPixel = bytesPerPixel; + Format = format; + DepthStencilMode = depthStencilMode; + Target = target; + SwizzleR = swizzleR; + SwizzleG = swizzleG; + SwizzleB = swizzleB; + SwizzleA = swizzleA; + } + + public int GetMipSize(int level) + { + return GetMipStride(level) * GetLevelHeight(level) * GetLevelDepth(level); + } + + public int GetMipStride(int level) + { + return BitUtils.AlignUp(GetLevelWidth(level) * BytesPerPixel, 4); + } + + private int GetLevelWidth(int level) + { + return BitUtils.DivRoundUp(GetLevelSize(Width, level), BlockWidth); + } + + private int GetLevelHeight(int level) + { + return BitUtils.DivRoundUp(GetLevelSize(Height, level), BlockHeight); + } + + private int GetLevelDepth(int level) + { + return Target == Target.Texture3D ? GetLevelSize(Depth, level) : GetLayers(); + } + + public int GetDepthOrLayers() + { + return Target == Target.Texture3D ? Depth : GetLayers(); + } + + public int GetLayers() + { + if (Target == Target.Texture2DArray || + Target == Target.Texture2DMultisampleArray || + Target == Target.CubemapArray) + { + return Depth; + } + else if (Target == Target.Cubemap) + { + return 6; + } + + return 1; + } + + private static int GetLevelSize(int size, int level) + { + return Math.Max(1, size >> level); + } + } +} diff --git a/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs b/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs new file mode 100644 index 000000000..c058df2bb --- /dev/null +++ b/Ryujinx.Graphics.GAL/TextureReleaseCallback.cs @@ -0,0 +1,4 @@ +namespace Ryujinx.Graphics.GAL +{ + public delegate void TextureReleaseCallback(object context); +} diff --git a/Ryujinx.Graphics.GAL/Viewport.cs b/Ryujinx.Graphics.GAL/Viewport.cs new file mode 100644 index 000000000..d9d6e20a4 --- /dev/null +++ b/Ryujinx.Graphics.GAL/Viewport.cs @@ -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; + } + } +} diff --git a/Ryujinx.Graphics.GAL/ViewportSwizzle.cs b/Ryujinx.Graphics.GAL/ViewportSwizzle.cs new file mode 100644 index 000000000..5f04bf87d --- /dev/null +++ b/Ryujinx.Graphics.GAL/ViewportSwizzle.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.GAL +{ + public enum ViewportSwizzle + { + PositiveX, + NegativeX, + PositiveY, + NegativeY, + PositiveZ, + NegativeZ, + PositiveW, + NegativeW + } +} diff --git a/Ryujinx.Graphics.Gpu/ClassId.cs b/Ryujinx.Graphics.Gpu/ClassId.cs new file mode 100644 index 000000000..3cde0ac06 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/ClassId.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gpu +{ + enum ClassId + { + Engine2D = 0x902d, + Engine3D = 0xb197, + EngineCompute = 0xb1c0, + EngineInline2Memory = 0xa140, + EngineDma = 0xb0b5 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Constants.cs b/Ryujinx.Graphics.Gpu/Constants.cs new file mode 100644 index 000000000..501933fbe --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Constants.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gpu +{ + static class Constants + { + public const int TotalCpUniformBuffers = 8; + public const int TotalCpStorageBuffers = 16; + public const int TotalGpUniformBuffers = 18; + public const int TotalGpStorageBuffers = 16; + public const int TotalRenderTargets = 8; + public const int TotalShaderStages = 5; + public const int TotalVertexBuffers = 16; + public const int TotalViewports = 8; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Debugging.cs b/Ryujinx.Graphics.Gpu/Debugging.cs new file mode 100644 index 000000000..cb99cee18 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Debugging.cs @@ -0,0 +1,25 @@ +using System; + +namespace Ryujinx.Graphics.Gpu +{ + static class Debugging + { + public static void PrintTexInfo(string prefix, Image.Texture tex) + { + if (tex == null) + { + Console.WriteLine(prefix + " null"); + + return; + } + + string range = $"{tex.Address:X}..{(tex.Address + tex.Size):X}"; + + int debugId = tex.HostTexture.GetStorageDebugId(); + + string str = $"{prefix} p {debugId:X8} {tex.Info.Target} {tex.Info.FormatInfo.Format} {tex.Info.Width}x{tex.Info.Height}x{tex.Info.DepthOrLayers} mips {tex.Info.Levels} addr {range}"; + + Console.WriteLine(str); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/DmaPusher.cs b/Ryujinx.Graphics.Gpu/DmaPusher.cs similarity index 81% rename from Ryujinx.Graphics/DmaPusher.cs rename to Ryujinx.Graphics.Gpu/DmaPusher.cs index 74a32a4a2..ae9cc868c 100644 --- a/Ryujinx.Graphics/DmaPusher.cs +++ b/Ryujinx.Graphics.Gpu/DmaPusher.cs @@ -1,15 +1,14 @@ -using Ryujinx.Graphics.Memory; using System.Collections.Concurrent; using System.Threading; -namespace Ryujinx.Graphics +namespace Ryujinx.Graphics.Gpu { public class DmaPusher { - private ConcurrentQueue<(NvGpuVmm, long)> _ibBuffer; + private ConcurrentQueue _ibBuffer; - private long _dmaPut; - private long _dmaGet; + private ulong _dmaPut; + private ulong _dmaGet; private struct DmaState { @@ -29,28 +28,26 @@ namespace Ryujinx.Graphics private bool _ibEnable; private bool _nonMain; - private long _dmaMGet; + private ulong _dmaMGet; - private NvGpuVmm _vmm; - - private NvGpu _gpu; + private GpuContext _context; private AutoResetEvent _event; - public DmaPusher(NvGpu gpu) + internal DmaPusher(GpuContext context) { - _gpu = gpu; + _context = context; - _ibBuffer = new ConcurrentQueue<(NvGpuVmm, long)>(); + _ibBuffer = new ConcurrentQueue(); _ibEnable = true; _event = new AutoResetEvent(false); } - public void Push(NvGpuVmm vmm, long entry) + public void Push(ulong entry) { - _ibBuffer.Enqueue((vmm, entry)); + _ibBuffer.Enqueue(entry); _event.Set(); } @@ -69,7 +66,7 @@ namespace Ryujinx.Graphics { if (_dmaGet != _dmaPut) { - int word = _vmm.ReadInt32(_dmaGet); + int word = _context.MemoryAccessor.ReadInt32(_dmaGet); _dmaGet += 4; @@ -148,20 +145,14 @@ namespace Ryujinx.Graphics } } } - else if (_ibEnable && _ibBuffer.TryDequeue(out (NvGpuVmm Vmm, long Entry) tuple)) + else if (_ibEnable && _ibBuffer.TryDequeue(out ulong entry)) { - _vmm = tuple.Vmm; - - long entry = tuple.Entry; - - int length = (int)(entry >> 42) & 0x1fffff; + ulong length = (entry >> 42) & 0x1fffff; _dmaGet = entry & 0xfffffffffc; _dmaPut = _dmaGet + length * 4; - _nonMain = (entry & (1L << 41)) != 0; - - _gpu.ResourceManager.ClearPbCache(); + _nonMain = (entry & (1UL << 41)) != 0; } else { @@ -180,7 +171,7 @@ namespace Ryujinx.Graphics private void CallMethod(int argument) { - _gpu.Fifo.CallMethod(_vmm, new GpuMethodCall( + _context.Fifo.CallMethod(new MethodParams( _state.Method, argument, _state.SubChannel, diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs new file mode 100644 index 000000000..c8627435f --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -0,0 +1,83 @@ +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Shader; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + public void Dispatch(int argument) + { + uint dispatchParamsAddress = (uint)_context.State.Get(MethodOffset.DispatchParamsAddress); + + var dispatchParams = _context.MemoryAccessor.Read((ulong)dispatchParamsAddress << 8); + + GpuVa shaderBaseAddress = _context.State.Get(MethodOffset.ShaderBaseAddress); + + ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)dispatchParams.ShaderOffset; + + ComputeShader cs = _shaderCache.GetComputeShader( + shaderGpuVa, + dispatchParams.UnpackBlockSizeX(), + dispatchParams.UnpackBlockSizeY(), + dispatchParams.UnpackBlockSizeZ()); + + _context.Renderer.ComputePipeline.SetProgram(cs.Interface); + + ShaderProgramInfo info = cs.Shader.Info; + + uint sbEnableMask = 0; + uint ubEnableMask = dispatchParams.UnpackUniformBuffersEnableMask(); + + for (int index = 0; index < dispatchParams.UniformBuffers.Length; index++) + { + if ((ubEnableMask & (1 << index)) == 0) + { + continue; + } + + ulong gpuVa = dispatchParams.UniformBuffers[index].PackAddress(); + ulong size = dispatchParams.UniformBuffers[index].UnpackSize(); + + _bufferManager.SetComputeUniformBuffer(index, gpuVa, size); + } + + for (int index = 0; index < info.SBuffers.Count; index++) + { + BufferDescriptor sb = info.SBuffers[index]; + + sbEnableMask |= 1u << sb.Slot; + + ulong sbDescAddress = _bufferManager.GetComputeUniformBufferAddress(0); + + int sbDescOffset = 0x310 + sb.Slot * 0x10; + + sbDescAddress += (ulong)sbDescOffset; + + Span sbDescriptorData = _context.PhysicalMemory.Read(sbDescAddress, 0x10); + + SbDescriptor sbDescriptor = MemoryMarshal.Cast(sbDescriptorData)[0]; + + _bufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size); + } + + ubEnableMask = 0; + + for (int index = 0; index < info.CBuffers.Count; index++) + { + ubEnableMask |= 1u << info.CBuffers[index].Slot; + } + + _bufferManager.SetComputeStorageBufferEnableMask(sbEnableMask); + _bufferManager.SetComputeUniformBufferEnableMask(ubEnableMask); + + _bufferManager.CommitComputeBindings(); + + _context.Renderer.ComputePipeline.Dispatch( + dispatchParams.UnpackGridSizeX(), + dispatchParams.UnpackGridSizeY(), + dispatchParams.UnpackGridSizeZ()); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs b/Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs new file mode 100644 index 000000000..03582f050 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs @@ -0,0 +1,126 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + struct UniformBufferParams + { + public int AddressLow; + public int AddressHighAndSize; + + public ulong PackAddress() + { + return (uint)AddressLow | ((ulong)(AddressHighAndSize & 0xff) << 32); + } + + public ulong UnpackSize() + { + return (ulong)((AddressHighAndSize >> 15) & 0x1ffff); + } + } + + struct ComputeParams + { + public int Unknown0; + public int Unknown1; + public int Unknown2; + public int Unknown3; + public int Unknown4; + public int Unknown5; + public int Unknown6; + public int Unknown7; + public int ShaderOffset; + public int Unknown9; + public int Unknown10; + public int Unknown11; + public int GridSizeX; + public int GridSizeYZ; + public int Unknown14; + public int Unknown15; + public int Unknown16; + public int Unknown17; + public int BlockSizeX; + public int BlockSizeYZ; + public int UniformBuffersConfig; + public int Unknown21; + public int Unknown22; + public int Unknown23; + public int Unknown24; + public int Unknown25; + public int Unknown26; + public int Unknown27; + public int Unknown28; + + private UniformBufferParams _uniformBuffer0; + private UniformBufferParams _uniformBuffer1; + private UniformBufferParams _uniformBuffer2; + private UniformBufferParams _uniformBuffer3; + private UniformBufferParams _uniformBuffer4; + private UniformBufferParams _uniformBuffer5; + private UniformBufferParams _uniformBuffer6; + private UniformBufferParams _uniformBuffer7; + + public Span UniformBuffers + { + get + { + return MemoryMarshal.CreateSpan(ref _uniformBuffer0, 8); + } + } + + public int Unknown45; + public int Unknown46; + public int Unknown47; + public int Unknown48; + public int Unknown49; + public int Unknown50; + public int Unknown51; + public int Unknown52; + public int Unknown53; + public int Unknown54; + public int Unknown55; + public int Unknown56; + public int Unknown57; + public int Unknown58; + public int Unknown59; + public int Unknown60; + public int Unknown61; + public int Unknown62; + public int Unknown63; + + public int UnpackGridSizeX() + { + return GridSizeX & 0x7fffffff; + } + + public int UnpackGridSizeY() + { + return GridSizeYZ & 0xffff; + } + + public int UnpackGridSizeZ() + { + return (GridSizeYZ >> 16) & 0xffff; + } + + public int UnpackBlockSizeX() + { + return (BlockSizeX >> 16) & 0xffff; + } + + public int UnpackBlockSizeY() + { + return BlockSizeYZ & 0xffff; + } + + public int UnpackBlockSizeZ() + { + return (BlockSizeYZ >> 16) & 0xffff; + } + + public uint UnpackUniformBuffersEnableMask() + { + return (uint)UniformBuffersConfig & 0xff; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs b/Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs new file mode 100644 index 000000000..cc7d4d996 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs @@ -0,0 +1,18 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + class ComputeShader + { + public IProgram Interface { get; set; } + + public ShaderProgram Shader { get; } + + public ComputeShader(IProgram program, ShaderProgram shader) + { + Interface = program; + Shader = shader; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs b/Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs new file mode 100644 index 000000000..a8ccc05ac --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs @@ -0,0 +1,17 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + class GraphicsShader + { + public IProgram Interface { get; set; } + + public ShaderProgram[] Shader { get; } + + public GraphicsShader() + { + Shader = new ShaderProgram[5]; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs b/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs new file mode 100644 index 000000000..d2816ac59 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs @@ -0,0 +1,42 @@ +using Ryujinx.Graphics.Gpu.State; +using System; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private Inline2MemoryParams _params; + + private bool _isLinear; + + private int _offset; + private int _size; + + public void Execute(int argument) + { + _params = _context.State.Get(MethodOffset.Inline2MemoryParams); + + _isLinear = (argument & 1) != 0; + + _offset = 0; + _size = _params.LineLengthIn * _params.LineCount; + } + + public void PushData(int argument) + { + if (_isLinear) + { + for (int shift = 0; shift < 32 && _offset < _size; shift += 8, _offset++) + { + ulong gpuVa = _params.DstAddress.Pack() + (ulong)_offset; + + _context.MemoryAccessor.Write(gpuVa, new byte[] { (byte)(argument >> shift) }); + } + } + else + { + throw new NotImplementedException(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs b/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs new file mode 100644 index 000000000..b4680fa57 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodClear.cs @@ -0,0 +1,55 @@ +using Ryujinx.Graphics.GAL.Color; +using Ryujinx.Graphics.Gpu.State; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private void Clear(int argument) + { + UpdateState(); + + bool clearDepth = (argument & 1) != 0; + bool clearStencil = (argument & 2) != 0; + + uint componentMask = (uint)((argument >> 2) & 0xf); + + int index = (argument >> 6) & 0xf; + + if (componentMask != 0) + { + ClearColors clearColor = _context.State.GetClearColors(); + + ColorF color = new ColorF( + clearColor.Red, + clearColor.Green, + clearColor.Blue, + clearColor.Alpha); + + _context.Renderer.GraphicsPipeline.ClearRenderTargetColor( + index, + componentMask, + color); + } + + if (clearDepth || clearStencil) + { + float depthValue = _context.State.GetClearDepthValue(); + int stencilValue = _context.State.GetClearStencilValue(); + + int stencilMask = 0; + + if (clearStencil) + { + stencilMask = _context.State.GetStencilTestState().FrontMask; + } + + _context.Renderer.GraphicsPipeline.ClearRenderTargetDepthStencil( + depthValue, + clearDepth, + stencilValue, + stencilMask); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs new file mode 100644 index 000000000..19ffb0e3c --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs @@ -0,0 +1,79 @@ +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Texture; +using System; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private void CopyBuffer(int argument) + { + var cbp = _context.State.Get(MethodOffset.CopyBufferParams); + + var swizzle = _context.State.Get(MethodOffset.CopyBufferSwizzle); + + bool srcLinear = (argument & (1 << 7)) != 0; + bool dstLinear = (argument & (1 << 8)) != 0; + bool copy2D = (argument & (1 << 9)) != 0; + + int size = cbp.XCount; + + if (size == 0) + { + return; + } + + if (copy2D) + { + // Buffer to texture copy. + int srcBpp = swizzle.UnpackSrcComponentsCount() * swizzle.UnpackComponentSize(); + int dstBpp = swizzle.UnpackDstComponentsCount() * swizzle.UnpackComponentSize(); + + var dst = _context.State.Get(MethodOffset.CopyBufferDstTexture); + var src = _context.State.Get(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 pixel = _context.PhysicalMemory.Read(srcAddress, (ulong)srcBpp); + + _context.PhysicalMemory.Write(dstAddress, pixel); + } + } + else + { + // Buffer to buffer copy. + _bufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size); + + Span data = _context.MemoryAccessor.Read(cbp.SrcAddress.Pack(), (uint)size); + + _context.MemoryAccessor.Write(cbp.DstAddress.Pack(), data); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs b/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs new file mode 100644 index 000000000..e2c40805b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs @@ -0,0 +1,70 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.State; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private void CopyTexture(int argument) + { + CopyTexture dstCopyTexture = _context.State.GetCopyDstTexture(); + CopyTexture srcCopyTexture = _context.State.GetCopySrcTexture(); + + Image.Texture srcTexture = _textureManager.FindOrCreateTexture(srcCopyTexture); + + if (srcTexture == null) + { + return; + } + + // When the source texture that was found has a depth format, + // we must enforce the target texture also has a depth format, + // as copies between depth and color formats are not allowed. + if (srcTexture.Format == Format.D32Float) + { + dstCopyTexture.Format = RtFormat.D32Float; + } + + Image.Texture dstTexture = _textureManager.FindOrCreateTexture(dstCopyTexture); + + if (dstTexture == null) + { + return; + } + + CopyTextureControl control = _context.State.GetCopyTextureControl(); + + CopyRegion region = _context.State.GetCopyRegion(); + + int srcX1 = (int)(region.SrcXF >> 32); + int srcY1 = (int)(region.SrcYF >> 32); + + int srcX2 = (int)((region.SrcXF + region.SrcWidthRF * region.DstWidth) >> 32); + int srcY2 = (int)((region.SrcYF + region.SrcHeightRF * region.DstHeight) >> 32); + + int dstX1 = region.DstX; + int dstY1 = region.DstY; + + int dstX2 = region.DstX + region.DstWidth; + int dstY2 = region.DstY + region.DstHeight; + + Extents2D srcRegion = new Extents2D( + srcX1 / srcTexture.Info.SamplesInX, + srcY1 / srcTexture.Info.SamplesInY, + srcX2 / srcTexture.Info.SamplesInX, + srcY2 / srcTexture.Info.SamplesInY); + + Extents2D dstRegion = new Extents2D( + dstX1 / dstTexture.Info.SamplesInX, + dstY1 / dstTexture.Info.SamplesInY, + dstX2 / dstTexture.Info.SamplesInX, + dstY2 / dstTexture.Info.SamplesInY); + + bool linearFilter = control.UnpackLinearFilter(); + + srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter); + + dstTexture.Modified = true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs b/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs new file mode 100644 index 000000000..dd360113b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs @@ -0,0 +1,133 @@ +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Gpu.Image; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private bool _drawIndexed; + + private int _firstIndex; + private int _indexCount; + + private bool _instancedHasState; + private bool _instancedIndexed; + + private int _instancedFirstIndex; + private int _instancedFirstVertex; + private int _instancedFirstInstance; + private int _instancedIndexCount; + private int _instancedDrawStateFirst; + private int _instancedDrawStateCount; + + private int _instanceIndex; + + public PrimitiveType PrimitiveType { get; private set; } + + private void DrawEnd(int argument) + { + UpdateState(); + + bool instanced = _vsUsesInstanceId || _isAnyVbInstanced; + + if (instanced) + { + if (!_instancedHasState) + { + _instancedHasState = true; + + _instancedIndexed = _drawIndexed; + + _instancedFirstIndex = _firstIndex; + _instancedFirstVertex = _context.State.GetBaseVertex(); + _instancedFirstInstance = _context.State.GetBaseInstance(); + + _instancedIndexCount = _indexCount; + + VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState(); + + _instancedDrawStateFirst = drawState.First; + _instancedDrawStateCount = drawState.Count; + } + + return; + } + + int firstInstance = _context.State.GetBaseInstance(); + + if (_drawIndexed) + { + _drawIndexed = false; + + int firstVertex = _context.State.GetBaseVertex(); + + _context.Renderer.GraphicsPipeline.DrawIndexed( + _indexCount, + 1, + _firstIndex, + firstVertex, + firstInstance); + } + else + { + VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState(); + + _context.Renderer.GraphicsPipeline.Draw( + drawState.Count, + 1, + drawState.First, + firstInstance); + } + } + + private void DrawBegin(int argument) + { + PrimitiveType type = (PrimitiveType)(argument & 0xffff); + + _context.Renderer.GraphicsPipeline.SetPrimitiveTopology(type.Convert()); + + PrimitiveType = type; + + if ((argument & (1 << 26)) != 0) + { + _instanceIndex++; + } + else if ((argument & (1 << 27)) == 0) + { + _instanceIndex = 0; + } + } + + private void SetIndexCount(int argument) + { + _drawIndexed = true; + } + + public void PerformDeferredDraws() + { + // Perform any pending instanced draw. + if (_instancedHasState) + { + _instancedHasState = false; + + if (_instancedIndexed) + { + _context.Renderer.GraphicsPipeline.DrawIndexed( + _instancedIndexCount, + _instanceIndex + 1, + _instancedFirstIndex, + _instancedFirstVertex, + _instancedFirstInstance); + } + else + { + _context.Renderer.GraphicsPipeline.Draw( + _instancedDrawStateCount, + _instanceIndex + 1, + _instancedDrawStateFirst, + _instancedFirstInstance); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs new file mode 100644 index 000000000..fd0a31a16 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs @@ -0,0 +1,100 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.State; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private ulong _runningCounter; + + private void Report(int argument) + { + ReportMode mode = (ReportMode)(argument & 3); + + ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f); + + switch (mode) + { + case ReportMode.Semaphore: ReportSemaphore(); break; + case ReportMode.Counter: ReportCounter(type); break; + } + } + + private void ReportSemaphore() + { + ReportState state = _context.State.GetReportState(); + + _context.MemoryAccessor.Write(state.Address.Pack(), state.Payload); + + _context.AdvanceSequence(); + } + + private struct CounterData + { + public ulong Counter; + public ulong Timestamp; + } + + private void ReportCounter(ReportCounterType type) + { + CounterData counterData = new CounterData(); + + ulong counter = 0; + + switch (type) + { + case ReportCounterType.Zero: + counter = 0; + break; + case ReportCounterType.SamplesPassed: + counter = _context.Renderer.GetCounter(CounterType.SamplesPassed); + break; + case ReportCounterType.PrimitivesGenerated: + counter = _context.Renderer.GetCounter(CounterType.PrimitivesGenerated); + break; + case ReportCounterType.TransformFeedbackPrimitivesWritten: + counter = _context.Renderer.GetCounter(CounterType.TransformFeedbackPrimitivesWritten); + break; + } + + ulong ticks; + + if (GraphicsConfig.FastGpuTime) + { + ticks = _runningCounter++; + } + else + { + ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds); + } + + counterData.Counter = counter; + counterData.Timestamp = ticks; + + Span counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1); + + Span data = MemoryMarshal.Cast(counterDataSpan); + + ReportState state = _context.State.GetReportState(); + + _context.MemoryAccessor.Write(state.Address.Pack(), data); + } + + private static ulong ConvertNanosecondsToTicks(ulong nanoseconds) + { + // We need to divide first to avoid overflows. + // We fix up the result later by calculating the difference and adding + // that to the result. + ulong divided = nanoseconds / 625; + + ulong rounded = divided * 625; + + ulong errorBias = ((nanoseconds - rounded) * 384) / 625; + + return divided * 384 + errorBias; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs b/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs new file mode 100644 index 000000000..12a878459 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs @@ -0,0 +1,26 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.State; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private void ResetCounter(int argument) + { + ResetCounterType type = (ResetCounterType)argument; + + switch (type) + { + case ResetCounterType.SamplesPassed: + _context.Renderer.ResetCounter(CounterType.SamplesPassed); + break; + case ResetCounterType.PrimitivesGenerated: + _context.Renderer.ResetCounter(CounterType.PrimitivesGenerated); + break; + case ResetCounterType.TransformFeedbackPrimitivesWritten: + _context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten); + break; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs new file mode 100644 index 000000000..a9ebce83c --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs @@ -0,0 +1,52 @@ +using Ryujinx.Graphics.Gpu.State; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private void UniformBufferBind0(int argument) + { + UniformBufferBind(argument, ShaderType.Vertex); + } + + private void UniformBufferBind1(int argument) + { + UniformBufferBind(argument, ShaderType.TessellationControl); + } + + private void UniformBufferBind2(int argument) + { + UniformBufferBind(argument, ShaderType.TessellationEvaluation); + } + + private void UniformBufferBind3(int argument) + { + UniformBufferBind(argument, ShaderType.Geometry); + } + + private void UniformBufferBind4(int argument) + { + UniformBufferBind(argument, ShaderType.Fragment); + } + + private void UniformBufferBind(int argument, ShaderType type) + { + bool enable = (argument & 1) != 0; + + int index = (argument >> 4) & 0x1f; + + if (enable) + { + UniformBufferState uniformBuffer = _context.State.GetUniformBufferState(); + + ulong address = uniformBuffer.Address.Pack(); + + _bufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size); + } + else + { + _bufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs new file mode 100644 index 000000000..f0b8e2d77 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs @@ -0,0 +1,18 @@ +using Ryujinx.Graphics.Gpu.State; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private void UniformBufferUpdate(int argument) + { + UniformBufferState uniformBuffer = _context.State.GetUniformBufferState(); + + _context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument); + + _context.State.SetUniformBufferOffset(uniformBuffer.Offset + 4); + + _context.AdvanceSequence(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs new file mode 100644 index 000000000..db72a861a --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -0,0 +1,784 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Blend; +using Ryujinx.Graphics.GAL.DepthStencil; +using Ryujinx.Graphics.GAL.InputAssembler; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Shader; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + partial class Methods + { + private GpuContext _context; + + private ShaderCache _shaderCache; + + private BufferManager _bufferManager; + private TextureManager _textureManager; + + public TextureManager TextureManager => _textureManager; + + private bool _isAnyVbInstanced; + private bool _vsUsesInstanceId; + + public Methods(GpuContext context) + { + _context = context; + + _shaderCache = new ShaderCache(_context); + + _bufferManager = new BufferManager(context); + _textureManager = new TextureManager(context, _bufferManager); + + RegisterCallbacks(); + } + + private void RegisterCallbacks() + { + _context.State.RegisterCopyBufferCallback(CopyBuffer); + _context.State.RegisterCopyTextureCallback(CopyTexture); + + _context.State.RegisterDrawEndCallback(DrawEnd); + + _context.State.RegisterDrawBeginCallback(DrawBegin); + + _context.State.RegisterSetIndexCountCallback(SetIndexCount); + + _context.State.RegisterClearCallback(Clear); + + _context.State.RegisterReportCallback(Report); + + _context.State.RegisterUniformBufferUpdateCallback(UniformBufferUpdate); + + _context.State.RegisterUniformBufferBind0Callback(UniformBufferBind0); + _context.State.RegisterUniformBufferBind1Callback(UniformBufferBind1); + _context.State.RegisterUniformBufferBind2Callback(UniformBufferBind2); + _context.State.RegisterUniformBufferBind3Callback(UniformBufferBind3); + _context.State.RegisterUniformBufferBind4Callback(UniformBufferBind4); + + _context.State.RegisterCallback(MethodOffset.InvalidateTextures, InvalidateTextures); + + _context.State.RegisterCallback(MethodOffset.ResetCounter, ResetCounter); + + _context.State.RegisterCallback(MethodOffset.Inline2MemoryExecute, Execute); + _context.State.RegisterCallback(MethodOffset.Inline2MemoryPushData, PushData); + + _context.State.RegisterCallback(MethodOffset.Dispatch, Dispatch); + } + + public Image.Texture GetTexture(ulong address) => _textureManager.Find2(address); + + private void UpdateState() + { + if ((_context.State.StateWriteFlags & StateWriteFlags.Any) == 0) + { + CommitBindings(); + + return; + } + + // Shaders must be the first one to be updated if modified, because + // some of the other state depends on information from the currently + // bound shaders. + if ((_context.State.StateWriteFlags & StateWriteFlags.ShaderState) != 0) + { + UpdateShaderState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.RenderTargetGroup) != 0) + { + UpdateRenderTargetGroupState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.DepthTestState) != 0) + { + UpdateDepthTestState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.ViewportTransform) != 0) + { + UpdateViewportTransform(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.DepthBiasState) != 0) + { + UpdateDepthBiasState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.StencilTestState) != 0) + { + UpdateStencilTestState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.SamplerPoolState) != 0) + { + UpdateSamplerPoolState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.TexturePoolState) != 0) + { + UpdateTexturePoolState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.InputAssemblerGroup) != 0) + { + UpdateInputAssemblerGroupState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.FaceState) != 0) + { + UpdateFaceState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.RtColorMask) != 0) + { + UpdateRtColorMask(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.BlendState) != 0) + { + UpdateBlendState(); + } + + _context.State.StateWriteFlags &= ~StateWriteFlags.Any; + + CommitBindings(); + } + + private void CommitBindings() + { + _bufferManager.CommitBindings(); + _textureManager.CommitBindings(); + } + + public void InvalidateRange(ulong address, ulong size) + { + _bufferManager.InvalidateRange(address, size); + _textureManager.InvalidateRange(address, size); + } + + public void InvalidateTextureRange(ulong address, ulong size) + { + _textureManager.InvalidateRange(address, size); + } + + private void UpdateRenderTargetGroupState() + { + TextureMsaaMode msaaMode = _context.State.GetRtMsaaMode(); + + int samplesInX = msaaMode.SamplesInX(); + int samplesInY = msaaMode.SamplesInY(); + + Image.Texture color3D = Get3DRenderTarget(samplesInX, samplesInY); + + if (color3D == null) + { + for (int index = 0; index < Constants.TotalRenderTargets; index++) + { + RtColorState colorState = _context.State.GetRtColorState(index); + + if (!IsRtEnabled(colorState)) + { + _textureManager.SetRenderTargetColor(index, null); + + continue; + } + + Image.Texture color = _textureManager.FindOrCreateTexture( + colorState, + samplesInX, + samplesInY); + + _textureManager.SetRenderTargetColor(index, color); + + color.Modified = true; + } + } + else + { + _textureManager.SetRenderTargetColor3D(color3D); + + color3D.Modified = true; + } + + bool dsEnable = _context.State.Get(MethodOffset.RtDepthStencilEnable); + + Image.Texture depthStencil = null; + + if (dsEnable) + { + var dsState = _context.State.GetRtDepthStencilState(); + var dsSize = _context.State.GetRtDepthStencilSize(); + + depthStencil = _textureManager.FindOrCreateTexture( + dsState, + dsSize, + samplesInX, + samplesInY); + } + + _textureManager.SetRenderTargetDepthStencil(depthStencil); + } + + private Image.Texture Get3DRenderTarget(int samplesInX, int samplesInY) + { + RtColorState colorState0 = _context.State.GetRtColorState(0); + + if (!IsRtEnabled(colorState0) || !colorState0.MemoryLayout.UnpackIsTarget3D() || colorState0.Depth != 1) + { + return null; + } + + int slices = 1; + int unused = 0; + + for (int index = 1; index < Constants.TotalRenderTargets; index++) + { + RtColorState colorState = _context.State.GetRtColorState(index); + + if (!IsRtEnabled(colorState)) + { + unused++; + + continue; + } + + if (colorState.MemoryLayout.UnpackIsTarget3D() && colorState.Depth == 1) + { + slices++; + } + } + + if (slices + unused == Constants.TotalRenderTargets) + { + colorState0.Depth = slices; + + return _textureManager.FindOrCreateTexture(colorState0, samplesInX, samplesInY); + } + + return null; + } + + private static bool IsRtEnabled(RtColorState colorState) + { + // Colors are disabled by writing 0 to the format. + return colorState.Format != 0 && colorState.WidthOrStride != 0; + } + + private void UpdateDepthTestState() + { + _context.Renderer.GraphicsPipeline.SetDepthTest(new DepthTestDescriptor( + _context.State.GetDepthTestEnable().IsTrue(), + _context.State.GetDepthWriteEnable().IsTrue(), + _context.State.GetDepthTestFunc())); + } + + private void UpdateViewportTransform() + { + Viewport[] viewports = new Viewport[Constants.TotalViewports]; + + for (int index = 0; index < Constants.TotalViewports; index++) + { + var transform = _context.State.Get(MethodOffset.ViewportTransform + index * 8); + var extents = _context.State.Get (MethodOffset.ViewportExtents + index * 4); + + float x = transform.TranslateX - MathF.Abs(transform.ScaleX); + float y = transform.TranslateY - MathF.Abs(transform.ScaleY); + + float width = transform.ScaleX * 2; + float height = transform.ScaleY * 2; + + RectangleF region = new RectangleF(x, y, width, height); + + viewports[index] = new Viewport( + region, + transform.UnpackSwizzleX(), + transform.UnpackSwizzleY(), + transform.UnpackSwizzleZ(), + transform.UnpackSwizzleW(), + extents.DepthNear, + extents.DepthFar); + } + + _context.Renderer.GraphicsPipeline.SetViewports(0, viewports); + } + + private void UpdateDepthBiasState() + { + var polygonOffset = _context.State.Get(MethodOffset.DepthBiasState); + + float factor = _context.State.Get(MethodOffset.DepthBiasFactor); + float units = _context.State.Get(MethodOffset.DepthBiasUnits); + float clamp = _context.State.Get(MethodOffset.DepthBiasClamp); + + PolygonModeMask enables = 0; + + enables = (polygonOffset.PointEnable.IsTrue() ? PolygonModeMask.Point : 0); + enables |= (polygonOffset.LineEnable.IsTrue() ? PolygonModeMask.Line : 0); + enables |= (polygonOffset.FillEnable.IsTrue() ? PolygonModeMask.Fill : 0); + + _context.Renderer.GraphicsPipeline.SetDepthBias(enables, factor, units, clamp); + } + + private void UpdateStencilTestState() + { + StencilBackMasks backMasks = _context.State.GetStencilBackMasks(); + StencilTestState test = _context.State.GetStencilTestState(); + StencilBackTestState backTest = _context.State.GetStencilBackTestState(); + + CompareOp backFunc; + StencilOp backSFail; + StencilOp backDpPass; + StencilOp backDpFail; + int backFuncRef; + int backFuncMask; + int backMask; + + if (backTest.TwoSided.IsTrue()) + { + backFunc = backTest.BackFunc; + backSFail = backTest.BackSFail; + backDpPass = backTest.BackDpPass; + backDpFail = backTest.BackDpFail; + backFuncRef = backMasks.FuncRef; + backFuncMask = backMasks.FuncMask; + backMask = backMasks.Mask; + } + else + { + backFunc = test.FrontFunc; + backSFail = test.FrontSFail; + backDpPass = test.FrontDpPass; + backDpFail = test.FrontDpFail; + backFuncRef = test.FrontFuncRef; + backFuncMask = test.FrontFuncMask; + backMask = test.FrontMask; + } + + _context.Renderer.GraphicsPipeline.SetStencilTest(new StencilTestDescriptor( + test.Enable.IsTrue(), + test.FrontFunc, + test.FrontSFail, + test.FrontDpPass, + test.FrontDpFail, + test.FrontFuncRef, + test.FrontFuncMask, + test.FrontMask, + backFunc, + backSFail, + backDpPass, + backDpFail, + backFuncRef, + backFuncMask, + backMask)); + } + + private void UpdateSamplerPoolState() + { + PoolState samplerPool = _context.State.GetSamplerPoolState(); + + _textureManager.SetSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId); + } + + private void UpdateTexturePoolState() + { + PoolState texturePool = _context.State.GetTexturePoolState(); + + _textureManager.SetTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); + + _textureManager.SetTextureBufferIndex(_context.State.GetTextureBufferIndex()); + } + + private void UpdateInputAssemblerGroupState() + { + // Must be updated before the vertex buffer. + if ((_context.State.StateWriteFlags & StateWriteFlags.VertexAttribState) != 0) + { + UpdateVertexAttribState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.PrimitiveRestartState) != 0) + { + UpdatePrimitiveRestartState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.IndexBufferState) != 0) + { + UpdateIndexBufferState(); + } + + if ((_context.State.StateWriteFlags & StateWriteFlags.VertexBufferState) != 0) + { + UpdateVertexBufferState(); + } + } + + private void UpdateVertexAttribState() + { + VertexAttribDescriptor[] vertexAttribs = new VertexAttribDescriptor[16]; + + for (int index = 0; index < 16; index++) + { + VertexAttribState vertexAttrib = _context.State.GetVertexAttribState(index); + + if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format)) + { + // TODO: warning. + + format = Format.R32G32B32A32Float; + } + + vertexAttribs[index] = new VertexAttribDescriptor( + vertexAttrib.UnpackBufferIndex(), + vertexAttrib.UnpackOffset(), + format); + } + + _context.Renderer.GraphicsPipeline.BindVertexAttribs(vertexAttribs); + } + + private void UpdatePrimitiveRestartState() + { + PrimitiveRestartState primitiveRestart = _context.State.Get(MethodOffset.PrimitiveRestartState); + + _context.Renderer.GraphicsPipeline.SetPrimitiveRestart( + primitiveRestart.Enable, + primitiveRestart.Index); + } + + private void UpdateIndexBufferState() + { + IndexBufferState indexBuffer = _context.State.GetIndexBufferState(); + + _firstIndex = indexBuffer.First; + _indexCount = indexBuffer.Count; + + if (_indexCount == 0) + { + return; + } + + ulong gpuVa = indexBuffer.Address.Pack(); + + // Do not use the end address to calculate the size, because + // the result may be much larger than the real size of the index buffer. + ulong size = (ulong)(_firstIndex + _indexCount); + + switch (indexBuffer.Type) + { + case IndexType.UShort: size *= 2; break; + case IndexType.UInt: size *= 4; break; + } + + _bufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type); + + // The index buffer affects the vertex buffer size calculation, we + // need to ensure that they are updated. + UpdateVertexBufferState(); + } + + private uint GetIndexBufferMaxIndex(ulong gpuVa, ulong size, IndexType type) + { + ulong address = _context.MemoryManager.Translate(gpuVa); + + Span data = _context.PhysicalMemory.Read(address, size); + + uint maxIndex = 0; + + switch (type) + { + case IndexType.UByte: + { + for (int index = 0; index < data.Length; index++) + { + if (maxIndex < data[index]) + { + maxIndex = data[index]; + } + } + + break; + } + + case IndexType.UShort: + { + Span indices = MemoryMarshal.Cast(data); + + for (int index = 0; index < indices.Length; index++) + { + if (maxIndex < indices[index]) + { + maxIndex = indices[index]; + } + } + + break; + } + + case IndexType.UInt: + { + Span indices = MemoryMarshal.Cast(data); + + for (int index = 0; index < indices.Length; index++) + { + if (maxIndex < indices[index]) + { + maxIndex = indices[index]; + } + } + + break; + } + } + + return maxIndex; + } + + private void UpdateVertexBufferState() + { + _isAnyVbInstanced = false; + + for (int index = 0; index < 16; index++) + { + VertexBufferState vertexBuffer = _context.State.GetVertexBufferState(index); + + if (!vertexBuffer.UnpackEnable()) + { + _bufferManager.SetVertexBuffer(index, 0, 0, 0, 0); + + continue; + } + + GpuVa endAddress = _context.State.GetVertexBufferEndAddress(index); + + ulong address = vertexBuffer.Address.Pack(); + + int stride = vertexBuffer.UnpackStride(); + + bool instanced = _context.State.Get(MethodOffset.VertexBufferInstanced + index); + + int divisor = instanced ? vertexBuffer.Divisor : 0; + + _isAnyVbInstanced |= divisor != 0; + + ulong size; + + if (_drawIndexed || stride == 0 || instanced) + { + // This size may be (much) larger than the real vertex buffer size. + // Avoid calculating it this way, unless we don't have any other option. + size = endAddress.Pack() - address + 1; + } + else + { + // For non-indexed draws, we can guess the size from the vertex count + // and stride. + int firstInstance = _context.State.GetBaseInstance(); + + VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState(); + + size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride); + } + + _bufferManager.SetVertexBuffer(index, address, size, stride, divisor); + } + } + + private void UpdateFaceState() + { + FaceState face = _context.State.GetFaceState(); + + _context.Renderer.GraphicsPipeline.SetFaceCulling(face.CullEnable.IsTrue(), face.CullFace); + + _context.Renderer.GraphicsPipeline.SetFrontFace(face.FrontFace); + } + + private void UpdateRtColorMask() + { + uint[] componentMasks = new uint[Constants.TotalRenderTargets]; + + for (int index = 0; index < Constants.TotalRenderTargets; index++) + { + RtColorMask colorMask = _context.State.Get(MethodOffset.RtColorMask + index); + + uint componentMask = 0; + + componentMask = (colorMask.UnpackRed() ? 1u : 0u); + componentMask |= (colorMask.UnpackGreen() ? 2u : 0u); + componentMask |= (colorMask.UnpackBlue() ? 4u : 0u); + componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u); + + componentMasks[index] = componentMask; + } + + _context.Renderer.GraphicsPipeline.SetRenderTargetColorMasks(componentMasks); + } + + private void UpdateBlendState() + { + BlendState[] blends = new BlendState[8]; + + for (int index = 0; index < 8; index++) + { + bool blendEnable = _context.State.GetBlendEnable(index).IsTrue(); + + BlendState blend = _context.State.GetBlendState(index); + + BlendDescriptor descriptor = new BlendDescriptor( + blendEnable, + blend.ColorOp, + blend.ColorSrcFactor, + blend.ColorDstFactor, + blend.AlphaOp, + blend.AlphaSrcFactor, + blend.AlphaDstFactor); + + _context.Renderer.GraphicsPipeline.BindBlendState(index, descriptor); + } + } + + private struct SbDescriptor + { + public uint AddressLow; + public uint AddressHigh; + public int Size; + public int Padding; + + public ulong PackAddress() + { + return AddressLow | ((ulong)AddressHigh << 32); + } + } + + private void UpdateShaderState() + { + ShaderAddresses addresses = new ShaderAddresses(); + + Span addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1); + + Span addressesArray = MemoryMarshal.Cast(addressesSpan); + + ulong baseAddress = _context.State.GetShaderBaseAddress().Pack(); + + for (int index = 0; index < 6; index++) + { + ShaderState shader = _context.State.GetShaderState(index); + + if (!shader.UnpackEnable() && index != 1) + { + continue; + } + + addressesArray[index] = baseAddress + shader.Offset; + } + + GraphicsShader gs = _shaderCache.GetGraphicsShader(addresses); + + _vsUsesInstanceId = gs.Shader[0].Info.UsesInstanceId; + + for (int stage = 0; stage < Constants.TotalShaderStages; stage++) + { + ShaderProgramInfo info = gs.Shader[stage]?.Info; + + if (info == null) + { + continue; + } + + var textureBindings = new TextureBindingInfo[info.Textures.Count]; + + for (int index = 0; index < info.Textures.Count; index++) + { + var descriptor = info.Textures[index]; + + Target target = GetTarget(descriptor.Target); + + textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex); + } + + _textureManager.BindTextures(stage, textureBindings); + + uint sbEnableMask = 0; + uint ubEnableMask = 0; + + for (int index = 0; index < info.SBuffers.Count; index++) + { + BufferDescriptor sb = info.SBuffers[index]; + + sbEnableMask |= 1u << sb.Slot; + + ulong sbDescAddress = _bufferManager.GetGraphicsUniformBufferAddress(stage, 0); + + int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10; + + sbDescAddress += (ulong)sbDescOffset; + + Span sbDescriptorData = _context.PhysicalMemory.Read(sbDescAddress, 0x10); + + SbDescriptor sbDescriptor = MemoryMarshal.Cast(sbDescriptorData)[0]; + + _bufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size); + } + + for (int index = 0; index < info.CBuffers.Count; index++) + { + ubEnableMask |= 1u << info.CBuffers[index].Slot; + } + + _bufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask); + _bufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask); + } + + _context.Renderer.GraphicsPipeline.BindProgram(gs.Interface); + } + + private static Target GetTarget(Shader.TextureTarget target) + { + target &= ~Shader.TextureTarget.Shadow; + + switch (target) + { + case Shader.TextureTarget.Texture1D: + return Target.Texture1D; + + case Shader.TextureTarget.Texture1D | Shader.TextureTarget.Array: + return Target.Texture1DArray; + + case Shader.TextureTarget.Texture2D: + return Target.Texture2D; + + case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Array: + return Target.Texture2DArray; + + case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Multisample: + return Target.Texture2DMultisample; + + case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Multisample | Shader.TextureTarget.Array: + return Target.Texture2DMultisampleArray; + + case Shader.TextureTarget.Texture3D: + return Target.Texture3D; + + case Shader.TextureTarget.TextureCube: + return Target.Cubemap; + + case Shader.TextureTarget.TextureCube | Shader.TextureTarget.Array: + return Target.CubemapArray; + } + + // TODO: Warning. + + return Target.Texture2D; + } + + private void InvalidateTextures(int argument) + { + _textureManager.Flush(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs b/Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs new file mode 100644 index 000000000..368b5a177 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs @@ -0,0 +1,34 @@ +using System; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + struct ShaderAddresses : IEquatable + { + public ulong VertexA; + public ulong Vertex; + public ulong TessControl; + public ulong TessEvaluation; + public ulong Geometry; + public ulong Fragment; + + public override bool Equals(object other) + { + return other is ShaderAddresses addresses && Equals(addresses); + } + + public bool Equals(ShaderAddresses other) + { + return VertexA == other.VertexA && + Vertex == other.Vertex && + TessControl == other.TessControl && + TessEvaluation == other.TessEvaluation && + Geometry == other.Geometry && + Fragment == other.Fragment; + } + + public override int GetHashCode() + { + return HashCode.Combine(VertexA, Vertex, TessControl, TessEvaluation, Geometry, Fragment); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs new file mode 100644 index 000000000..79a84a6d1 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs @@ -0,0 +1,228 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Shader.Translation; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Ryujinx.Graphics.Gpu.Engine +{ + class ShaderCache + { + private const int MaxProgramSize = 0x100000; + + private GpuContext _context; + + private ShaderDumper _dumper; + + private Dictionary _cpPrograms; + + private Dictionary _gpPrograms; + + public ShaderCache(GpuContext context) + { + _context = context; + + _dumper = new ShaderDumper(context); + + _cpPrograms = new Dictionary(); + + _gpPrograms = new Dictionary(); + } + + public ComputeShader GetComputeShader(ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ) + { + if (!_cpPrograms.TryGetValue(gpuVa, out ComputeShader cpShader)) + { + ShaderProgram shader = TranslateComputeShader(gpuVa); + + shader.Replace(DefineNames.LocalSizeX, localSizeX.ToString(CultureInfo.InvariantCulture)); + shader.Replace(DefineNames.LocalSizeY, localSizeY.ToString(CultureInfo.InvariantCulture)); + shader.Replace(DefineNames.LocalSizeZ, localSizeZ.ToString(CultureInfo.InvariantCulture)); + + IShader hostShader = _context.Renderer.CompileShader(shader); + + IProgram program = _context.Renderer.CreateProgram(new IShader[] { hostShader }); + + cpShader = new ComputeShader(program, shader); + + _cpPrograms.Add(gpuVa, cpShader); + } + + return cpShader; + } + + public GraphicsShader GetGraphicsShader(ShaderAddresses addresses) + { + if (!_gpPrograms.TryGetValue(addresses, out GraphicsShader gpShader)) + { + gpShader = new GraphicsShader(); + + if (addresses.VertexA != 0) + { + gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex, addresses.VertexA); + } + else + { + gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex); + } + + gpShader.Shader[1] = TranslateGraphicsShader(addresses.TessControl); + gpShader.Shader[2] = TranslateGraphicsShader(addresses.TessEvaluation); + gpShader.Shader[3] = TranslateGraphicsShader(addresses.Geometry); + gpShader.Shader[4] = TranslateGraphicsShader(addresses.Fragment); + + BackpropQualifiers(gpShader); + + List shaders = new List(); + + for (int stage = 0; stage < gpShader.Shader.Length; stage++) + { + if (gpShader.Shader[stage] == null) + { + continue; + } + + IShader shader = _context.Renderer.CompileShader(gpShader.Shader[stage]); + + shaders.Add(shader); + } + + gpShader.Interface = _context.Renderer.CreateProgram(shaders.ToArray()); + + _gpPrograms.Add(addresses, gpShader); + } + + return gpShader; + } + + private ShaderProgram TranslateComputeShader(ulong gpuVa) + { + if (gpuVa == 0) + { + return null; + } + + ShaderProgram program; + + const TranslationFlags flags = + TranslationFlags.Compute | + TranslationFlags.Unspecialized; + + TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags); + + Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); + + program = Translator.Translate(code, translationConfig); + + _dumper.Dump(gpuVa, compute : true); + + return program; + } + + private ShaderProgram TranslateGraphicsShader(ulong gpuVa, ulong gpuVaA = 0) + { + if (gpuVa == 0) + { + return null; + } + + ShaderProgram program; + + const TranslationFlags flags = + TranslationFlags.DebugMode | + TranslationFlags.Unspecialized; + + TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags); + + if (gpuVaA != 0) + { + Span codeA = _context.MemoryAccessor.Read(gpuVaA, MaxProgramSize); + Span codeB = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); + + program = Translator.Translate(codeA, codeB, translationConfig); + + _dumper.Dump(gpuVaA, compute: false); + _dumper.Dump(gpuVa, compute: false); + } + else + { + Span code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize); + + program = Translator.Translate(code, translationConfig); + + _dumper.Dump(gpuVa, compute: false); + } + + if (program.Stage == ShaderStage.Geometry) + { + PrimitiveType primitiveType = _context.Methods.PrimitiveType; + + string inPrimitive = "points"; + + switch (primitiveType) + { + case PrimitiveType.Points: + inPrimitive = "points"; + break; + case PrimitiveType.Lines: + case PrimitiveType.LineLoop: + case PrimitiveType.LineStrip: + inPrimitive = "lines"; + break; + case PrimitiveType.LinesAdjacency: + case PrimitiveType.LineStripAdjacency: + inPrimitive = "lines_adjacency"; + break; + case PrimitiveType.Triangles: + case PrimitiveType.TriangleStrip: + case PrimitiveType.TriangleFan: + inPrimitive = "triangles"; + break; + case PrimitiveType.TrianglesAdjacency: + case PrimitiveType.TriangleStripAdjacency: + inPrimitive = "triangles_adjacency"; + break; + } + + program.Replace(DefineNames.InputTopologyName, inPrimitive); + } + + return program; + } + + private void BackpropQualifiers(GraphicsShader program) + { + ShaderProgram fragmentShader = program.Shader[4]; + + bool isFirst = true; + + for (int stage = 3; stage >= 0; stage--) + { + if (program.Shader[stage] == null) + { + continue; + } + + // We need to iterate backwards, since we do name replacement, + // and it would otherwise replace a subset of the longer names. + for (int attr = 31; attr >= 0; attr--) + { + string iq = fragmentShader?.Info.InterpolationQualifiers[attr].ToGlslQualifier() ?? string.Empty; + + if (isFirst && iq != string.Empty) + { + program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq); + } + else + { + program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty); + } + } + + isFirst = false; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/ShaderDumper.cs b/Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs similarity index 50% rename from Ryujinx.Graphics/Gal/ShaderDumper.cs rename to Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs index e25127deb..fdcf06121 100644 --- a/Ryujinx.Graphics/Gal/ShaderDumper.cs +++ b/Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs @@ -1,27 +1,43 @@ -using System; using System.IO; -namespace Ryujinx.Graphics.Gal +namespace Ryujinx.Graphics.Gpu.Engine { - static class ShaderDumper + class ShaderDumper { - private static string _runtimeDir; + private const int ShaderHeaderSize = 0x50; - public static int DumpIndex { get; private set; } = 1; + private GpuContext _context; - public static void Dump(IGalMemory memory, long position, GalShaderType type, string extSuffix = "") + private string _runtimeDir; + private string _dumpPath; + private int _dumpIndex; + + public int CurrentDumpIndex => _dumpIndex; + + public ShaderDumper(GpuContext context) { - if (!IsDumpEnabled()) + _context = context; + + _dumpIndex = 1; + } + + public void Dump(ulong gpuVa, bool compute) + { + _dumpPath = GraphicsConfig.ShadersDumpPath; + + if (string.IsNullOrWhiteSpace(_dumpPath)) { return; } - string fileName = "Shader" + DumpIndex.ToString("d4") + "." + ShaderExtension(type) + extSuffix + ".bin"; + string fileName = "Shader" + _dumpIndex.ToString("d4") + ".bin"; string fullPath = Path.Combine(FullDir(), fileName); string codePath = Path.Combine(CodeDir(), fileName); - DumpIndex++; + _dumpIndex++; + + ulong headerSize = compute ? 0UL : ShaderHeaderSize; using (FileStream fullFile = File.Create(fullPath)) using (FileStream codeFile = File.Create(codePath)) @@ -29,25 +45,25 @@ namespace Ryujinx.Graphics.Gal BinaryWriter fullWriter = new BinaryWriter(fullFile); BinaryWriter codeWriter = new BinaryWriter(codeFile); - for (long i = 0; i < 0x50; i += 4) + for (ulong i = 0; i < headerSize; i += 4) { - fullWriter.Write(memory.ReadInt32(position + i)); + fullWriter.Write(_context.MemoryAccessor.ReadInt32(gpuVa + i)); } - long offset = 0; + ulong offset = 0; ulong instruction = 0; - // Dump until a NOP instruction is found + // Dump until a NOP instruction is found. while ((instruction >> 48 & 0xfff8) != 0x50b0) { - uint word0 = (uint)memory.ReadInt32(position + 0x50 + offset + 0); - uint word1 = (uint)memory.ReadInt32(position + 0x50 + offset + 4); + uint word0 = (uint)_context.MemoryAccessor.ReadInt32(gpuVa + headerSize + offset + 0); + uint word1 = (uint)_context.MemoryAccessor.ReadInt32(gpuVa + headerSize + offset + 4); instruction = word0 | (ulong)word1 << 32; // Zero instructions (other kind of NOP) stop immediately, - // this is to avoid two rows of zeroes + // this is to avoid two rows of zeroes. if (instruction == 0) { break; @@ -59,7 +75,7 @@ namespace Ryujinx.Graphics.Gal offset += 8; } - // Align to meet nvdisasm requirements + // Align to meet nvdisasm requirements. while (offset % 0x20 != 0) { fullWriter.Write(0); @@ -70,22 +86,17 @@ namespace Ryujinx.Graphics.Gal } } - public static bool IsDumpEnabled() - { - return !string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath); - } - - private static string FullDir() + private string FullDir() { return CreateAndReturn(Path.Combine(DumpDir(), "Full")); } - private static string CodeDir() + private string CodeDir() { return CreateAndReturn(Path.Combine(DumpDir(), "Code")); } - private static string DumpDir() + private string DumpDir() { if (string.IsNullOrEmpty(_runtimeDir)) { @@ -93,7 +104,7 @@ namespace Ryujinx.Graphics.Gal do { - _runtimeDir = Path.Combine(GraphicsConfig.ShadersDumpPath, "Dumps" + index.ToString("d2")); + _runtimeDir = Path.Combine(_dumpPath, "Dumps" + index.ToString("d2")); index++; } @@ -107,26 +118,9 @@ namespace Ryujinx.Graphics.Gal private static string CreateAndReturn(string dir) { - if (!Directory.Exists(dir)) - { - Directory.CreateDirectory(dir); - } + Directory.CreateDirectory(dir); return dir; } - - private static string ShaderExtension(GalShaderType type) - { - switch (type) - { - case GalShaderType.Vertex: return "vert"; - case GalShaderType.TessControl: return "tesc"; - case GalShaderType.TessEvaluation: return "tese"; - case GalShaderType.Geometry: return "geom"; - case GalShaderType.Fragment: return "frag"; - - default: throw new ArgumentException(nameof(type)); - } - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs new file mode 100644 index 000000000..1e865e6a7 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -0,0 +1,100 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Gpu.Engine; +using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.Graphics.Gpu.State; +using System; + +namespace Ryujinx.Graphics.Gpu +{ + public class GpuContext + { + public IRenderer Renderer { get; } + + internal GpuState State { get; } + + internal IPhysicalMemory PhysicalMemory { get; private set; } + + public MemoryManager MemoryManager { get; } + + internal MemoryAccessor MemoryAccessor { get; } + + internal Methods Methods { get; } + + internal NvGpuFifo Fifo { get; } + + public DmaPusher DmaPusher { get; } + + internal int SequenceNumber { get; private set; } + + private Lazy _caps; + + internal Capabilities Capabilities => _caps.Value; + + public GpuContext(IRenderer renderer) + { + Renderer = renderer; + + State = new GpuState(); + + MemoryManager = new MemoryManager(); + + MemoryAccessor = new MemoryAccessor(this); + + Methods = new Methods(this); + + Fifo = new NvGpuFifo(this); + + DmaPusher = new DmaPusher(this); + + _caps = new Lazy(GetCapabilities); + } + + internal void AdvanceSequence() + { + SequenceNumber++; + } + + public ITexture GetTexture( + ulong address, + int width, + int height, + int stride, + bool isLinear, + int gobBlocksInY, + Format format, + int bytesPerPixel) + { + FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel); + + TextureInfo info = new TextureInfo( + address, + width, + height, + 1, + 1, + 1, + 1, + stride, + isLinear, + gobBlocksInY, + 1, + 1, + Target.Texture2D, + formatInfo); + + return Methods.GetTexture(address)?.HostTexture; + } + + private Capabilities GetCapabilities() + { + return Renderer.GetCapabilities(); + } + + public void SetVmm(IPhysicalMemory mm) + { + PhysicalMemory = mm; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/GraphicsConfig.cs b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs new file mode 100644 index 000000000..c5eaa0b35 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/GraphicsConfig.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gpu +{ + public static class GraphicsConfig + { + public static string ShadersDumpPath; + + public static bool FastGpuTime = true; + + public static bool DisableTUpdate; + public static bool DisableBUpdate; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs new file mode 100644 index 000000000..33ed78818 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs @@ -0,0 +1,62 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Image +{ + class AutoDeleteCache : IEnumerable + { + private const int MaxCapacity = 2048; + + private LinkedList _textures; + + public AutoDeleteCache() + { + _textures = new LinkedList(); + } + + public void Add(Texture texture) + { + texture.IncrementReferenceCount(); + + texture.CacheNode = _textures.AddLast(texture); + + if (_textures.Count > MaxCapacity) + { + Texture oldestTexture = _textures.First.Value; + + _textures.RemoveFirst(); + + oldestTexture.DecrementReferenceCount(); + + oldestTexture.CacheNode = null; + } + } + + public void Lift(Texture texture) + { + if (texture.CacheNode != null) + { + if (texture.CacheNode != _textures.Last) + { + _textures.Remove(texture.CacheNode); + + texture.CacheNode = _textures.AddLast(texture); + } + } + else + { + Add(texture); + } + } + + public IEnumerator GetEnumerator() + { + return _textures.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _textures.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs b/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs new file mode 100644 index 000000000..a728c66e1 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/FormatInfo.cs @@ -0,0 +1,31 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.Image +{ + struct FormatInfo + { + private static FormatInfo _rgba8 = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4); + + public static FormatInfo Default => _rgba8; + + public Format Format { get; } + + public int BlockWidth { get; } + public int BlockHeight { get; } + public int BytesPerPixel { get; } + + public bool IsCompressed => (BlockWidth | BlockHeight) != 1; + + public FormatInfo( + Format format, + int blockWidth, + int blockHeight, + int bytesPerPixel) + { + Format = format; + BlockWidth = blockWidth; + BlockHeight = blockHeight; + BytesPerPixel = bytesPerPixel; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/FormatTable.cs b/Ryujinx.Graphics.Gpu/Image/FormatTable.cs new file mode 100644 index 000000000..9f5b26d4b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/FormatTable.cs @@ -0,0 +1,201 @@ +using Ryujinx.Graphics.GAL; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Image +{ + static class FormatTable + { + private static Dictionary _textureFormats = new Dictionary() + { + { 0x2491d, new FormatInfo(Format.R8Unorm, 1, 1, 1) }, + { 0x1249d, new FormatInfo(Format.R8Snorm, 1, 1, 1) }, + { 0x4921d, new FormatInfo(Format.R8Uint, 1, 1, 1) }, + { 0x36d9d, new FormatInfo(Format.R8Sint, 1, 1, 1) }, + { 0x7ff9b, new FormatInfo(Format.R16Float, 1, 1, 2) }, + { 0x2491b, new FormatInfo(Format.R16Unorm, 1, 1, 2) }, + { 0x1249b, new FormatInfo(Format.R16Snorm, 1, 1, 2) }, + { 0x4921b, new FormatInfo(Format.R16Uint, 1, 1, 2) }, + { 0x36d9b, new FormatInfo(Format.R16Sint, 1, 1, 2) }, + { 0x7ff8f, new FormatInfo(Format.R32Float, 1, 1, 4) }, + { 0x4920f, new FormatInfo(Format.R32Uint, 1, 1, 4) }, + { 0x36d8f, new FormatInfo(Format.R32Sint, 1, 1, 4) }, + { 0x24918, new FormatInfo(Format.R8G8Unorm, 1, 1, 2) }, + { 0x12498, new FormatInfo(Format.R8G8Snorm, 1, 1, 2) }, + { 0x49218, new FormatInfo(Format.R8G8Uint, 1, 1, 2) }, + { 0x36d98, new FormatInfo(Format.R8G8Sint, 1, 1, 2) }, + { 0x7ff8c, new FormatInfo(Format.R16G16Float, 1, 1, 4) }, + { 0x2490c, new FormatInfo(Format.R16G16Unorm, 1, 1, 4) }, + { 0x1248c, new FormatInfo(Format.R16G16Snorm, 1, 1, 4) }, + { 0x4920c, new FormatInfo(Format.R16G16Uint, 1, 1, 4) }, + { 0x36d8c, new FormatInfo(Format.R16G16Sint, 1, 1, 4) }, + { 0x7ff84, new FormatInfo(Format.R32G32Float, 1, 1, 8) }, + { 0x49204, new FormatInfo(Format.R32G32Uint, 1, 1, 8) }, + { 0x36d84, new FormatInfo(Format.R32G32Sint, 1, 1, 8) }, + { 0x7ff82, new FormatInfo(Format.R32G32B32Float, 1, 1, 12) }, + { 0x49202, new FormatInfo(Format.R32G32B32Uint, 1, 1, 12) }, + { 0x36d82, new FormatInfo(Format.R32G32B32Sint, 1, 1, 12) }, + { 0x24908, new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4) }, + { 0x12488, new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4) }, + { 0x49208, new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4) }, + { 0x36d88, new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4) }, + { 0x7ff83, new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8) }, + { 0x24903, new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8) }, + { 0x12483, new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8) }, + { 0x49203, new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8) }, + { 0x36d83, new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8) }, + { 0x7ff81, new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16) }, + { 0x49201, new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16) }, + { 0x36d81, new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16) }, + { 0x2493a, new FormatInfo(Format.D16Unorm, 1, 1, 2) }, + { 0x7ffaf, new FormatInfo(Format.D32Float, 1, 1, 4) }, + { 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4) }, + { 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8) }, + { 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4) }, + { 0x24912, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2) }, + { 0x24914, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2) }, + { 0x24915, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2) }, + { 0x24909, new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4) }, + { 0x49209, new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4) }, + { 0x7ffa1, new FormatInfo(Format.R11G11B10Float, 1, 1, 4) }, + { 0x7ffa0, new FormatInfo(Format.R9G9B9E5Float, 1, 1, 4) }, + { 0x24924, new FormatInfo(Format.Bc1RgbaUnorm, 4, 4, 8) }, + { 0x24925, new FormatInfo(Format.Bc2Unorm, 4, 4, 16) }, + { 0x24926, new FormatInfo(Format.Bc3Unorm, 4, 4, 16) }, + { 0xa4924, new FormatInfo(Format.Bc1RgbaSrgb, 4, 4, 8) }, + { 0xa4925, new FormatInfo(Format.Bc2Srgb, 4, 4, 16) }, + { 0xa4926, new FormatInfo(Format.Bc3Srgb, 4, 4, 16) }, + { 0x24927, new FormatInfo(Format.Bc4Unorm, 4, 4, 8) }, + { 0x124a7, new FormatInfo(Format.Bc4Snorm, 4, 4, 8) }, + { 0x24928, new FormatInfo(Format.Bc5Unorm, 4, 4, 16) }, + { 0x124a8, new FormatInfo(Format.Bc5Snorm, 4, 4, 16) }, + { 0x24917, new FormatInfo(Format.Bc7Unorm, 4, 4, 16) }, + { 0xa4917, new FormatInfo(Format.Bc7Srgb, 4, 4, 16) }, + { 0x7ff90, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16) }, + { 0x7ff91, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16) }, + { 0x24940, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16) }, + { 0x24950, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16) }, + { 0x24941, new FormatInfo(Format.Astc5x5Unorm, 5, 5, 16) }, + { 0x24951, new FormatInfo(Format.Astc6x5Unorm, 6, 5, 16) }, + { 0x24942, new FormatInfo(Format.Astc6x6Unorm, 6, 6, 16) }, + { 0x24955, new FormatInfo(Format.Astc8x5Unorm, 8, 5, 16) }, + { 0x24952, new FormatInfo(Format.Astc8x6Unorm, 8, 6, 16) }, + { 0x24944, new FormatInfo(Format.Astc8x8Unorm, 8, 8, 16) }, + { 0x24956, new FormatInfo(Format.Astc10x5Unorm, 10, 5, 16) }, + { 0x24957, new FormatInfo(Format.Astc10x6Unorm, 10, 6, 16) }, + { 0x24953, new FormatInfo(Format.Astc10x8Unorm, 10, 8, 16) }, + { 0x24945, new FormatInfo(Format.Astc10x10Unorm, 10, 10, 16) }, + { 0x24954, new FormatInfo(Format.Astc12x10Unorm, 12, 10, 16) }, + { 0x24946, new FormatInfo(Format.Astc12x12Unorm, 12, 12, 16) }, + { 0xa4940, new FormatInfo(Format.Astc4x4Srgb, 4, 4, 16) }, + { 0xa4950, new FormatInfo(Format.Astc5x4Srgb, 5, 4, 16) }, + { 0xa4941, new FormatInfo(Format.Astc5x5Srgb, 5, 5, 16) }, + { 0xa4951, new FormatInfo(Format.Astc6x5Srgb, 6, 5, 16) }, + { 0xa4942, new FormatInfo(Format.Astc6x6Srgb, 6, 6, 16) }, + { 0xa4955, new FormatInfo(Format.Astc8x5Srgb, 8, 5, 16) }, + { 0xa4952, new FormatInfo(Format.Astc8x6Srgb, 8, 6, 16) }, + { 0xa4944, new FormatInfo(Format.Astc8x8Srgb, 8, 8, 16) }, + { 0xa4956, new FormatInfo(Format.Astc10x5Srgb, 10, 5, 16) }, + { 0xa4957, new FormatInfo(Format.Astc10x6Srgb, 10, 6, 16) }, + { 0xa4953, new FormatInfo(Format.Astc10x8Srgb, 10, 8, 16) }, + { 0xa4945, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16) }, + { 0xa4954, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16) }, + { 0xa4946, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16) }, + { 0x24913, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2) } + }; + + private static Dictionary _attribFormats = new Dictionary() + { + { 0x13a00000, Format.R8Unorm }, + { 0x0ba00000, Format.R8Snorm }, + { 0x23a00000, Format.R8Uint }, + { 0x1ba00000, Format.R8Sint }, + { 0x3b600000, Format.R16Float }, + { 0x13600000, Format.R16Unorm }, + { 0x0b600000, Format.R16Snorm }, + { 0x23600000, Format.R16Uint }, + { 0x1b600000, Format.R16Sint }, + { 0x3a400000, Format.R32Float }, + { 0x22400000, Format.R32Uint }, + { 0x1a400000, Format.R32Sint }, + { 0x13000000, Format.R8G8Unorm }, + { 0x0b000000, Format.R8G8Snorm }, + { 0x23000000, Format.R8G8Uint }, + { 0x1b000000, Format.R8G8Sint }, + { 0x39e00000, Format.R16G16Float }, + { 0x11e00000, Format.R16G16Unorm }, + { 0x09e00000, Format.R16G16Snorm }, + { 0x21e00000, Format.R16G16Uint }, + { 0x19e00000, Format.R16G16Sint }, + { 0x38800000, Format.R32G32Float }, + { 0x20800000, Format.R32G32Uint }, + { 0x18800000, Format.R32G32Sint }, + { 0x12600000, Format.R8G8B8Unorm }, + { 0x0a600000, Format.R8G8B8Snorm }, + { 0x22600000, Format.R8G8B8Uint }, + { 0x1a600000, Format.R8G8B8Sint }, + { 0x38a00000, Format.R16G16B16Float }, + { 0x10a00000, Format.R16G16B16Unorm }, + { 0x08a00000, Format.R16G16B16Snorm }, + { 0x20a00000, Format.R16G16B16Uint }, + { 0x18a00000, Format.R16G16B16Sint }, + { 0x38400000, Format.R32G32B32Float }, + { 0x20400000, Format.R32G32B32Uint }, + { 0x18400000, Format.R32G32B32Sint }, + { 0x11400000, Format.R8G8B8A8Unorm }, + { 0x09400000, Format.R8G8B8A8Snorm }, + { 0x21400000, Format.R8G8B8A8Uint }, + { 0x19400000, Format.R8G8B8A8Sint }, + { 0x38600000, Format.R16G16B16A16Float }, + { 0x10600000, Format.R16G16B16A16Unorm }, + { 0x08600000, Format.R16G16B16A16Snorm }, + { 0x20600000, Format.R16G16B16A16Uint }, + { 0x18600000, Format.R16G16B16A16Sint }, + { 0x38200000, Format.R32G32B32A32Float }, + { 0x20200000, Format.R32G32B32A32Uint }, + { 0x18200000, Format.R32G32B32A32Sint }, + { 0x16000000, Format.R10G10B10A2Unorm }, + { 0x26000000, Format.R10G10B10A2Uint }, + { 0x3e200000, Format.R11G11B10Float }, + { 0x2ba00000, Format.R8Uscaled }, + { 0x33a00000, Format.R8Sscaled }, + { 0x2b600000, Format.R16Uscaled }, + { 0x33600000, Format.R16Sscaled }, + { 0x2a400000, Format.R32Uscaled }, + { 0x32400000, Format.R32Sscaled }, + { 0x2b000000, Format.R8G8Uscaled }, + { 0x33000000, Format.R8G8Sscaled }, + { 0x29e00000, Format.R16G16Uscaled }, + { 0x31e00000, Format.R16G16Sscaled }, + { 0x28800000, Format.R32G32Uscaled }, + { 0x30800000, Format.R32G32Sscaled }, + { 0x2a600000, Format.R8G8B8Uscaled }, + { 0x32600000, Format.R8G8B8Sscaled }, + { 0x28a00000, Format.R16G16B16Uscaled }, + { 0x30a00000, Format.R16G16B16Sscaled }, + { 0x28400000, Format.R32G32B32Uscaled }, + { 0x30400000, Format.R32G32B32Sscaled }, + { 0x29400000, Format.R8G8B8A8Uscaled }, + { 0x31400000, Format.R8G8B8A8Sscaled }, + { 0x28600000, Format.R16G16B16A16Uscaled }, + { 0x30600000, Format.R16G16B16A16Sscaled }, + { 0x28200000, Format.R32G32B32A32Uscaled }, + { 0x30200000, Format.R32G32B32A32Sscaled }, + { 0x0e000000, Format.R10G10B10A2Snorm }, + { 0x1e000000, Format.R10G10B10A2Sint }, + { 0x2e000000, Format.R10G10B10A2Uscaled }, + { 0x36000000, Format.R10G10B10A2Sscaled } + }; + + public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format) + { + encoded |= (isSrgb ? 1u << 19 : 0u); + + return _textureFormats.TryGetValue(encoded, out format); + } + + public static bool TryGetAttribFormat(uint encoded, out Format format) + { + return _attribFormats.TryGetValue(encoded, out format); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/Pool.cs b/Ryujinx.Graphics.Gpu/Image/Pool.cs new file mode 100644 index 000000000..196fc137e --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/Pool.cs @@ -0,0 +1,99 @@ +using System; + +namespace Ryujinx.Graphics.Gpu.Image +{ + abstract class Pool : IDisposable + { + protected const int DescriptorSize = 0x20; + + protected GpuContext Context; + + protected T[] Items; + + public ulong Address { get; } + public ulong Size { get; } + + public Pool(GpuContext context, ulong address, int maximumId) + { + Context = context; + + int count = maximumId + 1; + + ulong size = (ulong)(uint)count * DescriptorSize;; + + Items = new T[count]; + + Address = address; + Size = size; + } + + public abstract T Get(int id); + + public void SynchronizeMemory() + { + (ulong, ulong)[] modifiedRanges = Context.PhysicalMemory.GetModifiedRanges(Address, Size); + + for (int index = 0; index < modifiedRanges.Length; index++) + { + (ulong mAddress, ulong mSize) = modifiedRanges[index]; + + if (mAddress < Address) + { + mAddress = Address; + } + + ulong maxSize = Address + Size - mAddress; + + if (mSize > maxSize) + { + mSize = maxSize; + } + + InvalidateRangeImpl(mAddress, mSize); + } + } + + public void InvalidateRange(ulong address, ulong size) + { + ulong endAddress = address + size; + + ulong texturePoolEndAddress = Address + Size; + + // If the range being invalidated is not overlapping the texture pool range, + // then we don't have anything to do, exit early. + if (address >= texturePoolEndAddress || endAddress <= Address) + { + return; + } + + if (address < Address) + { + address = Address; + } + + if (endAddress > texturePoolEndAddress) + { + endAddress = texturePoolEndAddress; + } + + InvalidateRangeImpl(address, size); + } + + protected abstract void InvalidateRangeImpl(ulong address, ulong size); + + protected abstract void Delete(T item); + + public void Dispose() + { + if (Items != null) + { + for (int index = 0; index < Items.Length; index++) + { + Delete(Items[index]); + } + + Items = null; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs b/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs new file mode 100644 index 000000000..f14a17f2e --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu.Image +{ + enum ReductionFilter + { + Average, + Minimum, + Maximum + } +} diff --git a/Ryujinx.Graphics.Gpu/Image/Sampler.cs b/Ryujinx.Graphics.Gpu/Image/Sampler.cs new file mode 100644 index 000000000..06fedd8a0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/Sampler.cs @@ -0,0 +1,52 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Color; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.Gpu.Image +{ + class Sampler : IDisposable + { + public ISampler HostSampler { get; } + + public Sampler(GpuContext context, SamplerDescriptor descriptor) + { + MinFilter minFilter = descriptor.UnpackMinFilter(); + MagFilter magFilter = descriptor.UnpackMagFilter(); + + AddressMode addressU = descriptor.UnpackAddressU(); + AddressMode addressV = descriptor.UnpackAddressV(); + AddressMode addressP = descriptor.UnpackAddressP(); + + CompareMode compareMode = descriptor.UnpackCompareMode(); + CompareOp compareOp = descriptor.UnpackCompareOp(); + + ColorF color = new ColorF(0, 0, 0, 0); + + float minLod = descriptor.UnpackMinLod(); + float maxLod = descriptor.UnpackMaxLod(); + float mipLodBias = descriptor.UnpackMipLodBias(); + + float maxAnisotropy = descriptor.UnpackMaxAnisotropy(); + + HostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo( + minFilter, + magFilter, + addressU, + addressV, + addressP, + compareMode, + compareOp, + color, + minLod, + maxLod, + mipLodBias, + maxAnisotropy)); + } + + public void Dispose() + { + HostSampler.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs new file mode 100644 index 000000000..0a43dd1b9 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs @@ -0,0 +1,132 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Sampler; + +namespace Ryujinx.Graphics.Gpu.Image +{ + struct SamplerDescriptor + { + private static readonly float[] _f5ToF32ConversionLut = new float[] + { + 0.0f, + 0.055555556f, + 0.1f, + 0.13636364f, + 0.16666667f, + 0.1923077f, + 0.21428572f, + 0.23333333f, + 0.25f, + 0.2777778f, + 0.3f, + 0.3181818f, + 0.33333334f, + 0.34615386f, + 0.35714287f, + 0.36666667f, + 0.375f, + 0.3888889f, + 0.4f, + 0.4090909f, + 0.41666666f, + 0.42307693f, + 0.42857143f, + 0.43333334f, + 0.4375f, + 0.44444445f, + 0.45f, + 0.45454547f, + 0.45833334f, + 0.46153846f, + 0.4642857f, + 0.46666667f + }; + + private static readonly float[] _maxAnisotropyLut = new float[] + { + 1, 2, 4, 6, 8, 10, 12, 16 + }; + + private const float Frac8ToF32 = 1.0f / 256.0f; + + public uint Word0; + public uint Word1; + public uint Word2; + public uint Word3; + public uint BorderColorR; + public uint BorderColorG; + public uint BorderColorB; + public uint BorderColorA; + + public AddressMode UnpackAddressU() + { + return (AddressMode)(Word0 & 7); + } + + public AddressMode UnpackAddressV() + { + return (AddressMode)((Word0 >> 3) & 7); + } + + public AddressMode UnpackAddressP() + { + return (AddressMode)((Word0 >> 6) & 7); + } + + public CompareMode UnpackCompareMode() + { + return (CompareMode)((Word0 >> 9) & 1); + } + + public CompareOp UnpackCompareOp() + { + return (CompareOp)(((Word0 >> 10) & 7) + 1); + } + + public float UnpackMaxAnisotropy() + { + return _maxAnisotropyLut[(Word0 >> 20) & 7]; + } + + public MagFilter UnpackMagFilter() + { + return (MagFilter)(Word1 & 3); + } + + public MinFilter UnpackMinFilter() + { + int minFilter = (int)(Word1 >> 4) & 3; + int mipFilter = (int)(Word1 >> 6) & 3; + + return (MinFilter)(minFilter + (mipFilter - 1) * 2); + } + + public ReductionFilter UnpackReductionFilter() + { + return (ReductionFilter)((Word1 >> 10) & 3); + } + + public float UnpackMipLodBias() + { + int fixedValue = (int)(Word1 >> 12) & 0x1fff; + + fixedValue = (fixedValue << 19) >> 19; + + return fixedValue * Frac8ToF32; + } + + public float UnpackLodSnap() + { + return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f]; + } + + public float UnpackMinLod() + { + return (Word2 & 0xfff) * Frac8ToF32; + } + + public float UnpackMaxLod() + { + return ((Word2 >> 12) & 0xfff) * Frac8ToF32; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs new file mode 100644 index 000000000..970a09832 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/SamplerPool.cs @@ -0,0 +1,61 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Image +{ + class SamplerPool : Pool + { + public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { } + + public override Sampler Get(int id) + { + if ((uint)id >= Items.Length) + { + return null; + } + + SynchronizeMemory(); + + Sampler sampler = Items[id]; + + if (sampler == null) + { + ulong address = Address + (ulong)(uint)id * DescriptorSize; + + Span data = Context.PhysicalMemory.Read(address, DescriptorSize); + + SamplerDescriptor descriptor = MemoryMarshal.Cast(data)[0]; + + sampler = new Sampler(Context, descriptor); + + Items[id] = sampler; + } + + return sampler; + } + + protected override void InvalidateRangeImpl(ulong address, ulong size) + { + ulong endAddress = address + size; + + for (; address < endAddress; address += DescriptorSize) + { + int id = (int)((address - Address) / DescriptorSize); + + Sampler sampler = Items[id]; + + if (sampler != null) + { + sampler.Dispose(); + + Items[id] = null; + } + } + } + + protected override void Delete(Sampler item) + { + item?.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs new file mode 100644 index 000000000..32db86882 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -0,0 +1,719 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.Graphics.Texture; +using Ryujinx.Graphics.Texture.Astc; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Image +{ + class Texture : IRange + { + private GpuContext _context; + + private TextureInfo _info; + + private SizeInfo _sizeInfo; + + public Format Format => _info.FormatInfo.Format; + + public TextureInfo Info => _info; + + private int _depth; + private int _layers; + private int _firstLayer; + private int _firstLevel; + + private bool _hasData; + + private ITexture _arrayViewTexture; + private Target _arrayViewTarget; + + private Texture _viewStorage; + + private List _views; + + public ITexture HostTexture { get; private set; } + + public LinkedListNode CacheNode { get; set; } + + public bool Modified { get; set; } + + public ulong Address => _info.Address; + public ulong EndAddress => _info.Address + Size; + + public ulong Size => (ulong)_sizeInfo.TotalSize; + + private int _referenceCount; + + private int _sequenceNumber; + + private Texture( + GpuContext context, + TextureInfo info, + SizeInfo sizeInfo, + int firstLayer, + int firstLevel) + { + InitializeTexture(context, info, sizeInfo); + + _firstLayer = firstLayer; + _firstLevel = firstLevel; + + _hasData = true; + } + + public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo) + { + InitializeTexture(context, info, sizeInfo); + + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, context.Capabilities); + + HostTexture = _context.Renderer.CreateTexture(createInfo); + } + + private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo) + { + _context = context; + _sizeInfo = sizeInfo; + + SetInfo(info); + + _viewStorage = this; + + _views = new List(); + } + + public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, int firstLayer, int firstLevel) + { + Texture texture = new Texture( + _context, + info, + sizeInfo, + _firstLayer + firstLayer, + _firstLevel + firstLevel); + + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, _context.Capabilities); + + texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel); + + _viewStorage.AddView(texture); + + return texture; + } + + private void AddView(Texture texture) + { + _views.Add(texture); + + texture._viewStorage = this; + } + + private void RemoveView(Texture texture) + { + _views.Remove(texture); + + texture._viewStorage = null; + } + + public void ChangeSize(int width, int height, int depthOrLayers) + { + width <<= _firstLevel; + height <<= _firstLevel; + + if (_info.Target == Target.Texture3D) + { + depthOrLayers <<= _firstLevel; + } + else + { + depthOrLayers = _viewStorage._info.DepthOrLayers; + } + + _viewStorage.RecreateStorageOrView(width, height, depthOrLayers); + + foreach (Texture view in _viewStorage._views) + { + int viewWidth = Math.Max(1, width >> view._firstLevel); + int viewHeight = Math.Max(1, height >> view._firstLevel); + + int viewDepthOrLayers; + + if (view._info.Target == Target.Texture3D) + { + viewDepthOrLayers = Math.Max(1, depthOrLayers >> view._firstLevel); + } + else + { + viewDepthOrLayers = view._info.DepthOrLayers; + } + + view.RecreateStorageOrView(viewWidth, viewHeight, viewDepthOrLayers); + } + } + + private void RecreateStorageOrView(int width, int height, int depthOrLayers) + { + SetInfo(new TextureInfo( + _info.Address, + width, + height, + depthOrLayers, + _info.Levels, + _info.SamplesInX, + _info.SamplesInY, + _info.Stride, + _info.IsLinear, + _info.GobBlocksInY, + _info.GobBlocksInZ, + _info.GobBlocksInTileX, + _info.Target, + _info.FormatInfo, + _info.DepthStencilMode, + _info.SwizzleR, + _info.SwizzleG, + _info.SwizzleB, + _info.SwizzleA)); + + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(_info, _context.Capabilities); + + if (_viewStorage != this) + { + ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, _firstLayer, _firstLevel)); + } + else + { + ITexture newStorage = _context.Renderer.CreateTexture(createInfo); + + HostTexture.CopyTo(newStorage); + + ReplaceStorage(newStorage); + } + } + + public void SynchronizeMemory() + { + if (_sequenceNumber == _context.SequenceNumber && _hasData) + { + return; + } + + _sequenceNumber = _context.SequenceNumber; + + bool modified = _context.PhysicalMemory.GetModifiedRanges(Address, Size).Length != 0; + + if (!modified && _hasData) + { + return; + } + + ulong pageSize = (uint)_context.PhysicalMemory.GetPageSize(); + + ulong pageMask = pageSize - 1; + + ulong rangeAddress = Address & ~pageMask; + + ulong rangeSize = (EndAddress - Address + pageMask) & ~pageMask; + + _context.Methods.InvalidateRange(rangeAddress, rangeSize); + + Span data = _context.PhysicalMemory.Read(Address, Size); + + if (_info.IsLinear) + { + data = LayoutConverter.ConvertLinearStridedToLinear( + _info.Width, + _info.Height, + _info.FormatInfo.BlockWidth, + _info.FormatInfo.BlockHeight, + _info.Stride, + _info.FormatInfo.BytesPerPixel, + data); + } + else + { + data = LayoutConverter.ConvertBlockLinearToLinear( + _info.Width, + _info.Height, + _depth, + _info.Levels, + _layers, + _info.FormatInfo.BlockWidth, + _info.FormatInfo.BlockHeight, + _info.FormatInfo.BytesPerPixel, + _info.GobBlocksInY, + _info.GobBlocksInZ, + _info.GobBlocksInTileX, + _sizeInfo, + data); + } + + if (!_context.Capabilities.SupportsAstcCompression && _info.FormatInfo.Format.IsAstc()) + { + int blockWidth = _info.FormatInfo.BlockWidth; + int blockHeight = _info.FormatInfo.BlockHeight; + + data = AstcDecoder.DecodeToRgba8( + data, + blockWidth, + blockHeight, + 1, + _info.Width, + _info.Height, + _depth); + } + + HostTexture.SetData(data); + + _hasData = true; + } + + public void Flush() + { + byte[] data = HostTexture.GetData(0); + + _context.PhysicalMemory.Write(Address, data); + } + + public bool IsPerfectMatch(TextureInfo info, TextureSearchFlags flags) + { + if (!FormatMatches(info, (flags & TextureSearchFlags.Strict) != 0)) + { + return false; + } + + if (!LayoutMatches(info)) + { + return false; + } + + if (!SizeMatches(info, (flags & TextureSearchFlags.Strict) == 0)) + { + return false; + } + + if ((flags & TextureSearchFlags.Sampler) != 0) + { + if (!SamplerParamsMatches(info)) + { + return false; + } + } + + if ((flags & TextureSearchFlags.IgnoreMs) != 0) + { + bool msTargetCompatible = _info.Target == Target.Texture2DMultisample && + info.Target == Target.Texture2D; + + if (!msTargetCompatible && !TargetAndSamplesCompatible(info)) + { + return false; + } + } + else if (!TargetAndSamplesCompatible(info)) + { + return false; + } + + return _info.Address == info.Address && _info.Levels == info.Levels; + } + + private bool FormatMatches(TextureInfo info, bool strict) + { + // D32F and R32F texture have the same representation internally, + // however the R32F format is used to sample from depth textures. + if (_info.FormatInfo.Format == Format.D32Float && + info.FormatInfo.Format == Format.R32Float && !strict) + { + return true; + } + + if (_info.FormatInfo.Format == Format.R8G8B8A8Srgb && + info.FormatInfo.Format == Format.R8G8B8A8Unorm && !strict) + { + return true; + } + + if (_info.FormatInfo.Format == Format.R8G8B8A8Unorm && + info.FormatInfo.Format == Format.R8G8B8A8Srgb && !strict) + { + return true; + } + + return _info.FormatInfo.Format == info.FormatInfo.Format; + } + + private bool LayoutMatches(TextureInfo info) + { + if (_info.IsLinear != info.IsLinear) + { + return false; + } + + // For linear textures, gob block sizes are ignored. + // For block linear textures, the stride is ignored. + if (info.IsLinear) + { + return _info.Stride == info.Stride; + } + else + { + return _info.GobBlocksInY == info.GobBlocksInY && + _info.GobBlocksInZ == info.GobBlocksInZ; + } + } + + public bool SizeMatches(TextureInfo info) + { + return SizeMatches(info, alignSizes: false); + } + + public bool SizeMatches(TextureInfo info, int level) + { + return Math.Max(1, _info.Width >> level) == info.Width && + Math.Max(1, _info.Height >> level) == info.Height && + Math.Max(1, _info.GetDepth() >> level) == info.GetDepth(); + } + + private bool SizeMatches(TextureInfo info, bool alignSizes) + { + if (_info.GetLayers() != info.GetLayers()) + { + return false; + } + + if (alignSizes) + { + Size size0 = GetAlignedSize(_info); + Size size1 = GetAlignedSize(info); + + return size0.Width == size1.Width && + size0.Height == size1.Height && + size0.Depth == size1.Depth; + } + else + { + return _info.Width == info.Width && + _info.Height == info.Height && + _info.GetDepth() == info.GetDepth(); + } + } + + private bool SamplerParamsMatches(TextureInfo info) + { + return _info.DepthStencilMode == info.DepthStencilMode && + _info.SwizzleR == info.SwizzleR && + _info.SwizzleG == info.SwizzleG && + _info.SwizzleB == info.SwizzleB && + _info.SwizzleA == info.SwizzleA; + } + + private bool TargetAndSamplesCompatible(TextureInfo info) + { + return _info.Target == info.Target && + _info.SamplesInX == info.SamplesInX && + _info.SamplesInY == info.SamplesInY; + } + + public bool IsViewCompatible(TextureInfo info, ulong size, out int firstLayer, out int firstLevel) + { + // Out of range. + if (info.Address < Address || info.Address + size > EndAddress) + { + firstLayer = 0; + firstLevel = 0; + + return false; + } + + int offset = (int)(info.Address - Address); + + if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel)) + { + return false; + } + + if (!ViewLayoutCompatible(info, firstLevel)) + { + return false; + } + + if (!ViewFormatCompatible(info)) + { + return false; + } + + if (!ViewSizeMatches(info, firstLevel)) + { + return false; + } + + if (!ViewTargetCompatible(info)) + { + return false; + } + + return _info.SamplesInX == info.SamplesInX && + _info.SamplesInY == info.SamplesInY; + } + + private bool ViewLayoutCompatible(TextureInfo info, int level) + { + if (_info.IsLinear != info.IsLinear) + { + return false; + } + + // For linear textures, gob block sizes are ignored. + // For block linear textures, the stride is ignored. + if (info.IsLinear) + { + int width = Math.Max(1, _info.Width >> level); + + int stride = width * _info.FormatInfo.BytesPerPixel; + + stride = BitUtils.AlignUp(stride, 32); + + return stride == info.Stride; + } + else + { + int height = Math.Max(1, _info.Height >> level); + int depth = Math.Max(1, _info.GetDepth() >> level); + + (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( + height, + depth, + _info.FormatInfo.BlockHeight, + _info.GobBlocksInY, + _info.GobBlocksInZ); + + return gobBlocksInY == info.GobBlocksInY && + gobBlocksInZ == info.GobBlocksInZ; + } + } + + private bool ViewFormatCompatible(TextureInfo info) + { + return TextureCompatibility.FormatCompatible(_info.FormatInfo, info.FormatInfo); + } + + private bool ViewSizeMatches(TextureInfo info, int level) + { + Size size = GetAlignedSize(_info, level); + + Size otherSize = GetAlignedSize(info); + + return size.Width == otherSize.Width && + size.Height == otherSize.Height && + size.Depth == otherSize.Depth; + } + + private bool ViewTargetCompatible(TextureInfo info) + { + switch (_info.Target) + { + case Target.Texture1D: + case Target.Texture1DArray: + return info.Target == Target.Texture1D || + info.Target == Target.Texture1DArray; + + case Target.Texture2D: + return info.Target == Target.Texture2D || + info.Target == Target.Texture2DArray; + + case Target.Texture2DArray: + case Target.Cubemap: + case Target.CubemapArray: + return info.Target == Target.Texture2D || + info.Target == Target.Texture2DArray || + info.Target == Target.Cubemap || + info.Target == Target.CubemapArray; + + case Target.Texture2DMultisample: + case Target.Texture2DMultisampleArray: + return info.Target == Target.Texture2DMultisample || + info.Target == Target.Texture2DMultisampleArray; + + case Target.Texture3D: + return info.Target == Target.Texture3D; + } + + return false; + } + + private static Size GetAlignedSize(TextureInfo info, int level = 0) + { + int width = Math.Max(1, info.Width >> level); + int height = Math.Max(1, info.Height >> level); + + if (info.IsLinear) + { + return SizeCalculator.GetLinearAlignedSize( + width, + height, + info.FormatInfo.BlockWidth, + info.FormatInfo.BlockHeight, + info.FormatInfo.BytesPerPixel); + } + else + { + int depth = Math.Max(1, info.GetDepth() >> level); + + (int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes( + height, + depth, + info.FormatInfo.BlockHeight, + info.GobBlocksInY, + info.GobBlocksInZ); + + return SizeCalculator.GetBlockLinearAlignedSize( + width, + height, + depth, + info.FormatInfo.BlockWidth, + info.FormatInfo.BlockHeight, + info.FormatInfo.BytesPerPixel, + gobBlocksInY, + gobBlocksInZ, + info.GobBlocksInTileX); + } + } + + public ITexture GetTargetTexture(Target target) + { + if (target == _info.Target) + { + return HostTexture; + } + + if (_arrayViewTexture == null && IsSameDimensionsTarget(target)) + { + TextureCreateInfo createInfo = new TextureCreateInfo( + _info.Width, + _info.Height, + target == Target.CubemapArray ? 6 : 1, + _info.Levels, + _info.Samples, + _info.FormatInfo.BlockWidth, + _info.FormatInfo.BlockHeight, + _info.FormatInfo.BytesPerPixel, + _info.FormatInfo.Format, + _info.DepthStencilMode, + target, + _info.SwizzleR, + _info.SwizzleG, + _info.SwizzleB, + _info.SwizzleA); + + ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0); + + _arrayViewTexture = viewTexture; + _arrayViewTarget = target; + + return viewTexture; + } + else if (_arrayViewTarget == target) + { + return _arrayViewTexture; + } + + return null; + } + + private bool IsSameDimensionsTarget(Target target) + { + switch (_info.Target) + { + case Target.Texture1D: + case Target.Texture1DArray: + return target == Target.Texture1D || + target == Target.Texture1DArray; + + case Target.Texture2D: + case Target.Texture2DArray: + return target == Target.Texture2D || + target == Target.Texture2DArray; + + case Target.Cubemap: + case Target.CubemapArray: + return target == Target.Cubemap || + target == Target.CubemapArray; + + case Target.Texture2DMultisample: + case Target.Texture2DMultisampleArray: + return target == Target.Texture2DMultisample || + target == Target.Texture2DMultisampleArray; + + case Target.Texture3D: + return target == Target.Texture3D; + } + + return false; + } + + public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture) + { + ReplaceStorage(hostTexture); + + parent._viewStorage.AddView(this); + + SetInfo(info); + } + + private void SetInfo(TextureInfo info) + { + _info = info; + + _depth = info.GetDepth(); + _layers = info.GetLayers(); + } + + private void ReplaceStorage(ITexture hostTexture) + { + DisposeTextures(); + + HostTexture = hostTexture; + } + + public bool OverlapsWith(ulong address, ulong size) + { + return Address < address + size && address < EndAddress; + } + + public void Invalidate() + { + // _hasData = false; + } + + public void IncrementReferenceCount() + { + _referenceCount++; + } + + public void DecrementReferenceCount() + { + if (--_referenceCount == 0) + { + if (_viewStorage != this) + { + _viewStorage.RemoveView(this); + } + + _context.Methods.TextureManager.RemoveTextureFromCache(this); + + DisposeTextures(); + } + } + + private void DisposeTextures() + { + HostTexture.Dispose(); + + _arrayViewTexture?.Dispose(); + _arrayViewTexture = null; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs new file mode 100644 index 000000000..19090ab33 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs @@ -0,0 +1,17 @@ +using Ryujinx.Graphics.GAL.Texture; + +namespace Ryujinx.Graphics.Gpu.Image +{ + struct TextureBindingInfo + { + public Target Target { get; } + + public int Handle { get; } + + public TextureBindingInfo(Target target, int handle) + { + Target = target; + Handle = handle; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs new file mode 100644 index 000000000..524721647 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -0,0 +1,95 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.Image +{ + static class TextureCompatibility + { + private enum FormatClass + { + Unclassified, + BCn64, + BCn128, + Bc1Rgb, + Bc1Rgba, + Bc2, + Bc3, + Bc4, + Bc5, + Bc6, + Bc7 + } + + public static bool FormatCompatible(FormatInfo lhs, FormatInfo rhs) + { + if (IsDsFormat(lhs.Format) || IsDsFormat(rhs.Format)) + { + return lhs.Format == rhs.Format; + } + + if (lhs.Format.IsAstc() || rhs.Format.IsAstc()) + { + return lhs.Format == rhs.Format; + } + + if (lhs.IsCompressed && rhs.IsCompressed) + { + FormatClass lhsClass = GetFormatClass(lhs.Format); + FormatClass rhsClass = GetFormatClass(rhs.Format); + + return lhsClass == rhsClass; + } + else + { + return lhs.BytesPerPixel == rhs.BytesPerPixel; + } + } + + private static FormatClass GetFormatClass(Format format) + { + switch (format) + { + case Format.Bc1RgbSrgb: + case Format.Bc1RgbUnorm: + return FormatClass.Bc1Rgb; + case Format.Bc1RgbaSrgb: + case Format.Bc1RgbaUnorm: + return FormatClass.Bc1Rgba; + case Format.Bc2Srgb: + case Format.Bc2Unorm: + return FormatClass.Bc2; + case Format.Bc3Srgb: + case Format.Bc3Unorm: + return FormatClass.Bc3; + case Format.Bc4Snorm: + case Format.Bc4Unorm: + return FormatClass.Bc4; + case Format.Bc5Snorm: + case Format.Bc5Unorm: + return FormatClass.Bc5; + case Format.Bc6HSfloat: + case Format.Bc6HUfloat: + return FormatClass.Bc6; + case Format.Bc7Srgb: + case Format.Bc7Unorm: + return FormatClass.Bc7; + } + + return FormatClass.Unclassified; + } + + private static bool IsDsFormat(Format format) + { + switch (format) + { + case Format.D16Unorm: + case Format.D24X8Unorm: + case Format.D24UnormS8Uint: + case Format.D32Float: + case Format.D32FloatS8Uint: + return true; + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs b/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs new file mode 100644 index 000000000..37b3f65e2 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureComponent.cs @@ -0,0 +1,35 @@ +using Ryujinx.Graphics.GAL.Texture; + +namespace Ryujinx.Graphics.Gpu.Image +{ + enum TextureComponent + { + Zero = 0, + Red = 2, + Green = 3, + Blue = 4, + Alpha = 5, + OneSI = 6, + OneF = 7 + } + + static class TextureComponentConverter + { + public static SwizzleComponent Convert(this TextureComponent component) + { + switch (component) + { + case TextureComponent.Zero: return SwizzleComponent.Zero; + case TextureComponent.Red: return SwizzleComponent.Red; + case TextureComponent.Green: return SwizzleComponent.Green; + case TextureComponent.Blue: return SwizzleComponent.Blue; + case TextureComponent.Alpha: return SwizzleComponent.Alpha; + case TextureComponent.OneSI: + case TextureComponent.OneF: + return SwizzleComponent.One; + } + + return SwizzleComponent.Zero; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs new file mode 100644 index 000000000..79e4f55ea --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs @@ -0,0 +1,119 @@ +namespace Ryujinx.Graphics.Gpu.Image +{ + struct TextureDescriptor + { + public uint Word0; + public uint Word1; + public uint Word2; + public uint Word3; + public uint Word4; + public uint Word5; + public uint Word6; + public uint Word7; + + public uint UnpackFormat() + { + return Word0 & 0x8007ffff; + } + + public TextureComponent UnpackSwizzleR() + { + return(TextureComponent)((Word0 >> 19) & 7); + } + + public TextureComponent UnpackSwizzleG() + { + return(TextureComponent)((Word0 >> 22) & 7); + } + + public TextureComponent UnpackSwizzleB() + { + return(TextureComponent)((Word0 >> 25) & 7); + } + + public TextureComponent UnpackSwizzleA() + { + return(TextureComponent)((Word0 >> 28) & 7); + } + + public ulong UnpackAddress() + { + return Word1 | ((ulong)(Word2 & 0xffff) << 32); + } + + public TextureDescriptorType UnpackTextureDescriptorType() + { + return (TextureDescriptorType)((Word2 >> 21) & 7); + } + + public int UnpackStride() + { + return (int)(Word3 & 0xffff) << 5; + } + + public int UnpackGobBlocksInX() + { + return 1 << (int)(Word3 & 7); + } + + public int UnpackGobBlocksInY() + { + return 1 << (int)((Word3 >> 3) & 7); + } + + public int UnpackGobBlocksInZ() + { + return 1 << (int)((Word3 >> 6) & 7); + } + + public int UnpackGobBlocksInTileX() + { + return 1 << (int)((Word3 >> 10) & 7); + } + + public int UnpackLevels() + { + return (int)(Word3 >> 28) + 1; + } + + public int UnpackWidth() + { + return (int)(Word4 & 0xffff) + 1; + } + + public bool UnpackSrgb() + { + return (Word4 & (1 << 22)) != 0; + } + + public TextureTarget UnpackTextureTarget() + { + return (TextureTarget)((Word4 >> 23) & 0xf); + } + + public int UnpackHeight() + { + return (int)(Word5 & 0xffff) + 1; + } + + public int UnpackDepth() + { + return (int)((Word5 >> 16) & 0x3fff) + 1; + } + + public int UnpackBaseLevel() + { + return (int)(Word7 & 0xf); + } + + public int UnpackMaxLevelInclusive() + { + return (int)((Word7 >> 4) & 0xf); + } + + public TextureMsaaMode UnpackTextureMsaaMode() + { + return (TextureMsaaMode)((Word7 >> 8) & 0xf); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs b/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs new file mode 100644 index 000000000..6f6048a61 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gpu.Image +{ + enum TextureDescriptorType + { + Buffer, + LinearColorKey, + Linear, + BlockLinear, + BlockLinearColorKey + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs new file mode 100644 index 000000000..639abdd84 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs @@ -0,0 +1,101 @@ +using Ryujinx.Graphics.GAL.Texture; + +namespace Ryujinx.Graphics.Gpu.Image +{ + struct TextureInfo + { + public ulong Address { get; } + + public int Width { get; } + public int Height { get; } + public int DepthOrLayers { get; } + public int Levels { get; } + public int SamplesInX { get; } + public int SamplesInY { get; } + public int Stride { get; } + public bool IsLinear { get; } + public int GobBlocksInY { get; } + public int GobBlocksInZ { get; } + public int GobBlocksInTileX { get; } + + public int Samples => SamplesInX * SamplesInY; + + public Target Target { get; } + + public FormatInfo FormatInfo { get; } + + public DepthStencilMode DepthStencilMode { get; } + + public SwizzleComponent SwizzleR { get; } + public SwizzleComponent SwizzleG { get; } + public SwizzleComponent SwizzleB { get; } + public SwizzleComponent SwizzleA { get; } + + public TextureInfo( + ulong address, + int width, + int height, + int depthOrLayers, + int levels, + int samplesInX, + int samplesInY, + int stride, + bool isLinear, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX, + Target target, + FormatInfo formatInfo, + DepthStencilMode depthStencilMode = DepthStencilMode.Depth, + SwizzleComponent swizzleR = SwizzleComponent.Red, + SwizzleComponent swizzleG = SwizzleComponent.Green, + SwizzleComponent swizzleB = SwizzleComponent.Blue, + SwizzleComponent swizzleA = SwizzleComponent.Alpha) + { + Address = address; + Width = width; + Height = height; + DepthOrLayers = depthOrLayers; + Levels = levels; + SamplesInX = samplesInX; + SamplesInY = samplesInY; + Stride = stride; + IsLinear = isLinear; + GobBlocksInY = gobBlocksInY; + GobBlocksInZ = gobBlocksInZ; + GobBlocksInTileX = gobBlocksInTileX; + Target = target; + FormatInfo = formatInfo; + DepthStencilMode = depthStencilMode; + SwizzleR = swizzleR; + SwizzleG = swizzleG; + SwizzleB = swizzleB; + SwizzleA = swizzleA; + } + + public int GetDepth() + { + return Target == Target.Texture3D ? DepthOrLayers : 1; + } + + public int GetLayers() + { + if (Target == Target.Texture2DArray || Target == Target.Texture2DMultisampleArray) + { + return DepthOrLayers; + } + else if (Target == Target.CubemapArray) + { + return DepthOrLayers * 6; + } + else if (Target == Target.Cubemap) + { + return 6; + } + else + { + return 1; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs new file mode 100644 index 000000000..56dff9ad6 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -0,0 +1,669 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Gpu.Image; +using Ryujinx.Graphics.Gpu.Memory; +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Shader; +using Ryujinx.Graphics.Texture; +using System; + +namespace Ryujinx.Graphics.Gpu.Image +{ + class TextureManager + { + private GpuContext _context; + private BufferManager _bufferManager; + + private SamplerPool _samplerPool; + + private ulong _texturePoolAddress; + private int _texturePoolMaximumId; + + private TexturePoolCache _texturePoolCache; + + private Texture[] _rtColors; + private Texture _rtColor3D; + + private Texture _rtDepthStencil; + + private ITexture[] _rtHostColors; + + private ITexture _rtHostDs; + + private RangeList _textures; + + private AutoDeleteCache _cache; + + private TextureBindingInfo[][] _bindings; + + private struct TextureStatePerStage + { + public ITexture Texture; + public ISampler Sampler; + } + + private TextureStatePerStage[][] _textureState; + + private int _textureBufferIndex; + + public TextureManager(GpuContext context, BufferManager bufferManager) + { + _context = context; + _bufferManager = bufferManager; + + _texturePoolCache = new TexturePoolCache(context, this); + + _rtColors = new Texture[Constants.TotalRenderTargets]; + + _rtHostColors = new ITexture[Constants.TotalRenderTargets]; + + _textures = new RangeList(); + + _cache = new AutoDeleteCache(); + + _bindings = new TextureBindingInfo[Constants.TotalShaderStages][]; + + _textureState = new TextureStatePerStage[Constants.TotalShaderStages][]; + } + + public void BindTextures(int stage, TextureBindingInfo[] bindings) + { + _bindings[stage] = bindings; + + _textureState[stage] = new TextureStatePerStage[bindings.Length]; + } + + public void SetTextureBufferIndex(int index) + { + _textureBufferIndex = index; + } + + public void SetSamplerPool(ulong gpuVa, int maximumId) + { + ulong address = _context.MemoryManager.Translate(gpuVa); + + if (_samplerPool != null) + { + if (_samplerPool.Address == address) + { + return; + } + + _samplerPool.Dispose(); + } + + _samplerPool = new SamplerPool(_context, address, maximumId); + } + + public void SetTexturePool(ulong gpuVa, int maximumId) + { + ulong address = _context.MemoryManager.Translate(gpuVa); + + _texturePoolAddress = address; + _texturePoolMaximumId = maximumId; + } + + public void SetRenderTargetColor(int index, Texture color) + { + _rtColors[index] = color; + + _rtColor3D = null; + } + + public void SetRenderTargetColor3D(Texture color) + { + _rtColor3D = color; + } + + public void SetRenderTargetDepthStencil(Texture depthStencil) + { + _rtDepthStencil = depthStencil; + } + + public void CommitBindings() + { + UpdateTextures(); + UpdateRenderTargets(); + } + + private void UpdateTextures() + { + TexturePool texturePool = _texturePoolCache.FindOrCreate( + _texturePoolAddress, + _texturePoolMaximumId); + + for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++) + { + int stageIndex = (int)stage - 1; + + if (_bindings[stageIndex] == null) + { + continue; + } + + for (int index = 0; index < _bindings[stageIndex].Length; index++) + { + TextureBindingInfo binding = _bindings[stageIndex][index]; + + int packedId = ReadPackedId(stageIndex, binding.Handle); + + int textureId = (packedId >> 0) & 0xfffff; + int samplerId = (packedId >> 20) & 0xfff; + + Texture texture = texturePool.Get(textureId); + + ITexture hostTexture = texture?.GetTargetTexture(binding.Target); + + if (_textureState[stageIndex][index].Texture != hostTexture) + { + _textureState[stageIndex][index].Texture = hostTexture; + + _context.Renderer.GraphicsPipeline.BindTexture(index, stage, hostTexture); + } + + Sampler sampler = _samplerPool.Get(samplerId); + + ISampler hostSampler = sampler?.HostSampler; + + if (_textureState[stageIndex][index].Sampler != hostSampler) + { + _textureState[stageIndex][index].Sampler = hostSampler; + + _context.Renderer.GraphicsPipeline.BindSampler(index, stage, hostSampler); + } + } + } + } + + private void UpdateRenderTargets() + { + bool anyChanged = false; + + if (_rtHostDs != _rtDepthStencil?.HostTexture) + { + _rtHostDs = _rtDepthStencil?.HostTexture; + + anyChanged = true; + } + + if (_rtColor3D == null) + { + for (int index = 0; index < _rtColors.Length; index++) + { + ITexture hostTexture = _rtColors[index]?.HostTexture; + + if (_rtHostColors[index] != hostTexture) + { + _rtHostColors[index] = hostTexture; + + anyChanged = true; + } + } + + if (anyChanged) + { + _context.Renderer.GraphicsPipeline.SetRenderTargets(_rtHostColors, _rtHostDs); + } + } + else + { + if (_rtHostColors[0] != _rtColor3D.HostTexture) + { + _rtHostColors[0] = _rtColor3D.HostTexture; + + anyChanged = true; + } + + if (anyChanged) + { + _context.Renderer.GraphicsPipeline.SetRenderTargets(_rtColor3D.HostTexture, _rtHostDs); + } + } + } + + private int ReadPackedId(int stage, int wordOffset) + { + ulong address = _bufferManager.GetGraphicsUniformBufferAddress(stage, _textureBufferIndex); + + address += (uint)wordOffset * 4; + + return BitConverter.ToInt32(_context.PhysicalMemory.Read(address, 4)); + } + + public Texture FindOrCreateTexture(CopyTexture copyTexture) + { + ulong address = _context.MemoryManager.Translate(copyTexture.Address.Pack()); + + if (address == MemoryManager.BadAddress) + { + return null; + } + + int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY(); + int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ(); + + FormatInfo formatInfo = copyTexture.Format.Convert(); + + TextureInfo info = new TextureInfo( + address, + copyTexture.Width, + copyTexture.Height, + copyTexture.Depth, + 1, + 1, + 1, + copyTexture.Stride, + copyTexture.LinearLayout, + gobBlocksInY, + gobBlocksInZ, + 1, + Target.Texture2D, + formatInfo); + + Texture texture = FindOrCreateTexture(info, TextureSearchFlags.IgnoreMs); + + texture.SynchronizeMemory(); + + return texture; + } + + public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY) + { + ulong address = _context.MemoryManager.Translate(colorState.Address.Pack()); + + if (address == MemoryManager.BadAddress) + { + return null; + } + + bool isLinear = colorState.MemoryLayout.UnpackIsLinear(); + + int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY(); + int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ(); + + Target target; + + if (colorState.MemoryLayout.UnpackIsTarget3D()) + { + target = Target.Texture3D; + } + else if ((samplesInX | samplesInY) != 1) + { + target = colorState.Depth > 1 + ? Target.Texture2DMultisampleArray + : Target.Texture2DMultisample; + } + else + { + target = colorState.Depth > 1 + ? Target.Texture2DArray + : Target.Texture2D; + } + + FormatInfo formatInfo = colorState.Format.Convert(); + + int width, stride; + + // For linear textures, the width value is actually the stride. + // We can easily get the width by dividing the stride by the bpp, + // since the stride is the total number of bytes occupied by a + // line. The stride should also meet alignment constraints however, + // so the width we get here is the aligned width. + if (isLinear) + { + width = colorState.WidthOrStride / formatInfo.BytesPerPixel; + stride = colorState.WidthOrStride; + } + else + { + width = colorState.WidthOrStride; + stride = 0; + } + + TextureInfo info = new TextureInfo( + address, + width, + colorState.Height, + colorState.Depth, + 1, + samplesInX, + samplesInY, + stride, + isLinear, + gobBlocksInY, + gobBlocksInZ, + 1, + target, + formatInfo); + + Texture texture = FindOrCreateTexture(info); + + texture.SynchronizeMemory(); + + return texture; + } + + public Texture FindOrCreateTexture(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY) + { + ulong address = _context.MemoryManager.Translate(dsState.Address.Pack()); + + if (address == MemoryManager.BadAddress) + { + return null; + } + + int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY(); + int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ(); + + Target target = (samplesInX | samplesInY) != 1 + ? Target.Texture2DMultisample + : Target.Texture2D; + + FormatInfo formatInfo = dsState.Format.Convert(); + + TextureInfo info = new TextureInfo( + address, + size.Width, + size.Height, + size.Depth, + 1, + samplesInX, + samplesInY, + 0, + false, + gobBlocksInY, + gobBlocksInZ, + 1, + target, + formatInfo); + + Texture texture = FindOrCreateTexture(info); + + texture.SynchronizeMemory(); + + return texture; + } + + public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None) + { + bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0; + + // Try to find a perfect texture match, with the same address and parameters. + Texture[] sameAddressOverlaps = _textures.FindOverlaps(info.Address); + + foreach (Texture overlap in sameAddressOverlaps) + { + if (overlap.IsPerfectMatch(info, flags)) + { + if (!isSamplerTexture) + { + // If not a sampler texture, it is managed by the auto delete + // cache, ensure that it is on the "top" of the list to avoid + // deletion. + _cache.Lift(overlap); + } + else if (!overlap.SizeMatches(info)) + { + // If this is used for sampling, the size must match, + // otherwise the shader would sample garbage data. + // To fix that, we create a new texture with the correct + // size, and copy the data from the old one to the new one. + overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers); + } + + return overlap; + } + } + + // Calculate texture sizes, used to find all overlapping textures. + SizeInfo sizeInfo; + + if (info.IsLinear) + { + sizeInfo = SizeCalculator.GetLinearTextureSize( + info.Stride, + info.Height, + info.FormatInfo.BlockHeight); + } + else + { + sizeInfo = SizeCalculator.GetBlockLinearTextureSize( + info.Width, + info.Height, + info.GetDepth(), + info.Levels, + info.GetLayers(), + info.FormatInfo.BlockWidth, + info.FormatInfo.BlockHeight, + info.FormatInfo.BytesPerPixel, + info.GobBlocksInY, + info.GobBlocksInZ, + info.GobBlocksInTileX); + } + + // Find view compatible matches. + ulong size = (ulong)sizeInfo.TotalSize; + + Texture[] overlaps = _textures.FindOverlaps(info.Address, size); + + Texture texture = null; + + foreach (Texture overlap in overlaps) + { + if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel)) + { + if (!isSamplerTexture) + { + info = AdjustSizes(overlap, info, firstLevel); + } + + texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel); + + // The size only matters (and is only really reliable) when the + // texture is used on a sampler, because otherwise the size will be + // aligned. + if (!overlap.SizeMatches(info, firstLevel) && isSamplerTexture) + { + texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers); + } + + break; + } + } + + // No match, create a new texture. + if (texture == null) + { + texture = new Texture(_context, info, sizeInfo); + + // We need to synchronize before copying the old view data to the texture, + // otherwise the copied data would be overwritten by a future synchronization. + texture.SynchronizeMemory(); + + foreach (Texture overlap in overlaps) + { + if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel)) + { + TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel); + + TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities); + + ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel); + + overlap.HostTexture.CopyTo(newView); + + overlap.ReplaceView(texture, overlapInfo, newView); + } + } + } + + // Sampler textures are managed by the texture pool, all other textures + // are managed by the auto delete cache. + if (!isSamplerTexture) + { + _cache.Add(texture); + } + + _textures.Add(texture); + + return texture; + } + + private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel) + { + // When the texture is used as view of another texture, we must + // ensure that the sizes are valid, otherwise data uploads would fail + // (and the size wouldn't match the real size used on the host API). + // Given a parent texture from where the view is created, we have the + // following rules: + // - The view size must be equal to the parent size, divided by (2 ^ l), + // where l is the first mipmap level of the view. The division result must + // be rounded down, and the result must be clamped to 1. + // - If the parent format is compressed, and the view format isn't, the + // view size is calculated as above, but the width and height of the + // view must be also divided by the compressed format block width and height. + // - If the parent format is not compressed, and the view is, the view + // size is calculated as described on the first point, but the width and height + // of the view must be also multiplied by the block width and height. + int width = Math.Max(1, parent.Info.Width >> firstLevel); + int height = Math.Max(1, parent.Info.Height >> firstLevel); + + if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed) + { + width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth); + height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight); + } + else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed) + { + width *= info.FormatInfo.BlockWidth; + height *= info.FormatInfo.BlockHeight; + } + + int depthOrLayers; + + if (info.Target == Target.Texture3D) + { + depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel); + } + else + { + depthOrLayers = info.DepthOrLayers; + } + + return new TextureInfo( + info.Address, + width, + height, + depthOrLayers, + info.Levels, + info.SamplesInX, + info.SamplesInY, + info.Stride, + info.IsLinear, + info.GobBlocksInY, + info.GobBlocksInZ, + info.GobBlocksInTileX, + info.Target, + info.FormatInfo, + info.DepthStencilMode, + info.SwizzleR, + info.SwizzleG, + info.SwizzleB, + info.SwizzleA); + } + + public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps) + { + FormatInfo formatInfo = info.FormatInfo; + + if (!caps.SupportsAstcCompression) + { + if (formatInfo.Format.IsAstcUnorm()) + { + formatInfo = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4); + } + else if (formatInfo.Format.IsAstcSrgb()) + { + formatInfo = new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4); + } + } + + int width = info.Width / info.SamplesInX; + int height = info.Height / info.SamplesInY; + + int depth = info.GetDepth() * info.GetLayers(); + + return new TextureCreateInfo( + width, + height, + depth, + info.Levels, + info.Samples, + formatInfo.BlockWidth, + formatInfo.BlockHeight, + formatInfo.BytesPerPixel, + formatInfo.Format, + info.DepthStencilMode, + info.Target, + info.SwizzleR, + info.SwizzleG, + info.SwizzleB, + info.SwizzleA); + } + + public Texture Find2(ulong address) + { + Texture[] ts = _textures.FindOverlaps(address, 1); + + if (ts.Length == 2) + { + return ts[1]; + } + + if (ts.Length == 0) + { + ts = _textures.FindOverlaps(address - 1, 2); + } + + if (ts.Length == 0) + { + return null; + } + + return ts[0]; + } + + public void InvalidateRange(ulong address, ulong size) + { + Texture[] overlaps = _textures.FindOverlaps(address, size); + + foreach (Texture overlap in overlaps) + { + overlap.Invalidate(); + } + + _samplerPool?.InvalidateRange(address, size); + + _texturePoolCache.InvalidateRange(address, size); + } + + public void Flush() + { + foreach (Texture texture in _cache) + { + if (texture.Info.IsLinear && texture.Modified) + { + texture.Flush(); + + texture.Modified = false; + } + } + } + + public void RemoveTextureFromCache(Texture texture) + { + _textures.Remove(texture); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs b/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs new file mode 100644 index 000000000..134210671 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs @@ -0,0 +1,53 @@ +namespace Ryujinx.Graphics.Gpu.Image +{ + enum TextureMsaaMode + { + Ms1x1 = 0, + Ms2x2 = 2, + Ms4x2 = 4, + Ms2x1 = 5, + Ms4x4 = 6 + } + + static class TextureMsaaModeConverter + { + public static int SamplesCount(this TextureMsaaMode msaaMode) + { + switch (msaaMode) + { + case TextureMsaaMode.Ms2x1: return 2; + case TextureMsaaMode.Ms2x2: return 4; + case TextureMsaaMode.Ms4x2: return 8; + case TextureMsaaMode.Ms4x4: return 16; + } + + return 1; + } + + public static int SamplesInX(this TextureMsaaMode msaaMode) + { + switch (msaaMode) + { + case TextureMsaaMode.Ms2x1: return 2; + case TextureMsaaMode.Ms2x2: return 2; + case TextureMsaaMode.Ms4x2: return 4; + case TextureMsaaMode.Ms4x4: return 4; + } + + return 1; + } + + public static int SamplesInY(this TextureMsaaMode msaaMode) + { + switch (msaaMode) + { + case TextureMsaaMode.Ms2x1: return 1; + case TextureMsaaMode.Ms2x2: return 2; + case TextureMsaaMode.Ms4x2: return 2; + case TextureMsaaMode.Ms4x4: return 4; + } + + return 1; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs new file mode 100644 index 000000000..558f4def4 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TexturePool.cs @@ -0,0 +1,219 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Gpu.Memory; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Image +{ + class TexturePool : Pool + { + private TextureManager _textureManager; + + public LinkedListNode CacheNode { get; set; } + + private struct TextureContainer + { + public Texture Texture0 { get; set; } + public Texture Texture1 { get; set; } + } + + public TexturePool( + GpuContext context, + TextureManager textureManager, + ulong address, + int maximumId) : base(context, address, maximumId) + { + _textureManager = textureManager; + } + + public override Texture Get(int id) + { + if ((uint)id >= Items.Length) + { + return null; + } + + SynchronizeMemory(); + + Texture texture = Items[id]; + + if (texture == null) + { + ulong address = Address + (ulong)(uint)id * DescriptorSize; + + Span data = Context.PhysicalMemory.Read(address, DescriptorSize); + + TextureDescriptor descriptor = MemoryMarshal.Cast(data)[0]; + + TextureInfo info = GetInfo(descriptor); + + // Bad address. We can't add a texture with a invalid address + // to the cache. + if (info.Address == MemoryManager.BadAddress) + { + return null; + } + + texture = _textureManager.FindOrCreateTexture(info, TextureSearchFlags.Sampler); + + texture.IncrementReferenceCount(); + + Items[id] = texture; + } + else + { + // Memory is automatically synchronized on texture creation. + texture.SynchronizeMemory(); + } + + return texture; + } + + protected override void InvalidateRangeImpl(ulong address, ulong size) + { + ulong endAddress = address + size; + + for (; address < endAddress; address += DescriptorSize) + { + int id = (int)((address - Address) / DescriptorSize); + + Texture texture = Items[id]; + + if (texture != null) + { + Span data = Context.PhysicalMemory.Read(address, DescriptorSize); + + TextureDescriptor descriptor = MemoryMarshal.Cast(data)[0]; + + // If the descriptors are the same, the texture is the same, + // we don't need to remove as it was not modified. Just continue. + if (texture.IsPerfectMatch(GetInfo(descriptor), TextureSearchFlags.Strict)) + { + continue; + } + + texture.DecrementReferenceCount(); + + Items[id] = null; + } + } + } + + private TextureInfo GetInfo(TextureDescriptor descriptor) + { + ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress()); + + int width = descriptor.UnpackWidth(); + int height = descriptor.UnpackHeight(); + int depthOrLayers = descriptor.UnpackDepth(); + int levels = descriptor.UnpackLevels(); + + TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode(); + + int samplesInX = msaaMode.SamplesInX(); + int samplesInY = msaaMode.SamplesInY(); + + int stride = descriptor.UnpackStride(); + + TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType(); + + bool isLinear = descriptorType == TextureDescriptorType.Linear; + + Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1); + + uint format = descriptor.UnpackFormat(); + bool srgb = descriptor.UnpackSrgb(); + + if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo)) + { + // TODO: Warning. + + formatInfo = FormatInfo.Default; + } + + int gobBlocksInY = descriptor.UnpackGobBlocksInY(); + int gobBlocksInZ = descriptor.UnpackGobBlocksInZ(); + + int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX(); + + SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert(); + SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert(); + SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert(); + SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert(); + + DepthStencilMode depthStencilMode = GetDepthStencilMode( + formatInfo.Format, + swizzleR, + swizzleG, + swizzleB, + swizzleA); + + return new TextureInfo( + address, + width, + height, + depthOrLayers, + levels, + samplesInX, + samplesInY, + stride, + isLinear, + gobBlocksInY, + gobBlocksInZ, + gobBlocksInTileX, + target, + formatInfo, + depthStencilMode, + swizzleR, + swizzleG, + swizzleB, + swizzleA); + } + + private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components) + { + // R = Depth, G = Stencil. + // On 24-bits depth formats, this is inverted (Stencil is R etc). + // NVN setup: + // For depth, A is set to 1.0f, the other components are set to Depth. + // For stencil, all components are set to Stencil. + SwizzleComponent component = components[0]; + + for (int index = 1; index < 4 && !IsRG(component); index++) + { + component = components[index]; + } + + if (!IsRG(component)) + { + return DepthStencilMode.Depth; + } + + if (format == Format.D24X8Unorm || format == Format.D24UnormS8Uint) + { + return component == SwizzleComponent.Red + ? DepthStencilMode.Stencil + : DepthStencilMode.Depth; + } + else + { + return component == SwizzleComponent.Red + ? DepthStencilMode.Depth + : DepthStencilMode.Stencil; + } + } + + private static bool IsRG(SwizzleComponent component) + { + return component == SwizzleComponent.Red || + component == SwizzleComponent.Green; + } + + protected override void Delete(Texture item) + { + item?.DecrementReferenceCount(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs new file mode 100644 index 000000000..8e8313ae3 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Image +{ + class TexturePoolCache + { + private const int MaxCapacity = 4; + + private GpuContext _context; + private TextureManager _textureManager; + + private LinkedList _pools; + + public TexturePoolCache(GpuContext context, TextureManager textureManager) + { + _context = context; + _textureManager = textureManager; + + _pools = new LinkedList(); + } + + public TexturePool FindOrCreate(ulong address, int maximumId) + { + TexturePool pool; + + // First we try to find the pool. + for (LinkedListNode node = _pools.First; node != null; node = node.Next) + { + pool = node.Value; + + if (pool.Address == address) + { + if (pool.CacheNode != _pools.Last) + { + _pools.Remove(pool.CacheNode); + + pool.CacheNode = _pools.AddLast(pool); + } + + return pool; + } + } + + // If not found, create a new one. + pool = new TexturePool(_context, _textureManager, address, maximumId); + + pool.CacheNode = _pools.AddLast(pool); + + if (_pools.Count > MaxCapacity) + { + TexturePool oldestPool = _pools.First.Value; + + _pools.RemoveFirst(); + + oldestPool.Dispose(); + + oldestPool.CacheNode = null; + } + + return pool; + } + + public void InvalidateRange(ulong address, ulong size) + { + for (LinkedListNode node = _pools.First; node != null; node = node.Next) + { + TexturePool pool = node.Value; + + pool.InvalidateRange(address, size); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs new file mode 100644 index 000000000..a5c951b53 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs @@ -0,0 +1,13 @@ +using System; + +namespace Ryujinx.Graphics.Gpu.Image +{ + [Flags] + enum TextureSearchFlags + { + None = 0, + IgnoreMs = 1 << 0, + Strict = 1 << 1 | Sampler, + Sampler = 1 << 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs new file mode 100644 index 000000000..8f5139034 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Image/TextureTarget.cs @@ -0,0 +1,49 @@ +using Ryujinx.Graphics.GAL.Texture; + +namespace Ryujinx.Graphics.Gpu.Image +{ + enum TextureTarget + { + Texture1D, + Texture2D, + Texture3D, + Cubemap, + Texture1DArray, + Texture2DArray, + TextureBuffer, + Texture2DLinear, + CubemapArray + } + + static class TextureTargetConverter + { + public static Target Convert(this TextureTarget target, bool isMultisample) + { + if (isMultisample) + { + switch (target) + { + case TextureTarget.Texture2D: return Target.Texture2DMultisample; + case TextureTarget.Texture2DArray: return Target.Texture2DMultisampleArray; + } + } + else + { + switch (target) + { + case TextureTarget.Texture1D: return Target.Texture1D; + case TextureTarget.Texture2D: return Target.Texture2D; + case TextureTarget.Texture2DLinear: return Target.Texture2D; + case TextureTarget.Texture3D: return Target.Texture3D; + case TextureTarget.Texture1DArray: return Target.Texture1DArray; + case TextureTarget.Texture2DArray: return Target.Texture2DArray; + case TextureTarget.Cubemap: return Target.Cubemap; + case TextureTarget.CubemapArray: return Target.CubemapArray; + case TextureTarget.TextureBuffer: return Target.TextureBuffer; + } + } + + return Target.Texture1D; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs similarity index 92% rename from Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs rename to Ryujinx.Graphics.Gpu/MacroInterpreter.cs index 9a6206fa4..93d5c2ca2 100644 --- a/Ryujinx.Graphics/Graphics3d/MacroInterpreter.cs +++ b/Ryujinx.Graphics.Gpu/MacroInterpreter.cs @@ -1,9 +1,8 @@ using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Memory; using System; using System.Collections.Generic; -namespace Ryujinx.Graphics.Graphics3d +namespace Ryujinx.Graphics.Gpu { class MacroInterpreter { @@ -42,8 +41,9 @@ namespace Ryujinx.Graphics.Graphics3d BitwiseNotAnd = 12 } - private NvGpuFifo _pFifo; - private INvGpuEngine _engine; + private GpuContext _context; + + private NvGpuFifo _pFifo; public Queue Fifo { get; private set; } @@ -60,17 +60,17 @@ namespace Ryujinx.Graphics.Graphics3d private int _pc; - public MacroInterpreter(NvGpuFifo pFifo, INvGpuEngine engine) + public MacroInterpreter(GpuContext context, NvGpuFifo pFifo) { - _pFifo = pFifo; - _engine = engine; + _context = context; + _pFifo = pFifo; Fifo = new Queue(); _gprs = new int[8]; } - public void Execute(NvGpuVmm vmm, int[] mme, int position, int param) + public void Execute(int[] mme, int position, int param) { Reset(); @@ -80,11 +80,11 @@ namespace Ryujinx.Graphics.Graphics3d FetchOpCode(mme); - while (Step(vmm, mme)); + while (Step(mme)); // Due to the delay slot, we still need to execute // one more instruction before we actually exit. - Step(vmm, mme); + Step(mme); } private void Reset() @@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Graphics3d _carry = false; } - private bool Step(NvGpuVmm vmm, int[] mme) + private bool Step(int[] mme) { int baseAddr = _pc - 1; @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.Graphics3d { SetDstGpr(FetchParam()); - Send(vmm, result); + Send(result); break; } @@ -156,7 +156,7 @@ namespace Ryujinx.Graphics.Graphics3d { SetDstGpr(result); - Send(vmm, result); + Send(result); break; } @@ -178,7 +178,7 @@ namespace Ryujinx.Graphics.Graphics3d SetMethAddr(result); - Send(vmm, FetchParam()); + Send(FetchParam()); break; } @@ -190,7 +190,7 @@ namespace Ryujinx.Graphics.Graphics3d SetMethAddr(result); - Send(vmm, (result >> 12) & 0x3f); + Send((result >> 12) & 0x3f); break; } @@ -228,7 +228,6 @@ namespace Ryujinx.Graphics.Graphics3d private void FetchOpCode(int[] mme) { _opCode = _pipeOp; - _pipeOp = mme[_pc++]; } @@ -401,14 +400,14 @@ namespace Ryujinx.Graphics.Graphics3d private int Read(int reg) { - return _engine.Registers[reg]; + return _context.State.Read(reg); } - private void Send(NvGpuVmm vmm, int value) + private void Send(int value) { - GpuMethodCall methCall = new GpuMethodCall(_methAddr, value); + MethodParams meth = new MethodParams(_methAddr, value); - _engine.CallMethod(vmm, methCall); + _context.State.CallMethod(meth); _methAddr += _methIncr; } diff --git a/Ryujinx.Graphics.Gpu/Memory/Buffer.cs b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs new file mode 100644 index 000000000..30bd1ac07 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/Buffer.cs @@ -0,0 +1,99 @@ +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + class Buffer : IRange, IDisposable + { + private GpuContext _context; + + private IBuffer _buffer; + + public ulong Address { get; } + public ulong Size { get; } + + public ulong EndAddress => Address + Size; + + private int[] _sequenceNumbers; + + public Buffer(GpuContext context, ulong address, ulong size) + { + _context = context; + Address = address; + Size = size; + + _buffer = context.Renderer.CreateBuffer((int)size); + + _sequenceNumbers = new int[size / MemoryManager.PageSize]; + + Invalidate(); + } + + public BufferRange GetRange(ulong address, ulong size) + { + int offset = (int)(address - Address); + + return new BufferRange(_buffer, offset, (int)size); + } + + public bool OverlapsWith(ulong address, ulong size) + { + return Address < address + size && address < EndAddress; + } + + public void SynchronizeMemory(ulong address, ulong size) + { + int currentSequenceNumber = _context.SequenceNumber; + + bool needsSync = false; + + ulong buffOffset = address - Address; + + ulong buffEndOffset = (buffOffset + size + MemoryManager.PageMask) & ~MemoryManager.PageMask; + + int startIndex = (int)(buffOffset / MemoryManager.PageSize); + int endIndex = (int)(buffEndOffset / MemoryManager.PageSize); + + for (int index = startIndex; index < endIndex; index++) + { + if (_sequenceNumbers[index] != currentSequenceNumber) + { + _sequenceNumbers[index] = currentSequenceNumber; + + needsSync = true; + } + } + + if (!needsSync) + { + return; + } + + (ulong, ulong)[] modifiedRanges = _context.PhysicalMemory.GetModifiedRanges(address, size); + + for (int index = 0; index < modifiedRanges.Length; index++) + { + (ulong mAddress, ulong mSize) = modifiedRanges[index]; + + int offset = (int)(mAddress - Address); + + _buffer.SetData(offset, _context.PhysicalMemory.Read(mAddress, mSize)); + } + } + + public void CopyTo(Buffer destination, int dstOffset) + { + _buffer.CopyTo(destination._buffer, 0, dstOffset, (int)Size); + } + + public void Invalidate() + { + _buffer.SetData(0, _context.PhysicalMemory.Read(Address, Size)); + } + + public void Dispose() + { + _buffer.Dispose(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs new file mode 100644 index 000000000..2a074bd3d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + struct BufferBounds + { + public ulong Address; + public ulong Size; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs new file mode 100644 index 000000000..eb2e0ca9c --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/BufferManager.cs @@ -0,0 +1,530 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.InputAssembler; +using Ryujinx.Graphics.Gpu.State; +using Ryujinx.Graphics.Shader; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + class BufferManager + { + private const ulong BufferAlignmentSize = 0x1000; + private const ulong BufferAlignmentMask = BufferAlignmentSize - 1; + + private GpuContext _context; + + private RangeList _buffers; + + 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 bool _rebind; + + public BufferManager(GpuContext context) + { + _context = context; + + _buffers = new RangeList(); + + _vertexBuffers = new VertexBuffer[Constants.TotalVertexBuffers]; + + _cpStorageBuffers = new BuffersPerStage(Constants.TotalCpStorageBuffers); + _cpUniformBuffers = new BuffersPerStage(Constants.TotalCpUniformBuffers); + + _gpStorageBuffers = new BuffersPerStage[Constants.TotalShaderStages]; + _gpUniformBuffers = new BuffersPerStage[Constants.TotalShaderStages]; + + for (int index = 0; index < Constants.TotalShaderStages; index++) + { + _gpStorageBuffers[index] = new BuffersPerStage(Constants.TotalGpStorageBuffers); + _gpUniformBuffers[index] = new BuffersPerStage(Constants.TotalGpUniformBuffers); + } + } + + 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; + } + + 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; + } + + public void SetComputeStorageBuffer(int index, ulong gpuVa, ulong size) + { + // TODO: Improve + size += gpuVa & 0x3fUL; + + gpuVa &= ~0x3fUL; + + ulong address = TranslateAndCreateBuffer(gpuVa, size); + + _cpStorageBuffers.Bind(index, address, size); + } + + public void SetGraphicsStorageBuffer(int stage, int index, ulong gpuVa, ulong size) + { + // TODO: Improve + size += gpuVa & 0x3fUL; + + gpuVa &= ~0x3fUL; + + ulong address = TranslateAndCreateBuffer(gpuVa, size); + + _gpStorageBuffers[stage].Bind(index, address, size); + + _gpStorageBuffersDirty = true; + } + + public void SetComputeUniformBuffer(int index, ulong gpuVa, ulong size) + { + ulong address = TranslateAndCreateBuffer(gpuVa, size); + + _cpUniformBuffers.Bind(index, address, size); + } + + public void SetGraphicsUniformBuffer(int stage, int index, ulong gpuVa, ulong size) + { + ulong address = TranslateAndCreateBuffer(gpuVa, size); + + _gpUniformBuffers[stage].Bind(index, address, size); + + _gpUniformBuffersDirty = true; + } + + public void SetComputeStorageBufferEnableMask(uint mask) + { + _cpStorageBuffers.EnableMask = mask; + } + + public void SetGraphicsStorageBufferEnableMask(int stage, uint mask) + { + _gpStorageBuffers[stage].EnableMask = mask; + + _gpStorageBuffersDirty = true; + } + + public void SetComputeUniformBufferEnableMask(uint mask) + { + _cpUniformBuffers.EnableMask = mask; + } + + public void SetGraphicsUniformBufferEnableMask(int stage, uint mask) + { + _gpUniformBuffers[stage].EnableMask = mask; + + _gpUniformBuffersDirty = true; + } + + 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; + } + + private void CreateBuffer(ulong address, ulong size) + { + Buffer[] overlaps = _buffers.FindOverlaps(address, size); + + if (overlaps.Length != 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 (overlaps[0].Address > address || overlaps[0].EndAddress < endAddress) + { + foreach (Buffer buffer in overlaps) + { + 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); + + foreach (Buffer buffer in overlaps) + { + 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); + } + } + + public ulong GetComputeUniformBufferAddress(int index) + { + return _cpUniformBuffers.Buffers[index].Address; + } + + public ulong GetGraphicsUniformBufferAddress(int stage, int index) + { + return _gpUniformBuffers[stage].Buffers[index].Address; + } + + 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.ComputePipeline.SetStorageBuffer(index, 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.ComputePipeline.SetUniformBuffer(index, buffer); + + if (index == 0) + { + // TODO: Improve + Span data = _context.PhysicalMemory.Read(bounds.Address + 0x310, 0x100); + + Span words = MemoryMarshal.Cast(data); + + for (int offset = 0; offset < 0x40; offset += 4) + { + words[offset] &= 0x3f; + } + + buffer = GetBufferRange(bounds.Address + 0x310, 0x100); + + buffer.Buffer.SetData(buffer.Offset, data); + } + } + } + + public void CommitBindings() + { + if (_indexBufferDirty || _rebind) + { + _indexBufferDirty = false; + + if (_indexBuffer.Address != 0) + { + BufferRange buffer = GetBufferRange(_indexBuffer.Address, _indexBuffer.Size); + + _context.Renderer.GraphicsPipeline.BindIndexBuffer(buffer, _indexBuffer.Type); + } + } + else if (_indexBuffer.Address != 0) + { + SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size); + } + + if (_vertexBuffersDirty || _rebind) + { + _vertexBuffersDirty = false; + + VertexBufferDescriptor[] vertexBuffers = new VertexBufferDescriptor[Constants.TotalVertexBuffers]; + + for (int index = 0; index < Constants.TotalVertexBuffers; 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.GraphicsPipeline.BindVertexBuffers(vertexBuffers); + } + else + { + for (int index = 0; index < Constants.TotalVertexBuffers; 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; + } + + private void BindBuffers(BuffersPerStage[] bindings, bool isStorage) + { + BindOrUpdateBuffers(bindings, bind: true, isStorage); + } + + private void UpdateBuffers(BuffersPerStage[] bindings) + { + BindOrUpdateBuffers(bindings, bind: false); + } + + 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); + } + } + } + } + + private void BindBuffer(int index, ShaderStage stage, BufferBounds bounds, bool isStorage) + { + BufferRange buffer = GetBufferRange(bounds.Address, bounds.Size); + + BufferRange[] buffers = new BufferRange[] { buffer }; + + if (isStorage) + { + _context.Renderer.GraphicsPipeline.BindStorageBuffers(index, stage, buffers); + } + else + { + _context.Renderer.GraphicsPipeline.BindUniformBuffers(index, stage, buffers); + } + + if (!isStorage && index == 0) + { + // TODO: Improve + Span data = _context.PhysicalMemory.Read(bounds.Address + 0x110, 0x100); + + Span words = MemoryMarshal.Cast(data); + + for (int offset = 0; offset < 0x40; offset += 4) + { + words[offset] &= 0x3f; + } + + buffer = GetBufferRange(bounds.Address + 0x110, 0x100); + + buffer.Buffer.SetData(buffer.Offset, data); + } + } + + public void CopyBuffer(GpuVa srcVa, GpuVa dstVa, ulong size) + { + ulong srcAddress = TranslateAndCreateBuffer(srcVa.Pack(), size); + ulong dstAddress = TranslateAndCreateBuffer(dstVa.Pack(), size); + + BufferRange srcBuffer = GetBufferRange(srcAddress, size); + BufferRange dstBuffer = GetBufferRange(dstAddress, size); + + srcBuffer.Buffer.CopyTo( + dstBuffer.Buffer, + srcBuffer.Offset, + dstBuffer.Offset, + (int)size); + } + + private BufferRange GetBufferRange(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.GetRange(address, size); + } + + private void SynchronizeBufferRange(ulong address, ulong size) + { + if (size != 0) + { + Buffer buffer = _buffers.FindFirstOverlap(address, size); + + buffer.SynchronizeMemory(address, size); + } + } + + public void InvalidateRange(ulong address, ulong size) + { + Buffer[] overlappingBuffers = _buffers.FindOverlaps(address, size); + + foreach (Buffer buffer in overlappingBuffers) + { + buffer.Invalidate(); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs b/Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs new file mode 100644 index 000000000..5f21704d4 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs @@ -0,0 +1,15 @@ +using System; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + public interface IPhysicalMemory + { + int GetPageSize(); + + Span Read(ulong address, ulong size); + + void Write(ulong address, Span data); + + (ulong, ulong)[] GetModifiedRanges(ulong address, ulong size); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/IRange.cs b/Ryujinx.Graphics.Gpu/Memory/IRange.cs new file mode 100644 index 000000000..ee3d5c0be --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/IRange.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + interface IRange + { + ulong Address { get; } + ulong Size { get; } + + bool OverlapsWith(ulong address, ulong size); + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs b/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs new file mode 100644 index 000000000..ce2a2c744 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs @@ -0,0 +1,12 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + struct IndexBuffer + { + public ulong Address; + public ulong Size; + + public IndexType Type; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs new file mode 100644 index 000000000..500c36e50 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs @@ -0,0 +1,54 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + class MemoryAccessor + { + private GpuContext _context; + + public MemoryAccessor(GpuContext context) + { + _context = context; + } + + public Span Read(ulong gpuVa, ulong maxSize) + { + ulong processVa = _context.MemoryManager.Translate(gpuVa); + + ulong size = Math.Min(_context.MemoryManager.GetSubSize(gpuVa), maxSize); + + return _context.PhysicalMemory.Read(processVa, size); + } + + public T Read(ulong gpuVa) where T : struct + { + ulong processVa = _context.MemoryManager.Translate(gpuVa); + + ulong size = (uint)Marshal.SizeOf(); + + return MemoryMarshal.Cast(_context.PhysicalMemory.Read(processVa, size))[0]; + } + + public int ReadInt32(ulong gpuVa) + { + ulong processVa = _context.MemoryManager.Translate(gpuVa); + + return BitConverter.ToInt32(_context.PhysicalMemory.Read(processVa, 4)); + } + + public void Write(ulong gpuVa, int value) + { + ulong processVa = _context.MemoryManager.Translate(gpuVa); + + _context.PhysicalMemory.Write(processVa, BitConverter.GetBytes(value)); + } + + public void Write(ulong gpuVa, Span data) + { + ulong processVa = _context.MemoryManager.Translate(gpuVa); + + _context.PhysicalMemory.Write(processVa, data); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs new file mode 100644 index 000000000..d1a3e69cd --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -0,0 +1,265 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + public class MemoryManager + { + private const ulong AddressSpaceSize = 1UL << 40; + + public const ulong BadAddress = ulong.MaxValue; + + private const int PtLvl0Bits = 14; + private const int PtLvl1Bits = 14; + private const int PtPageBits = 12; + + private const ulong PtLvl0Size = 1UL << PtLvl0Bits; + private const ulong PtLvl1Size = 1UL << PtLvl1Bits; + public const ulong PageSize = 1UL << PtPageBits; + + private const ulong PtLvl0Mask = PtLvl0Size - 1; + private const ulong PtLvl1Mask = PtLvl1Size - 1; + public const ulong PageMask = PageSize - 1; + + private const int PtLvl0Bit = PtPageBits + PtLvl1Bits; + private const int PtLvl1Bit = PtPageBits; + + private const ulong PteUnmapped = 0xffffffff_ffffffff; + private const ulong PteReserved = 0xffffffff_fffffffe; + + private ulong[][] _pageTable; + + public MemoryManager() + { + _pageTable = new ulong[PtLvl0Size][]; + } + + public ulong Map(ulong pa, ulong va, ulong size) + { + lock (_pageTable) + { + for (ulong offset = 0; offset < size; offset += PageSize) + { + SetPte(va + offset, pa + offset); + } + } + + return va; + } + + public ulong Map(ulong pa, ulong size) + { + lock (_pageTable) + { + ulong va = GetFreePosition(size); + + if (va != PteUnmapped) + { + for (ulong offset = 0; offset < size; offset += PageSize) + { + SetPte(va + offset, pa + offset); + } + } + + return va; + } + } + + public ulong MapLow(ulong pa, ulong size) + { + lock (_pageTable) + { + ulong va = GetFreePosition(size, 1, PageSize); + + if (va != PteUnmapped && va <= uint.MaxValue && (va + size) <= uint.MaxValue) + { + for (ulong offset = 0; offset < size; offset += PageSize) + { + SetPte(va + offset, pa + offset); + } + } + else + { + va = PteUnmapped; + } + + return va; + } + } + + public ulong ReserveFixed(ulong va, ulong size) + { + lock (_pageTable) + { + for (ulong offset = 0; offset < size; offset += PageSize) + { + if (IsPageInUse(va + offset)) + { + return PteUnmapped; + } + } + + for (ulong offset = 0; offset < size; offset += PageSize) + { + SetPte(va + offset, PteReserved); + } + } + + return va; + } + + public ulong Reserve(ulong size, ulong alignment) + { + lock (_pageTable) + { + ulong address = GetFreePosition(size, alignment); + + if (address != PteUnmapped) + { + for (ulong offset = 0; offset < size; offset += PageSize) + { + SetPte(address + offset, PteReserved); + } + } + + return address; + } + } + + public void Free(ulong va, ulong size) + { + lock (_pageTable) + { + for (ulong offset = 0; offset < size; offset += PageSize) + { + SetPte(va + offset, PteUnmapped); + } + } + } + + private ulong GetFreePosition(ulong size, ulong alignment = 1, ulong start = 1UL << 32) + { + // Note: Address 0 is not considered valid by the driver, + // when 0 is returned it's considered a mapping error. + ulong address = start; + ulong freeSize = 0; + + if (alignment == 0) + { + alignment = 1; + } + + alignment = (alignment + PageMask) & ~PageMask; + + while (address + freeSize < AddressSpaceSize) + { + if (!IsPageInUse(address + freeSize)) + { + freeSize += PageSize; + + if (freeSize >= size) + { + return address; + } + } + else + { + address += freeSize + PageSize; + freeSize = 0; + + ulong remainder = address % alignment; + + if (remainder != 0) + { + address = (address - remainder) + alignment; + } + } + } + + return PteUnmapped; + } + + internal ulong GetSubSize(ulong gpuVa) + { + ulong size = 0; + + while (GetPte(gpuVa + size) != PteUnmapped) + { + size += PageSize; + } + + return size; + } + + internal ulong Translate(ulong gpuVa) + { + ulong baseAddress = GetPte(gpuVa); + + if (baseAddress == PteUnmapped || baseAddress == PteReserved) + { + return PteUnmapped; + } + + return baseAddress + (gpuVa & PageMask); + } + + public bool IsRegionFree(ulong va, ulong size) + { + for (ulong offset = 0; offset < size; offset += PageSize) + { + if (IsPageInUse(va + offset)) + { + return false; + } + } + + return true; + } + + private bool IsPageInUse(ulong va) + { + if (va >> PtLvl0Bits + PtLvl1Bits + PtPageBits != 0) + { + return false; + } + + ulong l0 = (va >> PtLvl0Bit) & PtLvl0Mask; + ulong l1 = (va >> PtLvl1Bit) & PtLvl1Mask; + + if (_pageTable[l0] == null) + { + return false; + } + + return _pageTable[l0][l1] != PteUnmapped; + } + + private ulong GetPte(ulong address) + { + ulong l0 = (address >> PtLvl0Bit) & PtLvl0Mask; + ulong l1 = (address >> PtLvl1Bit) & PtLvl1Mask; + + if (_pageTable[l0] == null) + { + return PteUnmapped; + } + + return _pageTable[l0][l1]; + } + + private void SetPte(ulong address, ulong tgtAddr) + { + ulong l0 = (address >> PtLvl0Bit) & PtLvl0Mask; + ulong l1 = (address >> PtLvl1Bit) & PtLvl1Mask; + + if (_pageTable[l0] == null) + { + _pageTable[l0] = new ulong[PtLvl1Size]; + + for (ulong index = 0; index < PtLvl1Size; index++) + { + _pageTable[l0][index] = PteUnmapped; + } + } + + _pageTable[l0][l1] = tgtAddr; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/RangeList.cs b/Ryujinx.Graphics.Gpu/Memory/RangeList.cs new file mode 100644 index 000000000..6114f15df --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/RangeList.cs @@ -0,0 +1,208 @@ +using System.Collections.Generic; + +namespace Ryujinx.Graphics.Gpu.Memory +{ + class RangeList where T : IRange + { + private List _items; + + public RangeList() + { + _items = new List(); + } + + public void Add(T item) + { + lock (_items) + { + int index = BinarySearch(item.Address); + + if (index < 0) + { + index = ~index; + } + + _items.Insert(index, item); + } + } + + public bool Remove(T item) + { + lock (_items) + { + int index = BinarySearch(item.Address); + + if (index >= 0) + { + while (index > 0 && _items[index - 1].Address == item.Address) + { + index--; + } + + while (index < _items.Count) + { + if (_items[index].Equals(item)) + { + _items.RemoveAt(index); + + return true; + } + + if (_items[index].Address > item.Address) + { + break; + } + + index++; + } + } + } + + return false; + } + + public T FindFirstOverlap(T item) + { + return FindFirstOverlap(item.Address, item.Size); + } + + public T FindFirstOverlap(ulong address, ulong size) + { + lock (_items) + { + int index = BinarySearch(address, size); + + if (index < 0) + { + return default(T); + } + + return _items[index]; + } + } + + public T[] FindOverlaps(T item) + { + return FindOverlaps(item.Address, item.Size); + } + + public T[] FindOverlaps(ulong address, ulong size) + { + List overlapsList = new List(); + + ulong endAddress = address + size; + + lock (_items) + { + foreach (T item in _items) + { + if (item.Address >= endAddress) + { + break; + } + + if (item.OverlapsWith(address, size)) + { + overlapsList.Add(item); + } + } + } + + return overlapsList.ToArray(); + } + + public T[] FindOverlaps(ulong address) + { + List overlapsList = new List(); + + lock (_items) + { + int index = BinarySearch(address); + + if (index >= 0) + { + while (index > 0 && _items[index - 1].Address == address) + { + index--; + } + + while (index < _items.Count) + { + T overlap = _items[index++]; + + if (overlap.Address != address) + { + break; + } + + overlapsList.Add(overlap); + } + } + } + + return overlapsList.ToArray(); + } + + private int BinarySearch(ulong address) + { + int left = 0; + int right = _items.Count - 1; + + while (left <= right) + { + int range = right - left; + + int middle = left + (range >> 1); + + T item = _items[middle]; + + if (item.Address == address) + { + return middle; + } + + if (address < item.Address) + { + right = middle - 1; + } + else + { + left = middle + 1; + } + } + + return ~left; + } + + private int BinarySearch(ulong address, ulong size) + { + int left = 0; + int right = _items.Count - 1; + + while (left <= right) + { + int range = right - left; + + int middle = left + (range >> 1); + + T item = _items[middle]; + + if (item.OverlapsWith(address, size)) + { + return middle; + } + + if (address < item.Address) + { + right = middle - 1; + } + else + { + left = middle + 1; + } + } + + return ~left; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs b/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs new file mode 100644 index 000000000..1cb854d63 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gpu.Memory +{ + struct VertexBuffer + { + public ulong Address; + public ulong Size; + public int Stride; + public int Divisor; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/GpuMethodCall.cs b/Ryujinx.Graphics.Gpu/MethodParams.cs similarity index 91% rename from Ryujinx.Graphics/GpuMethodCall.cs rename to Ryujinx.Graphics.Gpu/MethodParams.cs index 4a310b075..fea4eb303 100644 --- a/Ryujinx.Graphics/GpuMethodCall.cs +++ b/Ryujinx.Graphics.Gpu/MethodParams.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics { - struct GpuMethodCall + struct MethodParams { public int Method { get; private set; } public int Argument { get; private set; } @@ -9,7 +9,7 @@ namespace Ryujinx.Graphics public bool IsLastCall => MethodCount <= 1; - public GpuMethodCall( + public MethodParams( int method, int argument, int subChannel = 0, diff --git a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs new file mode 100644 index 000000000..f4deea54d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs @@ -0,0 +1,150 @@ +namespace Ryujinx.Graphics.Gpu +{ + class NvGpuFifo + { + private const int MacrosCount = 0x80; + private const int MacroIndexMask = MacrosCount - 1; + + // Note: The size of the macro memory is unknown, we just make + // a guess here and use 256kb as the size. Increase if needed. + private const int MmeWords = 256 * 256; + + private GpuContext _context; + + private struct CachedMacro + { + public int Position { get; private set; } + + private bool _executionPending; + private int _argument; + + private MacroInterpreter _interpreter; + + public CachedMacro(GpuContext context, NvGpuFifo fifo, int position) + { + Position = position; + + _executionPending = false; + _argument = 0; + + _interpreter = new MacroInterpreter(context, fifo); + } + + public void StartExecution(int argument) + { + _argument = argument; + + _executionPending = true; + } + + public void Execute(int[] mme) + { + if (_executionPending) + { + _executionPending = false; + + _interpreter?.Execute(mme, Position, _argument); + } + } + + public void PushArgument(int argument) + { + _interpreter?.Fifo.Enqueue(argument); + } + } + + private int _currMacroPosition; + private int _currMacroBindIndex; + + private CachedMacro[] _macros; + + private int[] _mme; + + private ClassId[] _subChannels; + + public NvGpuFifo(GpuContext context) + { + _context = context; + + _macros = new CachedMacro[MacrosCount]; + + _mme = new int[MmeWords]; + + _subChannels = new ClassId[8]; + } + + public void CallMethod(MethodParams meth) + { + if ((NvGpuFifoMeth)meth.Method == NvGpuFifoMeth.BindChannel) + { + _subChannels[meth.SubChannel] = (ClassId)meth.Argument; + } + else if (meth.Method < 0x60) + { + switch ((NvGpuFifoMeth)meth.Method) + { + case NvGpuFifoMeth.WaitForIdle: + { + _context.Renderer.FlushPipelines(); + + break; + } + + case NvGpuFifoMeth.SetMacroUploadAddress: + { + _currMacroPosition = meth.Argument; + + break; + } + + case NvGpuFifoMeth.SendMacroCodeData: + { + _mme[_currMacroPosition++] = meth.Argument; + + break; + } + + case NvGpuFifoMeth.SetMacroBindingIndex: + { + _currMacroBindIndex = meth.Argument; + + break; + } + + case NvGpuFifoMeth.BindMacro: + { + int position = meth.Argument; + + _macros[_currMacroBindIndex++] = new CachedMacro(_context, this, position); + + break; + } + } + } + else if (meth.Method < 0xe00) + { + _context.State.CallMethod(meth); + } + else + { + int macroIndex = (meth.Method >> 1) & MacroIndexMask; + + if ((meth.Method & 1) != 0) + { + _macros[macroIndex].PushArgument(meth.Argument); + } + else + { + _macros[macroIndex].StartExecution(meth.Argument); + } + + if (meth.IsLastCall) + { + _macros[macroIndex].Execute(_mme); + + _context.Methods.PerformDeferredDraws(); + } + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuFifoMeth.cs b/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs similarity index 76% rename from Ryujinx.Graphics/Graphics3d/NvGpuFifoMeth.cs rename to Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs index 9bf528b3f..09aaa6c3b 100644 --- a/Ryujinx.Graphics/Graphics3d/NvGpuFifoMeth.cs +++ b/Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs @@ -1,8 +1,9 @@ -namespace Ryujinx.Graphics.Graphics3d +namespace Ryujinx.Graphics.Gpu { enum NvGpuFifoMeth { BindChannel = 0, + WaitForIdle = 0x44, SetMacroUploadAddress = 0x45, SendMacroCodeData = 0x46, SetMacroBindingIndex = 0x47, diff --git a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj new file mode 100644 index 000000000..88761ddff --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj @@ -0,0 +1,14 @@ + + + + + + + + + + + netcoreapp3.0 + + + diff --git a/Ryujinx.Graphics.Gpu/State/BlendState.cs b/Ryujinx.Graphics.Gpu/State/BlendState.cs new file mode 100644 index 000000000..cf22dc1b0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/BlendState.cs @@ -0,0 +1,15 @@ +using Ryujinx.Graphics.GAL.Blend; + +namespace Ryujinx.Graphics.Gpu.State +{ + struct BlendState + { + public Bool SeparateAlpha; + public BlendOp ColorOp; + public BlendFactor ColorSrcFactor; + public BlendFactor ColorDstFactor; + public BlendOp AlphaOp; + public BlendFactor AlphaSrcFactor; + public BlendFactor AlphaDstFactor; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/Bool.cs b/Ryujinx.Graphics.Gpu/State/Bool.cs new file mode 100644 index 000000000..8aadcfcc9 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/Bool.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct Bool + { + private uint _value; + + public bool IsTrue() + { + return (_value & 1) != 0; + } + + public bool IsFalse() + { + return (_value & 1) == 0; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/ClearColors.cs b/Ryujinx.Graphics.Gpu/State/ClearColors.cs new file mode 100644 index 000000000..584c4791d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ClearColors.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct ClearColors + { + public float Red; + public float Green; + public float Blue; + public float Alpha; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/Condition.cs b/Ryujinx.Graphics.Gpu/State/Condition.cs new file mode 100644 index 000000000..41cce5197 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/Condition.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + enum Condition + { + Never, + Always, + ResultNonZero, + Equal, + NotEqual + } +} diff --git a/Ryujinx.Graphics.Gpu/State/ConditionState.cs b/Ryujinx.Graphics.Gpu/State/ConditionState.cs new file mode 100644 index 000000000..24302c607 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ConditionState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct ConditionState + { + public GpuVa Address; + public Condition Condition; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/CopyBufferParams.cs b/Ryujinx.Graphics.Gpu/State/CopyBufferParams.cs new file mode 100644 index 000000000..2876dedd6 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/CopyBufferParams.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct CopyBufferParams + { + public GpuVa SrcAddress; + public GpuVa DstAddress; + public int SrcStride; + public int DstStride; + public int XCount; + public int YCount; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs b/Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs new file mode 100644 index 000000000..24071372e --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct CopyBufferSwizzle + { + public uint Swizzle; + + public int UnpackComponentSize() + { + return (int)((Swizzle >> 16) & 3) + 1; + } + + public int UnpackSrcComponentsCount() + { + return (int)((Swizzle >> 20) & 7) + 1; + } + + public int UnpackDstComponentsCount() + { + return (int)((Swizzle >> 24) & 7) + 1; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs b/Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs new file mode 100644 index 000000000..cb63c607d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct CopyBufferTexture + { + public MemoryLayout MemoryLayout; + public int Width; + public int Height; + public int Depth; + public int RegionZ; + public ushort RegionX; + public ushort RegionY; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/CopyRegion.cs b/Ryujinx.Graphics.Gpu/State/CopyRegion.cs new file mode 100644 index 000000000..cad9a5db8 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/CopyRegion.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct CopyRegion + { + public int DstX; + public int DstY; + public int DstWidth; + public int DstHeight; + public long SrcWidthRF; + public long SrcHeightRF; + public long SrcXF; + public long SrcYF; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/CopyTexture.cs b/Ryujinx.Graphics.Gpu/State/CopyTexture.cs new file mode 100644 index 000000000..363d84a2b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/CopyTexture.cs @@ -0,0 +1,15 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct CopyTexture + { + public RtFormat Format; + public bool LinearLayout; + public MemoryLayout MemoryLayout; + public int Depth; + public int Layer; + public int Stride; + public int Width; + public int Height; + public GpuVa Address; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/CopyTextureControl.cs b/Ryujinx.Graphics.Gpu/State/CopyTextureControl.cs new file mode 100644 index 000000000..c49cf2765 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/CopyTextureControl.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct CopyTextureControl + { + public uint Packed; + + public bool UnpackLinearFilter() + { + return (Packed & (1u << 4)) != 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/DepthBiasState.cs b/Ryujinx.Graphics.Gpu/State/DepthBiasState.cs new file mode 100644 index 000000000..c88d27dda --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/DepthBiasState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct DepthBiasState + { + public Bool PointEnable; + public Bool LineEnable; + public Bool FillEnable; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/FaceState.cs b/Ryujinx.Graphics.Gpu/State/FaceState.cs new file mode 100644 index 000000000..53763032b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/FaceState.cs @@ -0,0 +1,11 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.State +{ + struct FaceState + { + public Bool CullEnable; + public FrontFace FrontFace; + public Face CullFace; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/GpuState.cs b/Ryujinx.Graphics.Gpu/State/GpuState.cs new file mode 100644 index 000000000..4e4241ca8 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/GpuState.cs @@ -0,0 +1,425 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.Gpu.State +{ + class GpuState + { + private const int RegistersCount = 0xe00; + + public delegate void MethodCallback(int argument); + + private int[] _backingMemory; + + private struct Register + { + public MethodCallback Callback; + + public StateWriteFlags WriteFlag; + } + + private Register[] _registers; + + public StateWriteFlags StateWriteFlags { get; set; } + + public GpuState() + { + _backingMemory = new int[RegistersCount]; + + _registers = new Register[RegistersCount]; + + StateWriteFlags = StateWriteFlags.Any; + + InitializeDefaultState(); + InitializeStateWatchers(); + } + + public bool ExitEarly; + + public void CallMethod(MethodParams meth) + { + if (ExitEarly) + { + return; + } + + Register register = _registers[meth.Method]; + + if (_backingMemory[meth.Method] != meth.Argument) + { + StateWriteFlags |= register.WriteFlag; + } + + _backingMemory[meth.Method] = meth.Argument; + + MethodCallback callback = register.Callback; + + if (callback != null) + { + callback(meth.Argument); + } + } + + public int Read(int offset) + { + return _backingMemory[offset]; + } + + public void RegisterCopyBufferCallback(MethodCallback callback) + { + RegisterCallback(0xc0, callback); + } + + public void RegisterCopyTextureCallback(MethodCallback callback) + { + RegisterCallback(0x237, callback); + } + + public void RegisterDrawEndCallback(MethodCallback callback) + { + RegisterCallback(0x585, callback); + } + + public void RegisterDrawBeginCallback(MethodCallback callback) + { + RegisterCallback(0x586, callback); + } + + public void RegisterSetIndexCountCallback(MethodCallback callback) + { + RegisterCallback(0x5f8, callback); + } + + public void RegisterClearCallback(MethodCallback callback) + { + RegisterCallback(0x674, callback); + } + + public void RegisterReportCallback(MethodCallback callback) + { + RegisterCallback(0x6c3, callback); + } + + public void RegisterUniformBufferUpdateCallback(MethodCallback callback) + { + for (int index = 0; index < 16; index++) + { + RegisterCallback(0x8e4 + index, callback); + } + } + + public void RegisterUniformBufferBind0Callback(MethodCallback callback) + { + RegisterCallback(0x904, callback); + } + + public void RegisterUniformBufferBind1Callback(MethodCallback callback) + { + RegisterCallback(0x90c, callback); + } + + public void RegisterUniformBufferBind2Callback(MethodCallback callback) + { + RegisterCallback(0x914, callback); + } + + public void RegisterUniformBufferBind3Callback(MethodCallback callback) + { + RegisterCallback(0x91c, callback); + } + + public void RegisterUniformBufferBind4Callback(MethodCallback callback) + { + RegisterCallback(0x924, callback); + } + + public CopyTexture GetCopyDstTexture() + { + return Get(MethodOffset.CopyDstTexture); + } + + public CopyTexture GetCopySrcTexture() + { + return Get(MethodOffset.CopySrcTexture); + } + + public RtColorState GetRtColorState(int index) + { + return Get(MethodOffset.RtColorState + 16 * index); + } + + public CopyTextureControl GetCopyTextureControl() + { + return Get(MethodOffset.CopyTextureControl); + } + + public CopyRegion GetCopyRegion() + { + return Get(MethodOffset.CopyRegion); + } + + public ViewportTransform GetViewportTransform(int index) + { + return Get(MethodOffset.ViewportTransform + 8 * index); + } + + public ViewportExtents GetViewportExtents(int index) + { + return Get(MethodOffset.ViewportExtents + 4 * index); + } + + public VertexBufferDrawState GetVertexBufferDrawState() + { + return Get(MethodOffset.VertexBufferDrawState); + } + + public ClearColors GetClearColors() + { + return Get(MethodOffset.ClearColors); + } + + public float GetClearDepthValue() + { + return Get(MethodOffset.ClearDepthValue); + } + + public int GetClearStencilValue() + { + return _backingMemory[(int)MethodOffset.ClearStencilValue]; + } + + public StencilBackMasks GetStencilBackMasks() + { + return Get(MethodOffset.StencilBackMasks); + } + + public RtDepthStencilState GetRtDepthStencilState() + { + return Get(MethodOffset.RtDepthStencilState); + } + + public VertexAttribState GetVertexAttribState(int index) + { + return Get(MethodOffset.VertexAttribState + index); + } + + public Size3D GetRtDepthStencilSize() + { + return Get(MethodOffset.RtDepthStencilSize); + } + + public Bool GetDepthTestEnable() + { + return Get(MethodOffset.DepthTestEnable); + } + + public CompareOp GetDepthTestFunc() + { + return Get(MethodOffset.DepthTestFunc); + } + + public Bool GetDepthWriteEnable() + { + return Get(MethodOffset.DepthWriteEnable); + } + + public Bool GetBlendEnable(int index) + { + return Get(MethodOffset.BlendEnable + index); + } + + public StencilTestState GetStencilTestState() + { + return Get(MethodOffset.StencilTestState); + } + + public int GetBaseVertex() + { + return _backingMemory[(int)MethodOffset.FirstVertex]; + } + + public int GetBaseInstance() + { + return _backingMemory[(int)MethodOffset.FirstInstance]; + } + + public PoolState GetSamplerPoolState() + { + return Get(MethodOffset.SamplerPoolState); + } + + public PoolState GetTexturePoolState() + { + return Get(MethodOffset.TexturePoolState); + } + + public StencilBackTestState GetStencilBackTestState() + { + return Get(MethodOffset.StencilBackTestState); + } + + public TextureMsaaMode GetRtMsaaMode() + { + return Get(MethodOffset.RtMsaaMode); + } + + public GpuVa GetShaderBaseAddress() + { + return Get(MethodOffset.ShaderBaseAddress); + } + + public PrimitiveRestartState GetPrimitiveRestartState() + { + return Get(MethodOffset.PrimitiveRestartState); + } + + public IndexBufferState GetIndexBufferState() + { + return Get(MethodOffset.IndexBufferState); + } + + public FaceState GetFaceState() + { + return Get(MethodOffset.FaceState); + } + + public ReportState GetReportState() + { + return Get(MethodOffset.ReportState); + } + + public VertexBufferState GetVertexBufferState(int index) + { + return Get(MethodOffset.VertexBufferState + 4 * index); + } + + public BlendState GetBlendState(int index) + { + return Get(MethodOffset.BlendState + 8 * index); + } + + public GpuVa GetVertexBufferEndAddress(int index) + { + return Get(MethodOffset.VertexBufferEndAddress + 2 * index); + } + + public ShaderState GetShaderState(int index) + { + return Get(MethodOffset.ShaderState + 16 * index); + } + + public UniformBufferState GetUniformBufferState() + { + return Get(MethodOffset.UniformBufferState); + } + + public void SetUniformBufferOffset(int offset) + { + _backingMemory[(int)MethodOffset.UniformBufferState + 3] = offset; + } + + public int GetTextureBufferIndex() + { + return _backingMemory[(int)MethodOffset.TextureBufferIndex]; + } + + private void InitializeDefaultState() + { + // Depth ranges. + for (int index = 0; index < 8; index++) + { + _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 2] = 0; + _backingMemory[(int)MethodOffset.ViewportExtents + index * 4 + 3] = 0x3F800000; + } + + // Default front stencil mask. + _backingMemory[0x4e7] = 0xff; + + // Default color mask. + _backingMemory[(int)MethodOffset.RtColorMask] = 0x1111; + } + + private void InitializeStateWatchers() + { + SetWriteStateFlag(MethodOffset.RtColorState, StateWriteFlags.RtColorState, 16 * 8); + + SetWriteStateFlag(MethodOffset.ViewportTransform, StateWriteFlags.ViewportTransform, 8 * 8); + SetWriteStateFlag(MethodOffset.ViewportExtents, StateWriteFlags.ViewportTransform, 4 * 8); + + SetWriteStateFlag(MethodOffset.VertexBufferDrawState, StateWriteFlags.VertexBufferState); + + SetWriteStateFlag(MethodOffset.DepthBiasState, StateWriteFlags.DepthBiasState); + + SetWriteStateFlag(MethodOffset.DepthBiasFactor, StateWriteFlags.DepthBiasState, 1); + SetWriteStateFlag(MethodOffset.DepthBiasUnits, StateWriteFlags.DepthBiasState, 1); + SetWriteStateFlag(MethodOffset.DepthBiasClamp, StateWriteFlags.DepthBiasState, 1); + + SetWriteStateFlag(MethodOffset.RtDepthStencilState, StateWriteFlags.RtDepthStencilState); + SetWriteStateFlag (MethodOffset.RtDepthStencilSize, StateWriteFlags.RtDepthStencilState); + + SetWriteStateFlag(MethodOffset.DepthTestEnable, StateWriteFlags.DepthTestState, 1); + SetWriteStateFlag(MethodOffset.DepthWriteEnable, StateWriteFlags.DepthTestState, 1); + SetWriteStateFlag(MethodOffset.DepthTestFunc, StateWriteFlags.DepthTestState, 1); + + SetWriteStateFlag(MethodOffset.VertexAttribState, StateWriteFlags.VertexAttribState, 16); + + SetWriteStateFlag (MethodOffset.StencilBackMasks, StateWriteFlags.StencilTestState); + SetWriteStateFlag (MethodOffset.StencilTestState, StateWriteFlags.StencilTestState); + SetWriteStateFlag(MethodOffset.StencilBackTestState, StateWriteFlags.StencilTestState); + + SetWriteStateFlag(MethodOffset.SamplerPoolState, StateWriteFlags.SamplerPoolState); + SetWriteStateFlag(MethodOffset.TexturePoolState, StateWriteFlags.TexturePoolState); + + SetWriteStateFlag(MethodOffset.ShaderBaseAddress, StateWriteFlags.ShaderState); + + SetWriteStateFlag(MethodOffset.PrimitiveRestartState, StateWriteFlags.PrimitiveRestartState); + + SetWriteStateFlag(MethodOffset.IndexBufferState, StateWriteFlags.IndexBufferState); + + SetWriteStateFlag(MethodOffset.FaceState, StateWriteFlags.FaceState); + + SetWriteStateFlag(MethodOffset.RtColorMask, StateWriteFlags.RtColorMask); + + SetWriteStateFlag(MethodOffset.VertexBufferInstanced, StateWriteFlags.VertexBufferState, 16); + SetWriteStateFlag(MethodOffset.VertexBufferState, StateWriteFlags.VertexBufferState, 4 * 16); + SetWriteStateFlag(MethodOffset.VertexBufferEndAddress, StateWriteFlags.VertexBufferState, 2 * 16); + + SetWriteStateFlag(MethodOffset.BlendEnable, StateWriteFlags.BlendState, 8); + SetWriteStateFlag(MethodOffset.BlendState, StateWriteFlags.BlendState, 8 * 8); + + SetWriteStateFlag(MethodOffset.ShaderState, StateWriteFlags.ShaderState, 16 * 6); + + SetWriteStateFlag(MethodOffset.TextureBufferIndex, StateWriteFlags.TexturePoolState, 1); + } + + private void SetWriteStateFlag(MethodOffset offset, StateWriteFlags flag) + { + SetWriteStateFlag(offset, flag, Marshal.SizeOf()); + } + + private void SetWriteStateFlag(MethodOffset offset, StateWriteFlags flag, int size) + { + for (int index = 0; index < size; index++) + { + _registers[(int)offset + index].WriteFlag = flag; + } + } + + public void RegisterCallback(MethodOffset offset, MethodCallback callback) + { + _registers[(int)offset].Callback = callback; + } + + private void RegisterCallback(int offset, MethodCallback callback) + { + _registers[offset].Callback = callback; + } + + public T Get(MethodOffset offset) where T : struct + { + return MemoryMarshal.Cast(_backingMemory.AsSpan().Slice((int)offset))[0]; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/GpuVa.cs b/Ryujinx.Graphics.Gpu/State/GpuVa.cs new file mode 100644 index 000000000..01ad70b7c --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/GpuVa.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct GpuVa + { + public uint High; + public uint Low; + + public ulong Pack() + { + return Low | ((ulong)High << 32); + } + + public bool IsNullPtr() + { + return (Low | High) == 0; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/IndexBufferState.cs b/Ryujinx.Graphics.Gpu/State/IndexBufferState.cs new file mode 100644 index 000000000..8a07bb520 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/IndexBufferState.cs @@ -0,0 +1,13 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.State +{ + struct IndexBufferState + { + public GpuVa Address; + public GpuVa EndAddress; + public IndexType Type; + public int First; + public int Count; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs b/Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs new file mode 100644 index 000000000..10d249002 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct Inline2MemoryParams + { + public int LineLengthIn; + public int LineCount; + public GpuVa DstAddress; + public int DstStride; + public MemoryLayout DstMemoryLayout; + public int DstWidth; + public int DstHeight; + public int DstDepth; + public int DstZ; + public int DstX; + public int DstY; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/MemoryLayout.cs b/Ryujinx.Graphics.Gpu/State/MemoryLayout.cs new file mode 100644 index 000000000..8e53a36a1 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/MemoryLayout.cs @@ -0,0 +1,32 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct MemoryLayout + { + public uint Packed; + + public int UnpackGobBlocksInX() + { + return 1 << (int)(Packed & 0xf); + } + + public int UnpackGobBlocksInY() + { + return 1 << (int)((Packed >> 4) & 0xf); + } + + public int UnpackGobBlocksInZ() + { + return 1 << (int)((Packed >> 8) & 0xf); + } + + public bool UnpackIsLinear() + { + return (Packed & 0x1000) != 0; + } + + public bool UnpackIsTarget3D() + { + return (Packed & 0x10000) != 0; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs new file mode 100644 index 000000000..91dffe9f3 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs @@ -0,0 +1,62 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + enum MethodOffset + { + Inline2MemoryParams = 0x60, + Inline2MemoryExecute = 0x6c, + Inline2MemoryPushData = 0x6d, + CopyDstTexture = 0x80, + CopySrcTexture = 0x8c, + DispatchParamsAddress = 0xad, + Dispatch = 0xaf, + CopyBufferParams = 0x100, + CopyBufferSwizzle = 0x1c2, + CopyBufferDstTexture = 0x1c3, + CopyBufferSrcTexture = 0x1ca, + RtColorState = 0x200, + CopyTextureControl = 0x223, + CopyRegion = 0x22c, + ViewportTransform = 0x280, + ViewportExtents = 0x300, + VertexBufferDrawState = 0x35d, + ClearColors = 0x360, + ClearDepthValue = 0x364, + ClearStencilValue = 0x368, + DepthBiasState = 0x370, + StencilBackMasks = 0x3d5, + InvalidateTextures = 0x3dd, + RtDepthStencilState = 0x3f8, + VertexAttribState = 0x458, + RtDepthStencilSize = 0x48a, + DepthTestEnable = 0x4b3, + DepthWriteEnable = 0x4ba, + DepthTestFunc = 0x4c3, + BlendEnable = 0x4d8, + StencilTestState = 0x4e0, + FirstVertex = 0x50d, + FirstInstance = 0x50e, + ResetCounter = 0x54c, + RtDepthStencilEnable = 0x54e, + ConditionState = 0x554, + SamplerPoolState = 0x557, + DepthBiasFactor = 0x55b, + TexturePoolState = 0x55d, + StencilBackTestState = 0x565, + DepthBiasUnits = 0x56f, + RtMsaaMode = 0x574, + ShaderBaseAddress = 0x582, + PrimitiveRestartState = 0x591, + IndexBufferState = 0x5f2, + DepthBiasClamp = 0x61f, + VertexBufferInstanced = 0x620, + FaceState = 0x646, + RtColorMask = 0x680, + ReportState = 0x6c0, + VertexBufferState = 0x700, + BlendState = 0x780, + VertexBufferEndAddress = 0x7c0, + ShaderState = 0x800, + UniformBufferState = 0x8e0, + TextureBufferIndex = 0x982 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/PoolState.cs b/Ryujinx.Graphics.Gpu/State/PoolState.cs new file mode 100644 index 000000000..3d51eb7e5 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/PoolState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct PoolState + { + public GpuVa Address; + public int MaximumId; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs b/Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs new file mode 100644 index 000000000..21405be78 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct PrimitiveRestartState + { + public bool Enable; + public int Index; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/PrimitiveTopology.cs b/Ryujinx.Graphics.Gpu/State/PrimitiveTopology.cs new file mode 100644 index 000000000..02df9ac16 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/PrimitiveTopology.cs @@ -0,0 +1,50 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.State +{ + enum PrimitiveType + { + Points, + Lines, + LineLoop, + LineStrip, + Triangles, + TriangleStrip, + TriangleFan, + Quads, + QuadStrip, + Polygon, + LinesAdjacency, + LineStripAdjacency, + TrianglesAdjacency, + TriangleStripAdjacency, + Patches + } + + static class PrimitiveTypeConverter + { + public static PrimitiveTopology Convert(this PrimitiveType topology) + { + switch (topology) + { + case PrimitiveType.Points: return PrimitiveTopology.Points; + case PrimitiveType.Lines: return PrimitiveTopology.Lines; + case PrimitiveType.LineLoop: return PrimitiveTopology.LineLoop; + case PrimitiveType.LineStrip: return PrimitiveTopology.LineStrip; + case PrimitiveType.Triangles: return PrimitiveTopology.Triangles; + case PrimitiveType.TriangleStrip: return PrimitiveTopology.TriangleStrip; + case PrimitiveType.TriangleFan: return PrimitiveTopology.TriangleFan; + case PrimitiveType.Quads: return PrimitiveTopology.Quads; + case PrimitiveType.QuadStrip: return PrimitiveTopology.QuadStrip; + case PrimitiveType.Polygon: return PrimitiveTopology.Polygon; + case PrimitiveType.LinesAdjacency: return PrimitiveTopology.LinesAdjacency; + case PrimitiveType.LineStripAdjacency: return PrimitiveTopology.LineStripAdjacency; + case PrimitiveType.TrianglesAdjacency: return PrimitiveTopology.TrianglesAdjacency; + case PrimitiveType.TriangleStripAdjacency: return PrimitiveTopology.TriangleStripAdjacency; + case PrimitiveType.Patches: return PrimitiveTopology.Patches; + } + + return PrimitiveTopology.Triangles; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/ReportCounterType.cs b/Ryujinx.Graphics.Gpu/State/ReportCounterType.cs new file mode 100644 index 000000000..38a0de711 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ReportCounterType.cs @@ -0,0 +1,25 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + enum ReportCounterType + { + Zero = 0, + InputVertices = 1, + InputPrimitives = 3, + VertexShaderInvocations = 5, + GeometryShaderInvocations = 7, + GeometryShaderPrimitives = 9, + TransformFeedbackPrimitivesWritten = 0xb, + ClipperInputPrimitives = 0xf, + ClipperOutputPrimitives = 0x11, + PrimitivesGenerated = 0x12, + FragmentShaderInvocations = 0x13, + SamplesPassed = 0x15, + TessControlShaderInvocations = 0x1b, + TessEvaluationShaderInvocations = 0x1d, + TessEvaluationShaderPrimitives = 0x1f, + ZcullStats0 = 0x2a, + ZcullStats1 = 0x2c, + ZcullStats2 = 0x2e, + ZcullStats3 = 0x30 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/ReportMode.cs b/Ryujinx.Graphics.Gpu/State/ReportMode.cs new file mode 100644 index 000000000..84af3d75f --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ReportMode.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + enum ReportMode + { + Semaphore = 0, + Counter = 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/ReportState.cs b/Ryujinx.Graphics.Gpu/State/ReportState.cs new file mode 100644 index 000000000..212281aa2 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ReportState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct ReportState + { + public GpuVa Address; + public int Payload; + public uint Control; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/ResetCounterType.cs b/Ryujinx.Graphics.Gpu/State/ResetCounterType.cs new file mode 100644 index 000000000..49b3b6da0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ResetCounterType.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + enum ResetCounterType + { + SamplesPassed = 1, + ZcullStats = 2, + TransformFeedbackPrimitivesWritten = 0x10, + InputVertices = 0x12, + InputPrimitives = 0x13, + VertexShaderInvocations = 0x15, + TessControlShaderInvocations = 0x16, + TessEvaluationShaderInvocations = 0x17, + TessEvaluationShaderPrimitives = 0x18, + GeometryShaderInvocations = 0x1a, + GeometryShaderPrimitives = 0x1b, + ClipperInputPrimitives = 0x1c, + ClipperOutputPrimitives = 0x1d, + FragmentShaderInvocations = 0x1e, + PrimitivesGenerated = 0x1f + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/RtColorMask.cs b/Ryujinx.Graphics.Gpu/State/RtColorMask.cs new file mode 100644 index 000000000..5992673f6 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/RtColorMask.cs @@ -0,0 +1,27 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct RtColorMask + { + public uint Packed; + + public bool UnpackRed() + { + return (Packed & 0x1) != 0; + } + + public bool UnpackGreen() + { + return (Packed & 0x10) != 0; + } + + public bool UnpackBlue() + { + return (Packed & 0x100) != 0; + } + + public bool UnpackAlpha() + { + return (Packed & 0x1000) != 0; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/RtColorState.cs b/Ryujinx.Graphics.Gpu/State/RtColorState.cs new file mode 100644 index 000000000..bb6ae208d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/RtColorState.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct RtColorState + { + public GpuVa Address; + public int WidthOrStride; + public int Height; + public RtFormat Format; + public MemoryLayout MemoryLayout; + public int Depth; + public int LayerSize; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs b/Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs new file mode 100644 index 000000000..bfa812cb9 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct RtDepthStencilState + { + public GpuVa Address; + public RtFormat Format; + public MemoryLayout MemoryLayout; + public int LayerSize; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/RtFormat.cs b/Ryujinx.Graphics.Gpu/State/RtFormat.cs new file mode 100644 index 000000000..960da445e --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/RtFormat.cs @@ -0,0 +1,137 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu.Image; + +namespace Ryujinx.Graphics.Gpu.State +{ + enum RtFormat + { + D32Float = 0xa, + D16Unorm = 0x13, + D24UnormS8Uint = 0x14, + S8Uint = 0x17, + D32FloatS8Uint = 0x19, + R32G32B32A32Float = 0xc0, + R32G32B32A32Sint = 0xc1, + R32G32B32A32Uint = 0xc2, + R32G32B32X32Float = 0xc3, + R32G32B32X32Sint = 0xc4, + R32G32B32X32Uint = 0xc5, + R16G16B16X16Unorm = 0xc6, + R16G16B16X16Snorm = 0xc7, + R16G16B16X16Sint = 0xc8, + R16G16B16X16Uint = 0xc9, + R16G16B16A16Float = 0xca, + R32G32Float = 0xcb, + R32G32Sint = 0xcc, + R32G32Uint = 0xcd, + R16G16B16X16Float = 0xce, + B8G8R8A8Unorm = 0xcf, + B8G8R8A8Srgb = 0xd0, + R10G10B10A2Unorm = 0xd1, + R10G10B10A2Uint = 0xd2, + R8G8B8A8Unorm = 0xd5, + R8G8B8A8Srgb = 0xd6, + R8G8B8X8Snorm = 0xd7, + R8G8B8X8Sint = 0xd8, + R8G8B8X8Uint = 0xd9, + R16G16Unorm = 0xda, + R16G16Snorm = 0xdb, + R16G16Sint = 0xdc, + R16G16Uint = 0xdd, + R16G16Float = 0xde, + R11G11B10Float = 0xe0, + R32Sint = 0xe3, + R32Uint = 0xe4, + R32Float = 0xe5, + B8G8R8X8Unorm = 0xe6, + B8G8R8X8Srgb = 0xe7, + B5G6R5Unorm = 0xe8, + B5G5R5A1Unorm = 0xe9, + R8G8Unorm = 0xea, + R8G8Snorm = 0xeb, + R8G8Sint = 0xec, + R8G8Uint = 0xed, + R16Unorm = 0xee, + R16Snorm = 0xef, + R16Sint = 0xf0, + R16Uint = 0xf1, + R16Float = 0xf2, + R8Unorm = 0xf3, + R8Snorm = 0xf4, + R8Sint = 0xf5, + R8Uint = 0xf6, + B5G5R5X1Unorm = 0xf8, + R8G8B8X8Unorm = 0xf9, + R8G8B8X8Srgb = 0xfa + } + + static class RtFormatConverter + { + public static FormatInfo Convert(this RtFormat format) + { + switch (format) + { + case RtFormat.D32Float: return new FormatInfo(Format.D32Float, 1, 1, 4); + case RtFormat.D16Unorm: return new FormatInfo(Format.D16Unorm, 1, 1, 2); + case RtFormat.D24UnormS8Uint: return new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4); + case RtFormat.S8Uint: return new FormatInfo(Format.S8Uint, 1, 1, 1); + case RtFormat.D32FloatS8Uint: return new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8); + case RtFormat.R32G32B32A32Float: return new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16); + case RtFormat.R32G32B32A32Sint: return new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16); + case RtFormat.R32G32B32A32Uint: return new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16); + case RtFormat.R32G32B32X32Float: return new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16); + case RtFormat.R32G32B32X32Sint: return new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16); + case RtFormat.R32G32B32X32Uint: return new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16); + case RtFormat.R16G16B16X16Unorm: return new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8); + case RtFormat.R16G16B16X16Snorm: return new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8); + case RtFormat.R16G16B16X16Sint: return new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8); + case RtFormat.R16G16B16X16Uint: return new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8); + case RtFormat.R16G16B16A16Float: return new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8); + case RtFormat.R32G32Float: return new FormatInfo(Format.R32G32Float, 1, 1, 8); + case RtFormat.R32G32Sint: return new FormatInfo(Format.R32G32Sint, 1, 1, 8); + case RtFormat.R32G32Uint: return new FormatInfo(Format.R32G32Uint, 1, 1, 8); + case RtFormat.R16G16B16X16Float: return new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8); + case RtFormat.B8G8R8A8Unorm: return new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4); + case RtFormat.B8G8R8A8Srgb: return new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4); + case RtFormat.R10G10B10A2Unorm: return new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4); + case RtFormat.R10G10B10A2Uint: return new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4); + case RtFormat.R8G8B8A8Unorm: return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4); + case RtFormat.R8G8B8A8Srgb: return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4); + case RtFormat.R8G8B8X8Snorm: return new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4); + case RtFormat.R8G8B8X8Sint: return new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4); + case RtFormat.R8G8B8X8Uint: return new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4); + case RtFormat.R16G16Unorm: return new FormatInfo(Format.R16G16Unorm, 1, 1, 4); + case RtFormat.R16G16Snorm: return new FormatInfo(Format.R16G16Snorm, 1, 1, 4); + case RtFormat.R16G16Sint: return new FormatInfo(Format.R16G16Sint, 1, 1, 4); + case RtFormat.R16G16Uint: return new FormatInfo(Format.R16G16Uint, 1, 1, 4); + case RtFormat.R16G16Float: return new FormatInfo(Format.R16G16Float, 1, 1, 4); + case RtFormat.R11G11B10Float: return new FormatInfo(Format.R11G11B10Float, 1, 1, 4); + case RtFormat.R32Sint: return new FormatInfo(Format.R32Sint, 1, 1, 4); + case RtFormat.R32Uint: return new FormatInfo(Format.R32Uint, 1, 1, 4); + case RtFormat.R32Float: return new FormatInfo(Format.R32Float, 1, 1, 4); + case RtFormat.B8G8R8X8Unorm: return new FormatInfo(Format.B8G8R8A8Unorm, 1, 1, 4); + case RtFormat.B8G8R8X8Srgb: return new FormatInfo(Format.B8G8R8A8Srgb, 1, 1, 4); + case RtFormat.B5G6R5Unorm: return new FormatInfo(Format.B5G6R5Unorm, 1, 1, 2); + case RtFormat.B5G5R5A1Unorm: return new FormatInfo(Format.B5G5R5A1Unorm, 1, 1, 2); + case RtFormat.R8G8Unorm: return new FormatInfo(Format.R8G8Unorm, 1, 1, 2); + case RtFormat.R8G8Snorm: return new FormatInfo(Format.R8G8Snorm, 1, 1, 2); + case RtFormat.R8G8Sint: return new FormatInfo(Format.R8G8Sint, 1, 1, 2); + case RtFormat.R8G8Uint: return new FormatInfo(Format.R8G8Uint, 1, 1, 2); + case RtFormat.R16Unorm: return new FormatInfo(Format.R16Unorm, 1, 1, 2); + case RtFormat.R16Snorm: return new FormatInfo(Format.R16Snorm, 1, 1, 2); + case RtFormat.R16Sint: return new FormatInfo(Format.R16Sint, 1, 1, 2); + case RtFormat.R16Uint: return new FormatInfo(Format.R16Uint, 1, 1, 2); + case RtFormat.R16Float: return new FormatInfo(Format.R16Float, 1, 1, 2); + case RtFormat.R8Unorm: return new FormatInfo(Format.R8Unorm, 1, 1, 1); + case RtFormat.R8Snorm: return new FormatInfo(Format.R8Snorm, 1, 1, 1); + case RtFormat.R8Sint: return new FormatInfo(Format.R8Sint, 1, 1, 1); + case RtFormat.R8Uint: return new FormatInfo(Format.R8Uint, 1, 1, 1); + case RtFormat.B5G5R5X1Unorm: return new FormatInfo(Format.B5G5R5X1Unorm, 1, 1, 2); + case RtFormat.R8G8B8X8Unorm: return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4); + case RtFormat.R8G8B8X8Srgb: return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4); + } + + return FormatInfo.Default; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/State/ShaderState.cs b/Ryujinx.Graphics.Gpu/State/ShaderState.cs new file mode 100644 index 000000000..536d7dcfd --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ShaderState.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct ShaderState + { + public uint Control; + public uint Offset; + public uint Unknown0x8; + public int MaxRegisters; + public ShaderType Type; + public uint Unknown0x14; + public uint Unknown0x18; + public uint Unknown0x1c; + + public bool UnpackEnable() + { + return (Control & 1) != 0; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/ShaderType.cs b/Ryujinx.Graphics.Gpu/State/ShaderType.cs new file mode 100644 index 000000000..703159f4a --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ShaderType.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + enum ShaderType + { + Vertex, + TessellationControl, + TessellationEvaluation, + Geometry, + Fragment + } +} diff --git a/Ryujinx.Graphics.Gpu/State/Size3D.cs b/Ryujinx.Graphics.Gpu/State/Size3D.cs new file mode 100644 index 000000000..827713897 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/Size3D.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct Size3D + { + public int Width; + public int Height; + public int Depth; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/StateWriteFlags.cs b/Ryujinx.Graphics.Gpu/State/StateWriteFlags.cs new file mode 100644 index 000000000..32d5127a2 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/StateWriteFlags.cs @@ -0,0 +1,34 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + enum StateWriteFlags + { + InputAssemblerGroup = + VertexAttribState | + PrimitiveRestartState | + IndexBufferState | + VertexBufferState, + + RenderTargetGroup = + RtColorState | + RtDepthStencilState, + + RtColorState = 1 << 0, + ViewportTransform = 1 << 1, + DepthBiasState = 1 << 2, + RtDepthStencilState = 1 << 3, + DepthTestState = 1 << 4, + VertexAttribState = 1 << 5, + StencilTestState = 1 << 6, + SamplerPoolState = 1 << 7, + TexturePoolState = 1 << 8, + PrimitiveRestartState = 1 << 9, + IndexBufferState = 1 << 10, + FaceState = 1 << 11, + RtColorMask = 1 << 12, + VertexBufferState = 1 << 13, + BlendState = 1 << 14, + ShaderState = 1 << 15, + + Any = -1 + } +} diff --git a/Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs b/Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs new file mode 100644 index 000000000..d3779f874 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct StencilBackMasks + { + public int FuncRef; + public int Mask; + public int FuncMask; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs b/Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs new file mode 100644 index 000000000..18b31eaa0 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.DepthStencil; + +namespace Ryujinx.Graphics.Gpu.State +{ + struct StencilBackTestState + { + public Bool TwoSided; + public StencilOp BackSFail; + public StencilOp BackDpFail; + public StencilOp BackDpPass; + public CompareOp BackFunc; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/StencilTestState.cs b/Ryujinx.Graphics.Gpu/State/StencilTestState.cs new file mode 100644 index 000000000..b60f002fc --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/StencilTestState.cs @@ -0,0 +1,17 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.DepthStencil; + +namespace Ryujinx.Graphics.Gpu.State +{ + struct StencilTestState + { + public Bool Enable; + public StencilOp FrontSFail; + public StencilOp FrontDpFail; + public StencilOp FrontDpPass; + public CompareOp FrontFunc; + public int FrontFuncRef; + public int FrontFuncMask; + public int FrontMask; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/UniformBufferState.cs b/Ryujinx.Graphics.Gpu/State/UniformBufferState.cs new file mode 100644 index 000000000..80e4b6bc5 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/UniformBufferState.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct UniformBufferState + { + public int Size; + public GpuVa Address; + public int Offset; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/VertexAttribState.cs b/Ryujinx.Graphics.Gpu/State/VertexAttribState.cs new file mode 100644 index 000000000..0e327a75c --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/VertexAttribState.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct VertexAttribState + { + public uint Attribute; + + public int UnpackBufferIndex() + { + return (int)(Attribute & 0x1f); + } + + public int UnpackOffset() + { + return (int)((Attribute >> 7) & 0x3fff); + } + + public uint UnpackFormat() + { + return Attribute & 0x3fe00000; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs b/Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs new file mode 100644 index 000000000..c2f3b7bbd --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs @@ -0,0 +1,8 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct VertexBufferDrawState + { + public int First; + public int Count; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/VertexBufferState.cs b/Ryujinx.Graphics.Gpu/State/VertexBufferState.cs new file mode 100644 index 000000000..a8bd2c925 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/VertexBufferState.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct VertexBufferState + { + public uint Control; + public GpuVa Address; + public int Divisor; + + public int UnpackStride() + { + return (int)(Control & 0xfff); + } + + public bool UnpackEnable() + { + return (Control & (1 << 12)) != 0; + } + } +} diff --git a/Ryujinx.Graphics.Gpu/State/ViewportExtents.cs b/Ryujinx.Graphics.Gpu/State/ViewportExtents.cs new file mode 100644 index 000000000..4be2db90d --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ViewportExtents.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Gpu.State +{ + struct ViewportExtents + { + public ushort X; + public ushort Width; + public ushort Y; + public ushort Height; + public float DepthNear; + public float DepthFar; + } +} diff --git a/Ryujinx.Graphics.Gpu/State/ViewportTransform.cs b/Ryujinx.Graphics.Gpu/State/ViewportTransform.cs new file mode 100644 index 000000000..335b039c8 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/State/ViewportTransform.cs @@ -0,0 +1,36 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.Gpu.State +{ + struct ViewportTransform + { + public float ScaleX; + public float ScaleY; + public float ScaleZ; + public float TranslateX; + public float TranslateY; + public float TranslateZ; + public uint Swizzle; + public uint SubpixelPrecisionBias; + + public ViewportSwizzle UnpackSwizzleX() + { + return (ViewportSwizzle)(Swizzle & 7); + } + + public ViewportSwizzle UnpackSwizzleY() + { + return (ViewportSwizzle)((Swizzle >> 4) & 7); + } + + public ViewportSwizzle UnpackSwizzleZ() + { + return (ViewportSwizzle)((Swizzle >> 8) & 7); + } + + public ViewportSwizzle UnpackSwizzleW() + { + return (ViewportSwizzle)((Swizzle >> 12) & 7); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Buffer.cs b/Ryujinx.Graphics.OpenGL/Buffer.cs new file mode 100644 index 000000000..b86719ceb --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Buffer.cs @@ -0,0 +1,74 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class Buffer : IBuffer + { + public int Handle { get; } + + public Buffer(int size) + { + Handle = GL.GenBuffer(); + + GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + GL.BufferData(BufferTarget.CopyWriteBuffer, size, IntPtr.Zero, BufferUsageHint.DynamicDraw); + } + + public void CopyTo(IBuffer destination, int srcOffset, int dstOffset, int size) + { + GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle); + GL.BindBuffer(BufferTarget.CopyWriteBuffer, ((Buffer)destination).Handle); + + GL.CopyBufferSubData( + BufferTarget.CopyReadBuffer, + BufferTarget.CopyWriteBuffer, + (IntPtr)srcOffset, + (IntPtr)dstOffset, + (IntPtr)size); + } + + public byte[] GetData(int offset, int size) + { + GL.BindBuffer(BufferTarget.CopyReadBuffer, Handle); + + byte[] data = new byte[size]; + + GL.GetBufferSubData(BufferTarget.CopyReadBuffer, (IntPtr)offset, size, data); + + return data; + } + + public void SetData(Span data) + { + unsafe + { + GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + + fixed (byte* ptr = data) + { + GL.BufferData(BufferTarget.CopyWriteBuffer, data.Length, (IntPtr)ptr, BufferUsageHint.DynamicDraw); + } + } + } + + public void SetData(int offset, Span data) + { + GL.BindBuffer(BufferTarget.CopyWriteBuffer, Handle); + + unsafe + { + fixed (byte* ptr = data) + { + GL.BufferSubData(BufferTarget.CopyWriteBuffer, (IntPtr)offset, data.Length, (IntPtr)ptr); + } + } + } + + public void Dispose() + { + GL.DeleteBuffer(Handle); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/ComputePipeline.cs b/Ryujinx.Graphics.OpenGL/ComputePipeline.cs new file mode 100644 index 000000000..bee96832d --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/ComputePipeline.cs @@ -0,0 +1,95 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class ComputePipeline : IComputePipeline + { + private Renderer _renderer; + + private Program _program; + + public ComputePipeline(Renderer renderer) + { + _renderer = renderer; + } + + public void Dispatch(int groupsX, int groupsY, int groupsZ) + { + BindProgram(); + + GL.DispatchCompute(groupsX, groupsY, groupsZ); + + UnbindProgram(); + } + + public void SetProgram(IProgram program) + { + _program = (Program)program; + } + + public void SetStorageBuffer(int index, BufferRange buffer) + { + BindProgram(); + + BindBuffer(index, buffer, isStorage: true); + + UnbindProgram(); + } + + public void SetUniformBuffer(int index, BufferRange buffer) + { + BindProgram(); + + BindBuffer(index, buffer, isStorage: false); + + UnbindProgram(); + } + + private void BindBuffer(int index, BufferRange buffer, bool isStorage) + { + int bindingPoint = isStorage + ? _program.GetStorageBufferBindingPoint(ShaderStage.Compute, index) + : _program.GetUniformBufferBindingPoint(ShaderStage.Compute, index); + + if (bindingPoint == -1) + { + return; + } + + BufferRangeTarget target = isStorage + ? BufferRangeTarget.ShaderStorageBuffer + : BufferRangeTarget.UniformBuffer; + + if (buffer.Buffer == null) + { + GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); + + return; + } + + int bufferHandle = ((Buffer)buffer.Buffer).Handle; + + IntPtr bufferOffset = (IntPtr)buffer.Offset; + + GL.BindBufferRange( + target, + bindingPoint, + bufferHandle, + bufferOffset, + buffer.Size); + } + + private void BindProgram() + { + _program.Bind(); + } + + private void UnbindProgram() + { + ((GraphicsPipeline)_renderer.GraphicsPipeline).RebindProgram(); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs new file mode 100644 index 000000000..8f9b5074f --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/AddressModeConverter.cs @@ -0,0 +1,26 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class AddressModeConverter + { + public static TextureWrapMode Convert(this AddressMode mode) + { + switch (mode) + { + case AddressMode.Clamp : return TextureWrapMode.Clamp; + case AddressMode.Repeat : return TextureWrapMode.Repeat; + case AddressMode.MirrorClamp : return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampExt; + case AddressMode.MirrorClampToEdge : return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToEdgeExt; + case AddressMode.MirrorClampToBorder : return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToBorderExt; + case AddressMode.ClampToBorder : return TextureWrapMode.ClampToBorder; + case AddressMode.MirroredRepeat : return TextureWrapMode.MirroredRepeat; + case AddressMode.ClampToEdge : return TextureWrapMode.ClampToEdge; + } + + throw new ArgumentException($"Invalid address mode \"{mode}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs new file mode 100644 index 000000000..1dc405226 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/BlendFactorConverter.cs @@ -0,0 +1,39 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Blend; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class BlendFactorConverter + { + public static All Convert(this BlendFactor factor) + { + switch (factor) + { + case BlendFactor.Zero: return All.Zero; + case BlendFactor.One: return All.One; + case BlendFactor.SrcColor: return All.SrcColor; + case BlendFactor.OneMinusSrcColor: return All.OneMinusSrcColor; + case BlendFactor.SrcAlpha: return All.SrcAlpha; + case BlendFactor.OneMinusSrcAlpha: return All.OneMinusSrcAlpha; + case BlendFactor.DstAlpha: return All.DstAlpha; + case BlendFactor.OneMinusDstAlpha: return All.OneMinusDstAlpha; + case BlendFactor.DstColor: return All.DstColor; + case BlendFactor.OneMinusDstColor: return All.OneMinusDstColor; + case BlendFactor.SrcAlphaSaturate: return All.SrcAlphaSaturate; + case BlendFactor.Src1Color: return All.Src1Color; + case BlendFactor.OneMinusSrc1Color: return All.OneMinusSrc1Color; + case BlendFactor.Src1Alpha: return All.Src1Alpha; + case BlendFactor.OneMinusSrc1Alpha: return All.OneMinusSrc1Alpha; + case BlendFactor.ConstantColor: return All.ConstantColor; + case BlendFactor.OneMinusConstantColor: return All.OneMinusConstantColor; + case BlendFactor.ConstantAlpha: return All.ConstantAlpha; + case BlendFactor.OneMinusConstantAlpha: return All.OneMinusConstantAlpha; + } + + return All.Zero; + + throw new ArgumentException($"Invalid blend factor \"{factor}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs new file mode 100644 index 000000000..b33a3bf89 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/BlendOpConverter.cs @@ -0,0 +1,25 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Blend; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class BlendOpConverter + { + public static BlendEquationMode Convert(this BlendOp op) + { + switch (op) + { + case BlendOp.Add: return BlendEquationMode.FuncAdd; + case BlendOp.Subtract: return BlendEquationMode.FuncSubtract; + case BlendOp.ReverseSubtract: return BlendEquationMode.FuncReverseSubtract; + case BlendOp.Minimum: return BlendEquationMode.Min; + case BlendOp.Maximum: return BlendEquationMode.Max; + } + + return BlendEquationMode.FuncAdd; + + throw new ArgumentException($"Invalid blend operation \"{op}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs new file mode 100644 index 000000000..c0287d8a3 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/CompareModeConverter.cs @@ -0,0 +1,20 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class CompareModeConverter + { + public static TextureCompareMode Convert(this CompareMode mode) + { + switch (mode) + { + case CompareMode.None: return TextureCompareMode.None; + case CompareMode.CompareRToTexture: return TextureCompareMode.CompareRToTexture; + } + + throw new ArgumentException($"Invalid compare mode \"{mode}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs new file mode 100644 index 000000000..f592735bd --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/CompareOpConverter.cs @@ -0,0 +1,28 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class CompareOpConverter + { + public static All Convert(this CompareOp op) + { + switch (op) + { + case CompareOp.Never: return All.Never; + case CompareOp.Less: return All.Less; + case CompareOp.Equal: return All.Equal; + case CompareOp.LessOrEqual: return All.Lequal; + case CompareOp.Greater: return All.Greater; + case CompareOp.NotEqual: return All.Notequal; + case CompareOp.GreaterOrEqual: return All.Gequal; + case CompareOp.Always: return All.Always; + } + + return All.Never; + + throw new ArgumentException($"Invalid compare operation \"{op}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/DepthStencilModeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/DepthStencilModeConverter.cs new file mode 100644 index 000000000..4d14bdea2 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/DepthStencilModeConverter.cs @@ -0,0 +1,20 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Texture; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class DepthStencilModeConverter + { + public static All Convert(this DepthStencilMode mode) + { + switch (mode) + { + case DepthStencilMode.Depth: return All.Depth; + case DepthStencilMode.Stencil: return All.Stencil; + } + + throw new ArgumentException($"Invalid depth stencil mode \"{mode}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs new file mode 100644 index 000000000..5c23b68a4 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/FaceConverter.cs @@ -0,0 +1,23 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class FaceConverter + { + public static CullFaceMode Convert(this Face face) + { + switch (face) + { + case Face.Back: return CullFaceMode.Back; + case Face.Front: return CullFaceMode.Front; + case Face.FrontAndBack: return CullFaceMode.FrontAndBack; + } + + return CullFaceMode.FrontAndBack; + + throw new ArgumentException($"Invalid face \"{face}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs new file mode 100644 index 000000000..1cae36cd1 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/FrontFaceConverter.cs @@ -0,0 +1,22 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class FrontFaceConverter + { + public static FrontFaceDirection Convert(this FrontFace frontFace) + { + switch (frontFace) + { + case FrontFace.Clockwise: return FrontFaceDirection.Cw; + case FrontFace.CounterClockwise: return FrontFaceDirection.Ccw; + } + + return FrontFaceDirection.Cw; + + throw new ArgumentException($"Invalid front face \"{frontFace}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs new file mode 100644 index 000000000..d3100b98f --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/IndexTypeConverter.cs @@ -0,0 +1,21 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class IndexTypeConverter + { + public static DrawElementsType Convert(this IndexType type) + { + switch (type) + { + case IndexType.UByte: return DrawElementsType.UnsignedByte; + case IndexType.UShort: return DrawElementsType.UnsignedShort; + case IndexType.UInt: return DrawElementsType.UnsignedInt; + } + + throw new ArgumentException($"Invalid index type \"{type}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs new file mode 100644 index 000000000..d86d8014a --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/MagFilterConverter.cs @@ -0,0 +1,20 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class MagFilterConverter + { + public static TextureMagFilter Convert(this MagFilter filter) + { + switch (filter) + { + case MagFilter.Nearest: return TextureMagFilter.Nearest; + case MagFilter.Linear: return TextureMagFilter.Linear; + } + + throw new ArgumentException($"Invalid filter \"{filter}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs new file mode 100644 index 000000000..128577f85 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/MinFilterConverter.cs @@ -0,0 +1,24 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Sampler; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class MinFilterConverter + { + public static TextureMinFilter Convert(this MinFilter filter) + { + switch (filter) + { + case MinFilter.Nearest: return TextureMinFilter.Nearest; + case MinFilter.Linear: return TextureMinFilter.Linear; + case MinFilter.NearestMipmapNearest: return TextureMinFilter.NearestMipmapNearest; + case MinFilter.LinearMipmapNearest: return TextureMinFilter.LinearMipmapNearest; + case MinFilter.NearestMipmapLinear: return TextureMinFilter.NearestMipmapLinear; + case MinFilter.LinearMipmapLinear: return TextureMinFilter.LinearMipmapLinear; + } + + throw new ArgumentException($"Invalid filter \"{filter}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/PrimitiveTopologyConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/PrimitiveTopologyConverter.cs new file mode 100644 index 000000000..fe1dfe39b --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/PrimitiveTopologyConverter.cs @@ -0,0 +1,33 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class PrimitiveTopologyConverter + { + public static PrimitiveType Convert(this PrimitiveTopology topology) + { + switch (topology) + { + case PrimitiveTopology.Points: return PrimitiveType.Points; + case PrimitiveTopology.Lines: return PrimitiveType.Lines; + case PrimitiveTopology.LineLoop: return PrimitiveType.LineLoop; + case PrimitiveTopology.LineStrip: return PrimitiveType.LineStrip; + case PrimitiveTopology.Triangles: return PrimitiveType.Triangles; + case PrimitiveTopology.TriangleStrip: return PrimitiveType.TriangleStrip; + case PrimitiveTopology.TriangleFan: return PrimitiveType.TriangleFan; + case PrimitiveTopology.Quads: return PrimitiveType.Quads; + case PrimitiveTopology.QuadStrip: return PrimitiveType.QuadStrip; + case PrimitiveTopology.Polygon: return PrimitiveType.Polygon; + case PrimitiveTopology.LinesAdjacency: return PrimitiveType.LinesAdjacency; + case PrimitiveTopology.LineStripAdjacency: return PrimitiveType.LineStripAdjacency; + case PrimitiveTopology.TrianglesAdjacency: return PrimitiveType.TrianglesAdjacency; + case PrimitiveTopology.TriangleStripAdjacency: return PrimitiveType.TriangleStripAdjacency; + case PrimitiveTopology.Patches: return PrimitiveType.Patches; + } + + throw new ArgumentException($"Invalid primitive topology \"{topology}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs new file mode 100644 index 000000000..44fbe0a50 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/StencilOpConverter.cs @@ -0,0 +1,27 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class StencilOpConverter + { + public static StencilOp Convert(this GAL.DepthStencil.StencilOp op) + { + switch (op) + { + case GAL.DepthStencil.StencilOp.Keep: return StencilOp.Keep; + case GAL.DepthStencil.StencilOp.Zero: return StencilOp.Zero; + case GAL.DepthStencil.StencilOp.Replace: return StencilOp.Replace; + case GAL.DepthStencil.StencilOp.IncrementAndClamp: return StencilOp.Incr; + case GAL.DepthStencil.StencilOp.DecrementAndClamp: return StencilOp.Decr; + case GAL.DepthStencil.StencilOp.Invert: return StencilOp.Invert; + case GAL.DepthStencil.StencilOp.IncrementAndWrap: return StencilOp.IncrWrap; + case GAL.DepthStencil.StencilOp.DecrementAndWrap: return StencilOp.DecrWrap; + } + + return StencilOp.Keep; + + throw new ArgumentException($"Invalid stencil operation \"{op}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/SwizzleComponentConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/SwizzleComponentConverter.cs new file mode 100644 index 000000000..d9ce650f4 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/SwizzleComponentConverter.cs @@ -0,0 +1,24 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Texture; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class SwizzleComponentConverter + { + public static All Convert(this SwizzleComponent swizzleComponent) + { + switch (swizzleComponent) + { + case SwizzleComponent.Zero: return All.Zero; + case SwizzleComponent.One: return All.One; + case SwizzleComponent.Red: return All.Red; + case SwizzleComponent.Green: return All.Green; + case SwizzleComponent.Blue: return All.Blue; + case SwizzleComponent.Alpha: return All.Alpha; + } + + throw new ArgumentException($"Invalid swizzle component \"{swizzleComponent}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs b/Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs new file mode 100644 index 000000000..6b5249800 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Converters/TargetConverter.cs @@ -0,0 +1,33 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.Texture; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class TargetConverter + { + public static ImageTarget ConvertToImageTarget(this Target target) + { + return (ImageTarget)target.Convert(); + } + + public static TextureTarget Convert(this Target target) + { + switch (target) + { + case Target.Texture1D: return TextureTarget.Texture1D; + case Target.Texture2D: return TextureTarget.Texture2D; + case Target.Texture3D: return TextureTarget.Texture3D; + case Target.Texture1DArray: return TextureTarget.Texture1DArray; + case Target.Texture2DArray: return TextureTarget.Texture2DArray; + case Target.Texture2DMultisample: return TextureTarget.Texture2DMultisample; + case Target.Rectangle: return TextureTarget.TextureRectangle; + case Target.Cubemap: return TextureTarget.TextureCubeMap; + case Target.CubemapArray: return TextureTarget.TextureCubeMapArray; + case Target.TextureBuffer: return TextureTarget.TextureBuffer; + } + + throw new ArgumentException($"Invalid target \"{target}\"."); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Counters.cs b/Ryujinx.Graphics.OpenGL/Counters.cs new file mode 100644 index 000000000..e82a040f0 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Counters.cs @@ -0,0 +1,77 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class Counters + { + private int[] _queryObjects; + + private ulong[] _accumulatedCounters; + + public Counters() + { + int count = Enum.GetNames(typeof(CounterType)).Length; + + _queryObjects = new int[count]; + + _accumulatedCounters = new ulong[count]; + } + + public void Initialize() + { + for (int index = 0; index < _queryObjects.Length; index++) + { + int handle = GL.GenQuery(); + + _queryObjects[index] = handle; + + CounterType type = (CounterType)index; + + GL.BeginQuery(GetTarget(type), handle); + } + } + + public ulong GetCounter(CounterType type) + { + UpdateAccumulatedCounter(type); + + return _accumulatedCounters[(int)type]; + } + + public void ResetCounter(CounterType type) + { + UpdateAccumulatedCounter(type); + + _accumulatedCounters[(int)type] = 0; + } + + private void UpdateAccumulatedCounter(CounterType type) + { + int handle = _queryObjects[(int)type]; + + QueryTarget target = GetTarget(type); + + GL.EndQuery(target); + + GL.GetQueryObject(handle, GetQueryObjectParam.QueryResult, out long result); + + _accumulatedCounters[(int)type] += (ulong)result; + + GL.BeginQuery(target, handle); + } + + private static QueryTarget GetTarget(CounterType type) + { + switch (type) + { + case CounterType.SamplesPassed: return QueryTarget.SamplesPassed; + case CounterType.PrimitivesGenerated: return QueryTarget.PrimitivesGenerated; + case CounterType.TransformFeedbackPrimitivesWritten: return QueryTarget.TransformFeedbackPrimitivesWritten; + } + + return QueryTarget.SamplesPassed; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Debugger.cs b/Ryujinx.Graphics.OpenGL/Debugger.cs new file mode 100644 index 000000000..9da98ae1a --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Debugger.cs @@ -0,0 +1,43 @@ +using OpenTK.Graphics.OpenGL; +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Graphics.OpenGL +{ + public static class Debugger + { + private static DebugProc _debugCallback; + + public static void Initialize() + { + GL.Enable(EnableCap.DebugOutputSynchronous); + + int[] array = null; + + GL.DebugMessageControl(DebugSourceControl.DontCare, DebugTypeControl.DontCare, DebugSeverityControl.DontCare, 0, array, true); + + _debugCallback = PrintDbg; + + GL.DebugMessageCallback(_debugCallback, IntPtr.Zero); + } + + private static void PrintDbg( + DebugSource source, + DebugType type, + int id, + DebugSeverity severity, + int length, + IntPtr message, + IntPtr userParam) + { + string msg = Marshal.PtrToStringAnsi(message); + + if (type == DebugType.DebugTypeError && !msg.Contains("link")) + { + throw new Exception(msg); + } + + System.Console.WriteLine("GL message: " + source + " " + type + " " + severity + " " + msg); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs b/Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs new file mode 100644 index 000000000..a1fd723f2 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Formats/FormatInfo.cs @@ -0,0 +1,45 @@ +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL.Formats +{ + struct FormatInfo + { + public int Components { get; } + public bool Normalized { get; } + public bool Scaled { get; } + + public PixelInternalFormat PixelInternalFormat { get; } + public PixelFormat PixelFormat { get; } + public PixelType PixelType { get; } + + public bool IsCompressed { get; } + + public FormatInfo( + int components, + bool normalized, + bool scaled, + All pixelInternalFormat, + PixelFormat pixelFormat, + PixelType pixelType) + { + Components = components; + Normalized = normalized; + Scaled = scaled; + PixelInternalFormat = (PixelInternalFormat)pixelInternalFormat; + PixelFormat = pixelFormat; + PixelType = pixelType; + IsCompressed = false; + } + + public FormatInfo(int components, bool normalized, bool scaled, All pixelFormat) + { + Components = components; + Normalized = normalized; + Scaled = scaled; + PixelInternalFormat = 0; + PixelFormat = (PixelFormat)pixelFormat; + PixelType = 0; + IsCompressed = true; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs b/Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs new file mode 100644 index 000000000..13e88c556 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Formats/FormatTable.cs @@ -0,0 +1,183 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL.Formats +{ + struct FormatTable + { + private static FormatInfo[] _table; + + static FormatTable() + { + _table = new FormatInfo[Enum.GetNames(typeof(Format)).Length]; + + Add(Format.R8Unorm, new FormatInfo(1, true, false, All.R8, PixelFormat.Red, PixelType.UnsignedByte)); + Add(Format.R8Snorm, new FormatInfo(1, true, false, All.R8Snorm, PixelFormat.Red, PixelType.Byte)); + Add(Format.R8Uint, new FormatInfo(1, false, false, All.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte)); + Add(Format.R8Sint, new FormatInfo(1, false, false, All.R8i, PixelFormat.RedInteger, PixelType.Byte)); + Add(Format.R16Float, new FormatInfo(1, false, false, All.R16f, PixelFormat.Red, PixelType.HalfFloat)); + Add(Format.R16Unorm, new FormatInfo(1, true, false, All.R16, PixelFormat.Red, PixelType.UnsignedShort)); + Add(Format.R16Snorm, new FormatInfo(1, true, false, All.R16Snorm, PixelFormat.Red, PixelType.Short)); + Add(Format.R16Uint, new FormatInfo(1, false, false, All.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort)); + Add(Format.R16Sint, new FormatInfo(1, false, false, All.R16i, PixelFormat.RedInteger, PixelType.Short)); + Add(Format.R32Float, new FormatInfo(1, false, false, All.R32f, PixelFormat.Red, PixelType.Float)); + Add(Format.R32Uint, new FormatInfo(1, false, false, All.R32ui, PixelFormat.RedInteger, PixelType.UnsignedInt)); + Add(Format.R32Sint, new FormatInfo(1, false, false, All.R32i, PixelFormat.RedInteger, PixelType.Int)); + Add(Format.R8G8Unorm, new FormatInfo(2, true, false, All.Rg8, PixelFormat.Rg, PixelType.UnsignedByte)); + Add(Format.R8G8Snorm, new FormatInfo(2, true, false, All.Rg8Snorm, PixelFormat.Rg, PixelType.Byte)); + Add(Format.R8G8Uint, new FormatInfo(2, false, false, All.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte)); + Add(Format.R8G8Sint, new FormatInfo(2, false, false, All.Rg8i, PixelFormat.RgInteger, PixelType.Byte)); + Add(Format.R16G16Float, new FormatInfo(2, false, false, All.Rg16f, PixelFormat.Rg, PixelType.HalfFloat)); + Add(Format.R16G16Unorm, new FormatInfo(2, true, false, All.Rg16, PixelFormat.Rg, PixelType.UnsignedShort)); + Add(Format.R16G16Snorm, new FormatInfo(2, true, false, All.Rg16Snorm, PixelFormat.Rg, PixelType.Short)); + Add(Format.R16G16Uint, new FormatInfo(2, false, false, All.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort)); + Add(Format.R16G16Sint, new FormatInfo(2, false, false, All.Rg16i, PixelFormat.RgInteger, PixelType.Short)); + Add(Format.R32G32Float, new FormatInfo(2, false, false, All.Rg32f, PixelFormat.Rg, PixelType.Float)); + Add(Format.R32G32Uint, new FormatInfo(2, false, false, All.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt)); + Add(Format.R32G32Sint, new FormatInfo(2, false, false, All.Rg32i, PixelFormat.RgInteger, PixelType.Int)); + Add(Format.R8G8B8Unorm, new FormatInfo(3, true, false, All.Rgb8, PixelFormat.Rgb, PixelType.UnsignedByte)); + Add(Format.R8G8B8Snorm, new FormatInfo(3, true, false, All.Rgb8Snorm, PixelFormat.Rgb, PixelType.Byte)); + Add(Format.R8G8B8Uint, new FormatInfo(3, false, false, All.Rgb8ui, PixelFormat.RgbInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8Sint, new FormatInfo(3, false, false, All.Rgb8i, PixelFormat.RgbInteger, PixelType.Byte)); + Add(Format.R16G16B16Float, new FormatInfo(3, false, false, All.Rgb16f, PixelFormat.Rgb, PixelType.HalfFloat)); + Add(Format.R16G16B16Unorm, new FormatInfo(3, true, false, All.Rgb16, PixelFormat.Rgb, PixelType.UnsignedShort)); + Add(Format.R16G16B16Snorm, new FormatInfo(3, true, false, All.Rgb16Snorm, PixelFormat.Rgb, PixelType.Short)); + Add(Format.R16G16B16Uint, new FormatInfo(3, false, false, All.Rgb16ui, PixelFormat.RgbInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16Sint, new FormatInfo(3, false, false, All.Rgb16i, PixelFormat.RgbInteger, PixelType.Short)); + Add(Format.R32G32B32Float, new FormatInfo(3, false, false, All.Rgb32f, PixelFormat.Rgb, PixelType.Float)); + Add(Format.R32G32B32Uint, new FormatInfo(3, false, false, All.Rgb32ui, PixelFormat.RgbInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32Sint, new FormatInfo(3, false, false, All.Rgb32i, PixelFormat.RgbInteger, PixelType.Int)); + Add(Format.R8G8B8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Snorm, new FormatInfo(4, true, false, All.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte)); + Add(Format.R8G8B8A8Uint, new FormatInfo(4, false, false, All.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Sint, new FormatInfo(4, false, false, All.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte)); + Add(Format.R16G16B16A16Float, new FormatInfo(4, false, false, All.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat)); + Add(Format.R16G16B16A16Unorm, new FormatInfo(4, true, false, All.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort)); + Add(Format.R16G16B16A16Snorm, new FormatInfo(4, true, false, All.Rgba16Snorm, PixelFormat.Rgba, PixelType.Short)); + Add(Format.R16G16B16A16Uint, new FormatInfo(4, false, false, All.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16A16Sint, new FormatInfo(4, false, false, All.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short)); + Add(Format.R32G32B32A32Float, new FormatInfo(4, false, false, All.Rgba32f, PixelFormat.Rgba, PixelType.Float)); + Add(Format.R32G32B32A32Uint, new FormatInfo(4, false, false, All.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32A32Sint, new FormatInfo(4, false, false, All.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int)); + Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte)); + Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort)); + Add(Format.D24X8Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt)); + Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float)); + Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248)); + Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev)); + Add(Format.R8G8B8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R4G4B4A4Unorm, new FormatInfo(4, true, false, All.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed)); + Add(Format.R5G5B5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Rgb, PixelType.UnsignedShort1555Reversed)); + Add(Format.R5G5B5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed)); + Add(Format.R5G6B5Unorm, new FormatInfo(3, true, false, All.Rgb565, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed)); + Add(Format.R10G10B10A2Unorm, new FormatInfo(4, true, false, All.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R10G10B10A2Uint, new FormatInfo(4, false, false, All.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R11G11B10Float, new FormatInfo(3, false, false, All.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev)); + Add(Format.R9G9B9E5Float, new FormatInfo(3, false, false, All.Rgb9E5, PixelFormat.Rgb, PixelType.UnsignedInt5999Rev)); + Add(Format.Bc1RgbUnorm, new FormatInfo(2, true, false, All.CompressedRgbS3tcDxt1Ext)); + Add(Format.Bc1RgbaUnorm, new FormatInfo(1, true, false, All.CompressedRgbaS3tcDxt1Ext)); + Add(Format.Bc2Unorm, new FormatInfo(1, true, false, All.CompressedRgbaS3tcDxt3Ext)); + Add(Format.Bc3Unorm, new FormatInfo(1, true, false, All.CompressedRgbaS3tcDxt5Ext)); + Add(Format.Bc1RgbSrgb, new FormatInfo(2, false, false, All.CompressedSrgbS3tcDxt1Ext)); + Add(Format.Bc1RgbaSrgb, new FormatInfo(1, true, false, All.CompressedSrgbAlphaS3tcDxt1Ext)); + Add(Format.Bc2Srgb, new FormatInfo(1, false, false, All.CompressedSrgbAlphaS3tcDxt3Ext)); + Add(Format.Bc3Srgb, new FormatInfo(1, false, false, All.CompressedSrgbAlphaS3tcDxt5Ext)); + Add(Format.Bc4Unorm, new FormatInfo(1, true, false, All.CompressedRedRgtc1)); + Add(Format.Bc4Snorm, new FormatInfo(1, true, false, All.CompressedSignedRedRgtc1)); + Add(Format.Bc5Unorm, new FormatInfo(1, true, false, All.CompressedRgRgtc2)); + Add(Format.Bc5Snorm, new FormatInfo(1, true, false, All.CompressedSignedRgRgtc2)); + Add(Format.Bc7Unorm, new FormatInfo(1, true, false, All.CompressedRgbaBptcUnorm)); + Add(Format.Bc7Srgb, new FormatInfo(1, false, false, All.CompressedSrgbAlphaBptcUnorm)); + Add(Format.Bc6HUfloat, new FormatInfo(1, false, false, All.CompressedRgbBptcUnsignedFloat)); + Add(Format.Bc6HSfloat, new FormatInfo(1, false, false, All.CompressedRgbBptcSignedFloat)); + Add(Format.R8Uscaled, new FormatInfo(1, false, true, All.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte)); + Add(Format.R8Sscaled, new FormatInfo(1, false, true, All.R8i, PixelFormat.RedInteger, PixelType.Byte)); + Add(Format.R16Uscaled, new FormatInfo(1, false, true, All.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort)); + Add(Format.R16Sscaled, new FormatInfo(1, false, true, All.R16i, PixelFormat.RedInteger, PixelType.Short)); + Add(Format.R32Uscaled, new FormatInfo(1, false, true, All.R32ui, PixelFormat.RedInteger, PixelType.UnsignedInt)); + Add(Format.R32Sscaled, new FormatInfo(1, false, true, All.R32i, PixelFormat.RedInteger, PixelType.Int)); + Add(Format.R8G8Uscaled, new FormatInfo(2, false, true, All.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte)); + Add(Format.R8G8Sscaled, new FormatInfo(2, false, true, All.Rg8i, PixelFormat.RgInteger, PixelType.Byte)); + Add(Format.R16G16Uscaled, new FormatInfo(2, false, true, All.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort)); + Add(Format.R16G16Sscaled, new FormatInfo(2, false, true, All.Rg16i, PixelFormat.RgInteger, PixelType.Short)); + Add(Format.R32G32Uscaled, new FormatInfo(2, false, true, All.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt)); + Add(Format.R32G32Sscaled, new FormatInfo(2, false, true, All.Rg32i, PixelFormat.RgInteger, PixelType.Int)); + Add(Format.R8G8B8Uscaled, new FormatInfo(3, false, true, All.Rgb8ui, PixelFormat.RgbInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8Sscaled, new FormatInfo(3, false, true, All.Rgb8i, PixelFormat.RgbInteger, PixelType.Byte)); + Add(Format.R16G16B16Uscaled, new FormatInfo(3, false, true, All.Rgb16ui, PixelFormat.RgbInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16Sscaled, new FormatInfo(3, false, true, All.Rgb16i, PixelFormat.RgbInteger, PixelType.Short)); + Add(Format.R32G32B32Uscaled, new FormatInfo(3, false, true, All.Rgb32ui, PixelFormat.RgbInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32Sscaled, new FormatInfo(3, false, true, All.Rgb32i, PixelFormat.RgbInteger, PixelType.Int)); + Add(Format.R8G8B8A8Uscaled, new FormatInfo(4, false, true, All.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8A8Sscaled, new FormatInfo(4, false, true, All.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte)); + Add(Format.R16G16B16A16Uscaled, new FormatInfo(4, false, true, All.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16A16Sscaled, new FormatInfo(4, false, true, All.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short)); + Add(Format.R32G32B32A32Uscaled, new FormatInfo(4, false, true, All.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32A32Sscaled, new FormatInfo(4, false, true, All.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int)); + Add(Format.R10G10B10A2Snorm, new FormatInfo(4, true, false, All.Rgb10A2, PixelFormat.Rgba, (PixelType)All.Int2101010Rev)); + Add(Format.R10G10B10A2Sint, new FormatInfo(4, false, false, All.Rgb10A2, PixelFormat.RgbaInteger, (PixelType)All.Int2101010Rev)); + Add(Format.R10G10B10A2Uscaled, new FormatInfo(4, false, true, All.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R10G10B10A2Sscaled, new FormatInfo(4, false, true, All.Rgb10A2, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed)); + Add(Format.R8G8B8X8Unorm, new FormatInfo(4, true, false, All.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.R8G8B8X8Snorm, new FormatInfo(4, true, false, All.Rgb8Snorm, PixelFormat.Rgba, PixelType.Byte)); + Add(Format.R8G8B8X8Uint, new FormatInfo(4, false, false, All.Rgb8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte)); + Add(Format.R8G8B8X8Sint, new FormatInfo(4, false, false, All.Rgb8i, PixelFormat.RgbaInteger, PixelType.Byte)); + Add(Format.R16G16B16X16Float, new FormatInfo(4, false, false, All.Rgb16f, PixelFormat.Rgba, PixelType.HalfFloat)); + Add(Format.R16G16B16X16Unorm, new FormatInfo(4, true, false, All.Rgb16, PixelFormat.Rgba, PixelType.UnsignedShort)); + Add(Format.R16G16B16X16Snorm, new FormatInfo(4, true, false, All.Rgb16Snorm, PixelFormat.Rgba, PixelType.Short)); + Add(Format.R16G16B16X16Uint, new FormatInfo(4, false, false, All.Rgb16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort)); + Add(Format.R16G16B16X16Sint, new FormatInfo(4, false, false, All.Rgb16i, PixelFormat.RgbaInteger, PixelType.Short)); + Add(Format.R32G32B32X32Float, new FormatInfo(4, false, false, All.Rgb32f, PixelFormat.Rgba, PixelType.Float)); + Add(Format.R32G32B32X32Uint, new FormatInfo(4, false, false, All.Rgb32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt)); + Add(Format.R32G32B32X32Sint, new FormatInfo(4, false, false, All.Rgb32i, PixelFormat.RgbaInteger, PixelType.Int)); + Add(Format.Astc4x4Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc4X4Khr)); + Add(Format.Astc5x4Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc5X4Khr)); + Add(Format.Astc5x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc5X5Khr)); + Add(Format.Astc6x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc6X5Khr)); + Add(Format.Astc6x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc6X6Khr)); + Add(Format.Astc8x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X5Khr)); + Add(Format.Astc8x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X6Khr)); + Add(Format.Astc8x8Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc8X8Khr)); + Add(Format.Astc10x5Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X5Khr)); + Add(Format.Astc10x6Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X6Khr)); + Add(Format.Astc10x8Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X8Khr)); + Add(Format.Astc10x10Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc10X10Khr)); + Add(Format.Astc12x10Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc12X10Khr)); + Add(Format.Astc12x12Unorm, new FormatInfo(1, true, false, All.CompressedRgbaAstc12X12Khr)); + Add(Format.Astc4x4Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc4X4Khr)); + Add(Format.Astc5x4Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc5X4Khr)); + Add(Format.Astc5x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc5X5Khr)); + Add(Format.Astc6x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc6X5Khr)); + Add(Format.Astc6x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc6X6Khr)); + Add(Format.Astc8x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X5Khr)); + Add(Format.Astc8x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X6Khr)); + Add(Format.Astc8x8Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc8X8Khr)); + Add(Format.Astc10x5Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X5Khr)); + Add(Format.Astc10x6Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X6Khr)); + Add(Format.Astc10x8Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X8Khr)); + Add(Format.Astc10x10Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc10X10Khr)); + Add(Format.Astc12x10Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc12X10Khr)); + Add(Format.Astc12x12Srgb, new FormatInfo(1, false, false, All.CompressedSrgb8Alpha8Astc12X12Khr)); + Add(Format.B5G6R5Unorm, new FormatInfo(3, true, false, All.Rgb565, PixelFormat.Bgr, PixelType.UnsignedShort565)); + Add(Format.B5G5R5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Bgra, PixelType.UnsignedShort5551)); + Add(Format.B5G5R5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort5551)); + Add(Format.A1B5G5R5Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort1555Reversed)); + Add(Format.B8G8R8X8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); + Add(Format.B8G8R8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); + } + + private static void Add(Format format, FormatInfo info) + { + _table[(int)format] = info; + } + + public static FormatInfo GetFormatInfo(Format format) + { + return _table[(int)format]; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Framebuffer.cs b/Ryujinx.Graphics.OpenGL/Framebuffer.cs new file mode 100644 index 000000000..409130094 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Framebuffer.cs @@ -0,0 +1,116 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class Framebuffer : IDisposable + { + public int Handle { get; private set; } + + private FramebufferAttachment _lastDsAttachment; + + public Framebuffer() + { + Handle = GL.GenFramebuffer(); + } + + public void Bind() + { + GL.BindFramebuffer(FramebufferTarget.Framebuffer, Handle); + } + + public void AttachColor(int index, TextureView color) + { + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0 + index, + color?.Handle ?? 0, + 0); + } + + public void AttachColor(int index, TextureView color, int layer) + { + GL.FramebufferTextureLayer( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0 + index, + color?.Handle ?? 0, + 0, + layer); + } + + public void AttachDepthStencil(TextureView depthStencil) + { + // Detach the last depth/stencil buffer if there is any. + if (_lastDsAttachment != 0) + { + GL.FramebufferTexture(FramebufferTarget.Framebuffer, _lastDsAttachment, 0, 0); + } + + if (depthStencil != null) + { + FramebufferAttachment attachment; + + if (IsPackedDepthStencilFormat(depthStencil.Format)) + { + attachment = FramebufferAttachment.DepthStencilAttachment; + } + else if (IsDepthOnlyFormat(depthStencil.Format)) + { + attachment = FramebufferAttachment.DepthAttachment; + } + else + { + attachment = FramebufferAttachment.StencilAttachment; + } + + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + attachment, + depthStencil.Handle, + 0); + + _lastDsAttachment = attachment; + } + else + { + _lastDsAttachment = 0; + } + } + + public void SetDrawBuffers(int colorsCount) + { + DrawBuffersEnum[] drawBuffers = new DrawBuffersEnum[colorsCount]; + + for (int index = 0; index < colorsCount; index++) + { + drawBuffers[index] = DrawBuffersEnum.ColorAttachment0 + index; + } + + GL.DrawBuffers(colorsCount, drawBuffers); + } + + private static bool IsPackedDepthStencilFormat(Format format) + { + return format == Format.D24UnormS8Uint || + format == Format.D32FloatS8Uint; + } + + private static bool IsDepthOnlyFormat(Format format) + { + return format == Format.D16Unorm || + format == Format.D24X8Unorm || + format == Format.D32Float; + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteFramebuffer(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs b/Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs new file mode 100644 index 000000000..e9f6b2fbf --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/GraphicsPipeline.cs @@ -0,0 +1,718 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Blend; +using Ryujinx.Graphics.GAL.Color; +using Ryujinx.Graphics.GAL.DepthStencil; +using Ryujinx.Graphics.GAL.InputAssembler; +using Ryujinx.Graphics.Shader; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class GraphicsPipeline : IGraphicsPipeline + { + private Program _program; + + private VertexArray _vertexArray; + private Framebuffer _framebuffer; + + private IntPtr _indexBaseOffset; + + private DrawElementsType _elementsType; + + private PrimitiveType _primitiveType; + + private int _stencilFrontMask; + private bool _depthMask; + private bool _depthTest; + private bool _hasDepthBuffer; + + private TextureView _unit0Texture; + + private ClipOrigin _clipOrigin; + + private uint[] _componentMasks; + + internal GraphicsPipeline() + { + _clipOrigin = ClipOrigin.LowerLeft; + } + + public void BindBlendState(int index, BlendDescriptor blend) + { + if (!blend.Enable) + { + GL.Disable(IndexedEnableCap.Blend, index); + + return; + } + + GL.BlendEquationSeparate( + index, + blend.ColorOp.Convert(), + blend.AlphaOp.Convert()); + + GL.BlendFuncSeparate( + index, + (BlendingFactorSrc) blend.ColorSrcFactor.Convert(), + (BlendingFactorDest)blend.ColorDstFactor.Convert(), + (BlendingFactorSrc) blend.AlphaSrcFactor.Convert(), + (BlendingFactorDest)blend.AlphaDstFactor.Convert()); + + GL.Enable(IndexedEnableCap.Blend, index); + } + + public void BindIndexBuffer(BufferRange buffer, IndexType type) + { + _elementsType = type.Convert(); + + _indexBaseOffset = (IntPtr)buffer.Offset; + + EnsureVertexArray(); + + _vertexArray.SetIndexBuffer((Buffer)buffer.Buffer); + } + + public void BindProgram(IProgram program) + { + _program = (Program)program; + + _program.Bind(); + } + + public void BindSampler(int index, ShaderStage stage, ISampler sampler) + { + int unit = _program.GetTextureUnit(stage, index); + + if (unit != -1 && sampler != null) + { + ((Sampler)sampler).Bind(unit); + } + } + + public void BindTexture(int index, ShaderStage stage, ITexture texture) + { + int unit = _program.GetTextureUnit(stage, index); + + if (unit != -1 && texture != null) + { + if (unit == 0) + { + _unit0Texture = ((TextureView)texture); + } + else + { + ((TextureView)texture).Bind(unit); + } + } + } + + public void BindStorageBuffers(int index, ShaderStage stage, BufferRange[] buffers) + { + BindBuffers(index, stage, buffers, isStorage: true); + } + + public void BindUniformBuffers(int index, ShaderStage stage, BufferRange[] buffers) + { + BindBuffers(index, stage, buffers, isStorage: false); + } + + private void BindBuffers(int index, ShaderStage stage, BufferRange[] buffers, bool isStorage) + { + for (int bufferIndex = 0; bufferIndex < buffers.Length; bufferIndex++, index++) + { + int bindingPoint = isStorage + ? _program.GetStorageBufferBindingPoint(stage, index) + : _program.GetUniformBufferBindingPoint(stage, index); + + if (bindingPoint == -1) + { + continue; + } + + BufferRange buffer = buffers[bufferIndex]; + + BufferRangeTarget target = isStorage + ? BufferRangeTarget.ShaderStorageBuffer + : BufferRangeTarget.UniformBuffer; + + if (buffer.Buffer == null) + { + GL.BindBufferRange(target, bindingPoint, 0, IntPtr.Zero, 0); + + continue; + } + + int bufferHandle = ((Buffer)buffer.Buffer).Handle; + + IntPtr bufferOffset = (IntPtr)buffer.Offset; + + GL.BindBufferRange( + target, + bindingPoint, + bufferHandle, + bufferOffset, + buffer.Size); + } + } + + public void BindVertexAttribs(VertexAttribDescriptor[] vertexAttribs) + { + EnsureVertexArray(); + + _vertexArray.SetVertexAttributes(vertexAttribs); + } + + public void BindVertexBuffers(VertexBufferDescriptor[] vertexBuffers) + { + EnsureVertexArray(); + + _vertexArray.SetVertexBuffers(vertexBuffers); + } + + public void ClearRenderTargetColor(int index, uint componentMask, ColorF color) + { + GL.ColorMask( + index, + (componentMask & 1) != 0, + (componentMask & 2) != 0, + (componentMask & 4) != 0, + (componentMask & 8) != 0); + + float[] colors = new float[] { color.Red, color.Green, color.Blue, color.Alpha }; + + GL.ClearBuffer(ClearBuffer.Color, index, colors); + + RestoreComponentMask(index); + } + + public void ClearRenderTargetColor(int index, uint componentMask, ColorSI color) + { + GL.ColorMask( + index, + (componentMask & 1u) != 0, + (componentMask & 2u) != 0, + (componentMask & 4u) != 0, + (componentMask & 8u) != 0); + + int[] colors = new int[] { color.Red, color.Green, color.Blue, color.Alpha }; + + GL.ClearBuffer(ClearBuffer.Color, index, colors); + + RestoreComponentMask(index); + } + + public void ClearRenderTargetColor(int index, uint componentMask, ColorUI color) + { + GL.ColorMask( + index, + (componentMask & 1u) != 0, + (componentMask & 2u) != 0, + (componentMask & 4u) != 0, + (componentMask & 8u) != 0); + + uint[] colors = new uint[] { color.Red, color.Green, color.Blue, color.Alpha }; + + GL.ClearBuffer(ClearBuffer.Color, index, colors); + + RestoreComponentMask(index); + } + + public void ClearRenderTargetDepthStencil( + float depthValue, + bool depthMask, + int stencilValue, + int stencilMask) + { + bool stencilMaskChanged = + stencilMask != 0 && + stencilMask != _stencilFrontMask; + + bool depthMaskChanged = depthMask && depthMask != _depthMask; + + if (stencilMaskChanged) + { + GL.StencilMaskSeparate(StencilFace.Front, stencilMask); + } + + if (depthMaskChanged) + { + GL.DepthMask(depthMask); + } + + if (depthMask && stencilMask != 0) + { + GL.ClearBuffer(ClearBufferCombined.DepthStencil, 0, depthValue, stencilValue); + } + else if (depthMask) + { + GL.ClearBuffer(ClearBuffer.Depth, 0, ref depthValue); + } + else if (stencilMask != 0) + { + GL.ClearBuffer(ClearBuffer.Stencil, 0, ref stencilValue); + } + + if (stencilMaskChanged) + { + GL.StencilMaskSeparate(StencilFace.Front, _stencilFrontMask); + } + + if (depthMaskChanged) + { + GL.DepthMask(_depthMask); + } + } + + public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + { + if (!_program.IsLinked) + { + return; + } + + PrepareForDraw(); + + if (firstInstance == 0 && instanceCount == 1) + { + if (_primitiveType == PrimitiveType.Quads) + { + for (int offset = 0; offset < vertexCount; offset += 4) + { + GL.DrawArrays(PrimitiveType.TriangleFan, firstVertex + offset, 4); + } + } + else if (_primitiveType == PrimitiveType.QuadStrip) + { + GL.DrawArrays(PrimitiveType.TriangleFan, firstVertex, 4); + + for (int offset = 2; offset < vertexCount; offset += 2) + { + GL.DrawArrays(PrimitiveType.TriangleFan, firstVertex + offset, 4); + } + } + else + { + GL.DrawArrays(_primitiveType, firstVertex, vertexCount); + } + + // GL.DrawArrays(_primitiveType, firstVertex, vertexCount); + } + else if (firstInstance == 0) + { + GL.DrawArraysInstanced(_primitiveType, firstVertex, vertexCount, instanceCount); + } + else + { + GL.DrawArraysInstancedBaseInstance( + _primitiveType, + firstVertex, + vertexCount, + instanceCount, + firstInstance); + } + } + + public void DrawIndexed( + int indexCount, + int instanceCount, + int firstIndex, + int firstVertex, + int firstInstance) + { + if (!_program.IsLinked) + { + return; + } + + PrepareForDraw(); + + int firstIndexOffset = firstIndex; + + switch (_elementsType) + { + case DrawElementsType.UnsignedShort: firstIndexOffset *= 2; break; + case DrawElementsType.UnsignedInt: firstIndexOffset *= 4; break; + } + + IntPtr indexBaseOffset = _indexBaseOffset + firstIndexOffset; + + if (firstInstance == 0 && firstVertex == 0 && instanceCount == 1) + { + GL.DrawElements(_primitiveType, indexCount, _elementsType, indexBaseOffset); + } + else if (firstInstance == 0 && instanceCount == 1) + { + GL.DrawElementsBaseVertex( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + firstVertex); + } + else if (firstInstance == 0 && firstVertex == 0) + { + GL.DrawElementsInstanced( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount); + } + else if (firstInstance == 0) + { + GL.DrawElementsInstancedBaseVertex( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount, + firstVertex); + } + else if (firstVertex == 0) + { + GL.DrawElementsInstancedBaseInstance( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount, + firstInstance); + } + else + { + GL.DrawElementsInstancedBaseVertexBaseInstance( + _primitiveType, + indexCount, + _elementsType, + indexBaseOffset, + instanceCount, + firstVertex, + firstInstance); + } + } + + public void DrawIndirect(BufferRange buffer, ulong offset, int drawCount, int stride) + { + + } + + public void DrawIndexedIndirect(BufferRange buffer, ulong offset, int drawCount, int stride) + { + + } + + public void SetBlendColor(ColorF color) + { + GL.BlendColor(color.Red, color.Green, color.Blue, color.Alpha); + } + + public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) + { + if ((enables & PolygonModeMask.Point) != 0) + { + GL.Enable(EnableCap.PolygonOffsetPoint); + } + else + { + GL.Disable(EnableCap.PolygonOffsetPoint); + } + + if ((enables & PolygonModeMask.Line) != 0) + { + GL.Enable(EnableCap.PolygonOffsetLine); + } + else + { + GL.Disable(EnableCap.PolygonOffsetLine); + } + + if ((enables & PolygonModeMask.Fill) != 0) + { + GL.Enable(EnableCap.PolygonOffsetFill); + } + else + { + GL.Disable(EnableCap.PolygonOffsetFill); + } + + if (enables == 0) + { + return; + } + + GL.PolygonOffset(factor, units); + // GL.PolygonOffsetClamp(factor, units, clamp); + } + + public void SetDepthTest(DepthTestDescriptor depthTest) + { + GL.DepthFunc((DepthFunction)depthTest.Func.Convert()); + + _depthMask = depthTest.WriteEnable; + _depthTest = depthTest.TestEnable; + + UpdateDepthTest(); + } + + public void SetFaceCulling(bool enable, Face face) + { + if (!enable) + { + GL.Disable(EnableCap.CullFace); + + return; + } + + GL.CullFace(face.Convert()); + + GL.Enable(EnableCap.CullFace); + } + + public void SetFrontFace(FrontFace frontFace) + { + GL.FrontFace(frontFace.Convert()); + } + + public void SetPrimitiveRestart(bool enable, int index) + { + if (!enable) + { + GL.Disable(EnableCap.PrimitiveRestart); + + return; + } + + GL.PrimitiveRestartIndex(index); + + GL.Enable(EnableCap.PrimitiveRestart); + } + + public void SetPrimitiveTopology(PrimitiveTopology topology) + { + _primitiveType = topology.Convert(); + } + + public void SetRenderTargetColorMasks(uint[] componentMasks) + { + _componentMasks = (uint[])componentMasks.Clone(); + + for (int index = 0; index < componentMasks.Length; index++) + { + RestoreComponentMask(index); + } + } + + public void SetRenderTargets(ITexture color3D, ITexture depthStencil) + { + EnsureFramebuffer(); + + TextureView color = (TextureView)color3D; + + for (int index = 0; index < color.DepthOrLayers; index++) + { + _framebuffer.AttachColor(index, color, index); + } + + TextureView depthStencilView = (TextureView)depthStencil; + + _framebuffer.AttachDepthStencil(depthStencilView); + + _framebuffer.SetDrawBuffers(color.DepthOrLayers); + + _hasDepthBuffer = depthStencil != null && depthStencilView.Format != Format.S8Uint; + + UpdateDepthTest(); + } + + public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + { + EnsureFramebuffer(); + + for (int index = 0; index < colors.Length; index++) + { + TextureView color = (TextureView)colors[index]; + + _framebuffer.AttachColor(index, color); + } + + TextureView depthStencilView = (TextureView)depthStencil; + + _framebuffer.AttachDepthStencil(depthStencilView); + + _framebuffer.SetDrawBuffers(colors.Length); + + _hasDepthBuffer = depthStencil != null && depthStencilView.Format != Format.S8Uint; + + UpdateDepthTest(); + } + + public void SetStencilTest(StencilTestDescriptor stencilTest) + { + if (!stencilTest.TestEnable) + { + GL.Disable(EnableCap.StencilTest); + + return; + } + + GL.StencilOpSeparate( + StencilFace.Front, + stencilTest.FrontSFail.Convert(), + stencilTest.FrontDpFail.Convert(), + stencilTest.FrontDpPass.Convert()); + + GL.StencilFuncSeparate( + StencilFace.Front, + (StencilFunction)stencilTest.FrontFunc.Convert(), + stencilTest.FrontFuncRef, + stencilTest.FrontFuncMask); + + GL.StencilMaskSeparate(StencilFace.Front, stencilTest.FrontMask); + + GL.StencilOpSeparate( + StencilFace.Back, + stencilTest.BackSFail.Convert(), + stencilTest.BackDpFail.Convert(), + stencilTest.BackDpPass.Convert()); + + GL.StencilFuncSeparate( + StencilFace.Back, + (StencilFunction)stencilTest.BackFunc.Convert(), + stencilTest.BackFuncRef, + stencilTest.BackFuncMask); + + GL.StencilMaskSeparate(StencilFace.Back, stencilTest.BackMask); + + GL.Enable(EnableCap.StencilTest); + + _stencilFrontMask = stencilTest.FrontMask; + } + + public void SetViewports(int first, Viewport[] viewports) + { + bool flipY = false; + + float[] viewportArray = new float[viewports.Length * 4]; + + double[] depthRangeArray = new double[viewports.Length * 2]; + + for (int index = 0; index < viewports.Length; index++) + { + int viewportElemIndex = index * 4; + + Viewport viewport = viewports[index]; + + viewportArray[viewportElemIndex + 0] = viewport.Region.X; + viewportArray[viewportElemIndex + 1] = viewport.Region.Y; + + // OpenGL does not support per-viewport flipping, so + // instead we decide that based on the viewport 0 value. + // It will apply to all viewports. + if (index == 0) + { + flipY = viewport.Region.Height < 0; + } + + if (viewport.SwizzleY == ViewportSwizzle.NegativeY) + { + flipY = !flipY; + } + + viewportArray[viewportElemIndex + 2] = MathF.Abs(viewport.Region.Width); + viewportArray[viewportElemIndex + 3] = MathF.Abs(viewport.Region.Height); + + depthRangeArray[index * 2 + 0] = viewport.DepthNear; + depthRangeArray[index * 2 + 1] = viewport.DepthFar; + } + + GL.ViewportArray(first, viewports.Length, viewportArray); + + GL.DepthRangeArray(first, viewports.Length, depthRangeArray); + + SetOrigin(flipY ? ClipOrigin.UpperLeft : ClipOrigin.LowerLeft); + } + + private void SetOrigin(ClipOrigin origin) + { + if (_clipOrigin != origin) + { + _clipOrigin = origin; + + GL.ClipControl(origin, ClipDepthMode.NegativeOneToOne); + } + } + + private void EnsureVertexArray() + { + if (_vertexArray == null) + { + _vertexArray = new VertexArray(); + + _vertexArray.Bind(); + } + } + + private void EnsureFramebuffer() + { + if (_framebuffer == null) + { + _framebuffer = new Framebuffer(); + + _framebuffer.Bind(); + + GL.Enable(EnableCap.FramebufferSrgb); + } + } + + private void UpdateDepthTest() + { + // Enabling depth operations is only valid when we have + // a depth buffer, otherwise it's not allowed. + if (_hasDepthBuffer) + { + if (_depthTest) + { + GL.Enable(EnableCap.DepthTest); + } + else + { + GL.Disable(EnableCap.DepthTest); + } + + GL.DepthMask(_depthMask); + } + else + { + GL.Disable(EnableCap.DepthTest); + + GL.DepthMask(false); + } + } + + private void PrepareForDraw() + { + _vertexArray.Validate(); + + if (_unit0Texture != null) + { + _unit0Texture.Bind(0); + } + } + + private void RestoreComponentMask(int index) + { + GL.ColorMask( + index, + (_componentMasks[index] & 1u) != 0, + (_componentMasks[index] & 2u) != 0, + (_componentMasks[index] & 4u) != 0, + (_componentMasks[index] & 8u) != 0); + } + + public void RebindProgram() + { + _program?.Bind(); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs new file mode 100644 index 000000000..f958946e7 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -0,0 +1,27 @@ +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class HwCapabilities + { + private static Lazy _astcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); + + public static bool SupportsAstcCompression => _astcCompression.Value; + + private static bool HasExtension(string name) + { + int numExtensions = GL.GetInteger(GetPName.NumExtensions); + + for (int extension = 0; extension < numExtensions; extension++) + { + if (GL.GetString(StringNameIndexed.Extensions, extension) == name) + { + return true; + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs new file mode 100644 index 000000000..1f95b449d --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -0,0 +1,175 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class Program : IProgram + { + private const int StageShift = 5; + private const int SbStageShift = 4; + + public int Handle { get; private set; } + + public bool IsLinked { get; private set; } + + private int[] _ubBindingPoints; + private int[] _sbBindingPoints; + private int[] _textureUnits; + + public Program(IShader[] shaders) + { + _ubBindingPoints = new int[32 * 6]; + _sbBindingPoints = new int[16 * 6]; + _textureUnits = new int[32 * 6]; + + for (int index = 0; index < _ubBindingPoints.Length; index++) + { + _ubBindingPoints[index] = -1; + } + + for (int index = 0; index < _sbBindingPoints.Length; index++) + { + _sbBindingPoints[index] = -1; + } + + for (int index = 0; index < _textureUnits.Length; index++) + { + _textureUnits[index] = -1; + } + + Handle = GL.CreateProgram(); + + for (int index = 0; index < shaders.Length; index++) + { + int shaderHandle = ((Shader)shaders[index]).Handle; + + GL.AttachShader(Handle, shaderHandle); + } + + GL.LinkProgram(Handle); + + CheckProgramLink(); + + Bind(); + + int extraBlockindex = GL.GetUniformBlockIndex(Handle, "Extra"); + + if (extraBlockindex >= 0) + { + GL.UniformBlockBinding(Handle, extraBlockindex, 0); + } + + int ubBindingPoint = 1; + int sbBindingPoint = 0; + int textureUnit = 0; + + for (int index = 0; index < shaders.Length; index++) + { + Shader shader = (Shader)shaders[index]; + + foreach (BufferDescriptor descriptor in shader.Info.CBuffers) + { + int location = GL.GetUniformBlockIndex(Handle, descriptor.Name); + + if (location < 0) + { + continue; + } + + GL.UniformBlockBinding(Handle, location, ubBindingPoint); + + int bpIndex = (int)shader.Stage << StageShift | descriptor.Slot; + + _ubBindingPoints[bpIndex] = ubBindingPoint; + + ubBindingPoint++; + } + + foreach (BufferDescriptor descriptor in shader.Info.SBuffers) + { + int location = GL.GetProgramResourceIndex(Handle, ProgramInterface.ShaderStorageBlock, descriptor.Name); + + if (location < 0) + { + continue; + } + + GL.ShaderStorageBlockBinding(Handle, location, sbBindingPoint); + + int bpIndex = (int)shader.Stage << SbStageShift | descriptor.Slot; + + _sbBindingPoints[bpIndex] = sbBindingPoint; + + sbBindingPoint++; + } + + int samplerIndex = 0; + + foreach (TextureDescriptor descriptor in shader.Info.Textures) + { + int location = GL.GetUniformLocation(Handle, descriptor.Name); + + if (location < 0) + { + continue; + } + + GL.Uniform1(location, textureUnit); + + int uIndex = (int)shader.Stage << StageShift | samplerIndex++; + + _textureUnits[uIndex] = textureUnit; + + textureUnit++; + } + } + } + + public void Bind() + { + GL.UseProgram(Handle); + } + + public int GetUniformBufferBindingPoint(ShaderStage stage, int index) + { + return _ubBindingPoints[(int)stage << StageShift | index]; + } + + public int GetStorageBufferBindingPoint(ShaderStage stage, int index) + { + return _sbBindingPoints[(int)stage << SbStageShift | index]; + } + + public int GetTextureUnit(ShaderStage stage, int index) + { + return _textureUnits[(int)stage << StageShift | index]; + } + + private void CheckProgramLink() + { + int status = 0; + + GL.GetProgram(Handle, GetProgramParameterName.LinkStatus, out status); + + if (status == 0) + { + // throw new System.Exception(GL.GetProgramInfoLog(Handle)); + } + else + { + IsLinked = true; + } + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteProgram(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Renderer.cs b/Ryujinx.Graphics.OpenGL/Renderer.cs new file mode 100644 index 000000000..56ba76244 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Renderer.cs @@ -0,0 +1,84 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Sampler; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.OpenGL +{ + public class Renderer : IRenderer + { + public IComputePipeline ComputePipeline { get; } + public IGraphicsPipeline GraphicsPipeline { get; } + + private Counters _counters; + + private Window _window; + + public IWindow Window => _window; + + internal TextureCopy TextureCopy { get; } + + public Renderer() + { + ComputePipeline = new ComputePipeline(this); + GraphicsPipeline = new GraphicsPipeline(); + + _counters = new Counters(); + + _window = new Window(); + + TextureCopy = new TextureCopy(); + } + + public IShader CompileShader(ShaderProgram shader) + { + return new Shader(shader); + } + + public IBuffer CreateBuffer(int size) + { + return new Buffer(size); + } + + public IProgram CreateProgram(IShader[] shaders) + { + return new Program(shaders); + } + + public ISampler CreateSampler(SamplerCreateInfo info) + { + return new Sampler(info); + } + + public ITexture CreateTexture(TextureCreateInfo info) + { + return new TextureStorage(this, info).CreateDefaultView(); + } + + public void FlushPipelines() + { + GL.Finish(); + } + + public Capabilities GetCapabilities() + { + return new Capabilities(HwCapabilities.SupportsAstcCompression); + } + + public ulong GetCounter(CounterType type) + { + return _counters.GetCounter(type); + } + + public void InitializeCounters() + { + _counters.Initialize(); + } + + public void ResetCounter(CounterType type) + { + _counters.ResetCounter(type); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj new file mode 100644 index 000000000..484e8682c --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Ryujinx.Graphics.OpenGL.csproj @@ -0,0 +1,17 @@ + + + + true + netcoreapp3.0 + + + + + + + + + + + + diff --git a/Ryujinx.Graphics.OpenGL/Sampler.cs b/Ryujinx.Graphics.OpenGL/Sampler.cs new file mode 100644 index 000000000..9b9f09a10 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Sampler.cs @@ -0,0 +1,60 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Sampler; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class Sampler : ISampler + { + public int Handle { get; private set; } + + public Sampler(SamplerCreateInfo info) + { + Handle = GL.GenSampler(); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMinFilter, (int)info.MinFilter.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMagFilter, (int)info.MagFilter.Convert()); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapS, (int)info.AddressU.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapT, (int)info.AddressV.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureWrapR, (int)info.AddressP.Convert()); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareMode, (int)info.CompareMode.Convert()); + GL.SamplerParameter(Handle, SamplerParameterName.TextureCompareFunc, (int)info.CompareOp.Convert()); + + unsafe + { + float* borderColor = stackalloc float[4] + { + info.BorderColor.Red, + info.BorderColor.Green, + info.BorderColor.Blue, + info.BorderColor.Alpha + }; + + GL.SamplerParameter(Handle, SamplerParameterName.TextureBorderColor, borderColor); + } + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMinLod, info.MinLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxLod, info.MaxLod); + GL.SamplerParameter(Handle, SamplerParameterName.TextureLodBias, info.MipLodBias); + + GL.SamplerParameter(Handle, SamplerParameterName.TextureMaxAnisotropyExt, info.MaxAnisotropy); + } + + public void Bind(int unit) + { + GL.BindSampler(unit, Handle); + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteSampler(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Shader.cs b/Ryujinx.Graphics.OpenGL/Shader.cs new file mode 100644 index 000000000..f25845cfd --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Shader.cs @@ -0,0 +1,49 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Shader; + +namespace Ryujinx.Graphics.OpenGL +{ + class Shader : IShader + { + public int Handle { get; private set; } + + private ShaderProgram _program; + + public ShaderProgramInfo Info => _program.Info; + + public ShaderStage Stage => _program.Stage; + + public Shader(ShaderProgram program) + { + _program = program; + + ShaderType type = ShaderType.VertexShader; + + switch (program.Stage) + { + case ShaderStage.Compute: type = ShaderType.ComputeShader; break; + case ShaderStage.Vertex: type = ShaderType.VertexShader; break; + case ShaderStage.TessellationControl: type = ShaderType.TessControlShader; break; + case ShaderStage.TessellationEvaluation: type = ShaderType.TessEvaluationShader; break; + case ShaderStage.Geometry: type = ShaderType.GeometryShader; break; + case ShaderStage.Fragment: type = ShaderType.FragmentShader; break; + } + + Handle = GL.CreateShader(type); + + GL.ShaderSource(Handle, program.Code); + GL.CompileShader(Handle); + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteShader(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/TextureCopy.cs new file mode 100644 index 000000000..75ef07c15 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureCopy.cs @@ -0,0 +1,128 @@ +using Ryujinx.Graphics.GAL; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureCopy + { + private int _srcFramebuffer; + private int _dstFramebuffer; + + public void Copy( + TextureView src, + TextureView dst, + Extents2D srcRegion, + Extents2D dstRegion, + bool linearFilter) + { + GL.Disable(EnableCap.FramebufferSrgb); + + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); + int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); + + Attach(FramebufferTarget.ReadFramebuffer, src.Format, src.Handle); + Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); + + ClearBufferMask mask = GetMask(src.Format); + + BlitFramebufferFilter filter = linearFilter + ? BlitFramebufferFilter.Linear + : BlitFramebufferFilter.Nearest; + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + + GL.BlitFramebuffer( + srcRegion.X1, + srcRegion.Y1, + srcRegion.X2, + srcRegion.Y2, + dstRegion.X1, + dstRegion.Y1, + dstRegion.X2, + dstRegion.Y2, + mask, + filter); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + GL.Enable(EnableCap.FramebufferSrgb); + } + + private static void Detach(FramebufferTarget target, Format format) + { + Attach(target, format, 0); + } + + private static void Attach(FramebufferTarget target, Format format, int handle) + { + if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) + { + GL.FramebufferTexture(target, FramebufferAttachment.DepthStencilAttachment, handle, 0); + } + else if (IsDepthOnly(format)) + { + GL.FramebufferTexture(target, FramebufferAttachment.DepthAttachment, handle, 0); + } + else if (format == Format.S8Uint) + { + GL.FramebufferTexture(target, FramebufferAttachment.StencilAttachment, handle, 0); + } + else + { + GL.FramebufferTexture(target, FramebufferAttachment.ColorAttachment0, handle, 0); + } + } + + private static ClearBufferMask GetMask(Format format) + { + if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint) + { + return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit; + } + else if (IsDepthOnly(format)) + { + return ClearBufferMask.DepthBufferBit; + } + else if (format == Format.S8Uint) + { + return ClearBufferMask.StencilBufferBit; + } + else + { + return ClearBufferMask.ColorBufferBit; + } + } + + private static bool IsDepthOnly(Format format) + { + return format == Format.D16Unorm || + format == Format.D24X8Unorm || + format == Format.D32Float; + } + + private int GetSrcFramebufferLazy() + { + if (_srcFramebuffer == 0) + { + _srcFramebuffer = GL.GenFramebuffer(); + } + + return _srcFramebuffer; + } + + private int GetDstFramebufferLazy() + { + if (_dstFramebuffer == 0) + { + _dstFramebuffer = GL.GenFramebuffer(); + } + + return _dstFramebuffer; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs b/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs new file mode 100644 index 000000000..dae492d9e --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureCopyUnscaled.cs @@ -0,0 +1,85 @@ +using Ryujinx.Common; +using Ryujinx.Graphics.GAL.Texture; +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + static class TextureCopyUnscaled + { + public static void Copy(TextureView src, TextureView dst, int dstLayer, int dstLevel) + { + int srcWidth = src.Width; + int srcHeight = src.Height; + int srcDepth = src.DepthOrLayers; + int srcLevels = src.Levels; + + srcWidth = Math.Max(1, srcWidth >> dstLevel); + srcHeight = Math.Max(1, srcHeight >> dstLevel); + + if (src.Target == Target.Texture3D) + { + srcDepth = Math.Max(1, srcDepth >> dstLevel); + } + + int dstWidth = dst.Width; + int dstHeight = dst.Height; + int dstDepth = dst.DepthOrLayers; + int dstLevels = dst.Levels; + + // When copying from a compressed to a non-compressed format, + // the non-compressed texture will have the size of the texture + // in blocks (not in texels), so we must adjust that size to + // match the size in texels of the compressed texture. + if (!src.IsCompressed && dst.IsCompressed) + { + dstWidth = BitUtils.DivRoundUp(dstWidth, dst.BlockWidth); + dstHeight = BitUtils.DivRoundUp(dstHeight, dst.BlockHeight); + } + else if (src.IsCompressed && !dst.IsCompressed) + { + dstWidth *= dst.BlockWidth; + dstHeight *= dst.BlockHeight; + } + + int width = Math.Min(srcWidth, dstWidth); + int height = Math.Min(srcHeight, dstHeight); + int depth = Math.Min(srcDepth, dstDepth); + int levels = Math.Min(srcLevels, dstLevels); + + for (int level = 0; level < levels; level++) + { + // Stop copy if we are already out of the levels range. + if (level >= src.Levels || dstLevel + level >= dst.Levels) + { + break; + } + + GL.CopyImageSubData( + src.Handle, + src.Target.ConvertToImageTarget(), + level, + 0, + 0, + 0, + dst.Handle, + dst.Target.ConvertToImageTarget(), + dstLevel + level, + 0, + 0, + dstLayer, + width, + height, + depth); + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (src.Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/TextureStorage.cs new file mode 100644 index 000000000..d74b0a8ef --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureStorage.cs @@ -0,0 +1,179 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.OpenGL.Formats; +using OpenTK.Graphics.OpenGL; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureStorage + { + public int Handle { get; private set; } + + private Renderer _renderer; + + private TextureCreateInfo _info; + + public Target Target => _info.Target; + + private int _viewsCount; + + public TextureStorage(Renderer renderer, TextureCreateInfo info) + { + _renderer = renderer; + _info = info; + + Handle = GL.GenTexture(); + + CreateImmutableStorage(); + } + + private void CreateImmutableStorage() + { + TextureTarget target = _info.Target.Convert(); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindTexture(target, Handle); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + SizedInternalFormat internalFormat; + + if (format.IsCompressed) + { + internalFormat = (SizedInternalFormat)format.PixelFormat; + } + else + { + internalFormat = (SizedInternalFormat)format.PixelInternalFormat; + } + + switch (_info.Target) + { + case Target.Texture1D: + GL.TexStorage1D( + TextureTarget1d.Texture1D, + _info.Levels, + internalFormat, + _info.Width); + break; + + case Target.Texture1DArray: + GL.TexStorage2D( + TextureTarget2d.Texture1DArray, + _info.Levels, + internalFormat, + _info.Width, + _info.Height); + break; + + case Target.Texture2D: + GL.TexStorage2D( + TextureTarget2d.Texture2D, + _info.Levels, + internalFormat, + _info.Width, + _info.Height); + break; + + case Target.Texture2DArray: + GL.TexStorage3D( + TextureTarget3d.Texture2DArray, + _info.Levels, + internalFormat, + _info.Width, + _info.Height, + _info.Depth); + break; + + case Target.Texture2DMultisample: + GL.TexStorage2DMultisample( + TextureTargetMultisample2d.Texture2DMultisample, + _info.Samples, + internalFormat, + _info.Width, + _info.Height, + true); + break; + + case Target.Texture2DMultisampleArray: + GL.TexStorage3DMultisample( + TextureTargetMultisample3d.Texture2DMultisampleArray, + _info.Samples, + internalFormat, + _info.Width, + _info.Height, + _info.Depth, + true); + break; + + case Target.Texture3D: + GL.TexStorage3D( + TextureTarget3d.Texture3D, + _info.Levels, + internalFormat, + _info.Width, + _info.Height, + _info.Depth); + break; + + case Target.Cubemap: + GL.TexStorage2D( + TextureTarget2d.TextureCubeMap, + _info.Levels, + internalFormat, + _info.Width, + _info.Height); + break; + + case Target.CubemapArray: + GL.TexStorage3D( + (TextureTarget3d)All.TextureCubeMapArray, + _info.Levels, + internalFormat, + _info.Width, + _info.Height, + _info.Depth); + break; + } + } + + public ITexture CreateDefaultView() + { + int layers = _info.GetLayers(); + + return CreateView(_info, 0, 0); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + IncrementViewsCount(); + + return new TextureView(_renderer, this, info, firstLayer, firstLevel); + } + + private void IncrementViewsCount() + { + _viewsCount++; + } + + public void DecrementViewsCount() + { + // If we don't have any views, then the storage is now useless, delete it. + if (--_viewsCount == 0) + { + Dispose(); + } + } + + public void Dispose() + { + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/TextureView.cs b/Ryujinx.Graphics.OpenGL/TextureView.cs new file mode 100644 index 000000000..8a2b50dc4 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/TextureView.cs @@ -0,0 +1,425 @@ +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.GAL.Texture; +using Ryujinx.Graphics.OpenGL.Formats; +using OpenTK.Graphics.OpenGL; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class TextureView : ITexture + { + public int Handle { get; private set; } + + private Renderer _renderer; + + private TextureStorage _parent; + + private TextureView _emulatedViewParent; + + private TextureCreateInfo _info; + + private int _firstLayer; + private int _firstLevel; + + private bool _acquired; + private bool _pendingDelete; + + public int Width => _info.Width; + public int Height => _info.Height; + public int DepthOrLayers => _info.GetDepthOrLayers(); + public int Levels => _info.Levels; + + public Target Target => _info.Target; + public Format Format => _info.Format; + + public int BlockWidth => _info.BlockWidth; + public int BlockHeight => _info.BlockHeight; + + public bool IsCompressed => _info.IsCompressed; + + public TextureView( + Renderer renderer, + TextureStorage parent, + TextureCreateInfo info, + int firstLayer, + int firstLevel) + { + _renderer = renderer; + _parent = parent; + _info = info; + + _firstLayer = firstLayer; + _firstLevel = firstLevel; + + Handle = GL.GenTexture(); + + CreateView(); + } + + private void CreateView() + { + TextureTarget target = Target.Convert(); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + PixelInternalFormat pixelInternalFormat; + + if (format.IsCompressed) + { + pixelInternalFormat = (PixelInternalFormat)format.PixelFormat; + } + else + { + pixelInternalFormat = format.PixelInternalFormat; + } + + GL.TextureView( + Handle, + target, + _parent.Handle, + pixelInternalFormat, + _firstLevel, + _info.Levels, + _firstLayer, + _info.GetLayers()); + + GL.ActiveTexture(TextureUnit.Texture0); + + GL.BindTexture(target, Handle); + + int[] swizzleRgba = new int[] + { + (int)_info.SwizzleR.Convert(), + (int)_info.SwizzleG.Convert(), + (int)_info.SwizzleB.Convert(), + (int)_info.SwizzleA.Convert() + }; + + GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); + + int maxLevel = _info.Levels - 1; + + if (maxLevel < 0) + { + maxLevel = 0; + } + + GL.TexParameter(target, TextureParameterName.TextureMaxLevel, maxLevel); + + // GL.TexParameter(target, TextureParameterName.DepthStencilTextureMode, (int)_info.DepthStencilMode.Convert()); + } + + public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + { + if (_info.IsCompressed == info.IsCompressed) + { + firstLayer += _firstLayer; + firstLevel += _firstLevel; + + return _parent.CreateView(info, firstLayer, firstLevel); + } + else + { + // TODO: Improve + TextureView emulatedView = (TextureView)_renderer.CreateTexture(info); + + emulatedView._emulatedViewParent = this; + + emulatedView._firstLayer = firstLayer; + emulatedView._firstLevel = firstLevel; + + return emulatedView; + } + } + + public int GetStorageDebugId() + { + return _parent.GetHashCode(); + } + + public void CopyTo(ITexture destination) + { + TextureView destinationView = (TextureView)destination; + + TextureCopyUnscaled.Copy(this, destinationView, 0, 0); + + int width = Math.Min(Width, destinationView.Width); + int height = Math.Min(Height, destinationView.Height); + + int depth = Math.Min(_info.GetDepthOrLayers(), destinationView._info.GetDepthOrLayers()); + + int levels = Math.Min(_info.Levels, destinationView._info.Levels); + + if (destinationView._emulatedViewParent != null) + { + TextureCopyUnscaled.Copy( + this, + destinationView._emulatedViewParent, + destinationView._firstLayer, + destinationView._firstLevel); + } + } + + public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + { + _renderer.TextureCopy.Copy(this, (TextureView)destination, srcRegion, dstRegion, linearFilter); + } + + public byte[] GetData(int face) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + int depth = _info.GetDepthOrLayers(); + + if (target == TextureTarget.TextureCubeMap) + { + target = TextureTarget.TextureCubeMapPositiveX + face; + } + + if (format.IsCompressed) + { + byte[] data = new byte[_info.Width * _info.Height * depth * 4]; + + GL.GetTexImage(target, 0, PixelFormat.Rgba, PixelType.UnsignedByte, data); + + return data; + } + else + { + byte[] data = new byte[_info.GetMipSize(0)]; + + GL.GetTexImage(target, 0, format.PixelFormat, format.PixelType, data); + + return data; + } + } + + public void SetData(Span data) + { + unsafe + { + fixed (byte* ptr = data) + { + SetData((IntPtr)ptr, data.Length); + } + } + } + + private void SetData(IntPtr data, int size) + { + TextureTarget target = Target.Convert(); + + Bind(target, 0); + + FormatInfo format = FormatTable.GetFormatInfo(_info.Format); + + int width = _info.Width; + int height = _info.Height; + int depth = _info.Depth; + + int offset = 0; + + for (int level = 0; level < _info.Levels; level++) + { + int mipSize = _info.GetMipSize(level); + + int endOffset = offset + mipSize; + + if ((uint)endOffset > (uint)size) + { + return; + } + + switch (_info.Target) + { + case Target.Texture1D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage1D( + target, + level, + 0, + width, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage1D( + target, + level, + 0, + width, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture1DArray: + case Target.Texture2D: + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + target, + level, + 0, + 0, + width, + height, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage2D( + target, + level, + 0, + 0, + width, + height, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Texture2DArray: + case Target.Texture3D: + case Target.CubemapArray: + if (format.IsCompressed) + { + GL.CompressedTexSubImage3D( + target, + level, + 0, + 0, + 0, + width, + height, + depth, + format.PixelFormat, + mipSize, + data); + } + else + { + GL.TexSubImage3D( + target, + level, + 0, + 0, + 0, + width, + height, + depth, + format.PixelFormat, + format.PixelType, + data); + } + break; + + case Target.Cubemap: + int faceOffset = 0; + + for (int face = 0; face < 6; face++, faceOffset += mipSize / 6) + { + if (format.IsCompressed) + { + GL.CompressedTexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + level, + 0, + 0, + width, + height, + format.PixelFormat, + mipSize / 6, + data + faceOffset); + } + else + { + GL.TexSubImage2D( + TextureTarget.TextureCubeMapPositiveX + face, + level, + 0, + 0, + width, + height, + format.PixelFormat, + format.PixelType, + data + faceOffset); + } + } + break; + } + + data += mipSize; + offset += mipSize; + + width = Math.Max(1, width >> 1); + height = Math.Max(1, height >> 1); + + if (Target == Target.Texture3D) + { + depth = Math.Max(1, depth >> 1); + } + } + } + + public void Bind(int unit) + { + Bind(Target.Convert(), unit); + } + + private void Bind(TextureTarget target, int unit) + { + GL.ActiveTexture(TextureUnit.Texture0 + unit); + + GL.BindTexture(target, Handle); + } + + public void Acquire() + { + _acquired = true; + } + + public void Release() + { + _acquired = false; + + if (_pendingDelete) + { + _pendingDelete = false; + + Dispose(); + } + } + + public void Dispose() + { + if (_acquired) + { + _pendingDelete = true; + + return; + } + + if (Handle != 0) + { + GL.DeleteTexture(Handle); + + _parent.DecrementViewsCount(); + + Handle = 0; + } + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/VertexArray.cs b/Ryujinx.Graphics.OpenGL/VertexArray.cs new file mode 100644 index 000000000..cf87c9530 --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/VertexArray.cs @@ -0,0 +1,135 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL.InputAssembler; +using Ryujinx.Graphics.OpenGL.Formats; +using System; + +namespace Ryujinx.Graphics.OpenGL +{ + class VertexArray : IDisposable + { + public int Handle { get; } + + private bool _needsAttribsUpdate; + + private VertexBufferDescriptor[] _vertexBuffers; + private VertexAttribDescriptor[] _vertexAttribs; + + public VertexArray() + { + Handle = GL.GenVertexArray(); + } + + public void Bind() + { + GL.BindVertexArray(Handle); + } + + public void SetVertexBuffers(VertexBufferDescriptor[] vertexBuffers) + { + int bindingIndex = 0; + + foreach (VertexBufferDescriptor vb in vertexBuffers) + { + if (vb.Buffer.Buffer != null) + { + int bufferHandle = ((Buffer)vb.Buffer.Buffer).Handle; + + GL.BindVertexBuffer(bindingIndex, bufferHandle, (IntPtr)vb.Buffer.Offset, vb.Stride); + + GL.VertexBindingDivisor(bindingIndex, vb.Divisor); + } + else + { + GL.BindVertexBuffer(bindingIndex, 0, IntPtr.Zero, 0); + } + + bindingIndex++; + } + + _vertexBuffers = vertexBuffers; + + _needsAttribsUpdate = true; + } + + public void SetVertexAttributes(VertexAttribDescriptor[] vertexAttribs) + { + int attribIndex = 0; + + foreach (VertexAttribDescriptor attrib in vertexAttribs) + { + FormatInfo fmtInfo = FormatTable.GetFormatInfo(attrib.Format); + + GL.EnableVertexAttribArray(attribIndex); + + int offset = attrib.Offset; + int size = fmtInfo.Components; + + bool isFloat = fmtInfo.PixelType == PixelType.Float || + fmtInfo.PixelType == PixelType.HalfFloat; + + if (isFloat || fmtInfo.Normalized || fmtInfo.Scaled) + { + VertexAttribType type = (VertexAttribType)fmtInfo.PixelType; + + GL.VertexAttribFormat(attribIndex, size, type, fmtInfo.Normalized, offset); + } + else + { + VertexAttribIntegerType type = (VertexAttribIntegerType)fmtInfo.PixelType; + + GL.VertexAttribIFormat(attribIndex, size, type, offset); + } + + GL.VertexAttribBinding(attribIndex, attrib.BufferIndex); + + attribIndex++; + } + + for (; attribIndex < 16; attribIndex++) + { + GL.DisableVertexAttribArray(attribIndex); + } + + _vertexAttribs = vertexAttribs; + } + + public void SetIndexBuffer(Buffer indexBuffer) + { + GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBuffer?.Handle ?? 0); + } + + public void Validate() + { + for (int attribIndex = 0; attribIndex < _vertexAttribs.Length; attribIndex++) + { + VertexAttribDescriptor attrib = _vertexAttribs[attribIndex]; + + if ((uint)attrib.BufferIndex >= _vertexBuffers.Length) + { + GL.DisableVertexAttribArray(attribIndex); + + continue; + } + + if (_vertexBuffers[attrib.BufferIndex].Buffer.Buffer == null) + { + GL.DisableVertexAttribArray(attribIndex); + + continue; + } + + if (_needsAttribsUpdate) + { + GL.EnableVertexAttribArray(attribIndex); + } + } + + _needsAttribsUpdate = false; + } + + public void Dispose() + { + GL.DeleteVertexArray(Handle); + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/VertexBuffer.cs b/Ryujinx.Graphics.OpenGL/VertexBuffer.cs new file mode 100644 index 000000000..19a58053c --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/VertexBuffer.cs @@ -0,0 +1,19 @@ +using Ryujinx.Graphics.GAL; + +namespace Ryujinx.Graphics.OpenGL +{ + struct VertexBuffer + { + public BufferRange Range { get; } + + public int Divisor { get; } + public int Stride { get; } + + public VertexBuffer(BufferRange range, int divisor, int stride) + { + Range = range; + Divisor = divisor; + Stride = stride; + } + } +} diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs new file mode 100644 index 000000000..a05eb1c0b --- /dev/null +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -0,0 +1,248 @@ +using OpenTK.Graphics.OpenGL; +using Ryujinx.Graphics.GAL; +using System; +using System.Collections.Generic; + +namespace Ryujinx.Graphics.OpenGL +{ + class Window : IWindow + { + private const int NativeWidth = 1280; + private const int NativeHeight = 720; + + private int _width = 1280; + private int _height = 720; + + private int _blitFramebufferHandle; + private int _copyFramebufferHandle; + + private int _screenTextureHandle; + + private TextureReleaseCallback _release; + + private struct PresentationTexture + { + public TextureView Texture { get; } + + public ImageCrop Crop { get; } + + public object Context { get; } + + public PresentationTexture(TextureView texture, ImageCrop crop, object context) + { + Texture = texture; + Crop = crop; + Context = context; + } + } + + private Queue _textures; + + public Window() + { + _textures = new Queue(); + } + + public void Present() + { + GL.Disable(EnableCap.FramebufferSrgb); + + CopyTextureFromQueue(); + + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); + int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetCopyFramebufferHandleLazy()); + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + + GL.Clear(ClearBufferMask.ColorBufferBit); + + GL.BlitFramebuffer( + 0, + 0, + 1280, + 720, + 0, + 0, + 1280, + 720, + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + GL.Enable(EnableCap.FramebufferSrgb); + } + + private void CopyTextureFromQueue() + { + if (!_textures.TryDequeue(out PresentationTexture presentationTexture)) + { + return; + } + + TextureView texture = presentationTexture.Texture; + ImageCrop crop = presentationTexture.Crop; + object context = presentationTexture.Context; + + int oldReadFramebufferHandle = GL.GetInteger(GetPName.ReadFramebufferBinding); + int oldDrawFramebufferHandle = GL.GetInteger(GetPName.DrawFramebufferBinding); + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetCopyFramebufferHandleLazy()); + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetBlitFramebufferHandleLazy()); + + GL.FramebufferTexture( + FramebufferTarget.ReadFramebuffer, + FramebufferAttachment.ColorAttachment0, + texture.Handle, + 0); + + GL.ReadBuffer(ReadBufferMode.ColorAttachment0); + + GL.Clear(ClearBufferMask.ColorBufferBit); + + int srcX0, srcX1, srcY0, srcY1; + + if (crop.Left == 0 && crop.Right == 0) + { + srcX0 = 0; + srcX1 = texture.Width; + } + else + { + srcX0 = crop.Left; + srcX1 = crop.Right; + } + + if (crop.Top == 0 && crop.Bottom == 0) + { + srcY0 = 0; + srcY1 = texture.Height; + } + else + { + srcY0 = crop.Top; + srcY1 = crop.Bottom; + } + + float ratioX = MathF.Min(1f, (_height * (float)NativeWidth) / ((float)NativeHeight * _width)); + float ratioY = MathF.Min(1f, (_width * (float)NativeHeight) / ((float)NativeWidth * _height)); + + int dstWidth = (int)(_width * ratioX); + int dstHeight = (int)(_height * ratioY); + + int dstPaddingX = (_width - dstWidth) / 2; + int dstPaddingY = (_height - dstHeight) / 2; + + int dstX0 = crop.FlipX ? _width - dstPaddingX : dstPaddingX; + int dstX1 = crop.FlipX ? dstPaddingX : _width - dstPaddingX; + + int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY; + int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY; + + GL.BlitFramebuffer( + srcX0, + srcY0, + srcX1, + srcY1, + dstX0, + dstY0, + dstX1, + dstY1, + ClearBufferMask.ColorBufferBit, + BlitFramebufferFilter.Linear); + + GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, oldReadFramebufferHandle); + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, oldDrawFramebufferHandle); + + texture.Release(); + + Release(context); + } + + public void QueueTexture(ITexture texture, ImageCrop crop, object context) + { + if (texture == null) + { + Release(context); + + return; + } + + TextureView textureView = (TextureView)texture; + + textureView.Acquire(); + + _textures.Enqueue(new PresentationTexture(textureView, crop, context)); + } + + public void RegisterTextureReleaseCallback(TextureReleaseCallback callback) + { + _release = callback; + } + + private void Release(object context) + { + if (_release != null) + { + _release(context); + } + } + + private int GetBlitFramebufferHandleLazy() + { + int handle = _blitFramebufferHandle; + + if (handle == 0) + { + handle = GL.GenFramebuffer(); + + _blitFramebufferHandle = handle; + } + + return handle; + } + + private int GetCopyFramebufferHandleLazy() + { + int handle = _copyFramebufferHandle; + + if (handle == 0) + { + int textureHandle = GL.GenTexture(); + + GL.BindTexture(TextureTarget.Texture2D, textureHandle); + + GL.TexImage2D( + TextureTarget.Texture2D, + 0, + PixelInternalFormat.Rgba8, + 1280, + 720, + 0, + PixelFormat.Rgba, + PixelType.UnsignedByte, + IntPtr.Zero); + + handle = GL.GenFramebuffer(); + + GL.BindFramebuffer(FramebufferTarget.Framebuffer, handle); + + GL.FramebufferTexture( + FramebufferTarget.Framebuffer, + FramebufferAttachment.ColorAttachment0, + textureHandle, + 0); + + _screenTextureHandle = textureHandle; + + _copyFramebufferHandle = handle; + } + + return handle; + } + } +} diff --git a/Ryujinx.Graphics/Shader/CBufferDescriptor.cs b/Ryujinx.Graphics.Shader/BufferDescriptor.cs similarity index 67% rename from Ryujinx.Graphics/Shader/CBufferDescriptor.cs rename to Ryujinx.Graphics.Shader/BufferDescriptor.cs index f99665e16..4cc999989 100644 --- a/Ryujinx.Graphics/Shader/CBufferDescriptor.cs +++ b/Ryujinx.Graphics.Shader/BufferDescriptor.cs @@ -1,12 +1,12 @@ namespace Ryujinx.Graphics.Shader { - public struct CBufferDescriptor + public struct BufferDescriptor { public string Name { get; } public int Slot { get; } - public CBufferDescriptor(string name, int slot) + public BufferDescriptor(string name, int slot) { Name = name; Slot = slot; diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/CodeGenContext.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs similarity index 87% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/CodeGenContext.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs index dcbdc3097..322bfbf56 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/CodeGenContext.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/CodeGenContext.cs @@ -9,7 +9,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public ShaderConfig Config { get; } - public List CBufferDescriptors { get; } + public List CBufferDescriptors { get; } + public List SBufferDescriptors { get; } public List TextureDescriptors { get; } public OperandManager OperandManager { get; } @@ -24,7 +25,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { Config = config; - CBufferDescriptors = new List(); + CBufferDescriptors = new List(); + SBufferDescriptors = new List(); TextureDescriptors = new List(); OperandManager = new OperandManager(); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs new file mode 100644 index 000000000..3644b21a6 --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -0,0 +1,319 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + static class Declarations + { + // At least 16 attributes are guaranteed by the spec. + public const int MaxAttributes = 16; + + public static void Declare(CodeGenContext context, StructuredProgramInfo info) + { + context.AppendLine("#version 420 core"); + context.AppendLine("#extension GL_ARB_shader_storage_buffer_object : enable"); + + if (context.Config.Stage == ShaderStage.Compute) + { + context.AppendLine("#extension GL_ARB_compute_shader : enable"); + } + + context.AppendLine(); + + context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;"); + context.AppendLine(); + + if (context.Config.Stage == ShaderStage.Geometry) + { + string inPrimitive = "points"; + + if ((context.Config.Flags & TranslationFlags.Unspecialized) != 0) + { + inPrimitive = DefineNames.InputTopologyName; + } + + context.AppendLine($"layout ({inPrimitive}) in;"); + + string outPrimitive = "triangle_strip"; + + switch (context.Config.OutputTopology) + { + case OutputTopology.LineStrip: outPrimitive = "line_strip"; break; + case OutputTopology.PointList: outPrimitive = "points"; break; + case OutputTopology.TriangleStrip: outPrimitive = "triangle_strip"; break; + } + + int maxOutputVertices = context.Config.MaxOutputVertices; + + context.AppendLine($"layout ({outPrimitive}, max_vertices = {maxOutputVertices}) out;"); + context.AppendLine(); + } + + context.AppendLine("layout (std140) uniform Extra"); + + context.EnterScope(); + + context.AppendLine("vec2 flip;"); + context.AppendLine("int instance;"); + + context.LeaveScope(";"); + + context.AppendLine(); + + context.AppendLine($"precise float {DefaultNames.LocalMemoryName}[0x100];"); + context.AppendLine(); + + if (info.CBuffers.Count != 0) + { + DeclareUniforms(context, info); + + context.AppendLine(); + } + + if (info.SBuffers.Count != 0) + { + DeclareStorage(context, info); + + context.AppendLine(); + } + + if (info.Samplers.Count != 0) + { + DeclareSamplers(context, info); + + context.AppendLine(); + } + + if (context.Config.Stage != ShaderStage.Compute) + { + if (info.IAttributes.Count != 0) + { + DeclareInputAttributes(context, info); + + context.AppendLine(); + } + + if (info.OAttributes.Count != 0 || context.Config.Stage != ShaderStage.Fragment) + { + DeclareOutputAttributes(context, info); + + context.AppendLine(); + } + } + else + { + string localSizeX = "1"; + string localSizeY = "1"; + string localSizeZ = "1"; + + if ((context.Config.Flags & TranslationFlags.Unspecialized) != 0) + { + localSizeX = DefineNames.LocalSizeX; + localSizeY = DefineNames.LocalSizeY; + localSizeZ = DefineNames.LocalSizeZ; + } + + context.AppendLine( + $"layout (" + + $"local_size_x = {localSizeX}, " + + $"local_size_y = {localSizeY}, " + + $"local_size_z = {localSizeZ}) in;"); + context.AppendLine(); + } + } + + public static void DeclareLocals(CodeGenContext context, StructuredProgramInfo info) + { + foreach (AstOperand decl in info.Locals) + { + string name = context.OperandManager.DeclareLocal(decl); + + context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); + } + } + + private static string GetVarTypeName(VariableType type) + { + switch (type) + { + case VariableType.Bool: return "bool"; + case VariableType.F32: return "precise float"; + case VariableType.S32: return "int"; + case VariableType.U32: return "uint"; + } + + throw new ArgumentException($"Invalid variable type \"{type}\"."); + } + + private static void DeclareUniforms(CodeGenContext context, StructuredProgramInfo info) + { + foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) + { + string ubName = OperandManager.GetShaderStagePrefix(context.Config.Stage); + + ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot; + + context.CBufferDescriptors.Add(new BufferDescriptor(ubName, cbufSlot)); + + context.AppendLine("layout (std140) uniform " + ubName); + + context.EnterScope(); + + string ubSize = "[" + NumberFormatter.FormatInt(context.Config.MaxCBufferSize / 16) + "]"; + + context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Stage, cbufSlot) + ubSize + ";"); + + context.LeaveScope(";"); + } + } + + private static void DeclareStorage(CodeGenContext context, StructuredProgramInfo info) + { + foreach (int sbufSlot in info.SBuffers.OrderBy(x => x)) + { + string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage); + + sbName += "_" + DefaultNames.StorageNamePrefix + sbufSlot; + + context.SBufferDescriptors.Add(new BufferDescriptor(sbName, sbufSlot)); + + context.AppendLine("layout (std430) buffer " + sbName); + + context.EnterScope(); + + context.AppendLine("precise float " + OperandManager.GetSbName(context.Config.Stage, sbufSlot) + "[];"); + + context.LeaveScope(";"); + } + } + + private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info) + { + Dictionary samplers = new Dictionary(); + + foreach (AstTextureOperation texOp in info.Samplers.OrderBy(x => x.Handle)) + { + string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp); + + if (!samplers.TryAdd(samplerName, texOp)) + { + continue; + } + + string samplerTypeName = GetSamplerTypeName(texOp.Target); + + context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";"); + } + + foreach (KeyValuePair kv in samplers) + { + string samplerName = kv.Key; + + AstTextureOperation texOp = kv.Value; + + TextureDescriptor desc; + + if ((texOp.Flags & TextureFlags.Bindless) != 0) + { + AstOperand operand = texOp.GetSource(0) as AstOperand; + + desc = new TextureDescriptor(samplerName, texOp.Target, operand.CbufSlot, operand.CbufOffset); + } + else + { + desc = new TextureDescriptor(samplerName, texOp.Target, texOp.Handle); + } + + context.TextureDescriptors.Add(desc); + } + } + + private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) + { + string suffix = context.Config.Stage == ShaderStage.Geometry ? "[]" : string.Empty; + + foreach (int attr in info.IAttributes.OrderBy(x => x)) + { + string iq = info.InterpolationQualifiers[attr].ToGlslQualifier(); + + if (iq != string.Empty) + { + iq += " "; + } + + context.AppendLine($"layout (location = {attr}) {iq}in vec4 {DefaultNames.IAttributePrefix}{attr}{suffix};"); + } + } + + private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) + { + if (context.Config.Stage == ShaderStage.Fragment) + { + DeclareUsedOutputAttributes(context, info); + } + else + { + DeclareAllOutputAttributes(context, info); + } + } + + private static void DeclareUsedOutputAttributes(CodeGenContext context, StructuredProgramInfo info) + { + foreach (int attr in info.OAttributes.OrderBy(x => x)) + { + context.AppendLine($"layout (location = {attr}) out vec4 {DefaultNames.OAttributePrefix}{attr};"); + } + } + + private static void DeclareAllOutputAttributes(CodeGenContext context, StructuredProgramInfo info) + { + for (int attr = 0; attr < MaxAttributes; attr++) + { + string iq = $"{DefineNames.OutQualifierPrefixName}{attr} "; + + context.AppendLine($"layout (location = {attr}) {iq}out vec4 {DefaultNames.OAttributePrefix}{attr};"); + } + + foreach (int attr in info.OAttributes.OrderBy(x => x).Where(x => x >= MaxAttributes)) + { + context.AppendLine($"layout (location = {attr}) out vec4 {DefaultNames.OAttributePrefix}{attr};"); + } + } + + private static string GetSamplerTypeName(TextureTarget type) + { + string typeName; + + switch (type & TextureTarget.Mask) + { + case TextureTarget.Texture1D: typeName = "sampler1D"; break; + case TextureTarget.Texture2D: typeName = "sampler2D"; break; + case TextureTarget.Texture3D: typeName = "sampler3D"; break; + case TextureTarget.TextureCube: typeName = "samplerCube"; break; + + default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); + } + + if ((type & TextureTarget.Multisample) != 0) + { + typeName += "MS"; + } + + if ((type & TextureTarget.Array) != 0) + { + typeName += "Array"; + } + + if ((type & TextureTarget.Shadow) != 0) + { + typeName += "Shadow"; + } + + return typeName; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs similarity index 74% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/DefaultNames.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 1d3939fb7..67de3b438 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/DefaultNames.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -9,9 +9,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string IAttributePrefix = "in_attr"; public const string OAttributePrefix = "out_attr"; + public const string StorageNamePrefix = "s"; + public const string StorageNameSuffix = "data"; + public const string UniformNamePrefix = "c"; public const string UniformNameSuffix = "data"; + public const string LocalMemoryName = "local_mem"; + public const string UndefinedName = "undef"; } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs similarity index 82% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs index 4edbda8b9..65246d975 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslGenerator.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslGenerator.cs @@ -1,4 +1,3 @@ -using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; @@ -20,6 +19,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return new GlslProgram( context.CBufferDescriptors.ToArray(), + context.SBufferDescriptors.ToArray(), context.TextureDescriptors.ToArray(), context.GetCode()); } @@ -32,6 +32,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl Declarations.DeclareLocals(context, info); + // Ensure that unused attributes are set, otherwise the downstream + // compiler may eliminate them. + // (Not needed for fragment shader as it is the last stage). + if (context.Config.Stage != ShaderStage.Compute && + context.Config.Stage != ShaderStage.Fragment) + { + for (int attr = 0; attr < Declarations.MaxAttributes; attr++) + { + if (info.OAttributes.Contains(attr)) + { + continue; + } + + context.AppendLine($"{DefaultNames.OAttributePrefix}{attr} = vec4(0);"); + } + } + PrintBlock(context, info.MainBlock); context.LeaveScope(); @@ -81,11 +98,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { if (node is AstOperation operation) { - if (operation.Inst == Instruction.Return) - { - PrepareForReturn(context); - } - context.AppendLine(InstGen.GetExpression(context, operation) + ";"); } else if (node is AstAssignment assignment) @@ -97,7 +109,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (assignment.Destination is AstOperand operand && operand.Type == OperandType.Attribute) { - dest = OperandManager.GetOutAttributeName(operand, context.Config.Type); + dest = OperandManager.GetOutAttributeName(operand, context.Config.Stage); } else { @@ -108,6 +120,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(dest + " = " + src + ";"); } + else if (node is AstComment comment) + { + context.AppendLine("// " + comment.Comment); + } else { throw new InvalidOperationException($"Found unexpected node type \"{node?.GetType().Name ?? "null"}\"."); @@ -121,13 +137,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return ReinterpretCast(context, cond, srcType, VariableType.Bool); } - - private static void PrepareForReturn(CodeGenContext context) - { - if (context.Config.Type == GalShaderType.Vertex) - { - context.AppendLine("gl_Position.xy *= flip;"); - } - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs new file mode 100644 index 000000000..7807cb495 --- /dev/null +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/GlslProgram.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Graphics.Shader.CodeGen.Glsl +{ + class GlslProgram + { + public BufferDescriptor[] CBufferDescriptors { get; } + public BufferDescriptor[] SBufferDescriptors { get; } + public TextureDescriptor[] TextureDescriptors { get; } + + public string Code { get; } + + public GlslProgram( + BufferDescriptor[] cBufferDescriptors, + BufferDescriptor[] sBufferDescriptors, + TextureDescriptor[] textureDescriptors, + string code) + { + CBufferDescriptors = cBufferDescriptors; + SBufferDescriptors = sBufferDescriptors; + TextureDescriptors = textureDescriptors; + Code = code; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs similarity index 83% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs index b0b2ec1a9..350de3480 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGen.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGen.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } else if (node is AstOperand operand) { - return context.OperandManager.GetExpression(operand, context.Config.Type); + return context.OperandManager.GetExpression(operand, context.Config.Stage); } throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); @@ -87,12 +87,27 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { switch (inst) { + case Instruction.LoadAttribute: + return InstGenMemory.LoadAttribute(context, operation); + case Instruction.LoadConstant: return InstGenMemory.LoadConstant(context, operation); + case Instruction.LoadLocal: + return InstGenMemory.LoadLocal(context, operation); + + case Instruction.LoadStorage: + return InstGenMemory.LoadStorage(context, operation); + case Instruction.PackHalf2x16: return InstGenPacking.PackHalf2x16(context, operation); + case Instruction.StoreLocal: + return InstGenMemory.StoreLocal(context, operation); + + case Instruction.StoreStorage: + return InstGenMemory.StoreStorage(context, operation); + case Instruction.TextureSample: return InstGenMemory.TextureSample(context, operation); @@ -104,6 +119,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions } } + return "0"; + throw new InvalidOperationException($"Unexpected instruction type \"{info.Type}\"."); } } diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs similarity index 96% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs index 9855cd913..6989e9974 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenHelper.cs @@ -49,7 +49,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.Floor, InstType.CallUnary, "floor"); Add(Instruction.FusedMultiplyAdd, InstType.CallTernary, "fma"); Add(Instruction.IsNan, InstType.CallUnary, "isnan"); + Add(Instruction.LoadAttribute, InstType.Special); Add(Instruction.LoadConstant, InstType.Special); + Add(Instruction.LoadLocal, InstType.Special); + Add(Instruction.LoadStorage, InstType.Special); Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^^", 10); @@ -71,6 +74,8 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions Add(Instruction.Return, InstType.OpNullary, "return"); Add(Instruction.Sine, InstType.CallUnary, "sin"); Add(Instruction.SquareRoot, InstType.CallUnary, "sqrt"); + Add(Instruction.StoreLocal, InstType.Special); + Add(Instruction.StoreStorage, InstType.Special); Add(Instruction.Subtract, InstType.OpBinary, "-", 2); Add(Instruction.TextureSample, InstType.Special); Add(Instruction.TextureSize, InstType.Special); diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs similarity index 61% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 8b5257fcb..5c9ec830c 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using System; using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper; using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo; @@ -8,31 +9,106 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions { static class InstGenMemory { + public static string LoadAttribute(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(0); + IAstNode src2 = operation.GetSource(1); + + if (!(src1 is AstOperand attr) || attr.Type != OperandType.Attribute) + { + throw new InvalidOperationException("First source of LoadAttribute must be a attribute."); + } + + string indexExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + + return OperandManager.GetAttributeName(attr, context.Config.Stage, isOutAttr: false, indexExpr); + } + public static string LoadConstant(CodeGenContext context, AstOperation operation) { - IAstNode src1 = operation.GetSource(1); + IAstNode src1 = operation.GetSource(0); + IAstNode src2 = operation.GetSource(1); - string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 1)); + string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); - offsetExpr = Enclose(offsetExpr, src1, Instruction.ShiftRightS32, isLhs: true); + offsetExpr = Enclose(offsetExpr, src2, Instruction.ShiftRightS32, isLhs: true); - return OperandManager.GetConstantBufferName(operation.GetSource(0), offsetExpr, context.Config.Type); + return OperandManager.GetConstantBufferName(src1, offsetExpr, context.Config.Stage); + } + + public static string LoadLocal(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(0); + + string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); + + return $"{DefaultNames.LocalMemoryName}[{offsetExpr}]"; + } + + public static string LoadStorage(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(0); + IAstNode src2 = operation.GetSource(1); + + string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + + return OperandManager.GetStorageBufferName(src1, offsetExpr, context.Config.Stage); + } + + public static string StoreLocal(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(0); + IAstNode src2 = operation.GetSource(1); + + string offsetExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0)); + + VariableType srcType = OperandManager.GetNodeDestType(src2); + + string src = TypeConversion.ReinterpretCast(context, src2, srcType, VariableType.F32); + + return $"{DefaultNames.LocalMemoryName}[{offsetExpr}] = {src}"; + } + + public static string StoreStorage(CodeGenContext context, AstOperation operation) + { + IAstNode src1 = operation.GetSource(0); + IAstNode src2 = operation.GetSource(1); + IAstNode src3 = operation.GetSource(2); + + string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1)); + + VariableType srcType = OperandManager.GetNodeDestType(src3); + + string src = TypeConversion.ReinterpretCast(context, src3, srcType, VariableType.F32); + + string sbName = OperandManager.GetStorageBufferName(src1, offsetExpr, context.Config.Stage); + + return $"{sbName} = {src}"; } public static string TextureSample(CodeGenContext context, AstOperation operation) { AstTextureOperation texOp = (AstTextureOperation)operation; - bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; - bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; - bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0; - bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; - bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; - bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; - bool isArray = (texOp.Type & TextureType.Array) != 0; - bool isMultisample = (texOp.Type & TextureType.Multisample) != 0; - bool isShadow = (texOp.Type & TextureType.Shadow) != 0; + bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; + bool isGather = (texOp.Flags & TextureFlags.Gather) != 0; + bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0; + bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0; + bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0; + bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0; + bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0; + + bool isArray = (texOp.Target & TextureTarget.Array) != 0; + bool isMultisample = (texOp.Target & TextureTarget.Multisample) != 0; + bool isShadow = (texOp.Target & TextureTarget.Shadow) != 0; + + // This combination is valid, but not available on GLSL. + // For now, ignore the LOD level and do a normal sample. + // TODO: How to implement it properly? + if (hasLodLevel && isArray && isShadow) + { + hasLodLevel = false; + } string texCall = intCoords ? "texelFetch" : "texture"; @@ -54,11 +130,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions texCall += "Offsets"; } - string samplerName = OperandManager.GetSamplerName(context.Config.Type, texOp); + string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp); texCall += "(" + samplerName; - int coordsCount = texOp.Type.GetCoordsCount(); + int coordsCount = texOp.Target.GetDimensions(); int pCount = coordsCount; @@ -71,7 +147,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions // The sampler 1D shadow overload expects a // dummy value on the middle of the vector, who knows why... - bool hasDummy1DShadowElem = texOp.Type == (TextureType.Texture1D | TextureType.Shadow); + bool hasDummy1DShadowElem = texOp.Target == (TextureTarget.Texture1D | TextureTarget.Shadow); if (hasDummy1DShadowElem) { @@ -217,7 +293,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0; - string samplerName = OperandManager.GetSamplerName(context.Config.Type, texOp); + string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp); IAstNode src0 = operation.GetSource(isBindless ? 1 : 0); diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs similarity index 100% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenPacking.cs diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs similarity index 100% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstInfo.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstInfo.cs diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs similarity index 100% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/Instructions/InstType.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstType.cs diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/NumberFormatter.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs similarity index 100% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/NumberFormatter.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/NumberFormatter.cs diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs similarity index 66% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 19f7185e9..880959208 100644 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -1,6 +1,6 @@ -using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; +using Ryujinx.Graphics.Shader.Translation; using System; using System.Collections.Generic; @@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { class OperandManager { - private static string[] _stagePrefixes = new string[] { "vp", "tcp", "tep", "gp", "fp" }; + private static string[] _stagePrefixes = new string[] { "cp", "vp", "tcp", "tep", "gp", "fp" }; private struct BuiltInAttribute { @@ -46,10 +46,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.PointCoordY, new BuiltInAttribute("gl_PointCoord.y", VariableType.F32) }, { AttributeConsts.TessCoordX, new BuiltInAttribute("gl_TessCoord.x", VariableType.F32) }, { AttributeConsts.TessCoordY, new BuiltInAttribute("gl_TessCoord.y", VariableType.F32) }, - { AttributeConsts.InstanceId, new BuiltInAttribute("instance", VariableType.S32) }, + { AttributeConsts.InstanceId, new BuiltInAttribute("gl_InstanceID", VariableType.S32) }, { AttributeConsts.VertexId, new BuiltInAttribute("gl_VertexID", VariableType.S32) }, { AttributeConsts.FrontFacing, new BuiltInAttribute("gl_FrontFacing", VariableType.Bool) }, - { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) } + + // Special. + { AttributeConsts.FragmentOutputDepth, new BuiltInAttribute("gl_FragDepth", VariableType.F32) }, + { AttributeConsts.ThreadIdX, new BuiltInAttribute("gl_LocalInvocationID.x", VariableType.U32) }, + { AttributeConsts.ThreadIdY, new BuiltInAttribute("gl_LocalInvocationID.y", VariableType.U32) }, + { AttributeConsts.ThreadIdZ, new BuiltInAttribute("gl_LocalInvocationID.z", VariableType.U32) }, + { AttributeConsts.CtaIdX, new BuiltInAttribute("gl_WorkGroupID.x", VariableType.U32) }, + { AttributeConsts.CtaIdY, new BuiltInAttribute("gl_WorkGroupID.y", VariableType.U32) }, + { AttributeConsts.CtaIdZ, new BuiltInAttribute("gl_WorkGroupID.z", VariableType.U32) }, }; private Dictionary _locals; @@ -68,18 +76,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return name; } - public string GetExpression(AstOperand operand, GalShaderType shaderType) + public string GetExpression(AstOperand operand, ShaderStage stage) { switch (operand.Type) { case OperandType.Attribute: - return GetAttributeName(operand, shaderType); + return GetAttributeName(operand, stage); case OperandType.Constant: return NumberFormatter.FormatInt(operand.Value); case OperandType.ConstantBuffer: - return GetConstantBufferName(operand, shaderType); + return GetConstantBufferName(operand, stage); case OperandType.LocalVariable: return _locals[operand]; @@ -91,36 +99,48 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl throw new ArgumentException($"Invalid operand type \"{operand.Type}\"."); } - public static string GetConstantBufferName(AstOperand cbuf, GalShaderType shaderType) + public static string GetConstantBufferName(AstOperand cbuf, ShaderStage stage) { - string ubName = GetUbName(shaderType, cbuf.CbufSlot); + string ubName = GetUbName(stage, cbuf.CbufSlot); ubName += "[" + (cbuf.CbufOffset >> 2) + "]"; return ubName + "." + GetSwizzleMask(cbuf.CbufOffset & 3); } - public static string GetConstantBufferName(IAstNode slot, string offsetExpr, GalShaderType shaderType) + public static string GetStorageBufferName(IAstNode slot, string offsetExpr, ShaderStage stage) { // Non-constant slots are not supported. // It is expected that upstream stages are never going to generate non-constant // slot access. AstOperand operand = (AstOperand)slot; - string ubName = GetUbName(shaderType, operand.Value); + string sbName = GetSbName(stage, operand.Value); - string index0 = "[" + offsetExpr + " >> 4]"; - string index1 = "[" + offsetExpr + " >> 2 & 3]"; + return $"{sbName}[{offsetExpr}]"; + } + + public static string GetConstantBufferName(IAstNode slot, string offsetExpr, ShaderStage stage) + { + // Non-constant slots are not supported. + // It is expected that upstream stages are never going to generate non-constant + // slot access. + AstOperand operand = (AstOperand)slot; + + string ubName = GetUbName(stage, operand.Value); + + string index0 = "[" + offsetExpr + " >> 2]"; + string index1 = "[" + offsetExpr + " & 3]"; return ubName + index0 + index1; } - public static string GetOutAttributeName(AstOperand attr, GalShaderType shaderType) + public static string GetOutAttributeName(AstOperand attr, ShaderStage stage) { - return GetAttributeName(attr, shaderType, isOutAttr: true); + return GetAttributeName(attr, stage, isOutAttr: true); } - private static string GetAttributeName(AstOperand attr, GalShaderType shaderType, bool isOutAttr = false) + public static string GetAttributeName(AstOperand attr, ShaderStage stage, bool isOutAttr = false, string indexExpr = "0") { int value = attr.Value; @@ -137,9 +157,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string name = $"{prefix}{(value >> 4)}"; - if (shaderType == GalShaderType.Geometry && !isOutAttr) + if (stage == ShaderStage.Geometry && !isOutAttr) { - name += "[0]"; + name += $"[{indexExpr}]"; } name += "." + swzMask; @@ -158,7 +178,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl else if (_builtInAttributes.TryGetValue(value & ~3, out BuiltInAttribute builtInAttr)) { // TODO: There must be a better way to handle this... - if (shaderType == GalShaderType.Fragment) + if (stage == ShaderStage.Fragment) { switch (value & ~3) { @@ -171,9 +191,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl string name = builtInAttr.Name; - if (shaderType == GalShaderType.Geometry && !isOutAttr) + if (stage == ShaderStage.Geometry && !isOutAttr) { - name = "gl_in[0]." + name; + name = $"gl_in[{indexExpr}].{name}"; } return name; @@ -185,16 +205,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl return isOutAttr ? "// bad_attr0x" + value.ToString("X") : "0.0"; } - public static string GetUbName(GalShaderType shaderType, int slot) + public static string GetSbName(ShaderStage stage, int slot) { - string ubName = GetShaderStagePrefix(shaderType); + string sbName = GetShaderStagePrefix(stage); + + sbName += "_" + DefaultNames.StorageNamePrefix + slot; + + return sbName + "_" + DefaultNames.StorageNameSuffix; + } + + public static string GetUbName(ShaderStage stage, int slot) + { + string ubName = GetShaderStagePrefix(stage); ubName += "_" + DefaultNames.UniformNamePrefix + slot; return ubName + "_" + DefaultNames.UniformNameSuffix; } - public static string GetSamplerName(GalShaderType shaderType, AstTextureOperation texOp) + public static string GetSamplerName(ShaderStage stage, AstTextureOperation texOp) { string suffix; @@ -209,12 +238,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl suffix = (texOp.Handle - 8).ToString(); } - return GetShaderStagePrefix(shaderType) + "_" + DefaultNames.SamplerNamePrefix + suffix; + return GetShaderStagePrefix(stage) + "_" + DefaultNames.SamplerNamePrefix + suffix; } - public static string GetShaderStagePrefix(GalShaderType shaderType) + public static string GetShaderStagePrefix(ShaderStage stage) { - return _stagePrefixes[(int)shaderType]; + int index = (int)stage; + + if ((uint)index >= _stagePrefixes.Length) + { + return "invalid"; + } + + return _stagePrefixes[index]; } private static string GetSwizzleMask(int value) @@ -226,24 +262,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { if (node is AstOperation operation) { + // Load attribute basically just returns the attribute value. + // Some built-in attributes may have different types, so we need + // to return the type based on the attribute that is being read. + if (operation.Inst == Instruction.LoadAttribute) + { + return GetOperandVarType((AstOperand)operation.GetSource(0)); + } + return GetDestVarType(operation.Inst); } else if (node is AstOperand operand) { - if (operand.Type == OperandType.Attribute) - { - if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) - { - return builtInAttr.Type; - } - } - - return OperandInfo.GetVarType(operand); + return GetOperandVarType(operand); } else { throw new ArgumentException($"Invalid node type \"{node?.GetType().Name ?? "null"}\"."); } } + + private static VariableType GetOperandVarType(AstOperand operand) + { + if (operand.Type == OperandType.Attribute) + { + if (_builtInAttributes.TryGetValue(operand.Value & ~3, out BuiltInAttribute builtInAttr)) + { + return builtInAttr.Type; + } + } + + return OperandInfo.GetVarType(operand); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs similarity index 100% rename from Ryujinx.Graphics/Shader/CodeGen/Glsl/TypeConversion.cs rename to Ryujinx.Graphics.Shader/CodeGen/Glsl/TypeConversion.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/BitfieldExtensions.cs b/Ryujinx.Graphics.Shader/Decoders/BitfieldExtensions.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/BitfieldExtensions.cs rename to Ryujinx.Graphics.Shader/Decoders/BitfieldExtensions.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/Block.cs b/Ryujinx.Graphics.Shader/Decoders/Block.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/Block.cs rename to Ryujinx.Graphics.Shader/Decoders/Block.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/Condition.cs b/Ryujinx.Graphics.Shader/Decoders/Condition.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/Condition.cs rename to Ryujinx.Graphics.Shader/Decoders/Condition.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/ConditionalOperation.cs b/Ryujinx.Graphics.Shader/Decoders/ConditionalOperation.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/ConditionalOperation.cs rename to Ryujinx.Graphics.Shader/Decoders/ConditionalOperation.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/Decoder.cs b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs similarity index 95% rename from Ryujinx.Graphics/Shader/Decoders/Decoder.cs rename to Ryujinx.Graphics.Shader/Decoders/Decoder.cs index 754e03885..dd5347d9e 100644 --- a/Ryujinx.Graphics/Shader/Decoders/Decoder.cs +++ b/Ryujinx.Graphics.Shader/Decoders/Decoder.cs @@ -1,6 +1,6 @@ -using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Shader.Instructions; using System; +using System.Buffers.Binary; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; @@ -12,8 +12,6 @@ namespace Ryujinx.Graphics.Shader.Decoders { static class Decoder { - private const long HeaderSize = 0x50; - private delegate object OpActivator(InstEmitter emitter, ulong address, long opCode); private static ConcurrentDictionary _opActivators; @@ -23,7 +21,7 @@ namespace Ryujinx.Graphics.Shader.Decoders _opActivators = new ConcurrentDictionary(); } - public static Block[] Decode(IGalMemory memory, ulong address) + public static Block[] Decode(Span code, ulong headerSize) { List blocks = new List(); @@ -45,7 +43,7 @@ namespace Ryujinx.Graphics.Shader.Decoders return block; } - ulong startAddress = address + HeaderSize; + ulong startAddress = headerSize; GetBlock(startAddress); @@ -69,7 +67,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } // If we have a block after the current one, set the limit address. - ulong limitAddress = ulong.MaxValue; + ulong limitAddress = (ulong)code.Length; if (nBlkIndex != blocks.Count) { @@ -87,7 +85,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } } - FillBlock(memory, currBlock, limitAddress, startAddress); + FillBlock(code, currBlock, limitAddress, startAddress); if (currBlock.OpCodes.Count != 0) { @@ -173,7 +171,7 @@ namespace Ryujinx.Graphics.Shader.Decoders } private static void FillBlock( - IGalMemory memory, + Span code, Block block, ulong limitAddress, ulong startAddress) @@ -195,8 +193,8 @@ namespace Ryujinx.Graphics.Shader.Decoders continue; } - uint word0 = (uint)memory.ReadInt32((long)(address + 0)); - uint word1 = (uint)memory.ReadInt32((long)(address + 4)); + uint word0 = BinaryPrimitives.ReadUInt32LittleEndian(code.Slice((int)address)); + uint word1 = BinaryPrimitives.ReadUInt32LittleEndian(code.Slice((int)address + 4)); ulong opAddress = address; @@ -209,6 +207,9 @@ namespace Ryujinx.Graphics.Shader.Decoders if (emitter == null) { // TODO: Warning, illegal encoding. + + block.OpCodes.Add(new OpCode(null, opAddress, opCode)); + continue; } diff --git a/Ryujinx.Graphics/Shader/Decoders/DecoderHelper.cs b/Ryujinx.Graphics.Shader/Decoders/DecoderHelper.cs similarity index 91% rename from Ryujinx.Graphics/Shader/Decoders/DecoderHelper.cs rename to Ryujinx.Graphics.Shader/Decoders/DecoderHelper.cs index fd0a45e82..77cd1bf72 100644 --- a/Ryujinx.Graphics/Shader/Decoders/DecoderHelper.cs +++ b/Ryujinx.Graphics.Shader/Decoders/DecoderHelper.cs @@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.Decoders { int imm = opCode.Extract(20, 19); - bool negate = opCode.Extract(56); + bool sign = opCode.Extract(56); - if (negate) + if (sign) { - imm = -imm; + imm = (imm << 13) >> 13; } return imm; diff --git a/Ryujinx.Graphics/Shader/Decoders/FPHalfSwizzle.cs b/Ryujinx.Graphics.Shader/Decoders/FPHalfSwizzle.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/FPHalfSwizzle.cs rename to Ryujinx.Graphics.Shader/Decoders/FPHalfSwizzle.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/FPType.cs b/Ryujinx.Graphics.Shader/Decoders/FPType.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/FPType.cs rename to Ryujinx.Graphics.Shader/Decoders/FPType.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/FmulScale.cs b/Ryujinx.Graphics.Shader/Decoders/FmulScale.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/FmulScale.cs rename to Ryujinx.Graphics.Shader/Decoders/FmulScale.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCode.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCode.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCode.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeAlu.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeAlu.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeAlu.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeFArith.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeFArith.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeFArith.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeFArith.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeHfma.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeHfma.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeHfma.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeHfma.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeImm.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeImm.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeImm.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeImm.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeImmF.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeImmF.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeImmF.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeImmF.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeLop.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeLop.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeLop.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeLop.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRa.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRa.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeRa.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeRa.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRc.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRc.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeRc.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeRc.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRd.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRd.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeRd.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeRd.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeReg.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeReg.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeReg.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeReg.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IOpCodeRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/IOpCodeRegCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IOpCodeRegCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/IOpCodeRegCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerCondition.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerCondition.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IntegerCondition.cs rename to Ryujinx.Graphics.Shader/Decoders/IntegerCondition.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerHalfPart.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerHalfPart.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IntegerHalfPart.cs rename to Ryujinx.Graphics.Shader/Decoders/IntegerHalfPart.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerShift.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerShift.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IntegerShift.cs rename to Ryujinx.Graphics.Shader/Decoders/IntegerShift.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/IntegerSize.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerSize.cs new file mode 100644 index 000000000..3ff8e1b26 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/IntegerSize.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum IntegerSize + { + U8 = 0, + S8 = 1, + U16 = 2, + S16 = 3, + B32 = 4, + B64 = 5, + B128 = 6 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerType.cs b/Ryujinx.Graphics.Shader/Decoders/IntegerType.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/IntegerType.cs rename to Ryujinx.Graphics.Shader/Decoders/IntegerType.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/InterpolationMode.cs b/Ryujinx.Graphics.Shader/Decoders/InterpolationMode.cs new file mode 100644 index 000000000..98ee3b970 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/InterpolationMode.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum InterpolationMode + { + Pass, + Default, + Constant, + Sc + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/LogicalOperation.cs b/Ryujinx.Graphics.Shader/Decoders/LogicalOperation.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/LogicalOperation.cs rename to Ryujinx.Graphics.Shader/Decoders/LogicalOperation.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/MufuOperation.cs b/Ryujinx.Graphics.Shader/Decoders/MufuOperation.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/MufuOperation.cs rename to Ryujinx.Graphics.Shader/Decoders/MufuOperation.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCode.cs b/Ryujinx.Graphics.Shader/Decoders/OpCode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCode.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCode.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAlu.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAlu.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAlu.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAlu.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAluCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAluCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm2x10.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm2x10.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm2x10.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm2x10.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm32.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAluImm32.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAluImm32.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluReg.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAluReg.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAluReg.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAluRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAluRegCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAluRegCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAluRegCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeAttribute.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeAttribute.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeAttribute.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeBranch.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeBranch.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeBranch.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeBranch.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeExit.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeExit.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeExit.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeExit.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArith.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArith.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeFArith.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeFArith.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeFArithCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeFArithCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm32.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeFArithImm32.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeFArithImm32.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithReg.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeFArithReg.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeFArithReg.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFArithRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFArithRegCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeFArithRegCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeFArithRegCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeFsetImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeFsetImm.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeFsetImm.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeFsetImm.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfma.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfma.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeHfma.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeHfma.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm2x10.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm2x10.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm2x10.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm2x10.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm32.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaImm32.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaImm32.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaReg.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaReg.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaReg.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaRegCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaRegCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeHfmaRegCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeHfmaRegCbuf.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeHsetImm2x10.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeHsetImm2x10.cs new file mode 100644 index 000000000..03e1e44ce --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeHsetImm2x10.cs @@ -0,0 +1,14 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeHsetImm2x10 : OpCodeSet, IOpCodeImm + { + public int Immediate { get; } + + public OpCodeHsetImm2x10(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Immediate = DecoderHelper.Decode2xF10Immediate(opCode); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeIpa.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs similarity index 70% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeIpa.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs index e21095a32..b475b6a16 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeIpa.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeIpa.cs @@ -6,9 +6,15 @@ namespace Ryujinx.Graphics.Shader.Decoders { public int AttributeOffset { get; } + public InterpolationMode Mode { get; } + public OpCodeIpa(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { AttributeOffset = opCode.Extract(28, 10); + + Saturate = opCode.Extract(51); + + Mode = (InterpolationMode)opCode.Extract(54, 2); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLdc.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeLdc.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeLdc.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLop.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLop.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeLop.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeLop.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeLopCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeLopCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm32.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm32.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeLopImm32.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeLopImm32.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeLopReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeLopReg.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeLopReg.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeLopReg.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeMemory.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeMemory.cs new file mode 100644 index 000000000..bece45622 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeMemory.cs @@ -0,0 +1,28 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeMemory : OpCode, IOpCodeRd, IOpCodeRa + { + public Register Rd { get; } + public Register Ra { get; } + + public int Offset { get; } + + public bool Extended { get; } + + public IntegerSize Size { get; } + + public OpCodeMemory(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + + Offset = opCode.Extract(20, 24); + + Extended = opCode.Extract(45); + + Size = (IntegerSize)opCode.Extract(48, 3); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodePsetp.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodePsetp.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodePsetp.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodePsetp.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSet.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSet.cs similarity index 77% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeSet.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeSet.cs index cd6773a13..b4ee10fb3 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeSet.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeSet.cs @@ -15,8 +15,8 @@ namespace Ryujinx.Graphics.Shader.Decoders public OpCodeSet(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { - Predicate0 = new Register(opCode.Extract(0, 3), RegisterType.Predicate); - Predicate3 = new Register(opCode.Extract(3, 3), RegisterType.Predicate); + Predicate0 = new Register(opCode.Extract(0, 3), RegisterType.Predicate); + Predicate3 = new Register(opCode.Extract(3, 3), RegisterType.Predicate); LogicalOp = (LogicalOperation)opCode.Extract(45, 2); diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSetCbuf.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetCbuf.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeSetCbuf.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeSetCbuf.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSetImm.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetImm.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeSetImm.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeSetImm.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSetReg.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSetReg.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeSetReg.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeSetReg.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSsy.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSsy.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeSsy.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeSsy.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeSync.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeSync.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeSync.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeSync.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs similarity index 89% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs index d588ce8ee..3fd1de865 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTable.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTable.cs @@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0011100x00000x", InstEmit.Bfe, typeof(OpCodeAluImm)); Set("0101110000000x", InstEmit.Bfe, typeof(OpCodeAluReg)); Set("111000100100xx", InstEmit.Bra, typeof(OpCodeBranch)); + Set("0101000010100x", InstEmit.Csetp, typeof(OpCodePsetp)); Set("111000110000xx", InstEmit.Exit, typeof(OpCodeExit)); Set("0100110010101x", InstEmit.F2F, typeof(OpCodeFArithCbuf)); Set("0011100x10101x", InstEmit.F2F, typeof(OpCodeFArithImm)); @@ -77,6 +78,9 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0111100x0xxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm2x10)); Set("0010101xxxxxxx", InstEmit.Hmul2, typeof(OpCodeAluImm32)); Set("0101110100001x", InstEmit.Hmul2, typeof(OpCodeAluReg)); + Set("0111111x1xxxxx", InstEmit.Hsetp2, typeof(OpCodeSetCbuf)); + Set("0111111x0xxxxx", InstEmit.Hsetp2, typeof(OpCodeHsetImm2x10)); + Set("0101110100100x", InstEmit.Hsetp2, typeof(OpCodeSetReg)); Set("0100110010111x", InstEmit.I2F, typeof(OpCodeAluCbuf)); Set("0011100x10111x", InstEmit.I2F, typeof(OpCodeAluImm)); Set("0101110010111x", InstEmit.I2F, typeof(OpCodeAluReg)); @@ -94,6 +98,7 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0011100x00100x", InstEmit.Imnmx, typeof(OpCodeAluImm)); Set("0101110000100x", InstEmit.Imnmx, typeof(OpCodeAluReg)); Set("11100000xxxxxx", InstEmit.Ipa, typeof(OpCodeIpa)); + Set("1110111111010x", InstEmit.Isberd, typeof(OpCodeAlu)); Set("0100110000011x", InstEmit.Iscadd, typeof(OpCodeAluCbuf)); Set("0011100x00011x", InstEmit.Iscadd, typeof(OpCodeAluImm)); Set("000101xxxxxxxx", InstEmit.Iscadd, typeof(OpCodeAluImm32)); @@ -105,7 +110,9 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0011011x0110xx", InstEmit.Isetp, typeof(OpCodeSetImm)); Set("010110110110xx", InstEmit.Isetp, typeof(OpCodeSetReg)); Set("111000110011xx", InstEmit.Kil, typeof(OpCodeExit)); + Set("1110111101000x", InstEmit.Ld, typeof(OpCodeMemory)); Set("1110111110010x", InstEmit.Ldc, typeof(OpCodeLdc)); + Set("1110111011010x", InstEmit.Ldg, typeof(OpCodeMemory)); Set("0100110001000x", InstEmit.Lop, typeof(OpCodeLopCbuf)); Set("0011100001000x", InstEmit.Lop, typeof(OpCodeLopImm)); Set("000001xxxxxxxx", InstEmit.Lop, typeof(OpCodeLopImm32)); @@ -123,8 +130,9 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0100110010010x", InstEmit.Rro, typeof(OpCodeFArithCbuf)); Set("0011100x10010x", InstEmit.Rro, typeof(OpCodeFArithImm)); Set("0101110010010x", InstEmit.Rro, typeof(OpCodeFArithReg)); + Set("1111000011001x", InstEmit.S2r, typeof(OpCodeAlu)); Set("0100110010100x", InstEmit.Sel, typeof(OpCodeAluCbuf)); - Set("0011100010100x", InstEmit.Sel, typeof(OpCodeAluImm)); + Set("0011100x10100x", InstEmit.Sel, typeof(OpCodeAluImm)); Set("0101110010100x", InstEmit.Sel, typeof(OpCodeAluReg)); Set("0100110001001x", InstEmit.Shl, typeof(OpCodeAluCbuf)); Set("0011100x01001x", InstEmit.Shl, typeof(OpCodeAluImm)); @@ -133,17 +141,20 @@ namespace Ryujinx.Graphics.Shader.Decoders Set("0011100x00101x", InstEmit.Shr, typeof(OpCodeAluImm)); Set("0101110000101x", InstEmit.Shr, typeof(OpCodeAluReg)); Set("111000101001xx", InstEmit.Ssy, typeof(OpCodeSsy)); + Set("1110111101010x", InstEmit.St, typeof(OpCodeMemory)); + Set("1110111011011x", InstEmit.Stg, typeof(OpCodeMemory)); Set("1111000011111x", InstEmit.Sync, typeof(OpCodeSync)); Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex)); - Set("1101111010111x", InstEmit.Tex_B, typeof(OpCodeTex)); + Set("1101111010111x", InstEmit.TexB, typeof(OpCodeTexB)); Set("1101x00xxxxxxx", InstEmit.Texs, typeof(OpCodeTexs)); Set("1101x01xxxxxxx", InstEmit.Texs, typeof(OpCodeTlds)); Set("1101x11100xxxx", InstEmit.Texs, typeof(OpCodeTld4s)); Set("11011100xx111x", InstEmit.Tld, typeof(OpCodeTld)); - Set("11011101xx111x", InstEmit.Tld_B, typeof(OpCodeTld)); + Set("11011101xx111x", InstEmit.TldB, typeof(OpCodeTld)); Set("110010xxxx111x", InstEmit.Tld4, typeof(OpCodeTld4)); Set("1101111101001x", InstEmit.Txq, typeof(OpCodeTex)); - Set("1101111101010x", InstEmit.Txq_B, typeof(OpCodeTex)); + Set("1101111101010x", InstEmit.TxqB, typeof(OpCodeTex)); + Set("01011111xxxxxx", InstEmit.Vmad, typeof(OpCodeVideo)); Set("0100111xxxxxxx", InstEmit.Xmad, typeof(OpCodeAluCbuf)); Set("0011011x00xxxx", InstEmit.Xmad, typeof(OpCodeAluImm)); Set("010100010xxxxx", InstEmit.Xmad, typeof(OpCodeAluRegCbuf)); @@ -189,9 +200,9 @@ namespace Ryujinx.Graphics.Shader.Decoders { value &= xMask; - for (int X = 0; X < xBits; X++) + for (int x = 0; x < xBits; x++) { - value |= ((index >> X) & 1) << xPos[X]; + value |= ((index >> x) & 1) << xPos[x]; } if (_opCodes[value] == null || _opCodes[value].XBits > xBits) @@ -201,9 +212,9 @@ namespace Ryujinx.Graphics.Shader.Decoders } } - public static (InstEmitter emitter, Type opCodeType) GetEmitter(long OpCode) + public static (InstEmitter emitter, Type opCodeType) GetEmitter(long opCode) { - TableEntry entry = _opCodes[(ulong)OpCode >> (64 - EncodingBits)]; + TableEntry entry = _opCodes[(ulong)opCode >> (64 - EncodingBits)]; if (entry != null) { diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTex.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTex.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTex.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeTexB.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexB.cs new file mode 100644 index 000000000..b18bf3bef --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexB.cs @@ -0,0 +1,20 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeTexB : OpCodeTex + { + public OpCodeTexB(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + switch (opCode.Extract(37, 3)) + { + case 0: LodMode = TextureLodMode.None; break; + case 1: LodMode = TextureLodMode.LodZero; break; + case 2: LodMode = TextureLodMode.LodBias; break; + case 3: LodMode = TextureLodMode.LodLevel; break; + case 6: LodMode = TextureLodMode.LodBiasA; break; + case 7: LodMode = TextureLodMode.LodLevelA; break; + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexs.cs similarity index 78% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTexs.cs index 0822c4c07..fb90ccf60 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexs.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexs.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { class OpCodeTexs : OpCodeTextureScalar { - public TextureScalarType Type => (TextureScalarType)RawType; + public TextureTarget Target => (TextureTarget)RawType; public OpCodeTexs(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { } } diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTexture.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTexture.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTexture.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTextureScalar.cs similarity index 97% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTextureScalar.cs index 470b81f5c..1c175e30b 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTextureScalar.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTextureScalar.cs @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Shader.Decoders public int Immediate { get; } - public int ComponentMask { get; } + public int ComponentMask { get; protected set; } protected int RawType; diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTld.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTld.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTld4.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTld4.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4s.cs similarity index 92% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTld4s.cs index 0d7b84606..7e51a9e50 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTld4s.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTld4s.cs @@ -15,6 +15,8 @@ namespace Ryujinx.Graphics.Shader.Decoders HasOffset = opCode.Extract(51); GatherCompIndex = opCode.Extract(52, 2); + + ComponentMask = Rd1.IsRZ ? 3 : 0xf; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeTlds.cs similarity index 78% rename from Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs rename to Ryujinx.Graphics.Shader/Decoders/OpCodeTlds.cs index e117721e9..1e4e943ff 100644 --- a/Ryujinx.Graphics/Shader/Decoders/OpCodeTlds.cs +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeTlds.cs @@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Shader.Decoders { class OpCodeTlds : OpCodeTextureScalar { - public TexelLoadScalarType Type => (TexelLoadScalarType)RawType; + public TexelLoadTarget Target => (TexelLoadTarget)RawType; public OpCodeTlds(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) { } } diff --git a/Ryujinx.Graphics.Shader/Decoders/OpCodeVideo.cs b/Ryujinx.Graphics.Shader/Decoders/OpCodeVideo.cs new file mode 100644 index 000000000..15dcfa981 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/OpCodeVideo.cs @@ -0,0 +1,24 @@ +using Ryujinx.Graphics.Shader.Instructions; + +namespace Ryujinx.Graphics.Shader.Decoders +{ + class OpCodeVideo : OpCode, IOpCodeRd, IOpCodeRa, IOpCodeRc + { + public Register Rd { get; } + public Register Ra { get; } + public Register Rc { get; } + + public bool SetCondCode { get; protected set; } + public bool Saturate { get; protected set; } + + public OpCodeVideo(InstEmitter emitter, ulong address, long opCode) : base(emitter, address, opCode) + { + Rd = new Register(opCode.Extract(0, 8), RegisterType.Gpr); + Ra = new Register(opCode.Extract(8, 8), RegisterType.Gpr); + Rc = new Register(opCode.Extract(39, 8), RegisterType.Gpr); + + SetCondCode = opCode.Extract(47); + Saturate = opCode.Extract(55); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/Register.cs b/Ryujinx.Graphics.Shader/Decoders/Register.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/Register.cs rename to Ryujinx.Graphics.Shader/Decoders/Register.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/RegisterConsts.cs b/Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/RegisterConsts.cs rename to Ryujinx.Graphics.Shader/Decoders/RegisterConsts.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/RegisterType.cs b/Ryujinx.Graphics.Shader/Decoders/RegisterType.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/RegisterType.cs rename to Ryujinx.Graphics.Shader/Decoders/RegisterType.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/RoundingMode.cs b/Ryujinx.Graphics.Shader/Decoders/RoundingMode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/RoundingMode.cs rename to Ryujinx.Graphics.Shader/Decoders/RoundingMode.cs diff --git a/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs b/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs new file mode 100644 index 000000000..3948c899f --- /dev/null +++ b/Ryujinx.Graphics.Shader/Decoders/SystemRegister.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.Decoders +{ + enum SystemRegister + { + ThreadIdX = 0x21, + ThreadIdY = 0x22, + ThreadIdZ = 0x23, + CtaIdX = 0x25, + CtaIdY = 0x26, + CtaIdZ = 0x27 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/TexelLoadScalarType.cs b/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs similarity index 93% rename from Ryujinx.Graphics/Shader/Decoders/TexelLoadScalarType.cs rename to Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs index cef5778a5..478cac44e 100644 --- a/Ryujinx.Graphics/Shader/Decoders/TexelLoadScalarType.cs +++ b/Ryujinx.Graphics.Shader/Decoders/TexelLoadTarget.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Shader.Decoders { - enum TexelLoadScalarType + enum TexelLoadTarget { Texture1DLodZero = 0x0, Texture1DLodLevel = 0x1, diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureDimensions.cs b/Ryujinx.Graphics.Shader/Decoders/TextureDimensions.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/TextureDimensions.cs rename to Ryujinx.Graphics.Shader/Decoders/TextureDimensions.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureGatherOffset.cs b/Ryujinx.Graphics.Shader/Decoders/TextureGatherOffset.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/TextureGatherOffset.cs rename to Ryujinx.Graphics.Shader/Decoders/TextureGatherOffset.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureLodMode.cs b/Ryujinx.Graphics.Shader/Decoders/TextureLodMode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/TextureLodMode.cs rename to Ryujinx.Graphics.Shader/Decoders/TextureLodMode.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureProperty.cs b/Ryujinx.Graphics.Shader/Decoders/TextureProperty.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/TextureProperty.cs rename to Ryujinx.Graphics.Shader/Decoders/TextureProperty.cs diff --git a/Ryujinx.Graphics/Shader/Decoders/TextureScalarType.cs b/Ryujinx.Graphics.Shader/Decoders/TextureTarget.cs similarity index 96% rename from Ryujinx.Graphics/Shader/Decoders/TextureScalarType.cs rename to Ryujinx.Graphics.Shader/Decoders/TextureTarget.cs index 0055174b4..181a0a0d6 100644 --- a/Ryujinx.Graphics/Shader/Decoders/TextureScalarType.cs +++ b/Ryujinx.Graphics.Shader/Decoders/TextureTarget.cs @@ -1,6 +1,6 @@ namespace Ryujinx.Graphics.Shader.Decoders { - enum TextureScalarType + enum TextureTarget { Texture1DLodZero = 0x0, Texture2D = 0x1, diff --git a/Ryujinx.Graphics/Shader/Decoders/XmadCMode.cs b/Ryujinx.Graphics.Shader/Decoders/XmadCMode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Decoders/XmadCMode.cs rename to Ryujinx.Graphics.Shader/Decoders/XmadCMode.cs diff --git a/Ryujinx.Graphics.Shader/DefineNames.cs b/Ryujinx.Graphics.Shader/DefineNames.cs new file mode 100644 index 000000000..f55cbd0cc --- /dev/null +++ b/Ryujinx.Graphics.Shader/DefineNames.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.Graphics.Shader +{ + public static class DefineNames + { + public const string InputTopologyName = "S_INPUT_TOPOLOGY"; + + public const string OutQualifierPrefixName = "S_OUT_QUALIFIER"; + + public const string LocalSizeX = "S_LOCAL_SIZE_X"; + public const string LocalSizeY = "S_LOCAL_SIZE_Y"; + public const string LocalSizeZ = "S_LOCAL_SIZE_Z"; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs similarity index 96% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs index 8e2b39bfe..5cbb3b732 100644 --- a/Ryujinx.Graphics/Shader/Instructions/InstEmitAlu.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitAlu.cs @@ -39,6 +39,25 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: CC, X, corner cases } + public static void Csetp(EmitterContext context) + { + OpCodePsetp op = (OpCodePsetp)context.CurrOp; + + // TODO: Implement that properly + + Operand p0Res = Const(IrConsts.True); + + Operand p1Res = context.BitwiseNot(p0Res); + + Operand pred = GetPredicate39(context); + + p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred); + p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred); + + context.Copy(Register(op.Predicate3), p0Res); + context.Copy(Register(op.Predicate0), p1Res); + } + public static void Iadd(EmitterContext context) { OpCodeAlu op = (OpCodeAlu)context.CurrOp; @@ -50,6 +69,11 @@ namespace Ryujinx.Graphics.Shader.Instructions negateB = op.RawOpCode.Extract(48); negateA = op.RawOpCode.Extract(49); } + else + { + // TODO: Other IADD32I variant without the negate. + negateA = op.RawOpCode.Extract(56); + } Operand srcA = context.INegate(GetSrcA(context), negateA); Operand srcB = context.INegate(GetSrcB(context), negateB); diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitAluHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitAluHelper.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitAluHelper.cs diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitConversion.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitConversion.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitConversion.cs diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitFArith.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs similarity index 90% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitFArith.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs index b22639de9..2ecdee274 100644 --- a/Ryujinx.Graphics/Shader/Instructions/InstEmitFArith.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitFArith.cs @@ -155,10 +155,10 @@ namespace Ryujinx.Graphics.Shader.Instructions Condition cmpOp = (Condition)op.RawOpCode.Extract(48, 4); + bool negateB = op.RawOpCode.Extract(6); bool absoluteA = op.RawOpCode.Extract(7); bool negateA = op.RawOpCode.Extract(43); bool absoluteB = op.RawOpCode.Extract(44); - bool negateB = op.RawOpCode.Extract(6); Operand srcA = context.FPAbsNeg(GetSrcA(context), absoluteA, negateA); Operand srcB = context.FPAbsNeg(GetSrcB(context), absoluteB, negateB); @@ -234,6 +234,39 @@ namespace Ryujinx.Graphics.Shader.Instructions context.Copy(GetDest(context), GetHalfPacked(context, res)); } + public static void Hsetp2(EmitterContext context) + { + OpCodeSet op = (OpCodeSet)context.CurrOp; + + bool hAnd = op.RawOpCode.Extract(53); + + Condition cmpOp = op is IOpCodeReg + ? (Condition)op.RawOpCode.Extract(35, 4) + : (Condition)op.RawOpCode.Extract(49, 4); + + Operand[] srcA = GetHalfSrcA(context); + Operand[] srcB = GetHalfSrcB(context); + + Operand[] res = new Operand[2]; + + Operand pred = GetPredicate39(context); + + Operand p0Res = GetFPComparison(context, cmpOp, srcA[0], srcB[0]); + Operand p1Res = GetFPComparison(context, cmpOp, srcA[1], srcB[1]); + + if (hAnd) + { + p0Res = context.BitwiseAnd(p0Res, p1Res); + p1Res = context.BitwiseNot(p0Res); + } + + p0Res = GetPredLogicalOp(context, op.LogicalOp, p0Res, pred); + p1Res = GetPredLogicalOp(context, op.LogicalOp, p1Res, pred); + + context.Copy(Register(op.Predicate3), p0Res); + context.Copy(Register(op.Predicate0), p1Res); + } + public static void Mufu(EmitterContext context) { IOpCodeFArith op = (IOpCodeFArith)context.CurrOp; @@ -346,14 +379,14 @@ namespace Ryujinx.Graphics.Shader.Instructions { IOpCodeHfma op = (IOpCodeHfma)context.CurrOp; - return GetHalfSources(context, GetSrcA(context), op.SwizzleA); + return GetHalfUnpacked(context, GetSrcA(context), op.SwizzleA); } private static Operand[] GetHfmaSrcB(EmitterContext context) { IOpCodeHfma op = (IOpCodeHfma)context.CurrOp; - Operand[] operands = GetHalfSources(context, GetSrcB(context), op.SwizzleB); + Operand[] operands = GetHalfUnpacked(context, GetSrcB(context), op.SwizzleB); return FPAbsNeg(context, operands, false, op.NegateB); } @@ -362,7 +395,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { IOpCodeHfma op = (IOpCodeHfma)context.CurrOp; - Operand[] operands = GetHalfSources(context, GetSrcC(context), op.SwizzleC); + Operand[] operands = GetHalfUnpacked(context, GetSrcC(context), op.SwizzleC); return FPAbsNeg(context, operands, false, op.NegateC); } diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitFlow.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitFlow.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitFlow.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitFlow.cs diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitHelper.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs similarity index 95% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitHelper.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs index c87e1789d..c0a3012a8 100644 --- a/Ryujinx.Graphics/Shader/Instructions/InstEmitHelper.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitHelper.cs @@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { int h = context.CurrOp.RawOpCode.Extract(41, 1); - return GetHalfSources(context, GetSrcB(context), FPHalfSwizzle.FP16)[h]; + return GetHalfUnpacked(context, GetSrcB(context), FPHalfSwizzle.FP16)[h]; } else if (floatType == FPType.FP64) { @@ -118,7 +118,7 @@ namespace Ryujinx.Graphics.Shader.Instructions FPHalfSwizzle swizzle = (FPHalfSwizzle)op.RawOpCode.Extract(47, 2); - Operand[] operands = GetHalfSources(context, GetSrcA(context), swizzle); + Operand[] operands = GetHalfUnpacked(context, GetSrcA(context), swizzle); return FPAbsNeg(context, operands, absoluteA, negateA); } @@ -145,7 +145,7 @@ namespace Ryujinx.Graphics.Shader.Instructions absoluteB = op.RawOpCode.Extract(54); } - Operand[] operands = GetHalfSources(context, GetSrcB(context), swizzle); + Operand[] operands = GetHalfUnpacked(context, GetSrcB(context), swizzle); return FPAbsNeg(context, operands, absoluteB, negateB); } @@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.Instructions return operands; } - public static Operand[] GetHalfSources(EmitterContext context, Operand src, FPHalfSwizzle swizzle) + public static Operand[] GetHalfUnpacked(EmitterContext context, Operand src, FPHalfSwizzle swizzle) { switch (swizzle) { diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs new file mode 100644 index 000000000..ee210f22e --- /dev/null +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMemory.cs @@ -0,0 +1,325 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Ald(EmitterContext context) + { + OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; + + Operand primVertex = context.Copy(GetSrcC(context)); + + for (int index = 0; index < op.Count; index++) + { + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + if (rd.IsRZ) + { + break; + } + + Operand src = Attribute(op.AttributeOffset + index * 4); + + context.Copy(Register(rd), context.LoadAttribute(src, primVertex)); + } + } + + public static void Ast(EmitterContext context) + { + OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; + + for (int index = 0; index < op.Count; index++) + { + if (op.Rd.Index + index > RegisterConsts.RegisterZeroIndex) + { + break; + } + + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + Operand dest = Attribute(op.AttributeOffset + index * 4); + + context.Copy(dest, Register(rd)); + } + } + + public static void Ipa(EmitterContext context) + { + OpCodeIpa op = (OpCodeIpa)context.CurrOp; + + InterpolationQualifier iq = InterpolationQualifier.None; + + switch (op.Mode) + { + case InterpolationMode.Pass: iq = InterpolationQualifier.NoPerspective; break; + } + + Operand srcA = Attribute(op.AttributeOffset, iq); + + Operand srcB = GetSrcB(context); + + Operand res = context.FPSaturate(srcA, op.Saturate); + + context.Copy(GetDest(context), res); + } + + public static void Isberd(EmitterContext context) + { + // This instruction performs a load from ISBE memory, + // however it seems to be only used to get some vertex + // input data, so we instead propagate the offset so that + // it can be used on the attribute load. + context.Copy(GetDest(context), GetSrcA(context)); + } + + public static void Ld(EmitterContext context) + { + LoadLocalOrGlobal(context, isGlobal: false); + } + + public static void Ldc(EmitterContext context) + { + OpCodeLdc op = (OpCodeLdc)context.CurrOp; + + if (op.Size > IntegerSize.B64) + { + // TODO: Warning. + } + + bool isSmallInt = op.Size < IntegerSize.B32; + + int count = op.Size == IntegerSize.B64 ? 2 : 1; + + Operand wordOffset = context.ShiftRightU32(GetSrcA(context), Const(2)); + + wordOffset = context.IAdd(wordOffset, Const(op.Offset)); + + Operand bitOffset = GetBitOffset(context, GetSrcA(context)); + + for (int index = 0; index < count; index++) + { + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + if (rd.IsRZ) + { + break; + } + + Operand offset = context.IAdd(wordOffset, Const(index)); + + Operand value = context.LoadConstant(Const(op.Slot), offset); + + if (isSmallInt) + { + value = ExtractSmallInt(context, op.Size, wordOffset, value); + } + + context.Copy(Register(rd), value); + } + } + + public static void Ldg(EmitterContext context) + { + LoadLocalOrGlobal(context, isGlobal: true); + } + + public static void Out(EmitterContext context) + { + OpCode op = context.CurrOp; + + bool emit = op.RawOpCode.Extract(39); + bool cut = op.RawOpCode.Extract(40); + + if (!(emit || cut)) + { + // TODO: Warning. + } + + if (emit) + { + context.EmitVertex(); + } + + if (cut) + { + context.EndPrimitive(); + } + } + + public static void St(EmitterContext context) + { + StoreLocalOrGlobal(context, isGlobal: false); + } + + public static void Stg(EmitterContext context) + { + StoreLocalOrGlobal(context, isGlobal: true); + } + + private static void LoadLocalOrGlobal(EmitterContext context, bool isGlobal) + { + OpCodeMemory op = (OpCodeMemory)context.CurrOp; + + if (op.Size > IntegerSize.B128) + { + // TODO: Warning. + } + + bool isSmallInt = op.Size < IntegerSize.B32; + + int count = 1; + + switch (op.Size) + { + case IntegerSize.B64: count = 2; break; + case IntegerSize.B128: count = 4; break; + } + + Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset)); + + // Word offset = byte offset / 4 (one word = 4 bytes). + Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2)); + + Operand bitOffset = GetBitOffset(context, baseOffset); + + for (int index = 0; index < count; index++) + { + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + if (rd.IsRZ) + { + break; + } + + Operand offset = context.IAdd(wordOffset, Const(index)); + + Operand value = isGlobal + ? context.LoadGlobal(offset) + : context.LoadLocal (offset); + + if (isSmallInt) + { + value = ExtractSmallInt(context, op.Size, bitOffset, value); + } + + context.Copy(Register(rd), value); + } + } + + private static void StoreLocalOrGlobal(EmitterContext context, bool isGlobal) + { + OpCodeMemory op = (OpCodeMemory)context.CurrOp; + + if (op.Size > IntegerSize.B128) + { + // TODO: Warning. + } + + bool isSmallInt = op.Size < IntegerSize.B32; + + int count = 1; + + switch (op.Size) + { + case IntegerSize.B64: count = 2; break; + case IntegerSize.B128: count = 4; break; + } + + Operand baseOffset = context.IAdd(GetSrcA(context), Const(op.Offset)); + + Operand wordOffset = context.ShiftRightU32(baseOffset, Const(2)); + + Operand bitOffset = GetBitOffset(context, baseOffset); + + for (int index = 0; index < count; index++) + { + Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); + + if (rd.IsRZ) + { + break; + } + + Operand value = Register(rd); + + Operand offset = context.IAdd(wordOffset, Const(index)); + + if (isSmallInt) + { + Operand word = isGlobal + ? context.LoadGlobal(offset) + : context.LoadLocal (offset); + + value = InsertSmallInt(context, op.Size, bitOffset, word, value); + } + + if (isGlobal) + { + context.StoreGlobal(offset, value); + } + else + { + context.StoreLocal(offset, value); + } + } + } + + private static Operand GetBitOffset(EmitterContext context, Operand baseOffset) + { + // Note: byte offset = (baseOffset & 0b11) * 8. + // Addresses should be always aligned to the integer type, + // so we don't need to take unaligned addresses into account. + return context.ShiftLeft(context.BitwiseAnd(baseOffset, Const(3)), Const(3)); + } + + private static Operand ExtractSmallInt( + EmitterContext context, + IntegerSize size, + Operand bitOffset, + Operand value) + { + value = context.ShiftRightU32(value, bitOffset); + + switch (size) + { + case IntegerSize.U8: value = ZeroExtendTo32(context, value, 8); break; + case IntegerSize.U16: value = ZeroExtendTo32(context, value, 16); break; + case IntegerSize.S8: value = SignExtendTo32(context, value, 8); break; + case IntegerSize.S16: value = SignExtendTo32(context, value, 16); break; + } + + return value; + } + + private static Operand InsertSmallInt( + EmitterContext context, + IntegerSize size, + Operand bitOffset, + Operand word, + Operand value) + { + switch (size) + { + case IntegerSize.U8: + case IntegerSize.S8: + value = context.BitwiseAnd(value, Const(0xff)); + value = context.BitfieldInsert(word, value, bitOffset, Const(8)); + break; + + case IntegerSize.U16: + case IntegerSize.S16: + value = context.BitwiseAnd(value, Const(0xffff)); + value = context.BitfieldInsert(word, value, bitOffset, Const(16)); + break; + } + + return value; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs new file mode 100644 index 000000000..f66ebc9fa --- /dev/null +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitMove.cs @@ -0,0 +1,57 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Mov(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + context.Copy(GetDest(context), GetSrcB(context)); + } + + public static void S2r(EmitterContext context) + { + // TODO: Better impl. + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + SystemRegister sysReg = (SystemRegister)op.RawOpCode.Extract(20, 8); + + Operand src; + + switch (sysReg) + { + case SystemRegister.ThreadIdX: src = Attribute(AttributeConsts.ThreadIdX); break; + case SystemRegister.ThreadIdY: src = Attribute(AttributeConsts.ThreadIdY); break; + case SystemRegister.ThreadIdZ: src = Attribute(AttributeConsts.ThreadIdZ); break; + case SystemRegister.CtaIdX: src = Attribute(AttributeConsts.CtaIdX); break; + case SystemRegister.CtaIdY: src = Attribute(AttributeConsts.CtaIdY); break; + case SystemRegister.CtaIdZ: src = Attribute(AttributeConsts.CtaIdZ); break; + + default: src = Const(0); break; + } + + context.Copy(GetDest(context), src); + } + + public static void Sel(EmitterContext context) + { + OpCodeAlu op = (OpCodeAlu)context.CurrOp; + + Operand pred = GetPredicate39(context); + + Operand srcA = GetSrcA(context); + Operand srcB = GetSrcB(context); + + Operand res = context.ConditionalSelect(pred, srcA, srcB); + + context.Copy(GetDest(context), res); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs similarity index 73% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs index a9b29f40e..554deb7ba 100644 --- a/Ryujinx.Graphics/Shader/Instructions/InstEmitTexture.cs +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitTexture.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Tex(context, TextureFlags.None); } - public static void Tex_B(EmitterContext context) + public static void TexB(EmitterContext context) { Tex(context, TextureFlags.Bindless); } @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Tex(context, TextureFlags.IntCoords); } - public static void Tld_B(EmitterContext context) + public static void TldB(EmitterContext context) { Tex(context, TextureFlags.IntCoords | TextureFlags.Bindless); } @@ -74,15 +74,15 @@ namespace Ryujinx.Graphics.Shader.Instructions } } - TextureType type; + TextureTarget type; TextureFlags flags; if (op is OpCodeTexs texsOp) { - type = GetTextureType (texsOp.Type); - flags = GetTextureFlags(texsOp.Type); + type = GetTextureType (texsOp.Target); + flags = GetTextureFlags(texsOp.Target); - if ((type & TextureType.Array) != 0) + if ((type & TextureTarget.Array) != 0) { Operand arrayIndex = Ra(); @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(arrayIndex); - if ((type & TextureType.Shadow) != 0) + if ((type & TextureTarget.Shadow) != 0) { sourcesList.Add(Rb()); } @@ -103,42 +103,42 @@ namespace Ryujinx.Graphics.Shader.Instructions } else { - switch (texsOp.Type) + switch (texsOp.Target) { - case TextureScalarType.Texture1DLodZero: + case Decoders.TextureTarget.Texture1DLodZero: sourcesList.Add(Ra()); break; - case TextureScalarType.Texture2D: + case Decoders.TextureTarget.Texture2D: sourcesList.Add(Ra()); sourcesList.Add(Rb()); break; - case TextureScalarType.Texture2DLodZero: + case Decoders.TextureTarget.Texture2DLodZero: sourcesList.Add(Ra()); sourcesList.Add(Rb()); sourcesList.Add(ConstF(0)); break; - case TextureScalarType.Texture2DLodLevel: - case TextureScalarType.Texture2DDepthCompare: - case TextureScalarType.Texture3D: - case TextureScalarType.TextureCube: + case Decoders.TextureTarget.Texture2DLodLevel: + case Decoders.TextureTarget.Texture2DDepthCompare: + case Decoders.TextureTarget.Texture3D: + case Decoders.TextureTarget.TextureCube: sourcesList.Add(Ra()); sourcesList.Add(Ra()); sourcesList.Add(Rb()); break; - case TextureScalarType.Texture2DLodZeroDepthCompare: - case TextureScalarType.Texture3DLodZero: + case Decoders.TextureTarget.Texture2DLodZeroDepthCompare: + case Decoders.TextureTarget.Texture3DLodZero: sourcesList.Add(Ra()); sourcesList.Add(Ra()); sourcesList.Add(Rb()); sourcesList.Add(ConstF(0)); break; - case TextureScalarType.Texture2DLodLevelDepthCompare: - case TextureScalarType.TextureCubeLodLevel: + case Decoders.TextureTarget.Texture2DLodLevelDepthCompare: + case Decoders.TextureTarget.TextureCubeLodLevel: sourcesList.Add(Ra()); sourcesList.Add(Ra()); sourcesList.Add(Rb()); @@ -149,49 +149,49 @@ namespace Ryujinx.Graphics.Shader.Instructions } else if (op is OpCodeTlds tldsOp) { - type = GetTextureType (tldsOp.Type); - flags = GetTextureFlags(tldsOp.Type) | TextureFlags.IntCoords; + type = GetTextureType (tldsOp.Target); + flags = GetTextureFlags(tldsOp.Target) | TextureFlags.IntCoords; - switch (tldsOp.Type) + switch (tldsOp.Target) { - case TexelLoadScalarType.Texture1DLodZero: + case TexelLoadTarget.Texture1DLodZero: sourcesList.Add(Ra()); sourcesList.Add(Const(0)); break; - case TexelLoadScalarType.Texture1DLodLevel: + case TexelLoadTarget.Texture1DLodLevel: sourcesList.Add(Ra()); sourcesList.Add(Rb()); break; - case TexelLoadScalarType.Texture2DLodZero: + case TexelLoadTarget.Texture2DLodZero: sourcesList.Add(Ra()); sourcesList.Add(Rb()); sourcesList.Add(Const(0)); break; - case TexelLoadScalarType.Texture2DLodZeroOffset: + case TexelLoadTarget.Texture2DLodZeroOffset: sourcesList.Add(Ra()); sourcesList.Add(Ra()); sourcesList.Add(Const(0)); break; - case TexelLoadScalarType.Texture2DLodZeroMultisample: - case TexelLoadScalarType.Texture2DLodLevel: - case TexelLoadScalarType.Texture2DLodLevelOffset: + case TexelLoadTarget.Texture2DLodZeroMultisample: + case TexelLoadTarget.Texture2DLodLevel: + case TexelLoadTarget.Texture2DLodLevelOffset: sourcesList.Add(Ra()); sourcesList.Add(Ra()); sourcesList.Add(Rb()); break; - case TexelLoadScalarType.Texture3DLodZero: + case TexelLoadTarget.Texture3DLodZero: sourcesList.Add(Ra()); sourcesList.Add(Ra()); sourcesList.Add(Rb()); sourcesList.Add(Const(0)); break; - case TexelLoadScalarType.Texture2DArrayLodZero: + case TexelLoadTarget.Texture2DArrayLodZero: sourcesList.Add(Rb()); sourcesList.Add(Rb()); sourcesList.Add(Ra()); @@ -201,7 +201,7 @@ namespace Ryujinx.Graphics.Shader.Instructions if ((flags & TextureFlags.Offset) != 0) { - AddTextureOffset(type.GetCoordsCount(), 4, 4); + AddTextureOffset(type.GetDimensions(), 4, 4); } } else if (op is OpCodeTld4s tld4sOp) @@ -217,19 +217,19 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(Ra()); } - type = TextureType.Texture2D; + type = TextureTarget.Texture2D; flags = TextureFlags.Gather; if (tld4sOp.HasDepthCompare) { sourcesList.Add(Rb()); - type |= TextureType.Shadow; + type |= TextureTarget.Shadow; } if (tld4sOp.HasOffset) { - AddTextureOffset(type.GetCoordsCount(), 8, 6); + AddTextureOffset(type.GetDimensions(), 8, 6); flags |= TextureFlags.Offset; } @@ -338,11 +338,11 @@ namespace Ryujinx.Graphics.Shader.Instructions List sourcesList = new List(); - TextureType type = GetTextureType(op.Dimensions); + TextureTarget type = GetTextureType(op.Dimensions); TextureFlags flags = TextureFlags.Gather; - int coordsCount = type.GetCoordsCount(); + int coordsCount = type.GetDimensions(); for (int index = 0; index < coordsCount; index++) { @@ -353,7 +353,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { sourcesList.Add(arrayIndex); - type |= TextureType.Array; + type |= TextureTarget.Array; } Operand[] packedOffs = new Operand[2]; @@ -365,7 +365,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { sourcesList.Add(Rb()); - type |= TextureType.Shadow; + type |= TextureTarget.Shadow; } if (op.Offset != TextureGatherOffset.None) @@ -427,7 +427,7 @@ namespace Ryujinx.Graphics.Shader.Instructions Txq(context, bindless: false); } - public static void Txq_B(EmitterContext context) + public static void TxqB(EmitterContext context) { Txq(context, bindless: true); } @@ -446,7 +446,7 @@ namespace Ryujinx.Graphics.Shader.Instructions // TODO: Validate and use property. Instruction inst = Instruction.TextureSize; - TextureType type = TextureType.Texture2D; + TextureTarget type = TextureTarget.Texture2D; TextureFlags flags = bindless ? TextureFlags.Bindless : TextureFlags.None; @@ -551,9 +551,9 @@ namespace Ryujinx.Graphics.Shader.Instructions sourcesList.Add(Rb()); } - TextureType type = GetTextureType(op.Dimensions); + TextureTarget type = GetTextureType(op.Dimensions); - int coordsCount = type.GetCoordsCount(); + int coordsCount = type.GetDimensions(); for (int index = 0; index < coordsCount; index++) { @@ -564,7 +564,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { sourcesList.Add(arrayIndex); - type |= TextureType.Array; + type |= TextureTarget.Array; } bool hasLod = op.LodMode > TextureLodMode.LodZero; @@ -577,7 +577,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { sourcesList.Add(Rb()); - type |= TextureType.Shadow; + type |= TextureTarget.Shadow; } if ((op.LodMode == TextureLodMode.LodZero || @@ -611,7 +611,7 @@ namespace Ryujinx.Graphics.Shader.Instructions { sourcesList.Add(Rb()); - type |= TextureType.Multisample; + type |= TextureTarget.Multisample; } Operand[] sources = sourcesList.ToArray(); @@ -650,123 +650,123 @@ namespace Ryujinx.Graphics.Shader.Instructions } } - private static TextureType GetTextureType(TextureDimensions dimensions) + private static TextureTarget GetTextureType(TextureDimensions dimensions) { switch (dimensions) { - case TextureDimensions.Texture1D: return TextureType.Texture1D; - case TextureDimensions.Texture2D: return TextureType.Texture2D; - case TextureDimensions.Texture3D: return TextureType.Texture3D; - case TextureDimensions.TextureCube: return TextureType.TextureCube; + case TextureDimensions.Texture1D: return TextureTarget.Texture1D; + case TextureDimensions.Texture2D: return TextureTarget.Texture2D; + case TextureDimensions.Texture3D: return TextureTarget.Texture3D; + case TextureDimensions.TextureCube: return TextureTarget.TextureCube; } throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\"."); } - private static TextureType GetTextureType(TextureScalarType type) + private static TextureTarget GetTextureType(Decoders.TextureTarget type) { switch (type) { - case TextureScalarType.Texture1DLodZero: - return TextureType.Texture1D; + case Decoders.TextureTarget.Texture1DLodZero: + return TextureTarget.Texture1D; - case TextureScalarType.Texture2D: - case TextureScalarType.Texture2DLodZero: - case TextureScalarType.Texture2DLodLevel: - return TextureType.Texture2D; + case Decoders.TextureTarget.Texture2D: + case Decoders.TextureTarget.Texture2DLodZero: + case Decoders.TextureTarget.Texture2DLodLevel: + return TextureTarget.Texture2D; - case TextureScalarType.Texture2DDepthCompare: - case TextureScalarType.Texture2DLodLevelDepthCompare: - case TextureScalarType.Texture2DLodZeroDepthCompare: - return TextureType.Texture2D | TextureType.Shadow; + case Decoders.TextureTarget.Texture2DDepthCompare: + case Decoders.TextureTarget.Texture2DLodLevelDepthCompare: + case Decoders.TextureTarget.Texture2DLodZeroDepthCompare: + return TextureTarget.Texture2D | TextureTarget.Shadow; - case TextureScalarType.Texture2DArray: - case TextureScalarType.Texture2DArrayLodZero: - return TextureType.Texture2D | TextureType.Array; + case Decoders.TextureTarget.Texture2DArray: + case Decoders.TextureTarget.Texture2DArrayLodZero: + return TextureTarget.Texture2D | TextureTarget.Array; - case TextureScalarType.Texture2DArrayLodZeroDepthCompare: - return TextureType.Texture2D | TextureType.Array | TextureType.Shadow; + case Decoders.TextureTarget.Texture2DArrayLodZeroDepthCompare: + return TextureTarget.Texture2D | TextureTarget.Array | TextureTarget.Shadow; - case TextureScalarType.Texture3D: - case TextureScalarType.Texture3DLodZero: - return TextureType.Texture3D; + case Decoders.TextureTarget.Texture3D: + case Decoders.TextureTarget.Texture3DLodZero: + return TextureTarget.Texture3D; - case TextureScalarType.TextureCube: - case TextureScalarType.TextureCubeLodLevel: - return TextureType.TextureCube; + case Decoders.TextureTarget.TextureCube: + case Decoders.TextureTarget.TextureCubeLodLevel: + return TextureTarget.TextureCube; } throw new ArgumentException($"Invalid texture type \"{type}\"."); } - private static TextureType GetTextureType(TexelLoadScalarType type) + private static TextureTarget GetTextureType(TexelLoadTarget type) { switch (type) { - case TexelLoadScalarType.Texture1DLodZero: - case TexelLoadScalarType.Texture1DLodLevel: - return TextureType.Texture1D; + case TexelLoadTarget.Texture1DLodZero: + case TexelLoadTarget.Texture1DLodLevel: + return TextureTarget.Texture1D; - case TexelLoadScalarType.Texture2DLodZero: - case TexelLoadScalarType.Texture2DLodZeroOffset: - case TexelLoadScalarType.Texture2DLodLevel: - case TexelLoadScalarType.Texture2DLodLevelOffset: - return TextureType.Texture2D; + case TexelLoadTarget.Texture2DLodZero: + case TexelLoadTarget.Texture2DLodZeroOffset: + case TexelLoadTarget.Texture2DLodLevel: + case TexelLoadTarget.Texture2DLodLevelOffset: + return TextureTarget.Texture2D; - case TexelLoadScalarType.Texture2DLodZeroMultisample: - return TextureType.Texture2D | TextureType.Multisample; + case TexelLoadTarget.Texture2DLodZeroMultisample: + return TextureTarget.Texture2D | TextureTarget.Multisample; - case TexelLoadScalarType.Texture3DLodZero: - return TextureType.Texture3D; + case TexelLoadTarget.Texture3DLodZero: + return TextureTarget.Texture3D; - case TexelLoadScalarType.Texture2DArrayLodZero: - return TextureType.Texture2D | TextureType.Array; + case TexelLoadTarget.Texture2DArrayLodZero: + return TextureTarget.Texture2D | TextureTarget.Array; } throw new ArgumentException($"Invalid texture type \"{type}\"."); } - private static TextureFlags GetTextureFlags(TextureScalarType type) + private static TextureFlags GetTextureFlags(Decoders.TextureTarget type) { switch (type) { - case TextureScalarType.Texture1DLodZero: - case TextureScalarType.Texture2DLodZero: - case TextureScalarType.Texture2DLodLevel: - case TextureScalarType.Texture2DLodLevelDepthCompare: - case TextureScalarType.Texture2DLodZeroDepthCompare: - case TextureScalarType.Texture2DArrayLodZero: - case TextureScalarType.Texture2DArrayLodZeroDepthCompare: - case TextureScalarType.Texture3DLodZero: - case TextureScalarType.TextureCubeLodLevel: + case Decoders.TextureTarget.Texture1DLodZero: + case Decoders.TextureTarget.Texture2DLodZero: + case Decoders.TextureTarget.Texture2DLodLevel: + case Decoders.TextureTarget.Texture2DLodLevelDepthCompare: + case Decoders.TextureTarget.Texture2DLodZeroDepthCompare: + case Decoders.TextureTarget.Texture2DArrayLodZero: + case Decoders.TextureTarget.Texture2DArrayLodZeroDepthCompare: + case Decoders.TextureTarget.Texture3DLodZero: + case Decoders.TextureTarget.TextureCubeLodLevel: return TextureFlags.LodLevel; - case TextureScalarType.Texture2D: - case TextureScalarType.Texture2DDepthCompare: - case TextureScalarType.Texture2DArray: - case TextureScalarType.Texture3D: - case TextureScalarType.TextureCube: + case Decoders.TextureTarget.Texture2D: + case Decoders.TextureTarget.Texture2DDepthCompare: + case Decoders.TextureTarget.Texture2DArray: + case Decoders.TextureTarget.Texture3D: + case Decoders.TextureTarget.TextureCube: return TextureFlags.None; } throw new ArgumentException($"Invalid texture type \"{type}\"."); } - private static TextureFlags GetTextureFlags(TexelLoadScalarType type) + private static TextureFlags GetTextureFlags(TexelLoadTarget type) { switch (type) { - case TexelLoadScalarType.Texture1DLodZero: - case TexelLoadScalarType.Texture1DLodLevel: - case TexelLoadScalarType.Texture2DLodZero: - case TexelLoadScalarType.Texture2DLodLevel: - case TexelLoadScalarType.Texture2DLodZeroMultisample: - case TexelLoadScalarType.Texture3DLodZero: - case TexelLoadScalarType.Texture2DArrayLodZero: + case TexelLoadTarget.Texture1DLodZero: + case TexelLoadTarget.Texture1DLodLevel: + case TexelLoadTarget.Texture2DLodZero: + case TexelLoadTarget.Texture2DLodLevel: + case TexelLoadTarget.Texture2DLodZeroMultisample: + case TexelLoadTarget.Texture3DLodZero: + case TexelLoadTarget.Texture2DArrayLodZero: return TextureFlags.LodLevel; - case TexelLoadScalarType.Texture2DLodZeroOffset: - case TexelLoadScalarType.Texture2DLodLevelOffset: + case TexelLoadTarget.Texture2DLodZeroOffset: + case TexelLoadTarget.Texture2DLodLevelOffset: return TextureFlags.LodLevel | TextureFlags.Offset; } diff --git a/Ryujinx.Graphics.Shader/Instructions/InstEmitVideo.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideo.cs new file mode 100644 index 000000000..b19e9fa13 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Instructions/InstEmitVideo.cs @@ -0,0 +1,19 @@ +using Ryujinx.Graphics.Shader.Decoders; +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; + +using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Instructions +{ + static partial class InstEmit + { + public static void Vmad(EmitterContext context) + { + OpCodeVideo op = (OpCodeVideo)context.CurrOp; + + context.Copy(GetDest(context), GetSrcC(context)); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitter.cs b/Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Instructions/InstEmitter.cs rename to Ryujinx.Graphics.Shader/Instructions/InstEmitter.cs diff --git a/Ryujinx.Graphics/Shader/Instructions/Lop3Expression.cs b/Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Instructions/Lop3Expression.cs rename to Ryujinx.Graphics.Shader/Instructions/Lop3Expression.cs diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/BasicBlock.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs similarity index 100% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/BasicBlock.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/BasicBlock.cs diff --git a/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs new file mode 100644 index 000000000..d4d87b067 --- /dev/null +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/CommentNode.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +{ + class CommentNode : Operation + { + public string Comment { get; } + + public CommentNode(string comment) : base(Instruction.Comment, null) + { + Comment = comment; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/INode.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/INode.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/INode.cs diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs similarity index 95% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs index ac0ebc2b0..e580965e7 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Instruction.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Instruction.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Ceiling, Clamp, ClampU32, + Comment, CompareEqual, CompareGreater, CompareGreaterOrEqual, @@ -45,9 +46,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Floor, FusedMultiplyAdd, IsNan, + LoadAttribute, LoadConstant, LoadGlobal, LoadLocal, + LoadStorage, LogarithmB2, LogicalAnd, LogicalExclusiveOr, @@ -73,6 +76,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation SquareRoot, StoreGlobal, StoreLocal, + StoreStorage, Subtract, TextureSample, TextureSize, diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/IrConsts.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs similarity index 100% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/IrConsts.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/IrConsts.cs diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operand.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs similarity index 86% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/Operand.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs index 1df88a3d9..567277a75 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operand.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operand.cs @@ -14,6 +14,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public int Value { get; } + public InterpolationQualifier Interpolation { get; } + public INode AsgOp { get; set; } public HashSet UseOps { get; } @@ -28,10 +30,11 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Type = type; } - public Operand(OperandType type, int value) : this() + public Operand(OperandType type, int value, InterpolationQualifier iq = InterpolationQualifier.None) : this() { - Type = type; - Value = value; + Type = type; + Value = value; + Interpolation = iq; } public Operand(Register reg) : this() diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandHelper.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs similarity index 88% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandHelper.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs index 6765f8a44..45c9ba1e5 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandHelper.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandHelper.cs @@ -5,9 +5,9 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { static class OperandHelper { - public static Operand Attribute(int value) + public static Operand Attribute(int value, InterpolationQualifier iq = InterpolationQualifier.None) { - return new Operand(OperandType.Attribute, value); + return new Operand(OperandType.Attribute, value, iq); } public static Operand Cbuf(int slot, int offset) diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandType.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs similarity index 84% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandType.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs index e0e2a6675..8f8df9e4f 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/OperandType.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/OperandType.cs @@ -5,9 +5,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation Attribute, Constant, ConstantBuffer, - GlobalMemory, Label, - LocalMemory, LocalVariable, Register, Undefined diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs similarity index 93% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs index c60f393e7..fc01d47ea 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/Operation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/Operation.cs @@ -70,7 +70,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation oldSrc.UseOps.Remove(this); } - if (source.Type == OperandType.LocalVariable) + if (source != null && source.Type == OperandType.LocalVariable) { source.UseOps.Add(this); } @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation foreach (Operand oldSrc in _sources) { - if (oldSrc.Type == OperandType.LocalVariable) + if (oldSrc != null && oldSrc.Type == OperandType.LocalVariable) { oldSrc.UseOps.Remove(this); } diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/PhiNode.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/PhiNode.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/PhiNode.cs diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs similarity index 100% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureFlags.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureFlags.cs diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs similarity index 76% rename from Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs rename to Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs index f5f2cc5c6..0c768345c 100644 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureOperation.cs +++ b/Ryujinx.Graphics.Shader/IntermediateRepresentation/TextureOperation.cs @@ -2,21 +2,21 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation { class TextureOperation : Operation { - public TextureType Type { get; } - public TextureFlags Flags { get; } + public TextureTarget Target { get; } + public TextureFlags Flags { get; } public int Handle { get; } public TextureOperation( Instruction inst, - TextureType type, + TextureTarget target, TextureFlags flags, int handle, int compIndex, Operand dest, params Operand[] sources) : base(inst, compIndex, dest, sources) { - Type = type; + Target = target; Flags = flags; Handle = handle; } diff --git a/Ryujinx.Graphics.Shader/InterpolationQualifier.cs b/Ryujinx.Graphics.Shader/InterpolationQualifier.cs new file mode 100644 index 000000000..e710427dd --- /dev/null +++ b/Ryujinx.Graphics.Shader/InterpolationQualifier.cs @@ -0,0 +1,45 @@ +using System; + +namespace Ryujinx.Graphics.Shader +{ + [Flags] + public enum InterpolationQualifier + { + None = 0, + + Flat = 1, + NoPerspective = 2, + Smooth = 3, + + Centroid = 1 << 16, + Sample = 1 << 17, + + FlagsMask = Centroid | Sample + } + + public static class InterpolationQualifierExtensions + { + public static string ToGlslQualifier(this InterpolationQualifier iq) + { + string output = string.Empty; + + switch (iq & ~InterpolationQualifier.FlagsMask) + { + case InterpolationQualifier.Flat: output = "flat"; break; + case InterpolationQualifier.NoPerspective: output = "noperspective"; break; + case InterpolationQualifier.Smooth: output = "smooth"; break; + } + + if ((iq & InterpolationQualifier.Centroid) != 0) + { + output = "centroid " + output; + } + else if ((iq & InterpolationQualifier.Sample) != 0) + { + output = "sample " + output; + } + + return output; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/OutputTopology.cs b/Ryujinx.Graphics.Shader/OutputTopology.cs new file mode 100644 index 000000000..e8336aa3f --- /dev/null +++ b/Ryujinx.Graphics.Shader/OutputTopology.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.Graphics.Shader +{ + enum OutputTopology + { + PointList = 1, + LineStrip = 6, + TriangleStrip = 7 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj new file mode 100644 index 000000000..ea83d2968 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Ryujinx.Graphics.Shader.csproj @@ -0,0 +1,7 @@ + + + + netcoreapp3.0 + + + diff --git a/Ryujinx.Graphics.Shader/ShaderConfig.cs b/Ryujinx.Graphics.Shader/ShaderConfig.cs new file mode 100644 index 000000000..6ab4689a6 --- /dev/null +++ b/Ryujinx.Graphics.Shader/ShaderConfig.cs @@ -0,0 +1,30 @@ +using Ryujinx.Graphics.Shader.Translation; + +namespace Ryujinx.Graphics.Shader +{ + struct ShaderConfig + { + public ShaderStage Stage { get; } + + public TranslationFlags Flags { get; } + + public int MaxCBufferSize { get; } + public int MaxOutputVertices { get; } + + public OutputTopology OutputTopology { get; } + + public ShaderConfig( + ShaderStage stage, + TranslationFlags flags, + int maxCBufferSize, + int maxOutputVertices, + OutputTopology outputTopology) + { + Stage = stage; + Flags = flags; + MaxCBufferSize = maxCBufferSize; + MaxOutputVertices = maxOutputVertices; + OutputTopology = outputTopology; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderHeader.cs b/Ryujinx.Graphics.Shader/ShaderHeader.cs similarity index 80% rename from Ryujinx.Graphics/Shader/ShaderHeader.cs rename to Ryujinx.Graphics.Shader/ShaderHeader.cs index 379f3f35d..94c574359 100644 --- a/Ryujinx.Graphics/Shader/ShaderHeader.cs +++ b/Ryujinx.Graphics.Shader/ShaderHeader.cs @@ -1,6 +1,6 @@ -using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Shader.Decoders; using System; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Shader { @@ -38,10 +38,9 @@ namespace Ryujinx.Graphics.Shader class ShaderHeader { public int SphType { get; } - public int Version { get; } - public int ShaderType { get; } + public ShaderStage Stage { get; } public bool MrtEnable { get; } @@ -52,10 +51,9 @@ namespace Ryujinx.Graphics.Shader public int SassVersion { get; } public bool DoesLoadOrStore { get; } + public bool DoesFp64 { get; } - public bool DoesFp64 { get; } - - public int StreamOutMask{ get; } + public int StreamOutMask { get; } public int ShaderLocalMemoryLowSize { get; } @@ -67,7 +65,7 @@ namespace Ryujinx.Graphics.Shader public int ShaderLocalMemoryCrsSize { get; } - public int OutputTopology { get; } + public OutputTopology OutputTopology { get; } public int MaxOutputVertexCount { get; } @@ -78,69 +76,6 @@ namespace Ryujinx.Graphics.Shader public bool OmapSampleMask { get; } public bool OmapDepth { get; } - public ShaderHeader(IGalMemory memory, ulong address) - { - int commonWord0 = memory.ReadInt32((long)address + 0); - int commonWord1 = memory.ReadInt32((long)address + 4); - int commonWord2 = memory.ReadInt32((long)address + 8); - int commonWord3 = memory.ReadInt32((long)address + 12); - int commonWord4 = memory.ReadInt32((long)address + 16); - - SphType = commonWord0.Extract(0, 5); - - Version = commonWord0.Extract(5, 5); - - ShaderType = commonWord0.Extract(10, 4); - - MrtEnable = commonWord0.Extract(14); - - KillsPixels = commonWord0.Extract(15); - - DoesGlobalStore = commonWord0.Extract(16); - - SassVersion = commonWord0.Extract(17, 4); - - DoesLoadOrStore = commonWord0.Extract(26); - - DoesFp64 = commonWord0.Extract(27); - - StreamOutMask = commonWord0.Extract(28, 4); - - ShaderLocalMemoryLowSize = commonWord1.Extract(0, 24); - - PerPatchAttributeCount = commonWord1.Extract(24, 8); - - ShaderLocalMemoryHighSize = commonWord2.Extract(0, 24); - - ThreadsPerInputPrimitive = commonWord2.Extract(24, 8); - - ShaderLocalMemoryCrsSize = commonWord3.Extract(0, 24); - - OutputTopology = commonWord3.Extract(24, 4); - - MaxOutputVertexCount = commonWord4.Extract(0, 12); - - StoreReqStart = commonWord4.Extract(12, 8); - StoreReqEnd = commonWord4.Extract(24, 8); - - int type2OmapTarget = memory.ReadInt32((long)address + 72); - int type2Omap = memory.ReadInt32((long)address + 76); - - OmapTargets = new OutputMapTarget[8]; - - for (int offset = 0; offset < OmapTargets.Length * 4; offset += 4) - { - OmapTargets[offset >> 2] = new OutputMapTarget( - type2OmapTarget.Extract(offset + 0), - type2OmapTarget.Extract(offset + 1), - type2OmapTarget.Extract(offset + 2), - type2OmapTarget.Extract(offset + 3)); - } - - OmapSampleMask = type2Omap.Extract(0); - OmapDepth = type2Omap.Extract(1); - } - public int DepthRegister { get @@ -162,5 +97,74 @@ namespace Ryujinx.Graphics.Shader return count + 1; } } + + public ShaderHeader(Span code) + { + Span header = MemoryMarshal.Cast(code); + + int commonWord0 = header[0]; + int commonWord1 = header[1]; + int commonWord2 = header[2]; + int commonWord3 = header[3]; + int commonWord4 = header[4]; + + SphType = commonWord0.Extract(0, 5); + Version = commonWord0.Extract(5, 5); + + Stage = (ShaderStage)commonWord0.Extract(10, 4); + + // Invalid. + if (Stage == ShaderStage.Compute) + { + Stage = ShaderStage.Vertex; + } + + MrtEnable = commonWord0.Extract(14); + + KillsPixels = commonWord0.Extract(15); + + DoesGlobalStore = commonWord0.Extract(16); + + SassVersion = commonWord0.Extract(17, 4); + + DoesLoadOrStore = commonWord0.Extract(26); + DoesFp64 = commonWord0.Extract(27); + + StreamOutMask = commonWord0.Extract(28, 4); + + ShaderLocalMemoryLowSize = commonWord1.Extract(0, 24); + + PerPatchAttributeCount = commonWord1.Extract(24, 8); + + ShaderLocalMemoryHighSize = commonWord2.Extract(0, 24); + + ThreadsPerInputPrimitive = commonWord2.Extract(24, 8); + + ShaderLocalMemoryCrsSize = commonWord3.Extract(0, 24); + + OutputTopology = (OutputTopology)commonWord3.Extract(24, 4); + + MaxOutputVertexCount = commonWord4.Extract(0, 12); + + StoreReqStart = commonWord4.Extract(12, 8); + StoreReqEnd = commonWord4.Extract(24, 8); + + int type2OmapTarget = header[18]; + int type2Omap = header[19]; + + OmapTargets = new OutputMapTarget[8]; + + for (int offset = 0; offset < OmapTargets.Length * 4; offset += 4) + { + OmapTargets[offset >> 2] = new OutputMapTarget( + type2OmapTarget.Extract(offset + 0), + type2OmapTarget.Extract(offset + 1), + type2OmapTarget.Extract(offset + 2), + type2OmapTarget.Extract(offset + 3)); + } + + OmapSampleMask = type2Omap.Extract(0); + OmapDepth = type2Omap.Extract(1); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderProgram.cs b/Ryujinx.Graphics.Shader/ShaderProgram.cs new file mode 100644 index 000000000..52c2d55b8 --- /dev/null +++ b/Ryujinx.Graphics.Shader/ShaderProgram.cs @@ -0,0 +1,23 @@ +namespace Ryujinx.Graphics.Shader +{ + public class ShaderProgram + { + public ShaderProgramInfo Info { get; } + + public ShaderStage Stage { get; } + + public string Code { get; private set; } + + internal ShaderProgram(ShaderProgramInfo info, ShaderStage stage, string code) + { + Info = info; + Stage = stage; + Code = code; + } + + public void Replace(string name, string value) + { + Code = Code.Replace(name, value); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs new file mode 100644 index 000000000..841e636dd --- /dev/null +++ b/Ryujinx.Graphics.Shader/ShaderProgramInfo.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.ObjectModel; + +namespace Ryujinx.Graphics.Shader +{ + public class ShaderProgramInfo + { + public ReadOnlyCollection CBuffers { get; } + public ReadOnlyCollection SBuffers { get; } + public ReadOnlyCollection Textures { get; } + + public ReadOnlyCollection InterpolationQualifiers { get; } + + public bool UsesInstanceId { get; } + + internal ShaderProgramInfo( + BufferDescriptor[] cBuffers, + BufferDescriptor[] sBuffers, + TextureDescriptor[] textures, + InterpolationQualifier[] interpolationQualifiers, + bool usesInstanceId) + { + CBuffers = Array.AsReadOnly(cBuffers); + SBuffers = Array.AsReadOnly(sBuffers); + Textures = Array.AsReadOnly(textures); + + InterpolationQualifiers = Array.AsReadOnly(interpolationQualifiers); + + UsesInstanceId = usesInstanceId; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/ShaderStage.cs b/Ryujinx.Graphics.Shader/ShaderStage.cs new file mode 100644 index 000000000..30b65348e --- /dev/null +++ b/Ryujinx.Graphics.Shader/ShaderStage.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader +{ + public enum ShaderStage + { + Compute, + Vertex, + TessellationControl, + TessellationEvaluation, + Geometry, + Fragment + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstAssignment.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/AstAssignment.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstAssignment.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstBlock.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/AstBlock.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstBlock.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstBlockType.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/AstBlockType.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstBlockType.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstBlockVisitor.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/AstBlockVisitor.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstBlockVisitor.cs diff --git a/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs new file mode 100644 index 000000000..dabe623fd --- /dev/null +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstComment.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.Graphics.Shader.StructuredIr +{ + class AstComment : AstNode + { + public string Comment { get; } + + public AstComment(string comment) + { + Comment = comment; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstHelper.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/AstHelper.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstHelper.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstNode.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/AstNode.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstNode.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstOperand.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs similarity index 88% rename from Ryujinx.Graphics/Shader/StructuredIr/AstOperand.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs index 97ff3ca97..25b09636f 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/AstOperand.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstOperand.cs @@ -12,6 +12,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public VariableType VarType { get; set; } + public InterpolationQualifier Interpolation { get; } + public int Value { get; } public int CbufSlot { get; } @@ -27,7 +29,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public AstOperand(Operand operand) : this() { - Type = operand.Type; + Type = operand.Type; + Interpolation = operand.Interpolation; if (Type == OperandType.ConstantBuffer) { diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/AstOperation.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstOperation.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstOptimizer.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs similarity index 75% rename from Ryujinx.Graphics/Shader/StructuredIr/AstOptimizer.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs index 0f5392b7d..a37e1a3e8 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/AstOptimizer.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstOptimizer.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; using System.Collections.Generic; using System.Linq; @@ -8,28 +9,33 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { static class AstOptimizer { - public static void Optimize(StructuredProgramInfo info) + public static void Optimize(StructuredProgramContext context) { - AstBlock mainBlock = info.MainBlock; + AstBlock mainBlock = context.Info.MainBlock; - AstBlockVisitor visitor = new AstBlockVisitor(mainBlock); - - foreach (IAstNode node in visitor.Visit()) + // When debug mode is enabled, we disable expression propagation + // (this makes comparison with the disassembly easier). + if ((context.Config.Flags & TranslationFlags.DebugMode) == 0) { - if (node is AstAssignment assignment && assignment.Destination is AstOperand propVar) + AstBlockVisitor visitor = new AstBlockVisitor(mainBlock); + + foreach (IAstNode node in visitor.Visit()) { - bool isWorthPropagating = propVar.Uses.Count == 1 || IsWorthPropagating(assignment.Source); - - if (propVar.Defs.Count == 1 && isWorthPropagating) + if (node is AstAssignment assignment && assignment.Destination is AstOperand propVar) { - PropagateExpression(propVar, assignment.Source); - } + bool isWorthPropagating = propVar.Uses.Count == 1 || IsWorthPropagating(assignment.Source); - if (propVar.Type == OperandType.LocalVariable && propVar.Uses.Count == 0) - { - visitor.Block.Remove(assignment); + if (propVar.Defs.Count == 1 && isWorthPropagating) + { + PropagateExpression(propVar, assignment.Source); + } - info.Locals.Remove(propVar); + if (propVar.Type == OperandType.LocalVariable && propVar.Uses.Count == 0) + { + visitor.Block.Remove(assignment); + + context.Info.Locals.Remove(propVar); + } } } } diff --git a/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs similarity index 77% rename from Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs rename to Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs index e40f7b70e..c9bd57509 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/AstTextureOperation.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/AstTextureOperation.cs @@ -4,20 +4,20 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { class AstTextureOperation : AstOperation { - public TextureType Type { get; } - public TextureFlags Flags { get; } + public TextureTarget Target { get; } + public TextureFlags Flags { get; } public int Handle { get; } public AstTextureOperation( Instruction inst, - TextureType type, + TextureTarget target, TextureFlags flags, int handle, int compMask, params IAstNode[] sources) : base(inst, compMask, sources) { - Type = type; + Target = target; Flags = flags; Handle = handle; } diff --git a/Ryujinx.Graphics/Shader/StructuredIr/GotoElimination.cs b/Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/GotoElimination.cs rename to Ryujinx.Graphics.Shader/StructuredIr/GotoElimination.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/GotoStatement.cs b/Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/GotoStatement.cs rename to Ryujinx.Graphics.Shader/StructuredIr/GotoStatement.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/IAstNode.cs b/Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/IAstNode.cs rename to Ryujinx.Graphics.Shader/StructuredIr/IAstNode.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs similarity index 90% rename from Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs rename to Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs index 46a61553b..cb08a213b 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/InstructionInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/InstructionInfo.cs @@ -24,6 +24,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { _infoTbl = new InstInfo[(int)Instruction.Count]; + // Inst Destination type Source 1 type Source 2 type Source 3 type Source 4 type Add(Instruction.Absolute, VariableType.Scalar, VariableType.Scalar); Add(Instruction.Add, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); Add(Instruction.BitfieldExtractS32, VariableType.S32, VariableType.S32, VariableType.S32, VariableType.S32); @@ -59,7 +60,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.Floor, VariableType.F32, VariableType.F32); Add(Instruction.FusedMultiplyAdd, VariableType.F32, VariableType.F32, VariableType.F32, VariableType.F32); Add(Instruction.IsNan, VariableType.Bool, VariableType.F32); + Add(Instruction.LoadAttribute, VariableType.F32, VariableType.S32, VariableType.S32); Add(Instruction.LoadConstant, VariableType.F32, VariableType.S32, VariableType.S32); + Add(Instruction.LoadGlobal, VariableType.F32, VariableType.S32, VariableType.S32); + Add(Instruction.LoadLocal, VariableType.F32, VariableType.S32); + Add(Instruction.LoadStorage, VariableType.F32, VariableType.S32, VariableType.S32); Add(Instruction.LogarithmB2, VariableType.Scalar, VariableType.Scalar); Add(Instruction.LogicalAnd, VariableType.Bool, VariableType.Bool, VariableType.Bool); Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool); @@ -78,6 +83,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Add(Instruction.ReciprocalSquareRoot, VariableType.Scalar, VariableType.Scalar); Add(Instruction.Sine, VariableType.Scalar, VariableType.Scalar); Add(Instruction.SquareRoot, VariableType.Scalar, VariableType.Scalar); + Add(Instruction.StoreGlobal, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32); + Add(Instruction.StoreLocal, VariableType.None, VariableType.S32, VariableType.F32); + Add(Instruction.StoreStorage, VariableType.None, VariableType.S32, VariableType.S32, VariableType.F32); Add(Instruction.Subtract, VariableType.Scalar, VariableType.Scalar, VariableType.Scalar); Add(Instruction.TextureSample, VariableType.F32); Add(Instruction.TextureSize, VariableType.S32, VariableType.S32, VariableType.S32); diff --git a/Ryujinx.Graphics/Shader/StructuredIr/OperandInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs similarity index 93% rename from Ryujinx.Graphics/Shader/StructuredIr/OperandInfo.cs rename to Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs index a3a8d1383..95c5731a9 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/OperandInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/OperandInfo.cs @@ -24,7 +24,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr case OperandType.Attribute: return VariableType.F32; case OperandType.Constant: return VariableType.S32; case OperandType.ConstantBuffer: return VariableType.F32; - case OperandType.GlobalMemory: return VariableType.F32; case OperandType.Undefined: return VariableType.S32; } diff --git a/Ryujinx.Graphics/Shader/StructuredIr/PhiFunctions.cs b/Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/PhiFunctions.cs rename to Ryujinx.Graphics.Shader/StructuredIr/PhiFunctions.cs diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs similarity index 84% rename from Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs rename to Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs index 26faaf36f..dc8226213 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgram.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgram.cs @@ -6,11 +6,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { static class StructuredProgram { - public static StructuredProgramInfo MakeStructuredProgram(BasicBlock[] blocks) + public static StructuredProgramInfo MakeStructuredProgram(BasicBlock[] blocks, ShaderConfig config) { PhiFunctions.Remove(blocks); - StructuredProgramContext context = new StructuredProgramContext(blocks.Length); + StructuredProgramContext context = new StructuredProgramContext(blocks.Length, config); for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { @@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr GotoElimination.Eliminate(context.GetGotos()); - AstOptimizer.Optimize(context.Info); + AstOptimizer.Optimize(context); return context.Info; } @@ -57,14 +57,25 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (inst == Instruction.LoadConstant) { - Operand ldcSource = operation.GetSource(0); + Operand slot = operation.GetSource(0); - if (ldcSource.Type != OperandType.Constant) + if (slot.Type != OperandType.Constant) { - throw new InvalidOperationException("Found LDC with non-constant constant buffer slot."); + throw new InvalidOperationException("Found load with non-constant constant buffer slot."); } - context.Info.CBuffers.Add(ldcSource.Value); + context.Info.CBuffers.Add(slot.Value); + } + else if (inst == Instruction.LoadStorage) + { + Operand slot = operation.GetSource(0); + + if (slot.Type != OperandType.Constant) + { + throw new InvalidOperationException("Found load or store with non-constant storage buffer slot."); + } + + context.Info.SBuffers.Add(slot.Value); } AstAssignment assignment; @@ -105,7 +116,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr { AstTextureOperation astTexOp = new AstTextureOperation( inst, - texOp.Type, + texOp.Target, texOp.Flags, texOp.Handle, componentMask, @@ -128,8 +139,24 @@ namespace Ryujinx.Graphics.Shader.StructuredIr context.AddNode(assignment); } + else if (operation.Inst == Instruction.Comment) + { + context.AddNode(new AstComment(((CommentNode)operation).Comment)); + } else { + if (inst == Instruction.StoreStorage) + { + Operand slot = operation.GetSource(0); + + if (slot.Type != OperandType.Constant) + { + throw new InvalidOperationException("Found load or store with non-constant storage buffer slot."); + } + + context.Info.SBuffers.Add(slot.Value); + } + context.AddNode(new AstOperation(inst, sources)); } } diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs similarity index 94% rename from Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs rename to Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs index 5d6ff8907..03ff88183 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramContext.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramContext.cs @@ -1,4 +1,5 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using Ryujinx.Graphics.Shader.Translation; using System.Collections.Generic; using System.Linq; @@ -24,7 +25,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public StructuredProgramInfo Info { get; } - public StructuredProgramContext(int blocksCount) + public ShaderConfig Config { get; } + + public StructuredProgramContext(int blocksCount, ShaderConfig config) { _loopTails = new HashSet(); @@ -41,6 +44,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr _currEndIndex = blocksCount; Info = new StructuredProgramInfo(_currBlock); + + Config = config; } public void EnterBlock(BasicBlock block) @@ -231,6 +236,12 @@ namespace Ryujinx.Graphics.Shader.StructuredIr if (TryGetUserAttributeIndex(operand, out int attrIndex)) { Info.IAttributes.Add(attrIndex); + + Info.InterpolationQualifiers[attrIndex] = operand.Interpolation; + } + else if (operand.Type == OperandType.Attribute && operand.Value == AttributeConsts.InstanceId) + { + Info.UsesInstanceId = true; } else if (operand.Type == OperandType.ConstantBuffer) { diff --git a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs similarity index 73% rename from Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs rename to Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs index d368ef005..27fd1487a 100644 --- a/Ryujinx.Graphics/Shader/StructuredIr/StructuredProgramInfo.cs +++ b/Ryujinx.Graphics.Shader/StructuredIr/StructuredProgramInfo.cs @@ -9,10 +9,15 @@ namespace Ryujinx.Graphics.Shader.StructuredIr public HashSet Locals { get; } public HashSet CBuffers { get; } + public HashSet SBuffers { get; } public HashSet IAttributes { get; } public HashSet OAttributes { get; } + public InterpolationQualifier[] InterpolationQualifiers { get; } + + public bool UsesInstanceId { get; set; } + public HashSet Samplers { get; } public StructuredProgramInfo(AstBlock mainBlock) @@ -22,10 +27,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr Locals = new HashSet(); CBuffers = new HashSet(); + SBuffers = new HashSet(); IAttributes = new HashSet(); OAttributes = new HashSet(); + InterpolationQualifiers = new InterpolationQualifier[32]; + Samplers = new HashSet(); } } diff --git a/Ryujinx.Graphics/Shader/StructuredIr/VariableType.cs b/Ryujinx.Graphics.Shader/StructuredIr/VariableType.cs similarity index 100% rename from Ryujinx.Graphics/Shader/StructuredIr/VariableType.cs rename to Ryujinx.Graphics.Shader/StructuredIr/VariableType.cs diff --git a/Ryujinx.Graphics/Shader/TextureDescriptor.cs b/Ryujinx.Graphics.Shader/TextureDescriptor.cs similarity index 68% rename from Ryujinx.Graphics/Shader/TextureDescriptor.cs rename to Ryujinx.Graphics.Shader/TextureDescriptor.cs index 96f0f5b16..484fa1bcc 100644 --- a/Ryujinx.Graphics/Shader/TextureDescriptor.cs +++ b/Ryujinx.Graphics.Shader/TextureDescriptor.cs @@ -4,6 +4,8 @@ namespace Ryujinx.Graphics.Shader { public string Name { get; } + public TextureTarget Target { get; } + public int HandleIndex { get; } public bool IsBindless { get; } @@ -11,9 +13,10 @@ namespace Ryujinx.Graphics.Shader public int CbufSlot { get; } public int CbufOffset { get; } - public TextureDescriptor(string name, int hIndex) + public TextureDescriptor(string name, TextureTarget target, int hIndex) { Name = name; + Target = target; HandleIndex = hIndex; IsBindless = false; @@ -22,9 +25,10 @@ namespace Ryujinx.Graphics.Shader CbufOffset = 0; } - public TextureDescriptor(string name, int cbufSlot, int cbufOffset) + public TextureDescriptor(string name, TextureTarget target, int cbufSlot, int cbufOffset) { Name = name; + Target = target; HandleIndex = 0; IsBindless = true; diff --git a/Ryujinx.Graphics.Shader/TextureTarget.cs b/Ryujinx.Graphics.Shader/TextureTarget.cs new file mode 100644 index 000000000..3642ef239 --- /dev/null +++ b/Ryujinx.Graphics.Shader/TextureTarget.cs @@ -0,0 +1,35 @@ +using System; + +namespace Ryujinx.Graphics.Shader +{ + [Flags] + public enum TextureTarget + { + Texture1D, + Texture2D, + Texture3D, + TextureCube, + + Mask = 0xff, + + Array = 1 << 8, + Multisample = 1 << 9, + Shadow = 1 << 10 + } + + static class TextureTargetExtensions + { + public static int GetDimensions(this TextureTarget type) + { + switch (type & TextureTarget.Mask) + { + case TextureTarget.Texture1D: return 1; + case TextureTarget.Texture2D: return 2; + case TextureTarget.Texture3D: return 3; + case TextureTarget.TextureCube: return 3; + } + + throw new ArgumentException($"Invalid texture type \"{type}\"."); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs similarity index 82% rename from Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs rename to Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index f21a6252a..08aac1cad 100644 --- a/Ryujinx.Graphics/Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.Graphics.Shader.IntermediateRepresentation +namespace Ryujinx.Graphics.Shader.Translation { static class AttributeConsts { @@ -34,5 +34,13 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation public const int FragmentOutputDepth = 0x1000000; public const int FragmentOutputColorBase = 0x1000010; public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16; + + public const int ThreadIdX = 0x2000000; + public const int ThreadIdY = 0x2000004; + public const int ThreadIdZ = 0x2000008; + + public const int CtaIdX = 0x2000010; + public const int CtaIdY = 0x2000014; + public const int CtaIdZ = 0x2000018; } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/ControlFlowGraph.cs b/Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Translation/ControlFlowGraph.cs rename to Ryujinx.Graphics.Shader/Translation/ControlFlowGraph.cs diff --git a/Ryujinx.Graphics/Shader/Translation/Dominance.cs b/Ryujinx.Graphics.Shader/Translation/Dominance.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Translation/Dominance.cs rename to Ryujinx.Graphics.Shader/Translation/Dominance.cs diff --git a/Ryujinx.Graphics/Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs similarity index 90% rename from Ryujinx.Graphics/Shader/Translation/EmitterContext.cs rename to Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 6c2bf6e47..3995d4306 100644 --- a/Ryujinx.Graphics/Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -1,4 +1,3 @@ -using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using System.Collections.Generic; @@ -12,7 +11,7 @@ namespace Ryujinx.Graphics.Shader.Translation public Block CurrBlock { get; set; } public OpCode CurrOp { get; set; } - private GalShaderType _shaderType; + private ShaderStage _stage; private ShaderHeader _header; @@ -20,10 +19,10 @@ namespace Ryujinx.Graphics.Shader.Translation private Dictionary _labels; - public EmitterContext(GalShaderType shaderType, ShaderHeader header) + public EmitterContext(ShaderStage stage, ShaderHeader header) { - _shaderType = shaderType; - _header = header; + _stage = stage; + _header = header; _operations = new List(); @@ -63,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.Translation public void PrepareForReturn() { - if (_shaderType == GalShaderType.Fragment) + if (_stage == ShaderStage.Fragment) { if (_header.OmapDepth) { diff --git a/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs similarity index 94% rename from Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs rename to Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index 604aa67d3..f0bede186 100644 --- a/Ryujinx.Graphics/Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -366,11 +366,26 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.IsNan, Local(), a); } + public static Operand LoadAttribute(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.LoadAttribute, Local(), a, b); + } + public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b) { return context.Add(Instruction.LoadConstant, Local(), a, b); } + public static Operand LoadGlobal(this EmitterContext context, Operand a) + { + return context.Add(Instruction.LoadGlobal, Local(), a); + } + + public static Operand LoadLocal(this EmitterContext context, Operand a) + { + return context.Add(Instruction.LoadLocal, Local(), a); + } + public static Operand PackHalf2x16(this EmitterContext context, Operand a, Operand b) { return context.Add(Instruction.PackHalf2x16, Local(), a, b); @@ -398,6 +413,16 @@ namespace Ryujinx.Graphics.Shader.Translation return context.Add(Instruction.ShiftRightU32, Local(), a, b); } + public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.StoreGlobal, null, a, b); + } + + public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b) + { + return context.Add(Instruction.StoreLocal, null, a, b); + } + public static Operand UnpackHalf2x16High(this EmitterContext context, Operand a) { return UnpackHalf2x16(context, a, 1); diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/BranchElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs similarity index 97% rename from Ryujinx.Graphics/Shader/Translation/Optimizations/BranchElimination.cs rename to Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs index 070f07a47..c87d14748 100644 --- a/Ryujinx.Graphics/Shader/Translation/Optimizations/BranchElimination.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BranchElimination.cs @@ -5,7 +5,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class BranchElimination { - public static bool Eliminate(BasicBlock block) + public static bool RunPass(BasicBlock block) { if (block.HasBranch && IsRedundantBranch((Operation)block.GetLastOp(), Next(block))) { diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/ConstantFolding.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs similarity index 99% rename from Ryujinx.Graphics/Shader/Translation/Optimizations/ConstantFolding.cs rename to Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs index a2e05ef12..d64579b71 100644 --- a/Ryujinx.Graphics/Shader/Translation/Optimizations/ConstantFolding.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/ConstantFolding.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class ConstantFolding { - public static void Fold(Operation operation) + public static void RunPass(Operation operation) { if (!AreAllSourcesConstant(operation)) { diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs new file mode 100644 index 000000000..06db2a804 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -0,0 +1,145 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System.Collections.Generic; + +using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; + +namespace Ryujinx.Graphics.Shader.Translation.Optimizations +{ + static class GlobalToStorage + { + private const int StorageDescsBaseOffset = 0x44; // In words. + + private const int UbeStorageDescsBaseOffset = 0x84; // In words. + private const int UbeStorageMaxCount = 14; + + private const int StorageDescSize = 4; // In words. + private const int StorageMaxCount = 16; + + private const int StorageDescsSize = StorageDescSize * StorageMaxCount; + + public static void RunPass(BasicBlock block, ShaderStage stage) + { + int sbStart = GetStorageBaseCbOffset(stage); + + int sbEnd = sbStart + StorageDescsSize; + + // This one is only used on compute shaders. + // Compute shaders uses two separate sets of storage. + int ubeSbStart = UbeStorageDescsBaseOffset; + int ubeSbEnd = UbeStorageDescsBaseOffset + StorageDescSize * UbeStorageMaxCount; + + for (LinkedListNode node = block.Operations.First; node != null; node = node.Next) + { + if (!(node.Value is Operation operation)) + { + continue; + } + + if (operation.Inst == Instruction.LoadGlobal || + operation.Inst == Instruction.StoreGlobal) + { + Operand source = operation.GetSource(0); + + if (source.AsgOp is Operation asgOperation) + { + int storageIndex = SearchForStorageBase(asgOperation, sbStart, sbEnd); + + /*if (storageIndex < 0 && stage == ShaderStage.Compute) + { + storageIndex = SearchForStorageBase(asgOperation, ubeSbStart, ubeSbEnd); + }*/ + + if (storageIndex >= 0) + { + node = ReplaceGlobalWithStorage(node, storageIndex); + } + } + } + } + } + + private static LinkedListNode ReplaceGlobalWithStorage(LinkedListNode node, int storageIndex) + { + Operation operation = (Operation)node.Value; + + Operation storageOp; + + if (operation.Inst == Instruction.LoadGlobal) + { + Operand source = operation.GetSource(0); + + storageOp = new Operation(Instruction.LoadStorage, operation.Dest, Const(storageIndex), source); + } + else + { + Operand src1 = operation.GetSource(0); + Operand src2 = operation.GetSource(1); + + storageOp = new Operation(Instruction.StoreStorage, null, Const(storageIndex), src1, src2); + } + + for (int index = 0; index < operation.SourcesCount; index++) + { + operation.SetSource(index, null); + } + + LinkedListNode oldNode = node; + + node = node.List.AddAfter(node, storageOp); + + node.List.Remove(oldNode); + + return node; + } + + private static int SearchForStorageBase(Operation operation, int sbStart, int sbEnd) + { + Queue assignments = new Queue(); + + assignments.Enqueue(operation); + + while (assignments.TryDequeue(out operation)) + { + for (int index = 0; index < operation.SourcesCount; index++) + { + Operand source = operation.GetSource(index); + + if (source.Type == OperandType.ConstantBuffer) + { + int slot = source.GetCbufSlot(); + int offset = source.GetCbufOffset(); + + if (slot == 0 && offset >= sbStart && offset < sbEnd) + { + int storageIndex = (offset - sbStart) / StorageDescSize; + + return storageIndex; + } + } + + if (source.AsgOp is Operation asgOperation) + { + assignments.Enqueue(asgOperation); + } + } + } + + return -1; + } + + private static int GetStorageBaseCbOffset(ShaderStage stage) + { + switch (stage) + { + case ShaderStage.Compute: return StorageDescsBaseOffset + 2 * StorageDescsSize; + case ShaderStage.Vertex: return StorageDescsBaseOffset; + case ShaderStage.TessellationControl: return StorageDescsBaseOffset + 1 * StorageDescsSize; + case ShaderStage.TessellationEvaluation: return StorageDescsBaseOffset + 2 * StorageDescsSize; + case ShaderStage.Geometry: return StorageDescsBaseOffset + 3 * StorageDescsSize; + case ShaderStage.Fragment: return StorageDescsBaseOffset + 4 * StorageDescsSize; + } + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/HalfConversion.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/HalfConversion.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Translation/Optimizations/HalfConversion.cs rename to Ryujinx.Graphics.Shader/Translation/Optimizations/HalfConversion.cs diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs similarity index 92% rename from Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs rename to Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index 8cce0e74e..d5e57546a 100644 --- a/Ryujinx.Graphics/Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -6,8 +6,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class Optimizer { - public static void Optimize(BasicBlock[] blocks) + public static void Optimize(BasicBlock[] blocks, ShaderStage stage) { + for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) + { + GlobalToStorage.RunPass(blocks[blkIndex], stage); + } + bool modified; do @@ -40,9 +45,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - ConstantFolding.Fold(operation); + ConstantFolding.RunPass(operation); - Simplification.Simplify(operation); + Simplification.RunPass(operation); if (DestIsLocalVar(operation)) { @@ -68,7 +73,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations node = nextNode; } - if (BranchElimination.Eliminate(block)) + if (BranchElimination.RunPass(block)) { RemoveNode(block, block.Operations.Last); diff --git a/Ryujinx.Graphics/Shader/Translation/Optimizations/Simplification.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs similarity index 98% rename from Ryujinx.Graphics/Shader/Translation/Optimizations/Simplification.cs rename to Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs index d6366dfe0..8d05f99af 100644 --- a/Ryujinx.Graphics/Shader/Translation/Optimizations/Simplification.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Simplification.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { private const int AllOnes = ~0; - public static void Simplify(Operation operation) + public static void RunPass(Operation operation) { switch (operation.Inst) { diff --git a/Ryujinx.Graphics/Shader/Translation/Ssa.cs b/Ryujinx.Graphics.Shader/Translation/Ssa.cs similarity index 100% rename from Ryujinx.Graphics/Shader/Translation/Ssa.cs rename to Ryujinx.Graphics.Shader/Translation/Ssa.cs diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationConfig.cs b/Ryujinx.Graphics.Shader/Translation/TranslationConfig.cs new file mode 100644 index 000000000..e5fa6d14d --- /dev/null +++ b/Ryujinx.Graphics.Shader/Translation/TranslationConfig.cs @@ -0,0 +1,25 @@ +using System; + +namespace Ryujinx.Graphics.Shader.Translation +{ + public struct TranslationConfig + { + public int MaxCBufferSize { get; } + + public int Version { get; } + + public TranslationFlags Flags { get; } + + public TranslationConfig(int maxCBufferSize, int version, TranslationFlags flags) + { + if (maxCBufferSize <= 0) + { + throw new ArgumentOutOfRangeException(nameof(maxCBufferSize)); + } + + MaxCBufferSize = maxCBufferSize; + Version = version; + Flags = flags; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs b/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs new file mode 100644 index 000000000..99b6107a9 --- /dev/null +++ b/Ryujinx.Graphics.Shader/Translation/TranslationFlags.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.Graphics.Shader.Translation +{ + public enum TranslationFlags + { + None = 0, + + Compute = 1 << 0, + DebugMode = 1 << 1, + Unspecialized = 1 << 2 + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs similarity index 64% rename from Ryujinx.Graphics/Shader/Translation/Translator.cs rename to Ryujinx.Graphics.Shader/Translation/Translator.cs index fcebe913e..4838b1e29 100644 --- a/Ryujinx.Graphics/Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -1,10 +1,10 @@ -using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Shader.CodeGen.Glsl; using Ryujinx.Graphics.Shader.Decoders; using Ryujinx.Graphics.Shader.Instructions; using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.StructuredIr; using Ryujinx.Graphics.Shader.Translation.Optimizations; +using System; using System.Collections.Generic; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; @@ -13,27 +13,56 @@ namespace Ryujinx.Graphics.Shader.Translation { public static class Translator { - public static ShaderProgram Translate(IGalMemory memory, ulong address, ShaderConfig config) + private const int HeaderSize = 0x50; + + public static ShaderProgram Translate(Span code, TranslationConfig translationConfig) { - return Translate(memory, address, 0, config); + return Translate(code, Span.Empty, translationConfig); } - public static ShaderProgram Translate( - IGalMemory memory, - ulong address, - ulong addressB, - ShaderConfig config) + public static ShaderProgram Translate(Span code, Span code2, TranslationConfig translationConfig) { - Operation[] shaderOps = DecodeShader(memory, address, config.Type); + bool compute = (translationConfig.Flags & TranslationFlags.Compute) != 0; + bool debugMode = (translationConfig.Flags & TranslationFlags.DebugMode) != 0; - if (addressB != 0) + Operation[] shaderOps = DecodeShader(code, compute, debugMode, out ShaderHeader header); + + if (code2 != Span.Empty) { // Dual vertex shader. - Operation[] shaderOpsB = DecodeShader(memory, addressB, config.Type); + Operation[] shaderOpsB = DecodeShader(code2, compute, debugMode, out header); shaderOps = Combine(shaderOps, shaderOpsB); } + ShaderStage stage; + + if (compute) + { + stage = ShaderStage.Compute; + } + else + { + stage = header.Stage; + } + + int maxOutputVertexCount = 0; + + OutputTopology outputTopology = OutputTopology.LineStrip; + + if (!compute) + { + maxOutputVertexCount = header.MaxOutputVertexCount; + outputTopology = header.OutputTopology; + } + + ShaderConfig config = new ShaderConfig( + stage, + translationConfig.Flags, + translationConfig.MaxCBufferSize, + maxOutputVertexCount, + outputTopology); + BasicBlock[] irBlocks = ControlFlowGraph.MakeCfg(shaderOps); Dominance.FindDominators(irBlocks[0], irBlocks.Length); @@ -42,26 +71,57 @@ namespace Ryujinx.Graphics.Shader.Translation Ssa.Rename(irBlocks); - Optimizer.Optimize(irBlocks); + Optimizer.Optimize(irBlocks, stage); - StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(irBlocks); + StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(irBlocks, config); GlslProgram program = GlslGenerator.Generate(sInfo, config); ShaderProgramInfo spInfo = new ShaderProgramInfo( program.CBufferDescriptors, - program.TextureDescriptors); + program.SBufferDescriptors, + program.TextureDescriptors, + sInfo.InterpolationQualifiers, + sInfo.UsesInstanceId); - return new ShaderProgram(spInfo, program.Code); + string glslCode = program.Code; + + if (translationConfig.Version != 0) + { + glslCode = "// " + translationConfig.Version + Environment.NewLine + glslCode; + } + + return new ShaderProgram(spInfo, stage, glslCode); } - private static Operation[] DecodeShader(IGalMemory memory, ulong address, GalShaderType shaderType) + private static Operation[] DecodeShader(Span code, bool compute, bool debugMode, out ShaderHeader header) { - ShaderHeader header = new ShaderHeader(memory, address); + Block[] cfg; - Block[] cfg = Decoder.Decode(memory, address); + EmitterContext context; - EmitterContext context = new EmitterContext(shaderType, header); + ulong headerSize; + + if (compute) + { + header = null; + + cfg = Decoder.Decode(code, 0); + + context = new EmitterContext(ShaderStage.Compute, header); + + headerSize = 0; + } + else + { + header = new ShaderHeader(code); + + cfg = Decoder.Decode(code, HeaderSize); + + context = new EmitterContext(header.Stage, header); + + headerSize = HeaderSize; + } for (int blkIndex = 0; blkIndex < cfg.Length; blkIndex++) { @@ -75,6 +135,24 @@ namespace Ryujinx.Graphics.Shader.Translation { OpCode op = block.OpCodes[opIndex]; + if (debugMode) + { + string instName; + + if (op.Emitter != null) + { + instName = op.Emitter.Method.Name; + } + else + { + instName = "???"; + } + + string dbgComment = $"0x{(op.Address - headerSize):X6}: 0x{op.RawOpCode:X16} {instName}"; + + context.Add(new CommentNode(dbgComment)); + } + if (op.NeverExecute) { continue; @@ -122,7 +200,10 @@ namespace Ryujinx.Graphics.Shader.Translation context.CurrOp = op; - op.Emitter(context); + if (op.Emitter != null) + { + op.Emitter(context); + } if (predSkipLbl != null) { diff --git a/Ryujinx.Graphics/Graphics3d/Texture/AstcDecoder.cs b/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs similarity index 95% rename from Ryujinx.Graphics/Graphics3d/Texture/AstcDecoder.cs rename to Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index 3e68be7d8..c192a6a9e 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/AstcDecoder.cs +++ b/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -4,13 +4,8 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -namespace Ryujinx.Graphics.Texture +namespace Ryujinx.Graphics.Texture.Astc { - public class AstcDecoderException : Exception - { - public AstcDecoderException(string exMsg) : base(exMsg) { } - } - // https://github.com/GammaUNC/FasTC/blob/master/ASTCEncoder/src/Decompressor.cpp public static class AstcDecoder { @@ -52,54 +47,54 @@ namespace Ryujinx.Graphics.Texture } } - public static byte[] DecodeToRgba8888( - byte[] inputBuffer, - int blockX, - int blockY, - int blockZ, - int x, - int y, - int z) + public static Span DecodeToRgba8( + Span data, + int blockWidth, + int blockHeight, + int blockDepth, + int width, + int height, + int depth) { - using (MemoryStream inputStream = new MemoryStream(inputBuffer)) + using (MemoryStream inputStream = new MemoryStream(data.ToArray())) { BinaryReader binReader = new BinaryReader(inputStream); - if (blockX > 12 || blockY > 12) + if (blockWidth > 12 || blockHeight > 12) { - throw new AstcDecoderException("Block size unsupported!"); + throw new AstcDecoderException("Invalid block size."); } - if (blockZ != 1 || z != 1) + if (blockDepth != 1 || depth != 1) { - // TODO: Support 3D textures? - throw new AstcDecoderException("3D compressed textures unsupported!"); + // TODO: Support 3D textures. + throw new NotImplementedException("3D compressed textures are not unsupported."); } using (MemoryStream outputStream = new MemoryStream()) { int blockIndex = 0; - for (int j = 0; j < y; j += blockY) + for (int j = 0; j < height; j += blockHeight) { - for (int i = 0; i < x; i += blockX) + for (int i = 0; i < width; i += blockWidth) { int[] decompressedData = new int[144]; - DecompressBlock(binReader.ReadBytes(0x10), decompressedData, blockX, blockY); + DecompressBlock(binReader.ReadBytes(0x10), decompressedData, blockWidth, blockHeight); - int decompressedWidth = Math.Min(blockX, x - i); - int decompressedHeight = Math.Min(blockY, y - j); - int baseOffsets = (j * x + i) * 4; + int decompressedWidth = Math.Min(blockWidth, width - i); + int decompressedHeight = Math.Min(blockHeight, height - j); + int baseOffsets = (j * width + i) * 4; for (int jj = 0; jj < decompressedHeight; jj++) { - outputStream.Seek(baseOffsets + jj * x * 4, SeekOrigin.Begin); + outputStream.Seek(baseOffsets + jj * width * 4, SeekOrigin.Begin); byte[] outputBuffer = new byte[decompressedData.Length * sizeof(int)]; Buffer.BlockCopy(decompressedData, 0, outputBuffer, 0, outputBuffer.Length); - outputStream.Write(outputBuffer, jj * blockX * 4, decompressedWidth * 4); + outputStream.Write(outputBuffer, jj * blockWidth * 4, decompressedWidth * 4); } blockIndex++; @@ -112,9 +107,9 @@ namespace Ryujinx.Graphics.Texture } public static bool DecompressBlock( - byte[] inputBuffer, - int[] outputBuffer, - int blockWidth, + byte[] inputBuffer, + int[] outputBuffer, + int blockWidth, int blockHeight) { BitArrayStream bitStream = new BitArrayStream(new BitArray(inputBuffer)); @@ -122,7 +117,7 @@ namespace Ryujinx.Graphics.Texture if (texelParams.Error) { - throw new AstcDecoderException("Invalid block mode"); + throw new AstcDecoderException("Invalid block mode."); } if (texelParams.VoidExtentLdr) @@ -134,17 +129,17 @@ namespace Ryujinx.Graphics.Texture if (texelParams.VoidExtentHdr) { - throw new AstcDecoderException("HDR void extent blocks are unsupported!"); + throw new AstcDecoderException("HDR void extent blocks are not supported."); } if (texelParams.Width > blockWidth) { - throw new AstcDecoderException("Texel weight grid width should be smaller than block width"); + throw new AstcDecoderException("Texel weight grid width should be smaller than block width."); } if (texelParams.Height > blockHeight) { - throw new AstcDecoderException("Texel weight grid height should be smaller than block height"); + throw new AstcDecoderException("Texel weight grid height should be smaller than block height."); } // Read num partitions @@ -153,7 +148,7 @@ namespace Ryujinx.Graphics.Texture if (numberPartitions == 4 && texelParams.DualPlane) { - throw new AstcDecoderException("Dual plane mode is incompatible with four partition blocks"); + throw new AstcDecoderException("Dual plane mode is incompatible with four partition blocks."); } // Based on the number of partitions, read the color endpoint mode for @@ -316,7 +311,7 @@ namespace Ryujinx.Graphics.Texture BitArrayStream weightBitStream = new BitArrayStream(new BitArray(texelWeightData)); IntegerEncoded.DecodeIntegerSequence(texelWeightValues, weightBitStream, texelParams.MaxWeight, texelParams.GetNumWeightValues()); - + // Blocks can be at most 12x12, so we can have as many as 144 weights int[][] weights = new int[2][]; weights[0] = new int[144]; @@ -456,10 +451,10 @@ namespace Ryujinx.Graphics.Texture } static void UnquantizeTexelWeights( - int[][] outputBuffer, - List weights, - TexelWeightParams texelParams, - int blockWidth, + int[][] outputBuffer, + List weights, + TexelWeightParams texelParams, + int blockWidth, int blockHeight) { int weightIndices = 0; @@ -530,12 +525,12 @@ namespace Ryujinx.Graphics.Texture { p01 = unquantized[plane][v0 + 1]; } - + if (v0 + texelParams.Width < (texelParams.Width * texelParams.Height)) { p10 = unquantized[plane][v0 + texelParams.Width]; } - + if (v0 + texelParams.Width + 1 < (texelParams.Width * texelParams.Height)) { p11 = unquantized[plane][v0 + texelParams.Width + 1]; @@ -603,11 +598,11 @@ namespace Ryujinx.Graphics.Texture } default: - throw new AstcDecoderException("Invalid trit encoding for texel weight"); + throw new AstcDecoderException("Invalid trit encoding for texel weight."); } break; - } + } case IntegerEncoded.EIntegerEncoding.Quint: { @@ -639,13 +634,13 @@ namespace Ryujinx.Graphics.Texture break; } - + default: - throw new AstcDecoderException("Invalid quint encoding for texel weight"); + throw new AstcDecoderException("Invalid quint encoding for texel weight."); } break; - } + } } if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits && bitLength > 0) @@ -698,9 +693,9 @@ namespace Ryujinx.Graphics.Texture } static void ComputeEndpoints( - AstcPixel[] endPoints, - int[] colorValues, - uint colorEndpointMode, + AstcPixel[] endPoints, + int[] colorValues, + uint colorEndpointMode, ref int colorValuesPosition) { switch (colorEndpointMode) @@ -714,7 +709,7 @@ namespace Ryujinx.Graphics.Texture break; } - + case 1: { @@ -867,10 +862,10 @@ namespace Ryujinx.Graphics.Texture } static void DecodeColorValues( - int[] outputValues, - byte[] inputData, - uint[] modes, - int numberPartitions, + int[] outputValues, + byte[] inputData, + uint[] modes, + int numberPartitions, int numberBitsForColorData) { // First figure out how many color values we have @@ -950,7 +945,7 @@ namespace Ryujinx.Graphics.Texture break; } - + case 2: { c = 93; @@ -970,7 +965,7 @@ namespace Ryujinx.Graphics.Texture break; } - + case 4: { @@ -1003,12 +998,12 @@ namespace Ryujinx.Graphics.Texture } default: - throw new AstcDecoderException("Unsupported trit encoding for color values!"); + throw new AstcDecoderException("Unsupported trit encoding for color values."); } break; } - + case IntegerEncoded.EIntegerEncoding.Quint: { d = intEncoded.QuintValue; @@ -1021,7 +1016,7 @@ namespace Ryujinx.Graphics.Texture break; } - + case 2: { c = 54; @@ -1031,7 +1026,7 @@ namespace Ryujinx.Graphics.Texture break; } - + case 3: { c = 26; @@ -1051,7 +1046,7 @@ namespace Ryujinx.Graphics.Texture break; } - + case 5: { c = 6; @@ -1063,10 +1058,10 @@ namespace Ryujinx.Graphics.Texture } default: - throw new AstcDecoderException("Unsupported quint encoding for color values!"); + throw new AstcDecoderException("Unsupported quint encoding for color values."); } break; - } + } } if (intEncoded.GetEncoding() != IntegerEncoded.EIntegerEncoding.JustBits) diff --git a/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs b/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs new file mode 100644 index 000000000..fdc482671 --- /dev/null +++ b/Ryujinx.Graphics.Texture/Astc/AstcDecoderException.cs @@ -0,0 +1,9 @@ +using System; + +namespace Ryujinx.Graphics.Texture.Astc +{ + public class AstcDecoderException : Exception + { + public AstcDecoderException(string exMsg) : base(exMsg) { } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/AstcPixel.cs b/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs similarity index 98% rename from Ryujinx.Graphics/Graphics3d/Texture/AstcPixel.cs rename to Ryujinx.Graphics.Texture/Astc/AstcPixel.cs index 2f73c62b0..7d1278782 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/AstcPixel.cs +++ b/Ryujinx.Graphics.Texture/Astc/AstcPixel.cs @@ -1,7 +1,7 @@ using System; using System.Diagnostics; -namespace Ryujinx.Graphics.Texture +namespace Ryujinx.Graphics.Texture.Astc { class AstcPixel { diff --git a/Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs b/Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs similarity index 98% rename from Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs rename to Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs index 24069d722..3c472a3c1 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/BitArrayStream.cs +++ b/Ryujinx.Graphics.Texture/Astc/BitArrayStream.cs @@ -1,7 +1,7 @@ using System; using System.Collections; -namespace Ryujinx.Graphics.Texture +namespace Ryujinx.Graphics.Texture.Astc { public class BitArrayStream { diff --git a/Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs b/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs similarity index 95% rename from Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs rename to Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs index e6d67058e..5aa610e74 100644 --- a/Ryujinx.Graphics/Graphics3d/Texture/IntegerEncoded.cs +++ b/Ryujinx.Graphics.Texture/Astc/IntegerEncoded.cs @@ -1,7 +1,7 @@ using System.Collections; using System.Collections.Generic; -namespace Ryujinx.Graphics.Texture +namespace Ryujinx.Graphics.Texture.Astc { public struct IntegerEncoded { @@ -84,8 +84,8 @@ namespace Ryujinx.Graphics.Texture } public static void DecodeTritBlock( - BitArrayStream bitStream, - List listIntegerEncoded, + BitArrayStream bitStream, + List listIntegerEncoded, int numberBitsPerValue) { // Implement the algorithm in section C.2.12 @@ -161,8 +161,8 @@ namespace Ryujinx.Graphics.Texture } public static void DecodeQuintBlock( - BitArrayStream bitStream, - List listIntegerEncoded, + BitArrayStream bitStream, + List listIntegerEncoded, int numberBitsPerValue) { // Implement the algorithm in section C.2.12 @@ -224,9 +224,9 @@ namespace Ryujinx.Graphics.Texture } public static void DecodeIntegerSequence( - List decodeIntegerSequence, - BitArrayStream bitStream, - int maxRange, + List decodeIntegerSequence, + BitArrayStream bitStream, + int maxRange, int numberValues) { // Determine encoding parameters diff --git a/Ryujinx.Graphics.Texture/BlockLinearConstants.cs b/Ryujinx.Graphics.Texture/BlockLinearConstants.cs new file mode 100644 index 000000000..d95691cf6 --- /dev/null +++ b/Ryujinx.Graphics.Texture/BlockLinearConstants.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.Graphics.Texture +{ + static class BlockLinearConstants + { + public const int GobStride = 64; + public const int GobHeight = 8; + + public const int GobSize = GobStride * GobHeight; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/BlockLinearLayout.cs b/Ryujinx.Graphics.Texture/BlockLinearLayout.cs new file mode 100644 index 000000000..3d3f13ba6 --- /dev/null +++ b/Ryujinx.Graphics.Texture/BlockLinearLayout.cs @@ -0,0 +1,106 @@ +using Ryujinx.Common; +using System; + +using static Ryujinx.Graphics.Texture.BlockLinearConstants; + +namespace Ryujinx.Graphics.Texture +{ + class BlockLinearLayout + { + private struct RobAndSliceSizes + { + public int RobSize; + public int SliceSize; + + public RobAndSliceSizes(int robSize, int sliceSize) + { + RobSize = robSize; + SliceSize = sliceSize; + } + } + + private int _texWidth; + private int _texHeight; + private int _texDepth; + private int _texGobBlocksInY; + private int _texGobBlocksInZ; + private int _texBpp; + + private int _bhMask; + private int _bdMask; + + private int _bhShift; + private int _bdShift; + private int _bppShift; + + private int _xShift; + + private int _robSize; + private int _sliceSize; + + public BlockLinearLayout( + int width, + int height, + int depth, + int gobBlocksInY, + int gobBlocksInZ, + int bpp) + { + _texWidth = width; + _texHeight = height; + _texDepth = depth; + _texGobBlocksInY = gobBlocksInY; + _texGobBlocksInZ = gobBlocksInZ; + _texBpp = bpp; + + _bppShift = BitUtils.CountTrailingZeros32(bpp); + + _bhMask = gobBlocksInY - 1; + _bdMask = gobBlocksInZ - 1; + + _bhShift = BitUtils.CountTrailingZeros32(gobBlocksInY); + _bdShift = BitUtils.CountTrailingZeros32(gobBlocksInZ); + + _xShift = BitUtils.CountTrailingZeros32(GobSize * gobBlocksInY * gobBlocksInZ); + + RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gobBlocksInY, gobBlocksInZ); + + _robSize = rsSizes.RobSize; + _sliceSize = rsSizes.SliceSize; + } + + private RobAndSliceSizes GetRobAndSliceSizes(int width, int height, int gobBlocksInY, int gobBlocksInZ) + { + int widthInGobs = BitUtils.DivRoundUp(width * _texBpp, GobStride); + + int robSize = GobSize * gobBlocksInY * gobBlocksInZ * widthInGobs; + + int sliceSize = BitUtils.DivRoundUp(height, gobBlocksInY * GobHeight) * robSize; + + return new RobAndSliceSizes(robSize, sliceSize); + } + + public int GetOffset(int x, int y, int z) + { + x <<= _bppShift; + + int yh = y / GobHeight; + + int position = (z >> _bdShift) * _sliceSize + (yh >> _bhShift) * _robSize; + + position += (x / GobStride) << _xShift; + + position += (yh & _bhMask) * GobSize; + + position += ((z & _bdMask) * GobSize) << _bhShift; + + position += ((x & 0x3f) >> 5) << 8; + position += ((y & 0x07) >> 1) << 6; + position += ((x & 0x1f) >> 4) << 5; + position += ((y & 0x01) >> 0) << 4; + position += ((x & 0x0f) >> 0) << 0; + + return position; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/LayoutConverter.cs b/Ryujinx.Graphics.Texture/LayoutConverter.cs new file mode 100644 index 000000000..36ae522b5 --- /dev/null +++ b/Ryujinx.Graphics.Texture/LayoutConverter.cs @@ -0,0 +1,167 @@ +using Ryujinx.Common; +using System; + +using static Ryujinx.Graphics.Texture.BlockLinearConstants; + +namespace Ryujinx.Graphics.Texture +{ + public static class LayoutConverter + { + private const int AlignmentSize = 4; + + public static Span ConvertBlockLinearToLinear( + int width, + int height, + int depth, + int levels, + int layers, + int blockWidth, + int blockHeight, + int bytesPerPixel, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX, + SizeInfo sizeInfo, + Span data) + { + int outSize = GetTextureSize( + width, + height, + depth, + levels, + layers, + blockWidth, + blockHeight, + bytesPerPixel); + + Span output = new byte[outSize]; + + int outOffs = 0; + + int wAlignment = gobBlocksInTileX * (GobStride / bytesPerPixel); + + int mipGobBlocksInY = gobBlocksInY; + int mipGobBlocksInZ = gobBlocksInZ; + + for (int level = 0; level < levels; level++) + { + int w = Math.Max(1, width >> level); + int h = Math.Max(1, height >> level); + int d = Math.Max(1, depth >> level); + + w = BitUtils.DivRoundUp(w, blockWidth); + h = BitUtils.DivRoundUp(h, blockHeight); + + while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1) + { + mipGobBlocksInY >>= 1; + } + + while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) + { + mipGobBlocksInZ >>= 1; + } + + int stride = BitUtils.AlignUp(w * bytesPerPixel, AlignmentSize); + int wAligned = BitUtils.AlignUp(w, wAlignment); + + BlockLinearLayout layoutConverter = new BlockLinearLayout( + wAligned, + h, + d, + mipGobBlocksInY, + mipGobBlocksInZ, + bytesPerPixel); + + for (int layer = 0; layer < layers; layer++) + { + int inBaseOffset = layer * sizeInfo.LayerSize + sizeInfo.GetMipOffset(level); + + for (int z = 0; z < d; z++) + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int offset = inBaseOffset + layoutConverter.GetOffset(x, y, z); + + Span dest = output.Slice(outOffs + x * bytesPerPixel, bytesPerPixel); + + data.Slice(offset, bytesPerPixel).CopyTo(dest); + } + + outOffs += stride; + } + } + } + + return output; + } + + public static Span ConvertLinearStridedToLinear( + int width, + int height, + int blockWidth, + int blockHeight, + int stride, + int bytesPerPixel, + Span data) + { + int outOffs = 0; + + int w = width; + int h = height; + + w = BitUtils.DivRoundUp(w, blockWidth); + h = BitUtils.DivRoundUp(h, blockHeight); + + int outStride = BitUtils.AlignUp(w * bytesPerPixel, AlignmentSize); + + Span output = new byte[h * outStride]; + + for (int y = 0; y < h; y++) + { + for (int x = 0; x < w; x++) + { + int offset = y * stride + x * bytesPerPixel; + + Span dest = output.Slice(outOffs + x * bytesPerPixel, bytesPerPixel); + + data.Slice(offset, bytesPerPixel).CopyTo(dest); + } + + outOffs += outStride; + } + + return output; + } + + private static int GetTextureSize( + int width, + int height, + int depth, + int levels, + int layers, + int blockWidth, + int blockHeight, + int bytesPerPixel) + { + int layerSize = 0; + + for (int level = 0; level < levels; level++) + { + int w = Math.Max(1, width >> level); + int h = Math.Max(1, height >> level); + int d = Math.Max(1, depth >> level); + + w = BitUtils.DivRoundUp(w, blockWidth); + h = BitUtils.DivRoundUp(h, blockHeight); + + int stride = BitUtils.AlignUp(w * bytesPerPixel, AlignmentSize); + + layerSize += stride * h * d; + } + + return layerSize * layers; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/OffsetCalculator.cs b/Ryujinx.Graphics.Texture/OffsetCalculator.cs new file mode 100644 index 000000000..bb5d606ca --- /dev/null +++ b/Ryujinx.Graphics.Texture/OffsetCalculator.cs @@ -0,0 +1,55 @@ +using Ryujinx.Common; + +using static Ryujinx.Graphics.Texture.BlockLinearConstants; + +namespace Ryujinx.Graphics.Texture +{ + public class OffsetCalculator + { + private int _stride; + private bool _isLinear; + private int _bytesPerPixel; + + private BlockLinearLayout _layoutConverter; + + public OffsetCalculator( + int width, + int height, + int stride, + bool isLinear, + int gobBlocksInY, + int bytesPerPixel) + { + _stride = stride; + _isLinear = isLinear; + _bytesPerPixel = bytesPerPixel; + + int wAlignment = GobStride / bytesPerPixel; + + int wAligned = BitUtils.AlignUp(width, wAlignment); + + if (!isLinear) + { + _layoutConverter = new BlockLinearLayout( + wAligned, + height, + 1, + gobBlocksInY, + 1, + bytesPerPixel); + } + } + + public int GetOffset(int x, int y) + { + if (_isLinear) + { + return x * _bytesPerPixel + y * _stride; + } + else + { + return _layoutConverter.GetOffset(x, y, 0); + } + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj new file mode 100644 index 000000000..f446f16cc --- /dev/null +++ b/Ryujinx.Graphics.Texture/Ryujinx.Graphics.Texture.csproj @@ -0,0 +1,11 @@ + + + + + + + + netcoreapp3.0 + + + diff --git a/Ryujinx.Graphics.Texture/Size.cs b/Ryujinx.Graphics.Texture/Size.cs new file mode 100644 index 000000000..4e070c908 --- /dev/null +++ b/Ryujinx.Graphics.Texture/Size.cs @@ -0,0 +1,19 @@ +using Ryujinx.Common; +using System; + +namespace Ryujinx.Graphics.Texture +{ + public struct Size + { + public int Width { get; } + public int Height { get; } + public int Depth { get; } + + public Size(int width, int height, int depth) + { + Width = width; + Height = height; + Depth = depth; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/SizeCalculator.cs b/Ryujinx.Graphics.Texture/SizeCalculator.cs new file mode 100644 index 000000000..873f41a32 --- /dev/null +++ b/Ryujinx.Graphics.Texture/SizeCalculator.cs @@ -0,0 +1,195 @@ +using Ryujinx.Common; +using System; + +using static Ryujinx.Graphics.Texture.BlockLinearConstants; + +namespace Ryujinx.Graphics.Texture +{ + public static class SizeCalculator + { + private const int StrideAlignment = 32; + + public static SizeInfo GetBlockLinearTextureSize( + int width, + int height, + int depth, + int levels, + int layers, + int blockWidth, + int blockHeight, + int bytesPerPixel, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX) + { + int layerSize = 0; + + int[] mipOffsets = new int[levels]; + + int mipGobBlocksInY = gobBlocksInY; + int mipGobBlocksInZ = gobBlocksInZ; + + for (int level = 0; level < levels; level++) + { + int w = Math.Max(1, width >> level); + int h = Math.Max(1, height >> level); + int d = Math.Max(1, depth >> level); + + w = BitUtils.DivRoundUp(w, blockWidth); + h = BitUtils.DivRoundUp(h, blockHeight); + + while (h <= (mipGobBlocksInY >> 1) * GobHeight && mipGobBlocksInY != 1) + { + mipGobBlocksInY >>= 1; + } + + while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1) + { + mipGobBlocksInZ >>= 1; + } + + int widthInGobs = BitUtils.AlignUp(BitUtils.DivRoundUp(w * bytesPerPixel, GobStride), gobBlocksInTileX); + + int totalBlocksOfGobsInZ = BitUtils.DivRoundUp(d, mipGobBlocksInZ); + int totalBlocksOfGobsInY = BitUtils.DivRoundUp(BitUtils.DivRoundUp(h, GobHeight), mipGobBlocksInY); + + int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize; + + mipOffsets[level] = layerSize; + + layerSize += totalBlocksOfGobsInZ * totalBlocksOfGobsInY * robSize; + } + + layerSize = AlignLayerSize( + layerSize, + height, + depth, + blockHeight, + gobBlocksInY, + gobBlocksInZ); + + int[] allOffsets = new int[levels * layers]; + + for (int layer = 0; layer < layers; layer++) + { + int baseIndex = layer * levels; + int baseOffset = layer * layerSize; + + for (int level = 0; level < levels; level++) + { + allOffsets[baseIndex + level] = baseOffset + mipOffsets[level]; + } + } + + int totalSize = layerSize * layers; + + return new SizeInfo(mipOffsets, allOffsets, levels, layerSize, totalSize); + } + + public static SizeInfo GetLinearTextureSize(int stride, int height, int blockHeight) + { + // Non-2D or mipmapped linear textures are not supported by the Switch GPU, + // so we only need to handle a single case (2D textures without mipmaps). + int totalSize = stride * BitUtils.DivRoundUp(height, blockHeight); + + return new SizeInfo(new int[] { 0 }, new int[] { 0 }, 1, totalSize, totalSize); + } + + private static int AlignLayerSize( + int size, + int height, + int depth, + int blockHeight, + int gobBlocksInY, + int gobBlocksInZ) + { + height = BitUtils.DivRoundUp(height, blockHeight); + + while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1) + { + gobBlocksInY >>= 1; + } + + while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1) + { + gobBlocksInZ >>= 1; + } + + int blockOfGobsSize = gobBlocksInY * gobBlocksInZ * GobSize; + + int sizeInBlockOfGobs = size / blockOfGobsSize; + + if (size != sizeInBlockOfGobs * blockOfGobsSize) + { + size = (sizeInBlockOfGobs + 1) * blockOfGobsSize; + } + + return size; + } + + public static Size GetBlockLinearAlignedSize( + int width, + int height, + int depth, + int blockWidth, + int blockHeight, + int bytesPerPixel, + int gobBlocksInY, + int gobBlocksInZ, + int gobBlocksInTileX) + { + width = BitUtils.DivRoundUp(width, blockWidth); + height = BitUtils.DivRoundUp(height, blockHeight); + + int gobWidth = gobBlocksInTileX * (GobStride / bytesPerPixel); + + int blockOfGobsHeight = gobBlocksInY * GobHeight; + int blockOfGobsDepth = gobBlocksInZ; + + width = BitUtils.AlignUp(width, gobWidth); + height = BitUtils.AlignUp(height, blockOfGobsHeight); + depth = BitUtils.AlignUp(depth, blockOfGobsDepth); + + return new Size(width, height, depth); + } + + public static Size GetLinearAlignedSize( + int width, + int height, + int blockWidth, + int blockHeight, + int bytesPerPixel) + { + width = BitUtils.DivRoundUp(width, blockWidth); + height = BitUtils.DivRoundUp(height, blockHeight); + + int widthAlignment = StrideAlignment / bytesPerPixel; + + width = BitUtils.AlignUp(width, widthAlignment); + + return new Size(width, height, 1); + } + + public static (int, int) GetMipGobBlockSizes( + int height, + int depth, + int blockHeight, + int gobBlocksInY, + int gobBlocksInZ) + { + height = BitUtils.DivRoundUp(height, blockHeight); + + while (height <= (gobBlocksInY >> 1) * GobHeight && gobBlocksInY != 1) + { + gobBlocksInY >>= 1; + } + + while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1) + { + gobBlocksInZ >>= 1; + } + + return (gobBlocksInY, gobBlocksInZ); + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics.Texture/SizeInfo.cs b/Ryujinx.Graphics.Texture/SizeInfo.cs new file mode 100644 index 000000000..37d824cc5 --- /dev/null +++ b/Ryujinx.Graphics.Texture/SizeInfo.cs @@ -0,0 +1,58 @@ +using Ryujinx.Common; +using System; + +namespace Ryujinx.Graphics.Texture +{ + public struct SizeInfo + { + private int[] _mipOffsets; + private int[] _allOffsets; + + private int _levels; + + public int LayerSize { get; } + public int TotalSize { get; } + + public SizeInfo( + int[] mipOffsets, + int[] allOffsets, + int levels, + int layerSize, + int totalSize) + { + _mipOffsets = mipOffsets; + _allOffsets = allOffsets; + _levels = levels; + LayerSize = layerSize; + TotalSize = totalSize; + } + + public int GetMipOffset(int level) + { + if ((uint)level > _mipOffsets.Length) + { + throw new ArgumentOutOfRangeException(nameof(level)); + } + + return _mipOffsets[level]; + } + + public bool FindView(int offset, int size, out int firstLayer, out int firstLevel) + { + int index = Array.BinarySearch(_allOffsets, offset); + + if (index < 0) + { + firstLayer = 0; + firstLevel = 0; + + return false; + } + + firstLayer = index / _levels; + firstLevel = index - (firstLayer * _levels); + + return true; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/CdmaProcessor.cs b/Ryujinx.Graphics/CdmaProcessor.cs deleted file mode 100644 index 4ff12fbf5..000000000 --- a/Ryujinx.Graphics/CdmaProcessor.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Ryujinx.Graphics.Memory; -using System.Collections.Generic; - -namespace Ryujinx.Graphics -{ - public class CdmaProcessor - { - private const int MethSetMethod = 0x10; - private const int MethSetData = 0x11; - - private NvGpu _gpu; - - public CdmaProcessor(NvGpu gpu) - { - _gpu = gpu; - } - - public void PushCommands(NvGpuVmm vmm, int[] cmdBuffer) - { - List commands = new List(); - - ChClassId currentClass = 0; - - for (int index = 0; index < cmdBuffer.Length; index++) - { - int cmd = cmdBuffer[index]; - - int value = (cmd >> 0) & 0xffff; - int methodOffset = (cmd >> 16) & 0xfff; - - ChSubmissionMode submissionMode = (ChSubmissionMode)((cmd >> 28) & 0xf); - - switch (submissionMode) - { - case ChSubmissionMode.SetClass: currentClass = (ChClassId)(value >> 6); break; - - case ChSubmissionMode.Incrementing: - { - int count = value; - - for (int argIdx = 0; argIdx < count; argIdx++) - { - int argument = cmdBuffer[++index]; - - commands.Add(new ChCommand(currentClass, methodOffset + argIdx, argument)); - } - - break; - } - - case ChSubmissionMode.NonIncrementing: - { - int count = value; - - int[] arguments = new int[count]; - - for (int argIdx = 0; argIdx < count; argIdx++) - { - arguments[argIdx] = cmdBuffer[++index]; - } - - commands.Add(new ChCommand(currentClass, methodOffset, arguments)); - - break; - } - } - } - - ProcessCommands(vmm, commands.ToArray()); - } - - private void ProcessCommands(NvGpuVmm vmm, ChCommand[] commands) - { - int methodOffset = 0; - - foreach (ChCommand command in commands) - { - switch (command.MethodOffset) - { - case MethSetMethod: methodOffset = command.Arguments[0]; break; - - case MethSetData: - { - if (command.ClassId == ChClassId.NvDec) - { - _gpu.VideoDecoder.Process(vmm, methodOffset, command.Arguments); - } - else if (command.ClassId == ChClassId.GraphicsVic) - { - _gpu.VideoImageComposer.Process(vmm, methodOffset, command.Arguments); - } - - break; - } - } - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/ChClassId.cs b/Ryujinx.Graphics/ChClassId.cs deleted file mode 100644 index 115f0b89c..000000000 --- a/Ryujinx.Graphics/ChClassId.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Ryujinx.Graphics -{ - enum ChClassId - { - Host1X = 0x1, - VideoEncodeMpeg = 0x20, - VideoEncodeNvEnc = 0x21, - VideoStreamingVi = 0x30, - VideoStreamingIsp = 0x32, - VideoStreamingIspB = 0x34, - VideoStreamingViI2c = 0x36, - GraphicsVic = 0x5d, - Graphics3D = 0x60, - GraphicsGpu = 0x61, - Tsec = 0xe0, - TsecB = 0xe1, - NvJpg = 0xc0, - NvDec = 0xf0 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/ChCommandEntry.cs b/Ryujinx.Graphics/ChCommandEntry.cs deleted file mode 100644 index b01b77eda..000000000 --- a/Ryujinx.Graphics/ChCommandEntry.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics -{ - struct ChCommand - { - public ChClassId ClassId { get; private set; } - - public int MethodOffset { get; private set; } - - public int[] Arguments { get; private set; } - - public ChCommand(ChClassId classId, int methodOffset, params int[] arguments) - { - ClassId = classId; - MethodOffset = methodOffset; - Arguments = arguments; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/ChSubmissionMode.cs b/Ryujinx.Graphics/ChSubmissionMode.cs deleted file mode 100644 index 5c6530196..000000000 --- a/Ryujinx.Graphics/ChSubmissionMode.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.Graphics -{ - enum ChSubmissionMode - { - SetClass = 0, - Incrementing = 1, - NonIncrementing = 2, - Mask = 3, - Immediate = 4, - Restart = 5, - Gather = 6 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/DepthCompareFunc.cs b/Ryujinx.Graphics/DepthCompareFunc.cs deleted file mode 100644 index 24c8854a4..000000000 --- a/Ryujinx.Graphics/DepthCompareFunc.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics -{ - public enum DepthCompareFunc - { - Never = 0, - Less = 1, - Equal = 2, - LEqual = 3, - Greater = 4, - NotEqual = 5, - GEqual = 6, - Always = 7 - } -} diff --git a/Ryujinx.Graphics/Gal/EmbeddedResource.cs b/Ryujinx.Graphics/Gal/EmbeddedResource.cs deleted file mode 100644 index ba6624991..000000000 --- a/Ryujinx.Graphics/Gal/EmbeddedResource.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.IO; -using System.Reflection; - -namespace Ryujinx.Graphics.Gal -{ - static class EmbeddedResource - { - public static string GetString(string name) - { - Assembly asm = typeof(EmbeddedResource).Assembly; - - using (Stream resStream = asm.GetManifestResourceStream(name)) - { - StreamReader reader = new StreamReader(resStream); - - return reader.ReadToEnd(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalBlendEquation.cs b/Ryujinx.Graphics/Gal/GalBlendEquation.cs deleted file mode 100644 index 7757faae9..000000000 --- a/Ryujinx.Graphics/Gal/GalBlendEquation.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalBlendEquation - { - FuncAdd = 1, - FuncSubtract = 2, - FuncReverseSubtract = 3, - Min = 4, - Max = 5, - - FuncAddGl = 0x8006, - FuncSubtractGl = 0x8007, - FuncReverseSubtractGl = 0x8008, - MinGl = 0x800a, - MaxGl = 0x800b - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalBlendFactor.cs b/Ryujinx.Graphics/Gal/GalBlendFactor.cs deleted file mode 100644 index f70b05011..000000000 --- a/Ryujinx.Graphics/Gal/GalBlendFactor.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalBlendFactor - { - Zero = 0x1, - One = 0x2, - SrcColor = 0x3, - OneMinusSrcColor = 0x4, - SrcAlpha = 0x5, - OneMinusSrcAlpha = 0x6, - DstAlpha = 0x7, - OneMinusDstAlpha = 0x8, - DstColor = 0x9, - OneMinusDstColor = 0xa, - SrcAlphaSaturate = 0xb, - Src1Color = 0x10, - OneMinusSrc1Color = 0x11, - Src1Alpha = 0x12, - OneMinusSrc1Alpha = 0x13, - ConstantColor = 0x61, - OneMinusConstantColor = 0x62, - ConstantAlpha = 0x63, - OneMinusConstantAlpha = 0x64, - - ZeroGl = 0x4000, - OneGl = 0x4001, - SrcColorGl = 0x4300, - OneMinusSrcColorGl = 0x4301, - SrcAlphaGl = 0x4302, - OneMinusSrcAlphaGl = 0x4303, - DstAlphaGl = 0x4304, - OneMinusDstAlphaGl = 0x4305, - DstColorGl = 0x4306, - OneMinusDstColorGl = 0x4307, - SrcAlphaSaturateGl = 0x4308, - ConstantColorGl = 0xc001, - OneMinusConstantColorGl = 0xc002, - ConstantAlphaGl = 0xc003, - OneMinusConstantAlphaGl = 0xc004, - Src1ColorGl = 0xc900, - OneMinusSrc1ColorGl = 0xc901, - Src1AlphaGl = 0xc902, - OneMinusSrc1AlphaGl = 0xc903 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs b/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs deleted file mode 100644 index 8565051ca..000000000 --- a/Ryujinx.Graphics/Gal/GalClearBufferFlags.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal -{ - [Flags] - public enum GalClearBufferFlags - { - Depth = 1 << 0, - Stencil = 1 << 1, - ColorRed = 1 << 2, - ColorGreen = 1 << 3, - ColorBlue = 1 << 4, - ColorAlpha = 1 << 5 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalColorF.cs b/Ryujinx.Graphics/Gal/GalColorF.cs deleted file mode 100644 index e915870c2..000000000 --- a/Ryujinx.Graphics/Gal/GalColorF.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public struct GalColorF - { - public float Red { get; private set; } - public float Green { get; private set; } - public float Blue { get; private set; } - public float Alpha { get; private set; } - - public GalColorF( - float red, - float green, - float blue, - float alpha) - { - Red = red; - Green = green; - Blue = blue; - Alpha = alpha; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalComparisonOp.cs b/Ryujinx.Graphics/Gal/GalComparisonOp.cs deleted file mode 100644 index f26a77533..000000000 --- a/Ryujinx.Graphics/Gal/GalComparisonOp.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalComparisonOp - { - Never = 0x1, - Less = 0x2, - Equal = 0x3, - Lequal = 0x4, - Greater = 0x5, - NotEqual = 0x6, - Gequal = 0x7, - Always = 0x8 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalFrontFace.cs b/Ryujinx.Graphics/Gal/GalFrontFace.cs deleted file mode 100644 index 6cc4a8024..000000000 --- a/Ryujinx.Graphics/Gal/GalFrontFace.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalFrontFace - { - Cw = 0x900, - Ccw = 0x901 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalImage.cs b/Ryujinx.Graphics/Gal/GalImage.cs deleted file mode 100644 index 1345704d2..000000000 --- a/Ryujinx.Graphics/Gal/GalImage.cs +++ /dev/null @@ -1,87 +0,0 @@ -using Ryujinx.Graphics.Texture; - -namespace Ryujinx.Graphics.Gal -{ - public struct GalImage - { - public int Width; - public int Height; - - // FIXME: separate layer and depth - public int Depth; - public int LayerCount; - public int TileWidth; - public int GobBlockHeight; - public int GobBlockDepth; - public int Pitch; - public int MaxMipmapLevel; - - public GalImageFormat Format; - public GalMemoryLayout Layout; - public GalTextureSource XSource; - public GalTextureSource YSource; - public GalTextureSource ZSource; - public GalTextureSource WSource; - public GalTextureTarget TextureTarget; - - public GalImage( - int width, - int height, - int depth, - int layerCount, - int tileWidth, - int gobBlockHeight, - int gobBlockDepth, - GalMemoryLayout layout, - GalImageFormat format, - GalTextureTarget textureTarget, - int maxMipmapLevel = 1, - GalTextureSource xSource = GalTextureSource.Red, - GalTextureSource ySource = GalTextureSource.Green, - GalTextureSource zSource = GalTextureSource.Blue, - GalTextureSource wSource = GalTextureSource.Alpha) - { - Width = width; - Height = height; - LayerCount = layerCount; - Depth = depth; - TileWidth = tileWidth; - GobBlockHeight = gobBlockHeight; - GobBlockDepth = gobBlockDepth; - Layout = layout; - Format = format; - MaxMipmapLevel = maxMipmapLevel; - XSource = xSource; - YSource = ySource; - ZSource = zSource; - WSource = wSource; - TextureTarget = textureTarget; - - Pitch = ImageUtils.GetPitch(format, width); - } - - public bool SizeMatches(GalImage image, bool ignoreLayer = false) - { - if (ImageUtils.GetBytesPerPixel(Format) != - ImageUtils.GetBytesPerPixel(image.Format)) - { - return false; - } - - if (ImageUtils.GetAlignedWidth(this) != - ImageUtils.GetAlignedWidth(image)) - { - return false; - } - - bool result = Height == image.Height && Depth == image.Depth; - - if (!ignoreLayer) - { - result = result && LayerCount == image.LayerCount; - } - - return result; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalImageFormat.cs b/Ryujinx.Graphics/Gal/GalImageFormat.cs deleted file mode 100644 index 19ce6cfc4..000000000 --- a/Ryujinx.Graphics/Gal/GalImageFormat.cs +++ /dev/null @@ -1,69 +0,0 @@ -// ReSharper disable InconsistentNaming -using System; - -namespace Ryujinx.Graphics.Gal -{ - [Flags] - public enum GalImageFormat - { - Astc2DStart, - Astc2D4x4, - Astc2D5x4, - Astc2D5x5, - Astc2D6x5, - Astc2D6x6, - Astc2D8x5, - Astc2D8x6, - Astc2D8x8, - Astc2D10x5, - Astc2D10x6, - Astc2D10x8, - Astc2D10x10, - Astc2D12x10, - Astc2D12x12, - Astc2DEnd, - - Rgba4, - Rgb565, - Bgr565, - Bgr5A1, - Rgb5A1, - R8, - Rg8, - Rgbx8, - Rgba8, - Bgra8, - Rgb10A2, - R16, - Rg16, - Rgba16, - R32, - Rg32, - Rgba32, - R11G11B10, - D16, - D24, - D32, - D24S8, - D32S8, - BC1, - BC2, - BC3, - BC4, - BC5, - BptcSfloat, - BptcUfloat, - BptcUnorm, - - Snorm = 1 << 26, - Unorm = 1 << 27, - Sint = 1 << 28, - Uint = 1 << 39, - Float = 1 << 30, - Srgb = 1 << 31, - - TypeMask = Snorm | Unorm | Sint | Uint | Float | Srgb, - - FormatMask = ~TypeMask - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalIndexFormat.cs b/Ryujinx.Graphics/Gal/GalIndexFormat.cs deleted file mode 100644 index 71a50cdba..000000000 --- a/Ryujinx.Graphics/Gal/GalIndexFormat.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalIndexFormat - { - Byte = 0, - Int16 = 1, - Int32 = 2 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalMemoryLayout.cs b/Ryujinx.Graphics/Gal/GalMemoryLayout.cs deleted file mode 100644 index 73fabf8c2..000000000 --- a/Ryujinx.Graphics/Gal/GalMemoryLayout.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalMemoryLayout - { - BlockLinear = 0, - Pitch = 1 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs deleted file mode 100644 index c044a55fc..000000000 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ /dev/null @@ -1,130 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public struct ColorMaskState - { - private static readonly ColorMaskState DefaultBackingField = new ColorMaskState() - { - Red = true, - Green = true, - Blue = true, - Alpha = true - }; - - public static ColorMaskState Default => DefaultBackingField; - - public bool Red; - public bool Green; - public bool Blue; - public bool Alpha; - } - - public struct BlendState - { - private static readonly BlendState DefaultBackingField = new BlendState() - { - Enabled = false, - SeparateAlpha = false, - EquationRgb = GalBlendEquation.FuncAdd, - FuncSrcRgb = GalBlendFactor.One, - FuncDstRgb = GalBlendFactor.Zero, - EquationAlpha = GalBlendEquation.FuncAdd, - FuncSrcAlpha = GalBlendFactor.One, - FuncDstAlpha = GalBlendFactor.Zero - }; - - public static BlendState Default => DefaultBackingField; - - public bool Enabled; - public bool SeparateAlpha; - public GalBlendEquation EquationRgb; - public GalBlendFactor FuncSrcRgb; - public GalBlendFactor FuncDstRgb; - public GalBlendEquation EquationAlpha; - public GalBlendFactor FuncSrcAlpha; - public GalBlendFactor FuncDstAlpha; - } - - public class GalPipelineState - { - public const int Stages = 5; - public const int ConstBuffersPerStage = 18; - public const int RenderTargetsCount = 8; - - public long[][] ConstBufferKeys; - - public GalVertexBinding[] VertexBindings; - - public bool FramebufferSrgb; - - public float FlipX; - public float FlipY; - - public int Instance; - - public GalFrontFace FrontFace; - - public bool CullFaceEnabled; - public GalCullFace CullFace; - - public bool DepthTestEnabled; - public bool DepthWriteEnabled; - public GalComparisonOp DepthFunc; - public float DepthRangeNear; - public float DepthRangeFar; - - public bool StencilTestEnabled; - public bool StencilTwoSideEnabled; - - public GalComparisonOp StencilBackFuncFunc; - public int StencilBackFuncRef; - public uint StencilBackFuncMask; - public GalStencilOp StencilBackOpFail; - public GalStencilOp StencilBackOpZFail; - public GalStencilOp StencilBackOpZPass; - public uint StencilBackMask; - - public GalComparisonOp StencilFrontFuncFunc; - public int StencilFrontFuncRef; - public uint StencilFrontFuncMask; - public GalStencilOp StencilFrontOpFail; - public GalStencilOp StencilFrontOpZFail; - public GalStencilOp StencilFrontOpZPass; - public uint StencilFrontMask; - - public int ScissorTestCount; - public bool[] ScissorTestEnabled; - public int[] ScissorTestX; - public int[] ScissorTestY; - public int[] ScissorTestWidth; - public int[] ScissorTestHeight; - - public bool BlendIndependent; - public BlendState[] Blends; - - public bool ColorMaskCommon; - public ColorMaskState[] ColorMasks; - - public bool PrimitiveRestartEnabled; - public uint PrimitiveRestartIndex; - - public GalPipelineState() - { - ConstBufferKeys = new long[Stages][]; - - for (int stage = 0; stage < Stages; stage++) - { - ConstBufferKeys[stage] = new long[ConstBuffersPerStage]; - } - - Blends = new BlendState[RenderTargetsCount]; - - ScissorTestEnabled = new bool[RenderTargetsCount]; - ScissorTestY = new int[RenderTargetsCount]; - ScissorTestX = new int[RenderTargetsCount]; - ScissorTestWidth = new int[RenderTargetsCount]; - ScissorTestHeight = new int[RenderTargetsCount]; - - ColorMasks = new ColorMaskState[RenderTargetsCount]; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalPrimitiveType.cs b/Ryujinx.Graphics/Gal/GalPrimitiveType.cs deleted file mode 100644 index ce084149d..000000000 --- a/Ryujinx.Graphics/Gal/GalPrimitiveType.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalPrimitiveType - { - Points = 0x0, - Lines = 0x1, - LineLoop = 0x2, - LineStrip = 0x3, - Triangles = 0x4, - TriangleStrip = 0x5, - TriangleFan = 0x6, - Quads = 0x7, - QuadStrip = 0x8, - Polygon = 0x9, - LinesAdjacency = 0xa, - LineStripAdjacency = 0xb, - TrianglesAdjacency = 0xc, - TriangleStripAdjacency = 0xd, - Patches = 0xe - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalShaderType.cs b/Ryujinx.Graphics/Gal/GalShaderType.cs deleted file mode 100644 index eb5aaf889..000000000 --- a/Ryujinx.Graphics/Gal/GalShaderType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalShaderType - { - Vertex = 0, - TessControl = 1, - TessEvaluation = 2, - Geometry = 3, - Fragment = 4 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalStencilOp.cs b/Ryujinx.Graphics/Gal/GalStencilOp.cs deleted file mode 100644 index fc83ca5ea..000000000 --- a/Ryujinx.Graphics/Gal/GalStencilOp.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalStencilOp - { - Keep = 0x1, - Zero = 0x2, - Replace = 0x3, - Incr = 0x4, - Decr = 0x5, - Invert = 0x6, - IncrWrap = 0x7, - DecrWrap = 0x8 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs b/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs deleted file mode 100644 index 9cebcc272..000000000 --- a/Ryujinx.Graphics/Gal/GalSurfaceFormat.cs +++ /dev/null @@ -1,68 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalSurfaceFormat - { - Bitmap = 0x1c, - Unknown1D = 0x1d, - Rgba32Float = 0xc0, - Rgba32Sint = 0xc1, - Rgba32Uint = 0xc2, - Rgbx32Float = 0xc3, - Rgbx32Sint = 0xc4, - Rgbx32Uint = 0xc5, - Rgba16Unorm = 0xc6, - Rgba16Snorm = 0xc7, - Rgba16Sint = 0xc8, - Rgba16Uint = 0xc9, - Rgba16Float = 0xca, - Rg32Float = 0xcb, - Rg32Sint = 0xcc, - Rg32Uint = 0xcd, - Rgbx16Float = 0xce, - Bgra8Unorm = 0xcf, - Bgra8Srgb = 0xd0, - Rgb10A2Unorm = 0xd1, - Rgb10A2Uint = 0xd2, - Rgba8Unorm = 0xd5, - Rgba8Srgb = 0xd6, - Rgba8Snorm = 0xd7, - Rgba8Sint = 0xd8, - Rgba8Uint = 0xd9, - Rg16Unorm = 0xda, - Rg16Snorm = 0xdb, - Rg16Sint = 0xdc, - Rg16Uint = 0xdd, - Rg16Float = 0xde, - Bgr10A2Unorm = 0xdf, - R11G11B10Float = 0xe0, - R32Sint = 0xe3, - R32Uint = 0xe4, - R32Float = 0xe5, - Bgrx8Unorm = 0xe6, - Bgrx8Srgb = 0xe7, - B5G6R5Unorm = 0xe8, - Bgr5A1Unorm = 0xe9, - Rg8Unorm = 0xea, - Rg8Snorm = 0xeb, - Rg8Sint = 0xec, - Rg8Uint = 0xed, - R16Unorm = 0xee, - R16Snorm = 0xef, - R16Sint = 0xf0, - R16Uint = 0xf1, - R16Float = 0xf2, - R8Unorm = 0xf3, - R8Snorm = 0xf4, - R8Sint = 0xf5, - R8Uint = 0xf6, - A8Unorm = 0xf7, - Bgr5x1Unorm = 0xf8, - Rgbx8Unorm = 0xf9, - Rgbx8Srgb = 0xfa, - Bgr5x1UnormUnknownFB = 0xfb, - Bgr5x1UnormUnknownFC = 0xfc, - Bgrx8UnormUnknownFD = 0xfd, - Bgrx8UnormUnknownFE = 0xfe, - Y32UintUnknownFF = 0xff - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureFilter.cs b/Ryujinx.Graphics/Gal/GalTextureFilter.cs deleted file mode 100644 index 8e9669f00..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureFilter.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalTextureFilter - { - Nearest = 1, - Linear = 2 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs deleted file mode 100644 index ed27180a2..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalTextureFormat - { - Rgba32 = 0x1, - Rgba16 = 0x3, - Rg32 = 0x4, - Rgba8 = 0x8, - Rgb10A2 = 0x9, - Rg16 = 0xc, - R32 = 0xf, - BptcSfloat = 0x10, - BptcUfloat = 0x11, - Rgba4 = 0x12, - Rgb5A1 = 0x14, - Rgb565 = 0x15, - BptcUnorm = 0x17, - Rg8 = 0x18, - R16 = 0x1b, - R8 = 0x1d, - R11G11B10F = 0x21, - BC1 = 0x24, - BC2 = 0x25, - BC3 = 0x26, - BC4 = 0x27, - BC5 = 0x28, - D24S8 = 0x29, - D32F = 0x2f, - D32Fx24S8 = 0x30, - D16 = 0x3a, - Astc2D4x4 = 0x40, - Astc2D5x5 = 0x41, - Astc2D6x6 = 0x42, - Astc2D8x8 = 0x44, - Astc2D10x10 = 0x45, - Astc2D12x12 = 0x46, - Astc2D5x4 = 0x50, - Astc2D6x5 = 0x51, - Astc2D8x6 = 0x52, - Astc2D10x8 = 0x53, - Astc2D12x10 = 0x54, - Astc2D8x5 = 0x55, - Astc2D10x5 = 0x56, - Astc2D10x6 = 0x57 - } -} diff --git a/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs b/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs deleted file mode 100644 index 2123ec7d2..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureMipFilter.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalTextureMipFilter - { - None = 1, - Nearest = 2, - Linear = 3 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureSampler.cs b/Ryujinx.Graphics/Gal/GalTextureSampler.cs deleted file mode 100644 index 2e57a130c..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureSampler.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public struct GalTextureSampler - { - public GalTextureWrap AddressU { get; private set; } - public GalTextureWrap AddressV { get; private set; } - public GalTextureWrap AddressP { get; private set; } - - public GalTextureFilter MinFilter { get; private set; } - public GalTextureFilter MagFilter { get; private set; } - public GalTextureMipFilter MipFilter { get; private set; } - - public GalColorF BorderColor { get; private set; } - - public bool DepthCompare { get; private set; } - public DepthCompareFunc DepthCompareFunc { get; private set; } - - public GalTextureSampler( - GalTextureWrap addressU, - GalTextureWrap addressV, - GalTextureWrap addressP, - GalTextureFilter minFilter, - GalTextureFilter magFilter, - GalTextureMipFilter mipFilter, - GalColorF borderColor, - bool depthCompare, - DepthCompareFunc depthCompareFunc) - { - AddressU = addressU; - AddressV = addressV; - AddressP = addressP; - MinFilter = minFilter; - MagFilter = magFilter; - MipFilter = mipFilter; - BorderColor = borderColor; - - DepthCompare = depthCompare; - DepthCompareFunc = depthCompareFunc; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureSource.cs b/Ryujinx.Graphics/Gal/GalTextureSource.cs deleted file mode 100644 index 72dbec606..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureSource.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalTextureSource - { - Zero = 0, - Red = 2, - Green = 3, - Blue = 4, - Alpha = 5, - OneInt = 6, - OneFloat = 7 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureTarget.cs b/Ryujinx.Graphics/Gal/GalTextureTarget.cs deleted file mode 100644 index bcc0c49a5..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureTarget.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalTextureTarget - { - OneD = 0, - TwoD = 1, - ThreeD = 2, - CubeMap = 3, - OneDArray = 4, - TwoDArray = 5, - OneDBuffer = 6, - TwoDNoMipMap = 7, - CubeArray = 8, - } -} diff --git a/Ryujinx.Graphics/Gal/GalTextureType.cs b/Ryujinx.Graphics/Gal/GalTextureType.cs deleted file mode 100644 index b02b8b37b..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalTextureType - { - Snorm = 1, - Unorm = 2, - Sint = 3, - Uint = 4, - SnormForceFp16 = 5, - UnormForceFp16 = 6, - Float = 7 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureWrap.cs b/Ryujinx.Graphics/Gal/GalTextureWrap.cs deleted file mode 100644 index 66e531540..000000000 --- a/Ryujinx.Graphics/Gal/GalTextureWrap.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalTextureWrap - { - Repeat = 0, - MirroredRepeat = 1, - ClampToEdge = 2, - ClampToBorder = 3, - Clamp = 4, - MirrorClampToEdge = 5, - MirrorClampToBorder = 6, - MirrorClamp = 7 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs deleted file mode 100644 index feca5aea3..000000000 --- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public struct GalVertexAttrib - { - public int Index { get; private set; } - public bool IsConst { get; private set; } - public int Offset { get; private set; } - public byte[] Data { get; private set; } - - public GalVertexAttribSize Size { get; private set; } - public GalVertexAttribType Type { get; private set; } - - public bool IsBgra { get; private set; } - - public GalVertexAttrib( - int index, - bool isConst, - int offset, - byte[] data, - GalVertexAttribSize size, - GalVertexAttribType type, - bool isBgra) - { - Index = index; - IsConst = isConst; - Data = data; - Offset = offset; - Size = size; - Type = type; - IsBgra = isBgra; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs b/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs deleted file mode 100644 index d3ce60ace..000000000 --- a/Ryujinx.Graphics/Gal/GalVertexAttribSize.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalVertexAttribSize - { - _32_32_32_32 = 0x1, - _32_32_32 = 0x2, - _16_16_16_16 = 0x3, - _32_32 = 0x4, - _16_16_16 = 0x5, - _8_8_8_8 = 0xa, - _16_16 = 0xf, - _32 = 0x12, - _8_8_8 = 0x13, - _8_8 = 0x18, - _16 = 0x1b, - _8 = 0x1d, - _10_10_10_2 = 0x30, - _11_11_10 = 0x31 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexAttribType.cs b/Ryujinx.Graphics/Gal/GalVertexAttribType.cs deleted file mode 100644 index 358836fda..000000000 --- a/Ryujinx.Graphics/Gal/GalVertexAttribType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalVertexAttribType - { - Snorm = 1, - Unorm = 2, - Sint = 3, - Uint = 4, - Uscaled = 5, - Sscaled = 6, - Float = 7 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalVertexBinding.cs b/Ryujinx.Graphics/Gal/GalVertexBinding.cs deleted file mode 100644 index 2337c2e32..000000000 --- a/Ryujinx.Graphics/Gal/GalVertexBinding.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public struct GalVertexBinding - { - // VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3 - - public bool Enabled; - public int Stride; - public long VboKey; - public bool Instanced; - public int Divisor; - public GalVertexAttrib[] Attribs; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalZetaFormat.cs b/Ryujinx.Graphics/Gal/GalZetaFormat.cs deleted file mode 100644 index 2429249e5..000000000 --- a/Ryujinx.Graphics/Gal/GalZetaFormat.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public enum GalZetaFormat - { - D32Float = 0x0a, - D16Unorm = 0x13, - S8D24Unorm = 0x14, - D24X8Unorm = 0x15, - D24S8Unorm = 0x16, - D24C8Unorm = 0x18, - D32S8X24Float = 0x19, - D24X8S8C8X16Unorm = 0x1d, - D32X8C8X16Float = 0x1e, - D32S8C8X16Float = 0x1f - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs b/Ryujinx.Graphics/Gal/IGalConstBuffer.cs deleted file mode 100644 index 8c4e6d032..000000000 --- a/Ryujinx.Graphics/Gal/IGalConstBuffer.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal -{ - public interface IGalConstBuffer - { - void LockCache(); - void UnlockCache(); - - void Create(long key, long size); - - bool IsCached(long key, long size); - - void SetData(long key, long size, IntPtr hostAddress); - void SetData(long key, byte[] data); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalMemory.cs b/Ryujinx.Graphics/Gal/IGalMemory.cs deleted file mode 100644 index 78eb7154b..000000000 --- a/Ryujinx.Graphics/Gal/IGalMemory.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public interface IGalMemory - { - int ReadInt32(long position); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalPipeline.cs b/Ryujinx.Graphics/Gal/IGalPipeline.cs deleted file mode 100644 index 1ecb26028..000000000 --- a/Ryujinx.Graphics/Gal/IGalPipeline.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public interface IGalPipeline - { - void Bind(GalPipelineState state); - void Unbind(GalPipelineState state); - - void ResetDepthMask(); - void ResetColorMask(int index); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs deleted file mode 100644 index 33bdeaad8..000000000 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal -{ - public interface IGalRasterizer - { - void LockCaches(); - void UnlockCaches(); - - void ClearBuffers( - GalClearBufferFlags flags, - int attachment, - float red, - float green, - float blue, - float alpha, - float depth, - int stencil); - - bool IsVboCached(long key, long dataSize); - - bool IsIboCached(long key, long dataSize); - - void CreateVbo(long key, int dataSize, IntPtr hostAddress); - void CreateVbo(long key, byte[] data); - - void CreateIbo(long key, int dataSize, IntPtr hostAddress); - void CreateIbo(long key, int dataSize, byte[] buffer); - - void SetIndexArray(int size, GalIndexFormat format); - - void DrawArrays(int first, int count, GalPrimitiveType primType); - - void DrawElements(long iboKey, int first, int vertexBase, GalPrimitiveType primType); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs deleted file mode 100644 index c281fe06c..000000000 --- a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs +++ /dev/null @@ -1,45 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public interface IGalRenderTarget - { - void Bind(); - - void BindColor(long key, int attachment); - - void UnbindColor(int attachment); - - void BindZeta(long key); - - void UnbindZeta(); - - void Present(long key); - - void SetMap(int[] map); - - void SetTransform(bool flipX, bool flipY, int top, int left, int right, int bottom); - - void SetWindowSize(int width, int height); - - void SetViewport(int attachment, int x, int y, int width, int height); - - void Render(); - - void Copy( - GalImage srcImage, - GalImage dstImage, - long srcKey, - long dstKey, - int srcLayer, - int dstLayer, - int srcX0, - int srcY0, - int srcX1, - int srcY1, - int dstX0, - int dstY0, - int dstX1, - int dstY1); - - void Reinterpret(long key, GalImage newImage); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRenderer.cs b/Ryujinx.Graphics/Gal/IGalRenderer.cs deleted file mode 100644 index 1acc4d03b..000000000 --- a/Ryujinx.Graphics/Gal/IGalRenderer.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal -{ - public interface IGalRenderer - { - void QueueAction(Action actionMthd); - - void RunActions(); - - IGalConstBuffer Buffer { get; } - - IGalRenderTarget RenderTarget { get; } - - IGalRasterizer Rasterizer { get; } - - IGalShader Shader { get; } - - IGalPipeline Pipeline { get; } - - IGalTexture Texture { get; } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalShader.cs b/Ryujinx.Graphics/Gal/IGalShader.cs deleted file mode 100644 index 6a9abe75b..000000000 --- a/Ryujinx.Graphics/Gal/IGalShader.cs +++ /dev/null @@ -1,21 +0,0 @@ -using Ryujinx.Graphics.Shader; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal -{ - public interface IGalShader - { - void Create(IGalMemory memory, long key, GalShaderType type); - - void Create(IGalMemory memory, long vpAPos, long key, GalShaderType type); - - IEnumerable GetConstBufferUsage(long key); - IEnumerable GetTextureUsage(long key); - - void Bind(long key); - - void Unbind(GalShaderType type); - - void BindProgram(); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalTexture.cs b/Ryujinx.Graphics/Gal/IGalTexture.cs deleted file mode 100644 index 23ce054ae..000000000 --- a/Ryujinx.Graphics/Gal/IGalTexture.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Ryujinx.Graphics.Gal -{ - public interface IGalTexture - { - void LockCache(); - void UnlockCache(); - - void Create(long key, int size, GalImage image); - - void Create(long key, byte[] data, GalImage image); - - bool TryGetImage(long key, out GalImage image); - - void Bind(long key, int index, GalImage image); - - void SetSampler(GalImage image, GalTextureSampler sampler); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs b/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs deleted file mode 100644 index 63b626f18..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/DeleteValueCallback.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Ryujinx.Graphics.Gal.OpenGL -{ - delegate void DeleteValue(T value); -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs b/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs deleted file mode 100644 index d7f6f0044..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/ImageHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Ryujinx.Graphics.Texture; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class ImageHandler - { - public GalImage Image { get; private set; } - - public int Width => Image.Width; - public int Height => Image.Height; - public int Depth => Image.Depth; - - public GalImageFormat Format => Image.Format; - - public int Handle { get; private set; } - - public bool HasColor => ImageUtils.HasColor(Image.Format); - public bool HasDepth => ImageUtils.HasDepth(Image.Format); - public bool HasStencil => ImageUtils.HasStencil(Image.Format); - - public ImageHandler(int handle, GalImage image) - { - Handle = handle; - Image = image; - } - } -} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs b/Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs deleted file mode 100644 index 91f0a7e16..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglCachedResource.cs +++ /dev/null @@ -1,191 +0,0 @@ -using Ryujinx.Common; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglCachedResource - { - public delegate void DeleteValue(T value); - - private const int MinTimeDelta = 5 * 60000; - private const int MaxRemovalsPerRun = 10; - - private struct CacheBucket - { - public T Value { get; private set; } - - public LinkedListNode Node { get; private set; } - - public long DataSize { get; private set; } - - public long Timestamp { get; private set; } - - public CacheBucket(T value, long dataSize, LinkedListNode node) - { - Value = value; - DataSize = dataSize; - Node = node; - - Timestamp = PerformanceCounter.ElapsedMilliseconds; - } - } - - private Dictionary _cache; - - private LinkedList _sortedCache; - - private DeleteValue _deleteValueCallback; - - private Queue _deletePending; - - private bool _locked; - - private long _maxSize; - private long _totalSize; - - public OglCachedResource(DeleteValue deleteValueCallback, long maxSize) - { - _maxSize = maxSize; - - if (deleteValueCallback == null) - { - throw new ArgumentNullException(nameof(deleteValueCallback)); - } - - _deleteValueCallback = deleteValueCallback; - - _cache = new Dictionary(); - - _sortedCache = new LinkedList(); - - _deletePending = new Queue(); - } - - public void Lock() - { - _locked = true; - } - - public void Unlock() - { - _locked = false; - - while (_deletePending.TryDequeue(out T value)) - { - _deleteValueCallback(value); - } - - ClearCacheIfNeeded(); - } - - public void AddOrUpdate(long key, T value, long size) - { - if (!_locked) - { - ClearCacheIfNeeded(); - } - - LinkedListNode node = _sortedCache.AddLast(key); - - CacheBucket newBucket = new CacheBucket(value, size, node); - - if (_cache.TryGetValue(key, out CacheBucket bucket)) - { - if (_locked) - { - _deletePending.Enqueue(bucket.Value); - } - else - { - _deleteValueCallback(bucket.Value); - } - - _sortedCache.Remove(bucket.Node); - - _totalSize -= bucket.DataSize; - - _cache[key] = newBucket; - } - else - { - _cache.Add(key, newBucket); - } - - _totalSize += size; - } - - public bool TryGetValue(long key, out T value) - { - if (_cache.TryGetValue(key, out CacheBucket bucket)) - { - value = bucket.Value; - - _sortedCache.Remove(bucket.Node); - - LinkedListNode node = _sortedCache.AddLast(key); - - _cache[key] = new CacheBucket(value, bucket.DataSize, node); - - return true; - } - - value = default(T); - - return false; - } - - public bool TryGetSize(long key, out long size) - { - if (_cache.TryGetValue(key, out CacheBucket bucket)) - { - size = bucket.DataSize; - - return true; - } - - size = 0; - - return false; - } - - private void ClearCacheIfNeeded() - { - long timestamp = PerformanceCounter.ElapsedMilliseconds; - - int count = 0; - - while (count++ < MaxRemovalsPerRun) - { - LinkedListNode node = _sortedCache.First; - - if (node == null) - { - break; - } - - CacheBucket bucket = _cache[node.Value]; - - long timeDelta = timestamp - bucket.Timestamp; - - if (timeDelta <= MinTimeDelta && !UnderMemoryPressure()) - { - break; - } - - _sortedCache.Remove(node); - - _cache.Remove(node.Value); - - _deleteValueCallback(bucket.Value); - - _totalSize -= bucket.DataSize; - } - } - - private bool UnderMemoryPressure() - { - return _totalSize >= _maxSize; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs deleted file mode 100644 index e076be336..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglConstBuffer.cs +++ /dev/null @@ -1,74 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglConstBuffer : IGalConstBuffer - { - private const long MaxConstBufferCacheSize = 64 * 1024 * 1024; - - private OglCachedResource _cache; - - public OglConstBuffer() - { - _cache = new OglCachedResource(DeleteBuffer, MaxConstBufferCacheSize); - } - - public void LockCache() - { - _cache.Lock(); - } - - public void UnlockCache() - { - _cache.Unlock(); - } - - public void Create(long key, long size) - { - OglStreamBuffer buffer = new OglStreamBuffer(BufferTarget.UniformBuffer, size); - - _cache.AddOrUpdate(key, buffer, size); - } - - public bool IsCached(long key, long size) - { - return _cache.TryGetSize(key, out long cachedSize) && cachedSize == size; - } - - public void SetData(long key, long size, IntPtr hostAddress) - { - if (_cache.TryGetValue(key, out OglStreamBuffer buffer)) - { - buffer.SetData(size, hostAddress); - } - } - - public void SetData(long key, byte[] data) - { - if (_cache.TryGetValue(key, out OglStreamBuffer buffer)) - { - buffer.SetData(data); - } - } - - public bool TryGetUbo(long key, out int uboHandle) - { - if (_cache.TryGetValue(key, out OglStreamBuffer buffer)) - { - uboHandle = buffer.Handle; - - return true; - } - - uboHandle = 0; - - return false; - } - - private static void DeleteBuffer(OglStreamBuffer buffer) - { - buffer.Dispose(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OglEnumConverter.cs deleted file mode 100644 index c36d8bf93..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglEnumConverter.cs +++ /dev/null @@ -1,427 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - static class OglEnumConverter - { - public static FrontFaceDirection GetFrontFace(GalFrontFace frontFace) - { - switch (frontFace) - { - case GalFrontFace.Cw: return FrontFaceDirection.Cw; - case GalFrontFace.Ccw: return FrontFaceDirection.Ccw; - } - - throw new ArgumentException(nameof(frontFace) + " \"" + frontFace + "\" is not valid!"); - } - - public static CullFaceMode GetCullFace(GalCullFace cullFace) - { - switch (cullFace) - { - case GalCullFace.Front: return CullFaceMode.Front; - case GalCullFace.Back: return CullFaceMode.Back; - case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack; - } - - throw new ArgumentException(nameof(cullFace) + " \"" + cullFace + "\" is not valid!"); - } - - public static StencilOp GetStencilOp(GalStencilOp op) - { - switch (op) - { - case GalStencilOp.Keep: return StencilOp.Keep; - case GalStencilOp.Zero: return StencilOp.Zero; - case GalStencilOp.Replace: return StencilOp.Replace; - case GalStencilOp.Incr: return StencilOp.Incr; - case GalStencilOp.Decr: return StencilOp.Decr; - case GalStencilOp.Invert: return StencilOp.Invert; - case GalStencilOp.IncrWrap: return StencilOp.IncrWrap; - case GalStencilOp.DecrWrap: return StencilOp.DecrWrap; - } - - throw new ArgumentException(nameof(op) + " \"" + op + "\" is not valid!"); - } - - public static DepthFunction GetDepthFunc(GalComparisonOp func) - { - return (DepthFunction)GetFunc(func); - } - - public static StencilFunction GetStencilFunc(GalComparisonOp func) - { - return (StencilFunction)GetFunc(func); - } - - private static All GetFunc(GalComparisonOp func) - { - if ((int)func >= (int)All.Never && - (int)func <= (int)All.Always) - { - return (All)func; - } - - switch (func) - { - case GalComparisonOp.Never: return All.Never; - case GalComparisonOp.Less: return All.Less; - case GalComparisonOp.Equal: return All.Equal; - case GalComparisonOp.Lequal: return All.Lequal; - case GalComparisonOp.Greater: return All.Greater; - case GalComparisonOp.NotEqual: return All.Notequal; - case GalComparisonOp.Gequal: return All.Gequal; - case GalComparisonOp.Always: return All.Always; - } - - throw new ArgumentException(nameof(func) + " \"" + func + "\" is not valid!"); - } - - public static DrawElementsType GetDrawElementsType(GalIndexFormat format) - { - switch (format) - { - case GalIndexFormat.Byte: return DrawElementsType.UnsignedByte; - case GalIndexFormat.Int16: return DrawElementsType.UnsignedShort; - case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt; - } - - throw new ArgumentException(nameof(format) + " \"" + format + "\" is not valid!"); - } - - public static PrimitiveType GetPrimitiveType(GalPrimitiveType type) - { - switch (type) - { - case GalPrimitiveType.Points: return PrimitiveType.Points; - case GalPrimitiveType.Lines: return PrimitiveType.Lines; - case GalPrimitiveType.LineLoop: return PrimitiveType.LineLoop; - case GalPrimitiveType.LineStrip: return PrimitiveType.LineStrip; - case GalPrimitiveType.Triangles: return PrimitiveType.Triangles; - case GalPrimitiveType.TriangleStrip: return PrimitiveType.TriangleStrip; - case GalPrimitiveType.TriangleFan: return PrimitiveType.TriangleFan; - case GalPrimitiveType.Polygon: return PrimitiveType.Polygon; - case GalPrimitiveType.LinesAdjacency: return PrimitiveType.LinesAdjacency; - case GalPrimitiveType.LineStripAdjacency: return PrimitiveType.LineStripAdjacency; - case GalPrimitiveType.TrianglesAdjacency: return PrimitiveType.TrianglesAdjacency; - case GalPrimitiveType.TriangleStripAdjacency: return PrimitiveType.TriangleStripAdjacency; - case GalPrimitiveType.Patches: return PrimitiveType.Patches; - } - - throw new ArgumentException(nameof(type) + " \"" + type + "\" is not valid!"); - } - - public static ShaderType GetShaderType(GalShaderType type) - { - switch (type) - { - case GalShaderType.Vertex: return ShaderType.VertexShader; - case GalShaderType.TessControl: return ShaderType.TessControlShader; - case GalShaderType.TessEvaluation: return ShaderType.TessEvaluationShader; - case GalShaderType.Geometry: return ShaderType.GeometryShader; - case GalShaderType.Fragment: return ShaderType.FragmentShader; - } - - throw new ArgumentException(nameof(type) + " \"" + type + "\" is not valid!"); - } - - public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat format) - { - switch (format) - { - case GalImageFormat.Rgba32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float); - case GalImageFormat.Rgba32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int); - case GalImageFormat.Rgba32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt); - case GalImageFormat.Rgba16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat); - case GalImageFormat.Rgba16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short); - case GalImageFormat.Rgba16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort); - case GalImageFormat.Rgba16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba16, PixelFormat.Rgba, PixelType.UnsignedShort); - case GalImageFormat.Rg32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float); - case GalImageFormat.Rg32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int); - case GalImageFormat.Rg32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt); - case GalImageFormat.Rgbx8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb8, PixelFormat.Rgba, PixelType.UnsignedByte); - case GalImageFormat.Rgba8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte); - case GalImageFormat.Rgba8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte); - case GalImageFormat.Rgba8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte); - case GalImageFormat.Rgba8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte); - case GalImageFormat.Rgba8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte); - case GalImageFormat.Bgra8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte); - case GalImageFormat.Bgra8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Bgra, PixelType.UnsignedByte); - case GalImageFormat.Rgba4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed); - case GalImageFormat.Rgb10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed); - case GalImageFormat.Rgb10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); - case GalImageFormat.R32 | GalImageFormat.Float: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float); - case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int); - case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt); - case GalImageFormat.Bgr5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort5551); - case GalImageFormat.Rgb5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed); - case GalImageFormat.Rgb565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed); - case GalImageFormat.Bgr565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565); - case GalImageFormat.Rg16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat); - case GalImageFormat.Rg16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short); - case GalImageFormat.Rg16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short); - case GalImageFormat.Rg16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort); - case GalImageFormat.Rg16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort); - case GalImageFormat.Rg8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte); - case GalImageFormat.Rg8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); - case GalImageFormat.Rg8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte); - case GalImageFormat.Rg8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); - case GalImageFormat.R16 | GalImageFormat.Float: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat); - case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short); - case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Short); - case GalImageFormat.R16 | GalImageFormat.Uint: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort); - case GalImageFormat.R16 | GalImageFormat.Unorm: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort); - case GalImageFormat.R8 | GalImageFormat.Sint: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte); - case GalImageFormat.R8 | GalImageFormat.Snorm: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte); - case GalImageFormat.R8 | GalImageFormat.Uint: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte); - case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte); - case GalImageFormat.R11G11B10 | GalImageFormat.Float: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); - - case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort); - case GalImageFormat.D24 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt); - case GalImageFormat.D24S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); - case GalImageFormat.D24S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); - case GalImageFormat.D32 | GalImageFormat.Float: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float); - case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev); - } - - throw new NotImplementedException($"{format & GalImageFormat.FormatMask} {format & GalImageFormat.TypeMask}"); - } - - public static All GetDepthCompareFunc(DepthCompareFunc depthCompareFunc) - { - switch (depthCompareFunc) - { - case DepthCompareFunc.LEqual: - return All.Lequal; - case DepthCompareFunc.GEqual: - return All.Gequal; - case DepthCompareFunc.Less: - return All.Less; - case DepthCompareFunc.Greater: - return All.Greater; - case DepthCompareFunc.Equal: - return All.Equal; - case DepthCompareFunc.NotEqual: - return All.Notequal; - case DepthCompareFunc.Always: - return All.Always; - case DepthCompareFunc.Never: - return All.Never; - default: - throw new ArgumentException(nameof(depthCompareFunc) + " \"" + depthCompareFunc + "\" is not valid!"); - } - } - - public static InternalFormat GetCompressedImageFormat(GalImageFormat format) - { - switch (format) - { - case GalImageFormat.BptcSfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcSignedFloat; - case GalImageFormat.BptcUfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcUnsignedFloat; - case GalImageFormat.BptcUnorm | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm; - case GalImageFormat.BptcUnorm | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaBptcUnorm; - case GalImageFormat.BC1 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext; - case GalImageFormat.BC1 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt1Ext; - case GalImageFormat.BC2 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext; - case GalImageFormat.BC2 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt3Ext; - case GalImageFormat.BC3 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext; - case GalImageFormat.BC3 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt5Ext; - case GalImageFormat.BC4 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1; - case GalImageFormat.BC4 | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1; - case GalImageFormat.BC5 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2; - case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2; - } - - throw new NotImplementedException($"{format & GalImageFormat.FormatMask} {format & GalImageFormat.TypeMask}"); - } - - public static All GetTextureSwizzle(GalTextureSource source) - { - switch (source) - { - case GalTextureSource.Zero: return All.Zero; - case GalTextureSource.Red: return All.Red; - case GalTextureSource.Green: return All.Green; - case GalTextureSource.Blue: return All.Blue; - case GalTextureSource.Alpha: return All.Alpha; - case GalTextureSource.OneInt: return All.One; - case GalTextureSource.OneFloat: return All.One; - } - - throw new ArgumentException(nameof(source) + " \"" + source + "\" is not valid!"); - } - - public static TextureWrapMode GetTextureWrapMode(GalTextureWrap wrap) - { - switch (wrap) - { - case GalTextureWrap.Repeat: return TextureWrapMode.Repeat; - case GalTextureWrap.MirroredRepeat: return TextureWrapMode.MirroredRepeat; - case GalTextureWrap.ClampToEdge: return TextureWrapMode.ClampToEdge; - case GalTextureWrap.ClampToBorder: return TextureWrapMode.ClampToBorder; - case GalTextureWrap.Clamp: return TextureWrapMode.Clamp; - } - - if (OglExtension.TextureMirrorClamp) - { - switch (wrap) - { - case GalTextureWrap.MirrorClampToEdge: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToEdgeExt; - case GalTextureWrap.MirrorClampToBorder: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampToBorderExt; - case GalTextureWrap.MirrorClamp: return (TextureWrapMode)ExtTextureMirrorClamp.MirrorClampExt; - } - } - else - { - // Fallback to non-mirrored clamps - switch (wrap) - { - case GalTextureWrap.MirrorClampToEdge: return TextureWrapMode.ClampToEdge; - case GalTextureWrap.MirrorClampToBorder: return TextureWrapMode.ClampToBorder; - case GalTextureWrap.MirrorClamp: return TextureWrapMode.Clamp; - } - } - - throw new ArgumentException(nameof(wrap) + " \"" + wrap + "\" is not valid!"); - } - - public static TextureMinFilter GetTextureMinFilter( - GalTextureFilter minFilter, - GalTextureMipFilter mipFilter) - { - // TODO: Mip (needs mipmap support first). - switch (minFilter) - { - case GalTextureFilter.Nearest: return TextureMinFilter.Nearest; - case GalTextureFilter.Linear: return TextureMinFilter.Linear; - } - - throw new ArgumentException(nameof(minFilter) + " \"" + minFilter + "\" is not valid!"); - } - - public static TextureMagFilter GetTextureMagFilter(GalTextureFilter filter) - { - switch (filter) - { - case GalTextureFilter.Nearest: return TextureMagFilter.Nearest; - case GalTextureFilter.Linear: return TextureMagFilter.Linear; - } - - throw new ArgumentException(nameof(filter) + " \"" + filter + "\" is not valid!"); - } - - public static BlendEquationMode GetBlendEquation(GalBlendEquation blendEquation) - { - switch (blendEquation) - { - case GalBlendEquation.FuncAdd: - case GalBlendEquation.FuncAddGl: - return BlendEquationMode.FuncAdd; - - case GalBlendEquation.FuncSubtract: - case GalBlendEquation.FuncSubtractGl: - return BlendEquationMode.FuncSubtract; - - case GalBlendEquation.FuncReverseSubtract: - case GalBlendEquation.FuncReverseSubtractGl: - return BlendEquationMode.FuncReverseSubtract; - - case GalBlendEquation.Min: - case GalBlendEquation.MinGl: - return BlendEquationMode.Min; - - case GalBlendEquation.Max: - case GalBlendEquation.MaxGl: - return BlendEquationMode.Max; - } - - throw new ArgumentException(nameof(blendEquation) + " \"" + blendEquation + "\" is not valid!"); - } - - public static BlendingFactor GetBlendFactor(GalBlendFactor blendFactor) - { - switch (blendFactor) - { - case GalBlendFactor.Zero: - case GalBlendFactor.ZeroGl: - return BlendingFactor.Zero; - - case GalBlendFactor.One: - case GalBlendFactor.OneGl: - return BlendingFactor.One; - - case GalBlendFactor.SrcColor: - case GalBlendFactor.SrcColorGl: - return BlendingFactor.SrcColor; - - case GalBlendFactor.OneMinusSrcColor: - case GalBlendFactor.OneMinusSrcColorGl: - return BlendingFactor.OneMinusSrcColor; - - case GalBlendFactor.DstColor: - case GalBlendFactor.DstColorGl: - return BlendingFactor.DstColor; - - case GalBlendFactor.OneMinusDstColor: - case GalBlendFactor.OneMinusDstColorGl: - return BlendingFactor.OneMinusDstColor; - - case GalBlendFactor.SrcAlpha: - case GalBlendFactor.SrcAlphaGl: - return BlendingFactor.SrcAlpha; - - case GalBlendFactor.OneMinusSrcAlpha: - case GalBlendFactor.OneMinusSrcAlphaGl: - return BlendingFactor.OneMinusSrcAlpha; - - case GalBlendFactor.DstAlpha: - case GalBlendFactor.DstAlphaGl: - return BlendingFactor.DstAlpha; - - case GalBlendFactor.OneMinusDstAlpha: - case GalBlendFactor.OneMinusDstAlphaGl: - return BlendingFactor.OneMinusDstAlpha; - - case GalBlendFactor.OneMinusConstantColor: - case GalBlendFactor.OneMinusConstantColorGl: - return BlendingFactor.OneMinusConstantColor; - - case GalBlendFactor.ConstantAlpha: - case GalBlendFactor.ConstantAlphaGl: - return BlendingFactor.ConstantAlpha; - - case GalBlendFactor.OneMinusConstantAlpha: - case GalBlendFactor.OneMinusConstantAlphaGl: - return BlendingFactor.OneMinusConstantAlpha; - - case GalBlendFactor.SrcAlphaSaturate: - case GalBlendFactor.SrcAlphaSaturateGl: - return BlendingFactor.SrcAlphaSaturate; - - case GalBlendFactor.Src1Color: - case GalBlendFactor.Src1ColorGl: - return BlendingFactor.Src1Color; - - case GalBlendFactor.OneMinusSrc1Color: - case GalBlendFactor.OneMinusSrc1ColorGl: - return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Color; - - case GalBlendFactor.Src1Alpha: - case GalBlendFactor.Src1AlphaGl: - return BlendingFactor.Src1Alpha; - - case GalBlendFactor.OneMinusSrc1Alpha: - case GalBlendFactor.OneMinusSrc1AlphaGl: - return (BlendingFactor)BlendingFactorSrc.OneMinusSrc1Alpha; - - case GalBlendFactor.ConstantColor: - case GalBlendFactor.ConstantColorGl: - return BlendingFactor.ConstantColor; - } - - throw new ArgumentException(nameof(blendFactor) + " \"" + blendFactor + "\" is not valid!"); - } - } -} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs deleted file mode 100644 index 8a1a0510f..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglExtension.cs +++ /dev/null @@ -1,70 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Common.Logging; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - static class OglExtension - { - // Private lazy backing variables - private static Lazy _enhancedLayouts = new Lazy(() => HasExtension("GL_ARB_enhanced_layouts")); - private static Lazy _textureMirrorClamp = new Lazy(() => HasExtension("GL_EXT_texture_mirror_clamp")); - private static Lazy _viewportArray = new Lazy(() => HasExtension("GL_ARB_viewport_array")); - - private static Lazy _nvidiaDriver = new Lazy(() => IsNvidiaDriver()); - - // Public accessors - public static bool EnhancedLayouts => _enhancedLayouts.Value; - public static bool TextureMirrorClamp => _textureMirrorClamp.Value; - public static bool ViewportArray => _viewportArray.Value; - - public static bool NvidiaDriver => _nvidiaDriver.Value; - - private static bool HasExtension(string name) - { - int numExtensions = GL.GetInteger(GetPName.NumExtensions); - - for (int extension = 0; extension < numExtensions; extension++) - { - if (GL.GetString(StringNameIndexed.Extensions, extension) == name) - { - return true; - } - } - - Logger.PrintInfo(LogClass.Gpu, $"OpenGL extension {name} unavailable. You may experience some performance degradation"); - - return false; - } - - private static bool IsNvidiaDriver() - { - return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation"); - } - - public static class Required - { - // Public accessors - public static bool EnhancedLayouts => _enhancedLayoutsRequired.Value; - public static bool TextureMirrorClamp => _textureMirrorClampRequired.Value; - public static bool ViewportArray => _viewportArrayRequired.Value; - - // Private lazy backing variables - private static Lazy _enhancedLayoutsRequired = new Lazy(() => HasExtensionRequired(OglExtension.EnhancedLayouts, "GL_ARB_enhanced_layouts")); - private static Lazy _textureMirrorClampRequired = new Lazy(() => HasExtensionRequired(OglExtension.TextureMirrorClamp, "GL_EXT_texture_mirror_clamp")); - private static Lazy _viewportArrayRequired = new Lazy(() => HasExtensionRequired(OglExtension.ViewportArray, "GL_ARB_viewport_array")); - - private static bool HasExtensionRequired(bool value, string name) - { - if (value) - { - return true; - } - - Logger.PrintWarning(LogClass.Gpu, $"Required OpenGL extension {name} unavailable. You may experience some rendering issues"); - - return false; - } - } - } -} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs b/Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs deleted file mode 100644 index 2a227a374..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglLimit.cs +++ /dev/null @@ -1,12 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - static class OglLimit - { - private static Lazy _sMaxUboSize = new Lazy(() => GL.GetInteger(GetPName.MaxUniformBlockSize)); - - public static int MaxUboSize => _sMaxUboSize.Value; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OglPipeline.cs deleted file mode 100644 index 8d886be4e..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglPipeline.cs +++ /dev/null @@ -1,833 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.Shader; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglPipeline : IGalPipeline - { - private static Dictionary _attribElements = - new Dictionary() - { - { GalVertexAttribSize._32_32_32_32, 4 }, - { GalVertexAttribSize._32_32_32, 3 }, - { GalVertexAttribSize._16_16_16_16, 4 }, - { GalVertexAttribSize._32_32, 2 }, - { GalVertexAttribSize._16_16_16, 3 }, - { GalVertexAttribSize._8_8_8_8, 4 }, - { GalVertexAttribSize._16_16, 2 }, - { GalVertexAttribSize._32, 1 }, - { GalVertexAttribSize._8_8_8, 3 }, - { GalVertexAttribSize._8_8, 2 }, - { GalVertexAttribSize._16, 1 }, - { GalVertexAttribSize._8, 1 }, - { GalVertexAttribSize._10_10_10_2, 4 }, - { GalVertexAttribSize._11_11_10, 3 } - }; - - private static Dictionary _floatAttribTypes = - new Dictionary() - { - { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float }, - { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Float }, - { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.HalfFloat }, - { GalVertexAttribSize._32_32, VertexAttribPointerType.Float }, - { GalVertexAttribSize._16_16_16, VertexAttribPointerType.HalfFloat }, - { GalVertexAttribSize._16_16, VertexAttribPointerType.HalfFloat }, - { GalVertexAttribSize._32, VertexAttribPointerType.Float }, - { GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat } - }; - - private static Dictionary _signedAttribTypes = - new Dictionary() - { - { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._32_32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._16_16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._16_16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._32, VertexAttribPointerType.Int }, - { GalVertexAttribSize._8_8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._8_8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._16, VertexAttribPointerType.Short }, - { GalVertexAttribSize._8, VertexAttribPointerType.Byte }, - { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.Int2101010Rev } - }; - - private static Dictionary _unsignedAttribTypes = - new Dictionary() - { - { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.UnsignedInt }, - { GalVertexAttribSize._32_32_32, VertexAttribPointerType.UnsignedInt }, - { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.UnsignedShort }, - { GalVertexAttribSize._32_32, VertexAttribPointerType.UnsignedInt }, - { GalVertexAttribSize._16_16_16, VertexAttribPointerType.UnsignedShort }, - { GalVertexAttribSize._8_8_8_8, VertexAttribPointerType.UnsignedByte }, - { GalVertexAttribSize._16_16, VertexAttribPointerType.UnsignedShort }, - { GalVertexAttribSize._32, VertexAttribPointerType.UnsignedInt }, - { GalVertexAttribSize._8_8_8, VertexAttribPointerType.UnsignedByte }, - { GalVertexAttribSize._8_8, VertexAttribPointerType.UnsignedByte }, - { GalVertexAttribSize._16, VertexAttribPointerType.UnsignedShort }, - { GalVertexAttribSize._8, VertexAttribPointerType.UnsignedByte }, - { GalVertexAttribSize._10_10_10_2, VertexAttribPointerType.UnsignedInt2101010Rev }, - { GalVertexAttribSize._11_11_10, VertexAttribPointerType.UnsignedInt10F11F11FRev } - }; - - private GalPipelineState _old; - - private OglConstBuffer _buffer; - private OglRenderTarget _renderTarget; - private OglRasterizer _rasterizer; - private OglShader _shader; - - private int _vaoHandle; - - public OglPipeline( - OglConstBuffer buffer, - OglRenderTarget renderTarget, - OglRasterizer rasterizer, - OglShader shader) - { - _buffer = buffer; - _renderTarget = renderTarget; - _rasterizer = rasterizer; - _shader = shader; - - // These values match OpenGL's defaults - _old = new GalPipelineState - { - FrontFace = GalFrontFace.Ccw, - - CullFaceEnabled = false, - CullFace = GalCullFace.Back, - - DepthTestEnabled = false, - DepthWriteEnabled = true, - DepthFunc = GalComparisonOp.Less, - DepthRangeNear = 0, - DepthRangeFar = 1, - - StencilTestEnabled = false, - - StencilBackFuncFunc = GalComparisonOp.Always, - StencilBackFuncRef = 0, - StencilBackFuncMask = UInt32.MaxValue, - StencilBackOpFail = GalStencilOp.Keep, - StencilBackOpZFail = GalStencilOp.Keep, - StencilBackOpZPass = GalStencilOp.Keep, - StencilBackMask = UInt32.MaxValue, - - StencilFrontFuncFunc = GalComparisonOp.Always, - StencilFrontFuncRef = 0, - StencilFrontFuncMask = UInt32.MaxValue, - StencilFrontOpFail = GalStencilOp.Keep, - StencilFrontOpZFail = GalStencilOp.Keep, - StencilFrontOpZPass = GalStencilOp.Keep, - StencilFrontMask = UInt32.MaxValue, - - BlendIndependent = false, - - PrimitiveRestartEnabled = false, - PrimitiveRestartIndex = 0 - }; - - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - _old.Blends[index] = BlendState.Default; - - _old.ColorMasks[index] = ColorMaskState.Default; - } - } - - public void Bind(GalPipelineState New) - { - BindConstBuffers(New); - - BindVertexLayout(New); - - if (New.FramebufferSrgb != _old.FramebufferSrgb) - { - Enable(EnableCap.FramebufferSrgb, New.FramebufferSrgb); - - _renderTarget.FramebufferSrgb = New.FramebufferSrgb; - } - - if (New.FlipX != _old.FlipX || New.FlipY != _old.FlipY || New.Instance != _old.Instance) - { - _shader.SetExtraData(New.FlipX, New.FlipY, New.Instance); - } - - if (New.FrontFace != _old.FrontFace) - { - GL.FrontFace(OglEnumConverter.GetFrontFace(New.FrontFace)); - } - - if (New.CullFaceEnabled != _old.CullFaceEnabled) - { - Enable(EnableCap.CullFace, New.CullFaceEnabled); - } - - if (New.CullFaceEnabled) - { - if (New.CullFace != _old.CullFace) - { - GL.CullFace(OglEnumConverter.GetCullFace(New.CullFace)); - } - } - - if (New.DepthTestEnabled != _old.DepthTestEnabled) - { - Enable(EnableCap.DepthTest, New.DepthTestEnabled); - } - - if (New.DepthWriteEnabled != _old.DepthWriteEnabled) - { - GL.DepthMask(New.DepthWriteEnabled); - } - - if (New.DepthTestEnabled) - { - if (New.DepthFunc != _old.DepthFunc) - { - GL.DepthFunc(OglEnumConverter.GetDepthFunc(New.DepthFunc)); - } - } - - if (New.DepthRangeNear != _old.DepthRangeNear || - New.DepthRangeFar != _old.DepthRangeFar) - { - GL.DepthRange(New.DepthRangeNear, New.DepthRangeFar); - } - - if (New.StencilTestEnabled != _old.StencilTestEnabled) - { - Enable(EnableCap.StencilTest, New.StencilTestEnabled); - } - - if (New.StencilTwoSideEnabled != _old.StencilTwoSideEnabled) - { - Enable((EnableCap)All.StencilTestTwoSideExt, New.StencilTwoSideEnabled); - } - - if (New.StencilTestEnabled) - { - if (New.StencilBackFuncFunc != _old.StencilBackFuncFunc || - New.StencilBackFuncRef != _old.StencilBackFuncRef || - New.StencilBackFuncMask != _old.StencilBackFuncMask) - { - GL.StencilFuncSeparate( - StencilFace.Back, - OglEnumConverter.GetStencilFunc(New.StencilBackFuncFunc), - New.StencilBackFuncRef, - New.StencilBackFuncMask); - } - - if (New.StencilBackOpFail != _old.StencilBackOpFail || - New.StencilBackOpZFail != _old.StencilBackOpZFail || - New.StencilBackOpZPass != _old.StencilBackOpZPass) - { - GL.StencilOpSeparate( - StencilFace.Back, - OglEnumConverter.GetStencilOp(New.StencilBackOpFail), - OglEnumConverter.GetStencilOp(New.StencilBackOpZFail), - OglEnumConverter.GetStencilOp(New.StencilBackOpZPass)); - } - - if (New.StencilBackMask != _old.StencilBackMask) - { - GL.StencilMaskSeparate(StencilFace.Back, New.StencilBackMask); - } - - if (New.StencilFrontFuncFunc != _old.StencilFrontFuncFunc || - New.StencilFrontFuncRef != _old.StencilFrontFuncRef || - New.StencilFrontFuncMask != _old.StencilFrontFuncMask) - { - GL.StencilFuncSeparate( - StencilFace.Front, - OglEnumConverter.GetStencilFunc(New.StencilFrontFuncFunc), - New.StencilFrontFuncRef, - New.StencilFrontFuncMask); - } - - if (New.StencilFrontOpFail != _old.StencilFrontOpFail || - New.StencilFrontOpZFail != _old.StencilFrontOpZFail || - New.StencilFrontOpZPass != _old.StencilFrontOpZPass) - { - GL.StencilOpSeparate( - StencilFace.Front, - OglEnumConverter.GetStencilOp(New.StencilFrontOpFail), - OglEnumConverter.GetStencilOp(New.StencilFrontOpZFail), - OglEnumConverter.GetStencilOp(New.StencilFrontOpZPass)); - } - - if (New.StencilFrontMask != _old.StencilFrontMask) - { - GL.StencilMaskSeparate(StencilFace.Front, New.StencilFrontMask); - } - } - - - // Scissor Test - // All scissor test are disabled before drawing final framebuffer to screen so we don't need to handle disabling - // Skip if there are no scissor tests to enable - if (New.ScissorTestCount != 0) - { - int scissorsApplied = 0; - bool applyToAll = false; - - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - if (New.ScissorTestEnabled[index]) - { - // If viewport arrays are unavailable apply first scissor test to all or - // there is only 1 scissor test and it's the first, the scissor test applies to all viewports - if (!OglExtension.Required.ViewportArray || (index == 0 && New.ScissorTestCount == 1)) - { - GL.Enable(EnableCap.ScissorTest); - applyToAll = true; - } - else - { - GL.Enable(IndexedEnableCap.ScissorTest, index); - } - - if (New.ScissorTestEnabled[index] != _old.ScissorTestEnabled[index] || - New.ScissorTestX[index] != _old.ScissorTestX[index] || - New.ScissorTestY[index] != _old.ScissorTestY[index] || - New.ScissorTestWidth[index] != _old.ScissorTestWidth[index] || - New.ScissorTestHeight[index] != _old.ScissorTestHeight[index]) - { - if (applyToAll) - { - GL.Scissor(New.ScissorTestX[index], New.ScissorTestY[index], - New.ScissorTestWidth[index], New.ScissorTestHeight[index]); - } - else - { - GL.ScissorIndexed(index, New.ScissorTestX[index], New.ScissorTestY[index], - New.ScissorTestWidth[index], New.ScissorTestHeight[index]); - } - } - - // If all scissor tests have been applied, or viewport arrays are unavailable we can skip remaining iterations - if (!OglExtension.Required.ViewportArray || ++scissorsApplied == New.ScissorTestCount) - { - break; - } - } - } - } - - - if (New.BlendIndependent) - { - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - SetBlendState(index, New.Blends[index], _old.Blends[index]); - } - } - else - { - if (New.BlendIndependent != _old.BlendIndependent) - { - SetAllBlendState(New.Blends[0]); - } - else - { - SetBlendState(New.Blends[0], _old.Blends[0]); - } - } - - if (New.ColorMaskCommon) - { - if (New.ColorMaskCommon != _old.ColorMaskCommon || !New.ColorMasks[0].Equals(_old.ColorMasks[0])) - { - GL.ColorMask( - New.ColorMasks[0].Red, - New.ColorMasks[0].Green, - New.ColorMasks[0].Blue, - New.ColorMasks[0].Alpha); - } - } - else - { - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - if (!New.ColorMasks[index].Equals(_old.ColorMasks[index])) - { - GL.ColorMask( - index, - New.ColorMasks[index].Red, - New.ColorMasks[index].Green, - New.ColorMasks[index].Blue, - New.ColorMasks[index].Alpha); - } - } - } - - if (New.PrimitiveRestartEnabled != _old.PrimitiveRestartEnabled) - { - Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled); - } - - if (New.PrimitiveRestartEnabled) - { - if (New.PrimitiveRestartIndex != _old.PrimitiveRestartIndex) - { - GL.PrimitiveRestartIndex(New.PrimitiveRestartIndex); - } - } - - _old = New; - } - - public void Unbind(GalPipelineState state) - { - if (state.ScissorTestCount > 0) - { - GL.Disable(EnableCap.ScissorTest); - } - } - - private void SetAllBlendState(BlendState New) - { - Enable(EnableCap.Blend, New.Enabled); - - if (New.Enabled) - { - if (New.SeparateAlpha) - { - GL.BlendEquationSeparate( - OglEnumConverter.GetBlendEquation(New.EquationRgb), - OglEnumConverter.GetBlendEquation(New.EquationAlpha)); - - GL.BlendFuncSeparate( - (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), - (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), - (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); - } - else - { - GL.BlendEquation(OglEnumConverter.GetBlendEquation(New.EquationRgb)); - - GL.BlendFunc( - OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), - OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); - } - } - } - - private void SetBlendState(BlendState New, BlendState old) - { - if (New.Enabled != old.Enabled) - { - Enable(EnableCap.Blend, New.Enabled); - } - - if (New.Enabled) - { - if (New.SeparateAlpha) - { - if (New.EquationRgb != old.EquationRgb || - New.EquationAlpha != old.EquationAlpha) - { - GL.BlendEquationSeparate( - OglEnumConverter.GetBlendEquation(New.EquationRgb), - OglEnumConverter.GetBlendEquation(New.EquationAlpha)); - } - - if (New.FuncSrcRgb != old.FuncSrcRgb || - New.FuncDstRgb != old.FuncDstRgb || - New.FuncSrcAlpha != old.FuncSrcAlpha || - New.FuncDstAlpha != old.FuncDstAlpha) - { - GL.BlendFuncSeparate( - (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), - (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), - (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); - } - } - else - { - if (New.EquationRgb != old.EquationRgb) - { - GL.BlendEquation(OglEnumConverter.GetBlendEquation(New.EquationRgb)); - } - - if (New.FuncSrcRgb != old.FuncSrcRgb || - New.FuncDstRgb != old.FuncDstRgb) - { - GL.BlendFunc( - OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), - OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); - } - } - } - } - - private void SetBlendState(int index, BlendState New, BlendState old) - { - if (New.Enabled != old.Enabled) - { - Enable(IndexedEnableCap.Blend, index, New.Enabled); - } - - if (New.Enabled) - { - if (New.SeparateAlpha) - { - if (New.EquationRgb != old.EquationRgb || - New.EquationAlpha != old.EquationAlpha) - { - GL.BlendEquationSeparate( - index, - OglEnumConverter.GetBlendEquation(New.EquationRgb), - OglEnumConverter.GetBlendEquation(New.EquationAlpha)); - } - - if (New.FuncSrcRgb != old.FuncSrcRgb || - New.FuncDstRgb != old.FuncDstRgb || - New.FuncSrcAlpha != old.FuncSrcAlpha || - New.FuncDstAlpha != old.FuncDstAlpha) - { - GL.BlendFuncSeparate( - index, - (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb), - (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcAlpha), - (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstAlpha)); - } - } - else - { - if (New.EquationRgb != old.EquationRgb) - { - GL.BlendEquation(index, OglEnumConverter.GetBlendEquation(New.EquationRgb)); - } - - if (New.FuncSrcRgb != old.FuncSrcRgb || - New.FuncDstRgb != old.FuncDstRgb) - { - GL.BlendFunc( - index, - (BlendingFactorSrc) OglEnumConverter.GetBlendFactor(New.FuncSrcRgb), - (BlendingFactorDest)OglEnumConverter.GetBlendFactor(New.FuncDstRgb)); - } - } - } - } - - private void BindConstBuffers(GalPipelineState New) - { - int freeBinding = OglShader.ReservedCbufCount; - - void BindIfNotNull(OglShaderStage stage) - { - if (stage != null) - { - foreach (CBufferDescriptor desc in stage.ConstBufferUsage) - { - long key = New.ConstBufferKeys[(int)stage.Type][desc.Slot]; - - if (key != 0 && _buffer.TryGetUbo(key, out int uboHandle)) - { - GL.BindBufferBase(BufferRangeTarget.UniformBuffer, freeBinding, uboHandle); - } - - freeBinding++; - } - } - } - - BindIfNotNull(_shader.Current.Vertex); - BindIfNotNull(_shader.Current.TessControl); - BindIfNotNull(_shader.Current.TessEvaluation); - BindIfNotNull(_shader.Current.Geometry); - BindIfNotNull(_shader.Current.Fragment); - } - - private void BindVertexLayout(GalPipelineState New) - { - foreach (GalVertexBinding binding in New.VertexBindings) - { - if (!binding.Enabled || !_rasterizer.TryGetVbo(binding.VboKey, out int vboHandle)) - { - continue; - } - - if (_vaoHandle == 0) - { - _vaoHandle = GL.GenVertexArray(); - - // Vertex arrays shouldn't be used anywhere else in OpenGL's backend - // if you want to use it, move this line out of the if - GL.BindVertexArray(_vaoHandle); - } - - foreach (GalVertexAttrib attrib in binding.Attribs) - { - // Skip uninitialized attributes. - if (attrib.Size == 0) - { - continue; - } - - GL.BindBuffer(BufferTarget.ArrayBuffer, vboHandle); - - bool unsigned = - attrib.Type == GalVertexAttribType.Unorm || - attrib.Type == GalVertexAttribType.Uint || - attrib.Type == GalVertexAttribType.Uscaled; - - bool normalize = - attrib.Type == GalVertexAttribType.Snorm || - attrib.Type == GalVertexAttribType.Unorm; - - VertexAttribPointerType type = 0; - - if (attrib.Type == GalVertexAttribType.Float) - { - type = GetType(_floatAttribTypes, attrib); - } - else - { - if (unsigned) - { - type = GetType(_unsignedAttribTypes, attrib); - } - else - { - type = GetType(_signedAttribTypes, attrib); - } - } - - if (!_attribElements.TryGetValue(attrib.Size, out int size)) - { - throw new InvalidOperationException("Invalid attribute size \"" + attrib.Size + "\"!"); - } - - int offset = attrib.Offset; - - if (binding.Stride != 0) - { - GL.EnableVertexAttribArray(attrib.Index); - - if (attrib.Type == GalVertexAttribType.Sint || - attrib.Type == GalVertexAttribType.Uint) - { - IntPtr pointer = new IntPtr(offset); - - VertexAttribIntegerType iType = (VertexAttribIntegerType)type; - - GL.VertexAttribIPointer(attrib.Index, size, iType, binding.Stride, pointer); - } - else - { - GL.VertexAttribPointer(attrib.Index, size, type, normalize, binding.Stride, offset); - } - } - else - { - GL.DisableVertexAttribArray(attrib.Index); - - SetConstAttrib(attrib); - } - - if (binding.Instanced && binding.Divisor != 0) - { - GL.VertexAttribDivisor(attrib.Index, 1); - } - else - { - GL.VertexAttribDivisor(attrib.Index, 0); - } - } - } - } - - private static VertexAttribPointerType GetType(Dictionary dict, GalVertexAttrib attrib) - { - if (!dict.TryGetValue(attrib.Size, out VertexAttribPointerType type)) - { - ThrowUnsupportedAttrib(attrib); - } - - return type; - } - - private static unsafe void SetConstAttrib(GalVertexAttrib attrib) - { - if (attrib.Size == GalVertexAttribSize._10_10_10_2 || - attrib.Size == GalVertexAttribSize._11_11_10) - { - ThrowUnsupportedAttrib(attrib); - } - - fixed (byte* ptr = attrib.Data) - { - if (attrib.Type == GalVertexAttribType.Unorm) - { - switch (attrib.Size) - { - case GalVertexAttribSize._8: - case GalVertexAttribSize._8_8: - case GalVertexAttribSize._8_8_8: - case GalVertexAttribSize._8_8_8_8: - GL.VertexAttrib4N((uint)attrib.Index, ptr); - break; - - case GalVertexAttribSize._16: - case GalVertexAttribSize._16_16: - case GalVertexAttribSize._16_16_16: - case GalVertexAttribSize._16_16_16_16: - GL.VertexAttrib4N((uint)attrib.Index, (ushort*)ptr); - break; - - case GalVertexAttribSize._32: - case GalVertexAttribSize._32_32: - case GalVertexAttribSize._32_32_32: - case GalVertexAttribSize._32_32_32_32: - GL.VertexAttrib4N((uint)attrib.Index, (uint*)ptr); - break; - } - } - else if (attrib.Type == GalVertexAttribType.Snorm) - { - switch (attrib.Size) - { - case GalVertexAttribSize._8: - case GalVertexAttribSize._8_8: - case GalVertexAttribSize._8_8_8: - case GalVertexAttribSize._8_8_8_8: - GL.VertexAttrib4N((uint)attrib.Index, (sbyte*)ptr); - break; - - case GalVertexAttribSize._16: - case GalVertexAttribSize._16_16: - case GalVertexAttribSize._16_16_16: - case GalVertexAttribSize._16_16_16_16: - GL.VertexAttrib4N((uint)attrib.Index, (short*)ptr); - break; - - case GalVertexAttribSize._32: - case GalVertexAttribSize._32_32: - case GalVertexAttribSize._32_32_32: - case GalVertexAttribSize._32_32_32_32: - GL.VertexAttrib4N((uint)attrib.Index, (int*)ptr); - break; - } - } - else if (attrib.Type == GalVertexAttribType.Uint) - { - switch (attrib.Size) - { - case GalVertexAttribSize._8: - case GalVertexAttribSize._8_8: - case GalVertexAttribSize._8_8_8: - case GalVertexAttribSize._8_8_8_8: - GL.VertexAttribI4((uint)attrib.Index, ptr); - break; - - case GalVertexAttribSize._16: - case GalVertexAttribSize._16_16: - case GalVertexAttribSize._16_16_16: - case GalVertexAttribSize._16_16_16_16: - GL.VertexAttribI4((uint)attrib.Index, (ushort*)ptr); - break; - - case GalVertexAttribSize._32: - case GalVertexAttribSize._32_32: - case GalVertexAttribSize._32_32_32: - case GalVertexAttribSize._32_32_32_32: - GL.VertexAttribI4((uint)attrib.Index, (uint*)ptr); - break; - } - } - else if (attrib.Type == GalVertexAttribType.Sint) - { - switch (attrib.Size) - { - case GalVertexAttribSize._8: - case GalVertexAttribSize._8_8: - case GalVertexAttribSize._8_8_8: - case GalVertexAttribSize._8_8_8_8: - GL.VertexAttribI4((uint)attrib.Index, (sbyte*)ptr); - break; - - case GalVertexAttribSize._16: - case GalVertexAttribSize._16_16: - case GalVertexAttribSize._16_16_16: - case GalVertexAttribSize._16_16_16_16: - GL.VertexAttribI4((uint)attrib.Index, (short*)ptr); - break; - - case GalVertexAttribSize._32: - case GalVertexAttribSize._32_32: - case GalVertexAttribSize._32_32_32: - case GalVertexAttribSize._32_32_32_32: - GL.VertexAttribI4((uint)attrib.Index, (int*)ptr); - break; - } - } - else if (attrib.Type == GalVertexAttribType.Float) - { - switch (attrib.Size) - { - case GalVertexAttribSize._32: - case GalVertexAttribSize._32_32: - case GalVertexAttribSize._32_32_32: - case GalVertexAttribSize._32_32_32_32: - GL.VertexAttrib4(attrib.Index, (float*)ptr); - break; - - default: ThrowUnsupportedAttrib(attrib); break; - } - } - } - } - - private static void ThrowUnsupportedAttrib(GalVertexAttrib attrib) - { - throw new NotImplementedException("Unsupported size \"" + attrib.Size + "\" on type \"" + attrib.Type + "\"!"); - } - - private void Enable(EnableCap cap, bool enabled) - { - if (enabled) - { - GL.Enable(cap); - } - else - { - GL.Disable(cap); - } - } - - private void Enable(IndexedEnableCap cap, int index, bool enabled) - { - if (enabled) - { - GL.Enable(cap, index); - } - else - { - GL.Disable(cap, index); - } - } - - public void ResetDepthMask() - { - _old.DepthWriteEnabled = true; - } - - public void ResetColorMask(int index) - { - _old.ColorMasks[index] = ColorMaskState.Default; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs deleted file mode 100644 index c19911c57..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglRasterizer.cs +++ /dev/null @@ -1,207 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglRasterizer : IGalRasterizer - { - private const long MaxVertexBufferCacheSize = 128 * 1024 * 1024; - private const long MaxIndexBufferCacheSize = 64 * 1024 * 1024; - - private int[] _vertexBuffers; - - private OglCachedResource _vboCache; - private OglCachedResource _iboCache; - - private struct IbInfo - { - public int Count; - public int ElemSizeLog2; - - public DrawElementsType Type; - } - - private IbInfo _indexBuffer; - - public OglRasterizer() - { - _vertexBuffers = new int[32]; - - _vboCache = new OglCachedResource(GL.DeleteBuffer, MaxVertexBufferCacheSize); - _iboCache = new OglCachedResource(GL.DeleteBuffer, MaxIndexBufferCacheSize); - - _indexBuffer = new IbInfo(); - } - - public void LockCaches() - { - _vboCache.Lock(); - _iboCache.Lock(); - } - - public void UnlockCaches() - { - _vboCache.Unlock(); - _iboCache.Unlock(); - } - - public void ClearBuffers( - GalClearBufferFlags flags, - int attachment, - float red, - float green, - float blue, - float alpha, - float depth, - int stencil) - { - GL.ColorMask( - attachment, - flags.HasFlag(GalClearBufferFlags.ColorRed), - flags.HasFlag(GalClearBufferFlags.ColorGreen), - flags.HasFlag(GalClearBufferFlags.ColorBlue), - flags.HasFlag(GalClearBufferFlags.ColorAlpha)); - - GL.ClearBuffer(ClearBuffer.Color, attachment, new float[] { red, green, blue, alpha }); - - GL.ColorMask(attachment, true, true, true, true); - GL.DepthMask(true); - - if (flags.HasFlag(GalClearBufferFlags.Depth)) - { - GL.ClearBuffer(ClearBuffer.Depth, 0, ref depth); - } - - if (flags.HasFlag(GalClearBufferFlags.Stencil)) - { - GL.ClearBuffer(ClearBuffer.Stencil, 0, ref stencil); - } - } - - public bool IsVboCached(long key, long dataSize) - { - return _vboCache.TryGetSize(key, out long size) && size == dataSize; - } - - public bool IsIboCached(long key, long dataSize) - { - return _iboCache.TryGetSize(key, out long size) && size == dataSize; - } - - public void CreateVbo(long key, int dataSize, IntPtr hostAddress) - { - int handle = GL.GenBuffer(); - - _vboCache.AddOrUpdate(key, handle, dataSize); - - IntPtr length = new IntPtr(dataSize); - - GL.BindBuffer(BufferTarget.ArrayBuffer, handle); - GL.BufferData(BufferTarget.ArrayBuffer, length, hostAddress, BufferUsageHint.StreamDraw); - } - - public void CreateVbo(long key, byte[] data) - { - int handle = GL.GenBuffer(); - - _vboCache.AddOrUpdate(key, handle, data.Length); - - IntPtr length = new IntPtr(data.Length); - - GL.BindBuffer(BufferTarget.ArrayBuffer, handle); - GL.BufferData(BufferTarget.ArrayBuffer, length, data, BufferUsageHint.StreamDraw); - } - - public void CreateIbo(long key, int dataSize, IntPtr hostAddress) - { - int handle = GL.GenBuffer(); - - _iboCache.AddOrUpdate(key, handle, (uint)dataSize); - - IntPtr length = new IntPtr(dataSize); - - GL.BindBuffer(BufferTarget.ElementArrayBuffer, handle); - GL.BufferData(BufferTarget.ElementArrayBuffer, length, hostAddress, BufferUsageHint.StreamDraw); - } - - public void CreateIbo(long key, int dataSize, byte[] buffer) - { - int handle = GL.GenBuffer(); - - _iboCache.AddOrUpdate(key, handle, dataSize); - - IntPtr length = new IntPtr(buffer.Length); - - GL.BindBuffer(BufferTarget.ElementArrayBuffer, handle); - GL.BufferData(BufferTarget.ElementArrayBuffer, length, buffer, BufferUsageHint.StreamDraw); - } - - public void SetIndexArray(int size, GalIndexFormat format) - { - _indexBuffer.Type = OglEnumConverter.GetDrawElementsType(format); - - _indexBuffer.Count = size >> (int)format; - - _indexBuffer.ElemSizeLog2 = (int)format; - } - - public void DrawArrays(int first, int count, GalPrimitiveType primType) - { - if (count == 0) - { - return; - } - - if (primType == GalPrimitiveType.Quads) - { - for (int offset = 0; offset < count; offset += 4) - { - GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4); - } - } - else if (primType == GalPrimitiveType.QuadStrip) - { - GL.DrawArrays(PrimitiveType.TriangleFan, first, 4); - - for (int offset = 2; offset < count; offset += 2) - { - GL.DrawArrays(PrimitiveType.TriangleFan, first + offset, 4); - } - } - else - { - GL.DrawArrays(OglEnumConverter.GetPrimitiveType(primType), first, count); - } - } - - public void DrawElements(long iboKey, int first, int vertexBase, GalPrimitiveType primType) - { - if (!_iboCache.TryGetValue(iboKey, out int iboHandle)) - { - return; - } - - PrimitiveType mode = OglEnumConverter.GetPrimitiveType(primType); - - GL.BindBuffer(BufferTarget.ElementArrayBuffer, iboHandle); - - first <<= _indexBuffer.ElemSizeLog2; - - if (vertexBase != 0) - { - IntPtr indices = new IntPtr(first); - - GL.DrawElementsBaseVertex(mode, _indexBuffer.Count, _indexBuffer.Type, indices, vertexBase); - } - else - { - GL.DrawElements(mode, _indexBuffer.Count, _indexBuffer.Type, first); - } - } - - public bool TryGetVbo(long vboKey, out int vboHandle) - { - return _vboCache.TryGetValue(vboKey, out vboHandle); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs deleted file mode 100644 index 11fc98cb4..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglRenderTarget.cs +++ /dev/null @@ -1,549 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.Texture; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglRenderTarget : IGalRenderTarget - { - private const int NativeWidth = 1280; - private const int NativeHeight = 720; - - private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount; - - private struct Rect - { - public int X { get; private set; } - public int Y { get; private set; } - public int Width { get; private set; } - public int Height { get; private set; } - - public Rect(int x, int y, int width, int height) - { - X = x; - Y = y; - Width = width; - Height = height; - } - } - - private class FrameBufferAttachments - { - public int MapCount { get; set; } - - public DrawBuffersEnum[] Map { get; private set; } - - public long[] Colors { get; private set; } - - public long Zeta { get; set; } - - public FrameBufferAttachments() - { - Colors = new long[RenderTargetsCount]; - - Map = new DrawBuffersEnum[RenderTargetsCount]; - } - - public void Update(FrameBufferAttachments source) - { - for (int index = 0; index < RenderTargetsCount; index++) - { - Map[index] = source.Map[index]; - - Colors[index] = source.Colors[index]; - } - - MapCount = source.MapCount; - Zeta = source.Zeta; - } - } - - private int[] _colorHandles; - private int _zetaHandle; - - private OglTexture _texture; - - private ImageHandler _readTex; - - private Rect _window; - - private float[] _viewports; - - private bool _flipX; - private bool _flipY; - - private int _cropTop; - private int _cropLeft; - private int _cropRight; - private int _cropBottom; - - // This framebuffer is used to attach guest rendertargets, - // think of it as a dummy OpenGL VAO - private int _dummyFrameBuffer; - - // These framebuffers are used to blit images - private int _srcFb; - private int _dstFb; - - private FrameBufferAttachments _attachments; - private FrameBufferAttachments _oldAttachments; - - private int _copyPbo; - - public bool FramebufferSrgb { get; set; } - - public OglRenderTarget(OglTexture texture) - { - _attachments = new FrameBufferAttachments(); - - _oldAttachments = new FrameBufferAttachments(); - - _colorHandles = new int[RenderTargetsCount]; - - _viewports = new float[RenderTargetsCount * 4]; - - _texture = texture; - - texture.TextureDeleted += TextureDeletionHandler; - } - - private void TextureDeletionHandler(object sender, int handle) - { - // Texture was deleted, the handle is no longer valid, so - // reset all uses of this handle on a render target. - for (int attachment = 0; attachment < RenderTargetsCount; attachment++) - { - if (_colorHandles[attachment] == handle) - { - _colorHandles[attachment] = 0; - } - } - - if (_zetaHandle == handle) - { - _zetaHandle = 0; - } - } - - public void Bind() - { - if (_dummyFrameBuffer == 0) - { - _dummyFrameBuffer = GL.GenFramebuffer(); - } - - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, _dummyFrameBuffer); - - ImageHandler cachedImage; - - for (int attachment = 0; attachment < RenderTargetsCount; attachment++) - { - long key = _attachments.Colors[attachment]; - - int handle = 0; - - if (key != 0 && _texture.TryGetImageHandler(key, out cachedImage)) - { - handle = cachedImage.Handle; - } - - if (handle == _colorHandles[attachment]) - { - continue; - } - - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FramebufferAttachment.ColorAttachment0 + attachment, - handle, - 0); - - _colorHandles[attachment] = handle; - } - - if (_attachments.Zeta != 0 && _texture.TryGetImageHandler(_attachments.Zeta, out cachedImage)) - { - if (cachedImage.Handle != _zetaHandle) - { - if (cachedImage.HasDepth && cachedImage.HasStencil) - { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FramebufferAttachment.DepthStencilAttachment, - cachedImage.Handle, - 0); - } - else if (cachedImage.HasDepth) - { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FramebufferAttachment.DepthAttachment, - cachedImage.Handle, - 0); - - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FramebufferAttachment.StencilAttachment, - 0, - 0); - } - else - { - throw new InvalidOperationException("Invalid image format \"" + cachedImage.Format + "\" used as Zeta!"); - } - - _zetaHandle = cachedImage.Handle; - } - } - else if (_zetaHandle != 0) - { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FramebufferAttachment.DepthStencilAttachment, - 0, - 0); - - _zetaHandle = 0; - } - - if (OglExtension.ViewportArray) - { - GL.ViewportArray(0, RenderTargetsCount, _viewports); - } - else - { - GL.Viewport( - (int)_viewports[0], - (int)_viewports[1], - (int)_viewports[2], - (int)_viewports[3]); - } - - if (_attachments.MapCount > 1) - { - GL.DrawBuffers(_attachments.MapCount, _attachments.Map); - } - else if (_attachments.MapCount == 1) - { - GL.DrawBuffer((DrawBufferMode)_attachments.Map[0]); - } - else - { - GL.DrawBuffer(DrawBufferMode.None); - } - - _oldAttachments.Update(_attachments); - } - - public void BindColor(long key, int attachment) - { - _attachments.Colors[attachment] = key; - } - - public void UnbindColor(int attachment) - { - _attachments.Colors[attachment] = 0; - } - - public void BindZeta(long key) - { - _attachments.Zeta = key; - } - - public void UnbindZeta() - { - _attachments.Zeta = 0; - } - - public void Present(long key) - { - _texture.TryGetImageHandler(key, out _readTex); - } - - public void SetMap(int[] map) - { - if (map != null) - { - _attachments.MapCount = map.Length; - - for (int attachment = 0; attachment < _attachments.MapCount; attachment++) - { - _attachments.Map[attachment] = DrawBuffersEnum.ColorAttachment0 + map[attachment]; - } - } - else - { - _attachments.MapCount = 0; - } - } - - public void SetTransform(bool flipX, bool flipY, int top, int left, int right, int bottom) - { - _flipX = flipX; - _flipY = flipY; - - _cropTop = top; - _cropLeft = left; - _cropRight = right; - _cropBottom = bottom; - } - - public void SetWindowSize(int width, int height) - { - _window = new Rect(0, 0, width, height); - } - - public void SetViewport(int attachment, int x, int y, int width, int height) - { - int offset = attachment * 4; - - _viewports[offset + 0] = x; - _viewports[offset + 1] = y; - _viewports[offset + 2] = width; - _viewports[offset + 3] = height; - } - - public void Render() - { - if (_readTex == null) - { - return; - } - - int srcX0, srcX1, srcY0, srcY1; - - if (_cropLeft == 0 && _cropRight == 0) - { - srcX0 = 0; - srcX1 = _readTex.Width; - } - else - { - srcX0 = _cropLeft; - srcX1 = _cropRight; - } - - if (_cropTop == 0 && _cropBottom == 0) - { - srcY0 = 0; - srcY1 = _readTex.Height; - } - else - { - srcY0 = _cropTop; - srcY1 = _cropBottom; - } - - float ratioX = MathF.Min(1f, (_window.Height * (float)NativeWidth) / ((float)NativeHeight * _window.Width)); - float ratioY = MathF.Min(1f, (_window.Width * (float)NativeHeight) / ((float)NativeWidth * _window.Height)); - - int dstWidth = (int)(_window.Width * ratioX); - int dstHeight = (int)(_window.Height * ratioY); - - int dstPaddingX = (_window.Width - dstWidth) / 2; - int dstPaddingY = (_window.Height - dstHeight) / 2; - - int dstX0 = _flipX ? _window.Width - dstPaddingX : dstPaddingX; - int dstX1 = _flipX ? dstPaddingX : _window.Width - dstPaddingX; - - int dstY0 = _flipY ? dstPaddingY : _window.Height - dstPaddingY; - int dstY1 = _flipY ? _window.Height - dstPaddingY : dstPaddingY; - - GL.Viewport(0, 0, _window.Width, _window.Height); - - if (_srcFb == 0) - { - _srcFb = GL.GenFramebuffer(); - } - - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _srcFb); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, 0); - - GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, _readTex.Handle, 0); - - GL.ReadBuffer(ReadBufferMode.ColorAttachment0); - - GL.Clear(ClearBufferMask.ColorBufferBit); - - GL.Disable(EnableCap.FramebufferSrgb); - - GL.BlitFramebuffer( - srcX0, - srcY0, - srcX1, - srcY1, - dstX0, - dstY0, - dstX1, - dstY1, - ClearBufferMask.ColorBufferBit, - BlitFramebufferFilter.Linear); - - if (FramebufferSrgb) - { - GL.Enable(EnableCap.FramebufferSrgb); - } - } - - public void Copy( - GalImage srcImage, - GalImage dstImage, - long srcKey, - long dstKey, - int srcLayer, - int dstLayer, - int srcX0, - int srcY0, - int srcX1, - int srcY1, - int dstX0, - int dstY0, - int dstX1, - int dstY1) - { - if (_texture.TryGetImageHandler(srcKey, out ImageHandler srcTex) && - _texture.TryGetImageHandler(dstKey, out ImageHandler dstTex)) - { - if (srcTex.HasColor != dstTex.HasColor || - srcTex.HasDepth != dstTex.HasDepth || - srcTex.HasStencil != dstTex.HasStencil) - { - throw new NotImplementedException(); - } - - if (_srcFb == 0) - { - _srcFb = GL.GenFramebuffer(); - } - - if (_dstFb == 0) - { - _dstFb = GL.GenFramebuffer(); - } - - GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, _srcFb); - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, _dstFb); - - FramebufferAttachment attachment = GetAttachment(srcTex); - - if (ImageUtils.IsArray(srcImage.TextureTarget) && srcLayer > 0) - { - GL.FramebufferTextureLayer(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0, srcLayer); - } - else - { - GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, attachment, srcTex.Handle, 0); - } - - if (ImageUtils.IsArray(dstImage.TextureTarget) && dstLayer > 0) - { - GL.FramebufferTextureLayer(FramebufferTarget.DrawFramebuffer, attachment, dstTex.Handle, 0, dstLayer); - } - else - { - GL.FramebufferTexture(FramebufferTarget.DrawFramebuffer, attachment, dstTex.Handle, 0); - } - - - BlitFramebufferFilter filter = BlitFramebufferFilter.Nearest; - - if (srcTex.HasColor) - { - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); - - filter = BlitFramebufferFilter.Linear; - } - - ClearBufferMask mask = GetClearMask(srcTex); - - GL.BlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); - } - } - - public void Reinterpret(long key, GalImage newImage) - { - if (!_texture.TryGetImage(key, out GalImage oldImage)) - { - return; - } - - if (newImage.Format == oldImage.Format && - newImage.Width == oldImage.Width && - newImage.Height == oldImage.Height && - newImage.Depth == oldImage.Depth && - newImage.LayerCount == oldImage.LayerCount && - newImage.TextureTarget == oldImage.TextureTarget) - { - return; - } - - if (_copyPbo == 0) - { - _copyPbo = GL.GenBuffer(); - } - - GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPbo); - - // The buffer should be large enough to hold the largest texture. - int bufferSize = Math.Max(ImageUtils.GetSize(oldImage), - ImageUtils.GetSize(newImage)); - - GL.BufferData(BufferTarget.PixelPackBuffer, bufferSize, IntPtr.Zero, BufferUsageHint.StreamCopy); - - if (!_texture.TryGetImageHandler(key, out ImageHandler cachedImage)) - { - throw new InvalidOperationException(); - } - - (_, PixelFormat format, PixelType type) = OglEnumConverter.GetImageFormat(cachedImage.Format); - - TextureTarget target = ImageUtils.GetTextureTarget(newImage.TextureTarget); - - GL.BindTexture(target, cachedImage.Handle); - - GL.GetTexImage(target, 0, format, type, IntPtr.Zero); - - GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPbo); - - GL.PixelStore(PixelStoreParameter.UnpackRowLength, oldImage.Width); - - _texture.Create(key, ImageUtils.GetSize(newImage), newImage); - - GL.PixelStore(PixelStoreParameter.UnpackRowLength, 0); - - GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); - } - - private static FramebufferAttachment GetAttachment(ImageHandler cachedImage) - { - if (cachedImage.HasColor) - { - return FramebufferAttachment.ColorAttachment0; - } - else if (cachedImage.HasDepth && cachedImage.HasStencil) - { - return FramebufferAttachment.DepthStencilAttachment; - } - else if (cachedImage.HasDepth) - { - return FramebufferAttachment.DepthAttachment; - } - else if (cachedImage.HasStencil) - { - return FramebufferAttachment.StencilAttachment; - } - else - { - throw new InvalidOperationException(); - } - } - - private static ClearBufferMask GetClearMask(ImageHandler cachedImage) - { - return (cachedImage.HasColor ? ClearBufferMask.ColorBufferBit : 0) | - (cachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) | - (cachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglRenderer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglRenderer.cs deleted file mode 100644 index 1ff8c7ad5..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglRenderer.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Concurrent; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - public class OglRenderer : IGalRenderer - { - public IGalConstBuffer Buffer { get; private set; } - - public IGalRenderTarget RenderTarget { get; private set; } - - public IGalRasterizer Rasterizer { get; private set; } - - public IGalShader Shader { get; private set; } - - public IGalPipeline Pipeline { get; private set; } - - public IGalTexture Texture { get; private set; } - - private ConcurrentQueue _actionsQueue; - - public OglRenderer() - { - Buffer = new OglConstBuffer(); - - Texture = new OglTexture(); - - RenderTarget = new OglRenderTarget(Texture as OglTexture); - - Rasterizer = new OglRasterizer(); - - Shader = new OglShader(Buffer as OglConstBuffer); - - Pipeline = new OglPipeline( - Buffer as OglConstBuffer, - RenderTarget as OglRenderTarget, - Rasterizer as OglRasterizer, - Shader as OglShader); - - _actionsQueue = new ConcurrentQueue(); - } - - public void QueueAction(Action actionMthd) - { - _actionsQueue.Enqueue(actionMthd); - } - - public void RunActions() - { - int count = _actionsQueue.Count; - - while (count-- > 0 && _actionsQueue.TryDequeue(out Action renderAction)) - { - renderAction(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OglShader.cs deleted file mode 100644 index 8da0104e1..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglShader.cs +++ /dev/null @@ -1,291 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglShader : IGalShader - { - public const int ReservedCbufCount = 1; - - private const int ExtraDataSize = 4; - - public OglShaderProgram Current; - - private ConcurrentDictionary _stages; - - private Dictionary _programs; - - public int CurrentProgramHandle { get; private set; } - - private OglConstBuffer _buffer; - - private int _extraUboHandle; - - public OglShader(OglConstBuffer buffer) - { - _buffer = buffer; - - _stages = new ConcurrentDictionary(); - - _programs = new Dictionary(); - } - - public void Create(IGalMemory memory, long key, GalShaderType type) - { - _stages.GetOrAdd(key, (stage) => ShaderStageFactory(memory, key, 0, false, type)); - } - - public void Create(IGalMemory memory, long vpAPos, long key, GalShaderType type) - { - _stages.GetOrAdd(key, (stage) => ShaderStageFactory(memory, vpAPos, key, true, type)); - } - - private OglShaderStage ShaderStageFactory( - IGalMemory memory, - long position, - long positionB, - bool isDualVp, - GalShaderType type) - { - ShaderConfig config = new ShaderConfig(type, OglLimit.MaxUboSize); - - ShaderProgram program; - - if (isDualVp) - { - ShaderDumper.Dump(memory, position, type, "a"); - ShaderDumper.Dump(memory, positionB, type, "b"); - - program = Translator.Translate(memory, (ulong)position, (ulong)positionB, config); - } - else - { - ShaderDumper.Dump(memory, position, type); - - program = Translator.Translate(memory, (ulong)position, config); - } - - string code = program.Code; - - if (ShaderDumper.IsDumpEnabled()) - { - int shaderDumpIndex = ShaderDumper.DumpIndex; - - code = "//Shader " + shaderDumpIndex + Environment.NewLine + code; - } - - return new OglShaderStage(type, code, program.Info.CBuffers, program.Info.Textures); - } - - public IEnumerable GetConstBufferUsage(long key) - { - if (_stages.TryGetValue(key, out OglShaderStage stage)) - { - return stage.ConstBufferUsage; - } - - return Enumerable.Empty(); - } - - public IEnumerable GetTextureUsage(long key) - { - if (_stages.TryGetValue(key, out OglShaderStage stage)) - { - return stage.TextureUsage; - } - - return Enumerable.Empty(); - } - - public unsafe void SetExtraData(float flipX, float flipY, int instance) - { - BindProgram(); - - EnsureExtraBlock(); - - GL.BindBuffer(BufferTarget.UniformBuffer, _extraUboHandle); - - float* data = stackalloc float[ExtraDataSize]; - data[0] = flipX; - data[1] = flipY; - data[2] = BitConverter.Int32BitsToSingle(instance); - - // Invalidate buffer - GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw); - - GL.BufferSubData(BufferTarget.UniformBuffer, IntPtr.Zero, ExtraDataSize * sizeof(float), (IntPtr)data); - } - - public void Bind(long key) - { - if (_stages.TryGetValue(key, out OglShaderStage stage)) - { - Bind(stage); - } - } - - private void Bind(OglShaderStage stage) - { - switch (stage.Type) - { - case GalShaderType.Vertex: Current.Vertex = stage; break; - case GalShaderType.TessControl: Current.TessControl = stage; break; - case GalShaderType.TessEvaluation: Current.TessEvaluation = stage; break; - case GalShaderType.Geometry: Current.Geometry = stage; break; - case GalShaderType.Fragment: Current.Fragment = stage; break; - } - } - - public void Unbind(GalShaderType type) - { - switch (type) - { - case GalShaderType.Vertex: Current.Vertex = null; break; - case GalShaderType.TessControl: Current.TessControl = null; break; - case GalShaderType.TessEvaluation: Current.TessEvaluation = null; break; - case GalShaderType.Geometry: Current.Geometry = null; break; - case GalShaderType.Fragment: Current.Fragment = null; break; - } - } - - public void BindProgram() - { - if (Current.Vertex == null || - Current.Fragment == null) - { - return; - } - - if (!_programs.TryGetValue(Current, out int handle)) - { - handle = GL.CreateProgram(); - - AttachIfNotNull(handle, Current.Vertex); - AttachIfNotNull(handle, Current.TessControl); - AttachIfNotNull(handle, Current.TessEvaluation); - AttachIfNotNull(handle, Current.Geometry); - AttachIfNotNull(handle, Current.Fragment); - - GL.LinkProgram(handle); - - CheckProgramLink(handle); - - BindUniformBlocks(handle); - BindTextureLocations(handle); - - _programs.Add(Current, handle); - } - - GL.UseProgram(handle); - - CurrentProgramHandle = handle; - } - - private void EnsureExtraBlock() - { - if (_extraUboHandle == 0) - { - _extraUboHandle = GL.GenBuffer(); - - GL.BindBuffer(BufferTarget.UniformBuffer, _extraUboHandle); - - GL.BufferData(BufferTarget.UniformBuffer, ExtraDataSize * sizeof(float), IntPtr.Zero, BufferUsageHint.StreamDraw); - - GL.BindBufferBase(BufferRangeTarget.UniformBuffer, 0, _extraUboHandle); - } - } - - private void AttachIfNotNull(int programHandle, OglShaderStage stage) - { - if (stage != null) - { - stage.Compile(); - - GL.AttachShader(programHandle, stage.Handle); - } - } - - private void BindUniformBlocks(int programHandle) - { - int extraBlockindex = GL.GetUniformBlockIndex(programHandle, "Extra"); - - GL.UniformBlockBinding(programHandle, extraBlockindex, 0); - - int freeBinding = ReservedCbufCount; - - void BindUniformBlocksIfNotNull(OglShaderStage stage) - { - if (stage != null) - { - foreach (CBufferDescriptor desc in stage.ConstBufferUsage) - { - int blockIndex = GL.GetUniformBlockIndex(programHandle, desc.Name); - - if (blockIndex < 0) - { - // This may be fine, the compiler may optimize away unused uniform buffers, - // and in this case the above call would return -1 as the buffer has been - // optimized away. - continue; - } - - GL.UniformBlockBinding(programHandle, blockIndex, freeBinding); - - freeBinding++; - } - } - } - - BindUniformBlocksIfNotNull(Current.Vertex); - BindUniformBlocksIfNotNull(Current.TessControl); - BindUniformBlocksIfNotNull(Current.TessEvaluation); - BindUniformBlocksIfNotNull(Current.Geometry); - BindUniformBlocksIfNotNull(Current.Fragment); - } - - private void BindTextureLocations(int programHandle) - { - int index = 0; - - void BindTexturesIfNotNull(OglShaderStage stage) - { - if (stage != null) - { - foreach (TextureDescriptor desc in stage.TextureUsage) - { - int location = GL.GetUniformLocation(programHandle, desc.Name); - - GL.Uniform1(location, index); - - index++; - } - } - } - - GL.UseProgram(programHandle); - - BindTexturesIfNotNull(Current.Vertex); - BindTexturesIfNotNull(Current.TessControl); - BindTexturesIfNotNull(Current.TessEvaluation); - BindTexturesIfNotNull(Current.Geometry); - BindTexturesIfNotNull(Current.Fragment); - } - - private static void CheckProgramLink(int handle) - { - int status = 0; - - GL.GetProgram(handle, GetProgramParameterName.LinkStatus, out status); - - if (status == 0) - { - throw new ShaderException(GL.GetProgramInfoLog(handle)); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglShaderProgram.cs b/Ryujinx.Graphics/Gal/OpenGL/OglShaderProgram.cs deleted file mode 100644 index 86126bca4..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglShaderProgram.cs +++ /dev/null @@ -1,87 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.Shader; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - struct OglShaderProgram - { - public OglShaderStage Vertex; - public OglShaderStage TessControl; - public OglShaderStage TessEvaluation; - public OglShaderStage Geometry; - public OglShaderStage Fragment; - } - - class OglShaderStage : IDisposable - { - public int Handle { get; private set; } - - public bool IsCompiled { get; private set; } - - public GalShaderType Type { get; private set; } - - public string Code { get; private set; } - - public IEnumerable ConstBufferUsage { get; private set; } - public IEnumerable TextureUsage { get; private set; } - - public OglShaderStage( - GalShaderType type, - string code, - IEnumerable constBufferUsage, - IEnumerable textureUsage) - { - Type = type; - Code = code; - ConstBufferUsage = constBufferUsage; - TextureUsage = textureUsage; - } - - public void Compile() - { - if (Handle == 0) - { - Handle = GL.CreateShader(OglEnumConverter.GetShaderType(Type)); - - CompileAndCheck(Handle, Code); - } - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing && Handle != 0) - { - GL.DeleteShader(Handle); - - Handle = 0; - } - } - - public static void CompileAndCheck(int handle, string code) - { - GL.ShaderSource(handle, code); - GL.CompileShader(handle); - - CheckCompilation(handle); - } - - private static void CheckCompilation(int handle) - { - int status = 0; - - GL.GetShader(handle, ShaderParameter.CompileStatus, out status); - - if (status == 0) - { - throw new ShaderException(GL.GetShaderInfoLog(handle)); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglStreamBuffer.cs b/Ryujinx.Graphics/Gal/OpenGL/OglStreamBuffer.cs deleted file mode 100644 index 58b3ace5b..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglStreamBuffer.cs +++ /dev/null @@ -1,55 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglStreamBuffer : IDisposable - { - public int Handle { get; protected set; } - - public long Size { get; protected set; } - - protected BufferTarget Target { get; private set; } - - public OglStreamBuffer(BufferTarget target, long size) - { - Target = target; - Size = size; - - Handle = GL.GenBuffer(); - - GL.BindBuffer(target, Handle); - - GL.BufferData(target, (IntPtr)size, IntPtr.Zero, BufferUsageHint.StreamDraw); - } - - public void SetData(long size, IntPtr hostAddress) - { - GL.BindBuffer(Target, Handle); - - GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)size, hostAddress); - } - - public void SetData(byte[] data) - { - GL.BindBuffer(Target, Handle); - - GL.BufferSubData(Target, IntPtr.Zero, (IntPtr)data.Length, data); - } - - public void Dispose() - { - Dispose(true); - } - - protected virtual void Dispose(bool disposing) - { - if (disposing && Handle != 0) - { - GL.DeleteBuffer(Handle); - - Handle = 0; - } - } - } -} diff --git a/Ryujinx.Graphics/Gal/OpenGL/OglTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OglTexture.cs deleted file mode 100644 index b5ac66927..000000000 --- a/Ryujinx.Graphics/Gal/OpenGL/OglTexture.cs +++ /dev/null @@ -1,408 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.Texture; -using System; - -namespace Ryujinx.Graphics.Gal.OpenGL -{ - class OglTexture : IGalTexture - { - private const long MaxTextureCacheSize = 768 * 1024 * 1024; - - private OglCachedResource _textureCache; - - public EventHandler TextureDeleted { get; set; } - - public OglTexture() - { - _textureCache = new OglCachedResource(DeleteTexture, MaxTextureCacheSize); - } - - public void LockCache() - { - _textureCache.Lock(); - } - - public void UnlockCache() - { - _textureCache.Unlock(); - } - - private void DeleteTexture(ImageHandler cachedImage) - { - TextureDeleted?.Invoke(this, cachedImage.Handle); - - GL.DeleteTexture(cachedImage.Handle); - } - - public void Create(long key, int size, GalImage image) - { - int handle = GL.GenTexture(); - - TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); - - GL.BindTexture(target, handle); - - const int level = 0; //TODO: Support mipmap textures. - const int border = 0; - - _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)size); - - if (ImageUtils.IsCompressed(image.Format)) - { - throw new InvalidOperationException("Surfaces with compressed formats are not supported!"); - } - - (PixelInternalFormat internalFmt, - PixelFormat format, - PixelType type) = OglEnumConverter.GetImageFormat(image.Format); - - switch (target) - { - case TextureTarget.Texture1D: - GL.TexImage1D( - target, - level, - internalFmt, - image.Width, - border, - format, - type, - IntPtr.Zero); - break; - - case TextureTarget.Texture2D: - GL.TexImage2D( - target, - level, - internalFmt, - image.Width, - image.Height, - border, - format, - type, - IntPtr.Zero); - break; - case TextureTarget.Texture3D: - GL.TexImage3D( - target, - level, - internalFmt, - image.Width, - image.Height, - image.Depth, - border, - format, - type, - IntPtr.Zero); - break; - // Cube map arrays are just 2D texture arrays with 6 entries - // per cube map so we can handle them in the same way - case TextureTarget.TextureCubeMapArray: - case TextureTarget.Texture2DArray: - GL.TexImage3D( - target, - level, - internalFmt, - image.Width, - image.Height, - image.LayerCount, - border, - format, - type, - IntPtr.Zero); - break; - default: - throw new NotImplementedException($"Unsupported texture target type: {target}"); - } - } - - public void Create(long key, byte[] data, GalImage image) - { - int handle = GL.GenTexture(); - - TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); - - GL.BindTexture(target, handle); - - const int level = 0; //TODO: Support mipmap textures. - const int border = 0; - - _textureCache.AddOrUpdate(key, new ImageHandler(handle, image), (uint)data.Length); - - if (ImageUtils.IsCompressed(image.Format) && !IsAstc(image.Format)) - { - InternalFormat internalFmt = OglEnumConverter.GetCompressedImageFormat(image.Format); - - switch (target) - { - case TextureTarget.Texture1D: - GL.CompressedTexImage1D( - target, - level, - internalFmt, - image.Width, - border, - data.Length, - data); - break; - case TextureTarget.Texture2D: - GL.CompressedTexImage2D( - target, - level, - internalFmt, - image.Width, - image.Height, - border, - data.Length, - data); - break; - case TextureTarget.Texture3D: - GL.CompressedTexImage3D( - target, - level, - internalFmt, - image.Width, - image.Height, - image.Depth, - border, - data.Length, - data); - break; - // Cube map arrays are just 2D texture arrays with 6 entries - // per cube map so we can handle them in the same way - case TextureTarget.TextureCubeMapArray: - case TextureTarget.Texture2DArray: - GL.CompressedTexImage3D( - target, - level, - internalFmt, - image.Width, - image.Height, - image.LayerCount, - border, - data.Length, - data); - break; - case TextureTarget.TextureCubeMap: - Span array = new Span(data); - - int faceSize = ImageUtils.GetSize(image) / 6; - - for (int Face = 0; Face < 6; Face++) - { - GL.CompressedTexImage2D( - TextureTarget.TextureCubeMapPositiveX + Face, - level, - internalFmt, - image.Width, - image.Height, - border, - faceSize, - array.Slice(Face * faceSize, faceSize).ToArray()); - } - break; - default: - throw new NotImplementedException($"Unsupported texture target type: {target}"); - } - } - else - { - // TODO: Use KHR_texture_compression_astc_hdr when available - if (IsAstc(image.Format)) - { - int textureBlockWidth = ImageUtils.GetBlockWidth(image.Format); - int textureBlockHeight = ImageUtils.GetBlockHeight(image.Format); - int textureBlockDepth = ImageUtils.GetBlockDepth(image.Format); - - data = AstcDecoder.DecodeToRgba8888( - data, - textureBlockWidth, - textureBlockHeight, - textureBlockDepth, - image.Width, - image.Height, - image.Depth); - - image.Format = GalImageFormat.Rgba8 | (image.Format & GalImageFormat.TypeMask); - } - - (PixelInternalFormat internalFmt, - PixelFormat format, - PixelType type) = OglEnumConverter.GetImageFormat(image.Format); - - - switch (target) - { - case TextureTarget.Texture1D: - GL.TexImage1D( - target, - level, - internalFmt, - image.Width, - border, - format, - type, - data); - break; - case TextureTarget.Texture2D: - GL.TexImage2D( - target, - level, - internalFmt, - image.Width, - image.Height, - border, - format, - type, - data); - break; - case TextureTarget.Texture3D: - GL.TexImage3D( - target, - level, - internalFmt, - image.Width, - image.Height, - image.Depth, - border, - format, - type, - data); - break; - // Cube map arrays are just 2D texture arrays with 6 entries - // per cube map so we can handle them in the same way - case TextureTarget.TextureCubeMapArray: - case TextureTarget.Texture2DArray: - GL.TexImage3D( - target, - level, - internalFmt, - image.Width, - image.Height, - image.LayerCount, - border, - format, - type, - data); - break; - case TextureTarget.TextureCubeMap: - Span array = new Span(data); - - int faceSize = ImageUtils.GetSize(image) / 6; - - for (int face = 0; face < 6; face++) - { - GL.TexImage2D( - TextureTarget.TextureCubeMapPositiveX + face, - level, - internalFmt, - image.Width, - image.Height, - border, - format, - type, - array.Slice(face * faceSize, faceSize).ToArray()); - } - break; - default: - throw new NotImplementedException($"Unsupported texture target type: {target}"); - } - } - } - - private static bool IsAstc(GalImageFormat format) - { - format &= GalImageFormat.FormatMask; - - return format > GalImageFormat.Astc2DStart && format < GalImageFormat.Astc2DEnd; - } - - public bool TryGetImage(long key, out GalImage image) - { - if (_textureCache.TryGetValue(key, out ImageHandler cachedImage)) - { - image = cachedImage.Image; - - return true; - } - - image = default(GalImage); - - return false; - } - - public bool TryGetImageHandler(long key, out ImageHandler cachedImage) - { - if (_textureCache.TryGetValue(key, out cachedImage)) - { - return true; - } - - cachedImage = null; - - return false; - } - - public void Bind(long key, int index, GalImage image) - { - if (_textureCache.TryGetValue(key, out ImageHandler cachedImage)) - { - GL.ActiveTexture(TextureUnit.Texture0 + index); - - TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); - - GL.BindTexture(target, cachedImage.Handle); - - int[] swizzleRgba = new int[] - { - (int)OglEnumConverter.GetTextureSwizzle(image.XSource), - (int)OglEnumConverter.GetTextureSwizzle(image.YSource), - (int)OglEnumConverter.GetTextureSwizzle(image.ZSource), - (int)OglEnumConverter.GetTextureSwizzle(image.WSource) - }; - - GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); - } - } - - public void SetSampler(GalImage image, GalTextureSampler sampler) - { - int wrapS = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressU); - int wrapT = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressV); - int wrapR = (int)OglEnumConverter.GetTextureWrapMode(sampler.AddressP); - - int minFilter = (int)OglEnumConverter.GetTextureMinFilter(sampler.MinFilter, sampler.MipFilter); - int magFilter = (int)OglEnumConverter.GetTextureMagFilter(sampler.MagFilter); - - TextureTarget target = ImageUtils.GetTextureTarget(image.TextureTarget); - - GL.TexParameter(target, TextureParameterName.TextureWrapS, wrapS); - GL.TexParameter(target, TextureParameterName.TextureWrapT, wrapT); - GL.TexParameter(target, TextureParameterName.TextureWrapR, wrapR); - - GL.TexParameter(target, TextureParameterName.TextureMinFilter, minFilter); - GL.TexParameter(target, TextureParameterName.TextureMagFilter, magFilter); - - float[] color = new float[] - { - sampler.BorderColor.Red, - sampler.BorderColor.Green, - sampler.BorderColor.Blue, - sampler.BorderColor.Alpha - }; - - GL.TexParameter(target, TextureParameterName.TextureBorderColor, color); - - if (sampler.DepthCompare) - { - GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.CompareRToTexture); - GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)OglEnumConverter.GetDepthCompareFunc(sampler.DepthCompareFunc)); - } - else - { - GL.TexParameter(target, TextureParameterName.TextureCompareMode, (int)All.None); - GL.TexParameter(target, TextureParameterName.TextureCompareFunc, (int)All.Never); - } - } - } -} diff --git a/Ryujinx.Graphics/Gal/ShaderException.cs b/Ryujinx.Graphics/Gal/ShaderException.cs deleted file mode 100644 index b0aff42bf..000000000 --- a/Ryujinx.Graphics/Gal/ShaderException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Gal -{ - class ShaderException : Exception - { - public ShaderException() : base() { } - - public ShaderException(string message) : base(message) { } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/GpuResourceManager.cs b/Ryujinx.Graphics/GpuResourceManager.cs deleted file mode 100644 index 16e9f5790..000000000 --- a/Ryujinx.Graphics/GpuResourceManager.cs +++ /dev/null @@ -1,169 +0,0 @@ -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.Texture; -using System.Collections.Generic; - -namespace Ryujinx.Graphics -{ - public class GpuResourceManager - { - private enum ImageType - { - None, - Texture, - TextureArrayLayer, - ColorBuffer, - ZetaBuffer - } - - private NvGpu _gpu; - - private HashSet[] _uploadedKeys; - - private Dictionary _imageTypes; - private Dictionary _mirroredTextures; - - public GpuResourceManager(NvGpu gpu) - { - _gpu = gpu; - - _uploadedKeys = new HashSet[(int)NvGpuBufferType.Count]; - - for (int index = 0; index < _uploadedKeys.Length; index++) - { - _uploadedKeys[index] = new HashSet(); - } - - _imageTypes = new Dictionary(); - _mirroredTextures = new Dictionary(); - } - - public void SendColorBuffer(NvGpuVmm vmm, long position, int attachment, GalImage newImage) - { - long size = (uint)ImageUtils.GetSize(newImage); - - _imageTypes[position] = ImageType.ColorBuffer; - - if (!TryReuse(vmm, position, newImage)) - { - _gpu.Renderer.Texture.Create(position, (int)size, newImage); - } - - _gpu.Renderer.RenderTarget.BindColor(position, attachment); - } - - public void SendZetaBuffer(NvGpuVmm vmm, long position, GalImage newImage) - { - long size = (uint)ImageUtils.GetSize(newImage); - - _imageTypes[position] = ImageType.ZetaBuffer; - - if (!TryReuse(vmm, position, newImage)) - { - _gpu.Renderer.Texture.Create(position, (int)size, newImage); - } - - _gpu.Renderer.RenderTarget.BindZeta(position); - } - - public void SendTexture(NvGpuVmm vmm, long position, GalImage newImage) - { - PrepareSendTexture(vmm, position, newImage); - - _imageTypes[position] = ImageType.Texture; - } - - public bool TryGetTextureLayer(long position, out int layerIndex) - { - if (_mirroredTextures.TryGetValue(position, out layerIndex)) - { - ImageType type = _imageTypes[position]; - - // FIXME(thog): I'm actually unsure if we should deny all other image type, gpu testing needs to be done here. - if (type != ImageType.Texture && type != ImageType.TextureArrayLayer) - { - layerIndex = -1; - return false; - } - - return true; - } - - layerIndex = -1; - return false; - } - - public void SetTextureArrayLayer(long position, int layerIndex) - { - _imageTypes[position] = ImageType.TextureArrayLayer; - _mirroredTextures[position] = layerIndex; - } - - private void PrepareSendTexture(NvGpuVmm vmm, long position, GalImage newImage) - { - long size = ImageUtils.GetSize(newImage); - - bool skipCheck = false; - - if (_imageTypes.TryGetValue(position, out ImageType oldType)) - { - if (oldType == ImageType.ColorBuffer || oldType == ImageType.ZetaBuffer) - { - // Avoid data destruction - MemoryRegionModified(vmm, position, size, NvGpuBufferType.Texture); - - skipCheck = true; - } - } - - if (skipCheck || !MemoryRegionModified(vmm, position, size, NvGpuBufferType.Texture)) - { - if (TryReuse(vmm, position, newImage)) - { - return; - } - } - - byte[] data = ImageUtils.ReadTexture(vmm, newImage, position); - - _gpu.Renderer.Texture.Create(position, data, newImage); - } - - private bool TryReuse(NvGpuVmm vmm, long position, GalImage newImage) - { - if (_gpu.Renderer.Texture.TryGetImage(position, out GalImage cachedImage) && cachedImage.TextureTarget == newImage.TextureTarget && cachedImage.SizeMatches(newImage)) - { - _gpu.Renderer.RenderTarget.Reinterpret(position, newImage); - - return true; - } - - return false; - } - - public bool MemoryRegionModified(NvGpuVmm vmm, long position, long size, NvGpuBufferType type) - { - HashSet uploaded = _uploadedKeys[(int)type]; - - if (!uploaded.Add(position)) - { - return false; - } - - return vmm.IsRegionModified(position, size, type); - } - - public void ClearPbCache() - { - for (int index = 0; index < _uploadedKeys.Length; index++) - { - _uploadedKeys[index].Clear(); - } - } - - public void ClearPbCache(NvGpuBufferType type) - { - _uploadedKeys[(int)type].Clear(); - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs b/Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs deleted file mode 100644 index aa0db6826..000000000 --- a/Ryujinx.Graphics/Graphics3d/INvGpuEngine.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Ryujinx.Graphics.Memory; - -namespace Ryujinx.Graphics.Graphics3d -{ - interface INvGpuEngine - { - int[] Registers { get; } - - void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine.cs deleted file mode 100644 index 20c36fda3..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Graphics3d -{ - enum NvGpuEngine - { - _2d = 0x902d, - _3d = 0xb197, - Compute = 0xb1c0, - P2mf = 0xa140, - M2mf = 0xb0b5 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs deleted file mode 100644 index b6dae2a38..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2d.cs +++ /dev/null @@ -1,263 +0,0 @@ -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.Texture; -using Ryujinx.Profiler; - -namespace Ryujinx.Graphics.Graphics3d -{ - class NvGpuEngine2d : INvGpuEngine - { - private enum CopyOperation - { - SrcCopyAnd, - RopAnd, - Blend, - SrcCopy, - Rop, - SrcCopyPremult, - BlendPremult - } - - public int[] Registers { get; private set; } - - private NvGpu _gpu; - - public NvGpuEngine2d(NvGpu gpu) - { - _gpu = gpu; - - Registers = new int[0x238]; - } - - public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - WriteRegister(methCall); - - if ((NvGpuEngine2dReg)methCall.Method == NvGpuEngine2dReg.BlitSrcYInt) - { - TextureCopy(vmm); - } - } - - private void TextureCopy(NvGpuVmm vmm) - { - Profile.Begin(Profiles.GPU.Engine2d.TextureCopy); - - CopyOperation operation = (CopyOperation)ReadRegister(NvGpuEngine2dReg.CopyOperation); - - int dstFormat = ReadRegister(NvGpuEngine2dReg.DstFormat); - bool dstLinear = ReadRegister(NvGpuEngine2dReg.DstLinear) != 0; - int dstWidth = ReadRegister(NvGpuEngine2dReg.DstWidth); - int dstHeight = ReadRegister(NvGpuEngine2dReg.DstHeight); - int dstDepth = ReadRegister(NvGpuEngine2dReg.DstDepth); - int dstLayer = ReadRegister(NvGpuEngine2dReg.DstLayer); - int dstPitch = ReadRegister(NvGpuEngine2dReg.DstPitch); - int dstBlkDim = ReadRegister(NvGpuEngine2dReg.DstBlockDimensions); - - int srcFormat = ReadRegister(NvGpuEngine2dReg.SrcFormat); - bool srcLinear = ReadRegister(NvGpuEngine2dReg.SrcLinear) != 0; - int srcWidth = ReadRegister(NvGpuEngine2dReg.SrcWidth); - int srcHeight = ReadRegister(NvGpuEngine2dReg.SrcHeight); - int srcDepth = ReadRegister(NvGpuEngine2dReg.SrcDepth); - int srcLayer = ReadRegister(NvGpuEngine2dReg.SrcLayer); - int srcPitch = ReadRegister(NvGpuEngine2dReg.SrcPitch); - int srcBlkDim = ReadRegister(NvGpuEngine2dReg.SrcBlockDimensions); - - int dstBlitX = ReadRegister(NvGpuEngine2dReg.BlitDstX); - int dstBlitY = ReadRegister(NvGpuEngine2dReg.BlitDstY); - int dstBlitW = ReadRegister(NvGpuEngine2dReg.BlitDstW); - int dstBlitH = ReadRegister(NvGpuEngine2dReg.BlitDstH); - - long blitDuDx = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDuDxFract); - long blitDvDy = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitDvDyFract); - - long srcBlitX = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcXFract); - long srcBlitY = ReadRegisterFixed1_31_32(NvGpuEngine2dReg.BlitSrcYFract); - - GalImageFormat srcImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)srcFormat); - GalImageFormat dstImgFormat = ImageUtils.ConvertSurface((GalSurfaceFormat)dstFormat); - - GalMemoryLayout srcLayout = GetLayout(srcLinear); - GalMemoryLayout dstLayout = GetLayout(dstLinear); - - int srcBlockHeight = 1 << ((srcBlkDim >> 4) & 0xf); - int dstBlockHeight = 1 << ((dstBlkDim >> 4) & 0xf); - - long srcAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.SrcAddress); - long dstAddress = MakeInt64From2xInt32(NvGpuEngine2dReg.DstAddress); - - long srcKey = vmm.GetPhysicalAddress(srcAddress); - long dstKey = vmm.GetPhysicalAddress(dstAddress); - - bool isSrcLayered = false; - bool isDstLayered = false; - - GalTextureTarget srcTarget = GalTextureTarget.TwoD; - - if (srcDepth != 0) - { - srcTarget = GalTextureTarget.TwoDArray; - srcDepth++; - isSrcLayered = true; - } - else - { - srcDepth = 1; - } - - GalTextureTarget dstTarget = GalTextureTarget.TwoD; - - if (dstDepth != 0) - { - dstTarget = GalTextureTarget.TwoDArray; - dstDepth++; - isDstLayered = true; - } - else - { - dstDepth = 1; - } - - GalImage srcTexture = new GalImage( - srcWidth, - srcHeight, - 1, srcDepth, 1, - srcBlockHeight, 1, - srcLayout, - srcImgFormat, - srcTarget); - - GalImage dstTexture = new GalImage( - dstWidth, - dstHeight, - 1, dstDepth, 1, - dstBlockHeight, 1, - dstLayout, - dstImgFormat, - dstTarget); - - srcTexture.Pitch = srcPitch; - dstTexture.Pitch = dstPitch; - - long GetLayerOffset(GalImage image, int layer) - { - int targetMipLevel = image.MaxMipmapLevel <= 1 ? 1 : image.MaxMipmapLevel - 1; - return ImageUtils.GetLayerOffset(image, targetMipLevel) * layer; - } - - int srcLayerIndex = -1; - - if (isSrcLayered && _gpu.ResourceManager.TryGetTextureLayer(srcKey, out srcLayerIndex) && srcLayerIndex != 0) - { - srcKey = srcKey - GetLayerOffset(srcTexture, srcLayerIndex); - } - - int dstLayerIndex = -1; - - if (isDstLayered && _gpu.ResourceManager.TryGetTextureLayer(dstKey, out dstLayerIndex) && dstLayerIndex != 0) - { - dstKey = dstKey - GetLayerOffset(dstTexture, dstLayerIndex); - } - - _gpu.ResourceManager.SendTexture(vmm, srcKey, srcTexture); - _gpu.ResourceManager.SendTexture(vmm, dstKey, dstTexture); - - if (isSrcLayered && srcLayerIndex == -1) - { - for (int layer = 0; layer < srcTexture.LayerCount; layer++) - { - _gpu.ResourceManager.SetTextureArrayLayer(srcKey + GetLayerOffset(srcTexture, layer), layer); - } - - srcLayerIndex = 0; - } - - if (isDstLayered && dstLayerIndex == -1) - { - for (int layer = 0; layer < dstTexture.LayerCount; layer++) - { - _gpu.ResourceManager.SetTextureArrayLayer(dstKey + GetLayerOffset(dstTexture, layer), layer); - } - - dstLayerIndex = 0; - } - - int srcBlitX1 = (int)(srcBlitX >> 32); - int srcBlitY1 = (int)(srcBlitY >> 32); - - int srcBlitX2 = (int)(srcBlitX + dstBlitW * blitDuDx >> 32); - int srcBlitY2 = (int)(srcBlitY + dstBlitH * blitDvDy >> 32); - - _gpu.Renderer.RenderTarget.Copy( - srcTexture, - dstTexture, - srcKey, - dstKey, - srcLayerIndex, - dstLayerIndex, - srcBlitX1, - srcBlitY1, - srcBlitX2, - srcBlitY2, - dstBlitX, - dstBlitY, - dstBlitX + dstBlitW, - dstBlitY + dstBlitH); - - // Do a guest side copy as well. This is necessary when - // the texture is modified by the guest, however it doesn't - // work when resources that the gpu can write to are copied, - // like framebuffers. - - // FIXME: SUPPORT MULTILAYER CORRECTLY HERE (this will cause weird stuffs on the first layer) - ImageUtils.CopyTexture( - vmm, - srcTexture, - dstTexture, - srcAddress, - dstAddress, - srcBlitX1, - srcBlitY1, - dstBlitX, - dstBlitY, - dstBlitW, - dstBlitH); - - vmm.IsRegionModified(dstKey, ImageUtils.GetSize(dstTexture), NvGpuBufferType.Texture); - - Profile.End(Profiles.GPU.Engine2d.TextureCopy); - } - - private static GalMemoryLayout GetLayout(bool linear) - { - return linear - ? GalMemoryLayout.Pitch - : GalMemoryLayout.BlockLinear; - } - - private long MakeInt64From2xInt32(NvGpuEngine2dReg reg) - { - return - (long)Registers[(int)reg + 0] << 32 | - (uint)Registers[(int)reg + 1]; - } - - private void WriteRegister(GpuMethodCall methCall) - { - Registers[methCall.Method] = methCall.Argument; - } - - private long ReadRegisterFixed1_31_32(NvGpuEngine2dReg reg) - { - long low = (uint)ReadRegister(reg + 0); - long high = (uint)ReadRegister(reg + 1); - - return low | (high << 32); - } - - private int ReadRegister(NvGpuEngine2dReg reg) - { - return Registers[(int)reg]; - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs deleted file mode 100644 index 7747b5a3a..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine2dReg.cs +++ /dev/null @@ -1,41 +0,0 @@ -namespace Ryujinx.Graphics.Graphics3d -{ - enum NvGpuEngine2dReg - { - DstFormat = 0x80, - DstLinear = 0x81, - DstBlockDimensions = 0x82, - DstDepth = 0x83, - DstLayer = 0x84, - DstPitch = 0x85, - DstWidth = 0x86, - DstHeight = 0x87, - DstAddress = 0x88, - DstAddressLow = 0x89, - SrcFormat = 0x8c, - SrcLinear = 0x8d, - SrcBlockDimensions = 0x8e, - SrcDepth = 0x8f, - SrcLayer = 0x90, - SrcPitch = 0x91, - SrcWidth = 0x92, - SrcHeight = 0x93, - SrcAddress = 0x94, - SrcAddressLow = 0x95, - ClipEnable = 0xa4, - CopyOperation = 0xab, - BlitControl = 0x223, - BlitDstX = 0x22c, - BlitDstY = 0x22d, - BlitDstW = 0x22e, - BlitDstH = 0x22f, - BlitDuDxFract = 0x230, - BlitDuDxInt = 0x231, - BlitDvDyFract = 0x232, - BlitDvDyInt = 0x233, - BlitSrcXFract = 0x234, - BlitSrcXInt = 0x235, - BlitSrcYFract = 0x236, - BlitSrcYInt = 0x237 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs deleted file mode 100644 index e475a9641..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3d.cs +++ /dev/null @@ -1,1237 +0,0 @@ -using Ryujinx.Common; -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Texture; -using System; -using System.Collections.Generic; -using Ryujinx.Profiler; - -namespace Ryujinx.Graphics.Graphics3d -{ - class NvGpuEngine3d : INvGpuEngine - { - public int[] Registers { get; private set; } - - private NvGpu _gpu; - - private Dictionary _methods; - - private struct ConstBuffer - { - public bool Enabled; - public long Position; - public int Size; - } - - private ConstBuffer[][] _constBuffers; - - // Viewport dimensions kept for scissor test limits - private int _viewportX0 = 0; - private int _viewportY0 = 0; - private int _viewportX1 = 0; - private int _viewportY1 = 0; - private int _viewportWidth = 0; - private int _viewportHeight = 0; - - private int _currentInstance = 0; - - public NvGpuEngine3d(NvGpu gpu) - { - _gpu = gpu; - - Registers = new int[0xe00]; - - _methods = new Dictionary(); - - void AddMethod(int meth, int count, int stride, NvGpuMethod method) - { - while (count-- > 0) - { - _methods.Add(meth, method); - - meth += stride; - } - } - - AddMethod(0x585, 1, 1, VertexEndGl); - AddMethod(0x674, 1, 1, ClearBuffers); - AddMethod(0x6c3, 1, 1, QueryControl); - AddMethod(0x8e4, 16, 1, CbData); - AddMethod(0x904, 5, 8, CbBind); - - _constBuffers = new ConstBuffer[6][]; - - for (int index = 0; index < _constBuffers.Length; index++) - { - _constBuffers[index] = new ConstBuffer[18]; - } - - // Ensure that all components are enabled by default. - // FIXME: Is this correct? - WriteRegister(NvGpuEngine3dReg.ColorMaskN, 0x1111); - - WriteRegister(NvGpuEngine3dReg.FrameBufferSrgb, 1); - - WriteRegister(NvGpuEngine3dReg.FrontFace, (int)GalFrontFace.Cw); - - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - WriteRegister(NvGpuEngine3dReg.IBlendNEquationRgb + index * 8, (int)GalBlendEquation.FuncAdd); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcRgb + index * 8, (int)GalBlendFactor.One); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstRgb + index * 8, (int)GalBlendFactor.Zero); - WriteRegister(NvGpuEngine3dReg.IBlendNEquationAlpha + index * 8, (int)GalBlendEquation.FuncAdd); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncSrcAlpha + index * 8, (int)GalBlendFactor.One); - WriteRegister(NvGpuEngine3dReg.IBlendNFuncDstAlpha + index * 8, (int)GalBlendFactor.Zero); - } - } - - public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - if (_methods.TryGetValue(methCall.Method, out NvGpuMethod method)) - { - ProfileConfig profile = Profiles.GPU.Engine3d.CallMethod; - - profile.SessionItem = method.Method.Name; - - Profile.Begin(profile); - - method(vmm, methCall); - - Profile.End(profile); - } - else - { - WriteRegister(methCall); - } - } - - private void VertexEndGl(NvGpuVmm vmm, GpuMethodCall methCall) - { - Profile.Begin(Profiles.GPU.Engine3d.VertexEnd); - - LockCaches(); - - Profile.Begin(Profiles.GPU.Engine3d.ConfigureState); - - GalPipelineState state = new GalPipelineState(); - - // Framebuffer must be run configured because viewport dimensions may be used in other methods - SetFrameBuffer(state); - - Profile.End(Profiles.GPU.Engine3d.ConfigureState); - - for (int fbIndex = 0; fbIndex < 8; fbIndex++) - { - SetFrameBuffer(vmm, fbIndex); - } - - SetFrontFace(state); - SetCullFace(state); - SetDepth(state); - SetStencil(state); - SetScissor(state); - SetBlending(state); - SetColorMask(state); - SetPrimitiveRestart(state); - - SetZeta(vmm); - - SetRenderTargets(); - - long[] keys = UploadShaders(vmm); - - _gpu.Renderer.Shader.BindProgram(); - - UploadTextures(vmm, state, keys); - UploadConstBuffers(vmm, state, keys); - UploadVertexArrays(vmm, state); - - DispatchRender(vmm, state); - - UnlockCaches(); - - Profile.End(Profiles.GPU.Engine3d.VertexEnd); - } - - private void LockCaches() - { - _gpu.Renderer.Buffer.LockCache(); - _gpu.Renderer.Rasterizer.LockCaches(); - _gpu.Renderer.Texture.LockCache(); - } - - private void UnlockCaches() - { - _gpu.Renderer.Buffer.UnlockCache(); - _gpu.Renderer.Rasterizer.UnlockCaches(); - _gpu.Renderer.Texture.UnlockCache(); - } - - private void ClearBuffers(NvGpuVmm vmm, GpuMethodCall methCall) - { - Profile.Begin(Profiles.GPU.Engine3d.ClearBuffers); - - int attachment = (methCall.Argument >> 6) & 0xf; - - GalClearBufferFlags flags = (GalClearBufferFlags)(methCall.Argument & 0x3f); - - float red = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 0); - float green = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 1); - float blue = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 2); - float alpha = ReadRegisterFloat(NvGpuEngine3dReg.ClearNColor + 3); - - float depth = ReadRegisterFloat(NvGpuEngine3dReg.ClearDepth); - - int stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); - - SetFrameBuffer(vmm, attachment); - - SetZeta(vmm); - - SetRenderTargets(); - - _gpu.Renderer.RenderTarget.Bind(); - - _gpu.Renderer.Rasterizer.ClearBuffers(flags, attachment, red, green, blue, alpha, depth, stencil); - - _gpu.Renderer.Pipeline.ResetDepthMask(); - _gpu.Renderer.Pipeline.ResetColorMask(attachment); - - Profile.End(Profiles.GPU.Engine3d.ClearBuffers); - } - - private void SetFrameBuffer(NvGpuVmm vmm, int fbIndex) - { - ProfileConfig profile = Profiles.GPU.Engine3d.SetFrameBuffer; - - profile.SessionItem = fbIndex.ToString(); - - Profile.Begin(profile); - - long va = MakeInt64From2xInt32(NvGpuEngine3dReg.FrameBufferNAddress + fbIndex * 0x10); - - int surfFormat = ReadRegister(NvGpuEngine3dReg.FrameBufferNFormat + fbIndex * 0x10); - - if (va == 0 || surfFormat == 0) - { - _gpu.Renderer.RenderTarget.UnbindColor(fbIndex); - - Profile.End(profile); - - return; - } - - long key = vmm.GetPhysicalAddress(va); - - int width = ReadRegister(NvGpuEngine3dReg.FrameBufferNWidth + fbIndex * 0x10); - int height = ReadRegister(NvGpuEngine3dReg.FrameBufferNHeight + fbIndex * 0x10); - - int arrayMode = ReadRegister(NvGpuEngine3dReg.FrameBufferNArrayMode + fbIndex * 0x10); - int layerCount = arrayMode & 0xFFFF; - int layerStride = ReadRegister(NvGpuEngine3dReg.FrameBufferNLayerStride + fbIndex * 0x10); - int baseLayer = ReadRegister(NvGpuEngine3dReg.FrameBufferNBaseLayer + fbIndex * 0x10); - int blockDim = ReadRegister(NvGpuEngine3dReg.FrameBufferNBlockDim + fbIndex * 0x10); - - int gobBlockHeight = 1 << ((blockDim >> 4) & 7); - - GalMemoryLayout layout = (GalMemoryLayout)((blockDim >> 12) & 1); - - float tx = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateX + fbIndex * 8); - float ty = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNTranslateY + fbIndex * 8); - - float sx = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleX + fbIndex * 8); - float sy = ReadRegisterFloat(NvGpuEngine3dReg.ViewportNScaleY + fbIndex * 8); - - _viewportX0 = (int)MathF.Max(0, tx - MathF.Abs(sx)); - _viewportY0 = (int)MathF.Max(0, ty - MathF.Abs(sy)); - - _viewportX1 = (int)(tx + MathF.Abs(sx)); - _viewportY1 = (int)(ty + MathF.Abs(sy)); - - GalImageFormat format = ImageUtils.ConvertSurface((GalSurfaceFormat)surfFormat); - - GalImage image = new GalImage(width, height, 1, 1, 1, gobBlockHeight, 1, layout, format, GalTextureTarget.TwoD); - - _gpu.ResourceManager.SendColorBuffer(vmm, key, fbIndex, image); - - _gpu.Renderer.RenderTarget.SetViewport(fbIndex, _viewportX0, _viewportY0, _viewportX1 - _viewportX0, _viewportY1 - _viewportY0); - - Profile.End(profile); - } - - private void SetFrameBuffer(GalPipelineState state) - { - state.FramebufferSrgb = ReadRegisterBool(NvGpuEngine3dReg.FrameBufferSrgb); - - state.FlipX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); - state.FlipY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); - - int screenYControl = ReadRegister(NvGpuEngine3dReg.ScreenYControl); - - bool negateY = (screenYControl & 1) != 0; - - if (negateY) - { - state.FlipY = -state.FlipY; - } - } - - private void SetZeta(NvGpuVmm vmm) - { - Profile.Begin(Profiles.GPU.Engine3d.SetZeta); - - long va = MakeInt64From2xInt32(NvGpuEngine3dReg.ZetaAddress); - - int zetaFormat = ReadRegister(NvGpuEngine3dReg.ZetaFormat); - - int blockDim = ReadRegister(NvGpuEngine3dReg.ZetaBlockDimensions); - - int gobBlockHeight = 1 << ((blockDim >> 4) & 7); - - GalMemoryLayout layout = (GalMemoryLayout)((blockDim >> 12) & 1); //? - - bool zetaEnable = ReadRegisterBool(NvGpuEngine3dReg.ZetaEnable); - - if (va == 0 || zetaFormat == 0 || !zetaEnable) - { - _gpu.Renderer.RenderTarget.UnbindZeta(); - - Profile.End(Profiles.GPU.Engine3d.SetZeta); - - return; - } - - long key = vmm.GetPhysicalAddress(va); - - int width = ReadRegister(NvGpuEngine3dReg.ZetaHoriz); - int height = ReadRegister(NvGpuEngine3dReg.ZetaVert); - - GalImageFormat format = ImageUtils.ConvertZeta((GalZetaFormat)zetaFormat); - - // TODO: Support non 2D? - GalImage image = new GalImage(width, height, 1, 1, 1, gobBlockHeight, 1, layout, format, GalTextureTarget.TwoD); - - _gpu.ResourceManager.SendZetaBuffer(vmm, key, image); - - Profile.End(Profiles.GPU.Engine3d.SetZeta); - } - - private long[] UploadShaders(NvGpuVmm vmm) - { - Profile.Begin(Profiles.GPU.Engine3d.UploadShaders); - - long[] keys = new long[5]; - - long basePosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - - int index = 1; - - int vpAControl = ReadRegister(NvGpuEngine3dReg.ShaderNControl); - - bool vpAEnable = (vpAControl & 1) != 0; - - if (vpAEnable) - { - // Note: The maxwell supports 2 vertex programs, usually - // only VP B is used, but in some cases VP A is also used. - // In this case, it seems to function as an extra vertex - // shader stage. - // The graphics abstraction layer has a special overload for this - // case, which should merge the two shaders into one vertex shader. - int vpAOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset); - int vpBOffset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + 0x10); - - long vpAPos = basePosition + (uint)vpAOffset; - long vpBPos = basePosition + (uint)vpBOffset; - - keys[(int)GalShaderType.Vertex] = vpBPos; - - _gpu.Renderer.Shader.Create(vmm, vpAPos, vpBPos, GalShaderType.Vertex); - _gpu.Renderer.Shader.Bind(vpBPos); - - index = 2; - } - - for (; index < 6; index++) - { - GalShaderType type = GetTypeFromProgram(index); - - int control = ReadRegister(NvGpuEngine3dReg.ShaderNControl + index * 0x10); - int offset = ReadRegister(NvGpuEngine3dReg.ShaderNOffset + index * 0x10); - - // Note: Vertex Program (B) is always enabled. - bool enable = (control & 1) != 0 || index == 1; - - if (!enable) - { - _gpu.Renderer.Shader.Unbind(type); - - continue; - } - - long key = basePosition + (uint)offset; - - keys[(int)type] = key; - - _gpu.Renderer.Shader.Create(vmm, key, type); - _gpu.Renderer.Shader.Bind(key); - } - - Profile.End(Profiles.GPU.Engine3d.UploadShaders); - - return keys; - } - - private static GalShaderType GetTypeFromProgram(int program) - { - switch (program) - { - case 0: - case 1: return GalShaderType.Vertex; - case 2: return GalShaderType.TessControl; - case 3: return GalShaderType.TessEvaluation; - case 4: return GalShaderType.Geometry; - case 5: return GalShaderType.Fragment; - } - - throw new ArgumentOutOfRangeException(nameof(program)); - } - - private void SetFrontFace(GalPipelineState state) - { - float signX = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleX); - float signY = GetFlipSign(NvGpuEngine3dReg.ViewportNScaleY); - - GalFrontFace frontFace = (GalFrontFace)ReadRegister(NvGpuEngine3dReg.FrontFace); - - // Flipping breaks facing. Flipping front facing too fixes it - if (signX != signY) - { - switch (frontFace) - { - case GalFrontFace.Cw: frontFace = GalFrontFace.Ccw; break; - case GalFrontFace.Ccw: frontFace = GalFrontFace.Cw; break; - } - } - - state.FrontFace = frontFace; - } - - private void SetCullFace(GalPipelineState state) - { - state.CullFaceEnabled = ReadRegisterBool(NvGpuEngine3dReg.CullFaceEnable); - - if (state.CullFaceEnabled) - { - state.CullFace = (GalCullFace)ReadRegister(NvGpuEngine3dReg.CullFace); - } - } - - private void SetDepth(GalPipelineState state) - { - state.DepthTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthTestEnable); - - state.DepthWriteEnabled = ReadRegisterBool(NvGpuEngine3dReg.DepthWriteEnable); - - if (state.DepthTestEnabled) - { - state.DepthFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.DepthTestFunction); - } - - state.DepthRangeNear = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNNear); - state.DepthRangeFar = ReadRegisterFloat(NvGpuEngine3dReg.DepthRangeNFar); - } - - private void SetStencil(GalPipelineState state) - { - state.StencilTestEnabled = ReadRegisterBool(NvGpuEngine3dReg.StencilEnable); - - if (state.StencilTestEnabled) - { - state.StencilBackFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilBackFuncFunc); - state.StencilBackFuncRef = ReadRegister(NvGpuEngine3dReg.StencilBackFuncRef); - state.StencilBackFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackFuncMask); - state.StencilBackOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpFail); - state.StencilBackOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZFail); - state.StencilBackOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilBackOpZPass); - state.StencilBackMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilBackMask); - - state.StencilFrontFuncFunc = (GalComparisonOp)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncFunc); - state.StencilFrontFuncRef = ReadRegister(NvGpuEngine3dReg.StencilFrontFuncRef); - state.StencilFrontFuncMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontFuncMask); - state.StencilFrontOpFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpFail); - state.StencilFrontOpZFail = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZFail); - state.StencilFrontOpZPass = (GalStencilOp)ReadRegister(NvGpuEngine3dReg.StencilFrontOpZPass); - state.StencilFrontMask = (uint)ReadRegister(NvGpuEngine3dReg.StencilFrontMask); - } - } - - private void SetScissor(GalPipelineState state) - { - int count = 0; - - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - state.ScissorTestEnabled[index] = ReadRegisterBool(NvGpuEngine3dReg.ScissorEnable + index * 4); - - if (state.ScissorTestEnabled[index]) - { - uint scissorHorizontal = (uint)ReadRegister(NvGpuEngine3dReg.ScissorHorizontal + index * 4); - uint scissorVertical = (uint)ReadRegister(NvGpuEngine3dReg.ScissorVertical + index * 4); - - int left = (int)(scissorHorizontal & 0xFFFF); // Left, lower 16 bits - int right = (int)(scissorHorizontal >> 16); // Right, upper 16 bits - - int bottom = (int)(scissorVertical & 0xFFFF); // Bottom, lower 16 bits - int top = (int)(scissorVertical >> 16); // Top, upper 16 bits - - int width = Math.Abs(right - left); - int height = Math.Abs(top - bottom); - - // If the scissor test covers the whole possible viewport, i.e. uninitialized, disable scissor test - if ((width > NvGpu.MaxViewportSize && height > NvGpu.MaxViewportSize) || width <= 0 || height <= 0) - { - state.ScissorTestEnabled[index] = false; - continue; - } - - // Keep track of how many scissor tests are active. - // If only 1, and it's the first user should apply to all viewports - count++; - - // Flip X - if (state.FlipX == -1) - { - left = _viewportX1 - (left - _viewportX0); - right = _viewportX1 - (right - _viewportX0); - } - - // Ensure X is in the right order - if (left > right) - { - int temp = left; - left = right; - right = temp; - } - - // Flip Y - if (state.FlipY == -1) - { - bottom = _viewportY1 - (bottom - _viewportY0); - top = _viewportY1 - (top - _viewportY0); - } - - // Ensure Y is in the right order - if (bottom > top) - { - int temp = top; - top = bottom; - bottom = temp; - } - - // Handle out of active viewport dimensions - left = Math.Clamp(left, _viewportX0, _viewportX1); - right = Math.Clamp(right, _viewportX0, _viewportX1); - top = Math.Clamp(top, _viewportY0, _viewportY1); - bottom = Math.Clamp(bottom, _viewportY0, _viewportY1); - - // Save values to state - state.ScissorTestX[index] = left; - state.ScissorTestY[index] = bottom; - - state.ScissorTestWidth[index] = right - left; - state.ScissorTestHeight[index] = top - bottom; - } - } - - state.ScissorTestCount = count; - } - - private void SetBlending(GalPipelineState state) - { - bool blendIndependent = ReadRegisterBool(NvGpuEngine3dReg.BlendIndependent); - - state.BlendIndependent = blendIndependent; - - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - if (blendIndependent) - { - state.Blends[index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable + index); - - if (state.Blends[index].Enabled) - { - state.Blends[index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.IBlendNSeparateAlpha + index * 8); - - state.Blends[index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationRgb + index * 8); - state.Blends[index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcRgb + index * 8); - state.Blends[index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstRgb + index * 8); - state.Blends[index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.IBlendNEquationAlpha + index * 8); - state.Blends[index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncSrcAlpha + index * 8); - state.Blends[index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.IBlendNFuncDstAlpha + index * 8); - } - } - else - { - // It seems that even when independent blend is disabled, the first IBlend enable - // register is still set to indicate whenever blend is enabled or not (?). - state.Blends[index].Enabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable); - - if (state.Blends[index].Enabled) - { - state.Blends[index].SeparateAlpha = ReadRegisterBool(NvGpuEngine3dReg.BlendSeparateAlpha); - - state.Blends[index].EquationRgb = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationRgb); - state.Blends[index].FuncSrcRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcRgb); - state.Blends[index].FuncDstRgb = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstRgb); - state.Blends[index].EquationAlpha = ReadBlendEquation(NvGpuEngine3dReg.BlendEquationAlpha); - state.Blends[index].FuncSrcAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncSrcAlpha); - state.Blends[index].FuncDstAlpha = ReadBlendFactor (NvGpuEngine3dReg.BlendFuncDstAlpha); - } - } - } - } - - private GalBlendEquation ReadBlendEquation(NvGpuEngine3dReg register) - { - return (GalBlendEquation)ReadRegister(register); - } - - private GalBlendFactor ReadBlendFactor(NvGpuEngine3dReg register) - { - return (GalBlendFactor)ReadRegister(register); - } - - private void SetColorMask(GalPipelineState state) - { - bool colorMaskCommon = ReadRegisterBool(NvGpuEngine3dReg.ColorMaskCommon); - - state.ColorMaskCommon = colorMaskCommon; - - for (int index = 0; index < GalPipelineState.RenderTargetsCount; index++) - { - int colorMask = ReadRegister(NvGpuEngine3dReg.ColorMaskN + (colorMaskCommon ? 0 : index)); - - state.ColorMasks[index].Red = ((colorMask >> 0) & 0xf) != 0; - state.ColorMasks[index].Green = ((colorMask >> 4) & 0xf) != 0; - state.ColorMasks[index].Blue = ((colorMask >> 8) & 0xf) != 0; - state.ColorMasks[index].Alpha = ((colorMask >> 12) & 0xf) != 0; - } - } - - private void SetPrimitiveRestart(GalPipelineState state) - { - state.PrimitiveRestartEnabled = ReadRegisterBool(NvGpuEngine3dReg.PrimRestartEnable); - - if (state.PrimitiveRestartEnabled) - { - state.PrimitiveRestartIndex = (uint)ReadRegister(NvGpuEngine3dReg.PrimRestartIndex); - } - } - - private void SetRenderTargets() - { - // Commercial games do not seem to - // bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); - - uint control = (uint)(ReadRegister(NvGpuEngine3dReg.RtControl)); - - uint count = control & 0xf; - - if (count > 0) - { - int[] map = new int[count]; - - for (int index = 0; index < count; index++) - { - int shift = 4 + index * 3; - - map[index] = (int)((control >> shift) & 7); - } - - _gpu.Renderer.RenderTarget.SetMap(map); - } - else - { - _gpu.Renderer.RenderTarget.SetMap(null); - } - } - - private void UploadTextures(NvGpuVmm vmm, GalPipelineState state, long[] keys) - { - Profile.Begin(Profiles.GPU.Engine3d.UploadTextures); - - long baseShPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.ShaderAddress); - - int textureCbIndex = ReadRegister(NvGpuEngine3dReg.TextureCbIndex); - - List<(long, GalImage, GalTextureSampler)> unboundTextures = new List<(long, GalImage, GalTextureSampler)>(); - - for (int index = 0; index < keys.Length; index++) - { - foreach (TextureDescriptor desc in _gpu.Renderer.Shader.GetTextureUsage(keys[index])) - { - int textureHandle; - - if (desc.IsBindless) - { - long position = _constBuffers[index][desc.CbufSlot].Position; - - textureHandle = vmm.ReadInt32(position + desc.CbufOffset * 4); - } - else - { - long position = _constBuffers[index][textureCbIndex].Position; - - textureHandle = vmm.ReadInt32(position + desc.HandleIndex * 4); - } - - unboundTextures.Add(UploadTexture(vmm, textureHandle)); - } - } - - for (int index = 0; index < unboundTextures.Count; index++) - { - (long key, GalImage image, GalTextureSampler sampler) = unboundTextures[index]; - - if (key == 0) - { - continue; - } - - _gpu.Renderer.Texture.Bind(key, index, image); - _gpu.Renderer.Texture.SetSampler(image, sampler); - } - - Profile.End(Profiles.GPU.Engine3d.UploadTextures); - } - - private (long, GalImage, GalTextureSampler) UploadTexture(NvGpuVmm vmm, int textureHandle) - { - if (textureHandle == 0) - { - // FIXME: Some games like puyo puyo will use handles with the value 0. - // This is a bug, most likely caused by sync issues. - return (0, default(GalImage), default(GalTextureSampler)); - } - - Profile.Begin(Profiles.GPU.Engine3d.UploadTexture); - - bool linkedTsc = ReadRegisterBool(NvGpuEngine3dReg.LinkedTsc); - - int ticIndex = (textureHandle >> 0) & 0xfffff; - - int tscIndex = linkedTsc ? ticIndex : (textureHandle >> 20) & 0xfff; - - long ticPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexHeaderPoolOffset); - long tscPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.TexSamplerPoolOffset); - - ticPosition += ticIndex * 0x20; - tscPosition += tscIndex * 0x20; - - GalImage image = TextureFactory.MakeTexture(vmm, ticPosition); - - GalTextureSampler sampler = TextureFactory.MakeSampler(_gpu, vmm, tscPosition); - - long key = vmm.ReadInt64(ticPosition + 4) & 0xffffffffffff; - - if (image.Layout == GalMemoryLayout.BlockLinear) - { - key &= ~0x1ffL; - } - else if (image.Layout == GalMemoryLayout.Pitch) - { - key &= ~0x1fL; - } - - key = vmm.GetPhysicalAddress(key); - - if (key == -1) - { - Profile.End(Profiles.GPU.Engine3d.UploadTexture); - - // FIXME: Shouldn't ignore invalid addresses. - return (0, default(GalImage), default(GalTextureSampler)); - } - - _gpu.ResourceManager.SendTexture(vmm, key, image); - - Profile.End(Profiles.GPU.Engine3d.UploadTexture); - - return (key, image, sampler); - } - - private void UploadConstBuffers(NvGpuVmm vmm, GalPipelineState state, long[] keys) - { - Profile.Begin(Profiles.GPU.Engine3d.UploadConstBuffers); - - for (int stage = 0; stage < keys.Length; stage++) - { - foreach (CBufferDescriptor desc in _gpu.Renderer.Shader.GetConstBufferUsage(keys[stage])) - { - ConstBuffer cb = _constBuffers[stage][desc.Slot]; - - if (!cb.Enabled) - { - continue; - } - - long key = vmm.GetPhysicalAddress(cb.Position); - - if (_gpu.ResourceManager.MemoryRegionModified(vmm, key, cb.Size, NvGpuBufferType.ConstBuffer)) - { - if (vmm.TryGetHostAddress(cb.Position, cb.Size, out IntPtr cbPtr)) - { - _gpu.Renderer.Buffer.SetData(key, cb.Size, cbPtr); - } - else - { - _gpu.Renderer.Buffer.SetData(key, vmm.ReadBytes(cb.Position, cb.Size)); - } - } - - state.ConstBufferKeys[stage][desc.Slot] = key; - } - } - - Profile.End(Profiles.GPU.Engine3d.UploadConstBuffers); - } - - private void UploadVertexArrays(NvGpuVmm vmm, GalPipelineState state) - { - Profile.Begin(Profiles.GPU.Engine3d.UploadVertexArrays); - - long ibPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - - long iboKey = vmm.GetPhysicalAddress(ibPosition); - - int indexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int indexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); - int primCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); - - GalPrimitiveType primType = (GalPrimitiveType)(primCtrl & 0xffff); - - GalIndexFormat indexFormat = (GalIndexFormat)indexEntryFmt; - - int indexEntrySize = 1 << indexEntryFmt; - - if (indexEntrySize > 4) - { - throw new InvalidOperationException("Invalid index entry size \"" + indexEntrySize + "\"!"); - } - - if (indexCount != 0) - { - int ibSize = indexCount * indexEntrySize; - - bool iboCached = _gpu.Renderer.Rasterizer.IsIboCached(iboKey, (uint)ibSize); - - bool usesLegacyQuads = - primType == GalPrimitiveType.Quads || - primType == GalPrimitiveType.QuadStrip; - - if (!iboCached || _gpu.ResourceManager.MemoryRegionModified(vmm, iboKey, (uint)ibSize, NvGpuBufferType.Index)) - { - if (!usesLegacyQuads) - { - if (vmm.TryGetHostAddress(ibPosition, ibSize, out IntPtr ibPtr)) - { - _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, ibPtr); - } - else - { - _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, vmm.ReadBytes(ibPosition, ibSize)); - } - } - else - { - byte[] buffer = vmm.ReadBytes(ibPosition, ibSize); - - if (primType == GalPrimitiveType.Quads) - { - buffer = QuadHelper.ConvertQuadsToTris(buffer, indexEntrySize, indexCount); - } - else /* if (PrimType == GalPrimitiveType.QuadStrip) */ - { - buffer = QuadHelper.ConvertQuadStripToTris(buffer, indexEntrySize, indexCount); - } - - _gpu.Renderer.Rasterizer.CreateIbo(iboKey, ibSize, buffer); - } - } - - if (!usesLegacyQuads) - { - _gpu.Renderer.Rasterizer.SetIndexArray(ibSize, indexFormat); - } - else - { - if (primType == GalPrimitiveType.Quads) - { - _gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadsToTris(ibSize), indexFormat); - } - else /* if (PrimType == GalPrimitiveType.QuadStrip) */ - { - _gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertSizeQuadStripToTris(ibSize), indexFormat); - } - } - } - - List[] attribs = new List[32]; - - for (int attr = 0; attr < 16; attr++) - { - int packed = ReadRegister(NvGpuEngine3dReg.VertexAttribNFormat + attr); - - int arrayIndex = packed & 0x1f; - - if (attribs[arrayIndex] == null) - { - attribs[arrayIndex] = new List(); - } - - long vbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + arrayIndex * 4); - - if (vbPosition == 0) - { - continue; - } - - bool isConst = ((packed >> 6) & 1) != 0; - - int offset = (packed >> 7) & 0x3fff; - - GalVertexAttribSize size = (GalVertexAttribSize)((packed >> 21) & 0x3f); - GalVertexAttribType type = (GalVertexAttribType)((packed >> 27) & 0x7); - - bool isRgba = ((packed >> 31) & 1) != 0; - - // Check vertex array is enabled to avoid out of bounds exception when reading bytes - bool enable = (ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + arrayIndex * 4) & 0x1000) != 0; - - // Note: 16 is the maximum size of an attribute, - // having a component size of 32-bits with 4 elements (a vec4). - if (enable) - { - byte[] data = vmm.ReadBytes(vbPosition + offset, 16); - - attribs[arrayIndex].Add(new GalVertexAttrib(attr, isConst, offset, data, size, type, isRgba)); - } - } - - state.VertexBindings = new GalVertexBinding[32]; - - for (int index = 0; index < 32; index++) - { - if (attribs[index] == null) - { - continue; - } - - int control = ReadRegister(NvGpuEngine3dReg.VertexArrayNControl + index * 4); - - bool enable = (control & 0x1000) != 0; - - if (!enable) - { - continue; - } - - long vbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + index * 4); - long vbEndPos = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNEndAddr + index * 2); - - int vertexDivisor = ReadRegister(NvGpuEngine3dReg.VertexArrayNDivisor + index * 4); - - bool instanced = ReadRegisterBool(NvGpuEngine3dReg.VertexArrayNInstance + index); - - int stride = control & 0xfff; - - if (instanced && vertexDivisor != 0) - { - vbPosition += stride * (_currentInstance / vertexDivisor); - } - - if (vbPosition > vbEndPos) - { - // Instance is invalid, ignore the draw call - continue; - } - - long vboKey = vmm.GetPhysicalAddress(vbPosition); - - long vbSize = (vbEndPos - vbPosition) + 1; - int modifiedVbSize = (int)vbSize; - - - // If quads convert size to triangle length - if (stride == 0) - { - if (primType == GalPrimitiveType.Quads) - { - modifiedVbSize = QuadHelper.ConvertSizeQuadsToTris(modifiedVbSize); - } - else if (primType == GalPrimitiveType.QuadStrip) - { - modifiedVbSize = QuadHelper.ConvertSizeQuadStripToTris(modifiedVbSize); - } - } - - bool vboCached = _gpu.Renderer.Rasterizer.IsVboCached(vboKey, modifiedVbSize); - - if (!vboCached || _gpu.ResourceManager.MemoryRegionModified(vmm, vboKey, vbSize, NvGpuBufferType.Vertex)) - { - if ((primType == GalPrimitiveType.Quads | primType == GalPrimitiveType.QuadStrip) && stride != 0) - { - // Convert quad buffer to triangles - byte[] data = vmm.ReadBytes(vbPosition, vbSize); - - if (primType == GalPrimitiveType.Quads) - { - data = QuadHelper.ConvertQuadsToTris(data, stride, (int)(vbSize / stride)); - } - else - { - data = QuadHelper.ConvertQuadStripToTris(data, stride, (int)(vbSize / stride)); - } - _gpu.Renderer.Rasterizer.CreateVbo(vboKey, data); - } - else if (vmm.TryGetHostAddress(vbPosition, vbSize, out IntPtr vbPtr)) - { - _gpu.Renderer.Rasterizer.CreateVbo(vboKey, (int)vbSize, vbPtr); - } - else - { - _gpu.Renderer.Rasterizer.CreateVbo(vboKey, vmm.ReadBytes(vbPosition, vbSize)); - } - } - - state.VertexBindings[index].Enabled = true; - state.VertexBindings[index].Stride = stride; - state.VertexBindings[index].VboKey = vboKey; - state.VertexBindings[index].Instanced = instanced; - state.VertexBindings[index].Divisor = vertexDivisor; - state.VertexBindings[index].Attribs = attribs[index].ToArray(); - } - - Profile.End(Profiles.GPU.Engine3d.UploadVertexArrays); - } - - private void DispatchRender(NvGpuVmm vmm, GalPipelineState state) - { - int indexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); - int primCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); - - GalPrimitiveType primType = (GalPrimitiveType)(primCtrl & 0xffff); - - bool instanceNext = ((primCtrl >> 26) & 1) != 0; - bool instanceCont = ((primCtrl >> 27) & 1) != 0; - - if (instanceNext && instanceCont) - { - throw new InvalidOperationException("GPU tried to increase and reset instance count at the same time"); - } - - if (instanceNext) - { - _currentInstance++; - } - else if (!instanceCont) - { - _currentInstance = 0; - } - - state.Instance = _currentInstance; - - _gpu.Renderer.Pipeline.Bind(state); - - _gpu.Renderer.RenderTarget.Bind(); - - if (indexCount != 0) - { - int indexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); - int indexFirst = ReadRegister(NvGpuEngine3dReg.IndexBatchFirst); - int vertexBase = ReadRegister(NvGpuEngine3dReg.VertexArrayElemBase); - - long indexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - - long iboKey = vmm.GetPhysicalAddress(indexPosition); - - // Quad primitive types were deprecated on OpenGL 3.x, - // they are converted to a triangles index buffer on IB creation, - // so we should use the triangles type here too. - if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip) - { - // Note: We assume that index first points to the first - // vertex of a quad, if it points to the middle of a - // quad (First % 4 != 0 for Quads) then it will not work properly. - if (primType == GalPrimitiveType.Quads) - { - indexFirst = QuadHelper.ConvertSizeQuadsToTris(indexFirst); - } - else // QuadStrip - { - indexFirst = QuadHelper.ConvertSizeQuadStripToTris(indexFirst); - } - - primType = GalPrimitiveType.Triangles; - } - - _gpu.Renderer.Rasterizer.DrawElements(iboKey, indexFirst, vertexBase, primType); - } - else - { - int vertexFirst = ReadRegister(NvGpuEngine3dReg.VertexArrayFirst); - int vertexCount = ReadRegister(NvGpuEngine3dReg.VertexArrayCount); - - // Quad primitive types were deprecated on OpenGL 3.x, - // they are converted to a triangles index buffer on IB creation, - // so we should use the triangles type here too. - if (primType == GalPrimitiveType.Quads || primType == GalPrimitiveType.QuadStrip) - { - // Note: We assume that index first points to the first - // vertex of a quad, if it points to the middle of a - // quad (First % 4 != 0 for Quads) then it will not work properly. - if (primType == GalPrimitiveType.Quads) - { - vertexFirst = QuadHelper.ConvertSizeQuadsToTris(vertexFirst); - } - else // QuadStrip - { - vertexFirst = QuadHelper.ConvertSizeQuadStripToTris(vertexFirst); - } - - primType = GalPrimitiveType.Triangles; - vertexCount = QuadHelper.ConvertSizeQuadsToTris(vertexCount); - } - - _gpu.Renderer.Rasterizer.DrawArrays(vertexFirst, vertexCount, primType); - } - - // Reset pipeline for host OpenGL calls - _gpu.Renderer.Pipeline.Unbind(state); - - // Is the GPU really clearing those registers after draw? - WriteRegister(NvGpuEngine3dReg.IndexBatchFirst, 0); - WriteRegister(NvGpuEngine3dReg.IndexBatchCount, 0); - } - - private enum QueryMode - { - WriteSeq, - Sync, - WriteCounterAndTimestamp - } - - private void QueryControl(NvGpuVmm vmm, GpuMethodCall methCall) - { - WriteRegister(methCall); - - long position = MakeInt64From2xInt32(NvGpuEngine3dReg.QueryAddress); - - int seq = Registers[(int)NvGpuEngine3dReg.QuerySequence]; - int ctrl = Registers[(int)NvGpuEngine3dReg.QueryControl]; - - QueryMode mode = (QueryMode)(ctrl & 3); - - switch (mode) - { - case QueryMode.WriteSeq: vmm.WriteInt32(position, seq); break; - - case QueryMode.WriteCounterAndTimestamp: - { - // TODO: Implement counters. - long counter = 1; - - long timestamp = PerformanceCounter.ElapsedMilliseconds; - - vmm.WriteInt64(position + 0, counter); - vmm.WriteInt64(position + 8, timestamp); - - break; - } - } - } - - private void CbData(NvGpuVmm vmm, GpuMethodCall methCall) - { - long position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); - - int offset = ReadRegister(NvGpuEngine3dReg.ConstBufferOffset); - - vmm.WriteInt32(position + offset, methCall.Argument); - - WriteRegister(NvGpuEngine3dReg.ConstBufferOffset, offset + 4); - - _gpu.ResourceManager.ClearPbCache(NvGpuBufferType.ConstBuffer); - } - - private void CbBind(NvGpuVmm vmm, GpuMethodCall methCall) - { - int stage = (methCall.Method - 0x904) >> 3; - - int index = methCall.Argument; - - bool enabled = (index & 1) != 0; - - index = (index >> 4) & 0x1f; - - long position = MakeInt64From2xInt32(NvGpuEngine3dReg.ConstBufferAddress); - - long cbKey = vmm.GetPhysicalAddress(position); - - int size = ReadRegister(NvGpuEngine3dReg.ConstBufferSize); - - if (!_gpu.Renderer.Buffer.IsCached(cbKey, size)) - { - _gpu.Renderer.Buffer.Create(cbKey, size); - } - - ConstBuffer cb = _constBuffers[stage][index]; - - if (cb.Position != position || cb.Enabled != enabled || cb.Size != size) - { - _constBuffers[stage][index].Position = position; - _constBuffers[stage][index].Enabled = enabled; - _constBuffers[stage][index].Size = size; - } - } - - private float GetFlipSign(NvGpuEngine3dReg reg) - { - return MathF.Sign(ReadRegisterFloat(reg)); - } - - private long MakeInt64From2xInt32(NvGpuEngine3dReg reg) - { - return - (long)Registers[(int)reg + 0] << 32 | - (uint)Registers[(int)reg + 1]; - } - - private void WriteRegister(GpuMethodCall methCall) - { - Registers[methCall.Method] = methCall.Argument; - } - - private int ReadRegister(NvGpuEngine3dReg reg) - { - return Registers[(int)reg]; - } - - private float ReadRegisterFloat(NvGpuEngine3dReg reg) - { - return BitConverter.Int32BitsToSingle(ReadRegister(reg)); - } - - private bool ReadRegisterBool(NvGpuEngine3dReg reg) - { - return (ReadRegister(reg) & 1) != 0; - } - - private void WriteRegister(NvGpuEngine3dReg reg, int value) - { - Registers[(int)reg] = value; - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs deleted file mode 100644 index c6596a309..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngine3dReg.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace Ryujinx.Graphics.Graphics3d -{ - enum NvGpuEngine3dReg - { - FrameBufferNAddress = 0x200, - FrameBufferNWidth = 0x202, - FrameBufferNHeight = 0x203, - FrameBufferNFormat = 0x204, - FrameBufferNBlockDim = 0x205, - FrameBufferNArrayMode = 0x206, - FrameBufferNLayerStride = 0x207, - FrameBufferNBaseLayer = 0x208, - ViewportNScaleX = 0x280, - ViewportNScaleY = 0x281, - ViewportNScaleZ = 0x282, - ViewportNTranslateX = 0x283, - ViewportNTranslateY = 0x284, - ViewportNTranslateZ = 0x285, - ViewportNHoriz = 0x300, - ViewportNVert = 0x301, - DepthRangeNNear = 0x302, - DepthRangeNFar = 0x303, - VertexArrayFirst = 0x35d, - VertexArrayCount = 0x35e, - ClearNColor = 0x360, - ClearDepth = 0x364, - ClearStencil = 0x368, - ScissorEnable = 0x380, - ScissorHorizontal = 0x381, - ScissorVertical = 0x382, - StencilBackFuncRef = 0x3d5, - StencilBackMask = 0x3d6, - StencilBackFuncMask = 0x3d7, - ColorMaskCommon = 0x3e4, - RtSeparateFragData = 0x3eb, - ZetaAddress = 0x3f8, - ZetaFormat = 0x3fa, - ZetaBlockDimensions = 0x3fb, - ZetaLayerStride = 0x3fc, - VertexAttribNFormat = 0x458, - RtControl = 0x487, - ZetaHoriz = 0x48a, - ZetaVert = 0x48b, - ZetaArrayMode = 0x48c, - LinkedTsc = 0x48d, - DepthTestEnable = 0x4b3, - BlendIndependent = 0x4b9, - DepthWriteEnable = 0x4ba, - DepthTestFunction = 0x4c3, - BlendSeparateAlpha = 0x4cf, - BlendEquationRgb = 0x4d0, - BlendFuncSrcRgb = 0x4d1, - BlendFuncDstRgb = 0x4d2, - BlendEquationAlpha = 0x4d3, - BlendFuncSrcAlpha = 0x4d4, - BlendFuncDstAlpha = 0x4d6, - BlendEnable = 0x4d7, - IBlendNEnable = 0x4d8, - StencilEnable = 0x4e0, - StencilFrontOpFail = 0x4e1, - StencilFrontOpZFail = 0x4e2, - StencilFrontOpZPass = 0x4e3, - StencilFrontFuncFunc = 0x4e4, - StencilFrontFuncRef = 0x4e5, - StencilFrontFuncMask = 0x4e6, - StencilFrontMask = 0x4e7, - ScreenYControl = 0x4eb, - VertexArrayElemBase = 0x50d, - VertexArrayInstBase = 0x50e, - ZetaEnable = 0x54e, - TexHeaderPoolOffset = 0x55d, - TexSamplerPoolOffset = 0x557, - StencilTwoSideEnable = 0x565, - StencilBackOpFail = 0x566, - StencilBackOpZFail = 0x567, - StencilBackOpZPass = 0x568, - StencilBackFuncFunc = 0x569, - FrameBufferSrgb = 0x56e, - ShaderAddress = 0x582, - VertexBeginGl = 0x586, - PrimRestartEnable = 0x591, - PrimRestartIndex = 0x592, - IndexArrayAddress = 0x5f2, - IndexArrayEndAddr = 0x5f4, - IndexArrayFormat = 0x5f6, - IndexBatchFirst = 0x5f7, - IndexBatchCount = 0x5f8, - VertexArrayNInstance = 0x620, - CullFaceEnable = 0x646, - FrontFace = 0x647, - CullFace = 0x648, - ColorMaskN = 0x680, - QueryAddress = 0x6c0, - QuerySequence = 0x6c2, - QueryControl = 0x6c3, - VertexArrayNControl = 0x700, - VertexArrayNAddress = 0x701, - VertexArrayNDivisor = 0x703, - IBlendNSeparateAlpha = 0x780, - IBlendNEquationRgb = 0x781, - IBlendNFuncSrcRgb = 0x782, - IBlendNFuncDstRgb = 0x783, - IBlendNEquationAlpha = 0x784, - IBlendNFuncSrcAlpha = 0x785, - IBlendNFuncDstAlpha = 0x786, - VertexArrayNEndAddr = 0x7c0, - ShaderNControl = 0x800, - ShaderNOffset = 0x801, - ShaderNMaxGprs = 0x803, - ShaderNType = 0x804, - ConstBufferSize = 0x8e0, - ConstBufferAddress = 0x8e1, - ConstBufferOffset = 0x8e3, - TextureCbIndex = 0x982 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs deleted file mode 100644 index fca8ae228..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mf.cs +++ /dev/null @@ -1,214 +0,0 @@ -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.Texture; -using System.Collections.Generic; -using Ryujinx.Profiler; - -namespace Ryujinx.Graphics.Graphics3d -{ - class NvGpuEngineM2mf : INvGpuEngine - { - public int[] Registers { get; private set; } - - private NvGpu _gpu; - - private Dictionary _methods; - - public NvGpuEngineM2mf(NvGpu gpu) - { - _gpu = gpu; - - Registers = new int[0x1d6]; - - _methods = new Dictionary(); - - void AddMethod(int meth, int count, int stride, NvGpuMethod method) - { - while (count-- > 0) - { - _methods.Add(meth, method); - - meth += stride; - } - } - - AddMethod(0xc0, 1, 1, Execute); - } - - public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - if (_methods.TryGetValue(methCall.Method, out NvGpuMethod method)) - { - ProfileConfig profile = Profiles.GPU.EngineM2mf.CallMethod; - - profile.SessionItem = method.Method.Name; - - Profile.Begin(profile); - method(vmm, methCall); - Profile.End(profile); - } - else - { - WriteRegister(methCall); - } - } - - private void Execute(NvGpuVmm vmm, GpuMethodCall methCall) - { - Profile.Begin(Profiles.GPU.EngineM2mf.Execute); - - // TODO: Some registers and copy modes are still not implemented. - int control = methCall.Argument; - - bool srcLinear = ((control >> 7) & 1) != 0; - bool dstLinear = ((control >> 8) & 1) != 0; - bool copy2D = ((control >> 9) & 1) != 0; - - long srcAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.SrcAddress); - long dstAddress = MakeInt64From2xInt32(NvGpuEngineM2mfReg.DstAddress); - - int srcPitch = ReadRegister(NvGpuEngineM2mfReg.SrcPitch); - int dstPitch = ReadRegister(NvGpuEngineM2mfReg.DstPitch); - - int xCount = ReadRegister(NvGpuEngineM2mfReg.XCount); - int yCount = ReadRegister(NvGpuEngineM2mfReg.YCount); - - int swizzle = ReadRegister(NvGpuEngineM2mfReg.Swizzle); - - int dstBlkDim = ReadRegister(NvGpuEngineM2mfReg.DstBlkDim); - int dstSizeX = ReadRegister(NvGpuEngineM2mfReg.DstSizeX); - int dstSizeY = ReadRegister(NvGpuEngineM2mfReg.DstSizeY); - int dstSizeZ = ReadRegister(NvGpuEngineM2mfReg.DstSizeZ); - int dstPosXY = ReadRegister(NvGpuEngineM2mfReg.DstPosXY); - int dstPosZ = ReadRegister(NvGpuEngineM2mfReg.DstPosZ); - - int srcBlkDim = ReadRegister(NvGpuEngineM2mfReg.SrcBlkDim); - int srcSizeX = ReadRegister(NvGpuEngineM2mfReg.SrcSizeX); - int srcSizeY = ReadRegister(NvGpuEngineM2mfReg.SrcSizeY); - int srcSizeZ = ReadRegister(NvGpuEngineM2mfReg.SrcSizeZ); - int srcPosXY = ReadRegister(NvGpuEngineM2mfReg.SrcPosXY); - int srcPosZ = ReadRegister(NvGpuEngineM2mfReg.SrcPosZ); - - int srcCpp = ((swizzle >> 20) & 7) + 1; - int dstCpp = ((swizzle >> 24) & 7) + 1; - - int dstPosX = (dstPosXY >> 0) & 0xffff; - int dstPosY = (dstPosXY >> 16) & 0xffff; - - int srcPosX = (srcPosXY >> 0) & 0xffff; - int srcPosY = (srcPosXY >> 16) & 0xffff; - - int srcBlockHeight = 1 << ((srcBlkDim >> 4) & 0xf); - int dstBlockHeight = 1 << ((dstBlkDim >> 4) & 0xf); - - long srcPa = vmm.GetPhysicalAddress(srcAddress); - long dstPa = vmm.GetPhysicalAddress(dstAddress); - - if (copy2D) - { - if (srcLinear) - { - srcPosX = srcPosY = srcPosZ = 0; - } - - if (dstLinear) - { - dstPosX = dstPosY = dstPosZ = 0; - } - - if (srcLinear && dstLinear) - { - for (int y = 0; y < yCount; y++) - { - int srcOffset = (srcPosY + y) * srcPitch + srcPosX * srcCpp; - int dstOffset = (dstPosY + y) * dstPitch + dstPosX * dstCpp; - - long src = srcPa + (uint)srcOffset; - long dst = dstPa + (uint)dstOffset; - - vmm.Memory.CopyBytes(src, dst, xCount * srcCpp); - } - } - else - { - ISwizzle srcSwizzle; - - if (srcLinear) - { - srcSwizzle = new LinearSwizzle(srcPitch, srcCpp, srcSizeX, srcSizeY); - } - else - { - srcSwizzle = new BlockLinearSwizzle( - srcSizeX, - srcSizeY, 1, - srcBlockHeight, 1, - srcCpp); - } - - ISwizzle dstSwizzle; - - if (dstLinear) - { - dstSwizzle = new LinearSwizzle(dstPitch, dstCpp, srcSizeX, srcSizeY); - } - else - { - dstSwizzle = new BlockLinearSwizzle( - dstSizeX, - dstSizeY, 1, - dstBlockHeight, 1, - dstCpp); - } - - // Calculate the bits per pixel - int bpp = srcPitch / xCount; - - // Copying all the bits at the same time corrupts the texture, unknown why but probably because the texture isn't linear - // To avoid this we will simply loop more times to cover all the bits, - // this allows up to recalculate the memory locations for each iteration around the loop - xCount *= bpp / srcCpp; - - for (int y = 0; y < yCount; y++) - for (int x = 0; x < xCount; x++) - { - int srcOffset = srcSwizzle.GetSwizzleOffset(srcPosX + x, srcPosY + y, 0); - int dstOffset = dstSwizzle.GetSwizzleOffset(dstPosX + x, dstPosY + y, 0); - - long src = srcPa + (uint)srcOffset; - long dst = dstPa + (uint)dstOffset; - - vmm.Memory.CopyBytes(src, dst, srcCpp); - } - } - } - else - { - vmm.Memory.CopyBytes(srcPa, dstPa, xCount); - } - - Profile.End(Profiles.GPU.EngineM2mf.Execute); - } - - private long MakeInt64From2xInt32(NvGpuEngineM2mfReg reg) - { - return - (long)Registers[(int)reg + 0] << 32 | - (uint)Registers[(int)reg + 1]; - } - - private void WriteRegister(GpuMethodCall methCall) - { - Registers[methCall.Method] = methCall.Argument; - } - - private int ReadRegister(NvGpuEngineM2mfReg reg) - { - return Registers[(int)reg]; - } - - private void WriteRegister(NvGpuEngineM2mfReg reg, int value) - { - Registers[(int)reg] = value; - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mfReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mfReg.cs deleted file mode 100644 index 4bef8d9ed..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineM2mfReg.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace Ryujinx.Graphics.Graphics3d -{ - enum NvGpuEngineM2mfReg - { - SrcAddress = 0x100, - DstAddress = 0x102, - SrcPitch = 0x104, - DstPitch = 0x105, - XCount = 0x106, - YCount = 0x107, - Swizzle = 0x1c2, - DstBlkDim = 0x1c3, - DstSizeX = 0x1c4, - DstSizeY = 0x1c5, - DstSizeZ = 0x1c6, - DstPosZ = 0x1c7, - DstPosXY = 0x1c8, - SrcBlkDim = 0x1ca, - SrcSizeX = 0x1cb, - SrcSizeY = 0x1cc, - SrcSizeZ = 0x1cd, - SrcPosZ = 0x1ce, - SrcPosXY = 0x1cf - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs deleted file mode 100644 index 2b2c7b5f1..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mf.cs +++ /dev/null @@ -1,179 +0,0 @@ -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.Texture; -using System.Collections.Generic; -using Ryujinx.Profiler; - -namespace Ryujinx.Graphics.Graphics3d -{ - class NvGpuEngineP2mf : INvGpuEngine - { - public int[] Registers { get; private set; } - - private NvGpu _gpu; - - private Dictionary _methods; - - private int _copyStartX; - private int _copyStartY; - - private int _copyWidth; - private int _copyHeight; - private int _copyGobBlockHeight; - - private long _copyAddress; - - private int _copyOffset; - private int _copySize; - - private bool _copyLinear; - - private byte[] _buffer; - - public NvGpuEngineP2mf(NvGpu gpu) - { - _gpu = gpu; - - Registers = new int[0x80]; - - _methods = new Dictionary(); - - void AddMethod(int meth, int count, int stride, NvGpuMethod method) - { - while (count-- > 0) - { - _methods.Add(meth, method); - - meth += stride; - } - } - - AddMethod(0x6c, 1, 1, Execute); - AddMethod(0x6d, 1, 1, PushData); - } - - public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - if (_methods.TryGetValue(methCall.Method, out NvGpuMethod method)) - { - ProfileConfig profile = Profiles.GPU.EngineP2mf.PushData; - - profile.SessionItem = method.Method.Name; - - Profile.Begin(profile); - method(vmm, methCall); - Profile.End(profile); - } - else - { - WriteRegister(methCall); - } - } - - private void Execute(NvGpuVmm vmm, GpuMethodCall methCall) - { - Profile.Begin(Profiles.GPU.EngineP2mf.Execute); - - // TODO: Some registers and copy modes are still not implemented. - int control = methCall.Argument; - - long dstAddress = MakeInt64From2xInt32(NvGpuEngineP2mfReg.DstAddress); - - int dstPitch = ReadRegister(NvGpuEngineP2mfReg.DstPitch); - int dstBlkDim = ReadRegister(NvGpuEngineP2mfReg.DstBlockDim); - - int dstX = ReadRegister(NvGpuEngineP2mfReg.DstX); - int dstY = ReadRegister(NvGpuEngineP2mfReg.DstY); - - int dstWidth = ReadRegister(NvGpuEngineP2mfReg.DstWidth); - int dstHeight = ReadRegister(NvGpuEngineP2mfReg.DstHeight); - - int lineLengthIn = ReadRegister(NvGpuEngineP2mfReg.LineLengthIn); - int lineCount = ReadRegister(NvGpuEngineP2mfReg.LineCount); - - _copyLinear = (control & 1) != 0; - - _copyGobBlockHeight = 1 << ((dstBlkDim >> 4) & 0xf); - - _copyStartX = dstX; - _copyStartY = dstY; - - _copyWidth = dstWidth; - _copyHeight = dstHeight; - - _copyAddress = dstAddress; - - _copyOffset = 0; - _copySize = lineLengthIn * lineCount; - - _buffer = new byte[_copySize]; - - Profile.End(Profiles.GPU.EngineP2mf.Execute); - } - - private void PushData(NvGpuVmm vmm, GpuMethodCall methCall) - { - if (_buffer == null) - { - return; - } - - Profile.Begin(Profiles.GPU.EngineP2mf.PushData); - - for (int shift = 0; shift < 32 && _copyOffset < _copySize; shift += 8, _copyOffset++) - { - _buffer[_copyOffset] = (byte)(methCall.Argument >> shift); - } - - if (methCall.IsLastCall) - { - if (_copyLinear) - { - vmm.WriteBytes(_copyAddress, _buffer); - } - else - { - BlockLinearSwizzle swizzle = new BlockLinearSwizzle( - _copyWidth, - _copyHeight, 1, - _copyGobBlockHeight, 1, 1); - - int srcOffset = 0; - - for (int y = _copyStartY; y < _copyHeight && srcOffset < _copySize; y++) - for (int x = _copyStartX; x < _copyWidth && srcOffset < _copySize; x++) - { - int dstOffset = swizzle.GetSwizzleOffset(x, y, 0); - - vmm.WriteByte(_copyAddress + dstOffset, _buffer[srcOffset++]); - } - } - - _buffer = null; - } - - Profile.End(Profiles.GPU.EngineP2mf.PushData); - } - - private long MakeInt64From2xInt32(NvGpuEngineP2mfReg reg) - { - return - (long)Registers[(int)reg + 0] << 32 | - (uint)Registers[(int)reg + 1]; - } - - private void WriteRegister(GpuMethodCall methCall) - { - Registers[methCall.Method] = methCall.Argument; - } - - private int ReadRegister(NvGpuEngineP2mfReg reg) - { - return Registers[(int)reg]; - } - - private void WriteRegister(NvGpuEngineP2mfReg reg, int value) - { - Registers[(int)reg] = value; - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mfReg.cs b/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mfReg.cs deleted file mode 100644 index ab3a304dd..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuEngineP2mfReg.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Ryujinx.Graphics.Graphics3d -{ - enum NvGpuEngineP2mfReg - { - LineLengthIn = 0x60, - LineCount = 0x61, - DstAddress = 0x62, - DstPitch = 0x64, - DstBlockDim = 0x65, - DstWidth = 0x66, - DstHeight = 0x67, - DstDepth = 0x68, - DstZ = 0x69, - DstX = 0x6a, - DstY = 0x6b - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs b/Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs deleted file mode 100644 index 23bfd0b3c..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuFifo.cs +++ /dev/null @@ -1,176 +0,0 @@ -using Ryujinx.Graphics.Memory; - -namespace Ryujinx.Graphics.Graphics3d -{ - class NvGpuFifo - { - private const int MacrosCount = 0x80; - private const int MacroIndexMask = MacrosCount - 1; - - // Note: The size of the macro memory is unknown, we just make - // a guess here and use 256kb as the size. Increase if needed. - private const int MmeWords = 256 * 256; - - private NvGpu _gpu; - - private NvGpuEngine[] _subChannels; - - private struct CachedMacro - { - public int Position { get; private set; } - - private bool _executionPending; - private int _argument; - - private MacroInterpreter _interpreter; - - public CachedMacro(NvGpuFifo pFifo, INvGpuEngine engine, int position) - { - Position = position; - - _executionPending = false; - _argument = 0; - - _interpreter = new MacroInterpreter(pFifo, engine); - } - - public void StartExecution(int argument) - { - _argument = argument; - - _executionPending = true; - } - - public void Execute(NvGpuVmm vmm, int[] mme) - { - if (_executionPending) - { - _executionPending = false; - - _interpreter?.Execute(vmm, mme, Position, _argument); - } - } - - public void PushArgument(int argument) - { - _interpreter?.Fifo.Enqueue(argument); - } - } - - private int _currMacroPosition; - private int _currMacroBindIndex; - - private CachedMacro[] _macros; - - private int[] _mme; - - public NvGpuFifo(NvGpu gpu) - { - _gpu = gpu; - - _subChannels = new NvGpuEngine[8]; - - _macros = new CachedMacro[MacrosCount]; - - _mme = new int[MmeWords]; - } - - public void CallMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - if ((NvGpuFifoMeth)methCall.Method == NvGpuFifoMeth.BindChannel) - { - NvGpuEngine engine = (NvGpuEngine)methCall.Argument; - - _subChannels[methCall.SubChannel] = engine; - } - else - { - switch (_subChannels[methCall.SubChannel]) - { - case NvGpuEngine._2d: Call2dMethod (vmm, methCall); break; - case NvGpuEngine._3d: Call3dMethod (vmm, methCall); break; - case NvGpuEngine.P2mf: CallP2mfMethod(vmm, methCall); break; - case NvGpuEngine.M2mf: CallM2mfMethod(vmm, methCall); break; - } - } - } - - private void Call2dMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - _gpu.Engine2d.CallMethod(vmm, methCall); - } - - private void Call3dMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - if (methCall.Method < 0x80) - { - switch ((NvGpuFifoMeth)methCall.Method) - { - case NvGpuFifoMeth.SetMacroUploadAddress: - { - _currMacroPosition = methCall.Argument; - - break; - } - - case NvGpuFifoMeth.SendMacroCodeData: - { - _mme[_currMacroPosition++] = methCall.Argument; - - break; - } - - case NvGpuFifoMeth.SetMacroBindingIndex: - { - _currMacroBindIndex = methCall.Argument; - - break; - } - - case NvGpuFifoMeth.BindMacro: - { - int position = methCall.Argument; - - _macros[_currMacroBindIndex++] = new CachedMacro(this, _gpu.Engine3d, position); - - break; - } - - default: CallP2mfMethod(vmm, methCall); break; - } - } - else if (methCall.Method < 0xe00) - { - _gpu.Engine3d.CallMethod(vmm, methCall); - } - else - { - int macroIndex = (methCall.Method >> 1) & MacroIndexMask; - - if ((methCall.Method & 1) != 0) - { - _macros[macroIndex].PushArgument(methCall.Argument); - } - else - { - _macros[macroIndex].StartExecution(methCall.Argument); - } - - if (methCall.IsLastCall) - { - _macros[macroIndex].Execute(vmm, _mme); - } - } - } - - private void CallP2mfMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - _gpu.EngineP2mf.CallMethod(vmm, methCall); - } - - private void CallM2mfMethod(NvGpuVmm vmm, GpuMethodCall methCall) - { - _gpu.EngineM2mf.CallMethod(vmm, methCall); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs b/Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs deleted file mode 100644 index 23185c81f..000000000 --- a/Ryujinx.Graphics/Graphics3d/NvGpuMethod.cs +++ /dev/null @@ -1,6 +0,0 @@ -using Ryujinx.Graphics.Memory; - -namespace Ryujinx.Graphics.Graphics3d -{ - delegate void NvGpuMethod(NvGpuVmm vmm, GpuMethodCall methCall); -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs deleted file mode 100644 index 682f7d671..000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/BlockLinearSwizzle.cs +++ /dev/null @@ -1,186 +0,0 @@ -using Ryujinx.Common; -using System; - -namespace Ryujinx.Graphics.Texture -{ - class BlockLinearSwizzle : ISwizzle - { - private const int GobWidth = 64; - private const int GobHeight = 8; - - private const int GobSize = GobWidth * GobHeight; - - private int _texWidth; - private int _texHeight; - private int _texDepth; - private int _texGobBlockHeight; - private int _texGobBlockDepth; - private int _texBpp; - - private int _bhMask; - private int _bdMask; - - private int _bhShift; - private int _bdShift; - private int _bppShift; - - private int _xShift; - - private int _robSize; - private int _sliceSize; - - private int _baseOffset; - - public BlockLinearSwizzle( - int width, - int height, - int depth, - int gobBlockHeight, - int gobBlockDepth, - int bpp) - { - _texWidth = width; - _texHeight = height; - _texDepth = depth; - _texGobBlockHeight = gobBlockHeight; - _texGobBlockDepth = gobBlockDepth; - _texBpp = bpp; - - _bppShift = BitUtils.CountTrailingZeros32(bpp); - - SetMipLevel(0); - } - - public void SetMipLevel(int level) - { - _baseOffset = GetMipOffset(level); - - int width = Math.Max(1, _texWidth >> level); - int height = Math.Max(1, _texHeight >> level); - int depth = Math.Max(1, _texDepth >> level); - - GobBlockSizes gbSizes = AdjustGobBlockSizes(height, depth); - - _bhMask = gbSizes.Height - 1; - _bdMask = gbSizes.Depth - 1; - - _bhShift = BitUtils.CountTrailingZeros32(gbSizes.Height); - _bdShift = BitUtils.CountTrailingZeros32(gbSizes.Depth); - - _xShift = BitUtils.CountTrailingZeros32(GobSize * gbSizes.Height * gbSizes.Depth); - - RobAndSliceSizes gsSizes = GetRobAndSliceSizes(width, height, gbSizes); - - _robSize = gsSizes.RobSize; - _sliceSize = gsSizes.SliceSize; - } - - public int GetImageSize(int mipsCount) - { - int size = GetMipOffset(mipsCount); - - size = (size + 0x1fff) & ~0x1fff; - - return size; - } - - public int GetMipOffset(int level) - { - int totalSize = 0; - - for (int index = 0; index < level; index++) - { - int width = Math.Max(1, _texWidth >> index); - int height = Math.Max(1, _texHeight >> index); - int depth = Math.Max(1, _texDepth >> index); - - GobBlockSizes gbSizes = AdjustGobBlockSizes(height, depth); - - RobAndSliceSizes rsSizes = GetRobAndSliceSizes(width, height, gbSizes); - - totalSize += BitUtils.DivRoundUp(depth, gbSizes.Depth) * rsSizes.SliceSize; - } - - return totalSize; - } - - private struct GobBlockSizes - { - public int Height; - public int Depth; - - public GobBlockSizes(int gobBlockHeight, int gobBlockDepth) - { - Height = gobBlockHeight; - Depth = gobBlockDepth; - } - } - - private GobBlockSizes AdjustGobBlockSizes(int height, int depth) - { - int gobBlockHeight = _texGobBlockHeight; - int gobBlockDepth = _texGobBlockDepth; - - int pow2Height = BitUtils.Pow2RoundUp(height); - int pow2Depth = BitUtils.Pow2RoundUp(depth); - - while (gobBlockHeight * GobHeight > pow2Height && gobBlockHeight > 1) - { - gobBlockHeight >>= 1; - } - - while (gobBlockDepth > pow2Depth && gobBlockDepth > 1) - { - gobBlockDepth >>= 1; - } - - return new GobBlockSizes(gobBlockHeight, gobBlockDepth); - } - - private struct RobAndSliceSizes - { - public int RobSize; - public int SliceSize; - - public RobAndSliceSizes(int robSize, int sliceSize) - { - RobSize = robSize; - SliceSize = sliceSize; - } - } - - private RobAndSliceSizes GetRobAndSliceSizes(int width, int height, GobBlockSizes gbSizes) - { - int widthInGobs = BitUtils.DivRoundUp(width * _texBpp, GobWidth); - - int robSize = GobSize * gbSizes.Height * gbSizes.Depth * widthInGobs; - - int sliceSize = BitUtils.DivRoundUp(height, gbSizes.Height * GobHeight) * robSize; - - return new RobAndSliceSizes(robSize, sliceSize); - } - - public int GetSwizzleOffset(int x, int y, int z) - { - x <<= _bppShift; - - int yh = y / GobHeight; - - int position = (z >> _bdShift) * _sliceSize + (yh >> _bhShift) * _robSize; - - position += (x / GobWidth) << _xShift; - - position += (yh & _bhMask) * GobSize; - - position += ((z & _bdMask) * GobSize) << _bhShift; - - position += ((x & 0x3f) >> 5) << 8; - position += ((y & 0x07) >> 1) << 6; - position += ((x & 0x1f) >> 4) << 5; - position += ((y & 0x01) >> 0) << 4; - position += ((x & 0x0f) >> 0) << 0; - - return _baseOffset + position; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs deleted file mode 100644 index fae3eada8..000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/ISwizzle.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace Ryujinx.Graphics.Texture -{ - interface ISwizzle - { - int GetSwizzleOffset(int x, int y, int z); - - void SetMipLevel(int level); - - int GetMipOffset(int level); - - int GetImageSize(int mipsCount); - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs b/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs deleted file mode 100644 index 10c36fe1d..000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/ImageUtils.cs +++ /dev/null @@ -1,561 +0,0 @@ -using ARMeilleure.Memory; -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; -using System; -using System.Collections.Generic; - -namespace Ryujinx.Graphics.Texture -{ - public static class ImageUtils - { - [Flags] - private enum TargetBuffer - { - Color = 1 << 0, - Depth = 1 << 1, - Stencil = 1 << 2, - - DepthStencil = Depth | Stencil - } - - private struct ImageDescriptor - { - public int BytesPerPixel { get; private set; } - public int BlockWidth { get; private set; } - public int BlockHeight { get; private set; } - public int BlockDepth { get; private set; } - - public TargetBuffer Target { get; private set; } - - public ImageDescriptor(int bytesPerPixel, int blockWidth, int blockHeight, int blockDepth, TargetBuffer target) - { - BytesPerPixel = bytesPerPixel; - BlockWidth = blockWidth; - BlockHeight = blockHeight; - BlockDepth = blockDepth; - Target = target; - } - } - - private const GalImageFormat Snorm = GalImageFormat.Snorm; - private const GalImageFormat Unorm = GalImageFormat.Unorm; - private const GalImageFormat Sint = GalImageFormat.Sint; - private const GalImageFormat Uint = GalImageFormat.Uint; - private const GalImageFormat Float = GalImageFormat.Float; - private const GalImageFormat Srgb = GalImageFormat.Srgb; - - private static readonly Dictionary TextureTable = - new Dictionary() - { - { GalTextureFormat.Rgba32, GalImageFormat.Rgba32 | Sint | Uint | Float }, - { GalTextureFormat.Rgba16, GalImageFormat.Rgba16 | Snorm | Unorm | Sint | Uint | Float }, - { GalTextureFormat.Rg32, GalImageFormat.Rg32 | Sint | Uint | Float }, - { GalTextureFormat.Rgba8, GalImageFormat.Rgba8 | Snorm | Unorm | Sint | Uint | Srgb }, - { GalTextureFormat.Rgb10A2, GalImageFormat.Rgb10A2 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.Rg8, GalImageFormat.Rg8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Float }, - { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.Rg16, GalImageFormat.Rg16 | Snorm | Unorm | Sint | Float }, - { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Float }, - { GalTextureFormat.Rgba4, GalImageFormat.Rgba4 | Unorm }, - { GalTextureFormat.Rgb5A1, GalImageFormat.Rgb5A1 | Unorm }, - { GalTextureFormat.Rgb565, GalImageFormat.Rgb565 | Unorm }, - { GalTextureFormat.R11G11B10F, GalImageFormat.R11G11B10 | Float }, - { GalTextureFormat.D24S8, GalImageFormat.D24S8 | Unorm | Uint }, - { GalTextureFormat.D32F, GalImageFormat.D32 | Float }, - { GalTextureFormat.D32Fx24S8, GalImageFormat.D32S8 | Float }, - { GalTextureFormat.D16, GalImageFormat.D16 | Unorm }, - - // Compressed formats - { GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float }, - { GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float }, - { GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb }, - { GalTextureFormat.BC1, GalImageFormat.BC1 | Unorm | Srgb }, - { GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm | Srgb }, - { GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm | Srgb }, - { GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm }, - { GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm }, - { GalTextureFormat.Astc2D4x4, GalImageFormat.Astc2D4x4 | Unorm | Srgb }, - { GalTextureFormat.Astc2D5x5, GalImageFormat.Astc2D5x5 | Unorm | Srgb }, - { GalTextureFormat.Astc2D6x6, GalImageFormat.Astc2D6x6 | Unorm | Srgb }, - { GalTextureFormat.Astc2D8x8, GalImageFormat.Astc2D8x8 | Unorm | Srgb }, - { GalTextureFormat.Astc2D10x10, GalImageFormat.Astc2D10x10 | Unorm | Srgb }, - { GalTextureFormat.Astc2D12x12, GalImageFormat.Astc2D12x12 | Unorm | Srgb }, - { GalTextureFormat.Astc2D5x4, GalImageFormat.Astc2D5x4 | Unorm | Srgb }, - { GalTextureFormat.Astc2D6x5, GalImageFormat.Astc2D6x5 | Unorm | Srgb }, - { GalTextureFormat.Astc2D8x6, GalImageFormat.Astc2D8x6 | Unorm | Srgb }, - { GalTextureFormat.Astc2D10x8, GalImageFormat.Astc2D10x8 | Unorm | Srgb }, - { GalTextureFormat.Astc2D12x10, GalImageFormat.Astc2D12x10 | Unorm | Srgb }, - { GalTextureFormat.Astc2D8x5, GalImageFormat.Astc2D8x5 | Unorm | Srgb }, - { GalTextureFormat.Astc2D10x5, GalImageFormat.Astc2D10x5 | Unorm | Srgb }, - { GalTextureFormat.Astc2D10x6, GalImageFormat.Astc2D10x6 | Unorm | Srgb } - }; - - private static readonly Dictionary ImageTable = - new Dictionary() - { - { GalImageFormat.Rgba32, new ImageDescriptor(16, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rgba16, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rg32, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rgbx8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rgba8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Bgra8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rgb10A2, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rgba4, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.Bgr5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rgb5A1, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rgb565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Bgr565, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.Rg16, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.Rg8, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, 1, TargetBuffer.Color) }, - { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, 1, TargetBuffer.Color) }, - - { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D24, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.DepthStencil) }, - { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, 1, TargetBuffer.DepthStencil) } - }; - - public static GalImageFormat ConvertTexture( - GalTextureFormat format, - GalTextureType rType, - GalTextureType gType, - GalTextureType bType, - GalTextureType aType, - bool convSrgb) - { - if (!TextureTable.TryGetValue(format, out GalImageFormat imageFormat)) - { - throw new NotImplementedException($"Format 0x{((int)format):x} not implemented!"); - } - - if (!HasDepth(imageFormat) && (rType != gType || rType != bType || rType != aType)) - { - throw new NotImplementedException("Per component types are not implemented!"); - } - - GalImageFormat formatType = convSrgb ? Srgb : GetFormatType(rType); - - GalImageFormat combinedFormat = (imageFormat & GalImageFormat.FormatMask) | formatType; - - if (!imageFormat.HasFlag(formatType)) - { - throw new NotImplementedException($"Format \"{combinedFormat}\" not implemented!"); - } - - return combinedFormat; - } - - public static GalImageFormat ConvertSurface(GalSurfaceFormat format) - { - switch (format) - { - case GalSurfaceFormat.Rgba32Float: return GalImageFormat.Rgba32 | Float; - case GalSurfaceFormat.Rgba32Uint: return GalImageFormat.Rgba32 | Uint; - case GalSurfaceFormat.Rgba16Float: return GalImageFormat.Rgba16 | Float; - case GalSurfaceFormat.Rgba16Uint: return GalImageFormat.Rgba16 | Uint; - case GalSurfaceFormat.Rgba16Unorm: return GalImageFormat.Rgba16 | Unorm; - case GalSurfaceFormat.Rg32Float: return GalImageFormat.Rg32 | Float; - case GalSurfaceFormat.Rg32Sint: return GalImageFormat.Rg32 | Sint; - case GalSurfaceFormat.Rg32Uint: return GalImageFormat.Rg32 | Uint; - case GalSurfaceFormat.Bgra8Unorm: return GalImageFormat.Bgra8 | Unorm; - case GalSurfaceFormat.Bgra8Srgb: return GalImageFormat.Bgra8 | Srgb; - case GalSurfaceFormat.Rgb10A2Unorm: return GalImageFormat.Rgb10A2 | Unorm; - case GalSurfaceFormat.Rgba8Unorm: return GalImageFormat.Rgba8 | Unorm; - case GalSurfaceFormat.Rgba8Srgb: return GalImageFormat.Rgba8 | Srgb; - case GalSurfaceFormat.Rgba8Snorm: return GalImageFormat.Rgba8 | Snorm; - case GalSurfaceFormat.Rg16Snorm: return GalImageFormat.Rg16 | Snorm; - case GalSurfaceFormat.Rg16Unorm: return GalImageFormat.Rg16 | Unorm; - case GalSurfaceFormat.Rg16Sint: return GalImageFormat.Rg16 | Sint; - case GalSurfaceFormat.Rg16Float: return GalImageFormat.Rg16 | Float; - case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.R11G11B10 | Float; - case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Float; - case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint; - case GalSurfaceFormat.Rg8Unorm: return GalImageFormat.Rg8 | Unorm; - case GalSurfaceFormat.Rg8Snorm: return GalImageFormat.Rg8 | Snorm; - case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Float; - case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm; - case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint; - case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; - case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint; - case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.Rgb565 | Unorm; - case GalSurfaceFormat.Bgr5A1Unorm: return GalImageFormat.Bgr5A1 | Unorm; - case GalSurfaceFormat.Rgbx8Unorm: return GalImageFormat.Rgbx8 | Unorm; - } - - throw new NotImplementedException(format.ToString()); - } - - public static GalImageFormat ConvertZeta(GalZetaFormat format) - { - switch (format) - { - case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float; - case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm; - case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm; - case GalZetaFormat.D24X8Unorm: return GalImageFormat.D24 | Unorm; - case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm; - case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float; - } - - throw new NotImplementedException(format.ToString()); - } - - public static byte[] ReadTexture(IMemory memory, GalImage image, long position) - { - MemoryManager cpuMemory; - - if (memory is NvGpuVmm vmm) - { - cpuMemory = vmm.Memory; - } - else - { - cpuMemory = (MemoryManager)memory; - } - - ISwizzle swizzle = TextureHelper.GetSwizzle(image); - - ImageDescriptor desc = GetImageDescriptor(image.Format); - - (int width, int height, int depth) = GetImageSizeInBlocks(image); - - int bytesPerPixel = desc.BytesPerPixel; - - // Note: Each row of the texture needs to be aligned to 4 bytes. - int pitch = (width * bytesPerPixel + 3) & ~3; - - int dataLayerSize = height * pitch * depth; - byte[] data = new byte[dataLayerSize * image.LayerCount]; - - int targetMipLevel = image.MaxMipmapLevel <= 1 ? 1 : image.MaxMipmapLevel - 1; - int layerOffset = GetLayerOffset(image, targetMipLevel); - - for (int layer = 0; layer < image.LayerCount; layer++) - { - for (int z = 0; z < depth; z++) - { - for (int y = 0; y < height; y++) - { - int outOffs = (dataLayerSize * layer) + y * pitch + (z * width * height * bytesPerPixel); - - for (int x = 0; x < width; x++) - { - long offset = (uint)swizzle.GetSwizzleOffset(x, y, z); - - cpuMemory.ReadBytes(position + (layerOffset * layer) + offset, data, outOffs, bytesPerPixel); - - outOffs += bytesPerPixel; - } - } - } - } - - return data; - } - - public static void WriteTexture(NvGpuVmm vmm, GalImage image, long position, byte[] data) - { - ISwizzle swizzle = TextureHelper.GetSwizzle(image); - - ImageDescriptor desc = GetImageDescriptor(image.Format); - - (int width, int height, int depth) = GetImageSizeInBlocks(image); - - int bytesPerPixel = desc.BytesPerPixel; - - int inOffs = 0; - - for (int z = 0; z < depth; z++) - for (int y = 0; y < height; y++) - for (int x = 0; x < width; x++) - { - long offset = (uint)swizzle.GetSwizzleOffset(x, y, z); - - vmm.Memory.WriteBytes(position + offset, data, inOffs, bytesPerPixel); - - inOffs += bytesPerPixel; - } - } - - // TODO: Support non 2D - public static bool CopyTexture( - NvGpuVmm vmm, - GalImage srcImage, - GalImage dstImage, - long srcAddress, - long dstAddress, - int srcX, - int srcY, - int dstX, - int dstY, - int width, - int height) - { - ISwizzle srcSwizzle = TextureHelper.GetSwizzle(srcImage); - ISwizzle dstSwizzle = TextureHelper.GetSwizzle(dstImage); - - ImageDescriptor desc = GetImageDescriptor(srcImage.Format); - - if (GetImageDescriptor(dstImage.Format).BytesPerPixel != desc.BytesPerPixel) - { - return false; - } - - int bytesPerPixel = desc.BytesPerPixel; - - for (int y = 0; y < height; y++) - for (int x = 0; x < width; x++) - { - long srcOffset = (uint)srcSwizzle.GetSwizzleOffset(srcX + x, srcY + y, 0); - long dstOffset = (uint)dstSwizzle.GetSwizzleOffset(dstX + x, dstY + y, 0); - - byte[] texel = vmm.ReadBytes(srcAddress + srcOffset, bytesPerPixel); - - vmm.WriteBytes(dstAddress + dstOffset, texel); - } - - return true; - } - - public static int GetSize(GalImage image) - { - ImageDescriptor desc = GetImageDescriptor(image.Format); - - int componentCount = GetCoordsCountTextureTarget(image.TextureTarget); - - if (IsArray(image.TextureTarget)) - componentCount--; - - int width = DivRoundUp(image.Width, desc.BlockWidth); - int height = DivRoundUp(image.Height, desc.BlockHeight); - int depth = DivRoundUp(image.Depth, desc.BlockDepth); - - switch (componentCount) - { - case 1: - return desc.BytesPerPixel * width * image.LayerCount; - case 2: - return desc.BytesPerPixel * width * height * image.LayerCount; - case 3: - return desc.BytesPerPixel * width * height * depth * image.LayerCount; - default: - throw new InvalidOperationException($"Invalid component count: {componentCount}"); - } - } - - public static int GetGpuSize(GalImage image, bool forcePitch = false) - { - return TextureHelper.GetSwizzle(image).GetImageSize(image.MaxMipmapLevel) * image.LayerCount; - } - - public static int GetLayerOffset(GalImage image, int mipLevel) - { - if (mipLevel <= 0) - { - mipLevel = 1; - } - - return TextureHelper.GetSwizzle(image).GetMipOffset(mipLevel); - } - - public static int GetPitch(GalImageFormat format, int width) - { - ImageDescriptor desc = GetImageDescriptor(format); - - int pitch = desc.BytesPerPixel * DivRoundUp(width, desc.BlockWidth); - - pitch = (pitch + 0x1f) & ~0x1f; - - return pitch; - } - - public static int GetBlockWidth(GalImageFormat format) - { - return GetImageDescriptor(format).BlockWidth; - } - - public static int GetBlockHeight(GalImageFormat format) - { - return GetImageDescriptor(format).BlockHeight; - } - - public static int GetBlockDepth(GalImageFormat format) - { - return GetImageDescriptor(format).BlockDepth; - } - - public static int GetAlignedWidth(GalImage image) - { - ImageDescriptor desc = GetImageDescriptor(image.Format); - - int alignMask; - - if (image.Layout == GalMemoryLayout.BlockLinear) - { - alignMask = image.TileWidth * (64 / desc.BytesPerPixel) - 1; - } - else - { - alignMask = (32 / desc.BytesPerPixel) - 1; - } - - return (image.Width + alignMask) & ~alignMask; - } - - public static (int Width, int Height, int Depth) GetImageSizeInBlocks(GalImage image) - { - ImageDescriptor desc = GetImageDescriptor(image.Format); - - return (DivRoundUp(image.Width, desc.BlockWidth), - DivRoundUp(image.Height, desc.BlockHeight), - DivRoundUp(image.Depth, desc.BlockDepth)); - } - - public static int GetBytesPerPixel(GalImageFormat format) - { - return GetImageDescriptor(format).BytesPerPixel; - } - - private static int DivRoundUp(int lhs, int rhs) - { - return (lhs + (rhs - 1)) / rhs; - } - - public static bool HasColor(GalImageFormat format) - { - return (GetImageDescriptor(format).Target & TargetBuffer.Color) != 0; - } - - public static bool HasDepth(GalImageFormat format) - { - return (GetImageDescriptor(format).Target & TargetBuffer.Depth) != 0; - } - - public static bool HasStencil(GalImageFormat format) - { - return (GetImageDescriptor(format).Target & TargetBuffer.Stencil) != 0; - } - - public static bool IsCompressed(GalImageFormat format) - { - ImageDescriptor desc = GetImageDescriptor(format); - - return (desc.BlockWidth | desc.BlockHeight) != 1; - } - - private static ImageDescriptor GetImageDescriptor(GalImageFormat format) - { - GalImageFormat pixelFormat = format & GalImageFormat.FormatMask; - - if (ImageTable.TryGetValue(pixelFormat, out ImageDescriptor descriptor)) - { - return descriptor; - } - - throw new NotImplementedException($"Format \"{pixelFormat}\" not implemented!"); - } - - private static GalImageFormat GetFormatType(GalTextureType type) - { - switch (type) - { - case GalTextureType.Snorm: return Snorm; - case GalTextureType.Unorm: return Unorm; - case GalTextureType.Sint: return Sint; - case GalTextureType.Uint: return Uint; - case GalTextureType.Float: return Float; - - default: throw new NotImplementedException(((int)type).ToString()); - } - } - - public static TextureTarget GetTextureTarget(GalTextureTarget galTextureTarget) - { - switch (galTextureTarget) - { - case GalTextureTarget.OneD: - return TextureTarget.Texture1D; - case GalTextureTarget.TwoD: - case GalTextureTarget.TwoDNoMipMap: - return TextureTarget.Texture2D; - case GalTextureTarget.ThreeD: - return TextureTarget.Texture3D; - case GalTextureTarget.OneDArray: - return TextureTarget.Texture1DArray; - case GalTextureTarget.OneDBuffer: - return TextureTarget.TextureBuffer; - case GalTextureTarget.TwoDArray: - return TextureTarget.Texture2DArray; - case GalTextureTarget.CubeMap: - return TextureTarget.TextureCubeMap; - case GalTextureTarget.CubeArray: - return TextureTarget.TextureCubeMapArray; - default: - throw new NotSupportedException($"Texture target {galTextureTarget} currently not supported!"); - } - } - - public static bool IsArray(GalTextureTarget textureTarget) - { - switch (textureTarget) - { - case GalTextureTarget.OneDArray: - case GalTextureTarget.TwoDArray: - case GalTextureTarget.CubeArray: - return true; - default: - return false; - } - } - - public static int GetCoordsCountTextureTarget(GalTextureTarget textureTarget) - { - switch (textureTarget) - { - case GalTextureTarget.OneD: - return 1; - case GalTextureTarget.OneDArray: - case GalTextureTarget.OneDBuffer: - case GalTextureTarget.TwoD: - case GalTextureTarget.TwoDNoMipMap: - return 2; - case GalTextureTarget.ThreeD: - case GalTextureTarget.TwoDArray: - case GalTextureTarget.CubeMap: - return 3; - case GalTextureTarget.CubeArray: - return 4; - default: - throw new NotImplementedException($"TextureTarget.{textureTarget} not implemented yet."); - } - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs deleted file mode 100644 index fb1bd0985..000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/LinearSwizzle.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Texture -{ - class LinearSwizzle : ISwizzle - { - private int _pitch; - private int _bpp; - - private int _sliceSize; - - public LinearSwizzle(int pitch, int bpp, int width, int height) - { - _pitch = pitch; - _bpp = bpp; - _sliceSize = width * height * bpp; - } - - public void SetMipLevel(int level) - { - throw new NotImplementedException(); - } - - public int GetMipOffset(int level) - { - if (level == 1) - return _sliceSize; - throw new NotImplementedException(); - } - - public int GetImageSize(int mipsCount) - { - int size = GetMipOffset(mipsCount); - - size = (size + 0x1fff) & ~0x1fff; - - return size; - } - - public int GetSwizzleOffset(int x, int y, int z) - { - return z * _sliceSize + x * _bpp + y * _pitch; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs deleted file mode 100644 index 28c905025..000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureFactory.cs +++ /dev/null @@ -1,166 +0,0 @@ -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; -using System; - -namespace Ryujinx.Graphics.Texture -{ - static class TextureFactory - { - public static GalImage MakeTexture(NvGpuVmm vmm, long ticPosition) - { - int[] tic = ReadWords(vmm, ticPosition, 8); - - GalImageFormat format = GetImageFormat(tic); - - GalTextureTarget textureTarget = (GalTextureTarget)((tic[4] >> 23) & 0xF); - - GalTextureSource xSource = (GalTextureSource)((tic[0] >> 19) & 7); - GalTextureSource ySource = (GalTextureSource)((tic[0] >> 22) & 7); - GalTextureSource zSource = (GalTextureSource)((tic[0] >> 25) & 7); - GalTextureSource wSource = (GalTextureSource)((tic[0] >> 28) & 7); - - TextureSwizzle swizzle = (TextureSwizzle)((tic[2] >> 21) & 7); - - int maxMipmapLevel = (tic[3] >> 28) & 0xF + 1; - - GalMemoryLayout layout; - - if (swizzle == TextureSwizzle.BlockLinear || - swizzle == TextureSwizzle.BlockLinearColorKey) - { - layout = GalMemoryLayout.BlockLinear; - } - else - { - layout = GalMemoryLayout.Pitch; - } - - int gobBlockHeightLog2 = (tic[3] >> 3) & 7; - int gobBlockDepthLog2 = (tic[3] >> 6) & 7; - int tileWidthLog2 = (tic[3] >> 10) & 7; - - int gobBlockHeight = 1 << gobBlockHeightLog2; - int gobBlockDepth = 1 << gobBlockDepthLog2; - int tileWidth = 1 << tileWidthLog2; - - int width = ((tic[4] >> 0) & 0xffff) + 1; - int height = ((tic[5] >> 0) & 0xffff) + 1; - int depth = ((tic[5] >> 16) & 0x3fff) + 1; - - int layoutCount = 1; - - // TODO: check this - if (ImageUtils.IsArray(textureTarget)) - { - layoutCount = depth; - depth = 1; - } - - if (textureTarget == GalTextureTarget.OneD) - { - height = 1; - } - - if (textureTarget == GalTextureTarget.TwoD || textureTarget == GalTextureTarget.OneD) - { - depth = 1; - } - else if (textureTarget == GalTextureTarget.CubeMap) - { - // FIXME: This is a bit hacky but I guess it's fine for now - layoutCount = 6; - depth = 1; - } - else if (textureTarget == GalTextureTarget.CubeArray) - { - // FIXME: This is a really really hacky but I guess it's fine for now - layoutCount *= 6; - depth = 1; - } - - GalImage image = new GalImage( - width, - height, - depth, - layoutCount, - tileWidth, - gobBlockHeight, - gobBlockDepth, - layout, - format, - textureTarget, - maxMipmapLevel, - xSource, - ySource, - zSource, - wSource); - - if (layout == GalMemoryLayout.Pitch) - { - image.Pitch = (tic[3] & 0xffff) << 5; - } - - return image; - } - - public static GalTextureSampler MakeSampler(NvGpu gpu, NvGpuVmm vmm, long tscPosition) - { - int[] tsc = ReadWords(vmm, tscPosition, 8); - - GalTextureWrap addressU = (GalTextureWrap)((tsc[0] >> 0) & 7); - GalTextureWrap addressV = (GalTextureWrap)((tsc[0] >> 3) & 7); - GalTextureWrap addressP = (GalTextureWrap)((tsc[0] >> 6) & 7); - - bool depthCompare = ((tsc[0] >> 9) & 1) == 1; - - DepthCompareFunc depthCompareFunc = (DepthCompareFunc)((tsc[0] >> 10) & 7); - - GalTextureFilter magFilter = (GalTextureFilter) ((tsc[1] >> 0) & 3); - GalTextureFilter minFilter = (GalTextureFilter) ((tsc[1] >> 4) & 3); - GalTextureMipFilter mipFilter = (GalTextureMipFilter)((tsc[1] >> 6) & 3); - - GalColorF borderColor = new GalColorF( - BitConverter.Int32BitsToSingle(tsc[4]), - BitConverter.Int32BitsToSingle(tsc[5]), - BitConverter.Int32BitsToSingle(tsc[6]), - BitConverter.Int32BitsToSingle(tsc[7])); - - return new GalTextureSampler( - addressU, - addressV, - addressP, - minFilter, - magFilter, - mipFilter, - borderColor, - depthCompare, - depthCompareFunc); - } - - private static GalImageFormat GetImageFormat(int[] tic) - { - GalTextureType rType = (GalTextureType)((tic[0] >> 7) & 7); - GalTextureType gType = (GalTextureType)((tic[0] >> 10) & 7); - GalTextureType bType = (GalTextureType)((tic[0] >> 13) & 7); - GalTextureType aType = (GalTextureType)((tic[0] >> 16) & 7); - - GalTextureFormat format = (GalTextureFormat)(tic[0] & 0x7f); - - bool convSrgb = ((tic[4] >> 22) & 1) != 0; - - return ImageUtils.ConvertTexture(format, rType, gType, bType, aType, convSrgb); - } - - private static int[] ReadWords(NvGpuVmm vmm, long position, int count) - { - int[] words = new int[count]; - - for (int index = 0; index < count; index++, position += 4) - { - words[index] = vmm.ReadInt32(position); - } - - return words; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs deleted file mode 100644 index e07eb037d..000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureHelper.cs +++ /dev/null @@ -1,53 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Common; -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; - -namespace Ryujinx.Graphics.Texture -{ - static class TextureHelper - { - public static ISwizzle GetSwizzle(GalImage image) - { - int blockWidth = ImageUtils.GetBlockWidth (image.Format); - int blockHeight = ImageUtils.GetBlockHeight (image.Format); - int blockDepth = ImageUtils.GetBlockDepth (image.Format); - int bytesPerPixel = ImageUtils.GetBytesPerPixel(image.Format); - - int width = BitUtils.DivRoundUp(image.Width, blockWidth); - int height = BitUtils.DivRoundUp(image.Height, blockHeight); - int depth = BitUtils.DivRoundUp(image.Depth, blockDepth); - - if (image.Layout == GalMemoryLayout.BlockLinear) - { - int alignMask = image.TileWidth * (64 / bytesPerPixel) - 1; - - width = (width + alignMask) & ~alignMask; - - return new BlockLinearSwizzle( - width, - height, - depth, - image.GobBlockHeight, - image.GobBlockDepth, - bytesPerPixel); - } - else - { - return new LinearSwizzle(image.Pitch, bytesPerPixel, width, height); - } - } - - public static (MemoryManager Memory, long Position) GetMemoryAndPosition( - IMemory memory, - long position) - { - if (memory is NvGpuVmm vmm) - { - return (vmm.Memory, vmm.GetPhysicalAddress(position)); - } - - return ((MemoryManager)memory, position); - } - } -} diff --git a/Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs b/Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs deleted file mode 100644 index 2cc426ab9..000000000 --- a/Ryujinx.Graphics/Graphics3d/Texture/TextureSwizzle.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Texture -{ - public enum TextureSwizzle - { - _1DBuffer = 0, - PitchColorKey = 1, - Pitch = 2, - BlockLinear = 3, - BlockLinearColorKey = 4 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/GraphicsConfig.cs b/Ryujinx.Graphics/GraphicsConfig.cs deleted file mode 100644 index 3e3ef4ffa..000000000 --- a/Ryujinx.Graphics/GraphicsConfig.cs +++ /dev/null @@ -1,4 +0,0 @@ -public static class GraphicsConfig -{ - public static string ShadersDumpPath; -} diff --git a/Ryujinx.Graphics/Memory/NvGpuBufferType.cs b/Ryujinx.Graphics/Memory/NvGpuBufferType.cs deleted file mode 100644 index 6f0d25718..000000000 --- a/Ryujinx.Graphics/Memory/NvGpuBufferType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Memory -{ - public enum NvGpuBufferType - { - Index, - Vertex, - Texture, - ConstBuffer, - Count - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Memory/NvGpuVmm.cs b/Ryujinx.Graphics/Memory/NvGpuVmm.cs deleted file mode 100644 index c72b82e37..000000000 --- a/Ryujinx.Graphics/Memory/NvGpuVmm.cs +++ /dev/null @@ -1,399 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Graphics.Gal; -using System; - -namespace Ryujinx.Graphics.Memory -{ - public class NvGpuVmm : IMemory, IGalMemory - { - public const long AddrSize = 1L << 40; - - private const int PtLvl0Bits = 14; - private const int PtLvl1Bits = 14; - private const int PtPageBits = 12; - - private const int PtLvl0Size = 1 << PtLvl0Bits; - private const int PtLvl1Size = 1 << PtLvl1Bits; - public const int PageSize = 1 << PtPageBits; - - private const int PtLvl0Mask = PtLvl0Size - 1; - private const int PtLvl1Mask = PtLvl1Size - 1; - public const int PageMask = PageSize - 1; - - private const int PtLvl0Bit = PtPageBits + PtLvl1Bits; - private const int PtLvl1Bit = PtPageBits; - - public MemoryManager Memory { get; private set; } - - private NvGpuVmmCache _cache; - - private const long PteUnmapped = -1; - private const long PteReserved = -2; - - private long[][] _pageTable; - - public NvGpuVmm(MemoryManager memory) - { - Memory = memory; - - _cache = new NvGpuVmmCache(memory); - - _pageTable = new long[PtLvl0Size][]; - } - - public long Map(long pa, long va, long size) - { - lock (_pageTable) - { - for (long offset = 0; offset < size; offset += PageSize) - { - SetPte(va + offset, pa + offset); - } - } - - return va; - } - - public long Map(long pa, long size) - { - lock (_pageTable) - { - long va = GetFreePosition(size); - - if (va != -1) - { - for (long offset = 0; offset < size; offset += PageSize) - { - SetPte(va + offset, pa + offset); - } - } - - return va; - } - } - - public long MapLow(long pa, long size) - { - lock (_pageTable) - { - long va = GetFreePosition(size, 1, PageSize); - - if (va != -1 && (ulong)va <= uint.MaxValue && (ulong)(va + size) <= uint.MaxValue) - { - for (long offset = 0; offset < size; offset += PageSize) - { - SetPte(va + offset, pa + offset); - } - } - else - { - va = -1; - } - - return va; - } - } - - public long ReserveFixed(long va, long size) - { - lock (_pageTable) - { - for (long offset = 0; offset < size; offset += PageSize) - { - if (IsPageInUse(va + offset)) - { - return -1; - } - } - - for (long offset = 0; offset < size; offset += PageSize) - { - SetPte(va + offset, PteReserved); - } - } - - return va; - } - - public long Reserve(long size, long align) - { - lock (_pageTable) - { - long position = GetFreePosition(size, align); - - if (position != -1) - { - for (long offset = 0; offset < size; offset += PageSize) - { - SetPte(position + offset, PteReserved); - } - } - - return position; - } - } - - public void Free(long va, long size) - { - lock (_pageTable) - { - for (long offset = 0; offset < size; offset += PageSize) - { - SetPte(va + offset, PteUnmapped); - } - } - } - - private long GetFreePosition(long size, long align = 1, long start = 1L << 32) - { - // Note: Address 0 is not considered valid by the driver, - // when 0 is returned it's considered a mapping error. - long position = start; - long freeSize = 0; - - if (align < 1) - { - align = 1; - } - - align = (align + PageMask) & ~PageMask; - - while (position + freeSize < AddrSize) - { - if (!IsPageInUse(position + freeSize)) - { - freeSize += PageSize; - - if (freeSize >= size) - { - return position; - } - } - else - { - position += freeSize + PageSize; - freeSize = 0; - - long remainder = position % align; - - if (remainder != 0) - { - position = (position - remainder) + align; - } - } - } - - return -1; - } - - public long GetPhysicalAddress(long va) - { - long basePos = GetPte(va); - - if (basePos < 0) - { - return -1; - } - - return basePos + (va & PageMask); - } - - public bool IsRegionFree(long va, long size) - { - for (long offset = 0; offset < size; offset += PageSize) - { - if (IsPageInUse(va + offset)) - { - return false; - } - } - - return true; - } - - private bool IsPageInUse(long va) - { - if (va >> PtLvl0Bits + PtLvl1Bits + PtPageBits != 0) - { - return false; - } - - long l0 = (va >> PtLvl0Bit) & PtLvl0Mask; - long l1 = (va >> PtLvl1Bit) & PtLvl1Mask; - - if (_pageTable[l0] == null) - { - return false; - } - - return _pageTable[l0][l1] != PteUnmapped; - } - - private long GetPte(long position) - { - long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; - long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - - if (_pageTable[l0] == null) - { - return -1; - } - - return _pageTable[l0][l1]; - } - - private void SetPte(long position, long tgtAddr) - { - long l0 = (position >> PtLvl0Bit) & PtLvl0Mask; - long l1 = (position >> PtLvl1Bit) & PtLvl1Mask; - - if (_pageTable[l0] == null) - { - _pageTable[l0] = new long[PtLvl1Size]; - - for (int index = 0; index < PtLvl1Size; index++) - { - _pageTable[l0][index] = PteUnmapped; - } - } - - _pageTable[l0][l1] = tgtAddr; - } - - public bool IsRegionModified(long pa, long size, NvGpuBufferType bufferType) - { - return _cache.IsRegionModified(pa, size, bufferType); - } - - public bool TryGetHostAddress(long position, long size, out IntPtr ptr) - { - return Memory.TryGetHostAddress(GetPhysicalAddress(position), size, out ptr); - } - - public byte ReadByte(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadByte(position); - } - - public ushort ReadUInt16(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadUInt16(position); - } - - public uint ReadUInt32(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadUInt32(position); - } - - public ulong ReadUInt64(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadUInt64(position); - } - - public sbyte ReadSByte(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadSByte(position); - } - - public short ReadInt16(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadInt16(position); - } - - public int ReadInt32(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadInt32(position); - } - - public long ReadInt64(long position) - { - position = GetPhysicalAddress(position); - - return Memory.ReadInt64(position); - } - - public byte[] ReadBytes(long position, long size) - { - position = GetPhysicalAddress(position); - - return Memory.ReadBytes(position, size); - } - - public void WriteByte(long position, byte value) - { - position = GetPhysicalAddress(position); - - Memory.WriteByte(position, value); - } - - public void WriteUInt16(long position, ushort value) - { - position = GetPhysicalAddress(position); - - Memory.WriteUInt16(position, value); - } - - public void WriteUInt32(long position, uint value) - { - position = GetPhysicalAddress(position); - - Memory.WriteUInt32(position, value); - } - - public void WriteUInt64(long position, ulong value) - { - position = GetPhysicalAddress(position); - - Memory.WriteUInt64(position, value); - } - - public void WriteSByte(long position, sbyte value) - { - position = GetPhysicalAddress(position); - - Memory.WriteSByte(position, value); - } - - public void WriteInt16(long position, short value) - { - position = GetPhysicalAddress(position); - - Memory.WriteInt16(position, value); - } - - public void WriteInt32(long position, int value) - { - position = GetPhysicalAddress(position); - - Memory.WriteInt32(position, value); - } - - public void WriteInt64(long position, long value) - { - position = GetPhysicalAddress(position); - - Memory.WriteInt64(position, value); - } - - public void WriteBytes(long position, byte[] data) - { - position = GetPhysicalAddress(position); - - Memory.WriteBytes(position, data); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs b/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs deleted file mode 100644 index 2a5054434..000000000 --- a/Ryujinx.Graphics/Memory/NvGpuVmmCache.cs +++ /dev/null @@ -1,79 +0,0 @@ -using ARMeilleure.Memory; -using System.Collections.Concurrent; - -namespace Ryujinx.Graphics.Memory -{ - class NvGpuVmmCache - { - private const int PageBits = MemoryManager.PageBits; - - private const long PageSize = MemoryManager.PageSize; - private const long PageMask = MemoryManager.PageMask; - - private ConcurrentDictionary[] _cachedPages; - - private MemoryManager _memory; - - public NvGpuVmmCache(MemoryManager memory) - { - _memory = memory; - - _cachedPages = new ConcurrentDictionary[1 << 20]; - } - - public bool IsRegionModified(long position, long size, NvGpuBufferType bufferType) - { - long va = position; - - long pa = _memory.GetPhysicalAddress(va); - - long endAddr = (va + size + PageMask) & ~PageMask; - - long addrTruncated = va & ~PageMask; - - bool modified = _memory.IsRegionModified(addrTruncated, endAddr - addrTruncated); - - int newBuffMask = 1 << (int)bufferType; - - long cachedPagesCount = 0; - - while (va < endAddr) - { - long page = _memory.GetPhysicalAddress(va) >> PageBits; - - ConcurrentDictionary dictionary = _cachedPages[page]; - - if (dictionary == null) - { - dictionary = new ConcurrentDictionary(); - - _cachedPages[page] = dictionary; - } - else if (modified) - { - _cachedPages[page].Clear(); - } - - if (dictionary.TryGetValue(pa, out int currBuffMask)) - { - if ((currBuffMask & newBuffMask) != 0) - { - cachedPagesCount++; - } - else - { - dictionary[pa] |= newBuffMask; - } - } - else - { - dictionary[pa] = newBuffMask; - } - - va += PageSize; - } - - return cachedPagesCount != (endAddr - addrTruncated) >> PageBits; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/NvGpu.cs b/Ryujinx.Graphics/NvGpu.cs deleted file mode 100644 index baac0b2d2..000000000 --- a/Ryujinx.Graphics/NvGpu.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Graphics3d; -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.VDec; -using Ryujinx.Graphics.Vic; - -namespace Ryujinx.Graphics -{ - public class NvGpu - { - public const int MaxViewportSize = 0x3FFF; - - public IGalRenderer Renderer { get; private set; } - - public GpuResourceManager ResourceManager { get; private set; } - - public DmaPusher Pusher { get; private set; } - - internal NvGpuFifo Fifo { get; private set; } - internal NvGpuEngine2d Engine2d { get; private set; } - internal NvGpuEngine3d Engine3d { get; private set; } - internal NvGpuEngineM2mf EngineM2mf { get; private set; } - internal NvGpuEngineP2mf EngineP2mf { get; private set; } - - private CdmaProcessor _cdmaProcessor; - internal VideoDecoder VideoDecoder { get; private set; } - internal VideoImageComposer VideoImageComposer { get; private set; } - - public NvGpu(IGalRenderer renderer) - { - Renderer = renderer; - - ResourceManager = new GpuResourceManager(this); - - Pusher = new DmaPusher(this); - - Fifo = new NvGpuFifo(this); - Engine2d = new NvGpuEngine2d(this); - Engine3d = new NvGpuEngine3d(this); - EngineM2mf = new NvGpuEngineM2mf(this); - EngineP2mf = new NvGpuEngineP2mf(this); - - _cdmaProcessor = new CdmaProcessor(this); - VideoDecoder = new VideoDecoder(this); - VideoImageComposer = new VideoImageComposer(this); - } - - public void PushCommandBuffer(NvGpuVmm vmm, int[] cmdBuffer) - { - lock (_cdmaProcessor) - { - _cdmaProcessor.PushCommands(vmm, cmdBuffer); - } - } - - public void UninitializeVideoDecoder() - { - lock (_cdmaProcessor) - { - FFmpegWrapper.Uninitialize(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/QuadHelper.cs b/Ryujinx.Graphics/QuadHelper.cs deleted file mode 100644 index 643084ba4..000000000 --- a/Ryujinx.Graphics/QuadHelper.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; - -namespace Ryujinx.Graphics -{ - static class QuadHelper - { - public static int ConvertSizeQuadsToTris(int size) - { - return size <= 0 ? 0 : (size / 4) * 6; - } - - public static int ConvertSizeQuadStripToTris(int size) - { - return size <= 1 ? 0 : ((size - 2) / 2) * 6; - } - - public static byte[] ConvertQuadsToTris(byte[] data, int entrySize, int count) - { - int primitivesCount = count / 4; - - int quadPrimSize = 4 * entrySize; - int trisPrimSize = 6 * entrySize; - - byte[] output = new byte[primitivesCount * 6 * entrySize]; - - for (int prim = 0; prim < primitivesCount; prim++) - { - void AssignIndex(int src, int dst, int copyCount = 1) - { - src = prim * quadPrimSize + src * entrySize; - dst = prim * trisPrimSize + dst * entrySize; - - Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize); - } - - // 0 1 2 -> 0 1 2. - AssignIndex(0, 0, 3); - - // 2 3 -> 3 4. - AssignIndex(2, 3, 2); - - // 0 -> 5. - AssignIndex(0, 5); - } - - return output; - } - - public static byte[] ConvertQuadStripToTris(byte[] data, int entrySize, int count) - { - int primitivesCount = (count - 2) / 2; - - int quadPrimSize = 2 * entrySize; - int trisPrimSize = 6 * entrySize; - - byte[] output = new byte[primitivesCount * 6 * entrySize]; - - for (int prim = 0; prim < primitivesCount; prim++) - { - void AssignIndex(int src, int dst, int copyCount = 1) - { - src = prim * quadPrimSize + src * entrySize + 2 * entrySize; - dst = prim * trisPrimSize + dst * entrySize; - - Buffer.BlockCopy(data, src, output, dst, copyCount * entrySize); - } - - // -2 -1 0 -> 0 1 2. - AssignIndex(-2, 0, 3); - - // 0 1 -> 3 4. - AssignIndex(0, 3, 2); - - // -2 -> 5. - AssignIndex(-2, 5); - } - - return output; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj deleted file mode 100644 index 4467fcdd9..000000000 --- a/Ryujinx.Graphics/Ryujinx.Graphics.csproj +++ /dev/null @@ -1,40 +0,0 @@ - - - - netcoreapp3.0 - win-x64;osx-x64;linux-x64 - Debug;Release;Profile Debug;Profile Release - - - - true - - - - true - TRACE;USE_PROFILING - false - - - - true - - - - true - TRACE;USE_PROFILING - true - - - - - - - - - - - - - - diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs deleted file mode 100644 index 5412d8728..000000000 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/Declarations.cs +++ /dev/null @@ -1,206 +0,0 @@ -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.StructuredIr; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace Ryujinx.Graphics.Shader.CodeGen.Glsl -{ - static class Declarations - { - public static void Declare(CodeGenContext context, StructuredProgramInfo info) - { - context.AppendLine("#version 420 core"); - - context.AppendLine(); - - context.AppendLine($"const int {DefaultNames.UndefinedName} = 0;"); - - context.AppendLine(); - - if (context.Config.Type == GalShaderType.Geometry) - { - context.AppendLine("layout (points) in;"); - context.AppendLine("layout (triangle_strip, max_vertices = 4) out;"); - - context.AppendLine(); - } - - context.AppendLine("layout (std140) uniform Extra"); - - context.EnterScope(); - - context.AppendLine("vec2 flip;"); - context.AppendLine("int instance;"); - - context.LeaveScope(";"); - - context.AppendLine(); - - if (info.CBuffers.Count != 0) - { - DeclareUniforms(context, info); - - context.AppendLine(); - } - - if (info.Samplers.Count != 0) - { - DeclareSamplers(context, info); - - context.AppendLine(); - } - - if (info.IAttributes.Count != 0) - { - DeclareInputAttributes(context, info); - - context.AppendLine(); - } - - if (info.OAttributes.Count != 0) - { - DeclareOutputAttributes(context, info); - - context.AppendLine(); - } - } - - public static void DeclareLocals(CodeGenContext context, StructuredProgramInfo info) - { - foreach (AstOperand decl in info.Locals) - { - string name = context.OperandManager.DeclareLocal(decl); - - context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); - } - } - - private static string GetVarTypeName(VariableType type) - { - switch (type) - { - case VariableType.Bool: return "bool"; - case VariableType.F32: return "float"; - case VariableType.S32: return "int"; - case VariableType.U32: return "uint"; - } - - throw new ArgumentException($"Invalid variable type \"{type}\"."); - } - - private static void DeclareUniforms(CodeGenContext context, StructuredProgramInfo info) - { - foreach (int cbufSlot in info.CBuffers.OrderBy(x => x)) - { - string ubName = OperandManager.GetShaderStagePrefix(context.Config.Type); - - ubName += "_" + DefaultNames.UniformNamePrefix + cbufSlot; - - context.CBufferDescriptors.Add(new CBufferDescriptor(ubName, cbufSlot)); - - context.AppendLine("layout (std140) uniform " + ubName); - - context.EnterScope(); - - string ubSize = "[" + NumberFormatter.FormatInt(context.Config.MaxCBufferSize / 16) + "]"; - - context.AppendLine("vec4 " + OperandManager.GetUbName(context.Config.Type, cbufSlot) + ubSize + ";"); - - context.LeaveScope(";"); - } - } - - private static void DeclareSamplers(CodeGenContext context, StructuredProgramInfo info) - { - Dictionary samplers = new Dictionary(); - - foreach (AstTextureOperation texOp in info.Samplers.OrderBy(x => x.Handle)) - { - string samplerName = OperandManager.GetSamplerName(context.Config.Type, texOp); - - if (!samplers.TryAdd(samplerName, texOp)) - { - continue; - } - - string samplerTypeName = GetSamplerTypeName(texOp.Type); - - context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";"); - } - - foreach (KeyValuePair kv in samplers) - { - string samplerName = kv.Key; - - AstTextureOperation texOp = kv.Value; - - TextureDescriptor desc; - - if ((texOp.Flags & TextureFlags.Bindless) != 0) - { - AstOperand operand = texOp.GetSource(0) as AstOperand; - - desc = new TextureDescriptor(samplerName, operand.CbufSlot, operand.CbufOffset); - } - else - { - desc = new TextureDescriptor(samplerName, texOp.Handle); - } - - context.TextureDescriptors.Add(desc); - } - } - - private static void DeclareInputAttributes(CodeGenContext context, StructuredProgramInfo info) - { - string suffix = context.Config.Type == GalShaderType.Geometry ? "[]" : string.Empty; - - foreach (int attr in info.IAttributes.OrderBy(x => x)) - { - context.AppendLine($"layout (location = {attr}) in vec4 {DefaultNames.IAttributePrefix}{attr}{suffix};"); - } - } - - private static void DeclareOutputAttributes(CodeGenContext context, StructuredProgramInfo info) - { - foreach (int attr in info.OAttributes.OrderBy(x => x)) - { - context.AppendLine($"layout (location = {attr}) out vec4 {DefaultNames.OAttributePrefix}{attr};"); - } - } - - private static string GetSamplerTypeName(TextureType type) - { - string typeName; - - switch (type & TextureType.Mask) - { - case TextureType.Texture1D: typeName = "sampler1D"; break; - case TextureType.Texture2D: typeName = "sampler2D"; break; - case TextureType.Texture3D: typeName = "sampler3D"; break; - case TextureType.TextureCube: typeName = "samplerCube"; break; - - default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); - } - - if ((type & TextureType.Multisample) != 0) - { - typeName += "MS"; - } - - if ((type & TextureType.Array) != 0) - { - typeName += "Array"; - } - - if ((type & TextureType.Shadow) != 0) - { - typeName += "Shadow"; - } - - return typeName; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslProgram.cs b/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslProgram.cs deleted file mode 100644 index e616aa1f8..000000000 --- a/Ryujinx.Graphics/Shader/CodeGen/Glsl/GlslProgram.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace Ryujinx.Graphics.Shader.CodeGen.Glsl -{ - class GlslProgram - { - public CBufferDescriptor[] CBufferDescriptors { get; } - public TextureDescriptor[] TextureDescriptors { get; } - - public string Code { get; } - - public GlslProgram( - CBufferDescriptor[] cBufferDescs, - TextureDescriptor[] textureDescs, - string code) - { - CBufferDescriptors = cBufferDescs; - TextureDescriptors = textureDescs; - Code = code; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Decoders/IntegerSize.cs b/Ryujinx.Graphics/Shader/Decoders/IntegerSize.cs deleted file mode 100644 index 70fdfc3dc..000000000 --- a/Ryujinx.Graphics/Shader/Decoders/IntegerSize.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Shader.Decoders -{ - enum IntegerSize - { - U8 = 0, - S8 = 1, - U16 = 2, - S16 = 3, - B32 = 4, - B64 = 5 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs deleted file mode 100644 index a2a50fced..000000000 --- a/Ryujinx.Graphics/Shader/Instructions/InstEmitMemory.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Ryujinx.Graphics.Shader.Decoders; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.Translation; - -using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; -using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; - -namespace Ryujinx.Graphics.Shader.Instructions -{ - static partial class InstEmit - { - public static void Ald(EmitterContext context) - { - OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; - - Operand[] elems = new Operand[op.Count]; - - for (int index = 0; index < op.Count; index++) - { - Operand src = Attribute(op.AttributeOffset + index * 4); - - context.Copy(elems[index] = Local(), src); - } - - for (int index = 0; index < op.Count; index++) - { - Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); - - if (rd.IsRZ) - { - break; - } - - context.Copy(Register(rd), elems[index]); - } - } - - public static void Ast(EmitterContext context) - { - OpCodeAttribute op = (OpCodeAttribute)context.CurrOp; - - for (int index = 0; index < op.Count; index++) - { - if (op.Rd.Index + index > RegisterConsts.RegisterZeroIndex) - { - break; - } - - Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); - - Operand dest = Attribute(op.AttributeOffset + index * 4); - - context.Copy(dest, Register(rd)); - } - } - - public static void Ipa(EmitterContext context) - { - OpCodeIpa op = (OpCodeIpa)context.CurrOp; - - Operand srcA = new Operand(OperandType.Attribute, op.AttributeOffset); - - Operand srcB = GetSrcB(context); - - context.Copy(GetDest(context), srcA); - } - - public static void Ldc(EmitterContext context) - { - OpCodeLdc op = (OpCodeLdc)context.CurrOp; - - if (op.Size > IntegerSize.B64) - { - // TODO: Warning. - } - - bool isSmallInt = op.Size < IntegerSize.B32; - - int count = op.Size == IntegerSize.B64 ? 2 : 1; - - Operand baseOffset = context.Copy(GetSrcA(context)); - - for (int index = 0; index < count; index++) - { - Register rd = new Register(op.Rd.Index + index, RegisterType.Gpr); - - if (rd.IsRZ) - { - break; - } - - Operand offset = context.IAdd(baseOffset, Const((op.Offset + index) * 4)); - - Operand value = context.LoadConstant(Const(op.Slot), offset); - - if (isSmallInt) - { - Operand shift = context.BitwiseAnd(baseOffset, Const(3)); - - value = context.ShiftRightU32(value, shift); - - switch (op.Size) - { - case IntegerSize.U8: value = ZeroExtendTo32(context, value, 8); break; - case IntegerSize.U16: value = ZeroExtendTo32(context, value, 16); break; - case IntegerSize.S8: value = SignExtendTo32(context, value, 8); break; - case IntegerSize.S16: value = SignExtendTo32(context, value, 16); break; - } - } - - context.Copy(Register(rd), value); - } - } - - public static void Out(EmitterContext context) - { - OpCode op = context.CurrOp; - - bool emit = op.RawOpCode.Extract(39); - bool cut = op.RawOpCode.Extract(40); - - if (!(emit || cut)) - { - // TODO: Warning. - } - - if (emit) - { - context.EmitVertex(); - } - - if (cut) - { - context.EndPrimitive(); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/Instructions/InstEmitMove.cs b/Ryujinx.Graphics/Shader/Instructions/InstEmitMove.cs deleted file mode 100644 index b74dbfd72..000000000 --- a/Ryujinx.Graphics/Shader/Instructions/InstEmitMove.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Ryujinx.Graphics.Shader.Decoders; -using Ryujinx.Graphics.Shader.IntermediateRepresentation; -using Ryujinx.Graphics.Shader.Translation; - -using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper; - -namespace Ryujinx.Graphics.Shader.Instructions -{ - static partial class InstEmit - { - public static void Mov(EmitterContext context) - { - OpCodeAlu op = (OpCodeAlu)context.CurrOp; - - context.Copy(GetDest(context), GetSrcB(context)); - } - - public static void Sel(EmitterContext context) - { - OpCodeAlu op = (OpCodeAlu)context.CurrOp; - - Operand pred = GetPredicate39(context); - - Operand srcA = GetSrcA(context); - Operand srcB = GetSrcB(context); - - Operand res = context.ConditionalSelect(pred, srcA, srcB); - - context.Copy(GetDest(context), res); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs b/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs deleted file mode 100644 index bf2070077..000000000 --- a/Ryujinx.Graphics/Shader/IntermediateRepresentation/TextureType.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.Shader.IntermediateRepresentation -{ - [Flags] - enum TextureType - { - Texture1D, - Texture2D, - Texture3D, - TextureCube, - - Mask = 0xff, - - Array = 1 << 8, - Multisample = 1 << 9, - Shadow = 1 << 10 - } - - static class TextureTypeExtensions - { - public static int GetCoordsCount(this TextureType type) - { - switch (type & TextureType.Mask) - { - case TextureType.Texture1D: return 1; - case TextureType.Texture2D: return 2; - case TextureType.Texture3D: return 3; - case TextureType.TextureCube: return 3; - } - - throw new ArgumentException($"Invalid texture type \"{type}\"."); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderConfig.cs b/Ryujinx.Graphics/Shader/ShaderConfig.cs deleted file mode 100644 index c2a94814e..000000000 --- a/Ryujinx.Graphics/Shader/ShaderConfig.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Ryujinx.Graphics.Gal; -using System; - -namespace Ryujinx.Graphics.Shader -{ - public struct ShaderConfig - { - public GalShaderType Type { get; } - - public int MaxCBufferSize; - - public ShaderConfig(GalShaderType type, int maxCBufferSize) - { - if (maxCBufferSize <= 0) - { - throw new ArgumentOutOfRangeException(nameof(maxCBufferSize)); - } - - Type = type; - MaxCBufferSize = maxCBufferSize; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderProgram.cs b/Ryujinx.Graphics/Shader/ShaderProgram.cs deleted file mode 100644 index 9257fd262..000000000 --- a/Ryujinx.Graphics/Shader/ShaderProgram.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Ryujinx.Graphics.Shader -{ - public class ShaderProgram - { - public ShaderProgramInfo Info { get; } - - public string Code { get; } - - internal ShaderProgram(ShaderProgramInfo info, string code) - { - Info = info; - Code = code; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Shader/ShaderProgramInfo.cs b/Ryujinx.Graphics/Shader/ShaderProgramInfo.cs deleted file mode 100644 index c529a3536..000000000 --- a/Ryujinx.Graphics/Shader/ShaderProgramInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.ObjectModel; - -namespace Ryujinx.Graphics.Shader -{ - public class ShaderProgramInfo - { - public ReadOnlyCollection CBuffers { get; } - public ReadOnlyCollection Textures { get; } - - internal ShaderProgramInfo(CBufferDescriptor[] cBuffers, TextureDescriptor[] textures) - { - CBuffers = Array.AsReadOnly(cBuffers); - Textures = Array.AsReadOnly(textures); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/BitStreamWriter.cs b/Ryujinx.Graphics/VDec/BitStreamWriter.cs deleted file mode 100644 index db2d39e59..000000000 --- a/Ryujinx.Graphics/VDec/BitStreamWriter.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.IO; - -namespace Ryujinx.Graphics.VDec -{ - class BitStreamWriter - { - private const int BufferSize = 8; - - private Stream _baseStream; - - private int _buffer; - private int _bufferPos; - - public BitStreamWriter(Stream baseStream) - { - _baseStream = baseStream; - } - - public void WriteBit(bool value) - { - WriteBits(value ? 1 : 0, 1); - } - - public void WriteBits(int value, int valueSize) - { - int valuePos = 0; - - int remaining = valueSize; - - while (remaining > 0) - { - int copySize = remaining; - - int free = GetFreeBufferBits(); - - if (copySize > free) - { - copySize = free; - } - - int mask = (1 << copySize) - 1; - - int srcShift = (valueSize - valuePos) - copySize; - int dstShift = (BufferSize - _bufferPos) - copySize; - - _buffer |= ((value >> srcShift) & mask) << dstShift; - - valuePos += copySize; - _bufferPos += copySize; - remaining -= copySize; - } - } - - private int GetFreeBufferBits() - { - if (_bufferPos == BufferSize) - { - Flush(); - } - - return BufferSize - _bufferPos; - } - - public void Flush() - { - if (_bufferPos != 0) - { - _baseStream.WriteByte((byte)_buffer); - - _buffer = 0; - _bufferPos = 0; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/DecoderHelper.cs b/Ryujinx.Graphics/VDec/DecoderHelper.cs deleted file mode 100644 index 4f17d8d10..000000000 --- a/Ryujinx.Graphics/VDec/DecoderHelper.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; - -namespace Ryujinx.Graphics.VDec -{ - static class DecoderHelper - { - public static byte[] Combine(byte[] arr0, byte[] arr1) - { - byte[] output = new byte[arr0.Length + arr1.Length]; - - Buffer.BlockCopy(arr0, 0, output, 0, arr0.Length); - Buffer.BlockCopy(arr1, 0, output, arr0.Length, arr1.Length); - - return output; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/FFmpeg.cs b/Ryujinx.Graphics/VDec/FFmpeg.cs deleted file mode 100644 index ccd01f0d3..000000000 --- a/Ryujinx.Graphics/VDec/FFmpeg.cs +++ /dev/null @@ -1,168 +0,0 @@ -using FFmpeg.AutoGen; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.VDec -{ - static unsafe class FFmpegWrapper - { - private static AVCodec* _codec; - private static AVCodecContext* _context; - private static AVFrame* _frame; - private static SwsContext* _scalerCtx; - - private static int _scalerWidth; - private static int _scalerHeight; - - public static bool IsInitialized { get; private set; } - - public static void H264Initialize() - { - EnsureCodecInitialized(AVCodecID.AV_CODEC_ID_H264); - } - - public static void Vp9Initialize() - { - EnsureCodecInitialized(AVCodecID.AV_CODEC_ID_VP9); - } - - private static void EnsureCodecInitialized(AVCodecID codecId) - { - if (IsInitialized) - { - Uninitialize(); - } - - _codec = ffmpeg.avcodec_find_decoder(codecId); - _context = ffmpeg.avcodec_alloc_context3(_codec); - _frame = ffmpeg.av_frame_alloc(); - - ffmpeg.avcodec_open2(_context, _codec, null); - - IsInitialized = true; - } - - public static int DecodeFrame(byte[] data) - { - if (!IsInitialized) - { - throw new InvalidOperationException("Tried to use uninitialized codec!"); - } - - AVPacket packet; - - ffmpeg.av_init_packet(&packet); - - fixed (byte* ptr = data) - { - packet.data = ptr; - packet.size = data.Length; - - ffmpeg.avcodec_send_packet(_context, &packet); - } - - return ffmpeg.avcodec_receive_frame(_context, _frame); - } - - public static FFmpegFrame GetFrame() - { - if (!IsInitialized) - { - throw new InvalidOperationException("Tried to use uninitialized codec!"); - } - - AVFrame managedFrame = Marshal.PtrToStructure((IntPtr)_frame); - - byte*[] data = managedFrame.data.ToArray(); - - return new FFmpegFrame() - { - Width = managedFrame.width, - Height = managedFrame.height, - - LumaPtr = data[0], - ChromaBPtr = data[1], - ChromaRPtr = data[2] - }; - } - - public static FFmpegFrame GetFrameRgba() - { - if (!IsInitialized) - { - throw new InvalidOperationException("Tried to use uninitialized codec!"); - } - - AVFrame managedFrame = Marshal.PtrToStructure((IntPtr)_frame); - - EnsureScalerSetup(managedFrame.width, managedFrame.height); - - byte*[] data = managedFrame.data.ToArray(); - - int[] lineSizes = managedFrame.linesize.ToArray(); - - byte[] dst = new byte[managedFrame.width * managedFrame.height * 4]; - - fixed (byte* ptr = dst) - { - byte*[] dstData = new byte*[] { ptr }; - - int[] dstLineSizes = new int[] { managedFrame.width * 4 }; - - ffmpeg.sws_scale(_scalerCtx, data, lineSizes, 0, managedFrame.height, dstData, dstLineSizes); - } - - return new FFmpegFrame() - { - Width = managedFrame.width, - Height = managedFrame.height, - - Data = dst - }; - } - - private static void EnsureScalerSetup(int width, int height) - { - if (width == 0 || height == 0) - { - return; - } - - if (_scalerCtx == null || _scalerWidth != width || _scalerHeight != height) - { - FreeScaler(); - - _scalerCtx = ffmpeg.sws_getContext( - width, height, AVPixelFormat.AV_PIX_FMT_YUV420P, - width, height, AVPixelFormat.AV_PIX_FMT_RGBA, 0, null, null, null); - - _scalerWidth = width; - _scalerHeight = height; - } - } - - public static void Uninitialize() - { - if (IsInitialized) - { - ffmpeg.av_frame_unref(_frame); - ffmpeg.av_free(_frame); - ffmpeg.avcodec_close(_context); - - FreeScaler(); - - IsInitialized = false; - } - } - - private static void FreeScaler() - { - if (_scalerCtx != null) - { - ffmpeg.sws_freeContext(_scalerCtx); - - _scalerCtx = null; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/FFmpegFrame.cs b/Ryujinx.Graphics/VDec/FFmpegFrame.cs deleted file mode 100644 index 535a70c94..000000000 --- a/Ryujinx.Graphics/VDec/FFmpegFrame.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Ryujinx.Graphics.VDec -{ - unsafe struct FFmpegFrame - { - public int Width; - public int Height; - - public byte* LumaPtr; - public byte* ChromaBPtr; - public byte* ChromaRPtr; - - public byte[] Data; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/H264BitStreamWriter.cs b/Ryujinx.Graphics/VDec/H264BitStreamWriter.cs deleted file mode 100644 index b4fad59be..000000000 --- a/Ryujinx.Graphics/VDec/H264BitStreamWriter.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.IO; - -namespace Ryujinx.Graphics.VDec -{ - class H264BitStreamWriter : BitStreamWriter - { - public H264BitStreamWriter(Stream baseStream) : base(baseStream) { } - - public void WriteU(int value, int valueSize) - { - WriteBits(value, valueSize); - } - - public void WriteSe(int value) - { - WriteExpGolombCodedInt(value); - } - - public void WriteUe(int value) - { - WriteExpGolombCodedUInt((uint)value); - } - - public void End() - { - WriteBit(true); - - Flush(); - } - - private void WriteExpGolombCodedInt(int value) - { - int sign = value <= 0 ? 0 : 1; - - if (value < 0) - { - value = -value; - } - - value = (value << 1) - sign; - - WriteExpGolombCodedUInt((uint)value); - } - - private void WriteExpGolombCodedUInt(uint value) - { - int size = 32 - CountLeadingZeros((int)value + 1); - - WriteBits(1, size); - - value -= (1u << (size - 1)) - 1; - - WriteBits((int)value, size - 1); - } - - private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; - - private static int CountLeadingZeros(int value) - { - if (value == 0) - { - return 32; - } - - int nibbleIdx = 32; - int preCount, count = 0; - - do - { - nibbleIdx -= 4; - preCount = ClzNibbleTbl[(value >> nibbleIdx) & 0b1111]; - count += preCount; - } - while (preCount == 4); - - return count; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/H264Decoder.cs b/Ryujinx.Graphics/VDec/H264Decoder.cs deleted file mode 100644 index 24c7e0b92..000000000 --- a/Ryujinx.Graphics/VDec/H264Decoder.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System.IO; - -namespace Ryujinx.Graphics.VDec -{ - class H264Decoder - { - private int _log2MaxPicOrderCntLsbMinus4; - private bool _deltaPicOrderAlwaysZeroFlag; - private bool _frameMbsOnlyFlag; - private int _picWidthInMbs; - private int _picHeightInMapUnits; - private bool _entropyCodingModeFlag; - private bool _bottomFieldPicOrderInFramePresentFlag; - private int _numRefIdxL0DefaultActiveMinus1; - private int _numRefIdxL1DefaultActiveMinus1; - private bool _deblockingFilterControlPresentFlag; - private bool _redundantPicCntPresentFlag; - private bool _transform8x8ModeFlag; - private bool _mbAdaptiveFrameFieldFlag; - private bool _direct8x8InferenceFlag; - private bool _weightedPredFlag; - private bool _constrainedIntraPredFlag; - private bool _fieldPicFlag; - private bool _bottomFieldFlag; - private int _log2MaxFrameNumMinus4; - private int _chromaFormatIdc; - private int _picOrderCntType; - private int _picInitQpMinus26; - private int _chromaQpIndexOffset; - private int _chromaQpIndexOffset2; - private int _weightedBipredIdc; - private int _frameNumber; - private byte[] _scalingMatrix4; - private byte[] _scalingMatrix8; - - public void Decode(H264ParameterSets Params, H264Matrices matrices, byte[] frameData) - { - _log2MaxPicOrderCntLsbMinus4 = Params.Log2MaxPicOrderCntLsbMinus4; - _deltaPicOrderAlwaysZeroFlag = Params.DeltaPicOrderAlwaysZeroFlag; - _frameMbsOnlyFlag = Params.FrameMbsOnlyFlag; - _picWidthInMbs = Params.PicWidthInMbs; - _picHeightInMapUnits = Params.PicHeightInMapUnits; - _entropyCodingModeFlag = Params.EntropyCodingModeFlag; - _bottomFieldPicOrderInFramePresentFlag = Params.BottomFieldPicOrderInFramePresentFlag; - _numRefIdxL0DefaultActiveMinus1 = Params.NumRefIdxL0DefaultActiveMinus1; - _numRefIdxL1DefaultActiveMinus1 = Params.NumRefIdxL1DefaultActiveMinus1; - _deblockingFilterControlPresentFlag = Params.DeblockingFilterControlPresentFlag; - _redundantPicCntPresentFlag = Params.RedundantPicCntPresentFlag; - _transform8x8ModeFlag = Params.Transform8x8ModeFlag; - - _mbAdaptiveFrameFieldFlag = ((Params.Flags >> 0) & 1) != 0; - _direct8x8InferenceFlag = ((Params.Flags >> 1) & 1) != 0; - _weightedPredFlag = ((Params.Flags >> 2) & 1) != 0; - _constrainedIntraPredFlag = ((Params.Flags >> 3) & 1) != 0; - _fieldPicFlag = ((Params.Flags >> 5) & 1) != 0; - _bottomFieldFlag = ((Params.Flags >> 6) & 1) != 0; - - _log2MaxFrameNumMinus4 = (int)(Params.Flags >> 8) & 0xf; - _chromaFormatIdc = (int)(Params.Flags >> 12) & 0x3; - _picOrderCntType = (int)(Params.Flags >> 14) & 0x3; - _picInitQpMinus26 = (int)(Params.Flags >> 16) & 0x3f; - _chromaQpIndexOffset = (int)(Params.Flags >> 22) & 0x1f; - _chromaQpIndexOffset2 = (int)(Params.Flags >> 27) & 0x1f; - _weightedBipredIdc = (int)(Params.Flags >> 32) & 0x3; - _frameNumber = (int)(Params.Flags >> 46) & 0x1ffff; - - _picInitQpMinus26 = (_picInitQpMinus26 << 26) >> 26; - _chromaQpIndexOffset = (_chromaQpIndexOffset << 27) >> 27; - _chromaQpIndexOffset2 = (_chromaQpIndexOffset2 << 27) >> 27; - - _scalingMatrix4 = matrices.ScalingMatrix4; - _scalingMatrix8 = matrices.ScalingMatrix8; - - if (FFmpegWrapper.IsInitialized) - { - FFmpegWrapper.DecodeFrame(frameData); - } - else - { - FFmpegWrapper.H264Initialize(); - - FFmpegWrapper.DecodeFrame(DecoderHelper.Combine(EncodeHeader(), frameData)); - } - } - - private byte[] EncodeHeader() - { - using (MemoryStream data = new MemoryStream()) - { - H264BitStreamWriter writer = new H264BitStreamWriter(data); - - // Sequence Parameter Set. - writer.WriteU(1, 24); - writer.WriteU(0, 1); - writer.WriteU(3, 2); - writer.WriteU(7, 5); - writer.WriteU(100, 8); - writer.WriteU(0, 8); - writer.WriteU(31, 8); - writer.WriteUe(0); - writer.WriteUe(_chromaFormatIdc); - - if (_chromaFormatIdc == 3) - { - writer.WriteBit(false); - } - - writer.WriteUe(0); - writer.WriteUe(0); - writer.WriteBit(false); - writer.WriteBit(false); //Scaling matrix present flag - - writer.WriteUe(_log2MaxFrameNumMinus4); - writer.WriteUe(_picOrderCntType); - - if (_picOrderCntType == 0) - { - writer.WriteUe(_log2MaxPicOrderCntLsbMinus4); - } - else if (_picOrderCntType == 1) - { - writer.WriteBit(_deltaPicOrderAlwaysZeroFlag); - - writer.WriteSe(0); - writer.WriteSe(0); - writer.WriteUe(0); - } - - int picHeightInMbs = _picHeightInMapUnits / (_frameMbsOnlyFlag ? 1 : 2); - - writer.WriteUe(16); - writer.WriteBit(false); - writer.WriteUe(_picWidthInMbs - 1); - writer.WriteUe(picHeightInMbs - 1); - writer.WriteBit(_frameMbsOnlyFlag); - - if (!_frameMbsOnlyFlag) - { - writer.WriteBit(_mbAdaptiveFrameFieldFlag); - } - - writer.WriteBit(_direct8x8InferenceFlag); - writer.WriteBit(false); //Frame cropping flag - writer.WriteBit(false); //VUI parameter present flag - - writer.End(); - - // Picture Parameter Set. - writer.WriteU(1, 24); - writer.WriteU(0, 1); - writer.WriteU(3, 2); - writer.WriteU(8, 5); - - writer.WriteUe(0); - writer.WriteUe(0); - - writer.WriteBit(_entropyCodingModeFlag); - writer.WriteBit(false); - writer.WriteUe(0); - writer.WriteUe(_numRefIdxL0DefaultActiveMinus1); - writer.WriteUe(_numRefIdxL1DefaultActiveMinus1); - writer.WriteBit(_weightedPredFlag); - writer.WriteU(_weightedBipredIdc, 2); - writer.WriteSe(_picInitQpMinus26); - writer.WriteSe(0); - writer.WriteSe(_chromaQpIndexOffset); - writer.WriteBit(_deblockingFilterControlPresentFlag); - writer.WriteBit(_constrainedIntraPredFlag); - writer.WriteBit(_redundantPicCntPresentFlag); - writer.WriteBit(_transform8x8ModeFlag); - - writer.WriteBit(true); - - for (int index = 0; index < 6; index++) - { - writer.WriteBit(true); - - WriteScalingList(writer, _scalingMatrix4, index * 16, 16); - } - - if (_transform8x8ModeFlag) - { - for (int index = 0; index < 2; index++) - { - writer.WriteBit(true); - - WriteScalingList(writer, _scalingMatrix8, index * 64, 64); - } - } - - writer.WriteSe(_chromaQpIndexOffset2); - - writer.End(); - - return data.ToArray(); - } - } - - // ZigZag LUTs from libavcodec. - private static readonly byte[] ZigZagDirect = new byte[] - { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 - }; - - private static readonly byte[] ZigZagScan = new byte[] - { - 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, - 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4, - 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, - 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4 - }; - - private static void WriteScalingList(H264BitStreamWriter writer, byte[] list, int start, int count) - { - byte[] scan = count == 16 ? ZigZagScan : ZigZagDirect; - - int lastScale = 8; - - for (int index = 0; index < count; index++) - { - byte value = list[start + scan[index]]; - - int deltaScale = value - lastScale; - - writer.WriteSe(deltaScale); - - lastScale = value; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/H264Matrices.cs b/Ryujinx.Graphics/VDec/H264Matrices.cs deleted file mode 100644 index a1524214f..000000000 --- a/Ryujinx.Graphics/VDec/H264Matrices.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.Graphics.VDec -{ - struct H264Matrices - { - public byte[] ScalingMatrix4; - public byte[] ScalingMatrix8; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/H264ParameterSets.cs b/Ryujinx.Graphics/VDec/H264ParameterSets.cs deleted file mode 100644 index f242f0f24..000000000 --- a/Ryujinx.Graphics/VDec/H264ParameterSets.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.VDec -{ - [StructLayout(LayoutKind.Sequential, Pack = 4)] - struct H264ParameterSets - { - public int Log2MaxPicOrderCntLsbMinus4; - public bool DeltaPicOrderAlwaysZeroFlag; - public bool FrameMbsOnlyFlag; - public int PicWidthInMbs; - public int PicHeightInMapUnits; - public int Reserved6C; - public bool EntropyCodingModeFlag; - public bool BottomFieldPicOrderInFramePresentFlag; - public int NumRefIdxL0DefaultActiveMinus1; - public int NumRefIdxL1DefaultActiveMinus1; - public bool DeblockingFilterControlPresentFlag; - public bool RedundantPicCntPresentFlag; - public bool Transform8x8ModeFlag; - public int Unknown8C; - public int Unknown90; - public int Reserved94; - public int Unknown98; - public int Reserved9C; - public int ReservedA0; - public int UnknownA4; - public int ReservedA8; - public int UnknownAC; - public long Flags; - public int FrameNumber; - public int FrameNumber2; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/VideoCodec.cs b/Ryujinx.Graphics/VDec/VideoCodec.cs deleted file mode 100644 index f031919dc..000000000 --- a/Ryujinx.Graphics/VDec/VideoCodec.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Graphics.VDec -{ - enum VideoCodec - { - H264 = 3, - Vp8 = 5, - H265 = 7, - Vp9 = 9 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/VideoDecoder.cs b/Ryujinx.Graphics/VDec/VideoDecoder.cs deleted file mode 100644 index 9bf60c31b..000000000 --- a/Ryujinx.Graphics/VDec/VideoDecoder.cs +++ /dev/null @@ -1,281 +0,0 @@ -using ARMeilleure.Memory; -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; -using Ryujinx.Graphics.Texture; -using Ryujinx.Graphics.Vic; -using System; - -namespace Ryujinx.Graphics.VDec -{ - unsafe class VideoDecoder - { - private NvGpu _gpu; - - private H264Decoder _h264Decoder; - private Vp9Decoder _vp9Decoder; - - private VideoCodec _currentVideoCodec; - - private long _decoderContextAddress; - private long _frameDataAddress; - private long _vpxCurrLumaAddress; - private long _vpxRef0LumaAddress; - private long _vpxRef1LumaAddress; - private long _vpxRef2LumaAddress; - private long _vpxCurrChromaAddress; - private long _vpxRef0ChromaAddress; - private long _vpxRef1ChromaAddress; - private long _vpxRef2ChromaAddress; - private long _vpxProbTablesAddress; - - public VideoDecoder(NvGpu gpu) - { - _gpu = gpu; - - _h264Decoder = new H264Decoder(); - _vp9Decoder = new Vp9Decoder(); - } - - public void Process(NvGpuVmm vmm, int methodOffset, int[] arguments) - { - VideoDecoderMeth method = (VideoDecoderMeth)methodOffset; - - switch (method) - { - case VideoDecoderMeth.SetVideoCodec: SetVideoCodec (vmm, arguments); break; - case VideoDecoderMeth.Execute: Execute (vmm, arguments); break; - case VideoDecoderMeth.SetDecoderCtxAddr: SetDecoderCtxAddr (vmm, arguments); break; - case VideoDecoderMeth.SetFrameDataAddr: SetFrameDataAddr (vmm, arguments); break; - case VideoDecoderMeth.SetVpxCurrLumaAddr: SetVpxCurrLumaAddr (vmm, arguments); break; - case VideoDecoderMeth.SetVpxRef0LumaAddr: SetVpxRef0LumaAddr (vmm, arguments); break; - case VideoDecoderMeth.SetVpxRef1LumaAddr: SetVpxRef1LumaAddr (vmm, arguments); break; - case VideoDecoderMeth.SetVpxRef2LumaAddr: SetVpxRef2LumaAddr (vmm, arguments); break; - case VideoDecoderMeth.SetVpxCurrChromaAddr: SetVpxCurrChromaAddr(vmm, arguments); break; - case VideoDecoderMeth.SetVpxRef0ChromaAddr: SetVpxRef0ChromaAddr(vmm, arguments); break; - case VideoDecoderMeth.SetVpxRef1ChromaAddr: SetVpxRef1ChromaAddr(vmm, arguments); break; - case VideoDecoderMeth.SetVpxRef2ChromaAddr: SetVpxRef2ChromaAddr(vmm, arguments); break; - case VideoDecoderMeth.SetVpxProbTablesAddr: SetVpxProbTablesAddr(vmm, arguments); break; - } - } - - private void SetVideoCodec(NvGpuVmm vmm, int[] arguments) - { - _currentVideoCodec = (VideoCodec)arguments[0]; - } - - private void Execute(NvGpuVmm vmm, int[] arguments) - { - if (_currentVideoCodec == VideoCodec.H264) - { - int frameDataSize = vmm.ReadInt32(_decoderContextAddress + 0x48); - - H264ParameterSets Params = MemoryHelper.Read(vmm.Memory, vmm.GetPhysicalAddress(_decoderContextAddress + 0x58)); - - H264Matrices matrices = new H264Matrices() - { - ScalingMatrix4 = vmm.ReadBytes(_decoderContextAddress + 0x1c0, 6 * 16), - ScalingMatrix8 = vmm.ReadBytes(_decoderContextAddress + 0x220, 2 * 64) - }; - - byte[] frameData = vmm.ReadBytes(_frameDataAddress, frameDataSize); - - _h264Decoder.Decode(Params, matrices, frameData); - } - else if (_currentVideoCodec == VideoCodec.Vp9) - { - int frameDataSize = vmm.ReadInt32(_decoderContextAddress + 0x30); - - Vp9FrameKeys keys = new Vp9FrameKeys() - { - CurrKey = vmm.GetPhysicalAddress(_vpxCurrLumaAddress), - Ref0Key = vmm.GetPhysicalAddress(_vpxRef0LumaAddress), - Ref1Key = vmm.GetPhysicalAddress(_vpxRef1LumaAddress), - Ref2Key = vmm.GetPhysicalAddress(_vpxRef2LumaAddress) - }; - - Vp9FrameHeader header = MemoryHelper.Read(vmm.Memory, vmm.GetPhysicalAddress(_decoderContextAddress + 0x48)); - - Vp9ProbabilityTables probs = new Vp9ProbabilityTables() - { - SegmentationTreeProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x387, 0x7), - SegmentationPredProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x38e, 0x3), - Tx8x8Probs = vmm.ReadBytes(_vpxProbTablesAddress + 0x470, 0x2), - Tx16x16Probs = vmm.ReadBytes(_vpxProbTablesAddress + 0x472, 0x4), - Tx32x32Probs = vmm.ReadBytes(_vpxProbTablesAddress + 0x476, 0x6), - CoefProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x5a0, 0x900), - SkipProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x537, 0x3), - InterModeProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x400, 0x1c), - InterpFilterProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x52a, 0x8), - IsInterProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x41c, 0x4), - CompModeProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x532, 0x5), - SingleRefProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x580, 0xa), - CompRefProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x58a, 0x5), - YModeProbs0 = vmm.ReadBytes(_vpxProbTablesAddress + 0x480, 0x20), - YModeProbs1 = vmm.ReadBytes(_vpxProbTablesAddress + 0x47c, 0x4), - PartitionProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x4e0, 0x40), - MvJointProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x53b, 0x3), - MvSignProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x53e, 0x3), - MvClassProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x54c, 0x14), - MvClass0BitProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x540, 0x3), - MvBitsProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x56c, 0x14), - MvClass0FrProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x560, 0xc), - MvFrProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x542, 0x6), - MvClass0HpProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x548, 0x2), - MvHpProbs = vmm.ReadBytes(_vpxProbTablesAddress + 0x54a, 0x2) - }; - - byte[] frameData = vmm.ReadBytes(_frameDataAddress, frameDataSize); - - _vp9Decoder.Decode(keys, header, probs, frameData); - } - else - { - ThrowUnimplementedCodec(); - } - } - - private void SetDecoderCtxAddr(NvGpuVmm vmm, int[] arguments) - { - _decoderContextAddress = GetAddress(arguments); - } - - private void SetFrameDataAddr(NvGpuVmm vmm, int[] arguments) - { - _frameDataAddress = GetAddress(arguments); - } - - private void SetVpxCurrLumaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxCurrLumaAddress = GetAddress(arguments); - } - - private void SetVpxRef0LumaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxRef0LumaAddress = GetAddress(arguments); - } - - private void SetVpxRef1LumaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxRef1LumaAddress = GetAddress(arguments); - } - - private void SetVpxRef2LumaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxRef2LumaAddress = GetAddress(arguments); - } - - private void SetVpxCurrChromaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxCurrChromaAddress = GetAddress(arguments); - } - - private void SetVpxRef0ChromaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxRef0ChromaAddress = GetAddress(arguments); - } - - private void SetVpxRef1ChromaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxRef1ChromaAddress = GetAddress(arguments); - } - - private void SetVpxRef2ChromaAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxRef2ChromaAddress = GetAddress(arguments); - } - - private void SetVpxProbTablesAddr(NvGpuVmm vmm, int[] arguments) - { - _vpxProbTablesAddress = GetAddress(arguments); - } - - private static long GetAddress(int[] arguments) - { - return (long)(uint)arguments[0] << 8; - } - - internal void CopyPlanes(NvGpuVmm vmm, SurfaceOutputConfig outputConfig) - { - switch (outputConfig.PixelFormat) - { - case SurfacePixelFormat.Rgba8: CopyPlanesRgba8 (vmm, outputConfig); break; - case SurfacePixelFormat.Yuv420P: CopyPlanesYuv420P(vmm, outputConfig); break; - - default: ThrowUnimplementedPixelFormat(outputConfig.PixelFormat); break; - } - } - - private void CopyPlanesRgba8(NvGpuVmm vmm, SurfaceOutputConfig outputConfig) - { - FFmpegFrame frame = FFmpegWrapper.GetFrameRgba(); - - if ((frame.Width | frame.Height) == 0) - { - return; - } - - GalImage image = new GalImage( - outputConfig.SurfaceWidth, - outputConfig.SurfaceHeight, 1, 1, 1, - outputConfig.GobBlockHeight, 1, - GalMemoryLayout.BlockLinear, - GalImageFormat.Rgba8 | GalImageFormat.Unorm, - GalTextureTarget.TwoD); - - ImageUtils.WriteTexture(vmm, image, vmm.GetPhysicalAddress(outputConfig.SurfaceLumaAddress), frame.Data); - } - - private void CopyPlanesYuv420P(NvGpuVmm vmm, SurfaceOutputConfig outputConfig) - { - FFmpegFrame frame = FFmpegWrapper.GetFrame(); - - if ((frame.Width | frame.Height) == 0) - { - return; - } - - int halfSrcWidth = frame.Width / 2; - - int halfWidth = frame.Width / 2; - int halfHeight = frame.Height / 2; - - int alignedWidth = (outputConfig.SurfaceWidth + 0xff) & ~0xff; - - for (int y = 0; y < frame.Height; y++) - { - int src = y * frame.Width; - int dst = y * alignedWidth; - - int size = frame.Width; - - for (int offset = 0; offset < size; offset++) - { - vmm.WriteByte(outputConfig.SurfaceLumaAddress + dst + offset, *(frame.LumaPtr + src + offset)); - } - } - - // Copy chroma data from both channels with interleaving. - for (int y = 0; y < halfHeight; y++) - { - int src = y * halfSrcWidth; - int dst = y * alignedWidth; - - for (int x = 0; x < halfWidth; x++) - { - vmm.WriteByte(outputConfig.SurfaceChromaUAddress + dst + x * 2 + 0, *(frame.ChromaBPtr + src + x)); - vmm.WriteByte(outputConfig.SurfaceChromaUAddress + dst + x * 2 + 1, *(frame.ChromaRPtr + src + x)); - } - } - } - - private void ThrowUnimplementedCodec() - { - throw new NotImplementedException("Codec \"" + _currentVideoCodec + "\" is not supported!"); - } - - private void ThrowUnimplementedPixelFormat(SurfacePixelFormat pixelFormat) - { - throw new NotImplementedException("Pixel format \"" + pixelFormat + "\" is not supported!"); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/VideoDecoderMeth.cs b/Ryujinx.Graphics/VDec/VideoDecoderMeth.cs deleted file mode 100644 index 12286386a..000000000 --- a/Ryujinx.Graphics/VDec/VideoDecoderMeth.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Ryujinx.Graphics.VDec -{ - enum VideoDecoderMeth - { - SetVideoCodec = 0x80, - Execute = 0xc0, - SetDecoderCtxAddr = 0x101, - SetFrameDataAddr = 0x102, - SetVpxRef0LumaAddr = 0x10c, - SetVpxRef1LumaAddr = 0x10d, - SetVpxRef2LumaAddr = 0x10e, - SetVpxCurrLumaAddr = 0x10f, - SetVpxRef0ChromaAddr = 0x11d, - SetVpxRef1ChromaAddr = 0x11e, - SetVpxRef2ChromaAddr = 0x11f, - SetVpxCurrChromaAddr = 0x120, - SetVpxProbTablesAddr = 0x170 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/Vp9Decoder.cs b/Ryujinx.Graphics/VDec/Vp9Decoder.cs deleted file mode 100644 index b20a40bed..000000000 --- a/Ryujinx.Graphics/VDec/Vp9Decoder.cs +++ /dev/null @@ -1,879 +0,0 @@ -using System.Collections.Generic; -using System.IO; - -namespace Ryujinx.Graphics.VDec -{ - class Vp9Decoder - { - private const int DiffUpdateProbability = 252; - - private const int FrameSyncCode = 0x498342; - - private static readonly int[] MapLut = new int[] - { - 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, - 2, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 4, 74, 75, 76, 77, 78, - 79, 80, 81, 82, 83, 84, 85, 5, 86, 87, 88, 89, 90, 91, 92, 93, - 94, 95, 96, 97, 6, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, - 109, 7, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, - 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 9, 134, 135, 136, 137, - 138, 139, 140, 141, 142, 143, 144, 145, 10, 146, 147, 148, 149, 150, 151, 152, - 153, 154, 155, 156, 157, 11, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, - 168, 169, 12, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 13, - 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 14, 194, 195, 196, - 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207, 208, 209, 210, 211, - 212, 213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 229, 17, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, - 18, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 19 - }; - - private byte[] DefaultTx8x8Probs = new byte[] { 100, 66 }; - private byte[] DefaultTx16x16Probs = new byte[] { 20, 152, 15, 101 }; - private byte[] DefaultTx32x32Probs = new byte[] { 3, 136, 37, 5, 52, 13 }; - - private byte[] _defaultCoefProbs = new byte[] - { - 195, 29, 183, 0, 84, 49, 136, 0, 8, 42, 71, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 31, 107, 169, 0, 35, 99, 159, 0, - 17, 82, 140, 0, 8, 66, 114, 0, 2, 44, 76, 0, 1, 19, 32, 0, - 40, 132, 201, 0, 29, 114, 187, 0, 13, 91, 157, 0, 7, 75, 127, 0, - 3, 58, 95, 0, 1, 28, 47, 0, 69, 142, 221, 0, 42, 122, 201, 0, - 15, 91, 159, 0, 6, 67, 121, 0, 1, 42, 77, 0, 1, 17, 31, 0, - 102, 148, 228, 0, 67, 117, 204, 0, 17, 82, 154, 0, 6, 59, 114, 0, - 2, 39, 75, 0, 1, 15, 29, 0, 156, 57, 233, 0, 119, 57, 212, 0, - 58, 48, 163, 0, 29, 40, 124, 0, 12, 30, 81, 0, 3, 12, 31, 0, - 191, 107, 226, 0, 124, 117, 204, 0, 25, 99, 155, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 29, 148, 210, 0, 37, 126, 194, 0, - 8, 93, 157, 0, 2, 68, 118, 0, 1, 39, 69, 0, 1, 17, 33, 0, - 41, 151, 213, 0, 27, 123, 193, 0, 3, 82, 144, 0, 1, 58, 105, 0, - 1, 32, 60, 0, 1, 13, 26, 0, 59, 159, 220, 0, 23, 126, 198, 0, - 4, 88, 151, 0, 1, 66, 114, 0, 1, 38, 71, 0, 1, 18, 34, 0, - 114, 136, 232, 0, 51, 114, 207, 0, 11, 83, 155, 0, 3, 56, 105, 0, - 1, 33, 65, 0, 1, 17, 34, 0, 149, 65, 234, 0, 121, 57, 215, 0, - 61, 49, 166, 0, 28, 36, 114, 0, 12, 25, 76, 0, 3, 16, 42, 0, - 214, 49, 220, 0, 132, 63, 188, 0, 42, 65, 137, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 85, 137, 221, 0, 104, 131, 216, 0, - 49, 111, 192, 0, 21, 87, 155, 0, 2, 49, 87, 0, 1, 16, 28, 0, - 89, 163, 230, 0, 90, 137, 220, 0, 29, 100, 183, 0, 10, 70, 135, 0, - 2, 42, 81, 0, 1, 17, 33, 0, 108, 167, 237, 0, 55, 133, 222, 0, - 15, 97, 179, 0, 4, 72, 135, 0, 1, 45, 85, 0, 1, 19, 38, 0, - 124, 146, 240, 0, 66, 124, 224, 0, 17, 88, 175, 0, 4, 58, 122, 0, - 1, 36, 75, 0, 1, 18, 37, 0, 141, 79, 241, 0, 126, 70, 227, 0, - 66, 58, 182, 0, 30, 44, 136, 0, 12, 34, 96, 0, 2, 20, 47, 0, - 229, 99, 249, 0, 143, 111, 235, 0, 46, 109, 192, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 82, 158, 236, 0, 94, 146, 224, 0, - 25, 117, 191, 0, 9, 87, 149, 0, 3, 56, 99, 0, 1, 33, 57, 0, - 83, 167, 237, 0, 68, 145, 222, 0, 10, 103, 177, 0, 2, 72, 131, 0, - 1, 41, 79, 0, 1, 20, 39, 0, 99, 167, 239, 0, 47, 141, 224, 0, - 10, 104, 178, 0, 2, 73, 133, 0, 1, 44, 85, 0, 1, 22, 47, 0, - 127, 145, 243, 0, 71, 129, 228, 0, 17, 93, 177, 0, 3, 61, 124, 0, - 1, 41, 84, 0, 1, 21, 52, 0, 157, 78, 244, 0, 140, 72, 231, 0, - 69, 58, 184, 0, 31, 44, 137, 0, 14, 38, 105, 0, 8, 23, 61, 0, - 125, 34, 187, 0, 52, 41, 133, 0, 6, 31, 56, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 37, 109, 153, 0, 51, 102, 147, 0, - 23, 87, 128, 0, 8, 67, 101, 0, 1, 41, 63, 0, 1, 19, 29, 0, - 31, 154, 185, 0, 17, 127, 175, 0, 6, 96, 145, 0, 2, 73, 114, 0, - 1, 51, 82, 0, 1, 28, 45, 0, 23, 163, 200, 0, 10, 131, 185, 0, - 2, 93, 148, 0, 1, 67, 111, 0, 1, 41, 69, 0, 1, 14, 24, 0, - 29, 176, 217, 0, 12, 145, 201, 0, 3, 101, 156, 0, 1, 69, 111, 0, - 1, 39, 63, 0, 1, 14, 23, 0, 57, 192, 233, 0, 25, 154, 215, 0, - 6, 109, 167, 0, 3, 78, 118, 0, 1, 48, 69, 0, 1, 21, 29, 0, - 202, 105, 245, 0, 108, 106, 216, 0, 18, 90, 144, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 172, 219, 0, 64, 149, 206, 0, - 14, 117, 177, 0, 5, 90, 141, 0, 2, 61, 95, 0, 1, 37, 57, 0, - 33, 179, 220, 0, 11, 140, 198, 0, 1, 89, 148, 0, 1, 60, 104, 0, - 1, 33, 57, 0, 1, 12, 21, 0, 30, 181, 221, 0, 8, 141, 198, 0, - 1, 87, 145, 0, 1, 58, 100, 0, 1, 31, 55, 0, 1, 12, 20, 0, - 32, 186, 224, 0, 7, 142, 198, 0, 1, 86, 143, 0, 1, 58, 100, 0, - 1, 31, 55, 0, 1, 12, 22, 0, 57, 192, 227, 0, 20, 143, 204, 0, - 3, 96, 154, 0, 1, 68, 112, 0, 1, 42, 69, 0, 1, 19, 32, 0, - 212, 35, 215, 0, 113, 47, 169, 0, 29, 48, 105, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 74, 129, 203, 0, 106, 120, 203, 0, - 49, 107, 178, 0, 19, 84, 144, 0, 4, 50, 84, 0, 1, 15, 25, 0, - 71, 172, 217, 0, 44, 141, 209, 0, 15, 102, 173, 0, 6, 76, 133, 0, - 2, 51, 89, 0, 1, 24, 42, 0, 64, 185, 231, 0, 31, 148, 216, 0, - 8, 103, 175, 0, 3, 74, 131, 0, 1, 46, 81, 0, 1, 18, 30, 0, - 65, 196, 235, 0, 25, 157, 221, 0, 5, 105, 174, 0, 1, 67, 120, 0, - 1, 38, 69, 0, 1, 15, 30, 0, 65, 204, 238, 0, 30, 156, 224, 0, - 7, 107, 177, 0, 2, 70, 124, 0, 1, 42, 73, 0, 1, 18, 34, 0, - 225, 86, 251, 0, 144, 104, 235, 0, 42, 99, 181, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 85, 175, 239, 0, 112, 165, 229, 0, - 29, 136, 200, 0, 12, 103, 162, 0, 6, 77, 123, 0, 2, 53, 84, 0, - 75, 183, 239, 0, 30, 155, 221, 0, 3, 106, 171, 0, 1, 74, 128, 0, - 1, 44, 76, 0, 1, 17, 28, 0, 73, 185, 240, 0, 27, 159, 222, 0, - 2, 107, 172, 0, 1, 75, 127, 0, 1, 42, 73, 0, 1, 17, 29, 0, - 62, 190, 238, 0, 21, 159, 222, 0, 2, 107, 172, 0, 1, 72, 122, 0, - 1, 40, 71, 0, 1, 18, 32, 0, 61, 199, 240, 0, 27, 161, 226, 0, - 4, 113, 180, 0, 1, 76, 129, 0, 1, 46, 80, 0, 1, 23, 41, 0, - 7, 27, 153, 0, 5, 30, 95, 0, 1, 16, 30, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 50, 75, 127, 0, 57, 75, 124, 0, - 27, 67, 108, 0, 10, 54, 86, 0, 1, 33, 52, 0, 1, 12, 18, 0, - 43, 125, 151, 0, 26, 108, 148, 0, 7, 83, 122, 0, 2, 59, 89, 0, - 1, 38, 60, 0, 1, 17, 27, 0, 23, 144, 163, 0, 13, 112, 154, 0, - 2, 75, 117, 0, 1, 50, 81, 0, 1, 31, 51, 0, 1, 14, 23, 0, - 18, 162, 185, 0, 6, 123, 171, 0, 1, 78, 125, 0, 1, 51, 86, 0, - 1, 31, 54, 0, 1, 14, 23, 0, 15, 199, 227, 0, 3, 150, 204, 0, - 1, 91, 146, 0, 1, 55, 95, 0, 1, 30, 53, 0, 1, 11, 20, 0, - 19, 55, 240, 0, 19, 59, 196, 0, 3, 52, 105, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 41, 166, 207, 0, 104, 153, 199, 0, - 31, 123, 181, 0, 14, 101, 152, 0, 5, 72, 106, 0, 1, 36, 52, 0, - 35, 176, 211, 0, 12, 131, 190, 0, 2, 88, 144, 0, 1, 60, 101, 0, - 1, 36, 60, 0, 1, 16, 28, 0, 28, 183, 213, 0, 8, 134, 191, 0, - 1, 86, 142, 0, 1, 56, 96, 0, 1, 30, 53, 0, 1, 12, 20, 0, - 20, 190, 215, 0, 4, 135, 192, 0, 1, 84, 139, 0, 1, 53, 91, 0, - 1, 28, 49, 0, 1, 11, 20, 0, 13, 196, 216, 0, 2, 137, 192, 0, - 1, 86, 143, 0, 1, 57, 99, 0, 1, 32, 56, 0, 1, 13, 24, 0, - 211, 29, 217, 0, 96, 47, 156, 0, 22, 43, 87, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 78, 120, 193, 0, 111, 116, 186, 0, - 46, 102, 164, 0, 15, 80, 128, 0, 2, 49, 76, 0, 1, 18, 28, 0, - 71, 161, 203, 0, 42, 132, 192, 0, 10, 98, 150, 0, 3, 69, 109, 0, - 1, 44, 70, 0, 1, 18, 29, 0, 57, 186, 211, 0, 30, 140, 196, 0, - 4, 93, 146, 0, 1, 62, 102, 0, 1, 38, 65, 0, 1, 16, 27, 0, - 47, 199, 217, 0, 14, 145, 196, 0, 1, 88, 142, 0, 1, 57, 98, 0, - 1, 36, 62, 0, 1, 15, 26, 0, 26, 219, 229, 0, 5, 155, 207, 0, - 1, 94, 151, 0, 1, 60, 104, 0, 1, 36, 62, 0, 1, 16, 28, 0, - 233, 29, 248, 0, 146, 47, 220, 0, 43, 52, 140, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 100, 163, 232, 0, 179, 161, 222, 0, - 63, 142, 204, 0, 37, 113, 174, 0, 26, 89, 137, 0, 18, 68, 97, 0, - 85, 181, 230, 0, 32, 146, 209, 0, 7, 100, 164, 0, 3, 71, 121, 0, - 1, 45, 77, 0, 1, 18, 30, 0, 65, 187, 230, 0, 20, 148, 207, 0, - 2, 97, 159, 0, 1, 68, 116, 0, 1, 40, 70, 0, 1, 14, 29, 0, - 40, 194, 227, 0, 8, 147, 204, 0, 1, 94, 155, 0, 1, 65, 112, 0, - 1, 39, 66, 0, 1, 14, 26, 0, 16, 208, 228, 0, 3, 151, 207, 0, - 1, 98, 160, 0, 1, 67, 117, 0, 1, 41, 74, 0, 1, 17, 31, 0, - 17, 38, 140, 0, 7, 34, 80, 0, 1, 17, 29, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 37, 75, 128, 0, 41, 76, 128, 0, - 26, 66, 116, 0, 12, 52, 94, 0, 2, 32, 55, 0, 1, 10, 16, 0, - 50, 127, 154, 0, 37, 109, 152, 0, 16, 82, 121, 0, 5, 59, 85, 0, - 1, 35, 54, 0, 1, 13, 20, 0, 40, 142, 167, 0, 17, 110, 157, 0, - 2, 71, 112, 0, 1, 44, 72, 0, 1, 27, 45, 0, 1, 11, 17, 0, - 30, 175, 188, 0, 9, 124, 169, 0, 1, 74, 116, 0, 1, 48, 78, 0, - 1, 30, 49, 0, 1, 11, 18, 0, 10, 222, 223, 0, 2, 150, 194, 0, - 1, 83, 128, 0, 1, 48, 79, 0, 1, 27, 45, 0, 1, 11, 17, 0, - 36, 41, 235, 0, 29, 36, 193, 0, 10, 27, 111, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 85, 165, 222, 0, 177, 162, 215, 0, - 110, 135, 195, 0, 57, 113, 168, 0, 23, 83, 120, 0, 10, 49, 61, 0, - 85, 190, 223, 0, 36, 139, 200, 0, 5, 90, 146, 0, 1, 60, 103, 0, - 1, 38, 65, 0, 1, 18, 30, 0, 72, 202, 223, 0, 23, 141, 199, 0, - 2, 86, 140, 0, 1, 56, 97, 0, 1, 36, 61, 0, 1, 16, 27, 0, - 55, 218, 225, 0, 13, 145, 200, 0, 1, 86, 141, 0, 1, 57, 99, 0, - 1, 35, 61, 0, 1, 13, 22, 0, 15, 235, 212, 0, 1, 132, 184, 0, - 1, 84, 139, 0, 1, 57, 97, 0, 1, 34, 56, 0, 1, 14, 23, 0, - 181, 21, 201, 0, 61, 37, 123, 0, 10, 38, 71, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 47, 106, 172, 0, 95, 104, 173, 0, - 42, 93, 159, 0, 18, 77, 131, 0, 4, 50, 81, 0, 1, 17, 23, 0, - 62, 147, 199, 0, 44, 130, 189, 0, 28, 102, 154, 0, 18, 75, 115, 0, - 2, 44, 65, 0, 1, 12, 19, 0, 55, 153, 210, 0, 24, 130, 194, 0, - 3, 93, 146, 0, 1, 61, 97, 0, 1, 31, 50, 0, 1, 10, 16, 0, - 49, 186, 223, 0, 17, 148, 204, 0, 1, 96, 142, 0, 1, 53, 83, 0, - 1, 26, 44, 0, 1, 11, 17, 0, 13, 217, 212, 0, 2, 136, 180, 0, - 1, 78, 124, 0, 1, 50, 83, 0, 1, 29, 49, 0, 1, 14, 23, 0, - 197, 13, 247, 0, 82, 17, 222, 0, 25, 17, 162, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 126, 186, 247, 0, 234, 191, 243, 0, - 176, 177, 234, 0, 104, 158, 220, 0, 66, 128, 186, 0, 55, 90, 137, 0, - 111, 197, 242, 0, 46, 158, 219, 0, 9, 104, 171, 0, 2, 65, 125, 0, - 1, 44, 80, 0, 1, 17, 91, 0, 104, 208, 245, 0, 39, 168, 224, 0, - 3, 109, 162, 0, 1, 79, 124, 0, 1, 50, 102, 0, 1, 43, 102, 0, - 84, 220, 246, 0, 31, 177, 231, 0, 2, 115, 180, 0, 1, 79, 134, 0, - 1, 55, 77, 0, 1, 60, 79, 0, 43, 243, 240, 0, 8, 180, 217, 0, - 1, 115, 166, 0, 1, 84, 121, 0, 1, 51, 67, 0, 1, 16, 6, 0 - }; - - private byte[] _defaultSkipProbs = new byte[] { 192, 128, 64 }; - - private byte[] _defaultInterModeProbs = new byte[] - { - 2, 173, 34, 0, 7, 145, 85, 0, 7, 166, 63, 0, 7, 94, 66, 0, - 8, 64, 46, 0, 17, 81, 31, 0, 25, 29, 30, 0 - }; - - private byte[] _defaultInterpFilterProbs = new byte[] - { - 235, 162, 36, 255, 34, 3, 149, 144 - }; - - private byte[] _defaultIsInterProbs = new byte[] { 9, 102, 187, 225 }; - - private byte[] _defaultCompModeProbs = new byte[] { 239, 183, 119, 96, 41 }; - - private byte[] _defaultSingleRefProbs = new byte[] - { - 33, 16, 77, 74, 142, 142, 172, 170, 238, 247 - }; - - private byte[] _defaultCompRefProbs = new byte[] { 50, 126, 123, 221, 226 }; - - private byte[] _defaultYModeProbs0 = new byte[] - { - 65, 32, 18, 144, 162, 194, 41, 51, 132, 68, 18, 165, 217, 196, 45, 40, - 173, 80, 19, 176, 240, 193, 64, 35, 221, 135, 38, 194, 248, 121, 96, 85 - }; - - private byte[] _defaultYModeProbs1 = new byte[] { 98, 78, 46, 29 }; - - private byte[] _defaultPartitionProbs = new byte[] - { - 199, 122, 141, 0, 147, 63, 159, 0, 148, 133, 118, 0, 121, 104, 114, 0, - 174, 73, 87, 0, 92, 41, 83, 0, 82, 99, 50, 0, 53, 39, 39, 0, - 177, 58, 59, 0, 68, 26, 63, 0, 52, 79, 25, 0, 17, 14, 12, 0, - 222, 34, 30, 0, 72, 16, 44, 0, 58, 32, 12, 0, 10, 7, 6, 0 - }; - - private byte[] _defaultMvJointProbs = new byte[] { 32, 64, 96 }; - - private byte[] _defaultMvSignProbs = new byte[] { 128, 128 }; - - private byte[] _defaultMvClassProbs = new byte[] - { - 224, 144, 192, 168, 192, 176, 192, 198, 198, 245, 216, 128, 176, 160, 176, 176, - 192, 198, 198, 208 - }; - - private byte[] _defaultMvClass0BitProbs = new byte[] { 216, 208 }; - - private byte[] _defaultMvBitsProbs = new byte[] - { - 136, 140, 148, 160, 176, 192, 224, 234, 234, 240, 136, 140, 148, 160, 176, 192, - 224, 234, 234, 240 - }; - - private byte[] _defaultMvClass0FrProbs = new byte[] - { - 128, 128, 64, 96, 112, 64, 128, 128, 64, 96, 112, 64 - }; - - private byte[] _defaultMvFrProbs = new byte[] { 64, 96, 64, 64, 96, 64 }; - - private byte[] _defaultMvClass0HpProbs = new byte[] { 160, 160 }; - - private byte[] _defaultMvHpProbs = new byte[] { 128, 128 }; - - private sbyte[] _loopFilterRefDeltas; - private sbyte[] _loopFilterModeDeltas; - - private LinkedList _frameSlotByLastUse; - - private Dictionary> _cachedRefFrames; - - public Vp9Decoder() - { - _loopFilterRefDeltas = new sbyte[4]; - _loopFilterModeDeltas = new sbyte[2]; - - _frameSlotByLastUse = new LinkedList(); - - for (int slot = 0; slot < 8; slot++) - { - _frameSlotByLastUse.AddFirst(slot); - } - - _cachedRefFrames = new Dictionary>(); - } - - public void Decode( - Vp9FrameKeys keys, - Vp9FrameHeader header, - Vp9ProbabilityTables probs, - byte[] frameData) - { - bool isKeyFrame = ((header.Flags >> 0) & 1) != 0; - bool lastIsKeyFrame = ((header.Flags >> 1) & 1) != 0; - bool frameSizeChanged = ((header.Flags >> 2) & 1) != 0; - bool errorResilientMode = ((header.Flags >> 3) & 1) != 0; - bool lastShowFrame = ((header.Flags >> 4) & 1) != 0; - bool isFrameIntra = ((header.Flags >> 5) & 1) != 0; - - bool showFrame = !isFrameIntra; - - // Write compressed header. - byte[] compressedHeaderData; - - using (MemoryStream compressedHeader = new MemoryStream()) - { - VpxRangeEncoder writer = new VpxRangeEncoder(compressedHeader); - - if (!header.Lossless) - { - if ((uint)header.TxMode >= 3) - { - writer.Write(3, 2); - writer.Write(header.TxMode == 4); - } - else - { - writer.Write(header.TxMode, 2); - } - } - - if (header.TxMode == 4) - { - WriteProbabilityUpdate(writer, probs.Tx8x8Probs, DefaultTx8x8Probs); - WriteProbabilityUpdate(writer, probs.Tx16x16Probs, DefaultTx16x16Probs); - WriteProbabilityUpdate(writer, probs.Tx32x32Probs, DefaultTx32x32Probs); - } - - WriteCoefProbabilityUpdate(writer, header.TxMode, probs.CoefProbs, _defaultCoefProbs); - - WriteProbabilityUpdate(writer, probs.SkipProbs, _defaultSkipProbs); - - if (!isFrameIntra) - { - WriteProbabilityUpdateAligned4(writer, probs.InterModeProbs, _defaultInterModeProbs); - - if (header.RawInterpolationFilter == 4) - { - WriteProbabilityUpdate(writer, probs.InterpFilterProbs, _defaultInterpFilterProbs); - } - - WriteProbabilityUpdate(writer, probs.IsInterProbs, _defaultIsInterProbs); - - if ((header.RefFrameSignBias[1] & 1) != (header.RefFrameSignBias[2] & 1) || - (header.RefFrameSignBias[1] & 1) != (header.RefFrameSignBias[3] & 1)) - { - if ((uint)header.CompPredMode >= 1) - { - writer.Write(1, 1); - writer.Write(header.CompPredMode == 2); - } - else - { - writer.Write(0, 1); - } - } - - if (header.CompPredMode == 2) - { - WriteProbabilityUpdate(writer, probs.CompModeProbs, _defaultCompModeProbs); - } - - if (header.CompPredMode != 1) - { - WriteProbabilityUpdate(writer, probs.SingleRefProbs, _defaultSingleRefProbs); - } - - if (header.CompPredMode != 0) - { - WriteProbabilityUpdate(writer, probs.CompRefProbs, _defaultCompRefProbs); - } - - for (int index = 0; index < 4; index++) - { - int i = index * 8; - int j = index; - - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 0], _defaultYModeProbs0[i + 0]); - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 1], _defaultYModeProbs0[i + 1]); - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 2], _defaultYModeProbs0[i + 2]); - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 3], _defaultYModeProbs0[i + 3]); - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 4], _defaultYModeProbs0[i + 4]); - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 5], _defaultYModeProbs0[i + 5]); - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 6], _defaultYModeProbs0[i + 6]); - WriteProbabilityUpdate(writer, probs.YModeProbs0[i + 7], _defaultYModeProbs0[i + 7]); - WriteProbabilityUpdate(writer, probs.YModeProbs1[j + 0], _defaultYModeProbs1[j + 0]); - } - - WriteProbabilityUpdateAligned4(writer, probs.PartitionProbs, _defaultPartitionProbs); - - for (int i = 0; i < 3; i++) - { - WriteMvProbabilityUpdate(writer, probs.MvJointProbs[i], _defaultMvJointProbs[i]); - } - - for (int i = 0; i < 2; i++) - { - WriteMvProbabilityUpdate(writer, probs.MvSignProbs[i], _defaultMvSignProbs[i]); - - for (int j = 0; j < 10; j++) - { - int index = i * 10 + j; - - WriteMvProbabilityUpdate(writer, probs.MvClassProbs[index], _defaultMvClassProbs[index]); - } - - WriteMvProbabilityUpdate(writer, probs.MvClass0BitProbs[i], _defaultMvClass0BitProbs[i]); - - for (int j = 0; j < 10; j++) - { - int index = i * 10 + j; - - WriteMvProbabilityUpdate(writer, probs.MvBitsProbs[index], _defaultMvBitsProbs[index]); - } - } - - for (int i = 0; i < 2; i++) - { - for (int j = 0; j < 2; j++) - { - for (int k = 0; k < 3; k++) - { - int index = i * 2 * 3 + j * 3 + k; - - WriteMvProbabilityUpdate(writer, probs.MvClass0FrProbs[index], _defaultMvClass0FrProbs[index]); - } - } - - for (int j = 0; j < 3; j++) - { - int index = i * 3 + j; - - WriteMvProbabilityUpdate(writer, probs.MvFrProbs[index], _defaultMvFrProbs[index]); - } - } - - if (header.AllowHighPrecisionMv) - { - for (int index = 0; index < 2; index++) - { - WriteMvProbabilityUpdate(writer, probs.MvClass0HpProbs[index], _defaultMvClass0HpProbs[index]); - WriteMvProbabilityUpdate(writer, probs.MvHpProbs[index], _defaultMvHpProbs[index]); - } - } - } - - writer.End(); - - compressedHeaderData = compressedHeader.ToArray(); - } - - // Write uncompressed header. - using (MemoryStream encodedHeader = new MemoryStream()) - { - VpxBitStreamWriter writer = new VpxBitStreamWriter(encodedHeader); - - writer.WriteU(2, 2); //Frame marker. - writer.WriteU(0, 2); //Profile. - writer.WriteBit(false); //Show existing frame. - writer.WriteBit(!isKeyFrame); - writer.WriteBit(showFrame); - writer.WriteBit(errorResilientMode); - - if (isKeyFrame) - { - writer.WriteU(FrameSyncCode, 24); - writer.WriteU(0, 3); //Color space. - writer.WriteU(0, 1); //Color range. - writer.WriteU(header.CurrentFrame.Width - 1, 16); - writer.WriteU(header.CurrentFrame.Height - 1, 16); - writer.WriteBit(false); //Render and frame size different. - - _cachedRefFrames.Clear(); - - // On key frames, all frame slots are set to the current frame, - // so the value of the selected slot doesn't really matter. - GetNewFrameSlot(keys.CurrKey); - } - else - { - if (!showFrame) - { - writer.WriteBit(isFrameIntra); - } - - if (!errorResilientMode) - { - writer.WriteU(0, 2); //Reset frame context. - } - - int refreshFrameFlags = 1 << GetNewFrameSlot(keys.CurrKey); - - if (isFrameIntra) - { - writer.WriteU(FrameSyncCode, 24); - writer.WriteU(refreshFrameFlags, 8); - writer.WriteU(header.CurrentFrame.Width - 1, 16); - writer.WriteU(header.CurrentFrame.Height - 1, 16); - writer.WriteBit(false); //Render and frame size different. - } - else - { - writer.WriteU(refreshFrameFlags, 8); - - int[] refFrameIndex = new int[] - { - GetFrameSlot(keys.Ref0Key), - GetFrameSlot(keys.Ref1Key), - GetFrameSlot(keys.Ref2Key) - }; - - byte[] refFrameSignBias = header.RefFrameSignBias; - - for (int index = 1; index < 4; index++) - { - writer.WriteU(refFrameIndex[index - 1], 3); - writer.WriteU(refFrameSignBias[index], 1); - } - - writer.WriteBit(true); //Frame size with refs. - writer.WriteBit(false); //Render and frame size different. - writer.WriteBit(header.AllowHighPrecisionMv); - writer.WriteBit(header.RawInterpolationFilter == 4); - - if (header.RawInterpolationFilter != 4) - { - writer.WriteU(header.RawInterpolationFilter, 2); - } - } - } - - if (!errorResilientMode) - { - writer.WriteBit(false); //Refresh frame context. - writer.WriteBit(true); //Frame parallel decoding mode. - } - - writer.WriteU(0, 2); //Frame context index. - - writer.WriteU(header.LoopFilterLevel, 6); - writer.WriteU(header.LoopFilterSharpness, 3); - writer.WriteBit(header.LoopFilterDeltaEnabled); - - if (header.LoopFilterDeltaEnabled) - { - bool[] updateLoopFilterRefDeltas = new bool[4]; - bool[] updateLoopFilterModeDeltas = new bool[2]; - - bool loopFilterDeltaUpdate = false; - - for (int index = 0; index < header.LoopFilterRefDeltas.Length; index++) - { - sbyte old = _loopFilterRefDeltas[index]; - sbyte New = header.LoopFilterRefDeltas[index]; - - loopFilterDeltaUpdate |= (updateLoopFilterRefDeltas[index] = old != New); - } - - for (int index = 0; index < header.LoopFilterModeDeltas.Length; index++) - { - sbyte old = _loopFilterModeDeltas[index]; - sbyte New = header.LoopFilterModeDeltas[index]; - - loopFilterDeltaUpdate |= (updateLoopFilterModeDeltas[index] = old != New); - } - - writer.WriteBit(loopFilterDeltaUpdate); - - if (loopFilterDeltaUpdate) - { - for (int index = 0; index < header.LoopFilterRefDeltas.Length; index++) - { - writer.WriteBit(updateLoopFilterRefDeltas[index]); - - if (updateLoopFilterRefDeltas[index]) - { - writer.WriteS(header.LoopFilterRefDeltas[index], 6); - } - } - - for (int index = 0; index < header.LoopFilterModeDeltas.Length; index++) - { - writer.WriteBit(updateLoopFilterModeDeltas[index]); - - if (updateLoopFilterModeDeltas[index]) - { - writer.WriteS(header.LoopFilterModeDeltas[index], 6); - } - } - } - } - - writer.WriteU(header.BaseQIndex, 8); - - writer.WriteDeltaQ(header.DeltaQYDc); - writer.WriteDeltaQ(header.DeltaQUvDc); - writer.WriteDeltaQ(header.DeltaQUvAc); - - writer.WriteBit(false); //Segmentation enabled (TODO). - - int minTileColsLog2 = CalcMinLog2TileCols(header.CurrentFrame.Width); - int maxTileColsLog2 = CalcMaxLog2TileCols(header.CurrentFrame.Width); - - int tileColsLog2Diff = header.TileColsLog2 - minTileColsLog2; - - int tileColsLog2IncMask = (1 << tileColsLog2Diff) - 1; - - // If it's less than the maximum, we need to add an extra 0 on the bitstream - // to indicate that it should stop reading. - if (header.TileColsLog2 < maxTileColsLog2) - { - writer.WriteU(tileColsLog2IncMask << 1, tileColsLog2Diff + 1); - } - else - { - writer.WriteU(tileColsLog2IncMask, tileColsLog2Diff); - } - - bool tileRowsLog2IsNonZero = header.TileRowsLog2 != 0; - - writer.WriteBit(tileRowsLog2IsNonZero); - - if (tileRowsLog2IsNonZero) - { - writer.WriteBit(header.TileRowsLog2 > 1); - } - - writer.WriteU(compressedHeaderData.Length, 16); - - writer.Flush(); - - encodedHeader.Write(compressedHeaderData, 0, compressedHeaderData.Length); - - if (!FFmpegWrapper.IsInitialized) - { - FFmpegWrapper.Vp9Initialize(); - } - - FFmpegWrapper.DecodeFrame(DecoderHelper.Combine(encodedHeader.ToArray(), frameData)); - } - - _loopFilterRefDeltas = header.LoopFilterRefDeltas; - _loopFilterModeDeltas = header.LoopFilterModeDeltas; - } - - private int GetNewFrameSlot(long key) - { - LinkedListNode node = _frameSlotByLastUse.Last; - - _frameSlotByLastUse.RemoveLast(); - _frameSlotByLastUse.AddFirst(node); - - _cachedRefFrames[key] = node; - - return node.Value; - } - - private int GetFrameSlot(long key) - { - if (_cachedRefFrames.TryGetValue(key, out LinkedListNode node)) - { - _frameSlotByLastUse.Remove(node); - _frameSlotByLastUse.AddFirst(node); - - return node.Value; - } - - // Reference frame was lost. - // What we should do in this case? - return 0; - } - - private void WriteProbabilityUpdate(VpxRangeEncoder writer, byte[] New, byte[] old) - { - for (int offset = 0; offset < New.Length; offset++) - { - WriteProbabilityUpdate(writer, New[offset], old[offset]); - } - } - - private void WriteCoefProbabilityUpdate(VpxRangeEncoder writer, int txMode, byte[] New, byte[] old) - { - // Note: There's 1 byte added on each packet for alignment, - // this byte is ignored when doing updates. - const int blockBytes = 2 * 2 * 6 * 6 * 4; - - bool NeedsUpdate(int baseIndex) - { - int index = baseIndex; - - for (int i = 0; i < 2; i++) - for (int j = 0; j < 2; j++) - for (int k = 0; k < 6; k++) - for (int l = 0; l < 6; l++) - { - if (New[index + 0] != old[index + 0] || - New[index + 1] != old[index + 1] || - New[index + 2] != old[index + 2]) - { - return true; - } - - index += 4; - } - - return false; - } - - for (int blockIndex = 0; blockIndex < 4; blockIndex++) - { - int baseIndex = blockIndex * blockBytes; - - bool update = NeedsUpdate(baseIndex); - - writer.Write(update); - - if (update) - { - int index = baseIndex; - - for (int i = 0; i < 2; i++) - for (int j = 0; j < 2; j++) - for (int k = 0; k < 6; k++) - for (int l = 0; l < 6; l++) - { - if (k != 0 || l < 3) - { - WriteProbabilityUpdate(writer, New[index + 0], old[index + 0]); - WriteProbabilityUpdate(writer, New[index + 1], old[index + 1]); - WriteProbabilityUpdate(writer, New[index + 2], old[index + 2]); - } - - index += 4; - } - } - - if (blockIndex == txMode) - { - break; - } - } - } - - private void WriteProbabilityUpdateAligned4(VpxRangeEncoder writer, byte[] New, byte[] old) - { - for (int offset = 0; offset < New.Length; offset += 4) - { - WriteProbabilityUpdate(writer, New[offset + 0], old[offset + 0]); - WriteProbabilityUpdate(writer, New[offset + 1], old[offset + 1]); - WriteProbabilityUpdate(writer, New[offset + 2], old[offset + 2]); - } - } - - private void WriteProbabilityUpdate(VpxRangeEncoder writer, byte New, byte old) - { - bool update = New != old; - - writer.Write(update, DiffUpdateProbability); - - if (update) - { - WriteProbabilityDelta(writer, New, old); - } - } - - private void WriteProbabilityDelta(VpxRangeEncoder writer, int New, int old) - { - int delta = RemapProbability(New, old); - - EncodeTermSubExp(writer, delta); - } - - private int RemapProbability(int New, int old) - { - New--; - old--; - - int index; - - if (old * 2 <= 0xff) - { - index = RecenterNonNeg(New, old) - 1; - } - else - { - index = RecenterNonNeg(0xff - 1 - New, 0xff - 1 - old) - 1; - } - - return MapLut[index]; - } - - private int RecenterNonNeg(int New, int old) - { - if (New > old * 2) - { - return New; - } - else if (New >= old) - { - return (New - old) * 2; - } - else /* if (New < Old) */ - { - return (old - New) * 2 - 1; - } - } - - private void EncodeTermSubExp(VpxRangeEncoder writer, int value) - { - if (WriteLessThan(writer, value, 16)) - { - writer.Write(value, 4); - } - else if (WriteLessThan(writer, value, 32)) - { - writer.Write(value - 16, 4); - } - else if (WriteLessThan(writer, value, 64)) - { - writer.Write(value - 32, 5); - } - else - { - value -= 64; - - const int size = 8; - - int mask = (1 << size) - 191; - - int delta = value - mask; - - if (delta < 0) - { - writer.Write(value, size - 1); - } - else - { - writer.Write(delta / 2 + mask, size - 1); - writer.Write(delta & 1, 1); - } - } - } - - private bool WriteLessThan(VpxRangeEncoder writer, int value, int test) - { - bool isLessThan = value < test; - - writer.Write(!isLessThan); - - return isLessThan; - } - - private void WriteMvProbabilityUpdate(VpxRangeEncoder writer, byte New, byte old) - { - bool update = New != old; - - writer.Write(update, DiffUpdateProbability); - - if (update) - { - writer.Write(New >> 1, 7); - } - } - - private static int CalcMinLog2TileCols(int frameWidth) - { - int sb64Cols = (frameWidth + 63) / 64; - int minLog2 = 0; - - while ((64 << minLog2) < sb64Cols) - { - minLog2++; - } - - return minLog2; - } - - private static int CalcMaxLog2TileCols(int frameWidth) - { - int sb64Cols = (frameWidth + 63) / 64; - int maxLog2 = 1; - - while ((sb64Cols >> maxLog2) >= 4) - { - maxLog2++; - } - - return maxLog2 - 1; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/Vp9FrameHeader.cs b/Ryujinx.Graphics/VDec/Vp9FrameHeader.cs deleted file mode 100644 index bdba6de5a..000000000 --- a/Ryujinx.Graphics/VDec/Vp9FrameHeader.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.VDec -{ - [StructLayout(LayoutKind.Sequential, Pack = 2)] - struct Vp9FrameDimensions - { - public short Width; - public short Height; - public short SubsamplingX; //? - public short SubsamplingY; //? - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct Vp9FrameHeader - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public Vp9FrameDimensions[] RefFrames; - - public Vp9FrameDimensions CurrentFrame; - - public int Flags; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] RefFrameSignBias; - - public byte LoopFilterLevel; - public byte LoopFilterSharpness; - - public byte BaseQIndex; - public sbyte DeltaQYDc; - public sbyte DeltaQUvDc; - public sbyte DeltaQUvAc; - - [MarshalAs(UnmanagedType.I1)] - public bool Lossless; - - public byte TxMode; - - [MarshalAs(UnmanagedType.I1)] - public bool AllowHighPrecisionMv; - - public byte RawInterpolationFilter; - public byte CompPredMode; - public byte FixCompRef; - public byte VarCompRef0; - public byte VarCompRef1; - - public byte TileColsLog2; - public byte TileRowsLog2; - - [MarshalAs(UnmanagedType.I1)] - public bool SegmentationEnabled; - - [MarshalAs(UnmanagedType.I1)] - public bool SegmentationUpdate; - - [MarshalAs(UnmanagedType.I1)] - public bool SegmentationTemporalUpdate; - - [MarshalAs(UnmanagedType.I1)] - public bool SegmentationAbsOrDeltaUpdate; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8 * 4, ArraySubType = UnmanagedType.I1)] - public bool[] FeatureEnabled; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8 * 4)] - public short[] FeatureData; - - [MarshalAs(UnmanagedType.I1)] - public bool LoopFilterDeltaEnabled; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public sbyte[] LoopFilterRefDeltas; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public sbyte[] LoopFilterModeDeltas; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/Vp9FrameKeys.cs b/Ryujinx.Graphics/VDec/Vp9FrameKeys.cs deleted file mode 100644 index dfc31ea31..000000000 --- a/Ryujinx.Graphics/VDec/Vp9FrameKeys.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Ryujinx.Graphics.VDec -{ - struct Vp9FrameKeys - { - public long CurrKey; - public long Ref0Key; - public long Ref1Key; - public long Ref2Key; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/Vp9ProbabilityTables.cs b/Ryujinx.Graphics/VDec/Vp9ProbabilityTables.cs deleted file mode 100644 index 5a6dd0cf2..000000000 --- a/Ryujinx.Graphics/VDec/Vp9ProbabilityTables.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace Ryujinx.Graphics.VDec -{ - struct Vp9ProbabilityTables - { - public byte[] SegmentationTreeProbs; - public byte[] SegmentationPredProbs; - public byte[] Tx8x8Probs; - public byte[] Tx16x16Probs; - public byte[] Tx32x32Probs; - public byte[] CoefProbs; - public byte[] SkipProbs; - public byte[] InterModeProbs; - public byte[] InterpFilterProbs; - public byte[] IsInterProbs; - public byte[] CompModeProbs; - public byte[] SingleRefProbs; - public byte[] CompRefProbs; - public byte[] YModeProbs0; - public byte[] YModeProbs1; - public byte[] PartitionProbs; - public byte[] MvJointProbs; - public byte[] MvSignProbs; - public byte[] MvClassProbs; - public byte[] MvClass0BitProbs; - public byte[] MvBitsProbs; - public byte[] MvClass0FrProbs; - public byte[] MvFrProbs; - public byte[] MvClass0HpProbs; - public byte[] MvHpProbs; - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/VpxBitStreamWriter.cs b/Ryujinx.Graphics/VDec/VpxBitStreamWriter.cs deleted file mode 100644 index 97ada333e..000000000 --- a/Ryujinx.Graphics/VDec/VpxBitStreamWriter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.IO; - -namespace Ryujinx.Graphics.VDec -{ - class VpxBitStreamWriter : BitStreamWriter - { - public VpxBitStreamWriter(Stream baseStream) : base(baseStream) { } - - public void WriteU(int value, int valueSize) - { - WriteBits(value, valueSize); - } - - public void WriteS(int value, int valueSize) - { - bool sign = value < 0; - - if (sign) - { - value = -value; - } - - WriteBits((value << 1) | (sign ? 1 : 0), valueSize + 1); - } - - public void WriteDeltaQ(int value) - { - bool deltaCoded = value != 0; - - WriteBit(deltaCoded); - - if (deltaCoded) - { - WriteBits(value, 4); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/VDec/VpxRangeEncoder.cs b/Ryujinx.Graphics/VDec/VpxRangeEncoder.cs deleted file mode 100644 index c854c9d9d..000000000 --- a/Ryujinx.Graphics/VDec/VpxRangeEncoder.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System.IO; - -namespace Ryujinx.Graphics.VDec -{ - class VpxRangeEncoder - { - private const int HalfProbability = 128; - - private static readonly int[] NormLut = new int[] - { - 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - private Stream _baseStream; - - private uint _lowValue; - private uint _range; - private int _count; - - public VpxRangeEncoder(Stream baseStream) - { - _baseStream = baseStream; - - _range = 0xff; - _count = -24; - - Write(false); - } - - public void WriteByte(byte value) - { - Write(value, 8); - } - - public void Write(int value, int valueSize) - { - for (int bit = valueSize - 1; bit >= 0; bit--) - { - Write(((value >> bit) & 1) != 0); - } - } - - public void Write(bool bit) - { - Write(bit, HalfProbability); - } - - public void Write(bool bit, int probability) - { - uint range = _range; - - uint split = 1 + (((range - 1) * (uint)probability) >> 8); - - range = split; - - if (bit) - { - _lowValue += split; - range = _range - split; - } - - int shift = NormLut[range]; - - range <<= shift; - _count += shift; - - if (_count >= 0) - { - int offset = shift - _count; - - if (((_lowValue << (offset - 1)) >> 31) != 0) - { - long currentPos = _baseStream.Position; - - _baseStream.Seek(-1, SeekOrigin.Current); - - while (_baseStream.Position >= 0 && PeekByte() == 0xff) - { - _baseStream.WriteByte(0); - - _baseStream.Seek(-2, SeekOrigin.Current); - } - - _baseStream.WriteByte((byte)(PeekByte() + 1)); - - _baseStream.Seek(currentPos, SeekOrigin.Begin); - } - - _baseStream.WriteByte((byte)(_lowValue >> (24 - offset))); - - _lowValue <<= offset; - shift = _count; - _lowValue &= 0xffffff; - _count -= 8; - } - - _lowValue <<= shift; - - _range = range; - } - - private byte PeekByte() - { - byte value = (byte)_baseStream.ReadByte(); - - _baseStream.Seek(-1, SeekOrigin.Current); - - return value; - } - - public void End() - { - for (int index = 0; index < 32; index++) - { - Write(false); - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Vic/StructUnpacker.cs b/Ryujinx.Graphics/Vic/StructUnpacker.cs deleted file mode 100644 index 6b6b9795c..000000000 --- a/Ryujinx.Graphics/Vic/StructUnpacker.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Ryujinx.Graphics.Memory; -using System; - -namespace Ryujinx.Graphics.Vic -{ - class StructUnpacker - { - private NvGpuVmm _vmm; - - private long _position; - - private ulong _buffer; - private int _buffPos; - - public StructUnpacker(NvGpuVmm vmm, long position) - { - _vmm = vmm; - _position = position; - - _buffPos = 64; - } - - public int Read(int bits) - { - if ((uint)bits > 32) - { - throw new ArgumentOutOfRangeException(nameof(bits)); - } - - int value = 0; - - while (bits > 0) - { - RefillBufferIfNeeded(); - - int readBits = bits; - - int maxReadBits = 64 - _buffPos; - - if (readBits > maxReadBits) - { - readBits = maxReadBits; - } - - value <<= readBits; - - value |= (int)(_buffer >> _buffPos) & (int)(0xffffffff >> (32 - readBits)); - - _buffPos += readBits; - - bits -= readBits; - } - - return value; - } - - private void RefillBufferIfNeeded() - { - if (_buffPos >= 64) - { - _buffer = _vmm.ReadUInt64(_position); - - _position += 8; - - _buffPos = 0; - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Vic/SurfaceOutputConfig.cs b/Ryujinx.Graphics/Vic/SurfaceOutputConfig.cs deleted file mode 100644 index bdd55fc7c..000000000 --- a/Ryujinx.Graphics/Vic/SurfaceOutputConfig.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace Ryujinx.Graphics.Vic -{ - struct SurfaceOutputConfig - { - public SurfacePixelFormat PixelFormat; - - public int SurfaceWidth; - public int SurfaceHeight; - public int GobBlockHeight; - - public long SurfaceLumaAddress; - public long SurfaceChromaUAddress; - public long SurfaceChromaVAddress; - - public SurfaceOutputConfig( - SurfacePixelFormat pixelFormat, - int surfaceWidth, - int surfaceHeight, - int gobBlockHeight, - long outputSurfaceLumaAddress, - long outputSurfaceChromaUAddress, - long outputSurfaceChromaVAddress) - { - PixelFormat = pixelFormat; - SurfaceWidth = surfaceWidth; - SurfaceHeight = surfaceHeight; - GobBlockHeight = gobBlockHeight; - SurfaceLumaAddress = outputSurfaceLumaAddress; - SurfaceChromaUAddress = outputSurfaceChromaUAddress; - SurfaceChromaVAddress = outputSurfaceChromaVAddress; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Vic/SurfacePixelFormat.cs b/Ryujinx.Graphics/Vic/SurfacePixelFormat.cs deleted file mode 100644 index 8dabd0942..000000000 --- a/Ryujinx.Graphics/Vic/SurfacePixelFormat.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Ryujinx.Graphics.Vic -{ - enum SurfacePixelFormat - { - Rgba8 = 0x1f, - Yuv420P = 0x44 - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Vic/VideoImageComposer.cs b/Ryujinx.Graphics/Vic/VideoImageComposer.cs deleted file mode 100644 index e05bcfdb6..000000000 --- a/Ryujinx.Graphics/Vic/VideoImageComposer.cs +++ /dev/null @@ -1,107 +0,0 @@ -using Ryujinx.Graphics.Memory; - -namespace Ryujinx.Graphics.Vic -{ - class VideoImageComposer - { - private NvGpu _gpu; - - private long _configStructAddress; - private long _outputSurfaceLumaAddress; - private long _outputSurfaceChromaUAddress; - private long _outputSurfaceChromaVAddress; - - public VideoImageComposer(NvGpu gpu) - { - _gpu = gpu; - } - - public void Process(NvGpuVmm vmm, int methodOffset, int[] arguments) - { - VideoImageComposerMeth method = (VideoImageComposerMeth)methodOffset; - - switch (method) - { - case VideoImageComposerMeth.Execute: - Execute(vmm, arguments); - break; - - case VideoImageComposerMeth.SetConfigStructOffset: - SetConfigStructOffset(vmm, arguments); - break; - - case VideoImageComposerMeth.SetOutputSurfaceLumaOffset: - SetOutputSurfaceLumaOffset(vmm, arguments); - break; - - case VideoImageComposerMeth.SetOutputSurfaceChromaUOffset: - SetOutputSurfaceChromaUOffset(vmm, arguments); - break; - - case VideoImageComposerMeth.SetOutputSurfaceChromaVOffset: - SetOutputSurfaceChromaVOffset(vmm, arguments); - break; - } - } - - private void Execute(NvGpuVmm vmm, int[] arguments) - { - StructUnpacker unpacker = new StructUnpacker(vmm, _configStructAddress + 0x20); - - SurfacePixelFormat pixelFormat = (SurfacePixelFormat)unpacker.Read(7); - - int chromaLocHoriz = unpacker.Read(2); - int chromaLocVert = unpacker.Read(2); - - int blockLinearKind = unpacker.Read(4); - int blockLinearHeightLog2 = unpacker.Read(4); - - int reserved0 = unpacker.Read(3); - int reserved1 = unpacker.Read(10); - - int surfaceWidthMinus1 = unpacker.Read(14); - int surfaceHeightMinus1 = unpacker.Read(14); - - int gobBlockHeight = 1 << blockLinearHeightLog2; - - int surfaceWidth = surfaceWidthMinus1 + 1; - int surfaceHeight = surfaceHeightMinus1 + 1; - - SurfaceOutputConfig outputConfig = new SurfaceOutputConfig( - pixelFormat, - surfaceWidth, - surfaceHeight, - gobBlockHeight, - _outputSurfaceLumaAddress, - _outputSurfaceChromaUAddress, - _outputSurfaceChromaVAddress); - - _gpu.VideoDecoder.CopyPlanes(vmm, outputConfig); - } - - private void SetConfigStructOffset(NvGpuVmm vmm, int[] arguments) - { - _configStructAddress = GetAddress(arguments); - } - - private void SetOutputSurfaceLumaOffset(NvGpuVmm vmm, int[] arguments) - { - _outputSurfaceLumaAddress = GetAddress(arguments); - } - - private void SetOutputSurfaceChromaUOffset(NvGpuVmm vmm, int[] arguments) - { - _outputSurfaceChromaUAddress = GetAddress(arguments); - } - - private void SetOutputSurfaceChromaVOffset(NvGpuVmm vmm, int[] arguments) - { - _outputSurfaceChromaVAddress = GetAddress(arguments); - } - - private static long GetAddress(int[] arguments) - { - return (long)(uint)arguments[0] << 8; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Vic/VideoImageComposerMeth.cs b/Ryujinx.Graphics/Vic/VideoImageComposerMeth.cs deleted file mode 100644 index b30cabeaa..000000000 --- a/Ryujinx.Graphics/Vic/VideoImageComposerMeth.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Ryujinx.Graphics.Vic -{ - enum VideoImageComposerMeth - { - Execute = 0xc0, - SetControlParams = 0x1c1, - SetConfigStructOffset = 0x1c2, - SetOutputSurfaceLumaOffset = 0x1c8, - SetOutputSurfaceChromaUOffset = 0x1c9, - SetOutputSurfaceChromaVOffset = 0x1ca - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index 5a3132e25..cdd472955 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -24,8 +24,6 @@ namespace Ryujinx.HLE.HOS.Services.Mm // FinalizeOld(u32) public ResultCode FinalizeOld(ServiceCtx context) { - context.Device.Gpu.UninitializeVideoDecoder(); - Logger.PrintStub(LogClass.ServiceMm); return ResultCode.Success; @@ -69,8 +67,6 @@ namespace Ryujinx.HLE.HOS.Services.Mm // Finalize(u32) public ResultCode Finalize(ServiceCtx context) { - context.Device.Gpu.UninitializeVideoDecoder(); - Logger.PrintStub(LogClass.ServiceMm); return ResultCode.Success; diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 2b9f09fa2..a227d8924 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -102,7 +102,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv byte[] outputData = new byte[outputDataSize]; - context.Memory.ReadBytes(inputDataPosition, outputData, 0, (int)inputDataSize); + byte[] temp = context.Memory.ReadBytes(inputDataPosition, inputDataSize); + + Buffer.BlockCopy(temp, 0, outputData, 0, temp.Length); arguments = new Span(outputData); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs index 73007c911..de6c9ba9e 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvDeviceFile.cs @@ -9,11 +9,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices { abstract class NvDeviceFile { + public readonly ServiceCtx Context; public readonly KProcess Owner; public NvDeviceFile(ServiceCtx context) { - Owner = context.Process; + Context = context; + Owner = context.Process; } public virtual NvInternalResult QueryEvent(out int eventHandle, uint eventId) diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs index b48377a47..84a9d3d50 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/NvHostAsGpuDeviceFile.cs @@ -1,5 +1,5 @@ using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; @@ -79,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu private NvInternalResult AllocSpace(ref AllocSpaceArguments arguments) { - AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context); ulong size = (ulong)arguments.Pages * (ulong)arguments.PageSize; @@ -91,11 +91,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu // the Offset field holds the alignment size instead. if ((arguments.Flags & AddressSpaceFlags.FixedOffset) != 0) { - arguments.Offset = addressSpaceContext.Vmm.ReserveFixed(arguments.Offset, (long)size); + arguments.Offset = (long)addressSpaceContext.Gmm.ReserveFixed((ulong)arguments.Offset, size); } else { - arguments.Offset = addressSpaceContext.Vmm.Reserve((long)size, arguments.Offset); + arguments.Offset = (long)addressSpaceContext.Gmm.Reserve((ulong)size, (ulong)arguments.Offset); } if (arguments.Offset < 0) @@ -117,7 +117,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu private NvInternalResult FreeSpace(ref FreeSpaceArguments arguments) { - AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context); NvInternalResult result = NvInternalResult.Success; @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu if (addressSpaceContext.RemoveReservation(arguments.Offset)) { - addressSpaceContext.Vmm.Free(arguments.Offset, (long)size); + addressSpaceContext.Gmm.Free((ulong)arguments.Offset, size); } else { @@ -143,7 +143,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu private NvInternalResult UnmapBuffer(ref UnmapBufferArguments arguments) { - AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context); lock (addressSpaceContext) { @@ -151,7 +151,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu { if (size != 0) { - addressSpaceContext.Vmm.Free(arguments.Offset, size); + addressSpaceContext.Gmm.Free((ulong)arguments.Offset, (ulong)size); } } else @@ -167,7 +167,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu { const string mapErrorMsg = "Failed to map fixed buffer with offset 0x{0:x16} and size 0x{1:x16}!"; - AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Owner); + AddressSpaceContext addressSpaceContext = GetAddressSpaceContext(Context); NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments.NvMapHandle, true); @@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu physicalAddress += arguments.BufferOffset; - if (addressSpaceContext.Vmm.Map(physicalAddress, virtualAddress, arguments.MappingSize) < 0) + if ((long)addressSpaceContext.Gmm.Map((ulong)physicalAddress, (ulong)virtualAddress, (ulong)arguments.MappingSize) < 0) { string message = string.Format(mapErrorMsg, virtualAddress, arguments.MappingSize); @@ -231,7 +231,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu { if (addressSpaceContext.ValidateFixedBuffer(arguments.Offset, size)) { - arguments.Offset = addressSpaceContext.Vmm.Map(physicalAddress, arguments.Offset, size); + arguments.Offset = (long)addressSpaceContext.Gmm.Map((ulong)physicalAddress, (ulong)arguments.Offset, (ulong)size); } else { @@ -244,7 +244,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu } else { - arguments.Offset = addressSpaceContext.Vmm.Map(physicalAddress, size); + arguments.Offset = (long)addressSpaceContext.Gmm.Map((ulong)physicalAddress, (ulong)size); } if (arguments.Offset < 0) @@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu { for (int index = 0; index < arguments.Length; index++) { - NvGpuVmm vmm = GetAddressSpaceContext(Owner).Vmm; + MemoryManager gmm = GetAddressSpaceContext(Context).Gmm; NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(Owner, arguments[index].NvMapHandle, true); @@ -293,10 +293,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu return NvInternalResult.InvalidInput; } - long result = vmm.Map( - ((long)arguments[index].MapOffset << 16) + map.Address, - (long)arguments[index].GpuOffset << 16, - (long)arguments[index].Pages << 16); + long result = (long)gmm.Map( + ((ulong)arguments[index].MapOffset << 16) + (ulong)map.Address, + (ulong)arguments[index].GpuOffset << 16, + (ulong)arguments[index].Pages << 16); if (result < 0) { @@ -312,9 +312,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu public override void Close() { } - public static AddressSpaceContext GetAddressSpaceContext(KProcess process) + public static AddressSpaceContext GetAddressSpaceContext(ServiceCtx context) { - return _addressSpaceContextRegistry.GetOrAdd(process, (key) => new AddressSpaceContext(process)); + return _addressSpaceContextRegistry.GetOrAdd(context.Process, (key) => new AddressSpaceContext(context)); } } } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs index 5c2830000..5238645e5 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostAsGpu/Types/AddressSpaceContext.cs @@ -1,16 +1,12 @@ -using ARMeilleure.Memory; -using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using System; using System.Collections.Generic; -using System.Text; namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types { class AddressSpaceContext { - public NvGpuVmm Vmm { get; private set; } - private class Range { public ulong Start { get; private set; } @@ -42,9 +38,45 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types private SortedList _maps; private SortedList _reservations; - public AddressSpaceContext(KProcess process) + public MemoryManager Gmm { get; } + + private class MemoryProxy : IPhysicalMemory { - Vmm = new NvGpuVmm(process.CpuMemory); + private ARMeilleure.Memory.MemoryManager _cpuMemory; + + public MemoryProxy(ARMeilleure.Memory.MemoryManager cpuMemory) + { + _cpuMemory = cpuMemory; + } + + public Span Read(ulong address, ulong size) + { + return _cpuMemory.ReadBytes((long)address, (long)size); + } + + public void Write(ulong address, Span data) + { + _cpuMemory.WriteBytes((long)address, data.ToArray()); + } + + public (ulong, ulong)[] GetModifiedRanges(ulong address, ulong size) + { + return _cpuMemory.GetModifiedRanges(address, size); + } + + public int GetPageSize() + { + return 4096; + } + } + + public AddressSpaceContext(ServiceCtx context) + { + Gmm = context.Device.Gpu.MemoryManager; + + var memoryProxy = new MemoryProxy(context.Process.CpuMemory); + + context.Device.Gpu.SetVmm(memoryProxy); _maps = new SortedList(); _reservations = new SortedList(); @@ -61,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu.Types } // Check if address is page aligned. - if ((position & NvGpuVmm.PageMask) != 0) + if ((position & (long)MemoryManager.PageMask) != 0) { return false; } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs index 80a7ce800..72235427e 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs @@ -1,7 +1,6 @@ -using ARMeilleure.Memory; -using Ryujinx.Common.Logging; -using Ryujinx.Graphics; -using Ryujinx.Graphics.Memory; +using Ryujinx.Common.Logging; +using Ryujinx.Graphics.Gpu; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel.Types; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; @@ -16,8 +15,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel private uint _timeout; private uint _submitTimeout; private uint _timeslice; - private NvGpu _gpu; - private MemoryManager _memory; + private GpuContext _gpu; + private ARMeilleure.Memory.MemoryManager _memory; public NvHostChannelDeviceFile(ServiceCtx context) : base(context) { @@ -110,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel int headerSize = Unsafe.SizeOf(); SubmitArguments submitHeader = MemoryMarshal.Cast(arguments)[0]; Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, submitHeader.CmdBufsCount); - NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; + MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm; foreach (CommandBuffer commandBufferEntry in commandBufferEntries) { @@ -123,7 +122,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel commandBufferData[offset] = _memory.ReadInt32(map.Address + commandBufferEntry.Offset + offset * 4); } - _gpu.PushCommandBuffer(vmm, commandBufferData); + // TODO: Submit command to engines. } return NvInternalResult.Success; @@ -161,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel int headerSize = Unsafe.SizeOf(); MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); - NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; + MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm; foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) { @@ -178,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { if (map.DmaMapAddress == 0) { - map.DmaMapAddress = vmm.MapLow(map.Address, map.Size); + map.DmaMapAddress = (long)gmm.MapLow((ulong)map.Address, (uint)map.Size); } commandBufferEntry.MapAddress = (int)map.DmaMapAddress; @@ -193,7 +192,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel int headerSize = Unsafe.SizeOf(); MapCommandBufferArguments commandBufferHeader = MemoryMarshal.Cast(arguments)[0]; Span commandBufferEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, commandBufferHeader.NumEntries); - NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; + MemoryManager gmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Context).Gmm; foreach (ref CommandBufferHandle commandBufferEntry in commandBufferEntries) { @@ -210,7 +209,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { if (map.DmaMapAddress != 0) { - vmm.Free(map.DmaMapAddress, map.Size); + gmm.Free((ulong)map.DmaMapAddress, (uint)map.Size); map.DmaMapAddress = 0; } @@ -240,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel { int headerSize = Unsafe.SizeOf(); SubmitGpfifoArguments gpfifoSubmissionHeader = MemoryMarshal.Cast(arguments)[0]; - Span gpfifoEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries); + Span gpfifoEntries = MemoryMarshal.Cast(arguments.Slice(headerSize)).Slice(0, gpfifoSubmissionHeader.NumEntries); return SubmitGpfifo(ref gpfifoSubmissionHeader, gpfifoEntries); } @@ -327,13 +326,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel return NvInternalResult.Success; } - protected NvInternalResult SubmitGpfifo(ref SubmitGpfifoArguments header, Span entries) + protected NvInternalResult SubmitGpfifo(ref SubmitGpfifoArguments header, Span entries) { - NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(Owner).Vmm; - - foreach (long entry in entries) + foreach (ulong entry in entries) { - _gpu.Pusher.Push(vmm, entry); + _gpu.DmaPusher.Push(entry); } header.Fence.Id = 0; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs index 582ba50e9..7eaa5cc58 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostGpuDeviceFile.cs @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel switch (command.Number) { case 0x1b: - result = CallIoctlMethod(SubmitGpfifoEx, arguments, inlineInBuffer); + result = CallIoctlMethod(SubmitGpfifoEx, arguments, inlineInBuffer); break; } } @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel return NvInternalResult.Success; } - private NvInternalResult SubmitGpfifoEx(ref SubmitGpfifoArguments arguments, Span inlineData) + private NvInternalResult SubmitGpfifoEx(ref SubmitGpfifoArguments arguments, Span inlineData) { return SubmitGpfifo(ref arguments, inlineData); } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs index 2ca847a31..d03ede944 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvMap/NvMapDeviceFile.cs @@ -1,6 +1,6 @@ using Ryujinx.Common; using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.HLE.HOS.Kernel.Process; using System; using System.Collections.Concurrent; @@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap return NvInternalResult.InvalidInput; } - int size = BitUtils.AlignUp(arguments.Size, NvGpuVmm.PageSize); + int size = BitUtils.AlignUp(arguments.Size, (int)MemoryManager.PageSize); arguments.Handle = CreateHandleFromMap(new NvMapHandle(size)); @@ -118,9 +118,9 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap return NvInternalResult.InvalidInput; } - if ((uint)arguments.Align < NvGpuVmm.PageSize) + if ((uint)arguments.Align < MemoryManager.PageSize) { - arguments.Align = NvGpuVmm.PageSize; + arguments.Align = (int)MemoryManager.PageSize; } NvInternalResult result = NvInternalResult.Success; @@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap map.Align = arguments.Align; map.Kind = (byte)arguments.Kind; - int size = BitUtils.AlignUp(map.Size, NvGpuVmm.PageSize); + int size = BitUtils.AlignUp(map.Size, (int)MemoryManager.PageSize); long address = arguments.Address; diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs index e3e4a1a45..50b55ee06 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/NvFlinger.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Logging; -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Memory; +using Ryujinx.Graphics.GAL; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; @@ -23,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private KEvent _binderEvent; - private IGalRenderer _renderer; + private IRenderer _renderer; private const int BufferQueueCount = 0x40; private const int BufferQueueMask = BufferQueueCount - 1; @@ -34,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger private bool _disposed; - public NvFlinger(IGalRenderer renderer, KEvent binderEvent) + public NvFlinger(IRenderer renderer, KEvent binderEvent) { _commands = new Dictionary<(string, int), ServiceProcessParcel> { @@ -256,20 +255,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return ResultCode.Success; } - private GalImageFormat ConvertColorFormat(ColorFormat colorFormat) + private Format ConvertColorFormat(ColorFormat colorFormat) { switch (colorFormat) { case ColorFormat.A8B8G8R8: - return GalImageFormat.Rgba8 | GalImageFormat.Unorm; + return Format.R8G8B8A8Unorm; case ColorFormat.X8B8G8R8: - return GalImageFormat.Rgbx8 | GalImageFormat.Unorm; + return Format.R8G8B8A8Unorm; case ColorFormat.R5G6B5: - return GalImageFormat.Bgr565 | GalImageFormat.Unorm; + return Format.R5G6B5Unorm; case ColorFormat.A8R8G8B8: - return GalImageFormat.Bgra8 | GalImageFormat.Unorm; + return Format.B8G8R8A8Unorm; case ColorFormat.A4B4G4R4: - return GalImageFormat.Rgba4 | GalImageFormat.Unorm; + return Format.R4G4B4A4Unorm; default: throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!"); } @@ -292,7 +291,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(context.Process, nvMapHandle); - long fbAddr = map.Address + bufferOffset; + ulong fbAddr = (ulong)(map.Address + bufferOffset); _bufferQueue[slot].State = BufferState.Acquired; @@ -301,39 +300,42 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger bool flipX = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipX); bool flipY = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipY); - GalImageFormat imageFormat = ConvertColorFormat(_bufferQueue[slot].Data.Buffer.Surfaces[0].ColorFormat); + Format format = ConvertColorFormat(_bufferQueue[slot].Data.Buffer.Surfaces[0].ColorFormat); - int BlockHeight = 1 << _bufferQueue[slot].Data.Buffer.Surfaces[0].BlockHeightLog2; + int bytesPerPixel = + format == Format.R5G6B5Unorm || + format == Format.R4G4B4A4Unorm ? 2 : 4; + + int gobBlocksInY = 1 << _bufferQueue[slot].Data.Buffer.Surfaces[0].BlockHeightLog2; // Note: Rotation is being ignored. - int top = crop.Top; - int left = crop.Left; - int right = crop.Right; - int bottom = crop.Bottom; + ITexture texture = context.Device.Gpu.GetTexture( + fbAddr, + fbWidth, + fbHeight, + 0, + false, + gobBlocksInY, + format, + bytesPerPixel); - NvGpuVmm vmm = NvHostAsGpuDeviceFile.GetAddressSpaceContext(context.Process).Vmm; + _renderer.Window.RegisterTextureReleaseCallback(ReleaseBuffer); - _renderer.QueueAction(() => - { - if (!_renderer.Texture.TryGetImage(fbAddr, out GalImage image)) - { - image = new GalImage( - fbWidth, - fbHeight, 1, 1, 1, BlockHeight, 1, - GalMemoryLayout.BlockLinear, - imageFormat, - GalTextureTarget.TwoD); - } + ImageCrop imageCrop = new ImageCrop( + crop.Left, + crop.Right, + crop.Top, + crop.Bottom, + flipX, + flipY); - context.Device.Gpu.ResourceManager.ClearPbCache(); - context.Device.Gpu.ResourceManager.SendTexture(vmm, fbAddr, image); + _renderer.Window.QueueTexture(texture, imageCrop, slot); + } - _renderer.RenderTarget.SetTransform(flipX, flipY, top, left, right, bottom); - _renderer.RenderTarget.Present(fbAddr); - - ReleaseBuffer(slot); - }); + private void ReleaseBuffer(object context) + { + ReleaseBuffer((int)context); } private void ReleaseBuffer(int slot) diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IHOSBinderDriver.cs index 3364ce704..e30d159ca 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IHOSBinderDriver.cs @@ -1,4 +1,4 @@ -using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.GAL; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Threading; @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService private NvFlinger _flinger; - public IHOSBinderDriver(Horizon system, IGalRenderer renderer) + public IHOSBinderDriver(Horizon system, IRenderer renderer) { _binderEvent = new KEvent(system); diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index 683714654..15c0c8c0c 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -44,9 +44,10 @@ - + + diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index d1ca3d1f4..e5a5f2736 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -1,8 +1,8 @@ using LibHac.FsSystem; using Ryujinx.Audio; using Ryujinx.Configuration; -using Ryujinx.Graphics; -using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.GAL; +using Ryujinx.Graphics.Gpu; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services; @@ -19,7 +19,7 @@ namespace Ryujinx.HLE internal DeviceMemory Memory { get; private set; } - internal NvGpu Gpu { get; private set; } + internal GpuContext Gpu { get; private set; } public VirtualFileSystem FileSystem { get; private set; } @@ -35,7 +35,7 @@ namespace Ryujinx.HLE public event EventHandler Finish; - public Switch(IGalRenderer renderer, IAalOutput audioOut) + public Switch(IRenderer renderer, IAalOutput audioOut) { if (renderer == null) { @@ -51,7 +51,7 @@ namespace Ryujinx.HLE Memory = new DeviceMemory(); - Gpu = new NvGpu(renderer); + Gpu = new GpuContext(renderer); FileSystem = new VirtualFileSystem(); @@ -114,12 +114,12 @@ namespace Ryujinx.HLE public bool WaitFifo() { - return Gpu.Pusher.WaitForCommands(); + return Gpu.DmaPusher.WaitForCommands(); } public void ProcessFrame() { - Gpu.Pusher.DispatchCalls(); + Gpu.DmaPusher.DispatchCalls(); } internal void Unload() diff --git a/Ryujinx.ShaderTools/Memory.cs b/Ryujinx.ShaderTools/Memory.cs deleted file mode 100644 index c99224b5e..000000000 --- a/Ryujinx.ShaderTools/Memory.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Ryujinx.Graphics.Gal; -using System.IO; - -namespace Ryujinx.ShaderTools -{ - class Memory : IGalMemory - { - private Stream BaseStream; - - private BinaryReader Reader; - - public Memory(Stream BaseStream) - { - this.BaseStream = BaseStream; - - Reader = new BinaryReader(BaseStream); - } - - public int ReadInt32(long Position) - { - BaseStream.Seek(Position, SeekOrigin.Begin); - - return Reader.ReadInt32(); - } - } -} \ No newline at end of file diff --git a/Ryujinx.ShaderTools/Program.cs b/Ryujinx.ShaderTools/Program.cs index e763e2c1c..cb5805be1 100644 --- a/Ryujinx.ShaderTools/Program.cs +++ b/Ryujinx.ShaderTools/Program.cs @@ -1,6 +1,4 @@ -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Shader; -using Ryujinx.Graphics.Shader.Translation; +using Ryujinx.Graphics.Shader.Translation; using System; using System.IO; @@ -10,33 +8,26 @@ namespace Ryujinx.ShaderTools { static void Main(string[] args) { - if (args.Length == 2) + if (args.Length == 1 || args.Length == 2) { - GalShaderType type = GalShaderType.Vertex; + TranslationFlags flags = TranslationFlags.None; - switch (args[0].ToLower()) + if (args.Length == 2 && args[0] == "--compute") { - case "v": type = GalShaderType.Vertex; break; - case "tc": type = GalShaderType.TessControl; break; - case "te": type = GalShaderType.TessEvaluation; break; - case "g": type = GalShaderType.Geometry; break; - case "f": type = GalShaderType.Fragment; break; + flags |= TranslationFlags.Compute; } - using (FileStream fs = new FileStream(args[1], FileMode.Open, FileAccess.Read)) - { - Memory mem = new Memory(fs); + byte[] data = File.ReadAllBytes(args[args.Length - 1]); - ShaderConfig config = new ShaderConfig(type, 65536); + TranslationConfig translationConfig = new TranslationConfig(0x10000, 0, flags); - string code = Translator.Translate(mem, 0, config).Code; + string code = Translator.Translate(data, translationConfig).Code; - Console.WriteLine(code); - } + Console.WriteLine(code); } else { - Console.WriteLine("Usage: Ryujinx.ShaderTools [v|tc|te|g|f] shader.bin"); + Console.WriteLine("Usage: Ryujinx.ShaderTools [--compute] shader.bin"); } } } diff --git a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj index 2a71319dc..2f9c77727 100644 --- a/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj +++ b/Ryujinx.ShaderTools/Ryujinx.ShaderTools.csproj @@ -1,5 +1,9 @@ + + + + netcoreapp3.0 win-x64;osx-x64;linux-x64 @@ -17,8 +21,4 @@ false - - - - diff --git a/Ryujinx.sln b/Ryujinx.sln index e3cb4528f..1d21ff586 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -14,8 +14,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE", "Ryujinx.HLE\ {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34} = {4E69B67F-8CA7-42CF-A9E1-CCB0915DFB34} EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics", "Ryujinx.Graphics\Ryujinx.Graphics.csproj", "{EAAE36AF-7781-4578-A7E0-F0EFD2025569}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" @@ -28,6 +26,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Profiler", "Ryujinx EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ARMeilleure", "ARMeilleure\ARMeilleure.csproj", "{ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Gpu", "Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj", "{ADA7EA87-0D63-4D97-9433-922A2124401F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.GAL", "Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj", "{A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.OpenGL", "Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj", "{9558FB96-075D-4219-8FFF-401979DC0B69}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Texture", "Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj", "{E1B1AD28-289D-47B7-A106-326972240207}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.Graphics.Shader", "Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj", "{03B955CD-AD84-4B93-AAA7-BF17923BBAA5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -68,14 +76,14 @@ Global {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.ActiveCfg = Release|Any CPU {CB92CFF9-1D62-4D4F-9E88-8130EF61E351}.Release|Any CPU.Build.0 = Release|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Profile Debug|Any CPU.ActiveCfg = Profile Debug|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Profile Debug|Any CPU.Build.0 = Profile Debug|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Profile Release|Any CPU.ActiveCfg = Profile Release|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EAAE36AF-7781-4578-A7E0-F0EFD2025569}.Release|Any CPU.Build.0 = Release|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Profile Debug|Any CPU.ActiveCfg = Profile Debug|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Profile Debug|Any CPU.Build.0 = Profile Debug|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Profile Release|Any CPU.ActiveCfg = Profile Release|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Profile Release|Any CPU.Build.0 = Profile Release|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2345A1A7-8DEF-419B-9AFB-4DFD41D20D05}.Release|Any CPU.Build.0 = Release|Any CPU {5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5C1D818E-682A-46A5-9D54-30006E26C270}.Debug|Any CPU.Build.0 = Debug|Any CPU {5C1D818E-682A-46A5-9D54-30006E26C270}.Profile Debug|Any CPU.ActiveCfg = Profile Debug|Any CPU @@ -124,6 +132,46 @@ Global {ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Profile Release|Any CPU.Build.0 = Release|Any CPU {ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.ActiveCfg = Release|Any CPU {ABF09A5E-2D8B-4B6F-A51D-5CE414DDB15A}.Release|Any CPU.Build.0 = Release|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Profile Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Profile Debug|Any CPU.Build.0 = Debug|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Profile Release|Any CPU.ActiveCfg = Debug|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Profile Release|Any CPU.Build.0 = Debug|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADA7EA87-0D63-4D97-9433-922A2124401F}.Release|Any CPU.Build.0 = Release|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Profile Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Profile Debug|Any CPU.Build.0 = Debug|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Profile Release|Any CPU.ActiveCfg = Debug|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Profile Release|Any CPU.Build.0 = Debug|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A602AE97-91A5-4608-8DF1-EBF4ED7A0B9E}.Release|Any CPU.Build.0 = Release|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Profile Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Profile Debug|Any CPU.Build.0 = Debug|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Profile Release|Any CPU.ActiveCfg = Debug|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Profile Release|Any CPU.Build.0 = Debug|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9558FB96-075D-4219-8FFF-401979DC0B69}.Release|Any CPU.Build.0 = Release|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Profile Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Profile Debug|Any CPU.Build.0 = Debug|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Profile Release|Any CPU.ActiveCfg = Debug|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Profile Release|Any CPU.Build.0 = Debug|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1B1AD28-289D-47B7-A106-326972240207}.Release|Any CPU.Build.0 = Release|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Profile Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Profile Debug|Any CPU.Build.0 = Debug|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Profile Release|Any CPU.ActiveCfg = Debug|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Profile Release|Any CPU.Build.0 = Debug|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03B955CD-AD84-4B93-AAA7-BF17923BBAA5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index e610e8277..497708634 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -79,10 +79,11 @@ - + + diff --git a/Ryujinx/Ui/GLScreen.cs b/Ryujinx/Ui/GLScreen.cs index e1994803a..dc887534b 100644 --- a/Ryujinx/Ui/GLScreen.cs +++ b/Ryujinx/Ui/GLScreen.cs @@ -2,7 +2,7 @@ using OpenTK; using OpenTK.Graphics; using OpenTK.Input; using Ryujinx.Configuration; -using Ryujinx.Graphics.Gal; +using Ryujinx.Graphics.OpenGL; using Ryujinx.HLE; using Ryujinx.HLE.Input; using Ryujinx.Profiler.UI; @@ -23,7 +23,7 @@ namespace Ryujinx.Ui private Switch _device; - private IGalRenderer _renderer; + private Renderer _renderer; private HotkeyButtons _prevHotkeyButtons = 0; @@ -45,7 +45,7 @@ namespace Ryujinx.Ui private ProfileWindowManager _profileWindow; #endif - public GlScreen(Switch device, IGalRenderer renderer) + public GlScreen(Switch device, Renderer renderer) : base(1280, 720, new GraphicsMode(), "Ryujinx", 0, DisplayDevice.Default, 3, 3, @@ -59,7 +59,7 @@ namespace Ryujinx.Ui Location = new Point( (DisplayDevice.Default.Width / 2) - (Width / 2), (DisplayDevice.Default.Height / 2) - (Height / 2)); - + #if USE_PROFILING // Start profile window, it will handle itself from there _profileWindow = new ProfileWindowManager(); @@ -70,6 +70,8 @@ namespace Ryujinx.Ui { MakeCurrent(); + _renderer.InitializeCounters(); + Stopwatch chrono = new Stopwatch(); chrono.Start(); @@ -85,13 +87,11 @@ namespace Ryujinx.Ui _device.ProcessFrame(); } - _renderer.RunActions(); - if (_resizeEvent) { _resizeEvent = false; - _renderer.RenderTarget.SetWindowSize(Width, Height); + // TODO: Resize } ticks += chrono.ElapsedTicks; @@ -114,8 +114,6 @@ namespace Ryujinx.Ui Visible = true; - _renderer.RenderTarget.SetWindowSize(Width, Height); - Context.MakeCurrent(null); // OpenTK doesn't like sleeps in its thread, to avoid this a renderer thread is created @@ -188,7 +186,7 @@ namespace Ryujinx.Ui Keys = new int[0x8] }; } - + currentButton |= _primaryController.GetButtons(); // Keyboard has priority stick-wise @@ -296,7 +294,7 @@ namespace Ryujinx.Ui private new void RenderFrame() { - _renderer.RenderTarget.Render(); + _renderer.Window.Present(); _device.Statistics.RecordSystemFrameTime(); diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index e65e56ffd..69c5364b4 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -3,8 +3,7 @@ using JsonPrettyPrinterPlus; using Ryujinx.Audio; using Ryujinx.Common.Logging; using Ryujinx.Configuration; -using Ryujinx.Graphics.Gal; -using Ryujinx.Graphics.Gal.OpenGL; +using Ryujinx.Graphics.OpenGL; using Ryujinx.HLE.FileSystem; using Ryujinx.Profiler; using System; @@ -25,7 +24,7 @@ namespace Ryujinx.Ui { private static HLE.Switch _device; - private static IGalRenderer _renderer; + private static Renderer _renderer; private static IAalOutput _audioOut; @@ -74,12 +73,12 @@ namespace Ryujinx.Ui _gameTable.ButtonReleaseEvent += Row_Clicked; bool continueWithStartup = Migration.PromptIfMigrationNeededForStartup(this, out bool migrationNeeded); - if (!continueWithStartup) + if (!continueWithStartup) { End(); } - _renderer = new OglRenderer(); + _renderer = new Renderer(); _audioOut = InitializeAudioEngine(); @@ -231,7 +230,7 @@ namespace Ryujinx.Ui Logger.RestartTime(); // TODO: Move this somewhere else + reloadable? - GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; + Ryujinx.Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; if (Directory.Exists(path)) { @@ -406,11 +405,11 @@ namespace Ryujinx.Ui /// An supported by this machine private static IAalOutput InitializeAudioEngine() { - if (SoundIoAudioOut.IsSupported) + /*if (SoundIoAudioOut.IsSupported) { return new SoundIoAudioOut(); } - else if (OpenALAudioOut.IsSupported) + else*/ if (OpenALAudioOut.IsSupported) { return new OpenALAudioOut(); } @@ -452,7 +451,7 @@ namespace Ryujinx.Ui IJsonFormatterResolver resolver = CompositeResolver.Create(new[] { StandardResolver.AllowPrivateSnakeCase }); ApplicationMetadata appMetadata; - + using (Stream stream = File.OpenRead(metadataPath)) { appMetadata = JsonSerializer.Deserialize(stream, resolver);