Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
69ced3a6e8 | ||
|
2e43d01d36 | ||
|
7373ec5792 | ||
|
de162a648b | ||
|
131baebe2a | ||
|
187372cbde |
@@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
public readonly bool SupportsFragmentShaderOrderingIntel;
|
||||
public readonly bool SupportsGeometryShaderPassthrough;
|
||||
public readonly bool SupportsImageLoadFormatted;
|
||||
public readonly bool SupportsLayerVertexTessellation;
|
||||
public readonly bool SupportsMismatchingViewFormat;
|
||||
public readonly bool SupportsCubemapView;
|
||||
public readonly bool SupportsNonConstantTextureOffset;
|
||||
@@ -55,6 +56,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
bool supportsFragmentShaderOrderingIntel,
|
||||
bool supportsGeometryShaderPassthrough,
|
||||
bool supportsImageLoadFormatted,
|
||||
bool supportsLayerVertexTessellation,
|
||||
bool supportsMismatchingViewFormat,
|
||||
bool supportsCubemapView,
|
||||
bool supportsNonConstantTextureOffset,
|
||||
@@ -86,6 +88,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
|
||||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
||||
SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
|
||||
SupportsMismatchingViewFormat = supportsMismatchingViewFormat;
|
||||
SupportsCubemapView = supportsCubemapView;
|
||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||
|
@@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
|
||||
// Since the memory manager changed, make sure we will get pools from addresses of the new memory manager.
|
||||
TextureManager.ReloadPools();
|
||||
MemoryManager.Physical.BufferCache.QueuePrune();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -77,6 +78,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
private void MemoryUnmappedHandler(object sender, UnmapEventArgs e)
|
||||
{
|
||||
TextureManager.ReloadPools();
|
||||
MemoryManager.Physical.BufferCache.QueuePrune();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _dirtyCache;
|
||||
private readonly Dictionary<ulong, BufferCacheEntry> _modifiedCache;
|
||||
private bool _pruneCaches;
|
||||
|
||||
public event Action NotifyBuffersModified;
|
||||
|
||||
@@ -136,6 +137,11 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="size">Size in bytes of the buffer</param>
|
||||
public void ForceDirty(MemoryManager memoryManager, ulong gpuVa, ulong size)
|
||||
{
|
||||
if (_pruneCaches)
|
||||
{
|
||||
Prune();
|
||||
}
|
||||
|
||||
if (!_dirtyCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
||||
result.EndGpuAddress < gpuVa + size ||
|
||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||
@@ -158,17 +164,29 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <returns>True if modified, false otherwise</returns>
|
||||
public bool CheckModified(MemoryManager memoryManager, ulong gpuVa, ulong size, out ulong outAddr)
|
||||
{
|
||||
if (!_modifiedCache.TryGetValue(gpuVa, out BufferCacheEntry result) ||
|
||||
result.EndGpuAddress < gpuVa + size ||
|
||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||
if (_pruneCaches)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(memoryManager, gpuVa, size);
|
||||
result = new BufferCacheEntry(address, gpuVa, GetBuffer(address, size));
|
||||
|
||||
_modifiedCache[gpuVa] = result;
|
||||
Prune();
|
||||
}
|
||||
|
||||
outAddr = result.Address;
|
||||
// Align the address to avoid creating too many entries on the quick lookup dictionary.
|
||||
ulong mask = BufferAlignmentMask;
|
||||
ulong alignedGpuVa = gpuVa & (~mask);
|
||||
ulong alignedEndGpuVa = (gpuVa + size + mask) & (~mask);
|
||||
|
||||
size = alignedEndGpuVa - alignedGpuVa;
|
||||
|
||||
if (!_modifiedCache.TryGetValue(alignedGpuVa, out BufferCacheEntry result) ||
|
||||
result.EndGpuAddress < alignedEndGpuVa ||
|
||||
result.UnmappedSequence != result.Buffer.UnmappedSequence)
|
||||
{
|
||||
ulong address = TranslateAndCreateBuffer(memoryManager, alignedGpuVa, size);
|
||||
result = new BufferCacheEntry(address, alignedGpuVa, GetBuffer(address, size));
|
||||
|
||||
_modifiedCache[alignedGpuVa] = result;
|
||||
}
|
||||
|
||||
outAddr = result.Address | (gpuVa & mask);
|
||||
|
||||
return result.Buffer.IsModified(result.Address, size);
|
||||
}
|
||||
@@ -435,6 +453,54 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prune any invalid entries from a quick access dictionary.
|
||||
/// </summary>
|
||||
/// <param name="dictionary">Dictionary to prune</param>
|
||||
/// <param name="toDelete">List used to track entries to delete</param>
|
||||
private void Prune(Dictionary<ulong, BufferCacheEntry> dictionary, ref List<ulong> toDelete)
|
||||
{
|
||||
foreach (var entry in dictionary)
|
||||
{
|
||||
if (entry.Value.UnmappedSequence != entry.Value.Buffer.UnmappedSequence)
|
||||
{
|
||||
(toDelete ??= new()).Add(entry.Key);
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete != null)
|
||||
{
|
||||
foreach (ulong entry in toDelete)
|
||||
{
|
||||
dictionary.Remove(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prune any invalid entries from the quick access dictionaries.
|
||||
/// </summary>
|
||||
private void Prune()
|
||||
{
|
||||
List<ulong> toDelete = null;
|
||||
|
||||
Prune(_dirtyCache, ref toDelete);
|
||||
|
||||
toDelete?.Clear();
|
||||
|
||||
Prune(_modifiedCache, ref toDelete);
|
||||
|
||||
_pruneCaches = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queues a prune of invalid entries the next time a dictionary cache is accessed.
|
||||
/// </summary>
|
||||
public void QueuePrune()
|
||||
{
|
||||
_pruneCaches = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes all buffers in the cache.
|
||||
/// It's an error to use the buffer manager after disposal.
|
||||
|
@@ -325,13 +325,15 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
public void ReregisterRanges(Action<ulong, ulong> rangeAction)
|
||||
{
|
||||
ref var ranges = ref ThreadStaticArray<BufferModifiedRange>.Get();
|
||||
int count;
|
||||
|
||||
// Range list must be consistent for this operation.
|
||||
lock (_lock)
|
||||
{
|
||||
if (ranges.Length < Count)
|
||||
count = Count;
|
||||
if (ranges.Length < count)
|
||||
{
|
||||
Array.Resize(ref ranges, Count);
|
||||
Array.Resize(ref ranges, count);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
@@ -342,7 +344,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
}
|
||||
|
||||
ulong currentSync = _context.SyncNumber;
|
||||
for (int i = 0; i < Count; i++)
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
BufferModifiedRange range = ranges[i];
|
||||
if (range.SyncNumber != currentSync)
|
||||
|
@@ -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 = 3863;
|
||||
private const uint CodeGenVersion = 3868;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
@@ -379,7 +379,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
|
||||
if (context.Capabilities.Api == TargetApi.Vulkan)
|
||||
{
|
||||
ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode, isCompute);
|
||||
ShaderSource[] shaderSources = ShaderBinarySerializer.Unpack(shaders, hostCode);
|
||||
|
||||
hostProgram = context.Renderer.CreateProgram(shaderSources, shaderInfo);
|
||||
}
|
||||
|
@@ -636,6 +636,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
|
||||
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
|
||||
|
||||
TranslatorContext previousStage = null;
|
||||
|
||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||
{
|
||||
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
||||
@@ -668,6 +670,16 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
{
|
||||
translatedStages.Add(program);
|
||||
}
|
||||
|
||||
previousStage = currentStage;
|
||||
}
|
||||
else if (
|
||||
previousStage != null &&
|
||||
previousStage.LayerOutputWritten &&
|
||||
stageIndex == 3 &&
|
||||
!_context.Capabilities.SupportsLayerVertexTessellation)
|
||||
{
|
||||
translatedStages.Add(previousStage.GenerateGeometryPassthrough());
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
@@ -12,8 +14,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
using MemoryStream output = new MemoryStream();
|
||||
using BinaryWriter writer = new BinaryWriter(output);
|
||||
|
||||
writer.Write(sources.Length);
|
||||
|
||||
for (int i = 0; i < sources.Length; i++)
|
||||
{
|
||||
writer.Write((int)sources[i].Stage);
|
||||
writer.Write(sources[i].BinaryCode.Length);
|
||||
writer.Write(sources[i].BinaryCode);
|
||||
}
|
||||
@@ -21,29 +26,40 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code, bool compute)
|
||||
public static ShaderSource[] Unpack(CachedShaderStage[] stages, byte[] code)
|
||||
{
|
||||
using MemoryStream input = new MemoryStream(code);
|
||||
using BinaryReader reader = new BinaryReader(input);
|
||||
|
||||
List<ShaderSource> output = new List<ShaderSource>();
|
||||
|
||||
for (int i = compute ? 0 : 1; i < stages.Length; i++)
|
||||
int count = reader.ReadInt32();
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
CachedShaderStage stage = stages[i];
|
||||
|
||||
if (stage == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ShaderStage stage = (ShaderStage)reader.ReadInt32();
|
||||
int binaryCodeLength = reader.ReadInt32();
|
||||
byte[] binaryCode = reader.ReadBytes(binaryCodeLength);
|
||||
|
||||
output.Add(new ShaderSource(binaryCode, ShaderCache.GetBindings(stage.Info), stage.Info.Stage, TargetLanguage.Spirv));
|
||||
output.Add(new ShaderSource(binaryCode, GetBindings(stages, stage), stage, TargetLanguage.Spirv));
|
||||
}
|
||||
|
||||
return output.ToArray();
|
||||
}
|
||||
|
||||
private static ShaderBindings GetBindings(CachedShaderStage[] stages, ShaderStage stage)
|
||||
{
|
||||
for (int i = 0; i < stages.Length; i++)
|
||||
{
|
||||
CachedShaderStage currentStage = stages[i];
|
||||
|
||||
if (currentStage != null && currentStage.Info.Stage == stage && currentStage.Info != null)
|
||||
{
|
||||
return ShaderCache.GetBindings(currentStage.Info);
|
||||
}
|
||||
}
|
||||
|
||||
return new ShaderBindings(Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>(), Array.Empty<int>());
|
||||
}
|
||||
}
|
||||
}
|
@@ -128,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
|
||||
|
||||
public bool QueryHostSupportsLayerVertexTessellation() => _context.Capabilities.SupportsLayerVertexTessellation;
|
||||
|
||||
public bool QueryHostSupportsNonConstantTextureOffset() => _context.Capabilities.SupportsNonConstantTextureOffset;
|
||||
|
||||
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
|
||||
|
@@ -356,6 +356,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
||||
List<ShaderSource> shaderSources = new List<ShaderSource>();
|
||||
|
||||
TranslatorContext previousStage = null;
|
||||
|
||||
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
|
||||
{
|
||||
TranslatorContext currentStage = translatorContexts[stageIndex + 1];
|
||||
@@ -392,6 +394,16 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
shaderSources.Add(CreateShaderSource(program));
|
||||
}
|
||||
|
||||
previousStage = currentStage;
|
||||
}
|
||||
else if (
|
||||
previousStage != null &&
|
||||
previousStage.LayerOutputWritten &&
|
||||
stageIndex == 3 &&
|
||||
!_context.Capabilities.SupportsLayerVertexTessellation)
|
||||
{
|
||||
shaderSources.Add(CreateShaderSource(previousStage.GenerateGeometryPassthrough()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
private static readonly Lazy<bool> _supportsQuads = new Lazy<bool>(SupportsQuadsCheck);
|
||||
private static readonly Lazy<bool> _supportsSeamlessCubemapPerTexture = new Lazy<bool>(() => HasExtension("GL_ARB_seamless_cubemap_per_texture"));
|
||||
private static readonly Lazy<bool> _supportsShaderBallot = new Lazy<bool>(() => HasExtension("GL_ARB_shader_ballot"));
|
||||
private static readonly Lazy<bool> _supportsShaderViewportLayerArray = new Lazy<bool>(() => HasExtension("GL_ARB_shader_viewport_layer_array"));
|
||||
private static readonly Lazy<bool> _supportsTextureCompressionBptc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_bptc"));
|
||||
private static readonly Lazy<bool> _supportsTextureCompressionRgtc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_rgtc"));
|
||||
private static readonly Lazy<bool> _supportsTextureCompressionS3tc = new Lazy<bool>(() => HasExtension("GL_EXT_texture_compression_s3tc"));
|
||||
@@ -61,6 +62,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
public static bool SupportsQuads => _supportsQuads.Value;
|
||||
public static bool SupportsSeamlessCubemapPerTexture => _supportsSeamlessCubemapPerTexture.Value;
|
||||
public static bool SupportsShaderBallot => _supportsShaderBallot.Value;
|
||||
public static bool SupportsShaderViewportLayerArray => _supportsShaderViewportLayerArray.Value;
|
||||
public static bool SupportsTextureCompressionBptc => _supportsTextureCompressionBptc.Value;
|
||||
public static bool SupportsTextureCompressionRgtc => _supportsTextureCompressionRgtc.Value;
|
||||
public static bool SupportsTextureCompressionS3tc => _supportsTextureCompressionS3tc.Value;
|
||||
|
@@ -117,12 +117,13 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
|
||||
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
|
||||
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
||||
supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||
supportsMismatchingViewFormat: HwCapabilities.SupportsMismatchingViewFormat,
|
||||
supportsCubemapView: true,
|
||||
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||
supportsViewportIndex: true,
|
||||
supportsViewportIndex: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
||||
|
@@ -258,6 +258,15 @@ namespace Ryujinx.Graphics.Shader
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host support for writes to Layer from vertex or tessellation shader stages.
|
||||
/// </summary>
|
||||
/// <returns>True if writes to layer from vertex or tessellation are supported, false otherwise</returns>
|
||||
bool QueryHostSupportsLayerVertexTessellation()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries host GPU non-constant texture offset support.
|
||||
/// </summary>
|
||||
|
@@ -278,13 +278,21 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
|
||||
private static int FixedFuncToUserAttribute(ShaderConfig config, int attr, bool isOutput)
|
||||
{
|
||||
if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
||||
bool supportsLayerFromVertexOrTess = config.GpuAccessor.QueryHostSupportsLayerVertexTessellation();
|
||||
int fixedStartAttr = supportsLayerFromVertexOrTess ? 0 : 1;
|
||||
|
||||
if (attr == AttributeConsts.Layer && config.Stage != ShaderStage.Geometry && !supportsLayerFromVertexOrTess)
|
||||
{
|
||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, 0, isOutput);
|
||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.Layer, 0, isOutput);
|
||||
config.SetLayerOutputAttribute(attr);
|
||||
}
|
||||
else if (attr >= AttributeConsts.FrontColorDiffuseR && attr < AttributeConsts.ClipDistance0)
|
||||
{
|
||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.FrontColorDiffuseR, fixedStartAttr, isOutput);
|
||||
}
|
||||
else if (attr >= AttributeConsts.TexCoordBase && attr < AttributeConsts.TexCoordEnd)
|
||||
{
|
||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, 4, isOutput);
|
||||
attr = FixedFuncToUserAttribute(config, attr, AttributeConsts.TexCoordBase, fixedStartAttr + 4, isOutput);
|
||||
}
|
||||
|
||||
return attr;
|
||||
|
@@ -48,6 +48,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
public int Cb1DataSize { get; private set; }
|
||||
|
||||
public bool LayerOutputWritten { get; private set; }
|
||||
public int LayerOutputAttribute { get; private set; }
|
||||
|
||||
public bool NextUsesFixedFuncAttributes { get; private set; }
|
||||
public int UsedInputAttributes { get; private set; }
|
||||
public int UsedOutputAttributes { get; private set; }
|
||||
@@ -131,6 +134,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
|
||||
}
|
||||
|
||||
public ShaderConfig(
|
||||
ShaderStage stage,
|
||||
OutputTopology outputTopology,
|
||||
int maxOutputVertices,
|
||||
IGpuAccessor gpuAccessor,
|
||||
TranslationOptions options) : this(gpuAccessor, options)
|
||||
{
|
||||
Stage = stage;
|
||||
ThreadsPerInputPrimitive = 1;
|
||||
OutputTopology = outputTopology;
|
||||
MaxOutputVertices = maxOutputVertices;
|
||||
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
||||
}
|
||||
|
||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(gpuAccessor, options)
|
||||
{
|
||||
Stage = header.Stage;
|
||||
@@ -240,6 +257,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLayerOutputAttribute(int attr)
|
||||
{
|
||||
LayerOutputWritten = true;
|
||||
LayerOutputAttribute = attr;
|
||||
}
|
||||
|
||||
public void SetInputUserAttributeFixedFunc(int index)
|
||||
{
|
||||
UsedInputAttributes |= 1 << index;
|
||||
@@ -694,5 +717,20 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
||||
}
|
||||
|
||||
public ShaderProgramInfo CreateProgramInfo()
|
||||
{
|
||||
return new ShaderProgramInfo(
|
||||
GetConstantBufferDescriptors(),
|
||||
GetStorageBufferDescriptors(),
|
||||
GetTextureDescriptors(),
|
||||
GetImageDescriptors(),
|
||||
Stage,
|
||||
UsedFeatures.HasFlag(FeatureFlags.InstanceId),
|
||||
UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
|
||||
UsedFeatures.HasFlag(FeatureFlags.RtLayer),
|
||||
ClipDistancesWritten,
|
||||
OmapTargets);
|
||||
}
|
||||
}
|
||||
}
|
@@ -79,17 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
||||
|
||||
var info = new ShaderProgramInfo(
|
||||
config.GetConstantBufferDescriptors(),
|
||||
config.GetStorageBufferDescriptors(),
|
||||
config.GetTextureDescriptors(),
|
||||
config.GetImageDescriptors(),
|
||||
config.Stage,
|
||||
config.UsedFeatures.HasFlag(FeatureFlags.InstanceId),
|
||||
config.UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
|
||||
config.UsedFeatures.HasFlag(FeatureFlags.RtLayer),
|
||||
config.ClipDistancesWritten,
|
||||
config.OmapTargets);
|
||||
var info = config.CreateProgramInfo();
|
||||
|
||||
return config.Options.TargetLanguage switch
|
||||
{
|
||||
|
@@ -1,7 +1,12 @@
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.CodeGen.Glsl;
|
||||
using Ryujinx.Graphics.Shader.CodeGen.Spirv;
|
||||
using Ryujinx.Graphics.Shader.Decoders;
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
using static Ryujinx.Graphics.Shader.Translation.Translator;
|
||||
@@ -18,6 +23,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
public ShaderStage Stage => _config.Stage;
|
||||
public int Size => _config.Size;
|
||||
public int Cb1DataSize => _config.Cb1DataSize;
|
||||
public bool LayerOutputWritten => _config.LayerOutputWritten;
|
||||
|
||||
public IGpuAccessor GpuAccessor => _config.GpuAccessor;
|
||||
|
||||
@@ -149,5 +155,94 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
return Translator.Translate(code, _config);
|
||||
}
|
||||
|
||||
public ShaderProgram GenerateGeometryPassthrough()
|
||||
{
|
||||
int outputAttributesMask = _config.UsedOutputAttributes;
|
||||
int layerOutputAttr = _config.LayerOutputAttribute;
|
||||
|
||||
OutputTopology outputTopology;
|
||||
int maxOutputVertices;
|
||||
|
||||
switch (GpuAccessor.QueryPrimitiveTopology())
|
||||
{
|
||||
case InputTopology.Points:
|
||||
outputTopology = OutputTopology.PointList;
|
||||
maxOutputVertices = 1;
|
||||
break;
|
||||
case InputTopology.Lines:
|
||||
case InputTopology.LinesAdjacency:
|
||||
outputTopology = OutputTopology.LineStrip;
|
||||
maxOutputVertices = 2;
|
||||
break;
|
||||
default:
|
||||
outputTopology = OutputTopology.TriangleStrip;
|
||||
maxOutputVertices = 3;
|
||||
break;
|
||||
}
|
||||
|
||||
ShaderConfig config = new ShaderConfig(ShaderStage.Geometry, outputTopology, maxOutputVertices, GpuAccessor, _config.Options);
|
||||
|
||||
EmitterContext context = new EmitterContext(default, config, false);
|
||||
|
||||
for (int v = 0; v < maxOutputVertices; v++)
|
||||
{
|
||||
int outAttrsMask = outputAttributesMask;
|
||||
|
||||
while (outAttrsMask != 0)
|
||||
{
|
||||
int attrIndex = BitOperations.TrailingZeroCount(outAttrsMask);
|
||||
|
||||
outAttrsMask &= ~(1 << attrIndex);
|
||||
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
int attr = AttributeConsts.UserAttributeBase + attrIndex * 16 + c * 4;
|
||||
|
||||
Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
|
||||
|
||||
if (attr == layerOutputAttr)
|
||||
{
|
||||
context.Copy(Attribute(AttributeConsts.Layer), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Copy(Attribute(attr), value);
|
||||
config.SetOutputUserAttribute(attrIndex);
|
||||
}
|
||||
|
||||
config.SetInputUserAttribute(attrIndex, c);
|
||||
}
|
||||
}
|
||||
|
||||
for (int c = 0; c < 4; c++)
|
||||
{
|
||||
int attr = AttributeConsts.PositionX + c * 4;
|
||||
|
||||
Operand value = context.LoadAttribute(Const(attr), Const(0), Const(v));
|
||||
|
||||
context.Copy(Attribute(attr), value);
|
||||
}
|
||||
|
||||
context.EmitVertex();
|
||||
}
|
||||
|
||||
context.EndPrimitive();
|
||||
|
||||
var operations = context.GetOperations();
|
||||
var cfg = ControlFlowGraph.Create(operations);
|
||||
var function = new Function(cfg.Blocks, "main", false, 0, 0);
|
||||
|
||||
var sInfo = StructuredProgram.MakeStructuredProgram(new[] { function }, config);
|
||||
|
||||
var info = config.CreateProgramInfo();
|
||||
|
||||
return config.Options.TargetLanguage switch
|
||||
{
|
||||
TargetLanguage.Glsl => new ShaderProgram(info, TargetLanguage.Glsl, GlslGenerator.Generate(sInfo, config)),
|
||||
TargetLanguage.Spirv => new ShaderProgram(info, TargetLanguage.Spirv, SpirvGenerator.Generate(sInfo, config)),
|
||||
_ => throw new NotImplementedException(config.Options.TargetLanguage.ToString())
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -210,7 +210,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
if (cbs != null && !(_buffer.HasCommandBufferDependency(cbs.Value) && _waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
|
||||
if (cbs != null &&
|
||||
_gd.PipelineInternal.RenderPassActive &&
|
||||
!(_buffer.HasCommandBufferDependency(cbs.Value) &&
|
||||
_waitable.IsBufferRangeInUse(cbs.Value.CommandBufferIndex, offset, dataSize)))
|
||||
{
|
||||
// If the buffer hasn't been used on the command buffer yet, try to preload the data.
|
||||
// This avoids ending and beginning render passes on each buffer data upload.
|
||||
|
@@ -130,6 +130,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
1f));
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Span<byte> dummyTextureData = stackalloc byte[4];
|
||||
_dummyTexture.SetData(dummyTextureData);
|
||||
}
|
||||
|
||||
public void SetProgram(ShaderCollection program)
|
||||
{
|
||||
_program = program;
|
||||
|
@@ -49,7 +49,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private Auto<DisposableFramebuffer> _framebuffer;
|
||||
private Auto<DisposableRenderPass> _renderPass;
|
||||
private int _writtenAttachmentCount;
|
||||
private bool _renderPassActive;
|
||||
|
||||
private readonly DescriptorSetUpdater _descriptorSetUpdater;
|
||||
|
||||
@@ -73,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private PipelineColorBlendAttachmentState[] _storedBlend;
|
||||
|
||||
public ulong DrawCount { get; private set; }
|
||||
public bool RenderPassActive { get; private set; }
|
||||
|
||||
public unsafe PipelineBase(VulkanRenderer gd, Device device)
|
||||
{
|
||||
@@ -114,6 +114,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
_descriptorSetUpdater.Initialize();
|
||||
|
||||
SupportBufferUpdater = new SupportBufferUpdater(Gd);
|
||||
SupportBufferUpdater.UpdateRenderScale(_renderScale, 0, SupportBuffer.RenderScaleMaxCount);
|
||||
|
||||
@@ -838,6 +840,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
stages.CopyTo(_newState.Stages.AsSpan().Slice(0, stages.Length));
|
||||
|
||||
SignalStateChange();
|
||||
|
||||
if (_program.IsCompute)
|
||||
{
|
||||
EndRenderPass();
|
||||
}
|
||||
}
|
||||
|
||||
public void Specialize<T>(in T data) where T : unmanaged
|
||||
@@ -1451,7 +1458,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private unsafe void BeginRenderPass()
|
||||
{
|
||||
if (!_renderPassActive)
|
||||
if (!RenderPassActive)
|
||||
{
|
||||
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
|
||||
var clearValue = new ClearValue();
|
||||
@@ -1467,18 +1474,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
};
|
||||
|
||||
Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
|
||||
_renderPassActive = true;
|
||||
RenderPassActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void EndRenderPass()
|
||||
{
|
||||
if (_renderPassActive)
|
||||
if (RenderPassActive)
|
||||
{
|
||||
PauseTransformFeedbackInternal();
|
||||
Gd.Api.CmdEndRenderPass(CommandBuffer);
|
||||
SignalRenderPassEnd();
|
||||
_renderPassActive = false;
|
||||
RenderPassActive = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public bool HasMinimalLayout { get; }
|
||||
public bool UsePushDescriptors { get; }
|
||||
public bool IsCompute { get; }
|
||||
|
||||
public uint Stages { get; }
|
||||
|
||||
@@ -47,7 +48,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private VulkanRenderer _gd;
|
||||
private Device _device;
|
||||
private bool _initialized;
|
||||
private bool _isCompute;
|
||||
|
||||
private ProgramPipelineState _state;
|
||||
private DisposableRenderPass _dummyRenderPass;
|
||||
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (shader.StageFlags == ShaderStageFlags.ShaderStageComputeBit)
|
||||
{
|
||||
_isCompute = true;
|
||||
IsCompute = true;
|
||||
}
|
||||
|
||||
internalShaders[i] = shader;
|
||||
@@ -163,7 +163,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
try
|
||||
{
|
||||
if (_isCompute)
|
||||
if (IsCompute)
|
||||
{
|
||||
CreateBackgroundComputePipeline();
|
||||
}
|
||||
|
@@ -396,6 +396,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
supportsFragmentShaderOrderingIntel: false,
|
||||
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
|
||||
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
|
||||
supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
|
||||
supportsMismatchingViewFormat: true,
|
||||
supportsCubemapView: !IsAmdGcn,
|
||||
supportsNonConstantTextureOffset: false,
|
||||
|
Reference in New Issue
Block a user