Initial work

This commit is contained in:
gdk 2019-10-13 03:02:07 -03:00 committed by Thog
parent f617fb542a
commit 1876b346fe
518 changed files with 15170 additions and 12486 deletions

View 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());
}
}
}

View 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;
}
}
}

View 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;
}
}
}

View 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];
}
}
}

View 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();
}
}
}
}

View 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);
}
}
}
}

View 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);
}
}
}
}

View 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;
}
}
}

View 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);
}
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}
}

View 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);
}
}
}
}

View 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();
}
}
}

View 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();
}
}
}

View 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);
}
}
}

View 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;
}
}
}
}

View 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;
}
}
}