mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-07-22 04:37:11 +02:00
Implement transform feedback emulation for hardware without native support (#5080)
* Implement transform feedback emulation for hardware without native support * Stop doing some useless buffer updates and account for non-zero base instance * Reduce redundant updates even more * Update descriptor init logic to account for ResourceLayout * Fix transform feedback and storage buffers not being updated in some cases * Shader cache version bump * PR feedback * SetInstancedDrawVertexCount must be always called after UpdateState * Minor typo
This commit is contained in:
parent
0e95a8271a
commit
eb0bb36bbf
21 changed files with 375 additions and 68 deletions
|
@ -37,7 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
ShaderSpecializationState oldSpecState,
|
||||
ShaderSpecializationState newSpecState,
|
||||
ResourceCounts counts,
|
||||
int stageIndex) : base(context, counts, stageIndex)
|
||||
int stageIndex) : base(context, counts, stageIndex, oldSpecState.TransformFeedbackDescriptors != null)
|
||||
{
|
||||
_data = data;
|
||||
_cb1Data = cb1Data;
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 5044;
|
||||
private const uint CodeGenVersion = 5080;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@ -368,7 +368,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
|
||||
if (hostCode != null)
|
||||
{
|
||||
ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(context, shaders, specState.PipelineState);
|
||||
ShaderInfo shaderInfo = ShaderInfoBuilder.BuildForCache(
|
||||
context,
|
||||
shaders,
|
||||
specState.PipelineState,
|
||||
specState.TransformFeedbackDescriptors != null);
|
||||
|
||||
IProgram hostProgram;
|
||||
|
||||
|
|
|
@ -491,7 +491,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||
{
|
||||
ShaderSource[] shaderSources = new ShaderSource[compilation.TranslatedStages.Length];
|
||||
|
||||
ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context);
|
||||
ShaderInfoBuilder shaderInfoBuilder = new ShaderInfoBuilder(_context, compilation.SpecializationState.TransformFeedbackDescriptors != null);
|
||||
|
||||
for (int index = 0; index < compilation.TranslatedStages.Length; index++)
|
||||
{
|
||||
|
|
|
@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
GpuContext context,
|
||||
GpuChannel channel,
|
||||
GpuAccessorState state,
|
||||
int stageIndex) : base(context, state.ResourceCounts, stageIndex)
|
||||
int stageIndex) : base(context, state.ResourceCounts, stageIndex, state.TransformFeedbackDescriptors != null)
|
||||
{
|
||||
_isVulkan = context.Capabilities.Api == TargetApi.Vulkan;
|
||||
_channel = channel;
|
||||
|
@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="context">GPU context</param>
|
||||
/// <param name="channel">GPU channel</param>
|
||||
/// <param name="state">Current GPU state</param>
|
||||
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context, state.ResourceCounts, 0)
|
||||
public GpuAccessor(GpuContext context, GpuChannel channel, GpuAccessorState state) : base(context, state.ResourceCounts, 0, false)
|
||||
{
|
||||
_channel = channel;
|
||||
_state = state;
|
||||
|
|
|
@ -17,40 +17,56 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
private readonly ResourceCounts _resourceCounts;
|
||||
private readonly int _stageIndex;
|
||||
|
||||
private readonly int _reservedConstantBuffers;
|
||||
private readonly int _reservedStorageBuffers;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU accessor.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context</param>
|
||||
public GpuAccessorBase(GpuContext context, ResourceCounts resourceCounts, int stageIndex)
|
||||
/// <param name="resourceCounts">Counter of GPU resources used by the shader</param>
|
||||
/// <param name="stageIndex">Index of the shader stage, 0 for compute</param>
|
||||
/// <param name="tfEnabled">Indicates if the current graphics shader is used with transform feedback enabled</param>
|
||||
public GpuAccessorBase(GpuContext context, ResourceCounts resourceCounts, int stageIndex, bool tfEnabled)
|
||||
{
|
||||
_context = context;
|
||||
_resourceCounts = resourceCounts;
|
||||
_stageIndex = stageIndex;
|
||||
|
||||
_reservedConstantBuffers = 1; // For the support buffer.
|
||||
_reservedStorageBuffers = !context.Capabilities.SupportsTransformFeedback && tfEnabled ? 5 : 0;
|
||||
}
|
||||
|
||||
public int QueryBindingConstantBuffer(int index)
|
||||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
// We need to start counting from 1 since binding 0 is reserved for the support uniform buffer.
|
||||
return GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer") + 1;
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumUniformBuffersPerStage, "Uniform buffer");
|
||||
}
|
||||
else
|
||||
{
|
||||
return _resourceCounts.UniformBuffersCount++;
|
||||
binding = _resourceCounts.UniformBuffersCount++;
|
||||
}
|
||||
|
||||
return binding + _reservedConstantBuffers;
|
||||
}
|
||||
|
||||
public int QueryBindingStorageBuffer(int index)
|
||||
{
|
||||
int binding;
|
||||
|
||||
if (_context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
return GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
||||
binding = GetBindingFromIndex(index, _context.Capabilities.MaximumStorageBuffersPerStage, "Storage buffer");
|
||||
}
|
||||
else
|
||||
{
|
||||
return _resourceCounts.StorageBuffersCount++;
|
||||
binding = _resourceCounts.StorageBuffersCount++;
|
||||
}
|
||||
|
||||
return binding + _reservedStorageBuffers;
|
||||
}
|
||||
|
||||
public int QueryBindingTexture(int index, bool isBuffer)
|
||||
|
@ -149,6 +165,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
|
||||
|
||||
public bool QueryHostSupportsTransformFeedback() => _context.Capabilities.SupportsTransformFeedback;
|
||||
|
||||
public bool QueryHostSupportsViewportIndexVertexTessellation() => _context.Capabilities.SupportsViewportIndexVertexTessellation;
|
||||
|
||||
public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask;
|
||||
|
|
|
@ -24,13 +24,5 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// Total of images used by the shaders.
|
||||
/// </summary>
|
||||
public int ImagesCount;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the shader resource counts class.
|
||||
/// </summary>
|
||||
public ResourceCounts()
|
||||
{
|
||||
UniformBuffersCount = 1; // The first binding is reserved for the support buffer.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -362,7 +362,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
|
||||
TranslatorContext previousStage = null;
|
||||
|
||||
ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context);
|
||||
ShaderInfoBuilder infoBuilder = new ShaderInfoBuilder(_context, transformFeedbackDescriptors != null);
|
||||
|
||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||
{
|
||||
|
|
|
@ -16,15 +16,24 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
private const int TextureSetIndex = 2;
|
||||
private const int ImageSetIndex = 3;
|
||||
|
||||
private const ResourceStages SupportBufferStags =
|
||||
private const ResourceStages SupportBufferStages =
|
||||
ResourceStages.Compute |
|
||||
ResourceStages.Vertex |
|
||||
ResourceStages.Fragment;
|
||||
|
||||
private const ResourceStages VtgStages =
|
||||
ResourceStages.Vertex |
|
||||
ResourceStages.TessellationControl |
|
||||
ResourceStages.TessellationEvaluation |
|
||||
ResourceStages.Geometry;
|
||||
|
||||
private readonly GpuContext _context;
|
||||
|
||||
private int _fragmentOutputMap;
|
||||
|
||||
private readonly int _reservedConstantBuffers;
|
||||
private readonly int _reservedStorageBuffers;
|
||||
|
||||
private readonly List<ResourceDescriptor>[] _resourceDescriptors;
|
||||
private readonly List<ResourceUsage>[] _resourceUsages;
|
||||
|
||||
|
@ -32,7 +41,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// Creates a new shader info builder.
|
||||
/// </summary>
|
||||
/// <param name="context">GPU context that owns the shaders that will be added to the builder</param>
|
||||
public ShaderInfoBuilder(GpuContext context)
|
||||
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
||||
public ShaderInfoBuilder(GpuContext context, bool tfEnabled)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
|
@ -47,7 +57,22 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
_resourceUsages[index] = new();
|
||||
}
|
||||
|
||||
AddDescriptor(SupportBufferStags, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
|
||||
AddDescriptor(SupportBufferStages, ResourceType.UniformBuffer, UniformSetIndex, 0, 1);
|
||||
|
||||
_reservedConstantBuffers = 1; // For the support buffer.
|
||||
|
||||
if (!context.Capabilities.SupportsTransformFeedback && tfEnabled)
|
||||
{
|
||||
_reservedStorageBuffers = 5;
|
||||
|
||||
AddDescriptor(VtgStages, ResourceType.StorageBuffer, StorageSetIndex, 0, 5);
|
||||
AddUsage(VtgStages, ResourceType.StorageBuffer, ResourceAccess.Read, StorageSetIndex, 0, 1);
|
||||
AddUsage(VtgStages, ResourceType.StorageBuffer, ResourceAccess.Write, StorageSetIndex, 1, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
_reservedStorageBuffers = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -86,8 +111,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
int texturesPerStage = (int)_context.Capabilities.MaximumTexturesPerStage;
|
||||
int imagesPerStage = (int)_context.Capabilities.MaximumImagesPerStage;
|
||||
|
||||
int uniformBinding = 1 + stageIndex * uniformsPerStage;
|
||||
int storageBinding = stageIndex * storagesPerStage;
|
||||
int uniformBinding = _reservedConstantBuffers + stageIndex * uniformsPerStage;
|
||||
int storageBinding = _reservedStorageBuffers + stageIndex * storagesPerStage;
|
||||
int textureBinding = stageIndex * texturesPerStage * 2;
|
||||
int imageBinding = stageIndex * imagesPerStage * 2;
|
||||
|
||||
|
@ -133,6 +158,23 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
AddDescriptor(stages, type2, setIndex, binding + count, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds buffer usage information to the list of usages.
|
||||
/// </summary>
|
||||
/// <param name="stages">Shader stages where the resource is used</param>
|
||||
/// <param name="type">Type of the resource</param>
|
||||
/// <param name="access">How the resource is accessed by the shader stages where it is used</param>
|
||||
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||
/// <param name="count">Number of resources bound at the binding location</param>
|
||||
private void AddUsage(ResourceStages stages, ResourceType type, ResourceAccess access, int setIndex, int binding, int count)
|
||||
{
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
_resourceUsages[setIndex].Add(new ResourceUsage(binding + index, type, stages, access));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds buffer usage information to the list of usages.
|
||||
/// </summary>
|
||||
|
@ -212,10 +254,15 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <param name="context">GPU context that owns the shaders</param>
|
||||
/// <param name="programs">Shaders from the disk cache</param>
|
||||
/// <param name="pipeline">Optional pipeline for background compilation</param>
|
||||
/// <param name="tfEnabled">Indicates if the graphics shader is used with transform feedback enabled</param>
|
||||
/// <returns>Shader information</returns>
|
||||
public static ShaderInfo BuildForCache(GpuContext context, IEnumerable<CachedShaderStage> programs, ProgramPipelineState? pipeline)
|
||||
public static ShaderInfo BuildForCache(
|
||||
GpuContext context,
|
||||
IEnumerable<CachedShaderStage> programs,
|
||||
ProgramPipelineState? pipeline,
|
||||
bool tfEnabled)
|
||||
{
|
||||
ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
|
||||
ShaderInfoBuilder builder = new ShaderInfoBuilder(context, tfEnabled);
|
||||
|
||||
foreach (CachedShaderStage program in programs)
|
||||
{
|
||||
|
@ -237,7 +284,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||
/// <returns>Shader information</returns>
|
||||
public static ShaderInfo BuildForCompute(GpuContext context, ShaderProgramInfo info, bool fromCache = false)
|
||||
{
|
||||
ShaderInfoBuilder builder = new ShaderInfoBuilder(context);
|
||||
ShaderInfoBuilder builder = new ShaderInfoBuilder(context, tfEnabled: false);
|
||||
|
||||
builder.AddStageInfo(info);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue