Implement SULD shader instruction (#1117)

* Implement SULD shader instruction

* Some nits
This commit is contained in:
gdkchan 2020-04-21 20:35:28 -03:00 committed by GitHub
parent 4738113f29
commit 03711dd7b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 620 additions and 109 deletions

View file

@ -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,
@ -49,25 +68,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
TextureManager.SetComputeTextureBufferIndex(state.Get<int>(MethodOffset.TextureBufferIndex));
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);
}
ShaderProgramInfo info = cs.Shader.Program.Info;
for (int index = 0; index < info.CBuffers.Count; index++)
{

View file

@ -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>

View file

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

View file

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

View file

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

View file

@ -9,16 +9,16 @@ 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;
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool isArray = (texOp.Type & SamplerType.Array) != 0;
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));
}
string[] cElems = new string[4];
for (int index = 0; index < 4; index++)
if (texOp.Inst == Instruction.ImageStore)
{
if (srcIndex < texOp.SourcesCount)
VariableType type = texOp.Format.GetComponentType();
string[] cElems = new string[4];
for (int index = 0; index < 4; index++)
{
cElems[index] = Src(VariableType.F32);
if (srcIndex < texOp.SourcesCount)
{
cElems[index] = Src(type);
}
else
{
cElems[index] = type switch
{
VariableType.S32 => NumberFormatter.FormatInt(0),
VariableType.U32 => NumberFormatter.FormatUint(0),
_ => NumberFormatter.FormatFloat(0)
};
}
}
else
string prefix = type switch
{
cElems[index] = NumberFormatter.FormatFloat(0);
}
VariableType.S32 => "i",
VariableType.U32 => "u",
_ => string.Empty
};
Append(prefix + "vec4(" + string.Join(", ", cElems) + ")");
}
Append("vec4(" + string.Join(", ", cElems) + ")");
texCall += ")";
texCall += ")" + (texOp.Inst == Instruction.ImageLoad ? GetMask(texOp.Index) : "");
return texCall;
}

View file

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

View file

@ -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));

View file

@ -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;
IntegerSize.B64 => 2,
IntegerSize.B128 => 4,
IntegerSize.UB128 => 4,
_ => 1
};
}
case ImageDimensions.ImageBuffer:
return SamplerType.TextureBuffer;
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
};
}
case ImageDimensions.Image1DArray:
return SamplerType.Texture1D | SamplerType.Array;
private static TextureFormat GetTextureFormat(EmitterContext context, int handle)
{
var format = (TextureFormat)context.Config.QueryInfo(QueryInfoName.TextureFormat, handle);
case ImageDimensions.Image2D:
return SamplerType.Texture2D;
if (format == TextureFormat.Unknown)
{
context.Config.PrintLog($"Unknown format for texture {handle}.");
case ImageDimensions.Image2DArray:
return SamplerType.Texture2D | SamplerType.Array;
case ImageDimensions.Image3D:
return SamplerType.Texture3D;
format = TextureFormat.R8G8B8A8Unorm;
}
return SamplerType.None;
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)

View file

@ -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,

View file

@ -11,6 +11,7 @@ namespace Ryujinx.Graphics.Shader
IsTextureRectangle,
PrimitiveTopology,
StorageBufferOffsetAlignment,
SupportsNonConstantTextureOffset
SupportsNonConstantTextureOffset,
TextureFormat
}
}

View file

@ -4,8 +4,9 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{
class AstTextureOperation : AstOperation
{
public SamplerType Type { get; }
public TextureFlags Flags { get; }
public SamplerType Type { get; }
public TextureFormat Format { get; }
public TextureFlags Flags { get; }
public int Handle { get; }
public int ArraySize { 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;

View file

@ -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)

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

View file

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