Support non-constant texture offsets on non-NVIDIA gpus

This commit is contained in:
gdkchan 2019-12-11 03:54:18 -03:00 committed by Thog
parent 3323a3a042
commit f2c85c5d58
11 changed files with 319 additions and 33 deletions

View File

@ -3,6 +3,7 @@ namespace Ryujinx.Graphics.GAL
public struct Capabilities public struct Capabilities
{ {
public bool SupportsAstcCompression { get; } public bool SupportsAstcCompression { get; }
public bool SupportsNonConstantTextureOffset { get; }
public int MaximumViewportDimensions { get; } public int MaximumViewportDimensions { get; }
public int MaximumComputeSharedMemorySize { get; } public int MaximumComputeSharedMemorySize { get; }
@ -10,11 +11,13 @@ namespace Ryujinx.Graphics.GAL
public Capabilities( public Capabilities(
bool supportsAstcCompression, bool supportsAstcCompression,
bool supportsNonConstantTextureOffset,
int maximumViewportDimensions, int maximumViewportDimensions,
int maximumComputeSharedMemorySize, int maximumComputeSharedMemorySize,
int storageBufferOffsetAlignment) int storageBufferOffsetAlignment)
{ {
SupportsAstcCompression = supportsAstcCompression; SupportsAstcCompression = supportsAstcCompression;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
MaximumViewportDimensions = maximumViewportDimensions; MaximumViewportDimensions = maximumViewportDimensions;
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
StorageBufferOffsetAlignment = storageBufferOffsetAlignment; StorageBufferOffsetAlignment = storageBufferOffsetAlignment;

View File

@ -355,7 +355,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
return new ShaderCapabilities( return new ShaderCapabilities(
_context.Capabilities.MaximumViewportDimensions, _context.Capabilities.MaximumViewportDimensions,
_context.Capabilities.MaximumComputeSharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize,
_context.Capabilities.StorageBufferOffsetAlignment); _context.Capabilities.StorageBufferOffsetAlignment,
_context.Capabilities.SupportsNonConstantTextureOffset);
} }
} }
} }

View File

@ -11,7 +11,10 @@ namespace Ryujinx.Graphics.OpenGL
private static Lazy<int> _maximumComputeSharedMemorySize = new Lazy<int>(() => GetLimit(All.MaxComputeSharedMemorySize)); private static Lazy<int> _maximumComputeSharedMemorySize = new Lazy<int>(() => GetLimit(All.MaxComputeSharedMemorySize));
private static Lazy<int> _storageBufferOffsetAlignment = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment)); private static Lazy<int> _storageBufferOffsetAlignment = new Lazy<int>(() => GetLimit(All.ShaderStorageBufferOffsetAlignment));
private static Lazy<bool> _isNvidiaDriver = new Lazy<bool>(() => IsNvidiaDriver());
public static bool SupportsAstcCompression => _supportsAstcCompression.Value; public static bool SupportsAstcCompression => _supportsAstcCompression.Value;
public static bool SupportsNonConstantTextureOffset => _isNvidiaDriver.Value;
public static int MaximumViewportDimensions => _maximumViewportDimensions.Value; public static int MaximumViewportDimensions => _maximumViewportDimensions.Value;
public static int MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value; public static int MaximumComputeSharedMemorySize => _maximumComputeSharedMemorySize.Value;
@ -36,5 +39,10 @@ namespace Ryujinx.Graphics.OpenGL
{ {
return GL.GetInteger((GetPName)name); return GL.GetInteger((GetPName)name);
} }
private static bool IsNvidiaDriver()
{
return GL.GetString(StringName.Vendor).Equals("NVIDIA Corporation");
}
} }
} }

View File

