Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
518 changed files with 15170 additions and 12486 deletions
11
Ryujinx.Graphics.Gpu/ClassId.cs
Normal file
11
Ryujinx.Graphics.Gpu/ClassId.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
enum ClassId
|
||||
{
|
||||
Engine2D = 0x902d,
|
||||
Engine3D = 0xb197,
|
||||
EngineCompute = 0xb1c0,
|
||||
EngineInline2Memory = 0xa140,
|
||||
EngineDma = 0xb0b5
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics.Gpu/Constants.cs
Normal file
14
Ryujinx.Graphics.Gpu/Constants.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
static class Constants
|
||||
{
|
||||
public const int TotalCpUniformBuffers = 8;
|
||||
public const int TotalCpStorageBuffers = 16;
|
||||
public const int TotalGpUniformBuffers = 18;
|
||||
public const int TotalGpStorageBuffers = 16;
|
||||
public const int TotalRenderTargets = 8;
|
||||
public const int TotalShaderStages = 5;
|
||||
public const int TotalVertexBuffers = 16;
|
||||
public const int TotalViewports = 8;
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Gpu/Debugging.cs
Normal file
25
Ryujinx.Graphics.Gpu/Debugging.cs
Normal file
|
@ -0,0 +1,25 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
static class Debugging
|
||||
{
|
||||
public static void PrintTexInfo(string prefix, Image.Texture tex)
|
||||
{
|
||||
if (tex == null)
|
||||
{
|
||||
Console.WriteLine(prefix + " null");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
string range = $"{tex.Address:X}..{(tex.Address + tex.Size):X}";
|
||||
|
||||
int debugId = tex.HostTexture.GetStorageDebugId();
|
||||
|
||||
string str = $"{prefix} p {debugId:X8} {tex.Info.Target} {tex.Info.FormatInfo.Format} {tex.Info.Width}x{tex.Info.Height}x{tex.Info.DepthOrLayers} mips {tex.Info.Levels} addr {range}";
|
||||
|
||||
Console.WriteLine(str);
|
||||
}
|
||||
}
|
||||
}
|
181
Ryujinx.Graphics.Gpu/DmaPusher.cs
Normal file
181
Ryujinx.Graphics.Gpu/DmaPusher.cs
Normal file
|
@ -0,0 +1,181 @@
|
|||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
public class DmaPusher
|
||||
{
|
||||
private ConcurrentQueue<ulong> _ibBuffer;
|
||||
|
||||
private ulong _dmaPut;
|
||||
private ulong _dmaGet;
|
||||
|
||||
private struct DmaState
|
||||
{
|
||||
public int Method;
|
||||
public int SubChannel;
|
||||
public int MethodCount;
|
||||
public bool NonIncrementing;
|
||||
public bool IncrementOnce;
|
||||
public int LengthPending;
|
||||
}
|
||||
|
||||
private DmaState _state;
|
||||
|
||||
private bool _sliEnable;
|
||||
private bool _sliActive;
|
||||
|
||||
private bool _ibEnable;
|
||||
private bool _nonMain;
|
||||
|
||||
private ulong _dmaMGet;
|
||||
|
||||
private GpuContext _context;
|
||||
|
||||
private AutoResetEvent _event;
|
||||
|
||||
internal DmaPusher(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_ibBuffer = new ConcurrentQueue<ulong>();
|
||||
|
||||
_ibEnable = true;
|
||||
|
||||
_event = new AutoResetEvent(false);
|
||||
}
|
||||
|
||||
public void Push(ulong entry)
|
||||
{
|
||||
_ibBuffer.Enqueue(entry);
|
||||
|
||||
_event.Set();
|
||||
}
|
||||
|
||||
public bool WaitForCommands()
|
||||
{
|
||||
return _event.WaitOne(8);
|
||||
}
|
||||
|
||||
public void DispatchCalls()
|
||||
{
|
||||
while (Step());
|
||||
}
|
||||
|
||||
private bool Step()
|
||||
{
|
||||
if (_dmaGet != _dmaPut)
|
||||
{
|
||||
int word = _context.MemoryAccessor.ReadInt32(_dmaGet);
|
||||
|
||||
_dmaGet += 4;
|
||||
|
||||
if (!_nonMain)
|
||||
{
|
||||
_dmaMGet = _dmaGet;
|
||||
}
|
||||
|
||||
if (_state.LengthPending != 0)
|
||||
{
|
||||
_state.LengthPending = 0;
|
||||
_state.MethodCount = word & 0xffffff;
|
||||
}
|
||||
else if (_state.MethodCount != 0)
|
||||
{
|
||||
if (!_sliEnable || _sliActive)
|
||||
{
|
||||
CallMethod(word);
|
||||
}
|
||||
|
||||
if (!_state.NonIncrementing)
|
||||
{
|
||||
_state.Method++;
|
||||
}
|
||||
|
||||
if (_state.IncrementOnce)
|
||||
{
|
||||
_state.NonIncrementing = true;
|
||||
}
|
||||
|
||||
_state.MethodCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
int submissionMode = (word >> 29) & 7;
|
||||
|
||||
switch (submissionMode)
|
||||
{
|
||||
case 1:
|
||||
// Incrementing.
|
||||
SetNonImmediateState(word);
|
||||
|
||||
_state.NonIncrementing = false;
|
||||
_state.IncrementOnce = false;
|
||||
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Non-incrementing.
|
||||
SetNonImmediateState(word);
|
||||
|
||||
_state.NonIncrementing = true;
|
||||
_state.IncrementOnce = false;
|
||||
|
||||
break;
|
||||
|
||||
case 4:
|
||||
// Immediate.
|
||||
_state.Method = (word >> 0) & 0x1fff;
|
||||
_state.SubChannel = (word >> 13) & 7;
|
||||
_state.NonIncrementing = true;
|
||||
_state.IncrementOnce = false;
|
||||
|
||||
CallMethod((word >> 16) & 0x1fff);
|
||||
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Increment-once.
|
||||
SetNonImmediateState(word);
|
||||
|
||||
_state.NonIncrementing = false;
|
||||
_state.IncrementOnce = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (_ibEnable && _ibBuffer.TryDequeue(out ulong entry))
|
||||
{
|
||||
ulong length = (entry >> 42) & 0x1fffff;
|
||||
|
||||
_dmaGet = entry & 0xfffffffffc;
|
||||
_dmaPut = _dmaGet + length * 4;
|
||||
|
||||
_nonMain = (entry & (1UL << 41)) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void SetNonImmediateState(int word)
|
||||
{
|
||||
_state.Method = (word >> 0) & 0x1fff;
|
||||
_state.SubChannel = (word >> 13) & 7;
|
||||
_state.MethodCount = (word >> 16) & 0x1fff;
|
||||
}
|
||||
|
||||
private void CallMethod(int argument)
|
||||
{
|
||||
_context.Fifo.CallMethod(new MethodParams(
|
||||
_state.Method,
|
||||
argument,
|
||||
_state.SubChannel,
|
||||
_state.MethodCount));
|
||||
}
|
||||
}
|
||||
}
|
83
Ryujinx.Graphics.Gpu/Engine/Compute.cs
Normal file
83
Ryujinx.Graphics.Gpu/Engine/Compute.cs
Normal file
|
@ -0,0 +1,83 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
public void Dispatch(int argument)
|
||||
{
|
||||
uint dispatchParamsAddress = (uint)_context.State.Get<int>(MethodOffset.DispatchParamsAddress);
|
||||
|
||||
var dispatchParams = _context.MemoryAccessor.Read<ComputeParams>((ulong)dispatchParamsAddress << 8);
|
||||
|
||||
GpuVa shaderBaseAddress = _context.State.Get<GpuVa>(MethodOffset.ShaderBaseAddress);
|
||||
|
||||
ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)dispatchParams.ShaderOffset;
|
||||
|
||||
ComputeShader cs = _shaderCache.GetComputeShader(
|
||||
shaderGpuVa,
|
||||
dispatchParams.UnpackBlockSizeX(),
|
||||
dispatchParams.UnpackBlockSizeY(),
|
||||
dispatchParams.UnpackBlockSizeZ());
|
||||
|
||||
_context.Renderer.ComputePipeline.SetProgram(cs.Interface);
|
||||
|
||||
ShaderProgramInfo info = cs.Shader.Info;
|
||||
|
||||
uint sbEnableMask = 0;
|
||||
uint ubEnableMask = dispatchParams.UnpackUniformBuffersEnableMask();
|
||||
|
||||
for (int index = 0; index < dispatchParams.UniformBuffers.Length; index++)
|
||||
{
|
||||
if ((ubEnableMask & (1 << index)) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong gpuVa = dispatchParams.UniformBuffers[index].PackAddress();
|
||||
ulong size = dispatchParams.UniformBuffers[index].UnpackSize();
|
||||
|
||||
_bufferManager.SetComputeUniformBuffer(index, gpuVa, size);
|
||||
}
|
||||
|
||||
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||
{
|
||||
BufferDescriptor sb = info.SBuffers[index];
|
||||
|
||||
sbEnableMask |= 1u << sb.Slot;
|
||||
|
||||
ulong sbDescAddress = _bufferManager.GetComputeUniformBufferAddress(0);
|
||||
|
||||
int sbDescOffset = 0x310 + sb.Slot * 0x10;
|
||||
|
||||
sbDescAddress += (ulong)sbDescOffset;
|
||||
|
||||
Span<byte> sbDescriptorData = _context.PhysicalMemory.Read(sbDescAddress, 0x10);
|
||||
|
||||
SbDescriptor sbDescriptor = MemoryMarshal.Cast<byte, SbDescriptor>(sbDescriptorData)[0];
|
||||
|
||||
_bufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size);
|
||||
}
|
||||
|
||||
ubEnableMask = 0;
|
||||
|
||||
for (int index = 0; index < info.CBuffers.Count; index++)
|
||||
{
|
||||
ubEnableMask |= 1u << info.CBuffers[index].Slot;
|
||||
}
|
||||
|
||||
_bufferManager.SetComputeStorageBufferEnableMask(sbEnableMask);
|
||||
_bufferManager.SetComputeUniformBufferEnableMask(ubEnableMask);
|
||||
|
||||
_bufferManager.CommitComputeBindings();
|
||||
|
||||
_context.Renderer.ComputePipeline.Dispatch(
|
||||
dispatchParams.UnpackGridSizeX(),
|
||||
dispatchParams.UnpackGridSizeY(),
|
||||
dispatchParams.UnpackGridSizeZ());
|
||||
}
|
||||
}
|
||||
}
|
126
Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs
Normal file
126
Ryujinx.Graphics.Gpu/Engine/ComputeParams.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
struct UniformBufferParams
|
||||
{
|
||||
public int AddressLow;
|
||||
public int AddressHighAndSize;
|
||||
|
||||
public ulong PackAddress()
|
||||
{
|
||||
return (uint)AddressLow | ((ulong)(AddressHighAndSize & 0xff) << 32);
|
||||
}
|
||||
|
||||
public ulong UnpackSize()
|
||||
{
|
||||
return (ulong)((AddressHighAndSize >> 15) & 0x1ffff);
|
||||
}
|
||||
}
|
||||
|
||||
struct ComputeParams
|
||||
{
|
||||
public int Unknown0;
|
||||
public int Unknown1;
|
||||
public int Unknown2;
|
||||
public int Unknown3;
|
||||
public int Unknown4;
|
||||
public int Unknown5;
|
||||
public int Unknown6;
|
||||
public int Unknown7;
|
||||
public int ShaderOffset;
|
||||
public int Unknown9;
|
||||
public int Unknown10;
|
||||
public int Unknown11;
|
||||
public int GridSizeX;
|
||||
public int GridSizeYZ;
|
||||
public int Unknown14;
|
||||
public int Unknown15;
|
||||
public int Unknown16;
|
||||
public int Unknown17;
|
||||
public int BlockSizeX;
|
||||
public int BlockSizeYZ;
|
||||
public int UniformBuffersConfig;
|
||||
public int Unknown21;
|
||||
public int Unknown22;
|
||||
public int Unknown23;
|
||||
public int Unknown24;
|
||||
public int Unknown25;
|
||||
public int Unknown26;
|
||||
public int Unknown27;
|
||||
public int Unknown28;
|
||||
|
||||
private UniformBufferParams _uniformBuffer0;
|
||||
private UniformBufferParams _uniformBuffer1;
|
||||
private UniformBufferParams _uniformBuffer2;
|
||||
private UniformBufferParams _uniformBuffer3;
|
||||
private UniformBufferParams _uniformBuffer4;
|
||||
private UniformBufferParams _uniformBuffer5;
|
||||
private UniformBufferParams _uniformBuffer6;
|
||||
private UniformBufferParams _uniformBuffer7;
|
||||
|
||||
public Span<UniformBufferParams> UniformBuffers
|
||||
{
|
||||
get
|
||||
{
|
||||
return MemoryMarshal.CreateSpan(ref _uniformBuffer0, 8);
|
||||
}
|
||||
}
|
||||
|
||||
public int Unknown45;
|
||||
public int Unknown46;
|
||||
public int Unknown47;
|
||||
public int Unknown48;
|
||||
public int Unknown49;
|
||||
public int Unknown50;
|
||||
public int Unknown51;
|
||||
public int Unknown52;
|
||||
public int Unknown53;
|
||||
public int Unknown54;
|
||||
public int Unknown55;
|
||||
public int Unknown56;
|
||||
public int Unknown57;
|
||||
public int Unknown58;
|
||||
public int Unknown59;
|
||||
public int Unknown60;
|
||||
public int Unknown61;
|
||||
public int Unknown62;
|
||||
public int Unknown63;
|
||||
|
||||
public int UnpackGridSizeX()
|
||||
{
|
||||
return GridSizeX & 0x7fffffff;
|
||||
}
|
||||
|
||||
public int UnpackGridSizeY()
|
||||
{
|
||||
return GridSizeYZ & 0xffff;
|
||||
}
|
||||
|
||||
public int UnpackGridSizeZ()
|
||||
{
|
||||
return (GridSizeYZ >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
public int UnpackBlockSizeX()
|
||||
{
|
||||
return (BlockSizeX >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
public int UnpackBlockSizeY()
|
||||
{
|
||||
return BlockSizeYZ & 0xffff;
|
||||
}
|
||||
|
||||
public int UnpackBlockSizeZ()
|
||||
{
|
||||
return (BlockSizeYZ >> 16) & 0xffff;
|
||||
}
|
||||
|
||||
public uint UnpackUniformBuffersEnableMask()
|
||||
{
|
||||
return (uint)UniformBuffersConfig & 0xff;
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs
Normal file
18
Ryujinx.Graphics.Gpu/Engine/ComputeShader.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
class ComputeShader
|
||||
{
|
||||
public IProgram Interface { get; set; }
|
||||
|
||||
public ShaderProgram Shader { get; }
|
||||
|
||||
public ComputeShader(IProgram program, ShaderProgram shader)
|
||||
{
|
||||
Interface = program;
|
||||
Shader = shader;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs
Normal file
17
Ryujinx.Graphics.Gpu/Engine/GraphicsShader.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
class GraphicsShader
|
||||
{
|
||||
public IProgram Interface { get; set; }
|
||||
|
||||
public ShaderProgram[] Shader { get; }
|
||||
|
||||
public GraphicsShader()
|
||||
{
|
||||
Shader = new ShaderProgram[5];
|
||||
}
|
||||
}
|
||||
}
|
42
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
Normal file
42
Ryujinx.Graphics.Gpu/Engine/Inline2Memory.cs
Normal file
|
@ -0,0 +1,42 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private Inline2MemoryParams _params;
|
||||
|
||||
private bool _isLinear;
|
||||
|
||||
private int _offset;
|
||||
private int _size;
|
||||
|
||||
public void Execute(int argument)
|
||||
{
|
||||
_params = _context.State.Get<Inline2MemoryParams>(MethodOffset.Inline2MemoryParams);
|
||||
|
||||
_isLinear = (argument & 1) != 0;
|
||||
|
||||
_offset = 0;
|
||||
_size = _params.LineLengthIn * _params.LineCount;
|
||||
}
|
||||
|
||||
public void PushData(int argument)
|
||||
{
|
||||
if (_isLinear)
|
||||
{
|
||||
for (int shift = 0; shift < 32 && _offset < _size; shift += 8, _offset++)
|
||||
{
|
||||
ulong gpuVa = _params.DstAddress.Pack() + (ulong)_offset;
|
||||
|
||||
_context.MemoryAccessor.Write(gpuVa, new byte[] { (byte)(argument >> shift) });
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
55
Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
Normal file
55
Ryujinx.Graphics.Gpu/Engine/MethodClear.cs
Normal file
|
@ -0,0 +1,55 @@
|
|||
using Ryujinx.Graphics.GAL.Color;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private void Clear(int argument)
|
||||
{
|
||||
UpdateState();
|
||||
|
||||
bool clearDepth = (argument & 1) != 0;
|
||||
bool clearStencil = (argument & 2) != 0;
|
||||
|
||||
uint componentMask = (uint)((argument >> 2) & 0xf);
|
||||
|
||||
int index = (argument >> 6) & 0xf;
|
||||
|
||||
if (componentMask != 0)
|
||||
{
|
||||
ClearColors clearColor = _context.State.GetClearColors();
|
||||
|
||||
ColorF color = new ColorF(
|
||||
clearColor.Red,
|
||||
clearColor.Green,
|
||||
clearColor.Blue,
|
||||
clearColor.Alpha);
|
||||
|
||||
_context.Renderer.GraphicsPipeline.ClearRenderTargetColor(
|
||||
index,
|
||||
componentMask,
|
||||
color);
|
||||
}
|
||||
|
||||
if (clearDepth || clearStencil)
|
||||
{
|
||||
float depthValue = _context.State.GetClearDepthValue();
|
||||
int stencilValue = _context.State.GetClearStencilValue();
|
||||
|
||||
int stencilMask = 0;
|
||||
|
||||
if (clearStencil)
|
||||
{
|
||||
stencilMask = _context.State.GetStencilTestState().FrontMask;
|
||||
}
|
||||
|
||||
_context.Renderer.GraphicsPipeline.ClearRenderTargetDepthStencil(
|
||||
depthValue,
|
||||
clearDepth,
|
||||
stencilValue,
|
||||
stencilMask);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
79
Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
Normal file
79
Ryujinx.Graphics.Gpu/Engine/MethodCopyBuffer.cs
Normal file
|
@ -0,0 +1,79 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private void CopyBuffer(int argument)
|
||||
{
|
||||
var cbp = _context.State.Get<CopyBufferParams>(MethodOffset.CopyBufferParams);
|
||||
|
||||
var swizzle = _context.State.Get<CopyBufferSwizzle>(MethodOffset.CopyBufferSwizzle);
|
||||
|
||||
bool srcLinear = (argument & (1 << 7)) != 0;
|
||||
bool dstLinear = (argument & (1 << 8)) != 0;
|
||||
bool copy2D = (argument & (1 << 9)) != 0;
|
||||
|
||||
int size = cbp.XCount;
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (copy2D)
|
||||
{
|
||||
// Buffer to texture copy.
|
||||
int srcBpp = swizzle.UnpackSrcComponentsCount() * swizzle.UnpackComponentSize();
|
||||
int dstBpp = swizzle.UnpackDstComponentsCount() * swizzle.UnpackComponentSize();
|
||||
|
||||
var dst = _context.State.Get<CopyBufferTexture>(MethodOffset.CopyBufferDstTexture);
|
||||
var src = _context.State.Get<CopyBufferTexture>(MethodOffset.CopyBufferSrcTexture);
|
||||
|
||||
var srcCalculator = new OffsetCalculator(
|
||||
src.Width,
|
||||
src.Height,
|
||||
cbp.SrcStride,
|
||||
srcLinear,
|
||||
src.MemoryLayout.UnpackGobBlocksInY(),
|
||||
srcBpp);
|
||||
|
||||
var dstCalculator = new OffsetCalculator(
|
||||
dst.Width,
|
||||
dst.Height,
|
||||
cbp.DstStride,
|
||||
dstLinear,
|
||||
dst.MemoryLayout.UnpackGobBlocksInY(),
|
||||
dstBpp);
|
||||
|
||||
ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack());
|
||||
ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack());
|
||||
|
||||
for (int y = 0; y < cbp.YCount; y++)
|
||||
for (int x = 0; x < cbp.XCount; x++)
|
||||
{
|
||||
int srcOffset = srcCalculator.GetOffset(src.RegionX + x, src.RegionY + y);
|
||||
int dstOffset = dstCalculator.GetOffset(dst.RegionX + x, dst.RegionY + y);
|
||||
|
||||
ulong srcAddress = srcBaseAddress + (ulong)srcOffset;
|
||||
ulong dstAddress = dstBaseAddress + (ulong)dstOffset;
|
||||
|
||||
Span<byte> pixel = _context.PhysicalMemory.Read(srcAddress, (ulong)srcBpp);
|
||||
|
||||
_context.PhysicalMemory.Write(dstAddress, pixel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Buffer to buffer copy.
|
||||
_bufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size);
|
||||
|
||||
Span<byte> data = _context.MemoryAccessor.Read(cbp.SrcAddress.Pack(), (uint)size);
|
||||
|
||||
_context.MemoryAccessor.Write(cbp.DstAddress.Pack(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
70
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs
Normal file
70
Ryujinx.Graphics.Gpu/Engine/MethodCopyTexture.cs
Normal file
|
@ -0,0 +1,70 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private void CopyTexture(int argument)
|
||||
{
|
||||
CopyTexture dstCopyTexture = _context.State.GetCopyDstTexture();
|
||||
CopyTexture srcCopyTexture = _context.State.GetCopySrcTexture();
|
||||
|
||||
Image.Texture srcTexture = _textureManager.FindOrCreateTexture(srcCopyTexture);
|
||||
|
||||
if (srcTexture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// When the source texture that was found has a depth format,
|
||||
// we must enforce the target texture also has a depth format,
|
||||
// as copies between depth and color formats are not allowed.
|
||||
if (srcTexture.Format == Format.D32Float)
|
||||
{
|
||||
dstCopyTexture.Format = RtFormat.D32Float;
|
||||
}
|
||||
|
||||
Image.Texture dstTexture = _textureManager.FindOrCreateTexture(dstCopyTexture);
|
||||
|
||||
if (dstTexture == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CopyTextureControl control = _context.State.GetCopyTextureControl();
|
||||
|
||||
CopyRegion region = _context.State.GetCopyRegion();
|
||||
|
||||
int srcX1 = (int)(region.SrcXF >> 32);
|
||||
int srcY1 = (int)(region.SrcYF >> 32);
|
||||
|
||||
int srcX2 = (int)((region.SrcXF + region.SrcWidthRF * region.DstWidth) >> 32);
|
||||
int srcY2 = (int)((region.SrcYF + region.SrcHeightRF * region.DstHeight) >> 32);
|
||||
|
||||
int dstX1 = region.DstX;
|
||||
int dstY1 = region.DstY;
|
||||
|
||||
int dstX2 = region.DstX + region.DstWidth;
|
||||
int dstY2 = region.DstY + region.DstHeight;
|
||||
|
||||
Extents2D srcRegion = new Extents2D(
|
||||
srcX1 / srcTexture.Info.SamplesInX,
|
||||
srcY1 / srcTexture.Info.SamplesInY,
|
||||
srcX2 / srcTexture.Info.SamplesInX,
|
||||
srcY2 / srcTexture.Info.SamplesInY);
|
||||
|
||||
Extents2D dstRegion = new Extents2D(
|
||||
dstX1 / dstTexture.Info.SamplesInX,
|
||||
dstY1 / dstTexture.Info.SamplesInY,
|
||||
dstX2 / dstTexture.Info.SamplesInX,
|
||||
dstY2 / dstTexture.Info.SamplesInY);
|
||||
|
||||
bool linearFilter = control.UnpackLinearFilter();
|
||||
|
||||
srcTexture.HostTexture.CopyTo(dstTexture.HostTexture, srcRegion, dstRegion, linearFilter);
|
||||
|
||||
dstTexture.Modified = true;
|
||||
}
|
||||
}
|
||||
}
|
133
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
Normal file
133
Ryujinx.Graphics.Gpu/Engine/MethodDraw.cs
Normal file
|
@ -0,0 +1,133 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private bool _drawIndexed;
|
||||
|
||||
private int _firstIndex;
|
||||
private int _indexCount;
|
||||
|
||||
private bool _instancedHasState;
|
||||
private bool _instancedIndexed;
|
||||
|
||||
private int _instancedFirstIndex;
|
||||
private int _instancedFirstVertex;
|
||||
private int _instancedFirstInstance;
|
||||
private int _instancedIndexCount;
|
||||
private int _instancedDrawStateFirst;
|
||||
private int _instancedDrawStateCount;
|
||||
|
||||
private int _instanceIndex;
|
||||
|
||||
public PrimitiveType PrimitiveType { get; private set; }
|
||||
|
||||
private void DrawEnd(int argument)
|
||||
{
|
||||
UpdateState();
|
||||
|
||||
bool instanced = _vsUsesInstanceId || _isAnyVbInstanced;
|
||||
|
||||
if (instanced)
|
||||
{
|
||||
if (!_instancedHasState)
|
||||
{
|
||||
_instancedHasState = true;
|
||||
|
||||
_instancedIndexed = _drawIndexed;
|
||||
|
||||
_instancedFirstIndex = _firstIndex;
|
||||
_instancedFirstVertex = _context.State.GetBaseVertex();
|
||||
_instancedFirstInstance = _context.State.GetBaseInstance();
|
||||
|
||||
_instancedIndexCount = _indexCount;
|
||||
|
||||
VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState();
|
||||
|
||||
_instancedDrawStateFirst = drawState.First;
|
||||
_instancedDrawStateCount = drawState.Count;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int firstInstance = _context.State.GetBaseInstance();
|
||||
|
||||
if (_drawIndexed)
|
||||
{
|
||||
_drawIndexed = false;
|
||||
|
||||
int firstVertex = _context.State.GetBaseVertex();
|
||||
|
||||
_context.Renderer.GraphicsPipeline.DrawIndexed(
|
||||
_indexCount,
|
||||
1,
|
||||
_firstIndex,
|
||||
firstVertex,
|
||||
firstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState();
|
||||
|
||||
_context.Renderer.GraphicsPipeline.Draw(
|
||||
drawState.Count,
|
||||
1,
|
||||
drawState.First,
|
||||
firstInstance);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawBegin(int argument)
|
||||
{
|
||||
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetPrimitiveTopology(type.Convert());
|
||||
|
||||
PrimitiveType = type;
|
||||
|
||||
if ((argument & (1 << 26)) != 0)
|
||||
{
|
||||
_instanceIndex++;
|
||||
}
|
||||
else if ((argument & (1 << 27)) == 0)
|
||||
{
|
||||
_instanceIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetIndexCount(int argument)
|
||||
{
|
||||
_drawIndexed = true;
|
||||
}
|
||||
|
||||
public void PerformDeferredDraws()
|
||||
{
|
||||
// Perform any pending instanced draw.
|
||||
if (_instancedHasState)
|
||||
{
|
||||
_instancedHasState = false;
|
||||
|
||||
if (_instancedIndexed)
|
||||
{
|
||||
_context.Renderer.GraphicsPipeline.DrawIndexed(
|
||||
_instancedIndexCount,
|
||||
_instanceIndex + 1,
|
||||
_instancedFirstIndex,
|
||||
_instancedFirstVertex,
|
||||
_instancedFirstInstance);
|
||||
}
|
||||
else
|
||||
{
|
||||
_context.Renderer.GraphicsPipeline.Draw(
|
||||
_instancedDrawStateCount,
|
||||
_instanceIndex + 1,
|
||||
_instancedDrawStateFirst,
|
||||
_instancedFirstInstance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
100
Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
Normal file
100
Ryujinx.Graphics.Gpu/Engine/MethodReport.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private ulong _runningCounter;
|
||||
|
||||
private void Report(int argument)
|
||||
{
|
||||
ReportMode mode = (ReportMode)(argument & 3);
|
||||
|
||||
ReportCounterType type = (ReportCounterType)((argument >> 23) & 0x1f);
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case ReportMode.Semaphore: ReportSemaphore(); break;
|
||||
case ReportMode.Counter: ReportCounter(type); break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ReportSemaphore()
|
||||
{
|
||||
ReportState state = _context.State.GetReportState();
|
||||
|
||||
_context.MemoryAccessor.Write(state.Address.Pack(), state.Payload);
|
||||
|
||||
_context.AdvanceSequence();
|
||||
}
|
||||
|
||||
private struct CounterData
|
||||
{
|
||||
public ulong Counter;
|
||||
public ulong Timestamp;
|
||||
}
|
||||
|
||||
private void ReportCounter(ReportCounterType type)
|
||||
{
|
||||
CounterData counterData = new CounterData();
|
||||
|
||||
ulong counter = 0;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ReportCounterType.Zero:
|
||||
counter = 0;
|
||||
break;
|
||||
case ReportCounterType.SamplesPassed:
|
||||
counter = _context.Renderer.GetCounter(CounterType.SamplesPassed);
|
||||
break;
|
||||
case ReportCounterType.PrimitivesGenerated:
|
||||
counter = _context.Renderer.GetCounter(CounterType.PrimitivesGenerated);
|
||||
break;
|
||||
case ReportCounterType.TransformFeedbackPrimitivesWritten:
|
||||
counter = _context.Renderer.GetCounter(CounterType.TransformFeedbackPrimitivesWritten);
|
||||
break;
|
||||
}
|
||||
|
||||
ulong ticks;
|
||||
|
||||
if (GraphicsConfig.FastGpuTime)
|
||||
{
|
||||
ticks = _runningCounter++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds);
|
||||
}
|
||||
|
||||
counterData.Counter = counter;
|
||||
counterData.Timestamp = ticks;
|
||||
|
||||
Span<CounterData> counterDataSpan = MemoryMarshal.CreateSpan(ref counterData, 1);
|
||||
|
||||
Span<byte> data = MemoryMarshal.Cast<CounterData, byte>(counterDataSpan);
|
||||
|
||||
ReportState state = _context.State.GetReportState();
|
||||
|
||||
_context.MemoryAccessor.Write(state.Address.Pack(), data);
|
||||
}
|
||||
|
||||
private static ulong ConvertNanosecondsToTicks(ulong nanoseconds)
|
||||
{
|
||||
// We need to divide first to avoid overflows.
|
||||
// We fix up the result later by calculating the difference and adding
|
||||
// that to the result.
|
||||
ulong divided = nanoseconds / 625;
|
||||
|
||||
ulong rounded = divided * 625;
|
||||
|
||||
ulong errorBias = ((nanoseconds - rounded) * 384) / 625;
|
||||
|
||||
return divided * 384 + errorBias;
|
||||
}
|
||||
}
|
||||
}
|
26
Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
Normal file
26
Ryujinx.Graphics.Gpu/Engine/MethodResetCounter.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private void ResetCounter(int argument)
|
||||
{
|
||||
ResetCounterType type = (ResetCounterType)argument;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ResetCounterType.SamplesPassed:
|
||||
_context.Renderer.ResetCounter(CounterType.SamplesPassed);
|
||||
break;
|
||||
case ResetCounterType.PrimitivesGenerated:
|
||||
_context.Renderer.ResetCounter(CounterType.PrimitivesGenerated);
|
||||
break;
|
||||
case ResetCounterType.TransformFeedbackPrimitivesWritten:
|
||||
_context.Renderer.ResetCounter(CounterType.TransformFeedbackPrimitivesWritten);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
52
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
Normal file
52
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferBind.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private void UniformBufferBind0(int argument)
|
||||
{
|
||||
UniformBufferBind(argument, ShaderType.Vertex);
|
||||
}
|
||||
|
||||
private void UniformBufferBind1(int argument)
|
||||
{
|
||||
UniformBufferBind(argument, ShaderType.TessellationControl);
|
||||
}
|
||||
|
||||
private void UniformBufferBind2(int argument)
|
||||
{
|
||||
UniformBufferBind(argument, ShaderType.TessellationEvaluation);
|
||||
}
|
||||
|
||||
private void UniformBufferBind3(int argument)
|
||||
{
|
||||
UniformBufferBind(argument, ShaderType.Geometry);
|
||||
}
|
||||
|
||||
private void UniformBufferBind4(int argument)
|
||||
{
|
||||
UniformBufferBind(argument, ShaderType.Fragment);
|
||||
}
|
||||
|
||||
private void UniformBufferBind(int argument, ShaderType type)
|
||||
{
|
||||
bool enable = (argument & 1) != 0;
|
||||
|
||||
int index = (argument >> 4) & 0x1f;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
UniformBufferState uniformBuffer = _context.State.GetUniformBufferState();
|
||||
|
||||
ulong address = uniformBuffer.Address.Pack();
|
||||
|
||||
_bufferManager.SetGraphicsUniformBuffer((int)type, index, address, (uint)uniformBuffer.Size);
|
||||
}
|
||||
else
|
||||
{
|
||||
_bufferManager.SetGraphicsUniformBuffer((int)type, index, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
Normal file
18
Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs
Normal file
|
@ -0,0 +1,18 @@
|
|||
using Ryujinx.Graphics.Gpu.State;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private void UniformBufferUpdate(int argument)
|
||||
{
|
||||
UniformBufferState uniformBuffer = _context.State.GetUniformBufferState();
|
||||
|
||||
_context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument);
|
||||
|
||||
_context.State.SetUniformBufferOffset(uniformBuffer.Offset + 4);
|
||||
|
||||
_context.AdvanceSequence();
|
||||
}
|
||||
}
|
||||
}
|
784
Ryujinx.Graphics.Gpu/Engine/Methods.cs
Normal file
784
Ryujinx.Graphics.Gpu/Engine/Methods.cs
Normal file
|
@ -0,0 +1,784 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Blend;
|
||||
using Ryujinx.Graphics.GAL.DepthStencil;
|
||||
using Ryujinx.Graphics.GAL.InputAssembler;
|
||||
using Ryujinx.Graphics.GAL.Texture;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
partial class Methods
|
||||
{
|
||||
private GpuContext _context;
|
||||
|
||||
private ShaderCache _shaderCache;
|
||||
|
||||
private BufferManager _bufferManager;
|
||||
private TextureManager _textureManager;
|
||||
|
||||
public TextureManager TextureManager => _textureManager;
|
||||
|
||||
private bool _isAnyVbInstanced;
|
||||
private bool _vsUsesInstanceId;
|
||||
|
||||
public Methods(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_shaderCache = new ShaderCache(_context);
|
||||
|
||||
_bufferManager = new BufferManager(context);
|
||||
_textureManager = new TextureManager(context, _bufferManager);
|
||||
|
||||
RegisterCallbacks();
|
||||
}
|
||||
|
||||
private void RegisterCallbacks()
|
||||
{
|
||||
_context.State.RegisterCopyBufferCallback(CopyBuffer);
|
||||
_context.State.RegisterCopyTextureCallback(CopyTexture);
|
||||
|
||||
_context.State.RegisterDrawEndCallback(DrawEnd);
|
||||
|
||||
_context.State.RegisterDrawBeginCallback(DrawBegin);
|
||||
|
||||
_context.State.RegisterSetIndexCountCallback(SetIndexCount);
|
||||
|
||||
_context.State.RegisterClearCallback(Clear);
|
||||
|
||||
_context.State.RegisterReportCallback(Report);
|
||||
|
||||
_context.State.RegisterUniformBufferUpdateCallback(UniformBufferUpdate);
|
||||
|
||||
_context.State.RegisterUniformBufferBind0Callback(UniformBufferBind0);
|
||||
_context.State.RegisterUniformBufferBind1Callback(UniformBufferBind1);
|
||||
_context.State.RegisterUniformBufferBind2Callback(UniformBufferBind2);
|
||||
_context.State.RegisterUniformBufferBind3Callback(UniformBufferBind3);
|
||||
_context.State.RegisterUniformBufferBind4Callback(UniformBufferBind4);
|
||||
|
||||
_context.State.RegisterCallback(MethodOffset.InvalidateTextures, InvalidateTextures);
|
||||
|
||||
_context.State.RegisterCallback(MethodOffset.ResetCounter, ResetCounter);
|
||||
|
||||
_context.State.RegisterCallback(MethodOffset.Inline2MemoryExecute, Execute);
|
||||
_context.State.RegisterCallback(MethodOffset.Inline2MemoryPushData, PushData);
|
||||
|
||||
_context.State.RegisterCallback(MethodOffset.Dispatch, Dispatch);
|
||||
}
|
||||
|
||||
public Image.Texture GetTexture(ulong address) => _textureManager.Find2(address);
|
||||
|
||||
private void UpdateState()
|
||||
{
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.Any) == 0)
|
||||
{
|
||||
CommitBindings();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Shaders must be the first one to be updated if modified, because
|
||||
// some of the other state depends on information from the currently
|
||||
// bound shaders.
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.ShaderState) != 0)
|
||||
{
|
||||
UpdateShaderState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.RenderTargetGroup) != 0)
|
||||
{
|
||||
UpdateRenderTargetGroupState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.DepthTestState) != 0)
|
||||
{
|
||||
UpdateDepthTestState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.ViewportTransform) != 0)
|
||||
{
|
||||
UpdateViewportTransform();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.DepthBiasState) != 0)
|
||||
{
|
||||
UpdateDepthBiasState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.StencilTestState) != 0)
|
||||
{
|
||||
UpdateStencilTestState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.SamplerPoolState) != 0)
|
||||
{
|
||||
UpdateSamplerPoolState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.TexturePoolState) != 0)
|
||||
{
|
||||
UpdateTexturePoolState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.InputAssemblerGroup) != 0)
|
||||
{
|
||||
UpdateInputAssemblerGroupState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.FaceState) != 0)
|
||||
{
|
||||
UpdateFaceState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.RtColorMask) != 0)
|
||||
{
|
||||
UpdateRtColorMask();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.BlendState) != 0)
|
||||
{
|
||||
UpdateBlendState();
|
||||
}
|
||||
|
||||
_context.State.StateWriteFlags &= ~StateWriteFlags.Any;
|
||||
|
||||
CommitBindings();
|
||||
}
|
||||
|
||||
private void CommitBindings()
|
||||
{
|
||||
_bufferManager.CommitBindings();
|
||||
_textureManager.CommitBindings();
|
||||
}
|
||||
|
||||
public void InvalidateRange(ulong address, ulong size)
|
||||
{
|
||||
_bufferManager.InvalidateRange(address, size);
|
||||
_textureManager.InvalidateRange(address, size);
|
||||
}
|
||||
|
||||
public void InvalidateTextureRange(ulong address, ulong size)
|
||||
{
|
||||
_textureManager.InvalidateRange(address, size);
|
||||
}
|
||||
|
||||
private void UpdateRenderTargetGroupState()
|
||||
{
|
||||
TextureMsaaMode msaaMode = _context.State.GetRtMsaaMode();
|
||||
|
||||
int samplesInX = msaaMode.SamplesInX();
|
||||
int samplesInY = msaaMode.SamplesInY();
|
||||
|
||||
Image.Texture color3D = Get3DRenderTarget(samplesInX, samplesInY);
|
||||
|
||||
if (color3D == null)
|
||||
{
|
||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||
{
|
||||
RtColorState colorState = _context.State.GetRtColorState(index);
|
||||
|
||||
if (!IsRtEnabled(colorState))
|
||||
{
|
||||
_textureManager.SetRenderTargetColor(index, null);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
Image.Texture color = _textureManager.FindOrCreateTexture(
|
||||
colorState,
|
||||
samplesInX,
|
||||
samplesInY);
|
||||
|
||||
_textureManager.SetRenderTargetColor(index, color);
|
||||
|
||||
color.Modified = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_textureManager.SetRenderTargetColor3D(color3D);
|
||||
|
||||
color3D.Modified = true;
|
||||
}
|
||||
|
||||
bool dsEnable = _context.State.Get<bool>(MethodOffset.RtDepthStencilEnable);
|
||||
|
||||
Image.Texture depthStencil = null;
|
||||
|
||||
if (dsEnable)
|
||||
{
|
||||
var dsState = _context.State.GetRtDepthStencilState();
|
||||
var dsSize = _context.State.GetRtDepthStencilSize();
|
||||
|
||||
depthStencil = _textureManager.FindOrCreateTexture(
|
||||
dsState,
|
||||
dsSize,
|
||||
samplesInX,
|
||||
samplesInY);
|
||||
}
|
||||
|
||||
_textureManager.SetRenderTargetDepthStencil(depthStencil);
|
||||
}
|
||||
|
||||
private Image.Texture Get3DRenderTarget(int samplesInX, int samplesInY)
|
||||
{
|
||||
RtColorState colorState0 = _context.State.GetRtColorState(0);
|
||||
|
||||
if (!IsRtEnabled(colorState0) || !colorState0.MemoryLayout.UnpackIsTarget3D() || colorState0.Depth != 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int slices = 1;
|
||||
int unused = 0;
|
||||
|
||||
for (int index = 1; index < Constants.TotalRenderTargets; index++)
|
||||
{
|
||||
RtColorState colorState = _context.State.GetRtColorState(index);
|
||||
|
||||
if (!IsRtEnabled(colorState))
|
||||
{
|
||||
unused++;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (colorState.MemoryLayout.UnpackIsTarget3D() && colorState.Depth == 1)
|
||||
{
|
||||
slices++;
|
||||
}
|
||||
}
|
||||
|
||||
if (slices + unused == Constants.TotalRenderTargets)
|
||||
{
|
||||
colorState0.Depth = slices;
|
||||
|
||||
return _textureManager.FindOrCreateTexture(colorState0, samplesInX, samplesInY);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool IsRtEnabled(RtColorState colorState)
|
||||
{
|
||||
// Colors are disabled by writing 0 to the format.
|
||||
return colorState.Format != 0 && colorState.WidthOrStride != 0;
|
||||
}
|
||||
|
||||
private void UpdateDepthTestState()
|
||||
{
|
||||
_context.Renderer.GraphicsPipeline.SetDepthTest(new DepthTestDescriptor(
|
||||
_context.State.GetDepthTestEnable().IsTrue(),
|
||||
_context.State.GetDepthWriteEnable().IsTrue(),
|
||||
_context.State.GetDepthTestFunc()));
|
||||
}
|
||||
|
||||
private void UpdateViewportTransform()
|
||||
{
|
||||
Viewport[] viewports = new Viewport[Constants.TotalViewports];
|
||||
|
||||
for (int index = 0; index < Constants.TotalViewports; index++)
|
||||
{
|
||||
var transform = _context.State.Get<ViewportTransform>(MethodOffset.ViewportTransform + index * 8);
|
||||
var extents = _context.State.Get<ViewportExtents> (MethodOffset.ViewportExtents + index * 4);
|
||||
|
||||
float x = transform.TranslateX - MathF.Abs(transform.ScaleX);
|
||||
float y = transform.TranslateY - MathF.Abs(transform.ScaleY);
|
||||
|
||||
float width = transform.ScaleX * 2;
|
||||
float height = transform.ScaleY * 2;
|
||||
|
||||
RectangleF region = new RectangleF(x, y, width, height);
|
||||
|
||||
viewports[index] = new Viewport(
|
||||
region,
|
||||
transform.UnpackSwizzleX(),
|
||||
transform.UnpackSwizzleY(),
|
||||
transform.UnpackSwizzleZ(),
|
||||
transform.UnpackSwizzleW(),
|
||||
extents.DepthNear,
|
||||
extents.DepthFar);
|
||||
}
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetViewports(0, viewports);
|
||||
}
|
||||
|
||||
private void UpdateDepthBiasState()
|
||||
{
|
||||
var polygonOffset = _context.State.Get<DepthBiasState>(MethodOffset.DepthBiasState);
|
||||
|
||||
float factor = _context.State.Get<float>(MethodOffset.DepthBiasFactor);
|
||||
float units = _context.State.Get<float>(MethodOffset.DepthBiasUnits);
|
||||
float clamp = _context.State.Get<float>(MethodOffset.DepthBiasClamp);
|
||||
|
||||
PolygonModeMask enables = 0;
|
||||
|
||||
enables = (polygonOffset.PointEnable.IsTrue() ? PolygonModeMask.Point : 0);
|
||||
enables |= (polygonOffset.LineEnable.IsTrue() ? PolygonModeMask.Line : 0);
|
||||
enables |= (polygonOffset.FillEnable.IsTrue() ? PolygonModeMask.Fill : 0);
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetDepthBias(enables, factor, units, clamp);
|
||||
}
|
||||
|
||||
private void UpdateStencilTestState()
|
||||
{
|
||||
StencilBackMasks backMasks = _context.State.GetStencilBackMasks();
|
||||
StencilTestState test = _context.State.GetStencilTestState();
|
||||
StencilBackTestState backTest = _context.State.GetStencilBackTestState();
|
||||
|
||||
CompareOp backFunc;
|
||||
StencilOp backSFail;
|
||||
StencilOp backDpPass;
|
||||
StencilOp backDpFail;
|
||||
int backFuncRef;
|
||||
int backFuncMask;
|
||||
int backMask;
|
||||
|
||||
if (backTest.TwoSided.IsTrue())
|
||||
{
|
||||
backFunc = backTest.BackFunc;
|
||||
backSFail = backTest.BackSFail;
|
||||
backDpPass = backTest.BackDpPass;
|
||||
backDpFail = backTest.BackDpFail;
|
||||
backFuncRef = backMasks.FuncRef;
|
||||
backFuncMask = backMasks.FuncMask;
|
||||
backMask = backMasks.Mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
backFunc = test.FrontFunc;
|
||||
backSFail = test.FrontSFail;
|
||||
backDpPass = test.FrontDpPass;
|
||||
backDpFail = test.FrontDpFail;
|
||||
backFuncRef = test.FrontFuncRef;
|
||||
backFuncMask = test.FrontFuncMask;
|
||||
backMask = test.FrontMask;
|
||||
}
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetStencilTest(new StencilTestDescriptor(
|
||||
test.Enable.IsTrue(),
|
||||
test.FrontFunc,
|
||||
test.FrontSFail,
|
||||
test.FrontDpPass,
|
||||
test.FrontDpFail,
|
||||
test.FrontFuncRef,
|
||||
test.FrontFuncMask,
|
||||
test.FrontMask,
|
||||
backFunc,
|
||||
backSFail,
|
||||
backDpPass,
|
||||
backDpFail,
|
||||
backFuncRef,
|
||||
backFuncMask,
|
||||
backMask));
|
||||
}
|
||||
|
||||
private void UpdateSamplerPoolState()
|
||||
{
|
||||
PoolState samplerPool = _context.State.GetSamplerPoolState();
|
||||
|
||||
_textureManager.SetSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId);
|
||||
}
|
||||
|
||||
private void UpdateTexturePoolState()
|
||||
{
|
||||
PoolState texturePool = _context.State.GetTexturePoolState();
|
||||
|
||||
_textureManager.SetTexturePool(texturePool.Address.Pack(), texturePool.MaximumId);
|
||||
|
||||
_textureManager.SetTextureBufferIndex(_context.State.GetTextureBufferIndex());
|
||||
}
|
||||
|
||||
private void UpdateInputAssemblerGroupState()
|
||||
{
|
||||
// Must be updated before the vertex buffer.
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.VertexAttribState) != 0)
|
||||
{
|
||||
UpdateVertexAttribState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.PrimitiveRestartState) != 0)
|
||||
{
|
||||
UpdatePrimitiveRestartState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.IndexBufferState) != 0)
|
||||
{
|
||||
UpdateIndexBufferState();
|
||||
}
|
||||
|
||||
if ((_context.State.StateWriteFlags & StateWriteFlags.VertexBufferState) != 0)
|
||||
{
|
||||
UpdateVertexBufferState();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVertexAttribState()
|
||||
{
|
||||
VertexAttribDescriptor[] vertexAttribs = new VertexAttribDescriptor[16];
|
||||
|
||||
for (int index = 0; index < 16; index++)
|
||||
{
|
||||
VertexAttribState vertexAttrib = _context.State.GetVertexAttribState(index);
|
||||
|
||||
if (!FormatTable.TryGetAttribFormat(vertexAttrib.UnpackFormat(), out Format format))
|
||||
{
|
||||
// TODO: warning.
|
||||
|
||||
format = Format.R32G32B32A32Float;
|
||||
}
|
||||
|
||||
vertexAttribs[index] = new VertexAttribDescriptor(
|
||||
vertexAttrib.UnpackBufferIndex(),
|
||||
vertexAttrib.UnpackOffset(),
|
||||
format);
|
||||
}
|
||||
|
||||
_context.Renderer.GraphicsPipeline.BindVertexAttribs(vertexAttribs);
|
||||
}
|
||||
|
||||
private void UpdatePrimitiveRestartState()
|
||||
{
|
||||
PrimitiveRestartState primitiveRestart = _context.State.Get<PrimitiveRestartState>(MethodOffset.PrimitiveRestartState);
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetPrimitiveRestart(
|
||||
primitiveRestart.Enable,
|
||||
primitiveRestart.Index);
|
||||
}
|
||||
|
||||
private void UpdateIndexBufferState()
|
||||
{
|
||||
IndexBufferState indexBuffer = _context.State.GetIndexBufferState();
|
||||
|
||||
_firstIndex = indexBuffer.First;
|
||||
_indexCount = indexBuffer.Count;
|
||||
|
||||
if (_indexCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ulong gpuVa = indexBuffer.Address.Pack();
|
||||
|
||||
// Do not use the end address to calculate the size, because
|
||||
// the result may be much larger than the real size of the index buffer.
|
||||
ulong size = (ulong)(_firstIndex + _indexCount);
|
||||
|
||||
switch (indexBuffer.Type)
|
||||
{
|
||||
case IndexType.UShort: size *= 2; break;
|
||||
case IndexType.UInt: size *= 4; break;
|
||||
}
|
||||
|
||||
_bufferManager.SetIndexBuffer(gpuVa, size, indexBuffer.Type);
|
||||
|
||||
// The index buffer affects the vertex buffer size calculation, we
|
||||
// need to ensure that they are updated.
|
||||
UpdateVertexBufferState();
|
||||
}
|
||||
|
||||
private uint GetIndexBufferMaxIndex(ulong gpuVa, ulong size, IndexType type)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
Span<byte> data = _context.PhysicalMemory.Read(address, size);
|
||||
|
||||
uint maxIndex = 0;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case IndexType.UByte:
|
||||
{
|
||||
for (int index = 0; index < data.Length; index++)
|
||||
{
|
||||
if (maxIndex < data[index])
|
||||
{
|
||||
maxIndex = data[index];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IndexType.UShort:
|
||||
{
|
||||
Span<ushort> indices = MemoryMarshal.Cast<byte, ushort>(data);
|
||||
|
||||
for (int index = 0; index < indices.Length; index++)
|
||||
{
|
||||
if (maxIndex < indices[index])
|
||||
{
|
||||
maxIndex = indices[index];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case IndexType.UInt:
|
||||
{
|
||||
Span<uint> indices = MemoryMarshal.Cast<byte, uint>(data);
|
||||
|
||||
for (int index = 0; index < indices.Length; index++)
|
||||
{
|
||||
if (maxIndex < indices[index])
|
||||
{
|
||||
maxIndex = indices[index];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return maxIndex;
|
||||
}
|
||||
|
||||
private void UpdateVertexBufferState()
|
||||
{
|
||||
_isAnyVbInstanced = false;
|
||||
|
||||
for (int index = 0; index < 16; index++)
|
||||
{
|
||||
VertexBufferState vertexBuffer = _context.State.GetVertexBufferState(index);
|
||||
|
||||
if (!vertexBuffer.UnpackEnable())
|
||||
{
|
||||
_bufferManager.SetVertexBuffer(index, 0, 0, 0, 0);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
GpuVa endAddress = _context.State.GetVertexBufferEndAddress(index);
|
||||
|
||||
ulong address = vertexBuffer.Address.Pack();
|
||||
|
||||
int stride = vertexBuffer.UnpackStride();
|
||||
|
||||
bool instanced = _context.State.Get<bool>(MethodOffset.VertexBufferInstanced + index);
|
||||
|
||||
int divisor = instanced ? vertexBuffer.Divisor : 0;
|
||||
|
||||
_isAnyVbInstanced |= divisor != 0;
|
||||
|
||||
ulong size;
|
||||
|
||||
if (_drawIndexed || stride == 0 || instanced)
|
||||
{
|
||||
// This size may be (much) larger than the real vertex buffer size.
|
||||
// Avoid calculating it this way, unless we don't have any other option.
|
||||
size = endAddress.Pack() - address + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// For non-indexed draws, we can guess the size from the vertex count
|
||||
// and stride.
|
||||
int firstInstance = _context.State.GetBaseInstance();
|
||||
|
||||
VertexBufferDrawState drawState = _context.State.GetVertexBufferDrawState();
|
||||
|
||||
size = (ulong)((firstInstance + drawState.First + drawState.Count) * stride);
|
||||
}
|
||||
|
||||
_bufferManager.SetVertexBuffer(index, address, size, stride, divisor);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFaceState()
|
||||
{
|
||||
FaceState face = _context.State.GetFaceState();
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetFaceCulling(face.CullEnable.IsTrue(), face.CullFace);
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetFrontFace(face.FrontFace);
|
||||
}
|
||||
|
||||
private void UpdateRtColorMask()
|
||||
{
|
||||
uint[] componentMasks = new uint[Constants.TotalRenderTargets];
|
||||
|
||||
for (int index = 0; index < Constants.TotalRenderTargets; index++)
|
||||
{
|
||||
RtColorMask colorMask = _context.State.Get<RtColorMask>(MethodOffset.RtColorMask + index);
|
||||
|
||||
uint componentMask = 0;
|
||||
|
||||
componentMask = (colorMask.UnpackRed() ? 1u : 0u);
|
||||
componentMask |= (colorMask.UnpackGreen() ? 2u : 0u);
|
||||
componentMask |= (colorMask.UnpackBlue() ? 4u : 0u);
|
||||
componentMask |= (colorMask.UnpackAlpha() ? 8u : 0u);
|
||||
|
||||
componentMasks[index] = componentMask;
|
||||
}
|
||||
|
||||
_context.Renderer.GraphicsPipeline.SetRenderTargetColorMasks(componentMasks);
|
||||
}
|
||||
|
||||
private void UpdateBlendState()
|
||||
{
|
||||
BlendState[] blends = new BlendState[8];
|
||||
|
||||
for (int index = 0; index < 8; index++)
|
||||
{
|
||||
bool blendEnable = _context.State.GetBlendEnable(index).IsTrue();
|
||||
|
||||
BlendState blend = _context.State.GetBlendState(index);
|
||||
|
||||
BlendDescriptor descriptor = new BlendDescriptor(
|
||||
blendEnable,
|
||||
blend.ColorOp,
|
||||
blend.ColorSrcFactor,
|
||||
blend.ColorDstFactor,
|
||||
blend.AlphaOp,
|
||||
blend.AlphaSrcFactor,
|
||||
blend.AlphaDstFactor);
|
||||
|
||||
_context.Renderer.GraphicsPipeline.BindBlendState(index, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
private struct SbDescriptor
|
||||
{
|
||||
public uint AddressLow;
|
||||
public uint AddressHigh;
|
||||
public int Size;
|
||||
public int Padding;
|
||||
|
||||
public ulong PackAddress()
|
||||
{
|
||||
return AddressLow | ((ulong)AddressHigh << 32);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateShaderState()
|
||||
{
|
||||
ShaderAddresses addresses = new ShaderAddresses();
|
||||
|
||||
Span<ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1);
|
||||
|
||||
Span<ulong> addressesArray = MemoryMarshal.Cast<ShaderAddresses, ulong>(addressesSpan);
|
||||
|
||||
ulong baseAddress = _context.State.GetShaderBaseAddress().Pack();
|
||||
|
||||
for (int index = 0; index < 6; index++)
|
||||
{
|
||||
ShaderState shader = _context.State.GetShaderState(index);
|
||||
|
||||
if (!shader.UnpackEnable() && index != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
addressesArray[index] = baseAddress + shader.Offset;
|
||||
}
|
||||
|
||||
GraphicsShader gs = _shaderCache.GetGraphicsShader(addresses);
|
||||
|
||||
_vsUsesInstanceId = gs.Shader[0].Info.UsesInstanceId;
|
||||
|
||||
for (int stage = 0; stage < Constants.TotalShaderStages; stage++)
|
||||
{
|
||||
ShaderProgramInfo info = gs.Shader[stage]?.Info;
|
||||
|
||||
if (info == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var textureBindings = new TextureBindingInfo[info.Textures.Count];
|
||||
|
||||
for (int index = 0; index < info.Textures.Count; index++)
|
||||
{
|
||||
var descriptor = info.Textures[index];
|
||||
|
||||
Target target = GetTarget(descriptor.Target);
|
||||
|
||||
textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex);
|
||||
}
|
||||
|
||||
_textureManager.BindTextures(stage, textureBindings);
|
||||
|
||||
uint sbEnableMask = 0;
|
||||
uint ubEnableMask = 0;
|
||||
|
||||
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||
{
|
||||
BufferDescriptor sb = info.SBuffers[index];
|
||||
|
||||
sbEnableMask |= 1u << sb.Slot;
|
||||
|
||||
ulong sbDescAddress = _bufferManager.GetGraphicsUniformBufferAddress(stage, 0);
|
||||
|
||||
int sbDescOffset = 0x110 + stage * 0x100 + sb.Slot * 0x10;
|
||||
|
||||
sbDescAddress += (ulong)sbDescOffset;
|
||||
|
||||
Span<byte> sbDescriptorData = _context.PhysicalMemory.Read(sbDescAddress, 0x10);
|
||||
|
||||
SbDescriptor sbDescriptor = MemoryMarshal.Cast<byte, SbDescriptor>(sbDescriptorData)[0];
|
||||
|
||||
_bufferManager.SetGraphicsStorageBuffer(stage, sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size);
|
||||
}
|
||||
|
||||
for (int index = 0; index < info.CBuffers.Count; index++)
|
||||
{
|
||||
ubEnableMask |= 1u << info.CBuffers[index].Slot;
|
||||
}
|
||||
|
||||
_bufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask);
|
||||
_bufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask);
|
||||
}
|
||||
|
||||
_context.Renderer.GraphicsPipeline.BindProgram(gs.Interface);
|
||||
}
|
||||
|
||||
private static Target GetTarget(Shader.TextureTarget target)
|
||||
{
|
||||
target &= ~Shader.TextureTarget.Shadow;
|
||||
|
||||
switch (target)
|
||||
{
|
||||
case Shader.TextureTarget.Texture1D:
|
||||
return Target.Texture1D;
|
||||
|
||||
case Shader.TextureTarget.Texture1D | Shader.TextureTarget.Array:
|
||||
return Target.Texture1DArray;
|
||||
|
||||
case Shader.TextureTarget.Texture2D:
|
||||
return Target.Texture2D;
|
||||
|
||||
case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Array:
|
||||
return Target.Texture2DArray;
|
||||
|
||||
case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Multisample:
|
||||
return Target.Texture2DMultisample;
|
||||
|
||||
case Shader.TextureTarget.Texture2D | Shader.TextureTarget.Multisample | Shader.TextureTarget.Array:
|
||||
return Target.Texture2DMultisampleArray;
|
||||
|
||||
case Shader.TextureTarget.Texture3D:
|
||||
return Target.Texture3D;
|
||||
|
||||
case Shader.TextureTarget.TextureCube:
|
||||
return Target.Cubemap;
|
||||
|
||||
case Shader.TextureTarget.TextureCube | Shader.TextureTarget.Array:
|
||||
return Target.CubemapArray;
|
||||
}
|
||||
|
||||
// TODO: Warning.
|
||||
|
||||
return Target.Texture2D;
|
||||
}
|
||||
|
||||
private void InvalidateTextures(int argument)
|
||||
{
|
||||
_textureManager.Flush();
|
||||
}
|
||||
}
|
||||
}
|
34
Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs
Normal file
34
Ryujinx.Graphics.Gpu/Engine/ShaderAddresses.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
struct ShaderAddresses : IEquatable<ShaderAddresses>
|
||||
{
|
||||
public ulong VertexA;
|
||||
public ulong Vertex;
|
||||
public ulong TessControl;
|
||||
public ulong TessEvaluation;
|
||||
public ulong Geometry;
|
||||
public ulong Fragment;
|
||||
|
||||
public override bool Equals(object other)
|
||||
{
|
||||
return other is ShaderAddresses addresses && Equals(addresses);
|
||||
}
|
||||
|
||||
public bool Equals(ShaderAddresses other)
|
||||
{
|
||||
return VertexA == other.VertexA &&
|
||||
Vertex == other.Vertex &&
|
||||
TessControl == other.TessControl &&
|
||||
TessEvaluation == other.TessEvaluation &&
|
||||
Geometry == other.Geometry &&
|
||||
Fragment == other.Fragment;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashCode.Combine(VertexA, Vertex, TessControl, TessEvaluation, Geometry, Fragment);
|
||||
}
|
||||
}
|
||||
}
|
228
Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs
Normal file
228
Ryujinx.Graphics.Gpu/Engine/ShaderCache.cs
Normal file
|
@ -0,0 +1,228 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
class ShaderCache
|
||||
{
|
||||
private const int MaxProgramSize = 0x100000;
|
||||
|
||||
private GpuContext _context;
|
||||
|
||||
private ShaderDumper _dumper;
|
||||
|
||||
private Dictionary<ulong, ComputeShader> _cpPrograms;
|
||||
|
||||
private Dictionary<ShaderAddresses, GraphicsShader> _gpPrograms;
|
||||
|
||||
public ShaderCache(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_dumper = new ShaderDumper(context);
|
||||
|
||||
_cpPrograms = new Dictionary<ulong, ComputeShader>();
|
||||
|
||||
_gpPrograms = new Dictionary<ShaderAddresses, GraphicsShader>();
|
||||
}
|
||||
|
||||
public ComputeShader GetComputeShader(ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ)
|
||||
{
|
||||
if (!_cpPrograms.TryGetValue(gpuVa, out ComputeShader cpShader))
|
||||
{
|
||||
ShaderProgram shader = TranslateComputeShader(gpuVa);
|
||||
|
||||
shader.Replace(DefineNames.LocalSizeX, localSizeX.ToString(CultureInfo.InvariantCulture));
|
||||
shader.Replace(DefineNames.LocalSizeY, localSizeY.ToString(CultureInfo.InvariantCulture));
|
||||
shader.Replace(DefineNames.LocalSizeZ, localSizeZ.ToString(CultureInfo.InvariantCulture));
|
||||
|
||||
IShader hostShader = _context.Renderer.CompileShader(shader);
|
||||
|
||||
IProgram program = _context.Renderer.CreateProgram(new IShader[] { hostShader });
|
||||
|
||||
cpShader = new ComputeShader(program, shader);
|
||||
|
||||
_cpPrograms.Add(gpuVa, cpShader);
|
||||
}
|
||||
|
||||
return cpShader;
|
||||
}
|
||||
|
||||
public GraphicsShader GetGraphicsShader(ShaderAddresses addresses)
|
||||
{
|
||||
if (!_gpPrograms.TryGetValue(addresses, out GraphicsShader gpShader))
|
||||
{
|
||||
gpShader = new GraphicsShader();
|
||||
|
||||
if (addresses.VertexA != 0)
|
||||
{
|
||||
gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex, addresses.VertexA);
|
||||
}
|
||||
else
|
||||
{
|
||||
gpShader.Shader[0] = TranslateGraphicsShader(addresses.Vertex);
|
||||
}
|
||||
|
||||
gpShader.Shader[1] = TranslateGraphicsShader(addresses.TessControl);
|
||||
gpShader.Shader[2] = TranslateGraphicsShader(addresses.TessEvaluation);
|
||||
gpShader.Shader[3] = TranslateGraphicsShader(addresses.Geometry);
|
||||
gpShader.Shader[4] = TranslateGraphicsShader(addresses.Fragment);
|
||||
|
||||
BackpropQualifiers(gpShader);
|
||||
|
||||
List<IShader> shaders = new List<IShader>();
|
||||
|
||||
for (int stage = 0; stage < gpShader.Shader.Length; stage++)
|
||||
{
|
||||
if (gpShader.Shader[stage] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IShader shader = _context.Renderer.CompileShader(gpShader.Shader[stage]);
|
||||
|
||||
shaders.Add(shader);
|
||||
}
|
||||
|
||||
gpShader.Interface = _context.Renderer.CreateProgram(shaders.ToArray());
|
||||
|
||||
_gpPrograms.Add(addresses, gpShader);
|
||||
}
|
||||
|
||||
return gpShader;
|
||||
}
|
||||
|
||||
private ShaderProgram TranslateComputeShader(ulong gpuVa)
|
||||
{
|
||||
if (gpuVa == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
const TranslationFlags flags =
|
||||
TranslationFlags.Compute |
|
||||
TranslationFlags.Unspecialized;
|
||||
|
||||
TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags);
|
||||
|
||||
Span<byte> code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize);
|
||||
|
||||
program = Translator.Translate(code, translationConfig);
|
||||
|
||||
_dumper.Dump(gpuVa, compute : true);
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
private ShaderProgram TranslateGraphicsShader(ulong gpuVa, ulong gpuVaA = 0)
|
||||
{
|
||||
if (gpuVa == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
const TranslationFlags flags =
|
||||
TranslationFlags.DebugMode |
|
||||
TranslationFlags.Unspecialized;
|
||||
|
||||
TranslationConfig translationConfig = new TranslationConfig(0x10000, _dumper.CurrentDumpIndex, flags);
|
||||
|
||||
if (gpuVaA != 0)
|
||||
{
|
||||
Span<byte> codeA = _context.MemoryAccessor.Read(gpuVaA, MaxProgramSize);
|
||||
Span<byte> codeB = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize);
|
||||
|
||||
program = Translator.Translate(codeA, codeB, translationConfig);
|
||||
|
||||
_dumper.Dump(gpuVaA, compute: false);
|
||||
_dumper.Dump(gpuVa, compute: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
Span<byte> code = _context.MemoryAccessor.Read(gpuVa, MaxProgramSize);
|
||||
|
||||
program = Translator.Translate(code, translationConfig);
|
||||
|
||||
_dumper.Dump(gpuVa, compute: false);
|
||||
}
|
||||
|
||||
if (program.Stage == ShaderStage.Geometry)
|
||||
{
|
||||
PrimitiveType primitiveType = _context.Methods.PrimitiveType;
|
||||
|
||||
string inPrimitive = "points";
|
||||
|
||||
switch (primitiveType)
|
||||
{
|
||||
case PrimitiveType.Points:
|
||||
inPrimitive = "points";
|
||||
break;
|
||||
case PrimitiveType.Lines:
|
||||
case PrimitiveType.LineLoop:
|
||||
case PrimitiveType.LineStrip:
|
||||
inPrimitive = "lines";
|
||||
break;
|
||||
case PrimitiveType.LinesAdjacency:
|
||||
case PrimitiveType.LineStripAdjacency:
|
||||
inPrimitive = "lines_adjacency";
|
||||
break;
|
||||
case PrimitiveType.Triangles:
|
||||
case PrimitiveType.TriangleStrip:
|
||||
case PrimitiveType.TriangleFan:
|
||||
inPrimitive = "triangles";
|
||||
break;
|
||||
case PrimitiveType.TrianglesAdjacency:
|
||||
case PrimitiveType.TriangleStripAdjacency:
|
||||
inPrimitive = "triangles_adjacency";
|
||||
break;
|
||||
}
|
||||
|
||||
program.Replace(DefineNames.InputTopologyName, inPrimitive);
|
||||
}
|
||||
|
||||
return program;
|
||||
}
|
||||
|
||||
private void BackpropQualifiers(GraphicsShader program)
|
||||
{
|
||||
ShaderProgram fragmentShader = program.Shader[4];
|
||||
|
||||
bool isFirst = true;
|
||||
|
||||
for (int stage = 3; stage >= 0; stage--)
|
||||
{
|
||||
if (program.Shader[stage] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// We need to iterate backwards, since we do name replacement,
|
||||
// and it would otherwise replace a subset of the longer names.
|
||||
for (int attr = 31; attr >= 0; attr--)
|
||||
{
|
||||
string iq = fragmentShader?.Info.InterpolationQualifiers[attr].ToGlslQualifier() ?? string.Empty;
|
||||
|
||||
if (isFirst && iq != string.Empty)
|
||||
{
|
||||
program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr}", iq);
|
||||
}
|
||||
else
|
||||
{
|
||||
program.Shader[stage].Replace($"{DefineNames.OutQualifierPrefixName}{attr} ", string.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
isFirst = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
126
Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs
Normal file
126
Ryujinx.Graphics.Gpu/Engine/ShaderDumper.cs
Normal file
|
@ -0,0 +1,126 @@
|
|||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Engine
|
||||
{
|
||||
class ShaderDumper
|
||||
{
|
||||
private const int ShaderHeaderSize = 0x50;
|
||||
|
||||
private GpuContext _context;
|
||||
|
||||
private string _runtimeDir;
|
||||
private string _dumpPath;
|
||||
private int _dumpIndex;
|
||||
|
||||
public int CurrentDumpIndex => _dumpIndex;
|
||||
|
||||
public ShaderDumper(GpuContext context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
_dumpIndex = 1;
|
||||
}
|
||||
|
||||
public void Dump(ulong gpuVa, bool compute)
|
||||
{
|
||||
_dumpPath = GraphicsConfig.ShadersDumpPath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_dumpPath))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string fileName = "Shader" + _dumpIndex.ToString("d4") + ".bin";
|
||||
|
||||
string fullPath = Path.Combine(FullDir(), fileName);
|
||||
string codePath = Path.Combine(CodeDir(), fileName);
|
||||
|
||||
_dumpIndex++;
|
||||
|
||||
ulong headerSize = compute ? 0UL : ShaderHeaderSize;
|
||||
|
||||
using (FileStream fullFile = File.Create(fullPath))
|
||||
using (FileStream codeFile = File.Create(codePath))
|
||||
{
|
||||
BinaryWriter fullWriter = new BinaryWriter(fullFile);
|
||||
BinaryWriter codeWriter = new BinaryWriter(codeFile);
|
||||
|
||||
for (ulong i = 0; i < headerSize; i += 4)
|
||||
{
|
||||
fullWriter.Write(_context.MemoryAccessor.ReadInt32(gpuVa + i));
|
||||
}
|
||||
|
||||
ulong offset = 0;
|
||||
|
||||
ulong instruction = 0;
|
||||
|
||||
// Dump until a NOP instruction is found.
|
||||
while ((instruction >> 48 & 0xfff8) != 0x50b0)
|
||||
{
|
||||
uint word0 = (uint)_context.MemoryAccessor.ReadInt32(gpuVa + headerSize + offset + 0);
|
||||
uint word1 = (uint)_context.MemoryAccessor.ReadInt32(gpuVa + headerSize + offset + 4);
|
||||
|
||||
instruction = word0 | (ulong)word1 << 32;
|
||||
|
||||
// Zero instructions (other kind of NOP) stop immediately,
|
||||
// this is to avoid two rows of zeroes.
|
||||
if (instruction == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
fullWriter.Write(instruction);
|
||||
codeWriter.Write(instruction);
|
||||
|
||||
offset += 8;
|
||||
}
|
||||
|
||||
// Align to meet nvdisasm requirements.
|
||||
while (offset % 0x20 != 0)
|
||||
{
|
||||
fullWriter.Write(0);
|
||||
codeWriter.Write(0);
|
||||
|
||||
offset += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string FullDir()
|
||||
{
|
||||
return CreateAndReturn(Path.Combine(DumpDir(), "Full"));
|
||||
}
|
||||
|
||||
private string CodeDir()
|
||||
{
|
||||
return CreateAndReturn(Path.Combine(DumpDir(), "Code"));
|
||||
}
|
||||
|
||||
private string DumpDir()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_runtimeDir))
|
||||
{
|
||||
int index = 1;
|
||||
|
||||
do
|
||||
{
|
||||
_runtimeDir = Path.Combine(_dumpPath, "Dumps" + index.ToString("d2"));
|
||||
|
||||
index++;
|
||||
}
|
||||
while (Directory.Exists(_runtimeDir));
|
||||
|
||||
Directory.CreateDirectory(_runtimeDir);
|
||||
}
|
||||
|
||||
return _runtimeDir;
|
||||
}
|
||||
|
||||
private static string CreateAndReturn(string dir)
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
100
Ryujinx.Graphics.Gpu/GpuContext.cs
Normal file
100
Ryujinx.Graphics.Gpu/GpuContext.cs
Normal file
|
@ -0,0 +1,100 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Texture;
|
||||
using Ryujinx.Graphics.Gpu.Engine;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
public class GpuContext
|
||||
{
|
||||
public IRenderer Renderer { get; }
|
||||
|
||||
internal GpuState State { get; }
|
||||
|
||||
internal IPhysicalMemory PhysicalMemory { get; private set; }
|
||||
|
||||
public MemoryManager MemoryManager { get; }
|
||||
|
||||
internal MemoryAccessor MemoryAccessor { get; }
|
||||
|
||||
internal Methods Methods { get; }
|
||||
|
||||
internal NvGpuFifo Fifo { get; }
|
||||
|
||||
public DmaPusher DmaPusher { get; }
|
||||
|
||||
internal int SequenceNumber { get; private set; }
|
||||
|
||||
private Lazy<Capabilities> _caps;
|
||||
|
||||
internal Capabilities Capabilities => _caps.Value;
|
||||
|
||||
public GpuContext(IRenderer renderer)
|
||||
{
|
||||
Renderer = renderer;
|
||||
|
||||
State = new GpuState();
|
||||
|
||||
MemoryManager = new MemoryManager();
|
||||
|
||||
MemoryAccessor = new MemoryAccessor(this);
|
||||
|
||||
Methods = new Methods(this);
|
||||
|
||||
Fifo = new NvGpuFifo(this);
|
||||
|
||||
DmaPusher = new DmaPusher(this);
|
||||
|
||||
_caps = new Lazy<Capabilities>(GetCapabilities);
|
||||
}
|
||||
|
||||
internal void AdvanceSequence()
|
||||
{
|
||||
SequenceNumber++;
|
||||
}
|
||||
|
||||
public ITexture GetTexture(
|
||||
ulong address,
|
||||
int width,
|
||||
int height,
|
||||
int stride,
|
||||
bool isLinear,
|
||||
int gobBlocksInY,
|
||||
Format format,
|
||||
int bytesPerPixel)
|
||||
{
|
||||
FormatInfo formatInfo = new FormatInfo(format, 1, 1, bytesPerPixel);
|
||||
|
||||
TextureInfo info = new TextureInfo(
|
||||
address,
|
||||
width,
|
||||
height,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
stride,
|
||||
isLinear,
|
||||
gobBlocksInY,
|
||||
1,
|
||||
1,
|
||||
Target.Texture2D,
|
||||
formatInfo);
|
||||
|
||||
return Methods.GetTexture(address)?.HostTexture;
|
||||
}
|
||||
|
||||
private Capabilities GetCapabilities()
|
||||
{
|
||||
return Renderer.GetCapabilities();
|
||||
}
|
||||
|
||||
public void SetVmm(IPhysicalMemory mm)
|
||||
{
|
||||
PhysicalMemory = mm;
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.Gpu/GraphicsConfig.cs
Normal file
12
Ryujinx.Graphics.Gpu/GraphicsConfig.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
public static class GraphicsConfig
|
||||
{
|
||||
public static string ShadersDumpPath;
|
||||
|
||||
public static bool FastGpuTime = true;
|
||||
|
||||
public static bool DisableTUpdate;
|
||||
public static bool DisableBUpdate;
|
||||
}
|
||||
}
|
62
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal file
62
Ryujinx.Graphics.Gpu/Image/AutoDeleteCache.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
class AutoDeleteCache : IEnumerable<Texture>
|
||||
{
|
||||
private const int MaxCapacity = 2048;
|
||||
|
||||
private LinkedList<Texture> _textures;
|
||||
|
||||
public AutoDeleteCache()
|
||||
{
|
||||
_textures = new LinkedList<Texture>();
|
||||
}
|
||||
|
||||
public void Add(Texture texture)
|
||||
{
|
||||
texture.IncrementReferenceCount();
|
||||
|
||||
texture.CacheNode = _textures.AddLast(texture);
|
||||
|
||||
if (_textures.Count > MaxCapacity)
|
||||
{
|
||||
Texture oldestTexture = _textures.First.Value;
|
||||
|
||||
_textures.RemoveFirst();
|
||||
|
||||
oldestTexture.DecrementReferenceCount();
|
||||
|
||||
oldestTexture.CacheNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Lift(Texture texture)
|
||||
{
|
||||
if (texture.CacheNode != null)
|
||||
{
|
||||
if (texture.CacheNode != _textures.Last)
|
||||
{
|
||||
_textures.Remove(texture.CacheNode);
|
||||
|
||||
texture.CacheNode = _textures.AddLast(texture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Add(texture);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerator<Texture> GetEnumerator()
|
||||
{
|
||||
return _textures.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return _textures.GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
31
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal file
31
Ryujinx.Graphics.Gpu/Image/FormatInfo.cs
Normal file
|
@ -0,0 +1,31 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
struct FormatInfo
|
||||
{
|
||||
private static FormatInfo _rgba8 = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
|
||||
|
||||
public static FormatInfo Default => _rgba8;
|
||||
|
||||
public Format Format { get; }
|
||||
|
||||
public int BlockWidth { get; }
|
||||
public int BlockHeight { get; }
|
||||
public int BytesPerPixel { get; }
|
||||
|
||||
public bool IsCompressed => (BlockWidth | BlockHeight) != 1;
|
||||
|
||||
public FormatInfo(
|
||||
Format format,
|
||||
int blockWidth,
|
||||
int blockHeight,
|
||||
int bytesPerPixel)
|
||||
{
|
||||
Format = format;
|
||||
BlockWidth = blockWidth;
|
||||
BlockHeight = blockHeight;
|
||||
BytesPerPixel = bytesPerPixel;
|
||||
}
|
||||
}
|
||||
}
|
201
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal file
201
Ryujinx.Graphics.Gpu/Image/FormatTable.cs
Normal file
|
@ -0,0 +1,201 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
static class FormatTable
|
||||
{
|
||||
private static Dictionary<uint, FormatInfo> _textureFormats = new Dictionary<uint, FormatInfo>()
|
||||
{
|
||||
{ 0x2491d, new FormatInfo(Format.R8Unorm, 1, 1, 1) },
|
||||
{ 0x1249d, new FormatInfo(Format.R8Snorm, 1, 1, 1) },
|
||||
{ 0x4921d, new FormatInfo(Format.R8Uint, 1, 1, 1) },
|
||||
{ 0x36d9d, new FormatInfo(Format.R8Sint, 1, 1, 1) },
|
||||
{ 0x7ff9b, new FormatInfo(Format.R16Float, 1, 1, 2) },
|
||||
{ 0x2491b, new FormatInfo(Format.R16Unorm, 1, 1, 2) },
|
||||
{ 0x1249b, new FormatInfo(Format.R16Snorm, 1, 1, 2) },
|
||||
{ 0x4921b, new FormatInfo(Format.R16Uint, 1, 1, 2) },
|
||||
{ 0x36d9b, new FormatInfo(Format.R16Sint, 1, 1, 2) },
|
||||
{ 0x7ff8f, new FormatInfo(Format.R32Float, 1, 1, 4) },
|
||||
{ 0x4920f, new FormatInfo(Format.R32Uint, 1, 1, 4) },
|
||||
{ 0x36d8f, new FormatInfo(Format.R32Sint, 1, 1, 4) },
|
||||
{ 0x24918, new FormatInfo(Format.R8G8Unorm, 1, 1, 2) },
|
||||
{ 0x12498, new FormatInfo(Format.R8G8Snorm, 1, 1, 2) },
|
||||
{ 0x49218, new FormatInfo(Format.R8G8Uint, 1, 1, 2) },
|
||||
{ 0x36d98, new FormatInfo(Format.R8G8Sint, 1, 1, 2) },
|
||||
{ 0x7ff8c, new FormatInfo(Format.R16G16Float, 1, 1, 4) },
|
||||
{ 0x2490c, new FormatInfo(Format.R16G16Unorm, 1, 1, 4) },
|
||||
{ 0x1248c, new FormatInfo(Format.R16G16Snorm, 1, 1, 4) },
|
||||
{ 0x4920c, new FormatInfo(Format.R16G16Uint, 1, 1, 4) },
|
||||
{ 0x36d8c, new FormatInfo(Format.R16G16Sint, 1, 1, 4) },
|
||||
{ 0x7ff84, new FormatInfo(Format.R32G32Float, 1, 1, 8) },
|
||||
{ 0x49204, new FormatInfo(Format.R32G32Uint, 1, 1, 8) },
|
||||
{ 0x36d84, new FormatInfo(Format.R32G32Sint, 1, 1, 8) },
|
||||
{ 0x7ff82, new FormatInfo(Format.R32G32B32Float, 1, 1, 12) },
|
||||
{ 0x49202, new FormatInfo(Format.R32G32B32Uint, 1, 1, 12) },
|
||||
{ 0x36d82, new FormatInfo(Format.R32G32B32Sint, 1, 1, 12) },
|
||||
{ 0x24908, new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4) },
|
||||
{ 0x12488, new FormatInfo(Format.R8G8B8A8Snorm, 1, 1, 4) },
|
||||
{ 0x49208, new FormatInfo(Format.R8G8B8A8Uint, 1, 1, 4) },
|
||||
{ 0x36d88, new FormatInfo(Format.R8G8B8A8Sint, 1, 1, 4) },
|
||||
{ 0x7ff83, new FormatInfo(Format.R16G16B16A16Float, 1, 1, 8) },
|
||||
{ 0x24903, new FormatInfo(Format.R16G16B16A16Unorm, 1, 1, 8) },
|
||||
{ 0x12483, new FormatInfo(Format.R16G16B16A16Snorm, 1, 1, 8) },
|
||||
{ 0x49203, new FormatInfo(Format.R16G16B16A16Uint, 1, 1, 8) },
|
||||
{ 0x36d83, new FormatInfo(Format.R16G16B16A16Sint, 1, 1, 8) },
|
||||
{ 0x7ff81, new FormatInfo(Format.R32G32B32A32Float, 1, 1, 16) },
|
||||
{ 0x49201, new FormatInfo(Format.R32G32B32A32Uint, 1, 1, 16) },
|
||||
{ 0x36d81, new FormatInfo(Format.R32G32B32A32Sint, 1, 1, 16) },
|
||||
{ 0x2493a, new FormatInfo(Format.D16Unorm, 1, 1, 2) },
|
||||
{ 0x7ffaf, new FormatInfo(Format.D32Float, 1, 1, 4) },
|
||||
{ 0x24a29, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4) },
|
||||
{ 0x253b0, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8) },
|
||||
{ 0xa4908, new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4) },
|
||||
{ 0x24912, new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2) },
|
||||
{ 0x24914, new FormatInfo(Format.R5G5B5A1Unorm, 1, 1, 2) },
|
||||
{ 0x24915, new FormatInfo(Format.R5G6B5Unorm, 1, 1, 2) },
|
||||
{ 0x24909, new FormatInfo(Format.R10G10B10A2Unorm, 1, 1, 4) },
|
||||
{ 0x49209, new FormatInfo(Format.R10G10B10A2Uint, 1, 1, 4) },
|
||||
{ 0x7ffa1, new FormatInfo(Format.R11G11B10Float, 1, 1, 4) },
|
||||
{ 0x7ffa0, new FormatInfo(Format.R9G9B9E5Float, 1, 1, 4) },
|
||||
{ 0x24924, new FormatInfo(Format.Bc1RgbaUnorm, 4, 4, 8) },
|
||||
{ 0x24925, new FormatInfo(Format.Bc2Unorm, 4, 4, 16) },
|
||||
{ 0x24926, new FormatInfo(Format.Bc3Unorm, 4, 4, 16) },
|
||||
{ 0xa4924, new FormatInfo(Format.Bc1RgbaSrgb, 4, 4, 8) },
|
||||
{ 0xa4925, new FormatInfo(Format.Bc2Srgb, 4, 4, 16) },
|
||||
{ 0xa4926, new FormatInfo(Format.Bc3Srgb, 4, 4, 16) },
|
||||
{ 0x24927, new FormatInfo(Format.Bc4Unorm, 4, 4, 8) },
|
||||
{ 0x124a7, new FormatInfo(Format.Bc4Snorm, 4, 4, 8) },
|
||||
{ 0x24928, new FormatInfo(Format.Bc5Unorm, 4, 4, 16) },
|
||||
{ 0x124a8, new FormatInfo(Format.Bc5Snorm, 4, 4, 16) },
|
||||
{ 0x24917, new FormatInfo(Format.Bc7Unorm, 4, 4, 16) },
|
||||
{ 0xa4917, new FormatInfo(Format.Bc7Srgb, 4, 4, 16) },
|
||||
{ 0x7ff90, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16) },
|
||||
{ 0x7ff91, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16) },
|
||||
{ 0x24940, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16) },
|
||||
{ 0x24950, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16) },
|
||||
{ 0x24941, new FormatInfo(Format.Astc5x5Unorm, 5, 5, 16) },
|
||||
{ 0x24951, new FormatInfo(Format.Astc6x5Unorm, 6, 5, 16) },
|
||||
{ 0x24942, new FormatInfo(Format.Astc6x6Unorm, 6, 6, 16) },
|
||||
{ 0x24955, new FormatInfo(Format.Astc8x5Unorm, 8, 5, 16) },
|
||||
{ 0x24952, new FormatInfo(Format.Astc8x6Unorm, 8, 6, 16) },
|
||||
{ 0x24944, new FormatInfo(Format.Astc8x8Unorm, 8, 8, 16) },
|
||||
{ 0x24956, new FormatInfo(Format.Astc10x5Unorm, 10, 5, 16) },
|
||||
{ 0x24957, new FormatInfo(Format.Astc10x6Unorm, 10, 6, 16) },
|
||||
{ 0x24953, new FormatInfo(Format.Astc10x8Unorm, 10, 8, 16) },
|
||||
{ 0x24945, new FormatInfo(Format.Astc10x10Unorm, 10, 10, 16) },
|
||||
{ 0x24954, new FormatInfo(Format.Astc12x10Unorm, 12, 10, 16) },
|
||||
{ 0x24946, new FormatInfo(Format.Astc12x12Unorm, 12, 12, 16) },
|
||||
{ 0xa4940, new FormatInfo(Format.Astc4x4Srgb, 4, 4, 16) },
|
||||
{ 0xa4950, new FormatInfo(Format.Astc5x4Srgb, 5, 4, 16) },
|
||||
{ 0xa4941, new FormatInfo(Format.Astc5x5Srgb, 5, 5, 16) },
|
||||
{ 0xa4951, new FormatInfo(Format.Astc6x5Srgb, 6, 5, 16) },
|
||||
{ 0xa4942, new FormatInfo(Format.Astc6x6Srgb, 6, 6, 16) },
|
||||
{ 0xa4955, new FormatInfo(Format.Astc8x5Srgb, 8, 5, 16) },
|
||||
{ 0xa4952, new FormatInfo(Format.Astc8x6Srgb, 8, 6, 16) },
|
||||
{ 0xa4944, new FormatInfo(Format.Astc8x8Srgb, 8, 8, 16) },
|
||||
{ 0xa4956, new FormatInfo(Format.Astc10x5Srgb, 10, 5, 16) },
|
||||
{ 0xa4957, new FormatInfo(Format.Astc10x6Srgb, 10, 6, 16) },
|
||||
{ 0xa4953, new FormatInfo(Format.Astc10x8Srgb, 10, 8, 16) },
|
||||
{ 0xa4945, new FormatInfo(Format.Astc10x10Srgb, 10, 10, 16) },
|
||||
{ 0xa4954, new FormatInfo(Format.Astc12x10Srgb, 12, 10, 16) },
|
||||
{ 0xa4946, new FormatInfo(Format.Astc12x12Srgb, 12, 12, 16) },
|
||||
{ 0x24913, new FormatInfo(Format.A1B5G5R5Unorm, 1, 1, 2) }
|
||||
};
|
||||
|
||||
private static Dictionary<ulong, Format> _attribFormats = new Dictionary<ulong, Format>()
|
||||
{
|
||||
{ 0x13a00000, Format.R8Unorm },
|
||||
{ 0x0ba00000, Format.R8Snorm },
|
||||
{ 0x23a00000, Format.R8Uint },
|
||||
{ 0x1ba00000, Format.R8Sint },
|
||||
{ 0x3b600000, Format.R16Float },
|
||||
{ 0x13600000, Format.R16Unorm },
|
||||
{ 0x0b600000, Format.R16Snorm },
|
||||
{ 0x23600000, Format.R16Uint },
|
||||
{ 0x1b600000, Format.R16Sint },
|
||||
{ 0x3a400000, Format.R32Float },
|
||||
{ 0x22400000, Format.R32Uint },
|
||||
{ 0x1a400000, Format.R32Sint },
|
||||
{ 0x13000000, Format.R8G8Unorm },
|
||||
{ 0x0b000000, Format.R8G8Snorm },
|
||||
{ 0x23000000, Format.R8G8Uint },
|
||||
{ 0x1b000000, Format.R8G8Sint },
|
||||
{ 0x39e00000, Format.R16G16Float },
|
||||
{ 0x11e00000, Format.R16G16Unorm },
|
||||
{ 0x09e00000, Format.R16G16Snorm },
|
||||
{ 0x21e00000, Format.R16G16Uint },
|
||||
{ 0x19e00000, Format.R16G16Sint },
|
||||
{ 0x38800000, Format.R32G32Float },
|
||||
{ 0x20800000, Format.R32G32Uint },
|
||||
{ 0x18800000, Format.R32G32Sint },
|
||||
{ 0x12600000, Format.R8G8B8Unorm },
|
||||
{ 0x0a600000, Format.R8G8B8Snorm },
|
||||
{ 0x22600000, Format.R8G8B8Uint },
|
||||
{ 0x1a600000, Format.R8G8B8Sint },
|
||||
{ 0x38a00000, Format.R16G16B16Float },
|
||||
{ 0x10a00000, Format.R16G16B16Unorm },
|
||||
{ 0x08a00000, Format.R16G16B16Snorm },
|
||||
{ 0x20a00000, Format.R16G16B16Uint },
|
||||
{ 0x18a00000, Format.R16G16B16Sint },
|
||||
{ 0x38400000, Format.R32G32B32Float },
|
||||
{ 0x20400000, Format.R32G32B32Uint },
|
||||
{ 0x18400000, Format.R32G32B32Sint },
|
||||
{ 0x11400000, Format.R8G8B8A8Unorm },
|
||||
{ 0x09400000, Format.R8G8B8A8Snorm },
|
||||
{ 0x21400000, Format.R8G8B8A8Uint },
|
||||
{ 0x19400000, Format.R8G8B8A8Sint },
|
||||
{ 0x38600000, Format.R16G16B16A16Float },
|
||||
{ 0x10600000, Format.R16G16B16A16Unorm },
|
||||
{ 0x08600000, Format.R16G16B16A16Snorm },
|
||||
{ 0x20600000, Format.R16G16B16A16Uint },
|
||||
{ 0x18600000, Format.R16G16B16A16Sint },
|
||||
{ 0x38200000, Format.R32G32B32A32Float },
|
||||
{ 0x20200000, Format.R32G32B32A32Uint },
|
||||
{ 0x18200000, Format.R32G32B32A32Sint },
|
||||
{ 0x16000000, Format.R10G10B10A2Unorm },
|
||||
{ 0x26000000, Format.R10G10B10A2Uint },
|
||||
{ 0x3e200000, Format.R11G11B10Float },
|
||||
{ 0x2ba00000, Format.R8Uscaled },
|
||||
{ 0x33a00000, Format.R8Sscaled },
|
||||
{ 0x2b600000, Format.R16Uscaled },
|
||||
{ 0x33600000, Format.R16Sscaled },
|
||||
{ 0x2a400000, Format.R32Uscaled },
|
||||
{ 0x32400000, Format.R32Sscaled },
|
||||
{ 0x2b000000, Format.R8G8Uscaled },
|
||||
{ 0x33000000, Format.R8G8Sscaled },
|
||||
{ 0x29e00000, Format.R16G16Uscaled },
|
||||
{ 0x31e00000, Format.R16G16Sscaled },
|
||||
{ 0x28800000, Format.R32G32Uscaled },
|
||||
{ 0x30800000, Format.R32G32Sscaled },
|
||||
{ 0x2a600000, Format.R8G8B8Uscaled },
|
||||
{ 0x32600000, Format.R8G8B8Sscaled },
|
||||
{ 0x28a00000, Format.R16G16B16Uscaled },
|
||||
{ 0x30a00000, Format.R16G16B16Sscaled },
|
||||
{ 0x28400000, Format.R32G32B32Uscaled },
|
||||
{ 0x30400000, Format.R32G32B32Sscaled },
|
||||
{ 0x29400000, Format.R8G8B8A8Uscaled },
|
||||
{ 0x31400000, Format.R8G8B8A8Sscaled },
|
||||
{ 0x28600000, Format.R16G16B16A16Uscaled },
|
||||
{ 0x30600000, Format.R16G16B16A16Sscaled },
|
||||
{ 0x28200000, Format.R32G32B32A32Uscaled },
|
||||
{ 0x30200000, Format.R32G32B32A32Sscaled },
|
||||
{ 0x0e000000, Format.R10G10B10A2Snorm },
|
||||
{ 0x1e000000, Format.R10G10B10A2Sint },
|
||||
{ 0x2e000000, Format.R10G10B10A2Uscaled },
|
||||
{ 0x36000000, Format.R10G10B10A2Sscaled }
|
||||
};
|
||||
|
||||
public static bool TryGetTextureFormat(uint encoded, bool isSrgb, out FormatInfo format)
|
||||
{
|
||||
encoded |= (isSrgb ? 1u << 19 : 0u);
|
||||
|
||||
return _textureFormats.TryGetValue(encoded, out format);
|
||||
}
|
||||
|
||||
public static bool TryGetAttribFormat(uint encoded, out Format format)
|
||||
{
|
||||
return _attribFormats.TryGetValue(encoded, out format);
|
||||
}
|
||||
}
|
||||
}
|
99
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal file
99
Ryujinx.Graphics.Gpu/Image/Pool.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
abstract class Pool<T> : IDisposable
|
||||
{
|
||||
protected const int DescriptorSize = 0x20;
|
||||
|
||||
protected GpuContext Context;
|
||||
|
||||
protected T[] Items;
|
||||
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
|
||||
public Pool(GpuContext context, ulong address, int maximumId)
|
||||
{
|
||||
Context = context;
|
||||
|
||||
int count = maximumId + 1;
|
||||
|
||||
ulong size = (ulong)(uint)count * DescriptorSize;;
|
||||
|
||||
Items = new T[count];
|
||||
|
||||
Address = address;
|
||||
Size = size;
|
||||
}
|
||||
|
||||
public abstract T Get(int id);
|
||||
|
||||
public void SynchronizeMemory()
|
||||
{
|
||||
(ulong, ulong)[] modifiedRanges = Context.PhysicalMemory.GetModifiedRanges(Address, Size);
|
||||
|
||||
for (int index = 0; index < modifiedRanges.Length; index++)
|
||||
{
|
||||
(ulong mAddress, ulong mSize) = modifiedRanges[index];
|
||||
|
||||
if (mAddress < Address)
|
||||
{
|
||||
mAddress = Address;
|
||||
}
|
||||
|
||||
ulong maxSize = Address + Size - mAddress;
|
||||
|
||||
if (mSize > maxSize)
|
||||
{
|
||||
mSize = maxSize;
|
||||
}
|
||||
|
||||
InvalidateRangeImpl(mAddress, mSize);
|
||||
}
|
||||
}
|
||||
|
||||
public void InvalidateRange(ulong address, ulong size)
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
|
||||
ulong texturePoolEndAddress = Address + Size;
|
||||
|
||||
// If the range being invalidated is not overlapping the texture pool range,
|
||||
// then we don't have anything to do, exit early.
|
||||
if (address >= texturePoolEndAddress || endAddress <= Address)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (address < Address)
|
||||
{
|
||||
address = Address;
|
||||
}
|
||||
|
||||
if (endAddress > texturePoolEndAddress)
|
||||
{
|
||||
endAddress = texturePoolEndAddress;
|
||||
}
|
||||
|
||||
InvalidateRangeImpl(address, size);
|
||||
}
|
||||
|
||||
protected abstract void InvalidateRangeImpl(ulong address, ulong size);
|
||||
|
||||
protected abstract void Delete(T item);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (Items != null)
|
||||
{
|
||||
for (int index = 0; index < Items.Length; index++)
|
||||
{
|
||||
Delete(Items[index]);
|
||||
}
|
||||
|
||||
Items = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
9
Ryujinx.Graphics.Gpu/Image/ReductionFilter.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
enum ReductionFilter
|
||||
{
|
||||
Average,
|
||||
Minimum,
|
||||
Maximum
|
||||
}
|
||||
}
|
52
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal file
52
Ryujinx.Graphics.Gpu/Image/Sampler.cs
Normal file
|
@ -0,0 +1,52 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Color;
|
||||
using Ryujinx.Graphics.GAL.Sampler;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
class Sampler : IDisposable
|
||||
{
|
||||
public ISampler HostSampler { get; }
|
||||
|
||||
public Sampler(GpuContext context, SamplerDescriptor descriptor)
|
||||
{
|
||||
MinFilter minFilter = descriptor.UnpackMinFilter();
|
||||
MagFilter magFilter = descriptor.UnpackMagFilter();
|
||||
|
||||
AddressMode addressU = descriptor.UnpackAddressU();
|
||||
AddressMode addressV = descriptor.UnpackAddressV();
|
||||
AddressMode addressP = descriptor.UnpackAddressP();
|
||||
|
||||
CompareMode compareMode = descriptor.UnpackCompareMode();
|
||||
CompareOp compareOp = descriptor.UnpackCompareOp();
|
||||
|
||||
ColorF color = new ColorF(0, 0, 0, 0);
|
||||
|
||||
float minLod = descriptor.UnpackMinLod();
|
||||
float maxLod = descriptor.UnpackMaxLod();
|
||||
float mipLodBias = descriptor.UnpackMipLodBias();
|
||||
|
||||
float maxAnisotropy = descriptor.UnpackMaxAnisotropy();
|
||||
|
||||
HostSampler = context.Renderer.CreateSampler(new SamplerCreateInfo(
|
||||
minFilter,
|
||||
magFilter,
|
||||
addressU,
|
||||
addressV,
|
||||
addressP,
|
||||
compareMode,
|
||||
compareOp,
|
||||
color,
|
||||
minLod,
|
||||
maxLod,
|
||||
mipLodBias,
|
||||
maxAnisotropy));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
HostSampler.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
132
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal file
132
Ryujinx.Graphics.Gpu/Image/SamplerDescriptor.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Sampler;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
struct SamplerDescriptor
|
||||
{
|
||||
private static readonly float[] _f5ToF32ConversionLut = new float[]
|
||||
{
|
||||
0.0f,
|
||||
0.055555556f,
|
||||
0.1f,
|
||||
0.13636364f,
|
||||
0.16666667f,
|
||||
0.1923077f,
|
||||
0.21428572f,
|
||||
0.23333333f,
|
||||
0.25f,
|
||||
0.2777778f,
|
||||
0.3f,
|
||||
0.3181818f,
|
||||
0.33333334f,
|
||||
0.34615386f,
|
||||
0.35714287f,
|
||||
0.36666667f,
|
||||
0.375f,
|
||||
0.3888889f,
|
||||
0.4f,
|
||||
0.4090909f,
|
||||
0.41666666f,
|
||||
0.42307693f,
|
||||
0.42857143f,
|
||||
0.43333334f,
|
||||
0.4375f,
|
||||
0.44444445f,
|
||||
0.45f,
|
||||
0.45454547f,
|
||||
0.45833334f,
|
||||
0.46153846f,
|
||||
0.4642857f,
|
||||
0.46666667f
|
||||
};
|
||||
|
||||
private static readonly float[] _maxAnisotropyLut = new float[]
|
||||
{
|
||||
1, 2, 4, 6, 8, 10, 12, 16
|
||||
};
|
||||
|
||||
private const float Frac8ToF32 = 1.0f / 256.0f;
|
||||
|
||||
public uint Word0;
|
||||
public uint Word1;
|
||||
public uint Word2;
|
||||
public uint Word3;
|
||||
public uint BorderColorR;
|
||||
public uint BorderColorG;
|
||||
public uint BorderColorB;
|
||||
public uint BorderColorA;
|
||||
|
||||
public AddressMode UnpackAddressU()
|
||||
{
|
||||
return (AddressMode)(Word0 & 7);
|
||||
}
|
||||
|
||||
public AddressMode UnpackAddressV()
|
||||
{
|
||||
return (AddressMode)((Word0 >> 3) & 7);
|
||||
}
|
||||
|
||||
public AddressMode UnpackAddressP()
|
||||
{
|
||||
return (AddressMode)((Word0 >> 6) & 7);
|
||||
}
|
||||
|
||||
public CompareMode UnpackCompareMode()
|
||||
{
|
||||
return (CompareMode)((Word0 >> 9) & 1);
|
||||
}
|
||||
|
||||
public CompareOp UnpackCompareOp()
|
||||
{
|
||||
return (CompareOp)(((Word0 >> 10) & 7) + 1);
|
||||
}
|
||||
|
||||
public float UnpackMaxAnisotropy()
|
||||
{
|
||||
return _maxAnisotropyLut[(Word0 >> 20) & 7];
|
||||
}
|
||||
|
||||
public MagFilter UnpackMagFilter()
|
||||
{
|
||||
return (MagFilter)(Word1 & 3);
|
||||
}
|
||||
|
||||
public MinFilter UnpackMinFilter()
|
||||
{
|
||||
int minFilter = (int)(Word1 >> 4) & 3;
|
||||
int mipFilter = (int)(Word1 >> 6) & 3;
|
||||
|
||||
return (MinFilter)(minFilter + (mipFilter - 1) * 2);
|
||||
}
|
||||
|
||||
public ReductionFilter UnpackReductionFilter()
|
||||
{
|
||||
return (ReductionFilter)((Word1 >> 10) & 3);
|
||||
}
|
||||
|
||||
public float UnpackMipLodBias()
|
||||
{
|
||||
int fixedValue = (int)(Word1 >> 12) & 0x1fff;
|
||||
|
||||
fixedValue = (fixedValue << 19) >> 19;
|
||||
|
||||
return fixedValue * Frac8ToF32;
|
||||
}
|
||||
|
||||
public float UnpackLodSnap()
|
||||
{
|
||||
return _f5ToF32ConversionLut[(Word1 >> 26) & 0x1f];
|
||||
}
|
||||
|
||||
public float UnpackMinLod()
|
||||
{
|
||||
return (Word2 & 0xfff) * Frac8ToF32;
|
||||
}
|
||||
|
||||
public float UnpackMaxLod()
|
||||
{
|
||||
return ((Word2 >> 12) & 0xfff) * Frac8ToF32;
|
||||
}
|
||||
}
|
||||
}
|
61
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal file
61
Ryujinx.Graphics.Gpu/Image/SamplerPool.cs
Normal file
|
@ -0,0 +1,61 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
class SamplerPool : Pool<Sampler>
|
||||
{
|
||||
public SamplerPool(GpuContext context, ulong address, int maximumId) : base(context, address, maximumId) { }
|
||||
|
||||
public override Sampler Get(int id)
|
||||
{
|
||||
if ((uint)id >= Items.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SynchronizeMemory();
|
||||
|
||||
Sampler sampler = Items[id];
|
||||
|
||||
if (sampler == null)
|
||||
{
|
||||
ulong address = Address + (ulong)(uint)id * DescriptorSize;
|
||||
|
||||
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
|
||||
|
||||
SamplerDescriptor descriptor = MemoryMarshal.Cast<byte, SamplerDescriptor>(data)[0];
|
||||
|
||||
sampler = new Sampler(Context, descriptor);
|
||||
|
||||
Items[id] = sampler;
|
||||
}
|
||||
|
||||
return sampler;
|
||||
}
|
||||
|
||||
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
|
||||
for (; address < endAddress; address += DescriptorSize)
|
||||
{
|
||||
int id = (int)((address - Address) / DescriptorSize);
|
||||
|
||||
Sampler sampler = Items[id];
|
||||
|
||||
if (sampler != null)
|
||||
{
|
||||
sampler.Dispose();
|
||||
|
||||
Items[id] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Delete(Sampler item)
|
||||
{
|
||||
item?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
719
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal file
719
Ryujinx.Graphics.Gpu/Image/Texture.cs
Normal file
|
@ -0,0 +1,719 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Texture;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using Ryujinx.Graphics.Texture.Astc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
class Texture : IRange<Texture>
|
||||
{
|
||||
private GpuContext _context;
|
||||
|
||||
private TextureInfo _info;
|
||||
|
||||
private SizeInfo _sizeInfo;
|
||||
|
||||
public Format Format => _info.FormatInfo.Format;
|
||||
|
||||
public TextureInfo Info => _info;
|
||||
|
||||
private int _depth;
|
||||
private int _layers;
|
||||
private int _firstLayer;
|
||||
private int _firstLevel;
|
||||
|
||||
private bool _hasData;
|
||||
|
||||
private ITexture _arrayViewTexture;
|
||||
private Target _arrayViewTarget;
|
||||
|
||||
private Texture _viewStorage;
|
||||
|
||||
private List<Texture> _views;
|
||||
|
||||
public ITexture HostTexture { get; private set; }
|
||||
|
||||
public LinkedListNode<Texture> CacheNode { get; set; }
|
||||
|
||||
public bool Modified { get; set; }
|
||||
|
||||
public ulong Address => _info.Address;
|
||||
public ulong EndAddress => _info.Address + Size;
|
||||
|
||||
public ulong Size => (ulong)_sizeInfo.TotalSize;
|
||||
|
||||
private int _referenceCount;
|
||||
|
||||
private int _sequenceNumber;
|
||||
|
||||
private Texture(
|
||||
GpuContext context,
|
||||
TextureInfo info,
|
||||
SizeInfo sizeInfo,
|
||||
int firstLayer,
|
||||
int firstLevel)
|
||||
{
|
||||
InitializeTexture(context, info, sizeInfo);
|
||||
|
||||
_firstLayer = firstLayer;
|
||||
_firstLevel = firstLevel;
|
||||
|
||||
_hasData = true;
|
||||
}
|
||||
|
||||
public Texture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
|
||||
{
|
||||
InitializeTexture(context, info, sizeInfo);
|
||||
|
||||
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, context.Capabilities);
|
||||
|
||||
HostTexture = _context.Renderer.CreateTexture(createInfo);
|
||||
}
|
||||
|
||||
private void InitializeTexture(GpuContext context, TextureInfo info, SizeInfo sizeInfo)
|
||||
{
|
||||
_context = context;
|
||||
_sizeInfo = sizeInfo;
|
||||
|
||||
SetInfo(info);
|
||||
|
||||
_viewStorage = this;
|
||||
|
||||
_views = new List<Texture>();
|
||||
}
|
||||
|
||||
public Texture CreateView(TextureInfo info, SizeInfo sizeInfo, int firstLayer, int firstLevel)
|
||||
{
|
||||
Texture texture = new Texture(
|
||||
_context,
|
||||
info,
|
||||
sizeInfo,
|
||||
_firstLayer + firstLayer,
|
||||
_firstLevel + firstLevel);
|
||||
|
||||
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, _context.Capabilities);
|
||||
|
||||
texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel);
|
||||
|
||||
_viewStorage.AddView(texture);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
private void AddView(Texture texture)
|
||||
{
|
||||
_views.Add(texture);
|
||||
|
||||
texture._viewStorage = this;
|
||||
}
|
||||
|
||||
private void RemoveView(Texture texture)
|
||||
{
|
||||
_views.Remove(texture);
|
||||
|
||||
texture._viewStorage = null;
|
||||
}
|
||||
|
||||
public void ChangeSize(int width, int height, int depthOrLayers)
|
||||
{
|
||||
width <<= _firstLevel;
|
||||
height <<= _firstLevel;
|
||||
|
||||
if (_info.Target == Target.Texture3D)
|
||||
{
|
||||
depthOrLayers <<= _firstLevel;
|
||||
}
|
||||
else
|
||||
{
|
||||
depthOrLayers = _viewStorage._info.DepthOrLayers;
|
||||
}
|
||||
|
||||
_viewStorage.RecreateStorageOrView(width, height, depthOrLayers);
|
||||
|
||||
foreach (Texture view in _viewStorage._views)
|
||||
{
|
||||
int viewWidth = Math.Max(1, width >> view._firstLevel);
|
||||
int viewHeight = Math.Max(1, height >> view._firstLevel);
|
||||
|
||||
int viewDepthOrLayers;
|
||||
|
||||
if (view._info.Target == Target.Texture3D)
|
||||
{
|
||||
viewDepthOrLayers = Math.Max(1, depthOrLayers >> view._firstLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewDepthOrLayers = view._info.DepthOrLayers;
|
||||
}
|
||||
|
||||
view.RecreateStorageOrView(viewWidth, viewHeight, viewDepthOrLayers);
|
||||
}
|
||||
}
|
||||
|
||||
private void RecreateStorageOrView(int width, int height, int depthOrLayers)
|
||||
{
|
||||
SetInfo(new TextureInfo(
|
||||
_info.Address,
|
||||
width,
|
||||
height,
|
||||
depthOrLayers,
|
||||
_info.Levels,
|
||||
_info.SamplesInX,
|
||||
_info.SamplesInY,
|
||||
_info.Stride,
|
||||
_info.IsLinear,
|
||||
_info.GobBlocksInY,
|
||||
_info.GobBlocksInZ,
|
||||
_info.GobBlocksInTileX,
|
||||
_info.Target,
|
||||
_info.FormatInfo,
|
||||
_info.DepthStencilMode,
|
||||
_info.SwizzleR,
|
||||
_info.SwizzleG,
|
||||
_info.SwizzleB,
|
||||
_info.SwizzleA));
|
||||
|
||||
TextureCreateInfo createInfo = TextureManager.GetCreateInfo(_info, _context.Capabilities);
|
||||
|
||||
if (_viewStorage != this)
|
||||
{
|
||||
ReplaceStorage(_viewStorage.HostTexture.CreateView(createInfo, _firstLayer, _firstLevel));
|
||||
}
|
||||
else
|
||||
{
|
||||
ITexture newStorage = _context.Renderer.CreateTexture(createInfo);
|
||||
|
||||
HostTexture.CopyTo(newStorage);
|
||||
|
||||
ReplaceStorage(newStorage);
|
||||
}
|
||||
}
|
||||
|
||||
public void SynchronizeMemory()
|
||||
{
|
||||
if (_sequenceNumber == _context.SequenceNumber && _hasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_sequenceNumber = _context.SequenceNumber;
|
||||
|
||||
bool modified = _context.PhysicalMemory.GetModifiedRanges(Address, Size).Length != 0;
|
||||
|
||||
if (!modified && _hasData)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ulong pageSize = (uint)_context.PhysicalMemory.GetPageSize();
|
||||
|
||||
ulong pageMask = pageSize - 1;
|
||||
|
||||
ulong rangeAddress = Address & ~pageMask;
|
||||
|
||||
ulong rangeSize = (EndAddress - Address + pageMask) & ~pageMask;
|
||||
|
||||
_context.Methods.InvalidateRange(rangeAddress, rangeSize);
|
||||
|
||||
Span<byte> data = _context.PhysicalMemory.Read(Address, Size);
|
||||
|
||||
if (_info.IsLinear)
|
||||
{
|
||||
data = LayoutConverter.ConvertLinearStridedToLinear(
|
||||
_info.Width,
|
||||
_info.Height,
|
||||
_info.FormatInfo.BlockWidth,
|
||||
_info.FormatInfo.BlockHeight,
|
||||
_info.Stride,
|
||||
_info.FormatInfo.BytesPerPixel,
|
||||
data);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = LayoutConverter.ConvertBlockLinearToLinear(
|
||||
_info.Width,
|
||||
_info.Height,
|
||||
_depth,
|
||||
_info.Levels,
|
||||
_layers,
|
||||
_info.FormatInfo.BlockWidth,
|
||||
_info.FormatInfo.BlockHeight,
|
||||
_info.FormatInfo.BytesPerPixel,
|
||||
_info.GobBlocksInY,
|
||||
_info.GobBlocksInZ,
|
||||
_info.GobBlocksInTileX,
|
||||
_sizeInfo,
|
||||
data);
|
||||
}
|
||||
|
||||
if (!_context.Capabilities.SupportsAstcCompression && _info.FormatInfo.Format.IsAstc())
|
||||
{
|
||||
int blockWidth = _info.FormatInfo.BlockWidth;
|
||||
int blockHeight = _info.FormatInfo.BlockHeight;
|
||||
|
||||
data = AstcDecoder.DecodeToRgba8(
|
||||
data,
|
||||
blockWidth,
|
||||
blockHeight,
|
||||
1,
|
||||
_info.Width,
|
||||
_info.Height,
|
||||
_depth);
|
||||
}
|
||||
|
||||
HostTexture.SetData(data);
|
||||
|
||||
_hasData = true;
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
byte[] data = HostTexture.GetData(0);
|
||||
|
||||
_context.PhysicalMemory.Write(Address, data);
|
||||
}
|
||||
|
||||
public bool IsPerfectMatch(TextureInfo info, TextureSearchFlags flags)
|
||||
{
|
||||
if (!FormatMatches(info, (flags & TextureSearchFlags.Strict) != 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LayoutMatches(info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SizeMatches(info, (flags & TextureSearchFlags.Strict) == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & TextureSearchFlags.Sampler) != 0)
|
||||
{
|
||||
if (!SamplerParamsMatches(info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((flags & TextureSearchFlags.IgnoreMs) != 0)
|
||||
{
|
||||
bool msTargetCompatible = _info.Target == Target.Texture2DMultisample &&
|
||||
info.Target == Target.Texture2D;
|
||||
|
||||
if (!msTargetCompatible && !TargetAndSamplesCompatible(info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!TargetAndSamplesCompatible(info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _info.Address == info.Address && _info.Levels == info.Levels;
|
||||
}
|
||||
|
||||
private bool FormatMatches(TextureInfo info, bool strict)
|
||||
{
|
||||
// D32F and R32F texture have the same representation internally,
|
||||
// however the R32F format is used to sample from depth textures.
|
||||
if (_info.FormatInfo.Format == Format.D32Float &&
|
||||
info.FormatInfo.Format == Format.R32Float && !strict)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_info.FormatInfo.Format == Format.R8G8B8A8Srgb &&
|
||||
info.FormatInfo.Format == Format.R8G8B8A8Unorm && !strict)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_info.FormatInfo.Format == Format.R8G8B8A8Unorm &&
|
||||
info.FormatInfo.Format == Format.R8G8B8A8Srgb && !strict)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _info.FormatInfo.Format == info.FormatInfo.Format;
|
||||
}
|
||||
|
||||
private bool LayoutMatches(TextureInfo info)
|
||||
{
|
||||
if (_info.IsLinear != info.IsLinear)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For linear textures, gob block sizes are ignored.
|
||||
// For block linear textures, the stride is ignored.
|
||||
if (info.IsLinear)
|
||||
{
|
||||
return _info.Stride == info.Stride;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _info.GobBlocksInY == info.GobBlocksInY &&
|
||||
_info.GobBlocksInZ == info.GobBlocksInZ;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SizeMatches(TextureInfo info)
|
||||
{
|
||||
return SizeMatches(info, alignSizes: false);
|
||||
}
|
||||
|
||||
public bool SizeMatches(TextureInfo info, int level)
|
||||
{
|
||||
return Math.Max(1, _info.Width >> level) == info.Width &&
|
||||
Math.Max(1, _info.Height >> level) == info.Height &&
|
||||
Math.Max(1, _info.GetDepth() >> level) == info.GetDepth();
|
||||
}
|
||||
|
||||
private bool SizeMatches(TextureInfo info, bool alignSizes)
|
||||
{
|
||||
if (_info.GetLayers() != info.GetLayers())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (alignSizes)
|
||||
{
|
||||
Size size0 = GetAlignedSize(_info);
|
||||
Size size1 = GetAlignedSize(info);
|
||||
|
||||
return size0.Width == size1.Width &&
|
||||
size0.Height == size1.Height &&
|
||||
size0.Depth == size1.Depth;
|
||||
}
|
||||
else
|
||||
{
|
||||
return _info.Width == info.Width &&
|
||||
_info.Height == info.Height &&
|
||||
_info.GetDepth() == info.GetDepth();
|
||||
}
|
||||
}
|
||||
|
||||
private bool SamplerParamsMatches(TextureInfo info)
|
||||
{
|
||||
return _info.DepthStencilMode == info.DepthStencilMode &&
|
||||
_info.SwizzleR == info.SwizzleR &&
|
||||
_info.SwizzleG == info.SwizzleG &&
|
||||
_info.SwizzleB == info.SwizzleB &&
|
||||
_info.SwizzleA == info.SwizzleA;
|
||||
}
|
||||
|
||||
private bool TargetAndSamplesCompatible(TextureInfo info)
|
||||
{
|
||||
return _info.Target == info.Target &&
|
||||
_info.SamplesInX == info.SamplesInX &&
|
||||
_info.SamplesInY == info.SamplesInY;
|
||||
}
|
||||
|
||||
public bool IsViewCompatible(TextureInfo info, ulong size, out int firstLayer, out int firstLevel)
|
||||
{
|
||||
// Out of range.
|
||||
if (info.Address < Address || info.Address + size > EndAddress)
|
||||
{
|
||||
firstLayer = 0;
|
||||
firstLevel = 0;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int offset = (int)(info.Address - Address);
|
||||
|
||||
if (!_sizeInfo.FindView(offset, (int)size, out firstLayer, out firstLevel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ViewLayoutCompatible(info, firstLevel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ViewFormatCompatible(info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ViewSizeMatches(info, firstLevel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ViewTargetCompatible(info))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return _info.SamplesInX == info.SamplesInX &&
|
||||
_info.SamplesInY == info.SamplesInY;
|
||||
}
|
||||
|
||||
private bool ViewLayoutCompatible(TextureInfo info, int level)
|
||||
{
|
||||
if (_info.IsLinear != info.IsLinear)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// For linear textures, gob block sizes are ignored.
|
||||
// For block linear textures, the stride is ignored.
|
||||
if (info.IsLinear)
|
||||
{
|
||||
int width = Math.Max(1, _info.Width >> level);
|
||||
|
||||
int stride = width * _info.FormatInfo.BytesPerPixel;
|
||||
|
||||
stride = BitUtils.AlignUp(stride, 32);
|
||||
|
||||
return stride == info.Stride;
|
||||
}
|
||||
else
|
||||
{
|
||||
int height = Math.Max(1, _info.Height >> level);
|
||||
int depth = Math.Max(1, _info.GetDepth() >> level);
|
||||
|
||||
(int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
|
||||
height,
|
||||
depth,
|
||||
_info.FormatInfo.BlockHeight,
|
||||
_info.GobBlocksInY,
|
||||
_info.GobBlocksInZ);
|
||||
|
||||
return gobBlocksInY == info.GobBlocksInY &&
|
||||
gobBlocksInZ == info.GobBlocksInZ;
|
||||
}
|
||||
}
|
||||
|
||||
private bool ViewFormatCompatible(TextureInfo info)
|
||||
{
|
||||
return TextureCompatibility.FormatCompatible(_info.FormatInfo, info.FormatInfo);
|
||||
}
|
||||
|
||||
private bool ViewSizeMatches(TextureInfo info, int level)
|
||||
{
|
||||
Size size = GetAlignedSize(_info, level);
|
||||
|
||||
Size otherSize = GetAlignedSize(info);
|
||||
|
||||
return size.Width == otherSize.Width &&
|
||||
size.Height == otherSize.Height &&
|
||||
size.Depth == otherSize.Depth;
|
||||
}
|
||||
|
||||
private bool ViewTargetCompatible(TextureInfo info)
|
||||
{
|
||||
switch (_info.Target)
|
||||
{
|
||||
case Target.Texture1D:
|
||||
case Target.Texture1DArray:
|
||||
return info.Target == Target.Texture1D ||
|
||||
info.Target == Target.Texture1DArray;
|
||||
|
||||
case Target.Texture2D:
|
||||
return info.Target == Target.Texture2D ||
|
||||
info.Target == Target.Texture2DArray;
|
||||
|
||||
case Target.Texture2DArray:
|
||||
case Target.Cubemap:
|
||||
case Target.CubemapArray:
|
||||
return info.Target == Target.Texture2D ||
|
||||
info.Target == Target.Texture2DArray ||
|
||||
info.Target == Target.Cubemap ||
|
||||
info.Target == Target.CubemapArray;
|
||||
|
||||
case Target.Texture2DMultisample:
|
||||
case Target.Texture2DMultisampleArray:
|
||||
return info.Target == Target.Texture2DMultisample ||
|
||||
info.Target == Target.Texture2DMultisampleArray;
|
||||
|
||||
case Target.Texture3D:
|
||||
return info.Target == Target.Texture3D;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Size GetAlignedSize(TextureInfo info, int level = 0)
|
||||
{
|
||||
int width = Math.Max(1, info.Width >> level);
|
||||
int height = Math.Max(1, info.Height >> level);
|
||||
|
||||
if (info.IsLinear)
|
||||
{
|
||||
return SizeCalculator.GetLinearAlignedSize(
|
||||
width,
|
||||
height,
|
||||
info.FormatInfo.BlockWidth,
|
||||
info.FormatInfo.BlockHeight,
|
||||
info.FormatInfo.BytesPerPixel);
|
||||
}
|
||||
else
|
||||
{
|
||||
int depth = Math.Max(1, info.GetDepth() >> level);
|
||||
|
||||
(int gobBlocksInY, int gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(
|
||||
height,
|
||||
depth,
|
||||
info.FormatInfo.BlockHeight,
|
||||
info.GobBlocksInY,
|
||||
info.GobBlocksInZ);
|
||||
|
||||
return SizeCalculator.GetBlockLinearAlignedSize(
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
info.FormatInfo.BlockWidth,
|
||||
info.FormatInfo.BlockHeight,
|
||||
info.FormatInfo.BytesPerPixel,
|
||||
gobBlocksInY,
|
||||
gobBlocksInZ,
|
||||
info.GobBlocksInTileX);
|
||||
}
|
||||
}
|
||||
|
||||
public ITexture GetTargetTexture(Target target)
|
||||
{
|
||||
if (target == _info.Target)
|
||||
{
|
||||
return HostTexture;
|
||||
}
|
||||
|
||||
if (_arrayViewTexture == null && IsSameDimensionsTarget(target))
|
||||
{
|
||||
TextureCreateInfo createInfo = new TextureCreateInfo(
|
||||
_info.Width,
|
||||
_info.Height,
|
||||
target == Target.CubemapArray ? 6 : 1,
|
||||
_info.Levels,
|
||||
_info.Samples,
|
||||
_info.FormatInfo.BlockWidth,
|
||||
_info.FormatInfo.BlockHeight,
|
||||
_info.FormatInfo.BytesPerPixel,
|
||||
_info.FormatInfo.Format,
|
||||
_info.DepthStencilMode,
|
||||
target,
|
||||
_info.SwizzleR,
|
||||
_info.SwizzleG,
|
||||
_info.SwizzleB,
|
||||
_info.SwizzleA);
|
||||
|
||||
ITexture viewTexture = HostTexture.CreateView(createInfo, 0, 0);
|
||||
|
||||
_arrayViewTexture = viewTexture;
|
||||
_arrayViewTarget = target;
|
||||
|
||||
return viewTexture;
|
||||
}
|
||||
else if (_arrayViewTarget == target)
|
||||
{
|
||||
return _arrayViewTexture;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsSameDimensionsTarget(Target target)
|
||||
{
|
||||
switch (_info.Target)
|
||||
{
|
||||
case Target.Texture1D:
|
||||
case Target.Texture1DArray:
|
||||
return target == Target.Texture1D ||
|
||||
target == Target.Texture1DArray;
|
||||
|
||||
case Target.Texture2D:
|
||||
case Target.Texture2DArray:
|
||||
return target == Target.Texture2D ||
|
||||
target == Target.Texture2DArray;
|
||||
|
||||
case Target.Cubemap:
|
||||
case Target.CubemapArray:
|
||||
return target == Target.Cubemap ||
|
||||
target == Target.CubemapArray;
|
||||
|
||||
case Target.Texture2DMultisample:
|
||||
case Target.Texture2DMultisampleArray:
|
||||
return target == Target.Texture2DMultisample ||
|
||||
target == Target.Texture2DMultisampleArray;
|
||||
|
||||
case Target.Texture3D:
|
||||
return target == Target.Texture3D;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ReplaceView(Texture parent, TextureInfo info, ITexture hostTexture)
|
||||
{
|
||||
ReplaceStorage(hostTexture);
|
||||
|
||||
parent._viewStorage.AddView(this);
|
||||
|
||||
SetInfo(info);
|
||||
}
|
||||
|
||||
private void SetInfo(TextureInfo info)
|
||||
{
|
||||
_info = info;
|
||||
|
||||
_depth = info.GetDepth();
|
||||
_layers = info.GetLayers();
|
||||
}
|
||||
|
||||
private void ReplaceStorage(ITexture hostTexture)
|
||||
{
|
||||
DisposeTextures();
|
||||
|
||||
HostTexture = hostTexture;
|
||||
}
|
||||
|
||||
public bool OverlapsWith(ulong address, ulong size)
|
||||
{
|
||||
return Address < address + size && address < EndAddress;
|
||||
}
|
||||
|
||||
public void Invalidate()
|
||||
{
|
||||
// _hasData = false;
|
||||
}
|
||||
|
||||
public void IncrementReferenceCount()
|
||||
{
|
||||
_referenceCount++;
|
||||
}
|
||||
|
||||
public void DecrementReferenceCount()
|
||||
{
|
||||
if (--_referenceCount == 0)
|
||||
{
|
||||
if (_viewStorage != this)
|
||||
{
|
||||
_viewStorage.RemoveView(this);
|
||||
}
|
||||
|
||||
_context.Methods.TextureManager.RemoveTextureFromCache(this);
|
||||
|
||||
DisposeTextures();
|
||||
}
|
||||
}
|
||||
|
||||
private void DisposeTextures()
|
||||
{
|
||||
HostTexture.Dispose();
|
||||
|
||||
_arrayViewTexture?.Dispose();
|
||||
_arrayViewTexture = null;
|
||||
}
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal file
17
Ryujinx.Graphics.Gpu/Image/TextureBindingInfo.cs
Normal file
|
@ -0,0 +1,17 @@
|
|||
using Ryujinx.Graphics.GAL.Texture;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
struct TextureBindingInfo
|
||||
{
|
||||
public Target Target { get; }
|
||||
|
||||
public int Handle { get; }
|
||||
|
||||
public TextureBindingInfo(Target target, int handle)
|
||||
{
|
||||
Target = target;
|
||||
Handle = handle;
|
||||
}
|
||||
}
|
||||
}
|
95
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal file
95
Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
static class TextureCompatibility
|
||||
{
|
||||
private enum FormatClass
|
||||
{
|
||||
Unclassified,
|
||||
BCn64,
|
||||
BCn128,
|
||||
Bc1Rgb,
|
||||
Bc1Rgba,
|
||||
Bc2,
|
||||
Bc3,
|
||||
Bc4,
|
||||
Bc5,
|
||||
Bc6,
|
||||
Bc7
|
||||
}
|
||||
|
||||
public static bool FormatCompatible(FormatInfo lhs, FormatInfo rhs)
|
||||
{
|
||||
if (IsDsFormat(lhs.Format) || IsDsFormat(rhs.Format))
|
||||
{
|
||||
return lhs.Format == rhs.Format;
|
||||
}
|
||||
|
||||
if (lhs.Format.IsAstc() || rhs.Format.IsAstc())
|
||||
{
|
||||
return lhs.Format == rhs.Format;
|
||||
}
|
||||
|
||||
if (lhs.IsCompressed && rhs.IsCompressed)
|
||||
{
|
||||
FormatClass lhsClass = GetFormatClass(lhs.Format);
|
||||
FormatClass rhsClass = GetFormatClass(rhs.Format);
|
||||
|
||||
return lhsClass == rhsClass;
|
||||
}
|
||||
else
|
||||
{
|
||||
return lhs.BytesPerPixel == rhs.BytesPerPixel;
|
||||
}
|
||||
}
|
||||
|
||||
private static FormatClass GetFormatClass(Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Format.Bc1RgbSrgb:
|
||||
case Format.Bc1RgbUnorm:
|
||||
return FormatClass.Bc1Rgb;
|
||||
case Format.Bc1RgbaSrgb:
|
||||
case Format.Bc1RgbaUnorm:
|
||||
return FormatClass.Bc1Rgba;
|
||||
case Format.Bc2Srgb:
|
||||
case Format.Bc2Unorm:
|
||||
return FormatClass.Bc2;
|
||||
case Format.Bc3Srgb:
|
||||
case Format.Bc3Unorm:
|
||||
return FormatClass.Bc3;
|
||||
case Format.Bc4Snorm:
|
||||
case Format.Bc4Unorm:
|
||||
return FormatClass.Bc4;
|
||||
case Format.Bc5Snorm:
|
||||
case Format.Bc5Unorm:
|
||||
return FormatClass.Bc5;
|
||||
case Format.Bc6HSfloat:
|
||||
case Format.Bc6HUfloat:
|
||||
return FormatClass.Bc6;
|
||||
case Format.Bc7Srgb:
|
||||
case Format.Bc7Unorm:
|
||||
return FormatClass.Bc7;
|
||||
}
|
||||
|
||||
return FormatClass.Unclassified;
|
||||
}
|
||||
|
||||
private static bool IsDsFormat(Format format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case Format.D16Unorm:
|
||||
case Format.D24X8Unorm:
|
||||
case Format.D24UnormS8Uint:
|
||||
case Format.D32Float:
|
||||
case Format.D32FloatS8Uint:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
35
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal file
35
Ryujinx.Graphics.Gpu/Image/TextureComponent.cs
Normal file
|
@ -0,0 +1,35 @@
|
|||
using Ryujinx.Graphics.GAL.Texture;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
enum TextureComponent
|
||||
{
|
||||
Zero = 0,
|
||||
Red = 2,
|
||||
Green = 3,
|
||||
Blue = 4,
|
||||
Alpha = 5,
|
||||
OneSI = 6,
|
||||
OneF = 7
|
||||
}
|
||||
|
||||
static class TextureComponentConverter
|
||||
{
|
||||
public static SwizzleComponent Convert(this TextureComponent component)
|
||||
{
|
||||
switch (component)
|
||||
{
|
||||
case TextureComponent.Zero: return SwizzleComponent.Zero;
|
||||
case TextureComponent.Red: return SwizzleComponent.Red;
|
||||
case TextureComponent.Green: return SwizzleComponent.Green;
|
||||
case TextureComponent.Blue: return SwizzleComponent.Blue;
|
||||
case TextureComponent.Alpha: return SwizzleComponent.Alpha;
|
||||
case TextureComponent.OneSI:
|
||||
case TextureComponent.OneF:
|
||||
return SwizzleComponent.One;
|
||||
}
|
||||
|
||||
return SwizzleComponent.Zero;
|
||||
}
|
||||
}
|
||||
}
|
119
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal file
119
Ryujinx.Graphics.Gpu/Image/TextureDescriptor.cs
Normal file
|
@ -0,0 +1,119 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
struct TextureDescriptor
|
||||
{
|
||||
public uint Word0;
|
||||
public uint Word1;
|
||||
public uint Word2;
|
||||
public uint Word3;
|
||||
public uint Word4;
|
||||
public uint Word5;
|
||||
public uint Word6;
|
||||
public uint Word7;
|
||||
|
||||
public uint UnpackFormat()
|
||||
{
|
||||
return Word0 & 0x8007ffff;
|
||||
}
|
||||
|
||||
public TextureComponent UnpackSwizzleR()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 19) & 7);
|
||||
}
|
||||
|
||||
public TextureComponent UnpackSwizzleG()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 22) & 7);
|
||||
}
|
||||
|
||||
public TextureComponent UnpackSwizzleB()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 25) & 7);
|
||||
}
|
||||
|
||||
public TextureComponent UnpackSwizzleA()
|
||||
{
|
||||
return(TextureComponent)((Word0 >> 28) & 7);
|
||||
}
|
||||
|
||||
public ulong UnpackAddress()
|
||||
{
|
||||
return Word1 | ((ulong)(Word2 & 0xffff) << 32);
|
||||
}
|
||||
|
||||
public TextureDescriptorType UnpackTextureDescriptorType()
|
||||
{
|
||||
return (TextureDescriptorType)((Word2 >> 21) & 7);
|
||||
}
|
||||
|
||||
public int UnpackStride()
|
||||
{
|
||||
return (int)(Word3 & 0xffff) << 5;
|
||||
}
|
||||
|
||||
public int UnpackGobBlocksInX()
|
||||
{
|
||||
return 1 << (int)(Word3 & 7);
|
||||
}
|
||||
|
||||
public int UnpackGobBlocksInY()
|
||||
{
|
||||
return 1 << (int)((Word3 >> 3) & 7);
|
||||
}
|
||||
|
||||
public int UnpackGobBlocksInZ()
|
||||
{
|
||||
return 1 << (int)((Word3 >> 6) & 7);
|
||||
}
|
||||
|
||||
public int UnpackGobBlocksInTileX()
|
||||
{
|
||||
return 1 << (int)((Word3 >> 10) & 7);
|
||||
}
|
||||
|
||||
public int UnpackLevels()
|
||||
{
|
||||
return (int)(Word3 >> 28) + 1;
|
||||
}
|
||||
|
||||
public int UnpackWidth()
|
||||
{
|
||||
return (int)(Word4 & 0xffff) + 1;
|
||||
}
|
||||
|
||||
public bool UnpackSrgb()
|
||||
{
|
||||
return (Word4 & (1 << 22)) != 0;
|
||||
}
|
||||
|
||||
public TextureTarget UnpackTextureTarget()
|
||||
{
|
||||
return (TextureTarget)((Word4 >> 23) & 0xf);
|
||||
}
|
||||
|
||||
public int UnpackHeight()
|
||||
{
|
||||
return (int)(Word5 & 0xffff) + 1;
|
||||
}
|
||||
|
||||
public int UnpackDepth()
|
||||
{
|
||||
return (int)((Word5 >> 16) & 0x3fff) + 1;
|
||||
}
|
||||
|
||||
public int UnpackBaseLevel()
|
||||
{
|
||||
return (int)(Word7 & 0xf);
|
||||
}
|
||||
|
||||
public int UnpackMaxLevelInclusive()
|
||||
{
|
||||
return (int)((Word7 >> 4) & 0xf);
|
||||
}
|
||||
|
||||
public TextureMsaaMode UnpackTextureMsaaMode()
|
||||
{
|
||||
return (TextureMsaaMode)((Word7 >> 8) & 0xf);
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
11
Ryujinx.Graphics.Gpu/Image/TextureDescriptorType.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
enum TextureDescriptorType
|
||||
{
|
||||
Buffer,
|
||||
LinearColorKey,
|
||||
Linear,
|
||||
BlockLinear,
|
||||
BlockLinearColorKey
|
||||
}
|
||||
}
|
101
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal file
101
Ryujinx.Graphics.Gpu/Image/TextureInfo.cs
Normal file
|
@ -0,0 +1,101 @@
|
|||
using Ryujinx.Graphics.GAL.Texture;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
struct TextureInfo
|
||||
{
|
||||
public ulong Address { get; }
|
||||
|
||||
public int Width { get; }
|
||||
public int Height { get; }
|
||||
public int DepthOrLayers { get; }
|
||||
public int Levels { get; }
|
||||
public int SamplesInX { get; }
|
||||
public int SamplesInY { get; }
|
||||
public int Stride { get; }
|
||||
public bool IsLinear { get; }
|
||||
public int GobBlocksInY { get; }
|
||||
public int GobBlocksInZ { get; }
|
||||
public int GobBlocksInTileX { get; }
|
||||
|
||||
public int Samples => SamplesInX * SamplesInY;
|
||||
|
||||
public Target Target { get; }
|
||||
|
||||
public FormatInfo FormatInfo { get; }
|
||||
|
||||
public DepthStencilMode DepthStencilMode { get; }
|
||||
|
||||
public SwizzleComponent SwizzleR { get; }
|
||||
public SwizzleComponent SwizzleG { get; }
|
||||
public SwizzleComponent SwizzleB { get; }
|
||||
public SwizzleComponent SwizzleA { get; }
|
||||
|
||||
public TextureInfo(
|
||||
ulong address,
|
||||
int width,
|
||||
int height,
|
||||
int depthOrLayers,
|
||||
int levels,
|
||||
int samplesInX,
|
||||
int samplesInY,
|
||||
int stride,
|
||||
bool isLinear,
|
||||
int gobBlocksInY,
|
||||
int gobBlocksInZ,
|
||||
int gobBlocksInTileX,
|
||||
Target target,
|
||||
FormatInfo formatInfo,
|
||||
DepthStencilMode depthStencilMode = DepthStencilMode.Depth,
|
||||
SwizzleComponent swizzleR = SwizzleComponent.Red,
|
||||
SwizzleComponent swizzleG = SwizzleComponent.Green,
|
||||
SwizzleComponent swizzleB = SwizzleComponent.Blue,
|
||||
SwizzleComponent swizzleA = SwizzleComponent.Alpha)
|
||||
{
|
||||
Address = address;
|
||||
Width = width;
|
||||
Height = height;
|
||||
DepthOrLayers = depthOrLayers;
|
||||
Levels = levels;
|
||||
SamplesInX = samplesInX;
|
||||
SamplesInY = samplesInY;
|
||||
Stride = stride;
|
||||
IsLinear = isLinear;
|
||||
GobBlocksInY = gobBlocksInY;
|
||||
GobBlocksInZ = gobBlocksInZ;
|
||||
GobBlocksInTileX = gobBlocksInTileX;
|
||||
Target = target;
|
||||
FormatInfo = formatInfo;
|
||||
DepthStencilMode = depthStencilMode;
|
||||
SwizzleR = swizzleR;
|
||||
SwizzleG = swizzleG;
|
||||
SwizzleB = swizzleB;
|
||||
SwizzleA = swizzleA;
|
||||
}
|
||||
|
||||
public int GetDepth()
|
||||
{
|
||||
return Target == Target.Texture3D ? DepthOrLayers : 1;
|
||||
}
|
||||
|
||||
public int GetLayers()
|
||||
{
|
||||
if (Target == Target.Texture2DArray || Target == Target.Texture2DMultisampleArray)
|
||||
{
|
||||
return DepthOrLayers;
|
||||
}
|
||||
else if (Target == Target.CubemapArray)
|
||||
{
|
||||
return DepthOrLayers * 6;
|
||||
}
|
||||
else if (Target == Target.Cubemap)
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
669
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal file
669
Ryujinx.Graphics.Gpu/Image/TextureManager.cs
Normal file
|
@ -0,0 +1,669 @@
|
|||
using Ryujinx.Common;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Texture;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using Ryujinx.Graphics.Gpu.State;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Texture;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
class TextureManager
|
||||
{
|
||||
private GpuContext _context;
|
||||
private BufferManager _bufferManager;
|
||||
|
||||
private SamplerPool _samplerPool;
|
||||
|
||||
private ulong _texturePoolAddress;
|
||||
private int _texturePoolMaximumId;
|
||||
|
||||
private TexturePoolCache _texturePoolCache;
|
||||
|
||||
private Texture[] _rtColors;
|
||||
private Texture _rtColor3D;
|
||||
|
||||
private Texture _rtDepthStencil;
|
||||
|
||||
private ITexture[] _rtHostColors;
|
||||
|
||||
private ITexture _rtHostDs;
|
||||
|
||||
private RangeList<Texture> _textures;
|
||||
|
||||
private AutoDeleteCache _cache;
|
||||
|
||||
private TextureBindingInfo[][] _bindings;
|
||||
|
||||
private struct TextureStatePerStage
|
||||
{
|
||||
public ITexture Texture;
|
||||
public ISampler Sampler;
|
||||
}
|
||||
|
||||
private TextureStatePerStage[][] _textureState;
|
||||
|
||||
private int _textureBufferIndex;
|
||||
|
||||
public TextureManager(GpuContext context, BufferManager bufferManager)
|
||||
{
|
||||
_context = context;
|
||||
_bufferManager = bufferManager;
|
||||
|
||||
_texturePoolCache = new TexturePoolCache(context, this);
|
||||
|
||||
_rtColors = new Texture[Constants.TotalRenderTargets];
|
||||
|
||||
_rtHostColors = new ITexture[Constants.TotalRenderTargets];
|
||||
|
||||
_textures = new RangeList<Texture>();
|
||||
|
||||
_cache = new AutoDeleteCache();
|
||||
|
||||
_bindings = new TextureBindingInfo[Constants.TotalShaderStages][];
|
||||
|
||||
_textureState = new TextureStatePerStage[Constants.TotalShaderStages][];
|
||||
}
|
||||
|
||||
public void BindTextures(int stage, TextureBindingInfo[] bindings)
|
||||
{
|
||||
_bindings[stage] = bindings;
|
||||
|
||||
_textureState[stage] = new TextureStatePerStage[bindings.Length];
|
||||
}
|
||||
|
||||
public void SetTextureBufferIndex(int index)
|
||||
{
|
||||
_textureBufferIndex = index;
|
||||
}
|
||||
|
||||
public void SetSamplerPool(ulong gpuVa, int maximumId)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
if (_samplerPool != null)
|
||||
{
|
||||
if (_samplerPool.Address == address)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_samplerPool.Dispose();
|
||||
}
|
||||
|
||||
_samplerPool = new SamplerPool(_context, address, maximumId);
|
||||
}
|
||||
|
||||
public void SetTexturePool(ulong gpuVa, int maximumId)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
_texturePoolAddress = address;
|
||||
_texturePoolMaximumId = maximumId;
|
||||
}
|
||||
|
||||
public void SetRenderTargetColor(int index, Texture color)
|
||||
{
|
||||
_rtColors[index] = color;
|
||||
|
||||
_rtColor3D = null;
|
||||
}
|
||||
|
||||
public void SetRenderTargetColor3D(Texture color)
|
||||
{
|
||||
_rtColor3D = color;
|
||||
}
|
||||
|
||||
public void SetRenderTargetDepthStencil(Texture depthStencil)
|
||||
{
|
||||
_rtDepthStencil = depthStencil;
|
||||
}
|
||||
|
||||
public void CommitBindings()
|
||||
{
|
||||
UpdateTextures();
|
||||
UpdateRenderTargets();
|
||||
}
|
||||
|
||||
private void UpdateTextures()
|
||||
{
|
||||
TexturePool texturePool = _texturePoolCache.FindOrCreate(
|
||||
_texturePoolAddress,
|
||||
_texturePoolMaximumId);
|
||||
|
||||
for (ShaderStage stage = ShaderStage.Vertex; stage <= ShaderStage.Fragment; stage++)
|
||||
{
|
||||
int stageIndex = (int)stage - 1;
|
||||
|
||||
if (_bindings[stageIndex] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int index = 0; index < _bindings[stageIndex].Length; index++)
|
||||
{
|
||||
TextureBindingInfo binding = _bindings[stageIndex][index];
|
||||
|
||||
int packedId = ReadPackedId(stageIndex, binding.Handle);
|
||||
|
||||
int textureId = (packedId >> 0) & 0xfffff;
|
||||
int samplerId = (packedId >> 20) & 0xfff;
|
||||
|
||||
Texture texture = texturePool.Get(textureId);
|
||||
|
||||
ITexture hostTexture = texture?.GetTargetTexture(binding.Target);
|
||||
|
||||
if (_textureState[stageIndex][index].Texture != hostTexture)
|
||||
{
|
||||
_textureState[stageIndex][index].Texture = hostTexture;
|
||||
|
||||
_context.Renderer.GraphicsPipeline.BindTexture(index, stage, hostTexture);
|
||||
}
|
||||
|
||||
Sampler sampler = _samplerPool.Get(samplerId);
|
||||
|
||||
ISampler hostSampler = sampler?.HostSampler;
|
||||
|
||||
if (_textureState[stageIndex][index].Sampler != hostSampler)
|
||||
{
|
||||
_textureState[stageIndex][index].Sampler = hostSampler;
|
||||
|
||||
_context.Renderer.GraphicsPipeline.BindSampler(index, stage, hostSampler);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateRenderTargets()
|
||||
{
|
||||
bool anyChanged = false;
|
||||
|
||||
if (_rtHostDs != _rtDepthStencil?.HostTexture)
|
||||
{
|
||||
_rtHostDs = _rtDepthStencil?.HostTexture;
|
||||
|
||||
anyChanged = true;
|
||||
}
|
||||
|
||||
if (_rtColor3D == null)
|
||||
{
|
||||
for (int index = 0; index < _rtColors.Length; index++)
|
||||
{
|
||||
ITexture hostTexture = _rtColors[index]?.HostTexture;
|
||||
|
||||
if (_rtHostColors[index] != hostTexture)
|
||||
{
|
||||
_rtHostColors[index] = hostTexture;
|
||||
|
||||
anyChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyChanged)
|
||||
{
|
||||
_context.Renderer.GraphicsPipeline.SetRenderTargets(_rtHostColors, _rtHostDs);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_rtHostColors[0] != _rtColor3D.HostTexture)
|
||||
{
|
||||
_rtHostColors[0] = _rtColor3D.HostTexture;
|
||||
|
||||
anyChanged = true;
|
||||
}
|
||||
|
||||
if (anyChanged)
|
||||
{
|
||||
_context.Renderer.GraphicsPipeline.SetRenderTargets(_rtColor3D.HostTexture, _rtHostDs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int ReadPackedId(int stage, int wordOffset)
|
||||
{
|
||||
ulong address = _bufferManager.GetGraphicsUniformBufferAddress(stage, _textureBufferIndex);
|
||||
|
||||
address += (uint)wordOffset * 4;
|
||||
|
||||
return BitConverter.ToInt32(_context.PhysicalMemory.Read(address, 4));
|
||||
}
|
||||
|
||||
public Texture FindOrCreateTexture(CopyTexture copyTexture)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(copyTexture.Address.Pack());
|
||||
|
||||
if (address == MemoryManager.BadAddress)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int gobBlocksInY = copyTexture.MemoryLayout.UnpackGobBlocksInY();
|
||||
int gobBlocksInZ = copyTexture.MemoryLayout.UnpackGobBlocksInZ();
|
||||
|
||||
FormatInfo formatInfo = copyTexture.Format.Convert();
|
||||
|
||||
TextureInfo info = new TextureInfo(
|
||||
address,
|
||||
copyTexture.Width,
|
||||
copyTexture.Height,
|
||||
copyTexture.Depth,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
copyTexture.Stride,
|
||||
copyTexture.LinearLayout,
|
||||
gobBlocksInY,
|
||||
gobBlocksInZ,
|
||||
1,
|
||||
Target.Texture2D,
|
||||
formatInfo);
|
||||
|
||||
Texture texture = FindOrCreateTexture(info, TextureSearchFlags.IgnoreMs);
|
||||
|
||||
texture.SynchronizeMemory();
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public Texture FindOrCreateTexture(RtColorState colorState, int samplesInX, int samplesInY)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(colorState.Address.Pack());
|
||||
|
||||
if (address == MemoryManager.BadAddress)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isLinear = colorState.MemoryLayout.UnpackIsLinear();
|
||||
|
||||
int gobBlocksInY = colorState.MemoryLayout.UnpackGobBlocksInY();
|
||||
int gobBlocksInZ = colorState.MemoryLayout.UnpackGobBlocksInZ();
|
||||
|
||||
Target target;
|
||||
|
||||
if (colorState.MemoryLayout.UnpackIsTarget3D())
|
||||
{
|
||||
target = Target.Texture3D;
|
||||
}
|
||||
else if ((samplesInX | samplesInY) != 1)
|
||||
{
|
||||
target = colorState.Depth > 1
|
||||
? Target.Texture2DMultisampleArray
|
||||
: Target.Texture2DMultisample;
|
||||
}
|
||||
else
|
||||
{
|
||||
target = colorState.Depth > 1
|
||||
? Target.Texture2DArray
|
||||
: Target.Texture2D;
|
||||
}
|
||||
|
||||
FormatInfo formatInfo = colorState.Format.Convert();
|
||||
|
||||
int width, stride;
|
||||
|
||||
// For linear textures, the width value is actually the stride.
|
||||
// We can easily get the width by dividing the stride by the bpp,
|
||||
// since the stride is the total number of bytes occupied by a
|
||||
// line. The stride should also meet alignment constraints however,
|
||||
// so the width we get here is the aligned width.
|
||||
if (isLinear)
|
||||
{
|
||||
width = colorState.WidthOrStride / formatInfo.BytesPerPixel;
|
||||
stride = colorState.WidthOrStride;
|
||||
}
|
||||
else
|
||||
{
|
||||
width = colorState.WidthOrStride;
|
||||
stride = 0;
|
||||
}
|
||||
|
||||
TextureInfo info = new TextureInfo(
|
||||
address,
|
||||
width,
|
||||
colorState.Height,
|
||||
colorState.Depth,
|
||||
1,
|
||||
samplesInX,
|
||||
samplesInY,
|
||||
stride,
|
||||
isLinear,
|
||||
gobBlocksInY,
|
||||
gobBlocksInZ,
|
||||
1,
|
||||
target,
|
||||
formatInfo);
|
||||
|
||||
Texture texture = FindOrCreateTexture(info);
|
||||
|
||||
texture.SynchronizeMemory();
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public Texture FindOrCreateTexture(RtDepthStencilState dsState, Size3D size, int samplesInX, int samplesInY)
|
||||
{
|
||||
ulong address = _context.MemoryManager.Translate(dsState.Address.Pack());
|
||||
|
||||
if (address == MemoryManager.BadAddress)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
|
||||
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
|
||||
|
||||
Target target = (samplesInX | samplesInY) != 1
|
||||
? Target.Texture2DMultisample
|
||||
: Target.Texture2D;
|
||||
|
||||
FormatInfo formatInfo = dsState.Format.Convert();
|
||||
|
||||
TextureInfo info = new TextureInfo(
|
||||
address,
|
||||
size.Width,
|
||||
size.Height,
|
||||
size.Depth,
|
||||
1,
|
||||
samplesInX,
|
||||
samplesInY,
|
||||
0,
|
||||
false,
|
||||
gobBlocksInY,
|
||||
gobBlocksInZ,
|
||||
1,
|
||||
target,
|
||||
formatInfo);
|
||||
|
||||
Texture texture = FindOrCreateTexture(info);
|
||||
|
||||
texture.SynchronizeMemory();
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
public Texture FindOrCreateTexture(TextureInfo info, TextureSearchFlags flags = TextureSearchFlags.None)
|
||||
{
|
||||
bool isSamplerTexture = (flags & TextureSearchFlags.Sampler) != 0;
|
||||
|
||||
// Try to find a perfect texture match, with the same address and parameters.
|
||||
Texture[] sameAddressOverlaps = _textures.FindOverlaps(info.Address);
|
||||
|
||||
foreach (Texture overlap in sameAddressOverlaps)
|
||||
{
|
||||
if (overlap.IsPerfectMatch(info, flags))
|
||||
{
|
||||
if (!isSamplerTexture)
|
||||
{
|
||||
// If not a sampler texture, it is managed by the auto delete
|
||||
// cache, ensure that it is on the "top" of the list to avoid
|
||||
// deletion.
|
||||
_cache.Lift(overlap);
|
||||
}
|
||||
else if (!overlap.SizeMatches(info))
|
||||
{
|
||||
// If this is used for sampling, the size must match,
|
||||
// otherwise the shader would sample garbage data.
|
||||
// To fix that, we create a new texture with the correct
|
||||
// size, and copy the data from the old one to the new one.
|
||||
overlap.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
|
||||
}
|
||||
|
||||
return overlap;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate texture sizes, used to find all overlapping textures.
|
||||
SizeInfo sizeInfo;
|
||||
|
||||
if (info.IsLinear)
|
||||
{
|
||||
sizeInfo = SizeCalculator.GetLinearTextureSize(
|
||||
info.Stride,
|
||||
info.Height,
|
||||
info.FormatInfo.BlockHeight);
|
||||
}
|
||||
else
|
||||
{
|
||||
sizeInfo = SizeCalculator.GetBlockLinearTextureSize(
|
||||
info.Width,
|
||||
info.Height,
|
||||
info.GetDepth(),
|
||||
info.Levels,
|
||||
info.GetLayers(),
|
||||
info.FormatInfo.BlockWidth,
|
||||
info.FormatInfo.BlockHeight,
|
||||
info.FormatInfo.BytesPerPixel,
|
||||
info.GobBlocksInY,
|
||||
info.GobBlocksInZ,
|
||||
info.GobBlocksInTileX);
|
||||
}
|
||||
|
||||
// Find view compatible matches.
|
||||
ulong size = (ulong)sizeInfo.TotalSize;
|
||||
|
||||
Texture[] overlaps = _textures.FindOverlaps(info.Address, size);
|
||||
|
||||
Texture texture = null;
|
||||
|
||||
foreach (Texture overlap in overlaps)
|
||||
{
|
||||
if (overlap.IsViewCompatible(info, size, out int firstLayer, out int firstLevel))
|
||||
{
|
||||
if (!isSamplerTexture)
|
||||
{
|
||||
info = AdjustSizes(overlap, info, firstLevel);
|
||||
}
|
||||
|
||||
texture = overlap.CreateView(info, sizeInfo, firstLayer, firstLevel);
|
||||
|
||||
// The size only matters (and is only really reliable) when the
|
||||
// texture is used on a sampler, because otherwise the size will be
|
||||
// aligned.
|
||||
if (!overlap.SizeMatches(info, firstLevel) && isSamplerTexture)
|
||||
{
|
||||
texture.ChangeSize(info.Width, info.Height, info.DepthOrLayers);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// No match, create a new texture.
|
||||
if (texture == null)
|
||||
{
|
||||
texture = new Texture(_context, info, sizeInfo);
|
||||
|
||||
// We need to synchronize before copying the old view data to the texture,
|
||||
// otherwise the copied data would be overwritten by a future synchronization.
|
||||
texture.SynchronizeMemory();
|
||||
|
||||
foreach (Texture overlap in overlaps)
|
||||
{
|
||||
if (texture.IsViewCompatible(overlap.Info, overlap.Size, out int firstLayer, out int firstLevel))
|
||||
{
|
||||
TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, firstLevel);
|
||||
|
||||
TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities);
|
||||
|
||||
ITexture newView = texture.HostTexture.CreateView(createInfo, firstLayer, firstLevel);
|
||||
|
||||
overlap.HostTexture.CopyTo(newView);
|
||||
|
||||
overlap.ReplaceView(texture, overlapInfo, newView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sampler textures are managed by the texture pool, all other textures
|
||||
// are managed by the auto delete cache.
|
||||
if (!isSamplerTexture)
|
||||
{
|
||||
_cache.Add(texture);
|
||||
}
|
||||
|
||||
_textures.Add(texture);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
private static TextureInfo AdjustSizes(Texture parent, TextureInfo info, int firstLevel)
|
||||
{
|
||||
// When the texture is used as view of another texture, we must
|
||||
// ensure that the sizes are valid, otherwise data uploads would fail
|
||||
// (and the size wouldn't match the real size used on the host API).
|
||||
// Given a parent texture from where the view is created, we have the
|
||||
// following rules:
|
||||
// - The view size must be equal to the parent size, divided by (2 ^ l),
|
||||
// where l is the first mipmap level of the view. The division result must
|
||||
// be rounded down, and the result must be clamped to 1.
|
||||
// - If the parent format is compressed, and the view format isn't, the
|
||||
// view size is calculated as above, but the width and height of the
|
||||
// view must be also divided by the compressed format block width and height.
|
||||
// - If the parent format is not compressed, and the view is, the view
|
||||
// size is calculated as described on the first point, but the width and height
|
||||
// of the view must be also multiplied by the block width and height.
|
||||
int width = Math.Max(1, parent.Info.Width >> firstLevel);
|
||||
int height = Math.Max(1, parent.Info.Height >> firstLevel);
|
||||
|
||||
if (parent.Info.FormatInfo.IsCompressed && !info.FormatInfo.IsCompressed)
|
||||
{
|
||||
width = BitUtils.DivRoundUp(width, parent.Info.FormatInfo.BlockWidth);
|
||||
height = BitUtils.DivRoundUp(height, parent.Info.FormatInfo.BlockHeight);
|
||||
}
|
||||
else if (!parent.Info.FormatInfo.IsCompressed && info.FormatInfo.IsCompressed)
|
||||
{
|
||||
width *= info.FormatInfo.BlockWidth;
|
||||
height *= info.FormatInfo.BlockHeight;
|
||||
}
|
||||
|
||||
int depthOrLayers;
|
||||
|
||||
if (info.Target == Target.Texture3D)
|
||||
{
|
||||
depthOrLayers = Math.Max(1, parent.Info.DepthOrLayers >> firstLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
depthOrLayers = info.DepthOrLayers;
|
||||
}
|
||||
|
||||
return new TextureInfo(
|
||||
info.Address,
|
||||
width,
|
||||
height,
|
||||
depthOrLayers,
|
||||
info.Levels,
|
||||
info.SamplesInX,
|
||||
info.SamplesInY,
|
||||
info.Stride,
|
||||
info.IsLinear,
|
||||
info.GobBlocksInY,
|
||||
info.GobBlocksInZ,
|
||||
info.GobBlocksInTileX,
|
||||
info.Target,
|
||||
info.FormatInfo,
|
||||
info.DepthStencilMode,
|
||||
info.SwizzleR,
|
||||
info.SwizzleG,
|
||||
info.SwizzleB,
|
||||
info.SwizzleA);
|
||||
}
|
||||
|
||||
public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps)
|
||||
{
|
||||
FormatInfo formatInfo = info.FormatInfo;
|
||||
|
||||
if (!caps.SupportsAstcCompression)
|
||||
{
|
||||
if (formatInfo.Format.IsAstcUnorm())
|
||||
{
|
||||
formatInfo = new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4);
|
||||
}
|
||||
else if (formatInfo.Format.IsAstcSrgb())
|
||||
{
|
||||
formatInfo = new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4);
|
||||
}
|
||||
}
|
||||
|
||||
int width = info.Width / info.SamplesInX;
|
||||
int height = info.Height / info.SamplesInY;
|
||||
|
||||
int depth = info.GetDepth() * info.GetLayers();
|
||||
|
||||
return new TextureCreateInfo(
|
||||
width,
|
||||
height,
|
||||
depth,
|
||||
info.Levels,
|
||||
info.Samples,
|
||||
formatInfo.BlockWidth,
|
||||
formatInfo.BlockHeight,
|
||||
formatInfo.BytesPerPixel,
|
||||
formatInfo.Format,
|
||||
info.DepthStencilMode,
|
||||
info.Target,
|
||||
info.SwizzleR,
|
||||
info.SwizzleG,
|
||||
info.SwizzleB,
|
||||
info.SwizzleA);
|
||||
}
|
||||
|
||||
public Texture Find2(ulong address)
|
||||
{
|
||||
Texture[] ts = _textures.FindOverlaps(address, 1);
|
||||
|
||||
if (ts.Length == 2)
|
||||
{
|
||||
return ts[1];
|
||||
}
|
||||
|
||||
if (ts.Length == 0)
|
||||
{
|
||||
ts = _textures.FindOverlaps(address - 1, 2);
|
||||
}
|
||||
|
||||
if (ts.Length == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ts[0];
|
||||
}
|
||||
|
||||
public void InvalidateRange(ulong address, ulong size)
|
||||
{
|
||||
Texture[] overlaps = _textures.FindOverlaps(address, size);
|
||||
|
||||
foreach (Texture overlap in overlaps)
|
||||
{
|
||||
overlap.Invalidate();
|
||||
}
|
||||
|
||||
_samplerPool?.InvalidateRange(address, size);
|
||||
|
||||
_texturePoolCache.InvalidateRange(address, size);
|
||||
}
|
||||
|
||||
public void Flush()
|
||||
{
|
||||
foreach (Texture texture in _cache)
|
||||
{
|
||||
if (texture.Info.IsLinear && texture.Modified)
|
||||
{
|
||||
texture.Flush();
|
||||
|
||||
texture.Modified = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveTextureFromCache(Texture texture)
|
||||
{
|
||||
_textures.Remove(texture);
|
||||
}
|
||||
}
|
||||
}
|
53
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal file
53
Ryujinx.Graphics.Gpu/Image/TextureMsaaMode.cs
Normal file
|
@ -0,0 +1,53 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
enum TextureMsaaMode
|
||||
{
|
||||
Ms1x1 = 0,
|
||||
Ms2x2 = 2,
|
||||
Ms4x2 = 4,
|
||||
Ms2x1 = 5,
|
||||
Ms4x4 = 6
|
||||
}
|
||||
|
||||
static class TextureMsaaModeConverter
|
||||
{
|
||||
public static int SamplesCount(this TextureMsaaMode msaaMode)
|
||||
{
|
||||
switch (msaaMode)
|
||||
{
|
||||
case TextureMsaaMode.Ms2x1: return 2;
|
||||
case TextureMsaaMode.Ms2x2: return 4;
|
||||
case TextureMsaaMode.Ms4x2: return 8;
|
||||
case TextureMsaaMode.Ms4x4: return 16;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int SamplesInX(this TextureMsaaMode msaaMode)
|
||||
{
|
||||
switch (msaaMode)
|
||||
{
|
||||
case TextureMsaaMode.Ms2x1: return 2;
|
||||
case TextureMsaaMode.Ms2x2: return 2;
|
||||
case TextureMsaaMode.Ms4x2: return 4;
|
||||
case TextureMsaaMode.Ms4x4: return 4;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static int SamplesInY(this TextureMsaaMode msaaMode)
|
||||
{
|
||||
switch (msaaMode)
|
||||
{
|
||||
case TextureMsaaMode.Ms2x1: return 1;
|
||||
case TextureMsaaMode.Ms2x2: return 2;
|
||||
case TextureMsaaMode.Ms4x2: return 2;
|
||||
case TextureMsaaMode.Ms4x4: return 4;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
219
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal file
219
Ryujinx.Graphics.Gpu/Image/TexturePool.cs
Normal file
|
@ -0,0 +1,219 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.GAL.Texture;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
class TexturePool : Pool<Texture>
|
||||
{
|
||||
private TextureManager _textureManager;
|
||||
|
||||
public LinkedListNode<TexturePool> CacheNode { get; set; }
|
||||
|
||||
private struct TextureContainer
|
||||
{
|
||||
public Texture Texture0 { get; set; }
|
||||
public Texture Texture1 { get; set; }
|
||||
}
|
||||
|
||||
public TexturePool(
|
||||
GpuContext context,
|
||||
TextureManager textureManager,
|
||||
ulong address,
|
||||
int maximumId) : base(context, address, maximumId)
|
||||
{
|
||||
_textureManager = textureManager;
|
||||
}
|
||||
|
||||
public override Texture Get(int id)
|
||||
{
|
||||
if ((uint)id >= Items.Length)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
SynchronizeMemory();
|
||||
|
||||
Texture texture = Items[id];
|
||||
|
||||
if (texture == null)
|
||||
{
|
||||
ulong address = Address + (ulong)(uint)id * DescriptorSize;
|
||||
|
||||
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
|
||||
|
||||
TextureDescriptor descriptor = MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0];
|
||||
|
||||
TextureInfo info = GetInfo(descriptor);
|
||||
|
||||
// Bad address. We can't add a texture with a invalid address
|
||||
// to the cache.
|
||||
if (info.Address == MemoryManager.BadAddress)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
texture = _textureManager.FindOrCreateTexture(info, TextureSearchFlags.Sampler);
|
||||
|
||||
texture.IncrementReferenceCount();
|
||||
|
||||
Items[id] = texture;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Memory is automatically synchronized on texture creation.
|
||||
texture.SynchronizeMemory();
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
protected override void InvalidateRangeImpl(ulong address, ulong size)
|
||||
{
|
||||
ulong endAddress = address + size;
|
||||
|
||||
for (; address < endAddress; address += DescriptorSize)
|
||||
{
|
||||
int id = (int)((address - Address) / DescriptorSize);
|
||||
|
||||
Texture texture = Items[id];
|
||||
|
||||
if (texture != null)
|
||||
{
|
||||
Span<byte> data = Context.PhysicalMemory.Read(address, DescriptorSize);
|
||||
|
||||
TextureDescriptor descriptor = MemoryMarshal.Cast<byte, TextureDescriptor>(data)[0];
|
||||
|
||||
// If the descriptors are the same, the texture is the same,
|
||||
// we don't need to remove as it was not modified. Just continue.
|
||||
if (texture.IsPerfectMatch(GetInfo(descriptor), TextureSearchFlags.Strict))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
texture.DecrementReferenceCount();
|
||||
|
||||
Items[id] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TextureInfo GetInfo(TextureDescriptor descriptor)
|
||||
{
|
||||
ulong address = Context.MemoryManager.Translate(descriptor.UnpackAddress());
|
||||
|
||||
int width = descriptor.UnpackWidth();
|
||||
int height = descriptor.UnpackHeight();
|
||||
int depthOrLayers = descriptor.UnpackDepth();
|
||||
int levels = descriptor.UnpackLevels();
|
||||
|
||||
TextureMsaaMode msaaMode = descriptor.UnpackTextureMsaaMode();
|
||||
|
||||
int samplesInX = msaaMode.SamplesInX();
|
||||
int samplesInY = msaaMode.SamplesInY();
|
||||
|
||||
int stride = descriptor.UnpackStride();
|
||||
|
||||
TextureDescriptorType descriptorType = descriptor.UnpackTextureDescriptorType();
|
||||
|
||||
bool isLinear = descriptorType == TextureDescriptorType.Linear;
|
||||
|
||||
Target target = descriptor.UnpackTextureTarget().Convert((samplesInX | samplesInY) != 1);
|
||||
|
||||
uint format = descriptor.UnpackFormat();
|
||||
bool srgb = descriptor.UnpackSrgb();
|
||||
|
||||
if (!FormatTable.TryGetTextureFormat(format, srgb, out FormatInfo formatInfo))
|
||||
{
|
||||
// TODO: Warning.
|
||||
|
||||
formatInfo = FormatInfo.Default;
|
||||
}
|
||||
|
||||
int gobBlocksInY = descriptor.UnpackGobBlocksInY();
|
||||
int gobBlocksInZ = descriptor.UnpackGobBlocksInZ();
|
||||
|
||||
int gobBlocksInTileX = descriptor.UnpackGobBlocksInTileX();
|
||||
|
||||
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
|
||||
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
|
||||
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
|
||||
SwizzleComponent swizzleA = descriptor.UnpackSwizzleA().Convert();
|
||||
|
||||
DepthStencilMode depthStencilMode = GetDepthStencilMode(
|
||||
formatInfo.Format,
|
||||
swizzleR,
|
||||
swizzleG,
|
||||
swizzleB,
|
||||
swizzleA);
|
||||
|
||||
return new TextureInfo(
|
||||
address,
|
||||
width,
|
||||
height,
|
||||
depthOrLayers,
|
||||
levels,
|
||||
samplesInX,
|
||||
samplesInY,
|
||||
stride,
|
||||
isLinear,
|
||||
gobBlocksInY,
|
||||
gobBlocksInZ,
|
||||
gobBlocksInTileX,
|
||||
target,
|
||||
formatInfo,
|
||||
depthStencilMode,
|
||||
swizzleR,
|
||||
swizzleG,
|
||||
swizzleB,
|
||||
swizzleA);
|
||||
}
|
||||
|
||||
private static DepthStencilMode GetDepthStencilMode(Format format, params SwizzleComponent[] components)
|
||||
{
|
||||
// R = Depth, G = Stencil.
|
||||
// On 24-bits depth formats, this is inverted (Stencil is R etc).
|
||||
// NVN setup:
|
||||
// For depth, A is set to 1.0f, the other components are set to Depth.
|
||||
// For stencil, all components are set to Stencil.
|
||||
SwizzleComponent component = components[0];
|
||||
|
||||
for (int index = 1; index < 4 && !IsRG(component); index++)
|
||||
{
|
||||
component = components[index];
|
||||
}
|
||||
|
||||
if (!IsRG(component))
|
||||
{
|
||||
return DepthStencilMode.Depth;
|
||||
}
|
||||
|
||||
if (format == Format.D24X8Unorm || format == Format.D24UnormS8Uint)
|
||||
{
|
||||
return component == SwizzleComponent.Red
|
||||
? DepthStencilMode.Stencil
|
||||
: DepthStencilMode.Depth;
|
||||
}
|
||||
else
|
||||
{
|
||||
return component == SwizzleComponent.Red
|
||||
? DepthStencilMode.Depth
|
||||
: DepthStencilMode.Stencil;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRG(SwizzleComponent component)
|
||||
{
|
||||
return component == SwizzleComponent.Red ||
|
||||
component == SwizzleComponent.Green;
|
||||
}
|
||||
|
||||
protected override void Delete(Texture item)
|
||||
{
|
||||
item?.DecrementReferenceCount();
|
||||
}
|
||||
}
|
||||
}
|
73
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal file
73
Ryujinx.Graphics.Gpu/Image/TexturePoolCache.cs
Normal file
|
@ -0,0 +1,73 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
class TexturePoolCache
|
||||
{
|
||||
private const int MaxCapacity = 4;
|
||||
|
||||
private GpuContext _context;
|
||||
private TextureManager _textureManager;
|
||||
|
||||
private LinkedList<TexturePool> _pools;
|
||||
|
||||
public TexturePoolCache(GpuContext context, TextureManager textureManager)
|
||||
{
|
||||
_context = context;
|
||||
_textureManager = textureManager;
|
||||
|
||||
_pools = new LinkedList<TexturePool>();
|
||||
}
|
||||
|
||||
public TexturePool FindOrCreate(ulong address, int maximumId)
|
||||
{
|
||||
TexturePool pool;
|
||||
|
||||
// First we try to find the pool.
|
||||
for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next)
|
||||
{
|
||||
pool = node.Value;
|
||||
|
||||
if (pool.Address == address)
|
||||
{
|
||||
if (pool.CacheNode != _pools.Last)
|
||||
{
|
||||
_pools.Remove(pool.CacheNode);
|
||||
|
||||
pool.CacheNode = _pools.AddLast(pool);
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
}
|
||||
|
||||
// If not found, create a new one.
|
||||
pool = new TexturePool(_context, _textureManager, address, maximumId);
|
||||
|
||||
pool.CacheNode = _pools.AddLast(pool);
|
||||
|
||||
if (_pools.Count > MaxCapacity)
|
||||
{
|
||||
TexturePool oldestPool = _pools.First.Value;
|
||||
|
||||
_pools.RemoveFirst();
|
||||
|
||||
oldestPool.Dispose();
|
||||
|
||||
oldestPool.CacheNode = null;
|
||||
}
|
||||
|
||||
return pool;
|
||||
}
|
||||
|
||||
public void InvalidateRange(ulong address, ulong size)
|
||||
{
|
||||
for (LinkedListNode<TexturePool> node = _pools.First; node != null; node = node.Next)
|
||||
{
|
||||
TexturePool pool = node.Value;
|
||||
|
||||
pool.InvalidateRange(address, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal file
13
Ryujinx.Graphics.Gpu/Image/TextureSearchFlags.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
[Flags]
|
||||
enum TextureSearchFlags
|
||||
{
|
||||
None = 0,
|
||||
IgnoreMs = 1 << 0,
|
||||
Strict = 1 << 1 | Sampler,
|
||||
Sampler = 1 << 2
|
||||
}
|
||||
}
|
49
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal file
49
Ryujinx.Graphics.Gpu/Image/TextureTarget.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
using Ryujinx.Graphics.GAL.Texture;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Image
|
||||
{
|
||||
enum TextureTarget
|
||||
{
|
||||
Texture1D,
|
||||
Texture2D,
|
||||
Texture3D,
|
||||
Cubemap,
|
||||
Texture1DArray,
|
||||
Texture2DArray,
|
||||
TextureBuffer,
|
||||
Texture2DLinear,
|
||||
CubemapArray
|
||||
}
|
||||
|
||||
static class TextureTargetConverter
|
||||
{
|
||||
public static Target Convert(this TextureTarget target, bool isMultisample)
|
||||
{
|
||||
if (isMultisample)
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case TextureTarget.Texture2D: return Target.Texture2DMultisample;
|
||||
case TextureTarget.Texture2DArray: return Target.Texture2DMultisampleArray;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (target)
|
||||
{
|
||||
case TextureTarget.Texture1D: return Target.Texture1D;
|
||||
case TextureTarget.Texture2D: return Target.Texture2D;
|
||||
case TextureTarget.Texture2DLinear: return Target.Texture2D;
|
||||
case TextureTarget.Texture3D: return Target.Texture3D;
|
||||
case TextureTarget.Texture1DArray: return Target.Texture1DArray;
|
||||
case TextureTarget.Texture2DArray: return Target.Texture2DArray;
|
||||
case TextureTarget.Cubemap: return Target.Cubemap;
|
||||
case TextureTarget.CubemapArray: return Target.CubemapArray;
|
||||
case TextureTarget.TextureBuffer: return Target.TextureBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
return Target.Texture1D;
|
||||
}
|
||||
}
|
||||
}
|
415
Ryujinx.Graphics.Gpu/MacroInterpreter.cs
Normal file
415
Ryujinx.Graphics.Gpu/MacroInterpreter.cs
Normal file
|
@ -0,0 +1,415 @@
|
|||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
class MacroInterpreter
|
||||
{
|
||||
private enum AssignmentOperation
|
||||
{
|
||||
IgnoreAndFetch = 0,
|
||||
Move = 1,
|
||||
MoveAndSetMaddr = 2,
|
||||
FetchAndSend = 3,
|
||||
MoveAndSend = 4,
|
||||
FetchAndSetMaddr = 5,
|
||||
MoveAndSetMaddrThenFetchAndSend = 6,
|
||||
MoveAndSetMaddrThenSendHigh = 7
|
||||
}
|
||||
|
||||
private enum AluOperation
|
||||
{
|
||||
AluReg = 0,
|
||||
AddImmediate = 1,
|
||||
BitfieldReplace = 2,
|
||||
BitfieldExtractLslImm = 3,
|
||||
BitfieldExtractLslReg = 4,
|
||||
ReadImmediate = 5
|
||||
}
|
||||
|
||||
private enum AluRegOperation
|
||||
{
|
||||
Add = 0,
|
||||
AddWithCarry = 1,
|
||||
Subtract = 2,
|
||||
SubtractWithBorrow = 3,
|
||||
BitwiseExclusiveOr = 8,
|
||||
BitwiseOr = 9,
|
||||
BitwiseAnd = 10,
|
||||
BitwiseAndNot = 11,
|
||||
BitwiseNotAnd = 12
|
||||
}
|
||||
|
||||
private GpuContext _context;
|
||||
|
||||
private NvGpuFifo _pFifo;
|
||||
|
||||
public Queue<int> Fifo { get; private set; }
|
||||
|
||||
private int[] _gprs;
|
||||
|
||||
private int _methAddr;
|
||||
private int _methIncr;
|
||||
|
||||
private bool _carry;
|
||||
|
||||
private int _opCode;
|
||||
|
||||
private int _pipeOp;
|
||||
|
||||
private int _pc;
|
||||
|
||||
public MacroInterpreter(GpuContext context, NvGpuFifo pFifo)
|
||||
{
|
||||
_context = context;
|
||||
_pFifo = pFifo;
|
||||
|
||||
Fifo = new Queue<int>();
|
||||
|
||||
_gprs = new int[8];
|
||||
}
|
||||
|
||||
public void Execute(int[] mme, int position, int param)
|
||||
{
|
||||
Reset();
|
||||
|
||||
_gprs[1] = param;
|
||||
|
||||
_pc = position;
|
||||
|
||||
FetchOpCode(mme);
|
||||
|
||||
while (Step(mme));
|
||||
|
||||
// Due to the delay slot, we still need to execute
|
||||
// one more instruction before we actually exit.
|
||||
Step(mme);
|
||||
}
|
||||
|
||||
private void Reset()
|
||||
{
|
||||
for (int index = 0; index < _gprs.Length; index++)
|
||||
{
|
||||
_gprs[index] = 0;
|
||||
}
|
||||
|
||||
_methAddr = 0;
|
||||
_methIncr = 0;
|
||||
|
||||
_carry = false;
|
||||
}
|
||||
|
||||
private bool Step(int[] mme)
|
||||
{
|
||||
int baseAddr = _pc - 1;
|
||||
|
||||
FetchOpCode(mme);
|
||||
|
||||
if ((_opCode & 7) < 7)
|
||||
{
|
||||
// Operation produces a value.
|
||||
AssignmentOperation asgOp = (AssignmentOperation)((_opCode >> 4) & 7);
|
||||
|
||||
int result = GetAluResult();
|
||||
|
||||
switch (asgOp)
|
||||
{
|
||||
// Fetch parameter and ignore result.
|
||||
case AssignmentOperation.IgnoreAndFetch:
|
||||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Move result.
|
||||
case AssignmentOperation.Move:
|
||||
{
|
||||
SetDstGpr(result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Move result and use as Method Address.
|
||||
case AssignmentOperation.MoveAndSetMaddr:
|
||||
{
|
||||
SetDstGpr(result);
|
||||
|
||||
SetMethAddr(result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Fetch parameter and send result.
|
||||
case AssignmentOperation.FetchAndSend:
|
||||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
Send(result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Move and send result.
|
||||
case AssignmentOperation.MoveAndSend:
|
||||
{
|
||||
SetDstGpr(result);
|
||||
|
||||
Send(result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Fetch parameter and use result as Method Address.
|
||||
case AssignmentOperation.FetchAndSetMaddr:
|
||||
{
|
||||
SetDstGpr(FetchParam());
|
||||
|
||||
SetMethAddr(result);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Move result and use as Method Address, then fetch and send parameter.
|
||||
case AssignmentOperation.MoveAndSetMaddrThenFetchAndSend:
|
||||
{
|
||||
SetDstGpr(result);
|
||||
|
||||
SetMethAddr(result);
|
||||
|
||||
Send(FetchParam());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Move result and use as Method Address, then send bits 17:12 of result.
|
||||
case AssignmentOperation.MoveAndSetMaddrThenSendHigh:
|
||||
{
|
||||
SetDstGpr(result);
|
||||
|
||||
SetMethAddr(result);
|
||||
|
||||
Send((result >> 12) & 0x3f);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Branch.
|
||||
bool onNotZero = ((_opCode >> 4) & 1) != 0;
|
||||
|
||||
bool taken = onNotZero
|
||||
? GetGprA() != 0
|
||||
: GetGprA() == 0;
|
||||
|
||||
if (taken)
|
||||
{
|
||||
_pc = baseAddr + GetImm();
|
||||
|
||||
bool noDelays = (_opCode & 0x20) != 0;
|
||||
|
||||
if (noDelays)
|
||||
{
|
||||
FetchOpCode(mme);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool exit = (_opCode & 0x80) != 0;
|
||||
|
||||
return !exit;
|
||||
}
|
||||
|
||||
private void FetchOpCode(int[] mme)
|
||||
{
|
||||
_opCode = _pipeOp;
|
||||
_pipeOp = mme[_pc++];
|
||||
}
|
||||
|
||||
private int GetAluResult()
|
||||
{
|
||||
AluOperation op = (AluOperation)(_opCode & 7);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case AluOperation.AluReg:
|
||||
{
|
||||
AluRegOperation aluOp = (AluRegOperation)((_opCode >> 17) & 0x1f);
|
||||
|
||||
return GetAluResult(aluOp, GetGprA(), GetGprB());
|
||||
}
|
||||
|
||||
case AluOperation.AddImmediate:
|
||||
{
|
||||
return GetGprA() + GetImm();
|
||||
}
|
||||
|
||||
case AluOperation.BitfieldReplace:
|
||||
case AluOperation.BitfieldExtractLslImm:
|
||||
case AluOperation.BitfieldExtractLslReg:
|
||||
{
|
||||
int bfSrcBit = (_opCode >> 17) & 0x1f;
|
||||
int bfSize = (_opCode >> 22) & 0x1f;
|
||||
int bfDstBit = (_opCode >> 27) & 0x1f;
|
||||
|
||||
int bfMask = (1 << bfSize) - 1;
|
||||
|
||||
int dst = GetGprA();
|
||||
int src = GetGprB();
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case AluOperation.BitfieldReplace:
|
||||
{
|
||||
src = (int)((uint)src >> bfSrcBit) & bfMask;
|
||||
|
||||
dst &= ~(bfMask << bfDstBit);
|
||||
|
||||
dst |= src << bfDstBit;
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
case AluOperation.BitfieldExtractLslImm:
|
||||
{
|
||||
src = (int)((uint)src >> dst) & bfMask;
|
||||
|
||||
return src << bfDstBit;
|
||||
}
|
||||
|
||||
case AluOperation.BitfieldExtractLslReg:
|
||||
{
|
||||
src = (int)((uint)src >> bfSrcBit) & bfMask;
|
||||
|
||||
return src << dst;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case AluOperation.ReadImmediate:
|
||||
{
|
||||
return Read(GetGprA() + GetImm());
|
||||
}
|
||||
}
|
||||
|
||||
throw new ArgumentException(nameof(_opCode));
|
||||
}
|
||||
|
||||
private int GetAluResult(AluRegOperation aluOp, int a, int b)
|
||||
{
|
||||
switch (aluOp)
|
||||
{
|
||||
case AluRegOperation.Add:
|
||||
{
|
||||
ulong result = (ulong)a + (ulong)b;
|
||||
|
||||
_carry = result > 0xffffffff;
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
case AluRegOperation.AddWithCarry:
|
||||
{
|
||||
ulong result = (ulong)a + (ulong)b + (_carry ? 1UL : 0UL);
|
||||
|
||||
_carry = result > 0xffffffff;
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
case AluRegOperation.Subtract:
|
||||
{
|
||||
ulong result = (ulong)a - (ulong)b;
|
||||
|
||||
_carry = result < 0x100000000;
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
case AluRegOperation.SubtractWithBorrow:
|
||||
{
|
||||
ulong result = (ulong)a - (ulong)b - (_carry ? 0UL : 1UL);
|
||||
|
||||
_carry = result < 0x100000000;
|
||||
|
||||
return (int)result;
|
||||
}
|
||||
|
||||
case AluRegOperation.BitwiseExclusiveOr: return a ^ b;
|
||||
case AluRegOperation.BitwiseOr: return a | b;
|
||||
case AluRegOperation.BitwiseAnd: return a & b;
|
||||
case AluRegOperation.BitwiseAndNot: return a & ~b;
|
||||
case AluRegOperation.BitwiseNotAnd: return ~(a & b);
|
||||
}
|
||||
|
||||
throw new ArgumentOutOfRangeException(nameof(aluOp));
|
||||
}
|
||||
|
||||
private int GetImm()
|
||||
{
|
||||
// Note: The immediate is signed, the sign-extension is intended here.
|
||||
return _opCode >> 14;
|
||||
}
|
||||
|
||||
private void SetMethAddr(int value)
|
||||
{
|
||||
_methAddr = (value >> 0) & 0xfff;
|
||||
_methIncr = (value >> 12) & 0x3f;
|
||||
}
|
||||
|
||||
private void SetDstGpr(int value)
|
||||
{
|
||||
_gprs[(_opCode >> 8) & 7] = value;
|
||||
}
|
||||
|
||||
private int GetGprA()
|
||||
{
|
||||
return GetGprValue((_opCode >> 11) & 7);
|
||||
}
|
||||
|
||||
private int GetGprB()
|
||||
{
|
||||
return GetGprValue((_opCode >> 14) & 7);
|
||||
}
|
||||
|
||||
private int GetGprValue(int index)
|
||||
{
|
||||
return index != 0 ? _gprs[index] : 0;
|
||||
}
|
||||
|
||||
private int FetchParam()
|
||||
{
|
||||
int value;
|
||||
|
||||
if (!Fifo.TryDequeue(out value))
|
||||
{
|
||||
Logger.PrintWarning(LogClass.Gpu, "Macro attempted to fetch an inexistent argument.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private int Read(int reg)
|
||||
{
|
||||
return _context.State.Read(reg);
|
||||
}
|
||||
|
||||
private void Send(int value)
|
||||
{
|
||||
MethodParams meth = new MethodParams(_methAddr, value);
|
||||
|
||||
_context.State.CallMethod(meth);
|
||||
|
||||
_methAddr += _methIncr;
|
||||
}
|
||||
}
|
||||
}
|
99
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
99
Ryujinx.Graphics.Gpu/Memory/Buffer.cs
Normal file
|
@ -0,0 +1,99 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
class Buffer : IRange<Buffer>, IDisposable
|
||||
{
|
||||
private GpuContext _context;
|
||||
|
||||
private IBuffer _buffer;
|
||||
|
||||
public ulong Address { get; }
|
||||
public ulong Size { get; }
|
||||
|
||||
public ulong EndAddress => Address + Size;
|
||||
|
||||
private int[] _sequenceNumbers;
|
||||
|
||||
public Buffer(GpuContext context, ulong address, ulong size)
|
||||
{
|
||||
_context = context;
|
||||
Address = address;
|
||||
Size = size;
|
||||
|
||||
_buffer = context.Renderer.CreateBuffer((int)size);
|
||||
|
||||
_sequenceNumbers = new int[size / MemoryManager.PageSize];
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
public BufferRange GetRange(ulong address, ulong size)
|
||||
{
|
||||
int offset = (int)(address - Address);
|
||||
|
||||
return new BufferRange(_buffer, offset, (int)size);
|
||||
}
|
||||
|
||||
public bool OverlapsWith(ulong address, ulong size)
|
||||
{
|
||||
return Address < address + size && address < EndAddress;
|
||||
}
|
||||
|
||||
public void SynchronizeMemory(ulong address, ulong size)
|
||||
{
|
||||
int currentSequenceNumber = _context.SequenceNumber;
|
||||
|
||||
bool needsSync = false;
|
||||
|
||||
ulong buffOffset = address - Address;
|
||||
|
||||
ulong buffEndOffset = (buffOffset + size + MemoryManager.PageMask) & ~MemoryManager.PageMask;
|
||||
|
||||
int startIndex = (int)(buffOffset / MemoryManager.PageSize);
|
||||
int endIndex = (int)(buffEndOffset / MemoryManager.PageSize);
|
||||
|
||||
for (int index = startIndex; index < endIndex; index++)
|
||||
{
|
||||
if (_sequenceNumbers[index] != currentSequenceNumber)
|
||||
{
|
||||
_sequenceNumbers[index] = currentSequenceNumber;
|
||||
|
||||
needsSync = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsSync)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
(ulong, ulong)[] modifiedRanges = _context.PhysicalMemory.GetModifiedRanges(address, size);
|
||||
|
||||
for (int index = 0; index < modifiedRanges.Length; index++)
|
||||
{
|
||||
(ulong mAddress, ulong mSize) = modifiedRanges[index];
|
||||
|
||||
int offset = (int)(mAddress - Address);
|
||||
|
||||
_buffer.SetData(offset, _context.PhysicalMemory.Read(mAddress, mSize));
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(Buffer destination, int dstOffset)
|
||||
{
|
||||
_buffer.CopyTo(destination._buffer, 0, dstOffset, (int)Size);
|
||||
}
|
||||
|
||||
public void Invalidate()
|
||||
{
|
||||
_buffer.SetData(0, _context.PhysicalMemory.Read(Address, Size));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_buffer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
8
Ryujinx.Graphics.Gpu/Memory/BufferBounds.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
struct BufferBounds
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong Size;
|
||||
}
|
||||
}
|
530
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
Normal file
530
Ryujinx.Graphics.Gpu/Memory/BufferManager.cs
Normal file
|
@ -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<Buffer> _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<Buffer>();
|
||||
|
||||
_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<byte> data = _context.PhysicalMemory.Read(bounds.Address + 0x310, 0x100);
|
||||
|
||||
Span<int> words = MemoryMarshal.Cast<byte, int>(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<byte> data = _context.PhysicalMemory.Read(bounds.Address + 0x110, 0x100);
|
||||
|
||||
Span<int> words = MemoryMarshal.Cast<byte, int>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs
Normal file
15
Ryujinx.Graphics.Gpu/Memory/IPhysicalMemory.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
public interface IPhysicalMemory
|
||||
{
|
||||
int GetPageSize();
|
||||
|
||||
Span<byte> Read(ulong address, ulong size);
|
||||
|
||||
void Write(ulong address, Span<byte> data);
|
||||
|
||||
(ulong, ulong)[] GetModifiedRanges(ulong address, ulong size);
|
||||
}
|
||||
}
|
10
Ryujinx.Graphics.Gpu/Memory/IRange.cs
Normal file
10
Ryujinx.Graphics.Gpu/Memory/IRange.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
interface IRange<T>
|
||||
{
|
||||
ulong Address { get; }
|
||||
ulong Size { get; }
|
||||
|
||||
bool OverlapsWith(ulong address, ulong size);
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs
Normal file
12
Ryujinx.Graphics.Gpu/Memory/IndexBuffer.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
struct IndexBuffer
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong Size;
|
||||
|
||||
public IndexType Type;
|
||||
}
|
||||
}
|
54
Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs
Normal file
54
Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs
Normal file
|
@ -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<byte> 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<T>(ulong gpuVa) where T : struct
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
ulong size = (uint)Marshal.SizeOf<T>();
|
||||
|
||||
return MemoryMarshal.Cast<byte, T>(_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<byte> data)
|
||||
{
|
||||
ulong processVa = _context.MemoryManager.Translate(gpuVa);
|
||||
|
||||
_context.PhysicalMemory.Write(processVa, data);
|
||||
}
|
||||
}
|
||||
}
|
265
Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
Normal file
265
Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
208
Ryujinx.Graphics.Gpu/Memory/RangeList.cs
Normal file
208
Ryujinx.Graphics.Gpu/Memory/RangeList.cs
Normal file
|
@ -0,0 +1,208 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
class RangeList<T> where T : IRange<T>
|
||||
{
|
||||
private List<T> _items;
|
||||
|
||||
public RangeList()
|
||||
{
|
||||
_items = new List<T>();
|
||||
}
|
||||
|
||||
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<T> overlapsList = new List<T>();
|
||||
|
||||
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<T> overlapsList = new List<T>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs
Normal file
10
Ryujinx.Graphics.Gpu/Memory/VertexBuffer.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Graphics.Gpu.Memory
|
||||
{
|
||||
struct VertexBuffer
|
||||
{
|
||||
public ulong Address;
|
||||
public ulong Size;
|
||||
public int Stride;
|
||||
public int Divisor;
|
||||
}
|
||||
}
|
24
Ryujinx.Graphics.Gpu/MethodParams.cs
Normal file
24
Ryujinx.Graphics.Gpu/MethodParams.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
namespace Ryujinx.Graphics
|
||||
{
|
||||
struct MethodParams
|
||||
{
|
||||
public int Method { get; private set; }
|
||||
public int Argument { get; private set; }
|
||||
public int SubChannel { get; private set; }
|
||||
public int MethodCount { get; private set; }
|
||||
|
||||
public bool IsLastCall => MethodCount <= 1;
|
||||
|
||||
public MethodParams(
|
||||
int method,
|
||||
int argument,
|
||||
int subChannel = 0,
|
||||
int methodCount = 0)
|
||||
{
|
||||
Method = method;
|
||||
Argument = argument;
|
||||
SubChannel = subChannel;
|
||||
MethodCount = methodCount;
|
||||
}
|
||||
}
|
||||
}
|
150
Ryujinx.Graphics.Gpu/NvGpuFifo.cs
Normal file
150
Ryujinx.Graphics.Gpu/NvGpuFifo.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs
Normal file
12
Ryujinx.Graphics.Gpu/NvGpuFifoMeth.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
enum NvGpuFifoMeth
|
||||
{
|
||||
BindChannel = 0,
|
||||
WaitForIdle = 0x44,
|
||||
SetMacroUploadAddress = 0x45,
|
||||
SendMacroCodeData = 0x46,
|
||||
SetMacroBindingIndex = 0x47,
|
||||
BindMacro = 0x48
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj
Normal file
14
Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj
Normal file
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Texture\Ryujinx.Graphics.Texture.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Shader\Ryujinx.Graphics.Shader.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
15
Ryujinx.Graphics.Gpu/State/BlendState.cs
Normal file
15
Ryujinx.Graphics.Gpu/State/BlendState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.Gpu/State/Bool.cs
Normal file
17
Ryujinx.Graphics.Gpu/State/Bool.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
10
Ryujinx.Graphics.Gpu/State/ClearColors.cs
Normal file
10
Ryujinx.Graphics.Gpu/State/ClearColors.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct ClearColors
|
||||
{
|
||||
public float Red;
|
||||
public float Green;
|
||||
public float Blue;
|
||||
public float Alpha;
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Gpu/State/Condition.cs
Normal file
11
Ryujinx.Graphics.Gpu/State/Condition.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
enum Condition
|
||||
{
|
||||
Never,
|
||||
Always,
|
||||
ResultNonZero,
|
||||
Equal,
|
||||
NotEqual
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Gpu/State/ConditionState.cs
Normal file
8
Ryujinx.Graphics.Gpu/State/ConditionState.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct ConditionState
|
||||
{
|
||||
public GpuVa Address;
|
||||
public Condition Condition;
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.Gpu/State/CopyBufferParams.cs
Normal file
12
Ryujinx.Graphics.Gpu/State/CopyBufferParams.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
22
Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs
Normal file
22
Ryujinx.Graphics.Gpu/State/CopyBufferSwizzle.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs
Normal file
13
Ryujinx.Graphics.Gpu/State/CopyBufferTexture.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics.Gpu/State/CopyRegion.cs
Normal file
14
Ryujinx.Graphics.Gpu/State/CopyRegion.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
15
Ryujinx.Graphics.Gpu/State/CopyTexture.cs
Normal file
15
Ryujinx.Graphics.Gpu/State/CopyTexture.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
12
Ryujinx.Graphics.Gpu/State/CopyTextureControl.cs
Normal file
12
Ryujinx.Graphics.Gpu/State/CopyTextureControl.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct CopyTextureControl
|
||||
{
|
||||
public uint Packed;
|
||||
|
||||
public bool UnpackLinearFilter()
|
||||
{
|
||||
return (Packed & (1u << 4)) != 0;
|
||||
}
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.Gpu/State/DepthBiasState.cs
Normal file
9
Ryujinx.Graphics.Gpu/State/DepthBiasState.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct DepthBiasState
|
||||
{
|
||||
public Bool PointEnable;
|
||||
public Bool LineEnable;
|
||||
public Bool FillEnable;
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Gpu/State/FaceState.cs
Normal file
11
Ryujinx.Graphics.Gpu/State/FaceState.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using Ryujinx.Graphics.GAL;
|
||||
|
||||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct FaceState
|
||||
{
|
||||
public Bool CullEnable;
|
||||
public FrontFace FrontFace;
|
||||
public Face CullFace;
|
||||
}
|
||||
}
|
425
Ryujinx.Graphics.Gpu/State/GpuState.cs
Normal file
425
Ryujinx.Graphics.Gpu/State/GpuState.cs
Normal file
|
@ -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<CopyTexture>(MethodOffset.CopyDstTexture);
|
||||
}
|
||||
|
||||
public CopyTexture GetCopySrcTexture()
|
||||
{
|
||||
return Get<CopyTexture>(MethodOffset.CopySrcTexture);
|
||||
}
|
||||
|
||||
public RtColorState GetRtColorState(int index)
|
||||
{
|
||||
return Get<RtColorState>(MethodOffset.RtColorState + 16 * index);
|
||||
}
|
||||
|
||||
public CopyTextureControl GetCopyTextureControl()
|
||||
{
|
||||
return Get<CopyTextureControl>(MethodOffset.CopyTextureControl);
|
||||
}
|
||||
|
||||
public CopyRegion GetCopyRegion()
|
||||
{
|
||||
return Get<CopyRegion>(MethodOffset.CopyRegion);
|
||||
}
|
||||
|
||||
public ViewportTransform GetViewportTransform(int index)
|
||||
{
|
||||
return Get<ViewportTransform>(MethodOffset.ViewportTransform + 8 * index);
|
||||
}
|
||||
|
||||
public ViewportExtents GetViewportExtents(int index)
|
||||
{
|
||||
return Get<ViewportExtents>(MethodOffset.ViewportExtents + 4 * index);
|
||||
}
|
||||
|
||||
public VertexBufferDrawState GetVertexBufferDrawState()
|
||||
{
|
||||
return Get<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState);
|
||||
}
|
||||
|
||||
public ClearColors GetClearColors()
|
||||
{
|
||||
return Get<ClearColors>(MethodOffset.ClearColors);
|
||||
}
|
||||
|
||||
public float GetClearDepthValue()
|
||||
{
|
||||
return Get<float>(MethodOffset.ClearDepthValue);
|
||||
}
|
||||
|
||||
public int GetClearStencilValue()
|
||||
{
|
||||
return _backingMemory[(int)MethodOffset.ClearStencilValue];
|
||||
}
|
||||
|
||||
public StencilBackMasks GetStencilBackMasks()
|
||||
{
|
||||
return Get<StencilBackMasks>(MethodOffset.StencilBackMasks);
|
||||
}
|
||||
|
||||
public RtDepthStencilState GetRtDepthStencilState()
|
||||
{
|
||||
return Get<RtDepthStencilState>(MethodOffset.RtDepthStencilState);
|
||||
}
|
||||
|
||||
public VertexAttribState GetVertexAttribState(int index)
|
||||
{
|
||||
return Get<VertexAttribState>(MethodOffset.VertexAttribState + index);
|
||||
}
|
||||
|
||||
public Size3D GetRtDepthStencilSize()
|
||||
{
|
||||
return Get<Size3D>(MethodOffset.RtDepthStencilSize);
|
||||
}
|
||||
|
||||
public Bool GetDepthTestEnable()
|
||||
{
|
||||
return Get<Bool>(MethodOffset.DepthTestEnable);
|
||||
}
|
||||
|
||||
public CompareOp GetDepthTestFunc()
|
||||
{
|
||||
return Get<CompareOp>(MethodOffset.DepthTestFunc);
|
||||
}
|
||||
|
||||
public Bool GetDepthWriteEnable()
|
||||
{
|
||||
return Get<Bool>(MethodOffset.DepthWriteEnable);
|
||||
}
|
||||
|
||||
public Bool GetBlendEnable(int index)
|
||||
{
|
||||
return Get<Bool>(MethodOffset.BlendEnable + index);
|
||||
}
|
||||
|
||||
public StencilTestState GetStencilTestState()
|
||||
{
|
||||
return Get<StencilTestState>(MethodOffset.StencilTestState);
|
||||
}
|
||||
|
||||
public int GetBaseVertex()
|
||||
{
|
||||
return _backingMemory[(int)MethodOffset.FirstVertex];
|
||||
}
|
||||
|
||||
public int GetBaseInstance()
|
||||
{
|
||||
return _backingMemory[(int)MethodOffset.FirstInstance];
|
||||
}
|
||||
|
||||
public PoolState GetSamplerPoolState()
|
||||
{
|
||||
return Get<PoolState>(MethodOffset.SamplerPoolState);
|
||||
}
|
||||
|
||||
public PoolState GetTexturePoolState()
|
||||
{
|
||||
return Get<PoolState>(MethodOffset.TexturePoolState);
|
||||
}
|
||||
|
||||
public StencilBackTestState GetStencilBackTestState()
|
||||
{
|
||||
return Get<StencilBackTestState>(MethodOffset.StencilBackTestState);
|
||||
}
|
||||
|
||||
public TextureMsaaMode GetRtMsaaMode()
|
||||
{
|
||||
return Get<TextureMsaaMode>(MethodOffset.RtMsaaMode);
|
||||
}
|
||||
|
||||
public GpuVa GetShaderBaseAddress()
|
||||
{
|
||||
return Get<GpuVa>(MethodOffset.ShaderBaseAddress);
|
||||
}
|
||||
|
||||
public PrimitiveRestartState GetPrimitiveRestartState()
|
||||
{
|
||||
return Get<PrimitiveRestartState>(MethodOffset.PrimitiveRestartState);
|
||||
}
|
||||
|
||||
public IndexBufferState GetIndexBufferState()
|
||||
{
|
||||
return Get<IndexBufferState>(MethodOffset.IndexBufferState);
|
||||
}
|
||||
|
||||
public FaceState GetFaceState()
|
||||
{
|
||||
return Get<FaceState>(MethodOffset.FaceState);
|
||||
}
|
||||
|
||||
public ReportState GetReportState()
|
||||
{
|
||||
return Get<ReportState>(MethodOffset.ReportState);
|
||||
}
|
||||
|
||||
public VertexBufferState GetVertexBufferState(int index)
|
||||
{
|
||||
return Get<VertexBufferState>(MethodOffset.VertexBufferState + 4 * index);
|
||||
}
|
||||
|
||||
public BlendState GetBlendState(int index)
|
||||
{
|
||||
return Get<BlendState>(MethodOffset.BlendState + 8 * index);
|
||||
}
|
||||
|
||||
public GpuVa GetVertexBufferEndAddress(int index)
|
||||
{
|
||||
return Get<GpuVa>(MethodOffset.VertexBufferEndAddress + 2 * index);
|
||||
}
|
||||
|
||||
public ShaderState GetShaderState(int index)
|
||||
{
|
||||
return Get<ShaderState>(MethodOffset.ShaderState + 16 * index);
|
||||
}
|
||||
|
||||
public UniformBufferState GetUniformBufferState()
|
||||
{
|
||||
return Get<UniformBufferState>(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<VertexBufferDrawState>(MethodOffset.VertexBufferDrawState, StateWriteFlags.VertexBufferState);
|
||||
|
||||
SetWriteStateFlag<DepthBiasState>(MethodOffset.DepthBiasState, StateWriteFlags.DepthBiasState);
|
||||
|
||||
SetWriteStateFlag(MethodOffset.DepthBiasFactor, StateWriteFlags.DepthBiasState, 1);
|
||||
SetWriteStateFlag(MethodOffset.DepthBiasUnits, StateWriteFlags.DepthBiasState, 1);
|
||||
SetWriteStateFlag(MethodOffset.DepthBiasClamp, StateWriteFlags.DepthBiasState, 1);
|
||||
|
||||
SetWriteStateFlag<RtDepthStencilState>(MethodOffset.RtDepthStencilState, StateWriteFlags.RtDepthStencilState);
|
||||
SetWriteStateFlag<Size3D> (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<StencilBackMasks> (MethodOffset.StencilBackMasks, StateWriteFlags.StencilTestState);
|
||||
SetWriteStateFlag<StencilTestState> (MethodOffset.StencilTestState, StateWriteFlags.StencilTestState);
|
||||
SetWriteStateFlag<StencilBackTestState>(MethodOffset.StencilBackTestState, StateWriteFlags.StencilTestState);
|
||||
|
||||
SetWriteStateFlag<PoolState>(MethodOffset.SamplerPoolState, StateWriteFlags.SamplerPoolState);
|
||||
SetWriteStateFlag<PoolState>(MethodOffset.TexturePoolState, StateWriteFlags.TexturePoolState);
|
||||
|
||||
SetWriteStateFlag<ShaderState>(MethodOffset.ShaderBaseAddress, StateWriteFlags.ShaderState);
|
||||
|
||||
SetWriteStateFlag<PrimitiveRestartState>(MethodOffset.PrimitiveRestartState, StateWriteFlags.PrimitiveRestartState);
|
||||
|
||||
SetWriteStateFlag<IndexBufferState>(MethodOffset.IndexBufferState, StateWriteFlags.IndexBufferState);
|
||||
|
||||
SetWriteStateFlag<FaceState>(MethodOffset.FaceState, StateWriteFlags.FaceState);
|
||||
|
||||
SetWriteStateFlag<RtColorMask>(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<T>(MethodOffset offset, StateWriteFlags flag)
|
||||
{
|
||||
SetWriteStateFlag(offset, flag, Marshal.SizeOf<T>());
|
||||
}
|
||||
|
||||
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<T>(MethodOffset offset) where T : struct
|
||||
{
|
||||
return MemoryMarshal.Cast<int, T>(_backingMemory.AsSpan().Slice((int)offset))[0];
|
||||
}
|
||||
}
|
||||
}
|
18
Ryujinx.Graphics.Gpu/State/GpuVa.cs
Normal file
18
Ryujinx.Graphics.Gpu/State/GpuVa.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics.Gpu/State/IndexBufferState.cs
Normal file
13
Ryujinx.Graphics.Gpu/State/IndexBufferState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs
Normal file
17
Ryujinx.Graphics.Gpu/State/Inline2MemoryParams.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
32
Ryujinx.Graphics.Gpu/State/MemoryLayout.cs
Normal file
32
Ryujinx.Graphics.Gpu/State/MemoryLayout.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
62
Ryujinx.Graphics.Gpu/State/MethodOffset.cs
Normal file
62
Ryujinx.Graphics.Gpu/State/MethodOffset.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Gpu/State/PoolState.cs
Normal file
8
Ryujinx.Graphics.Gpu/State/PoolState.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct PoolState
|
||||
{
|
||||
public GpuVa Address;
|
||||
public int MaximumId;
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs
Normal file
8
Ryujinx.Graphics.Gpu/State/PrimitiveRestartState.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct PrimitiveRestartState
|
||||
{
|
||||
public bool Enable;
|
||||
public int Index;
|
||||
}
|
||||
}
|
50
Ryujinx.Graphics.Gpu/State/PrimitiveTopology.cs
Normal file
50
Ryujinx.Graphics.Gpu/State/PrimitiveTopology.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
25
Ryujinx.Graphics.Gpu/State/ReportCounterType.cs
Normal file
25
Ryujinx.Graphics.Gpu/State/ReportCounterType.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Gpu/State/ReportMode.cs
Normal file
8
Ryujinx.Graphics.Gpu/State/ReportMode.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
enum ReportMode
|
||||
{
|
||||
Semaphore = 0,
|
||||
Counter = 2
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.Gpu/State/ReportState.cs
Normal file
9
Ryujinx.Graphics.Gpu/State/ReportState.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct ReportState
|
||||
{
|
||||
public GpuVa Address;
|
||||
public int Payload;
|
||||
public uint Control;
|
||||
}
|
||||
}
|
21
Ryujinx.Graphics.Gpu/State/ResetCounterType.cs
Normal file
21
Ryujinx.Graphics.Gpu/State/ResetCounterType.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
27
Ryujinx.Graphics.Gpu/State/RtColorMask.cs
Normal file
27
Ryujinx.Graphics.Gpu/State/RtColorMask.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
13
Ryujinx.Graphics.Gpu/State/RtColorState.cs
Normal file
13
Ryujinx.Graphics.Gpu/State/RtColorState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
10
Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs
Normal file
10
Ryujinx.Graphics.Gpu/State/RtDepthStencilState.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct RtDepthStencilState
|
||||
{
|
||||
public GpuVa Address;
|
||||
public RtFormat Format;
|
||||
public MemoryLayout MemoryLayout;
|
||||
public int LayerSize;
|
||||
}
|
||||
}
|
137
Ryujinx.Graphics.Gpu/State/RtFormat.cs
Normal file
137
Ryujinx.Graphics.Gpu/State/RtFormat.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
19
Ryujinx.Graphics.Gpu/State/ShaderState.cs
Normal file
19
Ryujinx.Graphics.Gpu/State/ShaderState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
11
Ryujinx.Graphics.Gpu/State/ShaderType.cs
Normal file
11
Ryujinx.Graphics.Gpu/State/ShaderType.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
enum ShaderType
|
||||
{
|
||||
Vertex,
|
||||
TessellationControl,
|
||||
TessellationEvaluation,
|
||||
Geometry,
|
||||
Fragment
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.Gpu/State/Size3D.cs
Normal file
9
Ryujinx.Graphics.Gpu/State/Size3D.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct Size3D
|
||||
{
|
||||
public int Width;
|
||||
public int Height;
|
||||
public int Depth;
|
||||
}
|
||||
}
|
34
Ryujinx.Graphics.Gpu/State/StateWriteFlags.cs
Normal file
34
Ryujinx.Graphics.Gpu/State/StateWriteFlags.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs
Normal file
9
Ryujinx.Graphics.Gpu/State/StencilBackMasks.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct StencilBackMasks
|
||||
{
|
||||
public int FuncRef;
|
||||
public int Mask;
|
||||
public int FuncMask;
|
||||
}
|
||||
}
|
14
Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs
Normal file
14
Ryujinx.Graphics.Gpu/State/StencilBackTestState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
17
Ryujinx.Graphics.Gpu/State/StencilTestState.cs
Normal file
17
Ryujinx.Graphics.Gpu/State/StencilTestState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
9
Ryujinx.Graphics.Gpu/State/UniformBufferState.cs
Normal file
9
Ryujinx.Graphics.Gpu/State/UniformBufferState.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct UniformBufferState
|
||||
{
|
||||
public int Size;
|
||||
public GpuVa Address;
|
||||
public int Offset;
|
||||
}
|
||||
}
|
22
Ryujinx.Graphics.Gpu/State/VertexAttribState.cs
Normal file
22
Ryujinx.Graphics.Gpu/State/VertexAttribState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
8
Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs
Normal file
8
Ryujinx.Graphics.Gpu/State/VertexBufferDrawState.cs
Normal file
|
@ -0,0 +1,8 @@
|
|||
namespace Ryujinx.Graphics.Gpu.State
|
||||
{
|
||||
struct VertexBufferDrawState
|
||||
{
|
||||
public int First;
|
||||
public int Count;
|
||||
}
|
||||
}
|
19
Ryujinx.Graphics.Gpu/State/VertexBufferState.cs
Normal file
19
Ryujinx.Graphics.Gpu/State/VertexBufferState.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue