Logical simplifications in RDNA3 Vulkan

This commit is contained in:
Evan Husted 2025-01-06 00:54:48 -06:00
parent 9a57ac5921
commit 7a11f9f1b1
10 changed files with 39 additions and 256 deletions

View File

@ -35,7 +35,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
queue, queue,
queueLock, queueLock,
_gd.QueueFamilyIndex, _gd.QueueFamilyIndex,
_gd.IsQualcommProprietary, concurrentFenceWaitUnsupported: false,
isLight: true); isLight: true);
} }
} }

View File

@ -325,25 +325,20 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
if (_gd.IsTBDR) if (_gd.IsTBDR)
{ {
if (!_gd.IsMoltenVk) if (!anyIsNonAttachment)
{ {
if (!anyIsNonAttachment) // This case is a feedback loop. To prevent this from causing an absolute performance disaster,
{ // remove the barriers entirely.
// This case is a feedback loop. To prevent this from causing an absolute performance disaster, // If this is not here, there will be a lot of single draw render passes.
// remove the barriers entirely. _queuedBarrierCount -= _imageBarriers.Count;
// If this is not here, there will be a lot of single draw render passes. _imageBarriers.Clear();
// TODO: explicit handling for feedback loops, likely outside this class. }
else
_queuedBarrierCount -= _imageBarriers.Count; {
_imageBarriers.Clear(); // TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available.
} // Metal already has hazard tracking so MVK doesn't need this.
else endRenderPass();
{ inRenderPass = false;
// TBDR GPUs are sensitive to barriers, so we need to end the pass to ensure the data is available.
// Metal already has hazard tracking so MVK doesn't need this.
endRenderPass();
inRenderPass = false;
}
} }
} }
else else
@ -354,7 +349,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
{ {
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>( _memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier, int>(
barrier.Flags, barrier.Flags,
new MemoryBarrier() new MemoryBarrier
{ {
SType = StructureType.MemoryBarrier, SType = StructureType.MemoryBarrier,
SrcAccessMask = barrier.Barrier.SrcAccessMask, SrcAccessMask = barrier.Barrier.SrcAccessMask,
@ -375,7 +370,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
allFlags |= barrier.Flags.Dest; allFlags |= barrier.Flags.Dest;
} }
if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit) || !_gd.SupportsRenderPassBarrier(allFlags)) if (allFlags.HasFlag(PipelineStageFlags.DrawIndirectBit))
{ {
endRenderPass(); endRenderPass();
inRenderPass = false; inRenderPass = false;

View File

@ -699,14 +699,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
if (_dirty.HasFlag(DirtyFlags.Texture)) if (_dirty.HasFlag(DirtyFlags.Texture))
{ {
if (program.UpdateTexturesWithoutTemplate) UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
{
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
}
else
{
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
}
} }
if (_dirty.HasFlag(DirtyFlags.Image)) if (_dirty.HasFlag(DirtyFlags.Image))
@ -940,86 +933,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty); _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
} }
private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
{
int setIndex = PipelineBase.TextureSetIndex;
var bindingSegments = program.BindingSegments[setIndex];
if (bindingSegments.Length == 0)
{
return;
}
if (_updateDescriptorCacheCbIndex)
{
_updateDescriptorCacheCbIndex = false;
program.UpdateDescriptorCacheCommandBufferIndex(cbs.CommandBufferIndex);
}
var dsc = program.GetNewDescriptorSetCollection(setIndex, out _).Get(cbs);
foreach (ResourceBindingSegment segment in bindingSegments)
{
int binding = segment.Binding;
int count = segment.Count;
if (!segment.IsArray)
{
if (segment.Type != ResourceType.BufferTexture)
{
Span<DescriptorImageInfo> textures = _textures;
for (int i = 0; i < count; i++)
{
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
{
texture.ImageView = _dummyTexture.GetImageView().Get(cbs).Value;
}
if (texture.Sampler.Handle == 0)
{
texture.Sampler = _dummySampler.GetSampler().Get(cbs).Value;
}
}
dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler);
}
else
{
Span<BufferView> bufferTextures = _bufferTextures;
for (int i = 0; i < count; i++)
{
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
}
dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer);
}
}
else
{
if (segment.Type != ResourceType.BufferTexture)
{
dsc.UpdateImages(0, binding, _textureArrayRefs[binding].Array.GetImageInfos(_gd, cbs, _dummyTexture, _dummySampler), DescriptorType.CombinedImageSampler);
}
else
{
dsc.UpdateBufferImages(0, binding, _textureArrayRefs[binding].Array.GetBufferViews(cbs), DescriptorType.UniformTexelBuffer);
}
}
}
var sets = dsc.GetSets();
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs) private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs)
{ {

View File

@ -970,13 +970,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
{ {
_newState.RasterizerDiscardEnable = discard; _newState.RasterizerDiscardEnable = discard;
SignalStateChange(); SignalStateChange();
if (!discard && Gd.IsQualcommProprietary)
{
// On Adreno, enabling rasterizer discard somehow corrupts the viewport state.
// Force it to be updated on next use to work around this bug.
DynamicState.ForceAllDirty();
}
} }
public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask) public void SetRenderTargetColorMasks(ReadOnlySpan<uint> componentMask)
@ -1241,7 +1234,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
int vbSize = vertexBuffer.Buffer.Size; int vbSize = vertexBuffer.Buffer.Size;
if (Gd.Vendor == Vendor.Amd && !Gd.IsMoltenVk && vertexBuffer.Stride > 0) if (vertexBuffer.Stride > 0)
{ {
// AMD has a bug where if offset + stride * count is greater than // AMD has a bug where if offset + stride * count is greater than
// the size, then the last attribute will have the wrong value. // the size, then the last attribute will have the wrong value.

View File

@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
return; return;
} }
if (componentMask != 0xf || Gd.IsQualcommProprietary) if (componentMask != 0xf)
{ {
// We can't use CmdClearAttachments if not writing all components, // We can't use CmdClearAttachments if not writing all components,
// because on Vulkan, the pipeline state does not affect clears. // because on Vulkan, the pipeline state does not affect clears.
@ -90,7 +90,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
return; return;
} }
if ((stencilMask != 0 && stencilMask != 0xff) || Gd.IsQualcommProprietary) if (stencilMask != 0 && stencilMask != 0xff)
{ {
// We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits, // We can't use CmdClearAttachments if not clearing all (mask is all ones, 0xFF) or none (mask is 0) of the stencil bits,
// because on Vulkan, the pipeline state does not affect clears. // because on Vulkan, the pipeline state does not affect clears.

View File

@ -19,22 +19,10 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count]; DescriptorSetLayout[] layouts = new DescriptorSetLayout[setDescriptors.Count];
bool[] updateAfterBindFlags = new bool[setDescriptors.Count]; bool[] updateAfterBindFlags = new bool[setDescriptors.Count];
bool isMoltenVk = gd.IsMoltenVk;
for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++) for (int setIndex = 0; setIndex < setDescriptors.Count; setIndex++)
{ {
ResourceDescriptorCollection rdc = setDescriptors[setIndex]; ResourceDescriptorCollection rdc = setDescriptors[setIndex];
ResourceStages activeStages = ResourceStages.None;
if (isMoltenVk)
{
for (int descIndex = 0; descIndex < rdc.Descriptors.Count; descIndex++)
{
activeStages |= rdc.Descriptors[descIndex].Stages;
}
}
DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count]; DescriptorSetLayoutBinding[] layoutBindings = new DescriptorSetLayoutBinding[rdc.Descriptors.Count];
bool hasArray = false; bool hasArray = false;
@ -44,13 +32,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
ResourceDescriptor descriptor = rdc.Descriptors[descIndex]; ResourceDescriptor descriptor = rdc.Descriptors[descIndex];
ResourceStages stages = descriptor.Stages; ResourceStages stages = descriptor.Stages;
if (descriptor.Type == ResourceType.StorageBuffer && isMoltenVk)
{
// There's a bug on MoltenVK where using the same buffer across different stages
// causes invalid resource errors, allow the binding on all active stages as workaround.
stages = activeStages;
}
layoutBindings[descIndex] = new DescriptorSetLayoutBinding layoutBindings[descIndex] = new DescriptorSetLayoutBinding
{ {
Binding = (uint)descriptor.Binding, Binding = (uint)descriptor.Binding,
@ -74,15 +55,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
flags = DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr; flags = DescriptorSetLayoutCreateFlags.PushDescriptorBitKhr;
} }
if (gd.Vendor == Vendor.Intel && hasArray)
{
// Some vendors (like Intel) have low per-stage limits.
// We must set the flag if we exceed those limits.
flags |= DescriptorSetLayoutCreateFlags.UpdateAfterBindPoolBit;
updateAfterBindFlags[setIndex] = true;
}
var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo var descriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo
{ {
SType = StructureType.DescriptorSetLayoutCreateInfo, SType = StructureType.DescriptorSetLayoutCreateInfo,

View File

@ -393,15 +393,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
Pipeline pipelineHandle = default; Pipeline pipelineHandle = default;
bool isMoltenVk = gd.IsMoltenVk;
if (isMoltenVk)
{
UpdateVertexAttributeDescriptions(gd);
}
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0]) fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0]) fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
fixed (PipelineColorBlendAttachmentState* pColorBlendAttachmentState = &Internal.ColorBlendAttachmentState[0]) fixed (PipelineColorBlendAttachmentState* pColorBlendAttachmentState = &Internal.ColorBlendAttachmentState[0])
{ {
@ -409,7 +401,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
{ {
SType = StructureType.PipelineVertexInputStateCreateInfo, SType = StructureType.PipelineVertexInputStateCreateInfo,
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount, VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions, PVertexAttributeDescriptions = pVertexAttributeDescriptions,
VertexBindingDescriptionCount = VertexBindingDescriptionsCount, VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
PVertexBindingDescriptions = pVertexBindingDescriptions, PVertexBindingDescriptions = pVertexBindingDescriptions,
}; };
@ -519,27 +511,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
Back = stencilBack, Back = stencilBack,
}; };
uint blendEnables = 0;
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
{
// Blend can't be enabled for integer formats, so let's make sure it is disabled.
uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask;
while (attachmentIntegerFormatMask != 0)
{
int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
if (Internal.ColorBlendAttachmentState[i].BlendEnable)
{
blendEnables |= 1u << i;
}
Internal.ColorBlendAttachmentState[i].BlendEnable = false;
attachmentIntegerFormatMask &= ~(1u << i);
}
}
// Vendors other than NVIDIA have a bug where it enables logical operations even for float formats, // Vendors other than NVIDIA have a bug where it enables logical operations even for float formats,
// so we need to force disable them here. // so we need to force disable them here.
bool logicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed); bool logicOpEnable = LogicOpEnable && (gd.Vendor == Vendor.Nvidia || Internal.LogicOpsAllowed);
@ -650,15 +621,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
return null; return null;
} }
// Restore previous blend enable values if we changed it.
while (blendEnables != 0)
{
int i = BitOperations.TrailingZeroCount(blendEnables);
Internal.ColorBlendAttachmentState[i].BlendEnable = true;
blendEnables &= ~(1u << i);
}
} }
pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle)); pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));