@ -63,6 +63,7 @@ namespace Ryujinx.Graphics.OpenGL
{ {
return new Capabilities( return new Capabilities(
HwCapabilities.SupportsAstcCompression, HwCapabilities.SupportsAstcCompression,
HwCapabilities.SupportsNonConstantTextureOffset,
HwCapabilities.MaximumViewportDimensions, HwCapabilities.MaximumViewportDimensions,
HwCapabilities.MaximumComputeSharedMemorySize, HwCapabilities.MaximumComputeSharedMemorySize,
HwCapabilities.StorageBufferOffsetAlignment); HwCapabilities.StorageBufferOffsetAlignment);

View File

@ -133,6 +133,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
case Instruction.LoadStorage: case Instruction.LoadStorage:
return InstGenMemory.LoadStorage(context, operation); return InstGenMemory.LoadStorage(context, operation);
case Instruction.Lod:
return InstGenMemory.Lod(context, operation);
case Instruction.PackHalf2x16: case Instruction.PackHalf2x16:
return InstGenPacking.PackHalf2x16(context, operation); return InstGenPacking.PackHalf2x16(context, operation);

View File

@ -73,6 +73,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
Add(Instruction.LoadLocal, InstType.Special); Add(Instruction.LoadLocal, InstType.Special);
Add(Instruction.LoadShared, InstType.Special); Add(Instruction.LoadShared, InstType.Special);
Add(Instruction.LoadStorage, InstType.Special); Add(Instruction.LoadStorage, InstType.Special);
Add(Instruction.Lod, InstType.Special);
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2"); Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9); Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^^", 10); Add(Instruction.LogicalExclusiveOr, InstType.OpBinaryCom, "^^", 10);

View File

@ -148,6 +148,48 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage); return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
} }
public static string Lod(CodeGenContext context, AstOperation operation)
{
AstTextureOperation texOp = (AstTextureOperation)operation;
int coordsCount = texOp.Type.GetDimensions();
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
string indexExpr = null;
if (isIndexed)
{
indexExpr = GetSoureExpr(context, texOp.GetSource(0), VariableType.S32);
}
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
int coordsIndex = isBindless || isIndexed ? 1 : 0;
string coordsExpr;
if (coordsCount > 1)
{
string[] elems = new string[coordsCount];
for (int index = 0; index < coordsCount; index++)
{
elems[index] = GetSoureExpr(context, texOp.GetSource(coordsIndex + index), VariableType.F32);
}
coordsExpr = "vec" + coordsCount + "(" + string.Join(", ", elems) + ")";
}
else
{
coordsExpr = GetSoureExpr(context, texOp.GetSource(coordsIndex), VariableType.F32);
}
return $"textureQueryLod({samplerName}, {coordsExpr}){GetMask(texOp.Index)}";
}
public static string StoreLocal(CodeGenContext context, AstOperation operation) public static string StoreLocal(CodeGenContext context, AstOperation operation)
{ {
return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName); return StoreLocalOrShared(context, operation, DefaultNames.LocalMemoryName);
@ -359,17 +401,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
} }
} }
if (hasExtraCompareArg)
{
Append(Src(VariableType.F32));
}
if (hasDerivatives) if (hasDerivatives)
{ {
Append(AssembleDerivativesVector(coordsCount)); // dPdx Append(AssembleDerivativesVector(coordsCount)); // dPdx
Append(AssembleDerivativesVector(coordsCount)); // dPdy Append(AssembleDerivativesVector(coordsCount)); // dPdy
} }
if (hasExtraCompareArg)
{
Append(Src(VariableType.F32));
}
if (isMultisample) if (isMultisample)
{ {
Append(Src(VariableType.S32)); Append(Src(VariableType.S32));
@ -446,11 +488,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr); string samplerName = OperandManager.GetSamplerName(context.Config.Stage, texOp, indexExpr);
IAstNode src0 = operation.GetSource(isBindless || isIndexed ? 1 : 0); int lodSrcIndex = isBindless || isIndexed ? 1 : 0;
string src0Expr = GetSoureExpr(context, src0, GetSrcVarType(operation.Inst, 0)); IAstNode lod = operation.GetSource(lodSrcIndex);
return $"textureSize({samplerName}, {src0Expr}){GetMask(texOp.Index)}"; string lodExpr = GetSoureExpr(context, lod, GetSrcVarType(operation.Inst, lodSrcIndex));
return $"textureSize({samplerName}, {lodExpr}){GetMask(texOp.Index)}";
} }
private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage) private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)

View File

@ -71,6 +71,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
LoadLocal, LoadLocal,
LoadShared, LoadShared,
LoadStorage, LoadStorage,
Lod,
LogarithmB2, LogarithmB2,
LogicalAnd, LogicalAnd,
LogicalExclusiveOr, LogicalExclusiveOr,

View File

