Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
1668ba913f | |||
a830eb666b | |||
cfc75d7e78 |
@ -29,7 +29,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
lock (queueLock)
|
||||
{
|
||||
_pool = new CommandBufferPool(_gd.Api, _device, queue, queueLock, _gd.QueueFamilyIndex, isLight: true);
|
||||
_pool = new CommandBufferPool(
|
||||
_gd.Api,
|
||||
_device,
|
||||
queue,
|
||||
queueLock,
|
||||
_gd.QueueFamilyIndex,
|
||||
_gd.IsConcurrentFenceWaitUnsupported,
|
||||
isLight: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly Device _device;
|
||||
private readonly Queue _queue;
|
||||
private readonly object _queueLock;
|
||||
private readonly bool _concurrentFenceWaitUnsupported;
|
||||
private readonly CommandPool _pool;
|
||||
private readonly Thread _owner;
|
||||
|
||||
@ -61,12 +62,20 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private int _queuedCount;
|
||||
private int _inUseCount;
|
||||
|
||||
public unsafe CommandBufferPool(Vk api, Device device, Queue queue, object queueLock, uint queueFamilyIndex, bool isLight = false)
|
||||
public unsafe CommandBufferPool(
|
||||
Vk api,
|
||||
Device device,
|
||||
Queue queue,
|
||||
object queueLock,
|
||||
uint queueFamilyIndex,
|
||||
bool concurrentFenceWaitUnsupported,
|
||||
bool isLight = false)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
_queue = queue;
|
||||
_queueLock = queueLock;
|
||||
_concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported;
|
||||
_owner = Thread.CurrentThread;
|
||||
|
||||
var commandPoolCreateInfo = new CommandPoolCreateInfo
|
||||
@ -357,7 +366,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (refreshFence)
|
||||
{
|
||||
entry.Fence = new FenceHolder(_api, _device);
|
||||
entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -73,7 +73,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
private readonly PipelineBase _pipeline;
|
||||
private ShaderCollection _program;
|
||||
|
||||
private readonly BufferRef[] _uniformBufferRefs;
|
||||
@ -125,11 +124,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly TextureView _dummyTexture;
|
||||
private readonly SamplerHolder _dummySampler;
|
||||
|
||||
public DescriptorSetUpdater(VulkanRenderer gd, Device device, PipelineBase pipeline)
|
||||
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
_pipeline = pipeline;
|
||||
|
||||
// Some of the bindings counts needs to be multiplied by 2 because we have buffer and
|
||||
// regular textures/images interleaved on the same descriptor set.
|
||||
@ -684,7 +682,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.Texture))
|
||||
{
|
||||
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
||||
if (program.UpdateTexturesWithoutTemplate)
|
||||
{
|
||||
UpdateAndBindTexturesWithoutTemplate(cbs, program, pbp);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateAndBind(cbs, program, PipelineBase.TextureSetIndex, pbp);
|
||||
}
|
||||
}
|
||||
|
||||
if (_dirty.HasFlag(DirtyFlags.Image))
|
||||
@ -918,31 +923,84 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||
}
|
||||
|
||||
private unsafe void UpdateBuffers(
|
||||
CommandBufferScoped cbs,
|
||||
PipelineBindPoint pbp,
|
||||
int baseBinding,
|
||||
ReadOnlySpan<DescriptorBufferInfo> bufferInfo,
|
||||
DescriptorType type)
|
||||
private void UpdateAndBindTexturesWithoutTemplate(CommandBufferScoped cbs, ShaderCollection program, PipelineBindPoint pbp)
|
||||
{
|
||||
if (bufferInfo.Length == 0)
|
||||
int setIndex = PipelineBase.TextureSetIndex;
|
||||
var bindingSegments = program.BindingSegments[setIndex];
|
||||
|
||||
if (bindingSegments.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
|
||||
if (_updateDescriptorCacheCbIndex)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstBinding = (uint)baseBinding,
|
||||
DescriptorType = type,
|
||||
DescriptorCount = (uint)bufferInfo.Length,
|
||||
PBufferInfo = pBufferInfo,
|
||||
};
|
||||
|
||||
_gd.PushDescriptorApi.CmdPushDescriptorSet(cbs.CommandBuffer, pbp, _program.PipelineLayout, 0, 1, &writeDescriptorSet);
|
||||
_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.View?.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)]
|
||||
|
@ -10,12 +10,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly Device _device;
|
||||
private Fence _fence;
|
||||
private int _referenceCount;
|
||||
private int _lock;
|
||||
private readonly bool _concurrentWaitUnsupported;
|
||||
private bool _disposed;
|
||||
|
||||
public unsafe FenceHolder(Vk api, Device device)
|
||||
public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
|
||||
{
|
||||
_api = api;
|
||||
_device = device;
|
||||
_concurrentWaitUnsupported = concurrentWaitUnsupported;
|
||||
|
||||
var fenceCreateInfo = new FenceCreateInfo
|
||||
{
|
||||
@ -47,6 +50,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
|
||||
|
||||
if (_concurrentWaitUnsupported)
|
||||
{
|
||||
AcquireLock();
|
||||
}
|
||||
|
||||
fence = _fence;
|
||||
return true;
|
||||
}
|
||||
@ -57,6 +65,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return _fence;
|
||||
}
|
||||
|
||||
public void PutLock()
|
||||
{
|
||||
Put();
|
||||
|
||||
if (_concurrentWaitUnsupported)
|
||||
{
|
||||
ReleaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
public void Put()
|
||||
{
|
||||
if (Interlocked.Decrement(ref _referenceCount) == 0)
|
||||
@ -66,24 +84,67 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
private void AcquireLock()
|
||||
{
|
||||
while (!TryAcquireLock())
|
||||
{
|
||||
Thread.SpinWait(32);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryAcquireLock()
|
||||
{
|
||||
return Interlocked.Exchange(ref _lock, 1) == 0;
|
||||
}
|
||||
|
||||
private void ReleaseLock()
|
||||
{
|
||||
Interlocked.Exchange(ref _lock, 0);
|
||||
}
|
||||
|
||||
public void Wait()
|
||||
{
|
||||
Span<Fence> fences = stackalloc Fence[]
|
||||
if (_concurrentWaitUnsupported)
|
||||
{
|
||||
_fence,
|
||||
};
|
||||
AcquireLock();
|
||||
|
||||
FenceHelper.WaitAllIndefinitely(_api, _device, fences);
|
||||
try
|
||||
{
|
||||
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseLock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FenceHelper.WaitAllIndefinitely(_api, _device, stackalloc Fence[] { _fence });
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSignaled()
|
||||
{
|
||||
Span<Fence> fences = stackalloc Fence[]
|
||||
if (_concurrentWaitUnsupported)
|
||||
{
|
||||
_fence,
|
||||
};
|
||||
if (!TryAcquireLock())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return FenceHelper.AllSignaled(_api, _device, fences);
|
||||
try
|
||||
{
|
||||
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
|
||||
}
|
||||
finally
|
||||
{
|
||||
ReleaseLock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return FenceHelper.AllSignaled(_api, _device, stackalloc Fence[] { _fence });
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
@ -196,18 +196,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
bool signaled = true;
|
||||
|
||||
if (hasTimeout)
|
||||
try
|
||||
{
|
||||
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
|
||||
if (hasTimeout)
|
||||
{
|
||||
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < fenceCount; i++)
|
||||
{
|
||||
fenceHolders[i].Put();
|
||||
for (int i = 0; i < fenceCount; i++)
|
||||
{
|
||||
fenceHolders[i].PutLock();
|
||||
}
|
||||
}
|
||||
|
||||
return signaled;
|
||||
|
@ -105,7 +105,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
|
||||
|
||||
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device, this);
|
||||
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device);
|
||||
_vertexBufferUpdater = new VertexBufferUpdater(gd);
|
||||
|
||||
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
|
||||
@ -1020,6 +1020,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_newState.RasterizerDiscardEnable = discard;
|
||||
SignalStateChange();
|
||||
|
||||
if (!discard && Gd.Vendor == Vendor.Qualcomm)
|
||||
{
|
||||
// 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)
|
||||
|
@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public bool IsCompute { get; }
|
||||
public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
|
||||
|
||||
public bool UpdateTexturesWithoutTemplate { get; }
|
||||
|
||||
public uint Stages { get; }
|
||||
|
||||
public ResourceBindingSegment[][] ClearSegments { get; }
|
||||
@ -127,9 +129,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Stages = stages;
|
||||
|
||||
ClearSegments = BuildClearSegments(sets);
|
||||
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
||||
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages, out bool usesBufferTextures);
|
||||
Templates = BuildTemplates(usePushDescriptors);
|
||||
|
||||
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
|
||||
UpdateTexturesWithoutTemplate = gd.Vendor == Vendor.Qualcomm && usesBufferTextures;
|
||||
|
||||
_compileTask = Task.CompletedTask;
|
||||
_firstBackgroundUse = false;
|
||||
}
|
||||
@ -280,8 +285,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return segments;
|
||||
}
|
||||
|
||||
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages)
|
||||
private static ResourceBindingSegment[][] BuildBindingSegments(ReadOnlyCollection<ResourceUsageCollection> setUsages, out bool usesBufferTextures)
|
||||
{
|
||||
usesBufferTextures = false;
|
||||
|
||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[setUsages.Count][];
|
||||
|
||||
for (int setIndex = 0; setIndex < setUsages.Count; setIndex++)
|
||||
@ -295,6 +302,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
ResourceUsage usage = setUsages[setIndex].Usages[index];
|
||||
|
||||
if (usage.Type == ResourceType.BufferTexture)
|
||||
{
|
||||
usesBufferTextures = true;
|
||||
}
|
||||
|
||||
if (currentUsage.Binding + currentCount != usage.Binding ||
|
||||
currentUsage.Type != usage.Type ||
|
||||
currentUsage.Stages != usage.Stages ||
|
||||
|
@ -90,6 +90,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
internal bool IsMoltenVk { get; private set; }
|
||||
internal bool IsTBDR { get; private set; }
|
||||
internal bool IsSharedMemory { get; private set; }
|
||||
internal bool IsConcurrentFenceWaitUnsupported { get; private set; }
|
||||
|
||||
public string GpuVendor { get; private set; }
|
||||
public string GpuDriver { get; private set; }
|
||||
public string GpuRenderer { get; private set; }
|
||||
@ -323,6 +325,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Vendor == Vendor.Broadcom ||
|
||||
Vendor == Vendor.ImgTec;
|
||||
|
||||
IsConcurrentFenceWaitUnsupported = Vendor == Vendor.Qualcomm;
|
||||
|
||||
GpuVendor = VendorUtils.GetNameFromId(properties.VendorID);
|
||||
GpuDriver = hasDriverProperties && !OperatingSystem.IsMacOS() ?
|
||||
VendorUtils.GetFriendlyDriverName(driverProperties.DriverID) : GpuVendor; // Fallback to vendor name if driver is unavailable or on MacOS where vendor is preferred.
|
||||
@ -411,7 +415,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtExternalMemoryHost hostMemoryApi);
|
||||
HostMemoryAllocator = new HostMemoryAllocator(MemoryAllocator, Api, hostMemoryApi, _device);
|
||||
|
||||
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex);
|
||||
CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsConcurrentFenceWaitUnsupported);
|
||||
|
||||
PipelineLayoutCache = new PipelineLayoutCache();
|
||||
|
||||
|
Reference in New Issue
Block a user