View File

@ -23,8 +23,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
public bool IsCompute { get; } public bool IsCompute { get; }
public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0; public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
public bool UpdateTexturesWithoutTemplate { get; }
public uint Stages { get; } public uint Stages { get; }
public PipelineStageFlags IncoherentBufferWriteStages { get; } public PipelineStageFlags IncoherentBufferWriteStages { get; }
@ -118,7 +116,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
VulkanConfiguration.UsePushDescriptors && VulkanConfiguration.UsePushDescriptors &&
_gd.Capabilities.SupportsPushDescriptors && _gd.Capabilities.SupportsPushDescriptors &&
!IsCompute && !IsCompute &&
!HasPushDescriptorsBug(gd) &&
CanUsePushDescriptors(gd, resourceLayout, IsCompute); CanUsePushDescriptors(gd, resourceLayout, IsCompute);
ReadOnlyCollection<ResourceDescriptorCollection> sets = usePushDescriptors ? ReadOnlyCollection<ResourceDescriptorCollection> sets = usePushDescriptors ?
@ -136,9 +133,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
Templates = BuildTemplates(usePushDescriptors); Templates = BuildTemplates(usePushDescriptors);
(IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages); (IncoherentBufferWriteStages, IncoherentTextureWriteStages) = BuildIncoherentStages(resourceLayout.SetUsages);
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
_compileTask = Task.CompletedTask; _compileTask = Task.CompletedTask;
_firstBackgroundUse = false; _firstBackgroundUse = false;
} }
@ -157,12 +151,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
_firstBackgroundUse = !fromCache; _firstBackgroundUse = !fromCache;
} }
private static bool HasPushDescriptorsBug(VulkanRenderer gd)
{
// Those GPUs/drivers do not work properly with push descriptors, so we must force disable them.
return gd.IsNvidiaPreTuring || (gd.IsIntelArc && gd.IsIntelWindows);
}
private static bool CanUsePushDescriptors(VulkanRenderer gd, ResourceLayout layout, bool isCompute) private static bool CanUsePushDescriptors(VulkanRenderer gd, ResourceLayout layout, bool isCompute)
{ {
// If binding 3 is immediately used, use an alternate set of reserved bindings. // If binding 3 is immediately used, use an alternate set of reserved bindings.

View File

@ -99,7 +99,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
// This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube. // This flag causes mipmapped texture arrays to break on AMD GCN, so for that copy dependencies are forced for aliasing as cube.
bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray; bool isCube = info.Target == Target.Cubemap || info.Target == Target.CubemapArray;
bool cubeCompatible = gd.IsAmdGcn ? isCube : (info.Width == info.Height && layers >= 6); bool cubeCompatible = info.Width == info.Height && layers >= 6;
if (type == ImageType.Type2D && cubeCompatible) if (type == ImageType.Type2D && cubeCompatible)
{ {

View File

@ -87,12 +87,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
internal Vendor Vendor { get; private set; } internal Vendor Vendor { get; private set; }
internal bool IsAmdWindows { get; private set; } internal bool IsAmdWindows { get; private set; }
internal bool IsIntelWindows { get; private set; }
internal bool IsAmdGcn { get; private set; }
internal bool IsNvidiaPreTuring { get; private set; }
internal bool IsIntelArc { get; private set; }
internal bool IsQualcommProprietary { get; private set; }
internal bool IsMoltenVk { get; private set; }
internal bool IsTBDR { get; private set; } internal bool IsTBDR { get; private set; }
internal bool IsSharedMemory { get; private set; } internal bool IsSharedMemory { get; private set; }
@ -350,7 +344,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
Vendor = VendorUtils.FromId(properties.VendorID); Vendor = VendorUtils.FromId(properties.VendorID);
IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows(); IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows();
IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows();
IsTBDR = IsTBDR =
Vendor == Vendor.Apple || Vendor == Vendor.Apple ||
Vendor == Vendor.Qualcomm || Vendor == Vendor.Qualcomm ||
@ -369,28 +362,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}"; GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
if (Vendor == Vendor.Nvidia)
{
var match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
if (match != null && int.TryParse(match.Groups[2].Value, out int gpuNumber))
{
IsNvidiaPreTuring = gpuNumber < 2000;
}
else if (GpuRenderer.Contains("TITAN") && !GpuRenderer.Contains("RTX"))
{
IsNvidiaPreTuring = true;
}
}
else if (Vendor == Vendor.Intel)
{
IsIntelArc = GpuRenderer.StartsWith("Intel(R) Arc(TM)");
}
IsQualcommProprietary = hasDriverProperties && driverProperties.DriverID == DriverId.QualcommProprietary;
ulong minResourceAlignment = Math.Max( ulong minResourceAlignment = Math.Max(
Math.Max( Math.Max(
properties.Limits.MinStorageBufferOffsetAlignment, properties.Limits.MinStorageBufferOffsetAlignment,
@ -419,9 +390,9 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
features2.Features.ShaderStorageImageMultisample, features2.Features.ShaderStorageImageMultisample,
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue features2.Features.MultiViewport, // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk, featuresRobustness2.NullDescriptor,
supportsPushDescriptors && !IsMoltenVk, supportsPushDescriptors,
propertiesPushDescriptor.MaxPushDescriptors, propertiesPushDescriptor.MaxPushDescriptors,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
@ -450,7 +421,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi); Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device); HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsQualcommProprietary); CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, false);
PipelineLayoutCache = new PipelineLayoutCache(); PipelineLayoutCache = new PipelineLayoutCache();
@ -728,10 +699,10 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
api: TargetApi.Vulkan, api: TargetApi.Vulkan,
GpuVendor, GpuVendor,
memoryType: memoryType, memoryType: memoryType,
hasFrontFacingBug: IsIntelWindows, hasFrontFacingBug: false,
hasVectorIndexingBug: IsQualcommProprietary, hasVectorIndexingBug: false,
needsFragmentOutputSpecialization: IsMoltenVk, needsFragmentOutputSpecialization: false,
reduceShaderPrecision: IsMoltenVk, reduceShaderPrecision: false,
supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats, supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats,
supportsBc123Compression: supportsBc123CompressionFormat, supportsBc123Compression: supportsBc123CompressionFormat,
supportsBc45Compression: supportsBc45CompressionFormat, supportsBc45Compression: supportsBc45CompressionFormat,
@ -754,14 +725,14 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat, supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer, supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
supportsMismatchingViewFormat: true, supportsMismatchingViewFormat: true,
supportsCubemapView: !IsAmdGcn, supportsCubemapView: true,
supportsNonConstantTextureOffset: false, supportsNonConstantTextureOffset: false,
supportsQuads: false, supportsQuads: false,
supportsSeparateSampler: true, supportsSeparateSampler: true,
supportsShaderBallot: false, supportsShaderBallot: false,
supportsShaderBarrierDivergence: Vendor != Vendor.Intel, supportsShaderBarrierDivergence: Vendor != Vendor.Intel,
supportsShaderFloat64: Capabilities.SupportsShaderFloat64, supportsShaderFloat64: Capabilities.SupportsShaderFloat64,
supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended && !IsMoltenVk, supportsTextureGatherOffsets: features2.Features.ShaderImageGatherExtended,
supportsTextureShadowLod: false, supportsTextureShadowLod: false,
supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics, supportsVertexStoreAndAtomics: features2.Features.VertexPipelineStoresAndAtomics,
supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex, supportsViewportIndexVertexTessellation: featuresVk12.ShaderOutputViewportIndex,
@ -784,7 +755,7 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
shaderSubgroupSize: (int)Capabilities.SubgroupSize, shaderSubgroupSize: (int)Capabilities.SubgroupSize,
storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment, storageBufferOffsetAlignment: (int)limits.MinStorageBufferOffsetAlignment,
textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment, textureBufferOffsetAlignment: (int)limits.MinTexelBufferOffsetAlignment,
gatherBiasPrecision: IsIntelWindows || IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0, gatherBiasPrecision: IsAmdWindows ? (int)Capabilities.SubTexelPrecisionBits : 0,
maximumGpuMemory: GetTotalGPUMemory()); maximumGpuMemory: GetTotalGPUMemory());
} }
@ -910,20 +881,14 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
return true; return true;
} }
else if (Vendor != Vendor.Nvidia)
{
// Vulkan requires that vertex attributes are globally aligned by their component size,
// so buffer strides that don't divide by the largest scalar element are invalid.
// Guest applications do this, NVIDIA GPUs are OK with it, others are not.
alignment = attrScalarAlignment; // Vulkan requires that vertex attributes are globally aligned by their component size,
// so buffer strides that don't divide by the largest scalar element are invalid.
// Guest applications do this, NVIDIA GPUs are OK with it, others are not.
return true; alignment = attrScalarAlignment;
}
alignment = 1; return true;
return false;
} }
public void PreFrame() public void PreFrame()
@ -1001,11 +966,6 @@ namespace Ryujinx.Graphics.Rdna3Vulkan
ScreenCaptured?.Invoke(this, bitmap); ScreenCaptured?.Invoke(this, bitmap);
} }
public bool SupportsRenderPassBarrier(PipelineStageFlags flags)
{
return !(IsMoltenVk || IsQualcommProprietary);
}
public unsafe void Dispose() public unsafe void Dispose()
{ {
if (!_initialized) if (!_initialized)