@ -3,22 +3,25 @@ namespace Ryujinx.Graphics.Shader
public struct ShaderCapabilities public struct ShaderCapabilities
{ {
// Initialize with default values for Maxwell. // Initialize with default values for Maxwell.
private static readonly ShaderCapabilities _default = new ShaderCapabilities(32768, 49152, 16); private static readonly ShaderCapabilities _default = new ShaderCapabilities(0x8000, 0xc000, 16, true);
public static ShaderCapabilities Default => _default; public static ShaderCapabilities Default => _default;
public int MaximumViewportDimensions { get; } public int MaximumViewportDimensions { get; }
public int MaximumComputeSharedMemorySize { get; } public int MaximumComputeSharedMemorySize { get; }
public int StorageBufferOffsetAlignment { get; } public int StorageBufferOffsetAlignment { get; }
public bool SupportsNonConstantTextureOffset { get; }
public ShaderCapabilities( public ShaderCapabilities(
int maximumViewportDimensions, int maximumViewportDimensions,
int maximumComputeSharedMemorySize, int maximumComputeSharedMemorySize,
int storageBufferOffsetAlignment) int storageBufferOffsetAlignment,
bool supportsNonConstantTextureOffset)
{ {
MaximumViewportDimensions = maximumViewportDimensions; MaximumViewportDimensions = maximumViewportDimensions;
MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize; MaximumComputeSharedMemorySize = maximumComputeSharedMemorySize;
StorageBufferOffsetAlignment = storageBufferOffsetAlignment; StorageBufferOffsetAlignment = storageBufferOffsetAlignment;
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
} }
} }
} }

View File

@ -85,6 +85,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
Add(Instruction.LoadLocal, VariableType.U32, VariableType.S32); Add(Instruction.LoadLocal, VariableType.U32, VariableType.S32);
Add(Instruction.LoadShared, VariableType.U32, VariableType.S32); Add(Instruction.LoadShared, VariableType.U32, VariableType.S32);
Add(Instruction.LoadStorage, VariableType.U32, VariableType.S32, VariableType.S32); Add(Instruction.LoadStorage, VariableType.U32, VariableType.S32, VariableType.S32);
Add(Instruction.Lod, VariableType.F32);
Add(Instruction.LogarithmB2, VariableType.Scalar, VariableType.Scalar); Add(Instruction.LogarithmB2, VariableType.Scalar, VariableType.Scalar);
Add(Instruction.LogicalAnd, VariableType.Bool, VariableType.Bool, VariableType.Bool); Add(Instruction.LogicalAnd, VariableType.Bool, VariableType.Bool, VariableType.Bool);
Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool); Add(Instruction.LogicalExclusiveOr, VariableType.Bool, VariableType.Bool, VariableType.Bool);
@ -139,9 +140,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
{ {
// TODO: Return correct type depending on source index, // TODO: Return correct type depending on source index,
// that can improve the decompiler output. // that can improve the decompiler output.
if (inst == Instruction.TextureSample || if (
inst == Instruction.ImageLoad || inst == Instruction.ImageLoad ||
inst == Instruction.ImageStore) inst == Instruction.ImageStore ||
inst == Instruction.Lod ||
inst == Instruction.TextureSample)
{ {
return VariableType.F32; return VariableType.F32;
} }

View File

@ -1,5 +1,6 @@
using Ryujinx.Graphics.Shader.IntermediateRepresentation; using Ryujinx.Graphics.Shader.IntermediateRepresentation;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper; using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
using static Ryujinx.Graphics.Shader.Translation.GlobalMemory; using static Ryujinx.Graphics.Shader.Translation.GlobalMemory;
@ -23,13 +24,18 @@ namespace Ryujinx.Graphics.Shader.Translation
if (UsesGlobalMemory(operation.Inst)) if (UsesGlobalMemory(operation.Inst))
{ {
node = LowerGlobal(node, config); node = RewriteGlobalAccess(node, config);
}
if (!config.Capabilities.SupportsNonConstantTextureOffset && operation.Inst == Instruction.TextureSample)
{
node = RewriteTextureSample(node);
} }
} }
} }
} }
private static LinkedListNode<INode> LowerGlobal(LinkedListNode<INode> node, ShaderConfig config) private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config)
{ {
Operation operation = (Operation)node.Value; Operation operation = (Operation)node.Value;
@ -117,5 +123,217 @@ namespace Ryujinx.Graphics.Shader.Translation
return node; return node;
} }
private static LinkedListNode<INode> RewriteTextureSample(LinkedListNode<INode> node)
{
TextureOperation texOp = (TextureOperation)node.Value;
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
if (!(hasOffset || hasOffsets))
{
return node;
}
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
bool hasDerivatives = (texOp.Flags & TextureFlags.Derivatives) != 0;
bool hasLodBias = (texOp.Flags & TextureFlags.LodBias) != 0;
bool hasLodLevel = (texOp.Flags & TextureFlags.LodLevel) != 0;
bool isArray = (texOp.Type & SamplerType.Array) != 0;
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
bool isMultisample = (texOp.Type & SamplerType.Multisample) != 0;
bool isShadow = (texOp.Type & SamplerType.Shadow) != 0;
int coordsCount = texOp.Type.GetDimensions();
int offsetsCount = coordsCount * (hasOffsets ? 4 : 1);
Operand[] offsets = new Operand[offsetsCount];
Operand[] sources = new Operand[texOp.SourcesCount - offsetsCount];
int srcIndex = 0;
int dstIndex = 0;
int copyCount = 0;
if (isBindless || isIndexed)
{
copyCount++;
}
Operand[] lodSources = new Operand[copyCount + coordsCount];
for (int index = 0; index < lodSources.Length; index++)
{
lodSources[index] = texOp.GetSource(index);
}
copyCount += coordsCount;
if (isArray)
{
copyCount++;
}
if (isShadow)
{
copyCount++;
}
if (hasDerivatives)
{
copyCount += coordsCount * 2;
}
if (isMultisample)
{
copyCount++;
}
else if (hasLodLevel)
{
copyCount++;
}
for (int index = 0; index < copyCount; index++)
{
sources[dstIndex++] = texOp.GetSource(srcIndex++);
}
bool areAllOffsetsConstant = true;
for (int index = 0; index < offsetsCount; index++)
{
Operand offset = texOp.GetSource(srcIndex++);
areAllOffsetsConstant &= offset.Type == OperandType.Constant;
offsets[index] = offset;
}
if (areAllOffsetsConstant)
{
return node;
}
if (hasLodBias)
{
sources[dstIndex++] = texOp.GetSource(srcIndex++);
}
if (isGather && !isShadow)
{
sources[dstIndex++] = texOp.GetSource(srcIndex++);
}
Operand Int(Operand value)
{
Operand res = Local();
node.List.AddBefore(node, new Operation(Instruction.ConvertFPToS32, res, value));
return res;
}
Operand Float(Operand value)
{
Operand res = Local();
node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP, res, value));
return res;
}
Operand lod = Local();
node.List.AddBefore(node, new TextureOperation(
Instruction.Lod,
texOp.Type,
texOp.Flags,
texOp.Handle,
1,
lod,
lodSources));
int coordsIndex = isBindless || isIndexed ? 1 : 0;
for (int index = 0; index < coordsCount; index++)
{
Operand coordSize = Local();
Operand[] texSizeSources;
if (isBindless || isIndexed)
{
texSizeSources = new Operand[] { sources[0], Int(lod) };
}
else
{
texSizeSources = new Operand[] { Int(lod) };
}
node.List.AddBefore(node, new TextureOperation(
Instruction.TextureSize,
texOp.Type,
texOp.Flags,
texOp.Handle,
index,
coordSize,
texSizeSources));
Operand offset = Local();
Operand intOffset = offsets[index + (hasOffsets ? texOp.Index * coordsCount : 0)];
node.List.AddBefore(node, new Operation(Instruction.FP | Instruction.Divide, offset, Float(intOffset), Float(coordSize)));
Operand source = sources[coordsIndex + index];
Operand coordPlusOffset = Local();
node.List.AddBefore(node, new Operation(Instruction.FP | Instruction.Add, coordPlusOffset, source, offset));
sources[coordsIndex + index] = coordPlusOffset;
}
int componentIndex;
if (isGather && !isShadow)
{
Operand gatherComponent = sources[dstIndex - 1];
Debug.Assert(gatherComponent.Type == OperandType.Constant);
componentIndex = gatherComponent.Value;
}
else
{
componentIndex = texOp.Index;
}
TextureOperation newTexOp = new TextureOperation(
Instruction.TextureSample,
texOp.Type,
texOp.Flags & ~(TextureFlags.Offset | TextureFlags.Offsets),
texOp.Handle,
componentIndex,
texOp.Dest,
sources);
for (int index = 0; index < texOp.SourcesCount; index++)
{
texOp.SetSource(index, null);
}
LinkedListNode<INode> oldNode = node;
node = node.List.AddBefore(node, newTexOp);
node.List.Remove(oldNode);
return node;
}
} }
} }