Implement SULD shader instruction (#1117)
* Implement SULD shader instruction * Some nits
This commit is contained in:
parent
4738113f29
commit
03711dd7b5
@ -29,7 +29,26 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||
|
||||
int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize);
|
||||
|
||||
uint sbEnableMask = 0;
|
||||
uint ubEnableMask = 0;
|
||||
|
||||
for (int index = 0; index < Constants.TotalCpUniformBuffers; index++)
|
||||
{
|
||||
if (!qmd.ConstantBufferValid(index))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ubEnableMask |= 1u << index;
|
||||
|
||||
ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32;
|
||||
ulong size = (ulong)qmd.ConstantBufferSize(index);
|
||||
|
||||
BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
|
||||
}
|
||||
|
||||
ComputeShader cs = ShaderCache.GetComputeShader(
|
||||
state,
|
||||
shaderGpuVa,
|
||||
qmd.CtaThreadDimension0,
|
||||
qmd.CtaThreadDimension1,
|
||||
@ -51,24 +70,6 @@ namespace Ryujinx.Graphics.Gpu.Engine
|
||||
|
||||
ShaderProgramInfo info = cs.Shader.Program.Info;
|
||||
|
||||
uint sbEnableMask = 0;
|
||||
uint ubEnableMask = 0;
|
||||
|
||||
for (int index = 0; index < Constants.TotalCpUniformBuffers; index++)
|
||||
{
|
||||
if (!qmd.ConstantBufferValid(index))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ubEnableMask |= 1u << index;
|
||||
|
||||
ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32;
|
||||
ulong size = (ulong)qmd.ConstantBufferSize(index);
|
||||
|
||||
BufferManager.SetComputeUniformBuffer(index, gpuVa, size);
|
||||
}
|
||||
|
||||
for (int index = 0; index < info.CBuffers.Count; index++)
|
||||
{
|
||||
BufferDescriptor cb = info.CBuffers[index];
|
||||
|
@ -207,6 +207,17 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
UpdateRenderTargets();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a texture descriptor used on the compute pipeline.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="handle">Shader "fake" handle of the texture</param>
|
||||
/// <returns>The texture descriptor</returns>
|
||||
public TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle)
|
||||
{
|
||||
return _cpBindingsManager.GetTextureDescriptor(state, 0, handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a texture descriptor used on the graphics pipeline.
|
||||
/// </summary>
|
||||
|
@ -50,6 +50,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <remarks>
|
||||
/// This automatically translates, compiles and adds the code to the cache if not present.
|
||||
/// </remarks>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
|
||||
/// <param name="localSizeX">Local group size X of the computer shader</param>
|
||||
/// <param name="localSizeY">Local group size Y of the computer shader</param>
|
||||
@ -58,6 +59,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <param name="sharedMemorySize">Shared memory size of the compute shader</param>
|
||||
/// <returns>Compiled compute shader code</returns>
|
||||
public ComputeShader GetComputeShader(
|
||||
GpuState state,
|
||||
ulong gpuVa,
|
||||
int localSizeX,
|
||||
int localSizeY,
|
||||
@ -79,6 +81,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
|
||||
CachedShader shader = TranslateComputeShader(
|
||||
state,
|
||||
gpuVa,
|
||||
localSizeX,
|
||||
localSizeY,
|
||||
@ -241,6 +244,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <summary>
|
||||
/// Translates the binary Maxwell shader code to something that the host API accepts.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="gpuVa">GPU virtual address of the binary shader code</param>
|
||||
/// <param name="localSizeX">Local group size X of the computer shader</param>
|
||||
/// <param name="localSizeY">Local group size Y of the computer shader</param>
|
||||
@ -249,6 +253,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <param name="sharedMemorySize">Shared memory size of the compute shader</param>
|
||||
/// <returns>Compiled compute shader code</returns>
|
||||
private CachedShader TranslateComputeShader(
|
||||
GpuState state,
|
||||
ulong gpuVa,
|
||||
int localSizeX,
|
||||
int localSizeY,
|
||||
@ -265,12 +270,20 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
return info switch
|
||||
{
|
||||
QueryInfoName.ComputeLocalSizeX => localSizeX,
|
||||
QueryInfoName.ComputeLocalSizeY => localSizeY,
|
||||
QueryInfoName.ComputeLocalSizeZ => localSizeZ,
|
||||
QueryInfoName.ComputeLocalMemorySize => localMemorySize,
|
||||
QueryInfoName.ComputeSharedMemorySize => sharedMemorySize,
|
||||
_ => QueryInfoCommon(info)
|
||||
QueryInfoName.ComputeLocalSizeX
|
||||
=> localSizeX,
|
||||
QueryInfoName.ComputeLocalSizeY
|
||||
=> localSizeY,
|
||||
QueryInfoName.ComputeLocalSizeZ
|
||||
=> localSizeZ,
|
||||
QueryInfoName.ComputeLocalMemorySize
|
||||
=> localMemorySize,
|
||||
QueryInfoName.ComputeSharedMemorySize
|
||||
=> sharedMemorySize,
|
||||
QueryInfoName.TextureFormat
|
||||
=> (int)QueryComputeTextureFormat(state, index),
|
||||
_
|
||||
=> QueryInfoCommon(info)
|
||||
};
|
||||
}
|
||||
|
||||
@ -317,10 +330,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
return info switch
|
||||
{
|
||||
QueryInfoName.IsTextureBuffer => Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index)),
|
||||
QueryInfoName.IsTextureRectangle => Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index)),
|
||||
QueryInfoName.PrimitiveTopology => (int)GetPrimitiveTopology(),
|
||||
_ => QueryInfoCommon(info)
|
||||
QueryInfoName.IsTextureBuffer
|
||||
=> Convert.ToInt32(QueryIsTextureBuffer(state, (int)stage - 1, index)),
|
||||
QueryInfoName.IsTextureRectangle
|
||||
=> Convert.ToInt32(QueryIsTextureRectangle(state, (int)stage - 1, index)),
|
||||
QueryInfoName.PrimitiveTopology
|
||||
=> (int)QueryPrimitiveTopology(),
|
||||
QueryInfoName.TextureFormat
|
||||
=> (int)QueryGraphicsTextureFormat(state, (int)stage - 1, index),
|
||||
_
|
||||
=> QueryInfoCommon(info)
|
||||
};
|
||||
}
|
||||
|
||||
@ -378,7 +397,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// This is required by geometry shaders.
|
||||
/// </summary>
|
||||
/// <returns>Primitive topology</returns>
|
||||
private InputTopology GetPrimitiveTopology()
|
||||
private InputTopology QueryPrimitiveTopology()
|
||||
{
|
||||
switch (_context.Methods.PrimitiveType)
|
||||
{
|
||||
@ -414,7 +433,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <returns>True if the texture is a buffer texture, false otherwise</returns>
|
||||
private bool QueryIsTextureBuffer(GpuState state, int stageIndex, int index)
|
||||
{
|
||||
return GetTextureDescriptor(state, stageIndex, index).UnpackTextureTarget() == TextureTarget.TextureBuffer;
|
||||
return GetGraphicsTextureDescriptor(state, stageIndex, index).UnpackTextureTarget() == TextureTarget.TextureBuffer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -428,7 +447,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
/// <returns>True if the texture is a rectangle texture, false otherwise</returns>
|
||||
private bool QueryIsTextureRectangle(GpuState state, int stageIndex, int index)
|
||||
{
|
||||
var descriptor = GetTextureDescriptor(state, stageIndex, index);
|
||||
var descriptor = GetGraphicsTextureDescriptor(state, stageIndex, index);
|
||||
|
||||
TextureTarget target = descriptor.UnpackTextureTarget();
|
||||
|
||||
@ -439,15 +458,106 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor for a given texture on the pool.
|
||||
/// Queries the format of a given texture.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="index">Index of the texture (this is the shader "fake" handle)</param>
|
||||
/// <returns>The texture format</returns>
|
||||
private TextureFormat QueryComputeTextureFormat(GpuState state, int index)
|
||||
{
|
||||
return QueryTextureFormat(GetComputeTextureDescriptor(state, index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the format of a given texture.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="stageIndex">Index of the shader stage</param>
|
||||
/// <param name="index">Index of the texture (this is the shader "fake" handle)</param>
|
||||
/// <returns>Texture descriptor</returns>
|
||||
private TextureDescriptor GetTextureDescriptor(GpuState state, int stageIndex, int index)
|
||||
/// <returns>The texture format</returns>
|
||||
private TextureFormat QueryGraphicsTextureFormat(GpuState state, int stageIndex, int index)
|
||||
{
|
||||
return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, index);
|
||||
return QueryTextureFormat(GetGraphicsTextureDescriptor(state, stageIndex, index));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the format of a given texture.
|
||||
/// </summary>
|
||||
/// <param name="descriptor">Descriptor of the texture from the texture pool</param>
|
||||
/// <returns>The texture format</returns>
|
||||
private static TextureFormat QueryTextureFormat(TextureDescriptor descriptor)
|
||||
{
|
||||
if (!FormatTable.TryGetTextureFormat(descriptor.UnpackFormat(), descriptor.UnpackSrgb(), out FormatInfo formatInfo))
|
||||
{
|
||||
return TextureFormat.Unknown;
|
||||
}
|
||||
|
||||
return formatInfo.Format switch
|
||||
{
|
||||
Format.R8Unorm => TextureFormat.R8Unorm,
|
||||
Format.R8Snorm => TextureFormat.R8Snorm,
|
||||
Format.R8Uint => TextureFormat.R8Uint,
|
||||
Format.R8Sint => TextureFormat.R8Sint,
|
||||
Format.R16Float => TextureFormat.R16Float,
|
||||
Format.R16Unorm => TextureFormat.R16Unorm,
|
||||
Format.R16Snorm => TextureFormat.R16Snorm,
|
||||
Format.R16Uint => TextureFormat.R16Uint,
|
||||
Format.R16Sint => TextureFormat.R16Sint,
|
||||
Format.R32Float => TextureFormat.R32Float,
|
||||
Format.R32Uint => TextureFormat.R32Uint,
|
||||
Format.R32Sint => TextureFormat.R32Sint,
|
||||
Format.R8G8Unorm => TextureFormat.R8G8Unorm,
|
||||
Format.R8G8Snorm => TextureFormat.R8G8Snorm,
|
||||
Format.R8G8Uint => TextureFormat.R8G8Uint,
|
||||
Format.R8G8Sint => TextureFormat.R8G8Sint,
|
||||
Format.R16G16Float => TextureFormat.R16G16Float,
|
||||
Format.R16G16Unorm => TextureFormat.R16G16Unorm,
|
||||
Format.R16G16Snorm => TextureFormat.R16G16Snorm,
|
||||
Format.R16G16Uint => TextureFormat.R16G16Uint,
|
||||
Format.R16G16Sint => TextureFormat.R16G16Sint,
|
||||
Format.R32G32Float => TextureFormat.R32G32Float,
|
||||
Format.R32G32Uint => TextureFormat.R32G32Uint,
|
||||
Format.R32G32Sint => TextureFormat.R32G32Sint,
|
||||
Format.R8G8B8A8Unorm => TextureFormat.R8G8B8A8Unorm,
|
||||
Format.R8G8B8A8Snorm => TextureFormat.R8G8B8A8Snorm,
|
||||
Format.R8G8B8A8Uint => TextureFormat.R8G8B8A8Uint,
|
||||
Format.R8G8B8A8Sint => TextureFormat.R8G8B8A8Sint,
|
||||
Format.R16G16B16A16Float => TextureFormat.R16G16B16A16Float,
|
||||
Format.R16G16B16A16Unorm => TextureFormat.R16G16B16A16Unorm,
|
||||
Format.R16G16B16A16Snorm => TextureFormat.R16G16B16A16Snorm,
|
||||
Format.R16G16B16A16Uint => TextureFormat.R16G16B16A16Uint,
|
||||
Format.R16G16B16A16Sint => TextureFormat.R16G16B16A16Sint,
|
||||
Format.R32G32B32A32Float => TextureFormat.R32G32B32A32Float,
|
||||
Format.R32G32B32A32Uint => TextureFormat.R32G32B32A32Uint,
|
||||
Format.R32G32B32A32Sint => TextureFormat.R32G32B32A32Sint,
|
||||
Format.R10G10B10A2Unorm => TextureFormat.R10G10B10A2Unorm,
|
||||
Format.R10G10B10A2Uint => TextureFormat.R10G10B10A2Uint,
|
||||
Format.R11G11B10Float => TextureFormat.R11G11B10Float,
|
||||
_ => TextureFormat.Unknown
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor for a given texture on the pool.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="handle">Index of the texture (this is the shader "fake" handle)</param>
|
||||
/// <returns>Texture descriptor</returns>
|
||||
private TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle)
|
||||
{
|
||||
return _context.Methods.TextureManager.GetComputeTextureDescriptor(state, handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the texture descriptor for a given texture on the pool.
|
||||
/// </summary>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
/// <param name="stageIndex">Index of the shader stage</param>
|
||||
/// <param name="handle">Index of the texture (this is the shader "fake" handle)</param>
|
||||
/// <returns>Texture descriptor</returns>
|
||||
private TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle)
|
||||
{
|
||||
return _context.Methods.TextureManager.GetGraphicsTextureDescriptor(state, stageIndex, handle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -459,9 +569,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
return info switch
|
||||
{
|
||||
QueryInfoName.StorageBufferOffsetAlignment => _context.Capabilities.StorageBufferOffsetAlignment,
|
||||
QueryInfoName.SupportsNonConstantTextureOffset => Convert.ToInt32(_context.Capabilities.SupportsNonConstantTextureOffset),
|
||||
_ => 0
|
||||
QueryInfoName.StorageBufferOffsetAlignment
|
||||
=> _context.Capabilities.StorageBufferOffsetAlignment,
|
||||
QueryInfoName.SupportsNonConstantTextureOffset
|
||||
=> Convert.ToInt32(_context.Capabilities.SupportsNonConstantTextureOffset),
|
||||
_
|
||||
=> 0
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -326,9 +326,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
continue;
|
||||
}
|
||||
|
||||
string imageTypeName = GetImageTypeName(texOp.Type);
|
||||
string layout = texOp.Format.ToGlslFormat();
|
||||
|
||||
context.AppendLine("writeonly uniform " + imageTypeName + " " + imageName + ";");
|
||||
if (!string.IsNullOrEmpty(layout))
|
||||
{
|
||||
layout = "layout(" + layout + ") ";
|
||||
}
|
||||
|
||||
string imageTypeName = GetImageTypeName(texOp.Type, texOp.Format.GetComponentType());
|
||||
|
||||
context.AppendLine("uniform " + layout + imageTypeName + " " + imageName + ";");
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, AstTextureOperation> kv in images)
|
||||
@ -455,7 +462,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
return typeName;
|
||||
}
|
||||
|
||||
private static string GetImageTypeName(SamplerType type)
|
||||
private static string GetImageTypeName(SamplerType type, VariableType componentType)
|
||||
{
|
||||
string typeName;
|
||||
|
||||
@ -480,6 +487,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
typeName += "Array";
|
||||
}
|
||||
|
||||
switch (componentType)
|
||||
{
|
||||
case VariableType.U32: typeName = 'u' + typeName; break;
|
||||
case VariableType.S32: typeName = 'i' + typeName; break;
|
||||
}
|
||||
|
||||
return typeName;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ using System;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenHelper;
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenMemory;
|
||||
using static Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions.InstGenPacking;
|
||||
using static Ryujinx.Graphics.Shader.StructuredIr.InstructionInfo;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
@ -115,53 +116,56 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
case Instruction.ImageLoad:
|
||||
return ImageLoadOrStore(context, operation);
|
||||
|
||||
case Instruction.ImageStore:
|
||||
return InstGenMemory.ImageStore(context, operation);
|
||||
return ImageLoadOrStore(context, operation);
|
||||
|
||||
case Instruction.LoadAttribute:
|
||||
return InstGenMemory.LoadAttribute(context, operation);
|
||||
return LoadAttribute(context, operation);
|
||||
|
||||
case Instruction.LoadConstant:
|
||||
return InstGenMemory.LoadConstant(context, operation);
|
||||
return LoadConstant(context, operation);
|
||||
|
||||
case Instruction.LoadLocal:
|
||||
return InstGenMemory.LoadLocal(context, operation);
|
||||
return LoadLocal(context, operation);
|
||||
|
||||
case Instruction.LoadShared:
|
||||
return InstGenMemory.LoadShared(context, operation);
|
||||
return LoadShared(context, operation);
|
||||
|
||||
case Instruction.LoadStorage:
|
||||
return InstGenMemory.LoadStorage(context, operation);
|
||||
return LoadStorage(context, operation);
|
||||
|
||||
case Instruction.Lod:
|
||||
return InstGenMemory.Lod(context, operation);
|
||||
return Lod(context, operation);
|
||||
|
||||
case Instruction.PackDouble2x32:
|
||||
return InstGenPacking.PackDouble2x32(context, operation);
|
||||
return PackDouble2x32(context, operation);
|
||||
|
||||
case Instruction.PackHalf2x16:
|
||||
return InstGenPacking.PackHalf2x16(context, operation);
|
||||
return PackHalf2x16(context, operation);
|
||||
|
||||
case Instruction.StoreLocal:
|
||||
return InstGenMemory.StoreLocal(context, operation);
|
||||
return StoreLocal(context, operation);
|
||||
|
||||
case Instruction.StoreShared:
|
||||
return InstGenMemory.StoreShared(context, operation);
|
||||
return StoreShared(context, operation);
|
||||
|
||||
case Instruction.StoreStorage:
|
||||
return InstGenMemory.StoreStorage(context, operation);
|
||||
return StoreStorage(context, operation);
|
||||
|
||||
case Instruction.TextureSample:
|
||||
return InstGenMemory.TextureSample(context, operation);
|
||||
return TextureSample(context, operation);
|
||||
|
||||
case Instruction.TextureSize:
|
||||
return InstGenMemory.TextureSize(context, operation);
|
||||
return TextureSize(context, operation);
|
||||
|
||||
case Instruction.UnpackDouble2x32:
|
||||
return InstGenPacking.UnpackDouble2x32(context, operation);
|
||||
return UnpackDouble2x32(context, operation);
|
||||
|
||||
case Instruction.UnpackHalf2x16:
|
||||
return InstGenPacking.UnpackHalf2x16(context, operation);
|
||||
return UnpackHalf2x16(context, operation);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
{
|
||||
static class InstGenMemory
|
||||
{
|
||||
public static string ImageStore(CodeGenContext context, AstOperation operation)
|
||||
public static string ImageLoadOrStore(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
|
||||
@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||
|
||||
string texCall = "imageStore";
|
||||
string texCall = texOp.Inst == Instruction.ImageLoad ? "imageLoad" : "imageStore";
|
||||
|
||||
int srcIndex = isBindless ? 1 : 0;
|
||||
|
||||
@ -40,14 +40,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
|
||||
int coordsCount = texOp.Type.GetDimensions();
|
||||
|
||||
int pCount = coordsCount;
|
||||
|
||||
int arrayIndexElem = -1;
|
||||
|
||||
if (isArray)
|
||||
{
|
||||
arrayIndexElem = pCount++;
|
||||
}
|
||||
int pCount = coordsCount + (isArray ? 1 : 0);
|
||||
|
||||
void Append(string str)
|
||||
{
|
||||
@ -70,23 +63,40 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
Append(Src(VariableType.S32));
|
||||
}
|
||||
|
||||
if (texOp.Inst == Instruction.ImageStore)
|
||||
{
|
||||
VariableType type = texOp.Format.GetComponentType();
|
||||
|
||||
string[] cElems = new string[4];
|
||||
|
||||
for (int index = 0; index < 4; index++)
|
||||
{
|
||||
if (srcIndex < texOp.SourcesCount)
|
||||
{
|
||||
cElems[index] = Src(VariableType.F32);
|
||||
cElems[index] = Src(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
cElems[index] = NumberFormatter.FormatFloat(0);
|
||||
cElems[index] = type switch
|
||||
{
|
||||
VariableType.S32 => NumberFormatter.FormatInt(0),
|
||||
VariableType.U32 => NumberFormatter.FormatUint(0),
|
||||
_ => NumberFormatter.FormatFloat(0)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Append("vec4(" + string.Join(", ", cElems) + ")");
|
||||
string prefix = type switch
|
||||
{
|
||||
VariableType.S32 => "i",
|
||||
VariableType.U32 => "u",
|
||||
_ => string.Empty
|
||||
};
|
||||
|
||||
texCall += ")";
|
||||
Append(prefix + "vec4(" + string.Join(", ", cElems) + ")");
|
||||
}
|
||||
|
||||
texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : "");
|
||||
|
||||
return texCall;
|
||||
}
|
||||
|
@ -280,6 +280,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
{
|
||||
return GetOperandVarType((AstOperand)operation.GetSource(0));
|
||||
}
|
||||
else if (operation is AstTextureOperation texOp &&
|
||||
(texOp.Inst == Instruction.ImageLoad ||
|
||||
texOp.Inst == Instruction.ImageStore))
|
||||
{
|
||||
return texOp.Format.GetComponentType();
|
||||
}
|
||||
|
||||
return GetDestVarType(operation.Inst);
|
||||
}
|
||||
|
@ -219,6 +219,7 @@ namespace Ryujinx.Graphics.Shader.Decoders
|
||||
Set("1110111101010x", InstEmit.St, typeof(OpCodeMemory));
|
||||
Set("1110111011011x", InstEmit.Stg, typeof(OpCodeMemory));
|
||||
Set("1110111101011x", InstEmit.Sts, typeof(OpCodeMemory));
|
||||
Set("11101011000xxx", InstEmit.Suld, typeof(OpCodeImage));
|
||||
Set("11101011001xxx", InstEmit.Sust, typeof(OpCodeImage));
|
||||
Set("1111000011111x", InstEmit.Sync, typeof(OpCodeBranchPop));
|
||||
Set("110000xxxx111x", InstEmit.Tex, typeof(OpCodeTex));
|
||||
|
@ -4,12 +4,152 @@ using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Instructions
|
||||
{
|
||||
static partial class InstEmit
|
||||
{
|
||||
public static void Suld(EmitterContext context)
|
||||
{
|
||||
OpCodeImage op = (OpCodeImage)context.CurrOp;
|
||||
|
||||
SamplerType type = ConvertSamplerType(op.Dimensions);
|
||||
|
||||
if (type == SamplerType.None)
|
||||
{
|
||||
context.Config.PrintLog("Invalid image store sampler type.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Rb is Rd on the SULD instruction.
|
||||
int rdIndex = op.Rb.Index;
|
||||
int raIndex = op.Ra.Index;
|
||||
|
||||
Operand Ra()
|
||||
{
|
||||
if (raIndex > RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
return Const(0);
|
||||
}
|
||||
|
||||
return context.Copy(Register(raIndex++, RegisterType.Gpr));
|
||||
}
|
||||
|
||||
bool isArray = op.Dimensions == ImageDimensions.Image1DArray ||
|
||||
op.Dimensions == ImageDimensions.Image2DArray;
|
||||
|
||||
Operand arrayIndex = isArray ? Ra() : null;
|
||||
|
||||
List<Operand> sourcesList = new List<Operand>();
|
||||
|
||||
if (op.IsBindless)
|
||||
{
|
||||
sourcesList.Add(context.Copy(Register(op.Rc)));
|
||||
}
|
||||
|
||||
int coordsCount = type.GetDimensions();
|
||||
|
||||
for (int index = 0; index < coordsCount; index++)
|
||||
{
|
||||
sourcesList.Add(Ra());
|
||||
}
|
||||
|
||||
if (isArray)
|
||||
{
|
||||
sourcesList.Add(arrayIndex);
|
||||
|
||||
type |= SamplerType.Array;
|
||||
}
|
||||
|
||||
Operand[] sources = sourcesList.ToArray();
|
||||
|
||||
int handle = !op.IsBindless ? op.Immediate : 0;
|
||||
|
||||
TextureFlags flags = op.IsBindless ? TextureFlags.Bindless : TextureFlags.None;
|
||||
|
||||
if (op.UseComponents)
|
||||
{
|
||||
int componentMask = (int)op.Components;
|
||||
|
||||
for (int compMask = componentMask, compIndex = 0; compMask != 0; compMask >>= 1, compIndex++)
|
||||
{
|
||||
if ((compMask & 1) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rdIndex == RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Operand rd = Register(rdIndex++, RegisterType.Gpr);
|
||||
|
||||
TextureOperation operation = new TextureOperation(
|
||||
Instruction.ImageLoad,
|
||||
type,
|
||||
flags,
|
||||
handle,
|
||||
compIndex,
|
||||
rd,
|
||||
sources);
|
||||
|
||||
if (!op.IsBindless)
|
||||
{
|
||||
operation.Format = GetTextureFormat(context, handle);
|
||||
}
|
||||
|
||||
context.Add(operation);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (op.ByteAddress)
|
||||
{
|
||||
int xIndex = op.IsBindless ? 1 : 0;
|
||||
|
||||
sources[xIndex] = context.ShiftRightS32(sources[xIndex], Const(GetComponentSizeInBytesLog2(op.Size)));
|
||||
}
|
||||
|
||||
int components = GetComponents(op.Size);
|
||||
|
||||
for (int compIndex = 0; compIndex < components; compIndex++)
|
||||
{
|
||||
if (rdIndex == RegisterConsts.RegisterZeroIndex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Operand rd = Register(rdIndex++, RegisterType.Gpr);
|
||||
|
||||
TextureOperation operation = new TextureOperation(
|
||||
Instruction.ImageLoad,
|
||||
type,
|
||||
flags,
|
||||
handle,
|
||||
compIndex,
|
||||
rd,
|
||||
sources)
|
||||
{
|
||||
Format = GetTextureFormat(op.Size)
|
||||
};
|
||||
|
||||
context.Add(operation);
|
||||
|
||||
switch (op.Size)
|
||||
{
|
||||
case IntegerSize.U8: context.Copy(rd, ZeroExtendTo32(context, rd, 8)); break;
|
||||
case IntegerSize.U16: context.Copy(rd, ZeroExtendTo32(context, rd, 16)); break;
|
||||
case IntegerSize.S8: context.Copy(rd, SignExtendTo32(context, rd, 8)); break;
|
||||
case IntegerSize.S16: context.Copy(rd, SignExtendTo32(context, rd, 16)); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void Sust(EmitterContext context)
|
||||
{
|
||||
OpCodeImage op = (OpCodeImage)context.CurrOp;
|
||||
@ -72,6 +212,8 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
type |= SamplerType.Array;
|
||||
}
|
||||
|
||||
TextureFormat format = TextureFormat.Unknown;
|
||||
|
||||
if (op.UseComponents)
|
||||
{
|
||||
int componentMask = (int)op.Components;
|
||||
@ -83,12 +225,33 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
}
|
||||
|
||||
if (!op.IsBindless)
|
||||
{
|
||||
format = GetTextureFormat(context, op.Immediate);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Config.PrintLog("Unsized image store not supported.");
|
||||
if (op.ByteAddress)
|
||||
{
|
||||
int xIndex = op.IsBindless ? 1 : 0;
|
||||
|
||||
sourcesList[xIndex] = context.ShiftRightS32(sourcesList[xIndex], Const(GetComponentSizeInBytesLog2(op.Size)));
|
||||
}
|
||||
|
||||
int components = GetComponents(op.Size);
|
||||
|
||||
for (int compIndex = 0; compIndex < components; compIndex++)
|
||||
{
|
||||
sourcesList.Add(Rb());
|
||||
}
|
||||
|
||||
format = GetTextureFormat(op.Size);
|
||||
}
|
||||
|
||||
System.Console.WriteLine(format.ToString());
|
||||
|
||||
Operand[] sources = sourcesList.ToArray();
|
||||
|
||||
int handle = !op.IsBindless ? op.Immediate : 0;
|
||||
@ -102,7 +265,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
handle,
|
||||
0,
|
||||
null,
|
||||
sources);
|
||||
sources)
|
||||
{
|
||||
Format = format
|
||||
};
|
||||
|
||||
context.Add(operation);
|
||||
}
|
||||
@ -880,43 +1046,87 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
private static SamplerType ConvertSamplerType(ImageDimensions target)
|
||||
private static int GetComponents(IntegerSize size)
|
||||
{
|
||||
switch (target)
|
||||
return size switch
|
||||
{
|
||||
case ImageDimensions.Image1D:
|
||||
return SamplerType.Texture1D;
|
||||
|
||||
case ImageDimensions.ImageBuffer:
|
||||
return SamplerType.TextureBuffer;
|
||||
|
||||
case ImageDimensions.Image1DArray:
|
||||
return SamplerType.Texture1D | SamplerType.Array;
|
||||
|
||||
case ImageDimensions.Image2D:
|
||||
return SamplerType.Texture2D;
|
||||
|
||||
case ImageDimensions.Image2DArray:
|
||||
return SamplerType.Texture2D | SamplerType.Array;
|
||||
|
||||
case ImageDimensions.Image3D:
|
||||
return SamplerType.Texture3D;
|
||||
IntegerSize.B64 => 2,
|
||||
IntegerSize.B128 => 4,
|
||||
IntegerSize.UB128 => 4,
|
||||
_ => 1
|
||||
};
|
||||
}
|
||||
|
||||
return SamplerType.None;
|
||||
private static int GetComponentSizeInBytesLog2(IntegerSize size)
|
||||
{
|
||||
return size switch
|
||||
{
|
||||
IntegerSize.U8 => 0,
|
||||
IntegerSize.S8 => 0,
|
||||
IntegerSize.U16 => 1,
|
||||
IntegerSize.S16 => 1,
|
||||
IntegerSize.B32 => 2,
|
||||
IntegerSize.B64 => 3,
|
||||
IntegerSize.B128 => 4,
|
||||
IntegerSize.UB128 => 4,
|
||||
_ => 2
|
||||
};
|
||||
}
|
||||
|
||||
private static TextureFormat GetTextureFormat(EmitterContext context, int handle)
|
||||
{
|
||||
var format = (TextureFormat)context.Config.QueryInfo(QueryInfoName.TextureFormat, handle);
|
||||
|
||||
if (format == TextureFormat.Unknown)
|
||||
{
|
||||
context.Config.PrintLog($"Unknown format for texture {handle}.");
|
||||
|
||||
format = TextureFormat.R8G8B8A8Unorm;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
private static TextureFormat GetTextureFormat(IntegerSize size)
|
||||
{
|
||||
return size switch
|
||||
{
|
||||
IntegerSize.U8 => TextureFormat.R8Uint,
|
||||
IntegerSize.S8 => TextureFormat.R8Sint,
|
||||
IntegerSize.U16 => TextureFormat.R16Uint,
|
||||
IntegerSize.S16 => TextureFormat.R16Sint,
|
||||
IntegerSize.B32 => TextureFormat.R32Uint,
|
||||
IntegerSize.B64 => TextureFormat.R32G32Uint,
|
||||
IntegerSize.B128 => TextureFormat.R32G32B32A32Uint,
|
||||
IntegerSize.UB128 => TextureFormat.R32G32B32A32Uint,
|
||||
_ => TextureFormat.R32Uint
|
||||
};
|
||||
}
|
||||
|
||||
private static SamplerType ConvertSamplerType(ImageDimensions target)
|
||||
{
|
||||
return target switch
|
||||
{
|
||||
ImageDimensions.Image1D => SamplerType.Texture1D,
|
||||
ImageDimensions.ImageBuffer => SamplerType.TextureBuffer,
|
||||
ImageDimensions.Image1DArray => SamplerType.Texture1D | SamplerType.Array,
|
||||
ImageDimensions.Image2D => SamplerType.Texture2D,
|
||||
ImageDimensions.Image2DArray => SamplerType.Texture2D | SamplerType.Array,
|
||||
ImageDimensions.Image3D => SamplerType.Texture3D,
|
||||
_ => SamplerType.None
|
||||
};
|
||||
}
|
||||
|
||||
private static SamplerType ConvertSamplerType(TextureDimensions dimensions)
|
||||
{
|
||||
switch (dimensions)
|
||||
return dimensions switch
|
||||
{
|
||||
case TextureDimensions.Texture1D: return SamplerType.Texture1D;
|
||||
case TextureDimensions.Texture2D: return SamplerType.Texture2D;
|
||||
case TextureDimensions.Texture3D: return SamplerType.Texture3D;
|
||||
case TextureDimensions.TextureCube: return SamplerType.TextureCube;
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".");
|
||||
TextureDimensions.Texture1D => SamplerType.Texture1D,
|
||||
TextureDimensions.Texture2D => SamplerType.Texture2D,
|
||||
TextureDimensions.Texture3D => SamplerType.Texture3D,
|
||||
TextureDimensions.TextureCube => SamplerType.TextureCube,
|
||||
_ => throw new ArgumentException($"Invalid texture dimensions \"{dimensions}\".")
|
||||
};
|
||||
}
|
||||
|
||||
private static SamplerType ConvertSamplerType(TextureTarget type)
|
||||
|
@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
|
||||
public int Handle { get; private set; }
|
||||
|
||||
public TextureFormat Format { get; set; }
|
||||
|
||||
public TextureOperation(
|
||||
Instruction inst,
|
||||
SamplerType type,
|
||||
|
@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
IsTextureRectangle,
|
||||
PrimitiveTopology,
|
||||
StorageBufferOffsetAlignment,
|
||||
SupportsNonConstantTextureOffset
|
||||
SupportsNonConstantTextureOffset,
|
||||
TextureFormat
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
class AstTextureOperation : AstOperation
|
||||
{
|
||||
public SamplerType Type { get; }
|
||||
public TextureFormat Format { get; }
|
||||
public TextureFlags Flags { get; }
|
||||
|
||||
public int Handle { get; }
|
||||
@ -13,6 +14,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
public AstTextureOperation(
|
||||
Instruction inst,
|
||||
SamplerType type,
|
||||
TextureFormat format,
|
||||
TextureFlags flags,
|
||||
int handle,
|
||||
int arraySize,
|
||||
@ -20,6 +22,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
params IAstNode[] sources) : base(inst, index, sources)
|
||||
{
|
||||
Type = type;
|
||||
Format = format;
|
||||
Flags = flags;
|
||||
Handle = handle;
|
||||
ArraySize = arraySize;
|
||||
|
@ -57,6 +57,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
return new AstTextureOperation(
|
||||
inst,
|
||||
texOp.Type,
|
||||
texOp.Format,
|
||||
texOp.Flags,
|
||||
texOp.Handle,
|
||||
4, // TODO: Non-hardcoded array size.
|
||||
@ -118,6 +119,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
|
||||
if (operation is TextureOperation texOp)
|
||||
{
|
||||
if (texOp.Inst == Instruction.ImageLoad || texOp.Inst == Instruction.ImageStore)
|
||||
{
|
||||
dest.VarType = texOp.Format.GetComponentType();
|
||||
}
|
||||
|
||||
AstTextureOperation astTexOp = GetAstTextureOperation(texOp);
|
||||
|
||||
if (texOp.Inst == Instruction.ImageLoad)
|
||||
|
128
Ryujinx.Graphics.Shader/TextureFormat.cs
Normal file
128
Ryujinx.Graphics.Shader/TextureFormat.cs
Normal file
@ -0,0 +1,128 @@
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader
|
||||
{
|
||||
public enum TextureFormat
|
||||
{
|
||||
Unknown,
|
||||
R8Unorm,
|
||||
R8Snorm,
|
||||
R8Uint,
|
||||
R8Sint,
|
||||
R16Float,
|
||||
R16Unorm,
|
||||
R16Snorm,
|
||||
R16Uint,
|
||||
R16Sint,
|
||||
R32Float,
|
||||
R32Uint,
|
||||
R32Sint,
|
||||
R8G8Unorm,
|
||||
R8G8Snorm,
|
||||
R8G8Uint,
|
||||
R8G8Sint,
|
||||
R16G16Float,
|
||||
R16G16Unorm,
|
||||
R16G16Snorm,
|
||||
R16G16Uint,
|
||||
R16G16Sint,
|
||||
R32G32Float,
|
||||
R32G32Uint,
|
||||
R32G32Sint,
|
||||
R8G8B8A8Unorm,
|
||||
R8G8B8A8Snorm,
|
||||
R8G8B8A8Uint,
|
||||
R8G8B8A8Sint,
|
||||
R16G16B16A16Float,
|
||||
R16G16B16A16Unorm,
|
||||
R16G16B16A16Snorm,
|
||||
R16G16B16A16Uint,
|
||||
R16G16B16A16Sint,
|
||||
R32G32B32A32Float,
|
||||
R32G32B32A32Uint,
|
||||
R32G32B32A32Sint,
|
||||
R10G10B10A2Unorm,
|
||||
R10G10B10A2Uint,
|
||||
R11G11B10Float
|
||||
}
|
||||
|
||||
static class TextureFormatExtensions
|
||||
{
|
||||
public static string ToGlslFormat(this TextureFormat format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
TextureFormat.R8Unorm => "r8",
|
||||
TextureFormat.R8Snorm => "r8_snorm",
|
||||
TextureFormat.R8Uint => "r8ui",
|
||||
TextureFormat.R8Sint => "r8i",
|
||||
TextureFormat.R16Float => "r16f",
|
||||
TextureFormat.R16Unorm => "r16",
|
||||
TextureFormat.R16Snorm => "r16_snorm",
|
||||
TextureFormat.R16Uint => "r16ui",
|
||||
TextureFormat.R16Sint => "r16i",
|
||||
TextureFormat.R32Float => "r32f",
|
||||
TextureFormat.R32Uint => "r32ui",
|
||||
TextureFormat.R32Sint => "r32i",
|
||||
TextureFormat.R8G8Unorm => "rg8",
|
||||
TextureFormat.R8G8Snorm => "rg8_snorm",
|
||||
TextureFormat.R8G8Uint => "rg8ui",
|
||||
TextureFormat.R8G8Sint => "rg8i",
|
||||
TextureFormat.R16G16Float => "rg16f",
|
||||
TextureFormat.R16G16Unorm => "rg16",
|
||||
TextureFormat.R16G16Snorm => "rg16_snorm",
|
||||
TextureFormat.R16G16Uint => "rg16ui",
|
||||
TextureFormat.R16G16Sint => "rg16i",
|
||||
TextureFormat.R32G32Float => "rg32f",
|
||||
TextureFormat.R32G32Uint => "rg32ui",
|
||||
TextureFormat.R32G32Sint => "rg32i",
|
||||
TextureFormat.R8G8B8A8Unorm => "rgba8",
|
||||
TextureFormat.R8G8B8A8Snorm => "rgba8_snorm",
|
||||
TextureFormat.R8G8B8A8Uint => "rgba8ui",
|
||||
TextureFormat.R8G8B8A8Sint => "rgba8i",
|
||||
TextureFormat.R16G16B16A16Float => "rgba16f",
|
||||
TextureFormat.R16G16B16A16Unorm => "rgba16",
|
||||
TextureFormat.R16G16B16A16Snorm => "rgba16_snorm",
|
||||
TextureFormat.R16G16B16A16Uint => "rgba16ui",
|
||||
TextureFormat.R16G16B16A16Sint => "rgba16i",
|
||||
TextureFormat.R32G32B32A32Float => "rgba32f",
|
||||
TextureFormat.R32G32B32A32Uint => "rgba32ui",
|
||||
TextureFormat.R32G32B32A32Sint => "rgba32i",
|
||||
TextureFormat.R10G10B10A2Unorm => "rgb10_a2",
|
||||
TextureFormat.R10G10B10A2Uint => "rgb10_a2ui",
|
||||
TextureFormat.R11G11B10Float => "r11f_g11f_b10f",
|
||||
_ => string.Empty
|
||||
};
|
||||
}
|
||||
|
||||
public static VariableType GetComponentType(this TextureFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case TextureFormat.R8Uint:
|
||||
case TextureFormat.R16Uint:
|
||||
case TextureFormat.R32Uint:
|
||||
case TextureFormat.R8G8Uint:
|
||||
case TextureFormat.R16G16Uint:
|
||||
case TextureFormat.R32G32Uint:
|
||||
case TextureFormat.R8G8B8A8Uint:
|
||||
case TextureFormat.R16G16B16A16Uint:
|
||||
case TextureFormat.R32G32B32A32Uint:
|
||||
case TextureFormat.R10G10B10A2Uint:
|
||||
return VariableType.U32;
|
||||
case TextureFormat.R8Sint:
|
||||
case TextureFormat.R16Sint:
|
||||
case TextureFormat.R32Sint:
|
||||
case TextureFormat.R8G8Sint:
|
||||
case TextureFormat.R16G16Sint:
|
||||
case TextureFormat.R32G32Sint:
|
||||
case TextureFormat.R8G8B8A8Sint:
|
||||
case TextureFormat.R16G16B16A16Sint:
|
||||
case TextureFormat.R32G32B32A32Sint:
|
||||
return VariableType.S32;
|
||||
};
|
||||
|
||||
return VariableType.F32;
|
||||
}
|
||||
}
|
||||
}
|
@ -102,6 +102,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return 16;
|
||||
case QueryInfoName.SupportsNonConstantTextureOffset:
|
||||
return Convert.ToInt32(true);
|
||||
case QueryInfoName.TextureFormat:
|
||||
return (int)TextureFormat.R8G8B8A8Unorm;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user