diff options
Diffstat (limited to 'Ryujinx.Graphics.Shader/Translation')
10 files changed, 360 insertions, 54 deletions
diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 9b220177..49a89374 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -53,6 +53,36 @@ namespace Ryujinx.Graphics.Shader.Translation _operations.Add(operation); } + public TextureOperation CreateTextureOperation( + Instruction inst, + SamplerType type, + TextureFlags flags, + int handle, + int compIndex, + Operand dest, + params Operand[] sources) + { + return CreateTextureOperation(inst, type, TextureFormat.Unknown, flags, handle, compIndex, dest, sources); + } + + public TextureOperation CreateTextureOperation( + Instruction inst, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int handle, + int compIndex, + Operand dest, + params Operand[] sources) + { + if (!flags.HasFlag(TextureFlags.Bindless)) + { + Config.SetUsedTexture(inst, type, format, flags, TextureOperation.DefaultCbufSlot, handle); + } + + return new TextureOperation(inst, type, format, flags, handle, compIndex, dest, sources); + } + public void FlagAttributeRead(int attribute) { if (Config.Stage == ShaderStage.Vertex && attribute == AttributeConsts.InstanceId) diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs index dcefb591..e5ba04bc 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContextInsts.cs @@ -518,6 +518,15 @@ namespace Ryujinx.Graphics.Shader.Translation public static Operand LoadConstant(this EmitterContext context, Operand a, Operand b) { + if (a.Type == OperandType.Constant) + { + context.Config.SetUsedConstantBuffer(a.Value); + } + else + { + context.Config.SetUsedFeature(FeatureFlags.CbIndexing); + } + return context.Add(Instruction.LoadConstant, Local(), a, b); } diff --git a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs index 1b712896..b0c48410 100644 --- a/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs +++ b/Ryujinx.Graphics.Shader/Translation/FeatureFlags.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation FragCoordXY = 1 << 1, Bindless = 1 << 2, - - InstanceId = 1 << 3 + InstanceId = 1 << 3, + CbIndexing = 1 << 4 } } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs index f91a00eb..3a523adc 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessElimination.cs @@ -33,7 +33,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (bindlessHandle.Type == OperandType.ConstantBuffer) { - texOp.SetHandle(bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot()); + SetHandle(config, texOp, bindlessHandle.GetCbufOffset(), bindlessHandle.GetCbufSlot()); continue; } @@ -56,7 +56,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - texOp.SetHandle(src0.GetCbufOffset() | (src1.GetCbufOffset() << 16), src0.GetCbufSlot()); + SetHandle(config, texOp, src0.GetCbufOffset() | (src1.GetCbufOffset() << 16), src0.GetCbufSlot()); } else if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore) { @@ -64,11 +64,19 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations if (src0.Type == OperandType.ConstantBuffer) { - texOp.SetHandle(src0.GetCbufOffset(), src0.GetCbufSlot()); - texOp.Format = config.GetTextureFormat(texOp.Handle, texOp.CbufSlot); + int cbufOffset = src0.GetCbufOffset(); + int cbufSlot = src0.GetCbufSlot(); + texOp.Format = config.GetTextureFormat(cbufOffset, cbufSlot); + SetHandle(config, texOp, cbufOffset, cbufSlot); } } } } + + private static void SetHandle(ShaderConfig config, TextureOperation texOp, int cbufOffset, int cbufSlot) + { + texOp.SetHandle(cbufOffset, cbufSlot); + config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, cbufSlot, cbufOffset); + } } } diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs index 41f42dad..ca46a1f5 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/BindlessToIndexed.cs @@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { static class BindlessToIndexed { - public static void RunPass(BasicBlock block) + public static void RunPass(BasicBlock block, ShaderConfig config) { // We can turn a bindless texture access into a indexed access, // as long the following conditions are true: @@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations continue; } - texOp.TurnIntoIndexed(addSrc1.Value / 4); + TurnIntoIndexed(config, texOp, addSrc1.Value / 4); Operand index = Local(); @@ -75,5 +75,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations texOp.SetSource(0, index); } } + + private static void TurnIntoIndexed(ShaderConfig config, TextureOperation texOp, int handle) + { + texOp.TurnIntoIndexed(handle); + config.SetUsedTexture(texOp.Inst, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, handle); + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs index a3417544..bccb0cbe 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/GlobalToStorage.cs @@ -58,11 +58,16 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { Operation operation = (Operation)node.Value; + bool isAtomic = operation.Inst.IsAtomic(); + bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal; + + config.SetUsedStorageBuffer(storageIndex, isWrite); + Operand GetStorageOffset() { Operand addrLow = operation.GetSource(0); - Operand baseAddrLow = Cbuf(0, GetStorageCbOffset(config.Stage, storageIndex)); + Operand baseAddrLow = config.CreateCbuf(0, GetStorageCbOffset(config.Stage, storageIndex)); Operand baseAddrTrunc = Local(); @@ -96,7 +101,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operation storageOp; - if (operation.Inst.IsAtomic()) + if (isAtomic) { Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; @@ -133,7 +138,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations { Operand addrLow = operation.GetSource(0); - Operand baseAddrLow = Cbuf(0, UbeBaseOffset + storageIndex * StorageDescSize); + Operand baseAddrLow = config.CreateCbuf(0, UbeBaseOffset + storageIndex * StorageDescSize); Operand baseAddrTrunc = Local(); @@ -157,9 +162,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations Operand[] sources = new Operand[operation.SourcesCount]; - sources[0] = Const(UbeFirstCbuf + storageIndex); + int cbSlot = UbeFirstCbuf + storageIndex; + + sources[0] = Const(cbSlot); sources[1] = GetCbufOffset(); + config.SetUsedConstantBuffer(cbSlot); + for (int index = 2; index < operation.SourcesCount; index++) { sources[index] = operation.GetSource(index); diff --git a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs index ec8d8015..d184e6b4 100644 --- a/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs +++ b/Ryujinx.Graphics.Shader/Translation/Optimizations/Optimizer.cs @@ -16,7 +16,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++) { GlobalToStorage.RunPass(blocks[blkIndex], config); - BindlessToIndexed.RunPass(blocks[blkIndex]); + BindlessToIndexed.RunPass(blocks[blkIndex], config); BindlessElimination.RunPass(blocks[blkIndex], config); } diff --git a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs index 5427c013..07eeae48 100644 --- a/Ryujinx.Graphics.Shader/Translation/Rewriter.cs +++ b/Ryujinx.Graphics.Shader/Translation/Rewriter.cs @@ -48,6 +48,9 @@ namespace Ryujinx.Graphics.Shader.Translation { Operation operation = (Operation)node.Value; + bool isAtomic = operation.Inst.IsAtomic(); + bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal; + Operation storageOp; Operand PrependOperation(Instruction inst, params Operand[] sources) @@ -67,11 +70,13 @@ namespace Ryujinx.Graphics.Shader.Translation for (int slot = 0; slot < StorageMaxCount; slot++) { + config.SetUsedStorageBuffer(slot, isWrite); + int cbOffset = GetStorageCbOffset(config.Stage, slot); - Operand baseAddrLow = Cbuf(0, cbOffset); - Operand baseAddrHigh = Cbuf(0, cbOffset + 1); - Operand size = Cbuf(0, cbOffset + 2); + Operand baseAddrLow = config.CreateCbuf(0, cbOffset); + Operand baseAddrHigh = config.CreateCbuf(0, cbOffset + 1); + Operand size = config.CreateCbuf(0, cbOffset + 2); Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow); Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow); @@ -104,7 +109,7 @@ namespace Ryujinx.Graphics.Shader.Translation sources[index] = operation.GetSource(index); } - if (operation.Inst.IsAtomic()) + if (isAtomic) { Instruction inst = (operation.Inst & ~Instruction.MrMask) | Instruction.MrStorage; @@ -303,6 +308,7 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new TextureOperation( Instruction.TextureSize, texOp.Type, + texOp.Format, texOp.Flags, texOp.Handle, index, @@ -350,6 +356,7 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new TextureOperation( Instruction.Lod, texOp.Type, + texOp.Format, texOp.Flags, texOp.Handle, 1, @@ -374,6 +381,7 @@ namespace Ryujinx.Graphics.Shader.Translation node.List.AddBefore(node, new TextureOperation( Instruction.TextureSize, texOp.Type, + texOp.Format, texOp.Flags, texOp.Handle, index, @@ -409,6 +417,7 @@ namespace Ryujinx.Graphics.Shader.Translation TextureOperation newTexOp = new TextureOperation( Instruction.TextureSample, texOp.Type, + texOp.Format, texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets), texOp.Handle, componentIndex, diff --git a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs index 077ce70d..3230f4e6 100644 --- a/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs +++ b/Ryujinx.Graphics.Shader/Translation/ShaderConfig.cs @@ -1,9 +1,16 @@ +using Ryujinx.Graphics.Shader.IntermediateRepresentation; +using System; using System.Collections.Generic; +using System.Linq; +using System.Numerics; namespace Ryujinx.Graphics.Shader.Translation { class ShaderConfig { + // TODO: Non-hardcoded array size. + public const int SamplerArraySize = 4; + public ShaderStage Stage { get; } public bool GpPassthrough { get; } @@ -24,8 +31,6 @@ namespace Ryujinx.Graphics.Shader.Translation public TranslationFlags Flags { get; } - public TranslationCounts Counts { get; } - public int Size { get; private set; } public byte ClipDistancesWritten { get; private set; } @@ -34,42 +39,80 @@ namespace Ryujinx.Graphics.Shader.Translation public HashSet<int> TextureHandlesForCache { get; } + private readonly TranslationCounts _counts; + + private int _usedConstantBuffers; + private int _usedStorageBuffers; + private int _usedStorageBuffersWrite; + + private struct TextureInfo : IEquatable<TextureInfo> + { + public int CbufSlot { get; } + public int Handle { get; } + public bool Indexed { get; } + public TextureFormat Format { get; } + + public TextureInfo(int cbufSlot, int handle, bool indexed, TextureFormat format) + { + CbufSlot = cbufSlot; + Handle = handle; + Indexed = indexed; + Format = format; + } + + public override bool Equals(object obj) + { + return obj is TextureInfo other && Equals(other); + } + + public bool Equals(TextureInfo other) + { + return CbufSlot == other.CbufSlot && Handle == other.Handle && Indexed == other.Indexed && Format == other.Format; + } + + public override int GetHashCode() + { + return HashCode.Combine(CbufSlot, Handle, Indexed, Format); + } + } + + private struct TextureMeta + { + public bool AccurateType; + public SamplerType Type; + public TextureUsageFlags UsageFlags; + } + + private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures; + private readonly Dictionary<TextureInfo, TextureMeta> _usedImages; + + private BufferDescriptor[] _cachedConstantBufferDescriptors; + private BufferDescriptor[] _cachedStorageBufferDescriptors; + private TextureDescriptor[] _cachedTextureDescriptors; + private TextureDescriptor[] _cachedImageDescriptors; + public ShaderConfig(IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) { Stage = ShaderStage.Compute; - GpPassthrough = false; - OutputTopology = OutputTopology.PointList; - MaxOutputVertices = 0; - LocalMemorySize = 0; - ImapTypes = null; - OmapTargets = null; - OmapSampleMask = false; - OmapDepth = false; GpuAccessor = gpuAccessor; Flags = flags; - Size = 0; - UsedFeatures = FeatureFlags.None; - Counts = counts; + _counts = counts; TextureHandlesForCache = new HashSet<int>(); + _usedTextures = new Dictionary<TextureInfo, TextureMeta>(); + _usedImages = new Dictionary<TextureInfo, TextureMeta>(); } - public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) + public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationFlags flags, TranslationCounts counts) : this(gpuAccessor, flags, counts) { - Stage = header.Stage; - GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; - OutputTopology = header.OutputTopology; - MaxOutputVertices = header.MaxOutputVertexCount; - LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize; - ImapTypes = header.ImapTypes; - OmapTargets = header.OmapTargets; - OmapSampleMask = header.OmapSampleMask; - OmapDepth = header.OmapDepth; - GpuAccessor = gpuAccessor; - Flags = flags; - Size = 0; - UsedFeatures = FeatureFlags.None; - Counts = counts; - TextureHandlesForCache = new HashSet<int>(); + Stage = header.Stage; + GpPassthrough = header.Stage == ShaderStage.Geometry && header.GpPassthrough; + OutputTopology = header.OutputTopology; + MaxOutputVertices = header.MaxOutputVertexCount; + LocalMemorySize = header.ShaderLocalMemoryLowSize + header.ShaderLocalMemoryHighSize; + ImapTypes = header.ImapTypes; + OmapTargets = header.OmapTargets; + OmapSampleMask = header.OmapSampleMask; + OmapDepth = header.OmapDepth; } public int GetDepthRegister() @@ -126,5 +169,199 @@ namespace Ryujinx.Graphics.Shader.Translation { UsedFeatures |= flags; } + + public Operand CreateCbuf(int slot, int offset) + { + SetUsedConstantBuffer(slot); + return OperandHelper.Cbuf(slot, offset); + } + + public void SetUsedConstantBuffer(int slot) + { + _usedConstantBuffers |= 1 << slot; + } + + public void SetUsedStorageBuffer(int slot, bool write) + { + int mask = 1 << slot; + _usedStorageBuffers |= mask; + + if (write) + { + _usedStorageBuffersWrite |= mask; + } + } + + public void SetUsedTexture( + Instruction inst, + SamplerType type, + TextureFormat format, + TextureFlags flags, + int cbufSlot, + int handle) + { + inst &= Instruction.Mask; + bool isImage = inst == Instruction.ImageLoad || inst == Instruction.ImageStore; + bool isWrite = inst == Instruction.ImageStore; + bool accurateType = inst != Instruction.TextureSize && inst != Instruction.Lod; + + if (isImage) + { + SetUsedTextureOrImage(_usedImages, cbufSlot, handle, type, format, true, isWrite, false); + } + else + { + SetUsedTextureOrImage(_usedTextures, cbufSlot, handle, type, TextureFormat.Unknown, flags.HasFlag(TextureFlags.IntCoords), false, accurateType); + } + } + + private static void SetUsedTextureOrImage( + Dictionary<TextureInfo, TextureMeta> dict, + int cbufSlot, + int handle, + SamplerType type, + TextureFormat format, + bool intCoords, + bool write, + bool accurateType) + { + var dimensions = type.GetDimensions(); + var isArray = type.HasFlag(SamplerType.Array); + var isIndexed = type.HasFlag(SamplerType.Indexed); + + var usageFlags = TextureUsageFlags.None; + + if (intCoords) + { + usageFlags |= TextureUsageFlags.NeedsScaleValue; + + var canScale = (dimensions == 2 && !isArray) || (dimensions == 3 && isArray); + if (!canScale) + { + // Resolution scaling cannot be applied to this texture right now. + // Flag so that we know to blacklist scaling on related textures when binding them. + usageFlags |= TextureUsageFlags.ResScaleUnsupported; + } + } + + if (write) + { + usageFlags |= TextureUsageFlags.ImageStore; + } + + int arraySize = isIndexed ? SamplerArraySize : 1; + + for (int layer = 0; layer < arraySize; layer++) + { + var info = new TextureInfo(cbufSlot, handle + layer * 2, isIndexed, format); + var meta = new TextureMeta() + { + AccurateType = accurateType, + Type = type, + UsageFlags = usageFlags + }; + + if (dict.TryGetValue(info, out var existingMeta)) + { + meta.UsageFlags |= existingMeta.UsageFlags; + + // If the texture we have has inaccurate type information, then + // we prefer the most accurate one. + if (existingMeta.AccurateType) + { + meta.AccurateType = true; + meta.Type = existingMeta.Type; + } + + dict[info] = meta; + } + else + { + dict.Add(info, meta); + } + } + } + + public BufferDescriptor[] GetConstantBufferDescriptors() + { + if (_cachedConstantBufferDescriptors != null) + { + return _cachedConstantBufferDescriptors; + } + + int usedMask = _usedConstantBuffers; + + if (UsedFeatures.HasFlag(FeatureFlags.CbIndexing)) + { + usedMask = FillMask(usedMask); + } + + return _cachedConstantBufferDescriptors = GetBufferDescriptors(usedMask, 0, _counts.IncrementUniformBuffersCount); + } + + public BufferDescriptor[] GetStorageBufferDescriptors() + { + return _cachedStorageBufferDescriptors ??= GetBufferDescriptors(FillMask(_usedStorageBuffers), _usedStorageBuffersWrite, _counts.IncrementStorageBuffersCount); + } + + private static int FillMask(int mask) + { + // When the storage or uniform buffers are used as array, we must allocate a binding + // even for the "gaps" that are not used on the shader. + // For this reason, fill up the gaps so that all slots up to the highest one are + // marked as "used". + return mask != 0 ? (int)(uint.MaxValue >> BitOperations.LeadingZeroCount((uint)mask)) : 0; + } + + private static BufferDescriptor[] GetBufferDescriptors(int usedMask, int writtenMask, Func<int> getBindingCallback) + { + var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)]; + + for (int i = 0; i < descriptors.Length; i++) + { + int slot = BitOperations.TrailingZeroCount(usedMask); + + descriptors[i] = new BufferDescriptor(getBindingCallback(), slot); + + if ((writtenMask & (1 << slot)) != 0) + { + descriptors[i].SetFlag(BufferUsageFlags.Write); + } + + usedMask &= ~(1 << slot); + } + + return descriptors; + } + + public TextureDescriptor[] GetTextureDescriptors() + { + return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, _counts.IncrementTexturesCount); + } + + public TextureDescriptor[] GetImageDescriptors() + { + return _cachedImageDescriptors ??= GetTextureOrImageDescriptors(_usedImages, _counts.IncrementImagesCount); + } + + private static TextureDescriptor[] GetTextureOrImageDescriptors(Dictionary<TextureInfo, TextureMeta> dict, Func<int> getBindingCallback) + { + var descriptors = new TextureDescriptor[dict.Count]; + + int i = 0; + foreach (var kv in dict.OrderBy(x => x.Key.Indexed).OrderBy(x => x.Key.Handle)) + { + var info = kv.Key; + var meta = kv.Value; + + int binding = getBindingCallback(); + + descriptors[i] = new TextureDescriptor(binding, meta.Type, info.Format, info.CbufSlot, info.Handle); + descriptors[i].SetFlag(meta.UsageFlags); + i++; + } + + return descriptors; + } } }
\ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/Translation/Translator.cs b/Ryujinx.Graphics.Shader/Translation/Translator.cs index 1c15ccf2..74c6a653 100644 --- a/Ryujinx.Graphics.Shader/Translation/Translator.cs +++ b/Ryujinx.Graphics.Shader/Translation/Translator.cs @@ -87,18 +87,16 @@ namespace Ryujinx.Graphics.Shader.Translation StructuredProgramInfo sInfo = StructuredProgram.MakeStructuredProgram(funcs, config); - GlslProgram program = GlslGenerator.Generate(sInfo, config); + string glslCode = GlslGenerator.Generate(sInfo, config); shaderProgramInfo = new ShaderProgramInfo( - program.CBufferDescriptors, - program.SBufferDescriptors, - program.TextureDescriptors, - program.ImageDescriptors, + config.GetConstantBufferDescriptors(), + config.GetStorageBufferDescriptors(), + config.GetTextureDescriptors(), + config.GetImageDescriptors(), config.UsedFeatures.HasFlag(FeatureFlags.InstanceId), config.ClipDistancesWritten); - string glslCode = program.Code; - return new ShaderProgram(config.Stage, glslCode); } @@ -112,7 +110,7 @@ namespace Ryujinx.Graphics.Shader.Translation Block[][] cfg; ulong maxEndAddress = 0; - bool hasBindless = false; + bool hasBindless; if ((flags & TranslationFlags.Compute) != 0) { |
