Compare commits

..

3 Commits

Author SHA1 Message Date
gdkchan
07435ad844 Use draw clear on Adreno, instead of vkCmdClearAttachments (#7013)
* Use draw clear on Adreno, instead of vkCmdClearAttachments

* Fix GTX TITAN detection
2024-07-10 17:52:45 -03:00
gdkchan
1668ba913f Force dynamic state update after rasterizer discard disable (#7007) 2024-07-09 23:31:01 -03:00
gdkchan
a830eb666b Disallow concurrent fence waits on Adreno (#7001)
* Disallow concurrent fence waits on Adreno

* Ensure locks are released if exceptions are thrown
2024-07-07 19:33:28 -03:00
8 changed files with 122 additions and 27 deletions

View File

@@ -29,7 +29,14 @@ namespace Ryujinx.Graphics.Vulkan
lock (queueLock) 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.IsQualcommProprietary,
isLight: true);
} }
} }

View File

@@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device; private readonly Device _device;
private readonly Queue _queue; private readonly Queue _queue;
private readonly object _queueLock; private readonly object _queueLock;
private readonly bool _concurrentFenceWaitUnsupported;
private readonly CommandPool _pool; private readonly CommandPool _pool;
private readonly Thread _owner; private readonly Thread _owner;
@@ -61,12 +62,20 @@ namespace Ryujinx.Graphics.Vulkan
private int _queuedCount; private int _queuedCount;
private int _inUseCount; 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; _api = api;
_device = device; _device = device;
_queue = queue; _queue = queue;
_queueLock = queueLock; _queueLock = queueLock;
_concurrentFenceWaitUnsupported = concurrentFenceWaitUnsupported;
_owner = Thread.CurrentThread; _owner = Thread.CurrentThread;
var commandPoolCreateInfo = new CommandPoolCreateInfo var commandPoolCreateInfo = new CommandPoolCreateInfo
@@ -357,7 +366,7 @@ namespace Ryujinx.Graphics.Vulkan
if (refreshFence) if (refreshFence)
{ {
entry.Fence = new FenceHolder(_api, _device); entry.Fence = new FenceHolder(_api, _device, _concurrentFenceWaitUnsupported);
} }
else else
{ {

View File

@@ -10,12 +10,15 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device; private readonly Device _device;
private Fence _fence; private Fence _fence;
private int _referenceCount; private int _referenceCount;
private int _lock;
private readonly bool _concurrentWaitUnsupported;
private bool _disposed; private bool _disposed;
public unsafe FenceHolder(Vk api, Device device) public unsafe FenceHolder(Vk api, Device device, bool concurrentWaitUnsupported)
{ {
_api = api; _api = api;
_device = device; _device = device;
_concurrentWaitUnsupported = concurrentWaitUnsupported;
var fenceCreateInfo = new FenceCreateInfo var fenceCreateInfo = new FenceCreateInfo
{ {
@@ -47,6 +50,11 @@ namespace Ryujinx.Graphics.Vulkan
} }
while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue); while (Interlocked.CompareExchange(ref _referenceCount, lastValue + 1, lastValue) != lastValue);
if (_concurrentWaitUnsupported)
{
AcquireLock();
}
fence = _fence; fence = _fence;
return true; return true;
} }
@@ -57,6 +65,16 @@ namespace Ryujinx.Graphics.Vulkan
return _fence; return _fence;
} }
public void PutLock()
{
Put();
if (_concurrentWaitUnsupported)
{
ReleaseLock();
}
}
public void Put() public void Put()
{ {
if (Interlocked.Decrement(ref _referenceCount) == 0) 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() 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() 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() public void Dispose()

View File

@@ -196,6 +196,8 @@ namespace Ryujinx.Graphics.Vulkan
bool signaled = true; bool signaled = true;
try
{
if (hasTimeout) if (hasTimeout)
{ {
signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout); signaled = FenceHelper.AllSignaled(api, device, fences[..fenceCount], timeout);
@@ -204,10 +206,13 @@ namespace Ryujinx.Graphics.Vulkan
{ {
FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]); FenceHelper.WaitAllIndefinitely(api, device, fences[..fenceCount]);
} }
}
finally
{
for (int i = 0; i < fenceCount; i++) for (int i = 0; i < fenceCount; i++)
{ {
fenceHolders[i].Put(); fenceHolders[i].PutLock();
}
} }
return signaled; return signaled;

View File

@@ -1020,6 +1020,13 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_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)

View File

@@ -47,10 +47,11 @@ namespace Ryujinx.Graphics.Vulkan
return; return;
} }
if (componentMask != 0xf) if (componentMask != 0xf || Gd.IsQualcommProprietary)
{ {
// 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.
// On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all.
var dstTexture = FramebufferParams.GetColorView(index); var dstTexture = FramebufferParams.GetColorView(index);
if (dstTexture == null) if (dstTexture == null)
{ {
@@ -87,10 +88,11 @@ namespace Ryujinx.Graphics.Vulkan
return; return;
} }
if (stencilMask != 0 && stencilMask != 0xff) if ((stencilMask != 0 && stencilMask != 0xff) || Gd.IsQualcommProprietary)
{ {
// 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.
// On proprietary Adreno drivers, CmdClearAttachments appears to execute out of order, so it's better to not use it at all.
var dstTexture = FramebufferParams.GetDepthStencilView(); var dstTexture = FramebufferParams.GetDepthStencilView();
if (dstTexture == null) if (dstTexture == null)
{ {

View File

@@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Vulkan
Templates = BuildTemplates(usePushDescriptors); Templates = BuildTemplates(usePushDescriptors);
// Updating buffer texture bindings using template updates crashes the Adreno driver on Windows. // Updating buffer texture bindings using template updates crashes the Adreno driver on Windows.
UpdateTexturesWithoutTemplate = gd.Vendor == Vendor.Qualcomm && usesBufferTextures; UpdateTexturesWithoutTemplate = gd.IsQualcommProprietary && usesBufferTextures;
_compileTask = Task.CompletedTask; _compileTask = Task.CompletedTask;
_firstBackgroundUse = false; _firstBackgroundUse = false;

View File

@@ -87,9 +87,11 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsAmdGcn { get; private set; } internal bool IsAmdGcn { get; private set; }
internal bool IsNvidiaPreTuring { get; private set; } internal bool IsNvidiaPreTuring { get; private set; }
internal bool IsIntelArc { get; private set; } internal bool IsIntelArc { get; private set; }
internal bool IsQualcommProprietary { get; private set; }
internal bool IsMoltenVk { 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; }
public string GpuVendor { get; private set; } public string GpuVendor { get; private set; }
public string GpuDriver { get; private set; } public string GpuDriver { get; private set; }
public string GpuRenderer { get; private set; } public string GpuRenderer { get; private set; }
@@ -344,7 +346,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
IsNvidiaPreTuring = gpuNumber < 2000; IsNvidiaPreTuring = gpuNumber < 2000;
} }
else if (GpuDriver.Contains("TITAN") && !GpuDriver.Contains("RTX")) else if (GpuRenderer.Contains("TITAN") && !GpuRenderer.Contains("RTX"))
{ {
IsNvidiaPreTuring = true; IsNvidiaPreTuring = true;
} }
@@ -354,6 +356,8 @@ namespace Ryujinx.Graphics.Vulkan
IsIntelArc = GpuRenderer.StartsWith("Intel(R) Arc(TM)"); 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,
@@ -411,7 +415,7 @@ namespace Ryujinx.Graphics.Vulkan
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); CommandBufferPool = new CommandBufferPool(Api, _device, Queue, QueueLock, queueFamilyIndex, IsQualcommProprietary);
PipelineLayoutCache = new PipelineLayoutCache(); PipelineLayoutCache = new PipelineLayoutCache();
@@ -688,7 +692,7 @@ namespace Ryujinx.Graphics.Vulkan
GpuVendor, GpuVendor,
memoryType: memoryType, memoryType: memoryType,
hasFrontFacingBug: IsIntelWindows, hasFrontFacingBug: IsIntelWindows,
hasVectorIndexingBug: Vendor == Vendor.Qualcomm, hasVectorIndexingBug: IsQualcommProprietary,
needsFragmentOutputSpecialization: IsMoltenVk, needsFragmentOutputSpecialization: IsMoltenVk,
reduceShaderPrecision: IsMoltenVk, reduceShaderPrecision: IsMoltenVk,
supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats, supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats,