New shader cache implementation (#3194)
* New shader cache implementation * Remove some debug code * Take transform feedback varying count into account * Create shader cache directory if it does not exist + fragment output map related fixes * Remove debug code * Only check texture descriptors if the constant buffer is bound * Also check CPU VA on GetSpanMapped * Remove more unused code and move cache related code * XML docs + remove more unused methods * Better codegen for TransformFeedbackDescriptor.AsSpan * Support migration from old cache format, remove more unused code Shader cache rebuild now also rewrites the shared toc and data files * Fix migration error with BRX shaders * Add a limit to the async translation queue Avoid async translation threads not being able to keep up and the queue growing very large * Re-create specialization state on recompile This might be required if a new version of the shader translator requires more or less state, or if there is a bug related to the GPU state access * Make shader cache more error resilient * Add some missing XML docs and move GpuAccessor docs to the interface/use inheritdoc * Address early PR feedback * Fix rebase * Remove IRenderer.CompileShader and IShader interface, replace with new ShaderSource struct passed to CreateProgram directly * Handle some missing exceptions * Make shader cache purge delete both old and new shader caches * Register textures on new specialization state * Translate and compile shaders in forward order (eliminates diffs due to different binding numbers) * Limit in-flight shader compilation to the maximum number of compilation threads * Replace ParallelDiskCacheLoader state changed event with a callback function * Better handling for invalid constant buffer 1 data length * Do not create the old cache directory structure if the old cache does not exist * Constant buffer use should be per-stage. This change will invalidate existing new caches (file format version was incremented) * Replace rectangle texture with just coordinate normalization * Skip incompatible shaders that are missing texture information, instead of crashing This is required if we, for example, support new texture instruction to the shader translator, and then they allow access to textures that were not accessed before. In this scenario, the old cache entry is no longer usable * Fix coordinates normalization on cubemap textures * Check if title ID is null before combining shader cache path * More robust constant buffer address validation on spec state * More robust constant buffer address validation on spec state (2) * Regenerate shader cache with one stream, rather than one per shader. * Only create shader cache directory during initialization * Logging improvements * Proper shader program disposal * PR feedback, and add a comment on serialized structs * XML docs for RegisterTexture Co-authored-by: riperiperi <rhy3756547@hotmail.com>
This commit is contained in:
parent
26a881176e
commit
43ebd7a9bb
81 changed files with 6421 additions and 2406 deletions
|
@ -2,6 +2,8 @@ namespace Ryujinx.Graphics.Shader
|
|||
{
|
||||
public struct BufferDescriptor
|
||||
{
|
||||
// New fields should be added to the end of the struct to keep disk shader cache compatibility.
|
||||
|
||||
public readonly int Binding;
|
||||
public readonly int Slot;
|
||||
public BufferUsageFlags Flags;
|
||||
|
|
|
@ -373,7 +373,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
|||
|
||||
for (int i = 0; i < cbOffsetsCount; i++)
|
||||
{
|
||||
uint targetOffset = config.GpuAccessor.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
||||
uint targetOffset = config.ConstantBuffer1Read(cbBaseOffset + i * 4);
|
||||
Block target = getBlock(baseOffset + targetOffset);
|
||||
target.Predecessors.Add(block);
|
||||
block.Successors.Add(target);
|
||||
|
|
|
@ -2,153 +2,341 @@
|
|||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
/// <summary>
|
||||
/// GPU state access interface.
|
||||
/// </summary>
|
||||
public interface IGpuAccessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Prints a log message.
|
||||
/// </summary>
|
||||
/// <param name="message">Message to print</param>
|
||||
void Log(string message)
|
||||
{
|
||||
// No default log output.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from the constant buffer 1.
|
||||
/// </summary>
|
||||
/// <param name="offset">Offset in bytes to read from</param>
|
||||
/// <returns>Value at the given offset</returns>
|
||||
uint ConstantBuffer1Read(int offset)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a span of the specified memory location, containing shader code.
|
||||
/// </summary>
|
||||
/// <param name="address">GPU virtual address of the data</param>
|
||||
/// <param name="minimumSize">Minimum size that the returned span may have</param>
|
||||
/// <returns>Span of the memory location</returns>
|
||||
ReadOnlySpan<ulong> GetCode(ulong address, int minimumSize);
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of a constant buffer.
|
||||
/// </summary>
|
||||
/// <param name="index">Constant buffer index</param>
|
||||
/// <returns>Binding number</returns>
|
||||
int QueryBindingConstantBuffer(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of a storage buffer.
|
||||
/// </summary>
|
||||
/// <param name="index">Storage buffer index</param>
|
||||
/// <returns>Binding number</returns>
|
||||
int QueryBindingStorageBuffer(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of a texture.
|
||||
/// </summary>
|
||||
/// <param name="index">Texture index</param>
|
||||
/// <returns>Binding number</returns>
|
||||
int QueryBindingTexture(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the binding number of an image.
|
||||
/// </summary>
|
||||
/// <param name="index">Image index</param>
|
||||
/// <returns>Binding number</returns>
|
||||
int QueryBindingImage(int index)
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries Local Size X for compute shaders.
|
||||
/// </summary>
|
||||
/// <returns>Local Size X</returns>
|
||||
int QueryComputeLocalSizeX()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries Local Size Y for compute shaders.
|
||||
/// </summary>
|
||||
/// <returns>Local Size Y</returns>
|
||||
int QueryComputeLocalSizeY()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries Local Size Z for compute shaders.
|
||||
/// </summary>
|
||||
/// <returns>Local Size Z</returns>
|
||||
int QueryComputeLocalSizeZ()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries Local Memory size in bytes for compute shaders.
|
||||
/// </summary>
|
||||
/// <returns>Local Memory size in bytes</returns>
|
||||
int QueryComputeLocalMemorySize()
|
||||
{
|
||||
return 0x1000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries Shared Memory size in bytes for compute shaders.
|
||||
/// </summary>
|
||||
/// <returns>Shared Memory size in bytes</returns>
|
||||
int QueryComputeSharedMemorySize()
|
||||
{
|
||||
return 0xc000;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries Constant Buffer usage information.
|
||||
/// </summary>
|
||||
/// <returns>A mask where each bit set indicates a bound constant buffer</returns>
|
||||
uint QueryConstantBufferUse()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host about the presence of the FrontFacing built-in variable bug.
|
||||
/// </summary>
|
||||
/// <returns>True if the bug is present on the host device used, false otherwise</returns>
|
||||
bool QueryHostHasFrontFacingBug()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host about the presence of the vector indexing bug.
|
||||
/// </summary>
|
||||
/// <returns>True if the bug is present on the host device used, false otherwise</returns>
|
||||
bool QueryHostHasVectorIndexingBug()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host storage buffer alignment required.
|
||||
/// </summary>
|
||||
/// <returns>Host storage buffer alignment in bytes</returns>
|
||||
int QueryHostStorageBufferOffsetAlignment()
|
||||
{
|
||||
return 16;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for texture formats with BGRA component order (such as BGRA8).
|
||||
/// </summary>
|
||||
/// <returns>True if BGRA formats are supported, false otherwise</returns>
|
||||
bool QueryHostSupportsBgraFormat()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for fragment shader ordering critical sections on the shader code.
|
||||
/// </summary>
|
||||
/// <returns>True if fragment shader interlock is supported, false otherwise</returns>
|
||||
bool QueryHostSupportsFragmentShaderInterlock()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for fragment shader ordering scoped critical sections on the shader code.
|
||||
/// </summary>
|
||||
/// <returns>True if fragment shader ordering is supported, false otherwise</returns>
|
||||
bool QueryHostSupportsFragmentShaderOrderingIntel()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for readable images without a explicit format declaration on the shader.
|
||||
/// </summary>
|
||||
/// <returns>True if formatted image load is supported, false otherwise</returns>
|
||||
bool QueryHostSupportsImageLoadFormatted()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU non-constant texture offset support.
|
||||
/// </summary>
|
||||
/// <returns>True if the GPU and driver supports non-constant texture offsets, false otherwise</returns>
|
||||
bool QueryHostSupportsNonConstantTextureOffset()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU shader ballot support.
|
||||
/// </summary>
|
||||
/// <returns>True if the GPU and driver supports shader ballot, false otherwise</returns>
|
||||
bool QueryHostSupportsShaderBallot()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU texture shadow LOD support.
|
||||
/// </summary>
|
||||
/// <returns>True if the GPU and driver supports texture shadow LOD, false otherwise</returns>
|
||||
bool QueryHostSupportsTextureShadowLod()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries sampler type information.
|
||||
/// </summary>
|
||||
/// <param name="handle">Texture handle</param>
|
||||
/// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
|
||||
/// <returns>The sampler type value for the given handle</returns>
|
||||
SamplerType QuerySamplerType(int handle, int cbufSlot = -1)
|
||||
{
|
||||
return SamplerType.Texture2D;
|
||||
}
|
||||
|
||||
bool QueryIsTextureRectangle(int handle, int cbufSlot = -1)
|
||||
/// <summary>
|
||||
/// Queries texture coordinate normalization information.
|
||||
/// </summary>
|
||||
/// <param name="handle">Texture handle</param>
|
||||
/// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
|
||||
/// <returns>True if the coordinates are normalized, false otherwise</returns>
|
||||
bool QueryTextureCoordNormalized(int handle, int cbufSlot = -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries current primitive topology for geometry shaders.
|
||||
/// </summary>
|
||||
/// <returns>Current primitive topology</returns>
|
||||
InputTopology QueryPrimitiveTopology()
|
||||
{
|
||||
return InputTopology.Points;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader primitive winding order.
|
||||
/// </summary>
|
||||
/// <returns>True if the primitive winding order is clockwise, false if counter-clockwise</returns>
|
||||
bool QueryTessCw()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader abstract patch type.
|
||||
/// </summary>
|
||||
/// <returns>Abstract patch type</returns>
|
||||
TessPatchType QueryTessPatchType()
|
||||
{
|
||||
return TessPatchType.Triangles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the tessellation evaluation shader spacing between tessellated vertices of the patch.
|
||||
/// </summary>
|
||||
/// <returns>Spacing between tessellated vertices of the patch</returns>
|
||||
TessSpacing QueryTessSpacing()
|
||||
{
|
||||
return TessSpacing.EqualSpacing;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries texture format information, for shaders using image load or store.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This only returns non-compressed color formats.
|
||||
/// If the format of the texture is a compressed, depth or unsupported format, then a default value is returned.
|
||||
/// </remarks>
|
||||
/// <param name="handle">Texture handle</param>
|
||||
/// <param name="cbufSlot">Constant buffer slot for the texture handle</param>
|
||||
/// <returns>Color format of the non-compressed texture</returns>
|
||||
TextureFormat QueryTextureFormat(int handle, int cbufSlot = -1)
|
||||
{
|
||||
return TextureFormat.R8G8B8A8Unorm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries transform feedback enable state.
|
||||
/// </summary>
|
||||
/// <returns>True if the shader uses transform feedback, false otherwise</returns>
|
||||
bool QueryTransformFeedbackEnabled()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the varying locations that should be written to the transform feedback buffer.
|
||||
/// </summary>
|
||||
/// <param name="bufferIndex">Index of the transform feedback buffer</param>
|
||||
/// <returns>Varying locations for the specified buffer</returns>
|
||||
ReadOnlySpan<byte> QueryTransformFeedbackVaryingLocations(int bufferIndex)
|
||||
{
|
||||
return ReadOnlySpan<byte>.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the stride (in bytes) of the per vertex data written into the transform feedback buffer.
|
||||
/// </summary>
|
||||
/// <param name="bufferIndex">Index of the transform feedback buffer</param>
|
||||
/// <returns>Stride for the specified buffer</returns>
|
||||
int QueryTransformFeedbackStride(int bufferIndex)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries if host state forces early depth testing.
|
||||
/// </summary>
|
||||
/// <returns>True if early depth testing is forced</returns>
|
||||
bool QueryEarlyZForce()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers a texture used by the shader.
|
||||
/// </summary>
|
||||
/// <param name="handle">Texture handle word offset</param>
|
||||
/// <param name="cbufSlot">Constant buffer slot where the texture handle is located</param>
|
||||
void RegisterTexture(int handle, int cbufSlot)
|
||||
{
|
||||
// Only useful when recording information for a disk shader cache.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
public class ShaderProgram
|
||||
{
|
||||
public ShaderStage Stage { get; }
|
||||
public ShaderProgramInfo Info { get; }
|
||||
public TargetLanguage Language { get; }
|
||||
|
||||
public string Code { get; private set; }
|
||||
public byte[] BinaryCode { get; }
|
||||
|
||||
private ShaderProgram(ShaderStage stage)
|
||||
private ShaderProgram(ShaderProgramInfo info, TargetLanguage language)
|
||||
{
|
||||
Stage = stage;
|
||||
Info = info;
|
||||
Language = language;
|
||||
}
|
||||
|
||||
public ShaderProgram(ShaderStage stage, string code) : this(stage)
|
||||
public ShaderProgram(ShaderProgramInfo info, TargetLanguage language, string code) : this(info, language)
|
||||
{
|
||||
Code = code;
|
||||
}
|
||||
|
||||
public ShaderProgram(ShaderStage stage, byte[] binaryCode) : this(stage)
|
||||
public ShaderProgram(ShaderProgramInfo info, TargetLanguage language, byte[] binaryCode) : this(info, language)
|
||||
{
|
||||
BinaryCode = binaryCode;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
public ReadOnlyCollection<TextureDescriptor> Textures { get; }
|
||||
public ReadOnlyCollection<TextureDescriptor> Images { get; }
|
||||
|
||||
public ShaderStage Stage { get; }
|
||||
public bool UsesInstanceId { get; }
|
||||
public bool UsesRtLayer { get; }
|
||||
public byte ClipDistancesWritten { get; }
|
||||
|
@ -20,6 +21,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
BufferDescriptor[] sBuffers,
|
||||
TextureDescriptor[] textures,
|
||||
TextureDescriptor[] images,
|
||||
ShaderStage stage,
|
||||
bool usesInstanceId,
|
||||
bool usesRtLayer,
|
||||
byte clipDistancesWritten,
|
||||
|
@ -30,6 +32,7 @@ namespace Ryujinx.Graphics.Shader
|
|||
Textures = Array.AsReadOnly(textures);
|
||||
Images = Array.AsReadOnly(images);
|
||||
|
||||
Stage = stage;
|
||||
UsesInstanceId = usesInstanceId;
|
||||
UsesRtLayer = usesRtLayer;
|
||||
ClipDistancesWritten = clipDistancesWritten;
|
||||
|
|
|
@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
for (int j = 0; j < locations.Length; j++)
|
||||
{
|
||||
byte location = locations[j];
|
||||
if (location < 0x80)
|
||||
if (location < 0xc0)
|
||||
{
|
||||
context.Info.TransformFeedbackOutputs[location] = new TransformFeedbackOutput(tfbIndex, j * 4, stride);
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||
{
|
||||
Functions = new List<StructuredFunction>();
|
||||
|
||||
TransformFeedbackOutputs = new TransformFeedbackOutput[0x80];
|
||||
TransformFeedbackOutputs = new TransformFeedbackOutput[0xc0];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ namespace Ryujinx.Graphics.Shader
|
|||
{
|
||||
public struct TextureDescriptor
|
||||
{
|
||||
// New fields should be added to the end of the struct to keep disk shader cache compatibility.
|
||||
|
||||
public readonly int Binding;
|
||||
|
||||
public readonly SamplerType Type;
|
||||
|
|
|
@ -164,9 +164,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||
|
||||
bool isRect = !isBindless && config.GpuAccessor.QueryIsTextureRectangle(texOp.Handle, texOp.CbufSlot);
|
||||
bool isCoordNormalized = !isBindless && config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
||||
|
||||
if (!(hasInvalidOffset || isRect))
|
||||
if (!hasInvalidOffset && isCoordNormalized)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
@ -263,7 +263,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
hasInvalidOffset &= !areAllOffsetsConstant;
|
||||
|
||||
if (!(hasInvalidOffset || isRect))
|
||||
if (!hasInvalidOffset && isCoordNormalized)
|
||||
{
|
||||
return node;
|
||||
}
|
||||
|
@ -300,15 +300,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return res;
|
||||
}
|
||||
|
||||
// Emulate texture rectangle by normalizing the coordinates on the shader.
|
||||
// When sampler*Rect is used, the coords are expected to the in the [0, W or H] range,
|
||||
// Emulate non-normalized coordinates by normalizing the coordinates on the shader.
|
||||
// Without normalization, the coordinates are expected to the in the [0, W or H] range,
|
||||
// and otherwise, it is expected to be in the [0, 1] range.
|
||||
// We normalize by dividing the coords by the texture size.
|
||||
if (isRect && !intCoords)
|
||||
if (!isCoordNormalized && !intCoords)
|
||||
{
|
||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
||||
|
||||
for (int index = 0; index < normCoordsCount; index++)
|
||||
{
|
||||
Operand coordSize = Local();
|
||||
|
||||
|
|
|
@ -41,9 +41,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public FeatureFlags UsedFeatures { get; private set; }
|
||||
|
||||
public HashSet<int> TextureHandlesForCache { get; }
|
||||
|
||||
private readonly TranslationCounts _counts;
|
||||
public int Cb1DataSize { get; private set; }
|
||||
|
||||
public bool NextUsesFixedFuncAttributes { get; private set; }
|
||||
public int UsedInputAttributes { get; private set; }
|
||||
|
@ -109,21 +107,22 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
private TextureDescriptor[] _cachedTextureDescriptors;
|
||||
private TextureDescriptor[] _cachedImageDescriptors;
|
||||
|
||||
public int FirstConstantBufferBinding { get; private set; }
|
||||
public int FirstStorageBufferBinding { get; private set; }
|
||||
private int _firstConstantBufferBinding;
|
||||
private int _firstStorageBufferBinding;
|
||||
|
||||
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options, TranslationCounts counts)
|
||||
public int FirstConstantBufferBinding => _firstConstantBufferBinding;
|
||||
public int FirstStorageBufferBinding => _firstStorageBufferBinding;
|
||||
|
||||
public ShaderConfig(IGpuAccessor gpuAccessor, TranslationOptions options)
|
||||
{
|
||||
Stage = ShaderStage.Compute;
|
||||
GpuAccessor = gpuAccessor;
|
||||
Options = options;
|
||||
_counts = counts;
|
||||
TextureHandlesForCache = new HashSet<int>();
|
||||
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
|
||||
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
|
||||
Stage = ShaderStage.Compute;
|
||||
GpuAccessor = gpuAccessor;
|
||||
Options = options;
|
||||
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
|
||||
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
|
||||
}
|
||||
|
||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options, TranslationCounts counts) : this(gpuAccessor, options, counts)
|
||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options)
|
||||
{
|
||||
Stage = header.Stage;
|
||||
GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough;
|
||||
|
@ -144,6 +143,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return BitOperations.PopCount((uint)OmapTargets) + 1;
|
||||
}
|
||||
|
||||
public uint ConstantBuffer1Read(int offset)
|
||||
{
|
||||
if (Cb1DataSize < offset + 4)
|
||||
{
|
||||
Cb1DataSize = offset + 4;
|
||||
}
|
||||
|
||||
return GpuAccessor.ConstantBuffer1Read(offset);
|
||||
}
|
||||
|
||||
public TextureFormat GetTextureFormat(int handle, int cbufSlot = -1)
|
||||
{
|
||||
// When the formatted load extension is supported, we don't need to
|
||||
|
@ -197,8 +206,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
ClipDistancesWritten |= other.ClipDistancesWritten;
|
||||
UsedFeatures |= other.UsedFeatures;
|
||||
|
||||
TextureHandlesForCache.UnionWith(other.TextureHandlesForCache);
|
||||
|
||||
UsedInputAttributes |= other.UsedInputAttributes;
|
||||
UsedOutputAttributes |= other.UsedOutputAttributes;
|
||||
_usedConstantBuffers |= other._usedConstantBuffers;
|
||||
|
@ -391,6 +398,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
bool intCoords = flags.HasFlag(TextureFlags.IntCoords) || inst == Instruction.TextureSize;
|
||||
SetUsedTextureOrImage(_usedTextures, cbufSlot, handle, type, TextureFormat.Unknown, intCoords, false, accurateType, coherent);
|
||||
}
|
||||
|
||||
GpuAccessor.RegisterTexture(handle, cbufSlot);
|
||||
}
|
||||
|
||||
private void SetUsedTextureOrImage(
|
||||
|
@ -485,13 +494,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
usedMask |= (int)GpuAccessor.QueryConstantBufferUse();
|
||||
}
|
||||
|
||||
FirstConstantBufferBinding = _counts.UniformBuffersCount;
|
||||
|
||||
return _cachedConstantBufferDescriptors = GetBufferDescriptors(
|
||||
usedMask,
|
||||
0,
|
||||
UsedFeatures.HasFlag(FeatureFlags.CbIndexing),
|
||||
_counts.IncrementUniformBuffersCount);
|
||||
out _firstConstantBufferBinding,
|
||||
GpuAccessor.QueryBindingConstantBuffer);
|
||||
}
|
||||
|
||||
public BufferDescriptor[] GetStorageBufferDescriptors()
|
||||
|
@ -501,21 +509,23 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return _cachedStorageBufferDescriptors;
|
||||
}
|
||||
|
||||
FirstStorageBufferBinding = _counts.StorageBuffersCount;
|
||||
|
||||
return _cachedStorageBufferDescriptors = GetBufferDescriptors(
|
||||
_usedStorageBuffers,
|
||||
_usedStorageBuffersWrite,
|
||||
true,
|
||||
_counts.IncrementStorageBuffersCount);
|
||||
out _firstStorageBufferBinding,
|
||||
GpuAccessor.QueryBindingStorageBuffer);
|
||||
}
|
||||
|
||||
private static BufferDescriptor[] GetBufferDescriptors(
|
||||
int usedMask,
|
||||
int writtenMask,
|
||||
bool isArray,
|
||||
Func<int> getBindingCallback)
|
||||
out int firstBinding,
|
||||
Func<int, int> getBindingCallback)
|
||||
{
|
||||
firstBinding = 0;
|
||||
bool hasFirstBinding = false;
|
||||
var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)];
|
||||
|
||||
int lastSlot = -1;
|
||||
|
@ -529,13 +539,25 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
// The next array entries also consumes bindings, even if they are unused.
|
||||
for (int j = lastSlot + 1; j < slot; j++)
|
||||
{
|
||||
getBindingCallback();
|
||||
int binding = getBindingCallback(j);
|
||||
|
||||
if (!hasFirstBinding)
|
||||
{
|
||||
firstBinding = binding;
|
||||
hasFirstBinding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastSlot = slot;
|
||||
|
||||
descriptors[i] = new BufferDescriptor(getBindingCallback(), slot);
|
||||
descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot);
|
||||
|
||||
if (!hasFirstBinding)
|
||||
{
|
||||
firstBinding = descriptors[i].Binding;
|
||||
hasFirstBinding = true;
|
||||
}
|
||||
|
||||
if ((writtenMask & (1 << slot)) != 0)
|
||||
{
|
||||
|
@ -550,15 +572,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public TextureDescriptor[] GetTextureDescriptors()
|
||||
{
|
||||
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, _counts.IncrementTexturesCount);
|
||||
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture);
|
||||
}
|
||||
|
||||
public TextureDescriptor[] GetImageDescriptors()
|
||||
{
|
||||
return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, _counts.IncrementImagesCount);
|
||||
return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, GpuAccessor.QueryBindingImage);
|
||||
}
|
||||
|
||||
private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary<TextureInfo, TextureMeta> dict, Func<int> getBindingCallback)
|
||||
private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary<TextureInfo, TextureMeta> dict, Func<int, int> getBindingCallback)
|
||||
{
|
||||
var descriptors = new TextureDescriptor[dict.Count];
|
||||
|
||||
|
@ -568,7 +590,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
var info = kv.Key;
|
||||
var meta = kv.Value;
|
||||
|
||||
int binding = getBindingCallback();
|
||||
int binding = getBindingCallback(i);
|
||||
|
||||
descriptors[i] = new TextureDescriptor(binding, meta.Type, info.Format, info.CbufSlot, info.Handle);
|
||||
descriptors[i].SetFlag(meta.UsageFlags);
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
public class TranslationCounts
|
||||
{
|
||||
public int UniformBuffersCount { get; private set; }
|
||||
public int StorageBuffersCount { get; private set; }
|
||||
public int TexturesCount { get; private set; }
|
||||
public int ImagesCount { get; private set; }
|
||||
|
||||
public TranslationCounts()
|
||||
{
|
||||
// The first binding is reserved for the support buffer.
|
||||
UniformBuffersCount = 1;
|
||||
}
|
||||
|
||||
internal int IncrementUniformBuffersCount()
|
||||
{
|
||||
return UniformBuffersCount++;
|
||||
}
|
||||
|
||||
internal int IncrementStorageBuffersCount()
|
||||
{
|
||||
return StorageBuffersCount++;
|
||||
}
|
||||
|
||||
internal int IncrementTexturesCount()
|
||||
{
|
||||
return TexturesCount++;
|
||||
}
|
||||
|
||||
internal int IncrementImagesCount()
|
||||
{
|
||||
return ImagesCount++;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,18 +25,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
}
|
||||
}
|
||||
|
||||
public static TranslatorContext CreateContext(
|
||||
ulong address,
|
||||
IGpuAccessor gpuAccessor,
|
||||
TranslationOptions options,
|
||||
TranslationCounts counts = null)
|
||||
public static TranslatorContext CreateContext(ulong address, IGpuAccessor gpuAccessor, TranslationOptions options)
|
||||
{
|
||||
counts ??= new TranslationCounts();
|
||||
|
||||
return DecodeShader(address, gpuAccessor, options, counts);
|
||||
return DecodeShader(address, gpuAccessor, options);
|
||||
}
|
||||
|
||||
internal static ShaderProgram Translate(FunctionCode[] functions, ShaderConfig config, out ShaderProgramInfo shaderProgramInfo)
|
||||
internal static ShaderProgram Translate(FunctionCode[] functions, ShaderConfig config)
|
||||
{
|
||||
var cfgs = new ControlFlowGraph[functions.Length];
|
||||
var frus = new RegisterUsage.FunctionRegisterUsage[functions.Length];
|
||||
|
@ -87,31 +81,25 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
||||
|
||||
ShaderProgram program;
|
||||
|
||||
switch (config.Options.TargetLanguage)
|
||||
{
|
||||
case TargetLanguage.Glsl:
|
||||
program = new ShaderProgram(config.Stage, GlslGenerator.Generate(sInfo, config));
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedException(config.Options.TargetLanguage.ToString());
|
||||
}
|
||||
|
||||
shaderProgramInfo = new ShaderProgramInfo(
|
||||
ShaderProgramInfo info = new ShaderProgramInfo(
|
||||
config.GetConstantBufferDescriptors(),
|
||||
config.GetStorageBufferDescriptors(),
|
||||
config.GetTextureDescriptors(),
|
||||
config.GetImageDescriptors(),
|
||||
config.Stage,
|
||||
config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
|
||||
config.UsedFeatures.HasFlag(FeatureFlags.RtLayer),
|
||||
config.ClipDistancesWritten,
|
||||
config.OmapTargets);
|
||||
|
||||
return program;
|
||||
return config.Options.TargetLanguage switch
|
||||
{
|
||||
TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)),
|
||||
_ => throw new NotImplementedException(config.Options.TargetLanguage.ToString())
|
||||
};
|
||||
}
|
||||
|
||||
private static TranslatorContext DecodeShader(ulong address, IGpuAccessor gpuAccessor, TranslationOptions options, TranslationCounts counts)
|
||||
private static TranslatorContext DecodeShader(ulong address, IGpuAccessor gpuAccessor, TranslationOptions options)
|
||||
{
|
||||
ShaderConfig config;
|
||||
DecodedProgram program;
|
||||
|
@ -119,13 +107,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
if ((options.Flags & TranslationFlags.Compute) != 0)
|
||||
{
|
||||
config = new ShaderConfig(gpuAccessor, options, counts);
|
||||
config = new ShaderConfig(gpuAccessor, options);
|
||||
|
||||
program = Decoder.Decode(config, address);
|
||||
}
|
||||
else
|
||||
{
|
||||
config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, options, counts);
|
||||
config = new ShaderConfig(new ShaderHeader(gpuAccessor, address), gpuAccessor, options);
|
||||
|
||||
program = Decoder.Decode(config, address + HeaderSize);
|
||||
}
|
||||
|
@ -138,20 +126,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
{
|
||||
maxEndAddress = block.EndAddress;
|
||||
}
|
||||
|
||||
if (!config.UsedFeatures.HasFlag(FeatureFlags.Bindless))
|
||||
{
|
||||
for (int index = 0; index < block.OpCodes.Count; index++)
|
||||
{
|
||||
InstOp op = block.OpCodes[index];
|
||||
|
||||
if (op.Props.HasFlag(InstProps.Tex))
|
||||
{
|
||||
int tidB = (int)((op.RawOpCode >> 36) & 0x1fff);
|
||||
config.TextureHandlesForCache.Add(tidB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
|
||||
public ShaderStage Stage => _config.Stage;
|
||||
public int Size => _config.Size;
|
||||
|
||||
public FeatureFlags UsedFeatures => _config.UsedFeatures;
|
||||
|
||||
public HashSet<int> TextureHandlesForCache => _config.TextureHandlesForCache;
|
||||
public int Cb1DataSize => _config.Cb1DataSize;
|
||||
|
||||
public IGpuAccessor GpuAccessor => _config.GpuAccessor;
|
||||
|
||||
|
@ -129,16 +126,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
return output;
|
||||
}
|
||||
|
||||
public ShaderProgram Translate(
|
||||
out ShaderProgramInfo shaderProgramInfo,
|
||||
TranslatorContext nextStage = null,
|
||||
TranslatorContext other = null)
|
||||
public void SetNextStage(TranslatorContext nextStage)
|
||||
{
|
||||
if (nextStage != null)
|
||||
{
|
||||
_config.MergeFromtNextStage(nextStage._config);
|
||||
}
|
||||
_config.MergeFromtNextStage(nextStage._config);
|
||||
}
|
||||
|
||||
public ShaderProgram Translate(TranslatorContext other = null)
|
||||
{
|
||||
FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _);
|
||||
|
||||
if (other != null)
|
||||
|
@ -152,7 +146,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||
_config.InheritFrom(other._config);
|
||||
}
|
||||
|
||||
return Translator.Translate(code, _config, out shaderProgramInfo);
|
||||
return Translator.Translate(code, _config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue