Initial work
This commit is contained in:
parent
f617fb542a
commit
1876b346fe
518 changed files with 15170 additions and 12486 deletions
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue