Compare commits

..

3 Commits

Author SHA1 Message Date
gdkchan
296c4a3d01 Relax Vulkan requirements (#4282)
* Relax Vulkan requirements

* Fix MaxColorAttachmentIndex

* Fix ColorBlendAttachmentStateCount value mismatch for background pipelines

* Change query capability check to check for pipeline statistics query rather than geometry shader support
2023-01-26 18:34:35 -03:00
riperiperi
e7cf4e6eaf Vulkan: Reset queries on same command buffer (#4329)
* Reset queries on same command buffer

Vulkan seems to complain when the queries are reset on another command buffer. No idea why, the spec really could be written better in this regard. This fixes complaints, and hopefully any implementations that care extensively about them.

This change _guesses_ how many queries need to be reset and resets as many as possible at the same time to avoid splitting render passes. If it resets too many queries, we didn't waste too much time - if it runs out of resets it will batch reset 10 more.

The number of queries reset is the maximum number of queries in the last 3 frames. This has been worked into the AutoFlushCounter so that it only resets up to 32 if it is yet to force a command buffer submission in this attachment.

This is only done for samples passed queries right now, as they have by far the most resets.

* Address Feedback
2023-01-24 13:32:56 -03:00
gdkchan
a1a4771ac1 Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer (#4337)
* Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer

* Rename FuncPtr to FuncPointer
2023-01-23 22:37:53 +00:00
21 changed files with 262 additions and 101 deletions

View File

@@ -48,9 +48,21 @@ namespace ARMeilleure.CodeGen
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns> /// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
public T Map<T>() public T Map<T>()
{ {
IntPtr codePtr = JitCache.Map(this); return MapWithPointer<T>(out _);
}
return Marshal.GetDelegateForFunctionPointer<T>(codePtr); /// <summary>
/// Maps the <see cref="CompiledFunction"/> onto the <see cref="JitCache"/> and returns a delegate of type
/// <typeparamref name="T"/> pointing to the mapped function.
/// </summary>
/// <typeparam name="T">Type of delegate</typeparam>
/// <param name="codePointer">Pointer to the function code in memory</param>
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
public T MapWithPointer<T>(out IntPtr codePointer)
{
codePointer = JitCache.Map(this);
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
} }
} }
} }

View File

@@ -191,7 +191,7 @@ namespace ARMeilleure.Instructions
{ {
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
return (ulong)function.FuncPtr.ToInt64(); return (ulong)function.FuncPointer.ToInt64();
} }
public static void InvalidateCacheLine(ulong address) public static void InvalidateCacheLine(ulong address)

View File

@@ -745,9 +745,9 @@ namespace ARMeilleure.Translation.PTC
bool highCq) bool highCq)
{ {
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty); var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
var gFunc = cFunc.Map<GuestFunction>(); var gFunc = cFunc.MapWithPointer<GuestFunction>(out IntPtr gFuncPointer);
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq); return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
} }
private void UpdateInfo(InfoEntry infoEntry) private void UpdateInfo(InfoEntry infoEntry)

View File

@@ -1,6 +1,5 @@
using ARMeilleure.Common; using ARMeilleure.Common;
using System; using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
@@ -8,18 +7,18 @@ namespace ARMeilleure.Translation
{ {
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected. private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
public IntPtr FuncPointer { get; }
public Counter<uint> CallCounter { get; } public Counter<uint> CallCounter { get; }
public ulong GuestSize { get; } public ulong GuestSize { get; }
public bool HighCq { get; } public bool HighCq { get; }
public IntPtr FuncPtr { get; }
public TranslatedFunction(GuestFunction func, Counter<uint> callCounter, ulong guestSize, bool highCq) public TranslatedFunction(GuestFunction func, IntPtr funcPointer, Counter<uint> callCounter, ulong guestSize, bool highCq)
{ {
_func = func; _func = func;
FuncPointer = funcPointer;
CallCounter = callCounter; CallCounter = callCounter;
GuestSize = guestSize; GuestSize = guestSize;
HighCq = highCq; HighCq = highCq;
FuncPtr = Marshal.GetFunctionPointerForDelegate(func);
} }
public ulong Execute(State.ExecutionContext context) public ulong Execute(State.ExecutionContext context)

View File

@@ -211,7 +211,7 @@ namespace ARMeilleure.Translation
if (oldFunc != func) if (oldFunc != func)
{ {
JitCache.Unmap(func.FuncPtr); JitCache.Unmap(func.FuncPointer);
func = oldFunc; func = oldFunc;
} }
@@ -230,7 +230,7 @@ namespace ARMeilleure.Translation
{ {
if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq)) if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq))
{ {
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPtr); Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPointer);
} }
} }
@@ -292,11 +292,11 @@ namespace ARMeilleure.Translation
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); _ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
} }
GuestFunction func = compiledFunc.Map<GuestFunction>(); GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out IntPtr funcPointer);
Allocators.ResetAll(); Allocators.ResetAll();
return new TranslatedFunction(func, counter, funcSize, highCq); return new TranslatedFunction(func, funcPointer, counter, funcSize, highCq);
} }
private void BackgroundTranslate() private void BackgroundTranslate()
@@ -537,7 +537,7 @@ namespace ARMeilleure.Translation
foreach (var func in functions) foreach (var func in functions)
{ {
JitCache.Unmap(func.FuncPtr); JitCache.Unmap(func.FuncPointer);
func.CallCounter?.Dispose(); func.CallCounter?.Dispose();
} }
@@ -546,7 +546,7 @@ namespace ARMeilleure.Translation
while (_oldFuncs.TryDequeue(out var kv)) while (_oldFuncs.TryDequeue(out var kv))
{ {
JitCache.Unmap(kv.Value.FuncPtr); JitCache.Unmap(kv.Value.FuncPointer);
kv.Value.CallCounter?.Dispose(); kv.Value.CallCounter?.Dispose();
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -16,6 +17,10 @@ namespace Ryujinx.Graphics.Vulkan
private bool _hasPendingQuery; private bool _hasPendingQuery;
private int _queryCount; private int _queryCount;
private int[] _queryCountHistory = new int[3];
private int _queryCountHistoryIndex;
private int _remainingQueries;
public void RegisterFlush(ulong drawCount) public void RegisterFlush(ulong drawCount)
{ {
_lastFlush = Stopwatch.GetTimestamp(); _lastFlush = Stopwatch.GetTimestamp();
@@ -27,6 +32,9 @@ namespace Ryujinx.Graphics.Vulkan
public bool RegisterPendingQuery() public bool RegisterPendingQuery()
{ {
_hasPendingQuery = true; _hasPendingQuery = true;
_remainingQueries--;
_queryCountHistory[_queryCountHistoryIndex]++;
// Interrupt render passes to flush queries, so that early results arrive sooner. // Interrupt render passes to flush queries, so that early results arrive sooner.
if (++_queryCount == InitialQueryCountForFlush) if (++_queryCount == InitialQueryCountForFlush)
@@ -37,6 +45,21 @@ namespace Ryujinx.Graphics.Vulkan
return false; return false;
} }
public int GetRemainingQueries()
{
if (_remainingQueries <= 0)
{
_remainingQueries = 16;
}
if (_queryCount < InitialQueryCountForFlush)
{
return Math.Min(InitialQueryCountForFlush - _queryCount, _remainingQueries);
}
return _remainingQueries;
}
public bool ShouldFlushQuery() public bool ShouldFlushQuery()
{ {
return _hasPendingQuery; return _hasPendingQuery;
@@ -69,5 +92,14 @@ namespace Ryujinx.Graphics.Vulkan
return now > _lastFlush + flushTimeout; return now > _lastFlush + flushTimeout;
} }
public void Present()
{
_queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3;
_remainingQueries = _queryCountHistory.Max() + 10;
_queryCountHistory[_queryCountHistoryIndex] = 0;
}
} }
} }

View File

@@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan
MemoryPropertyFlags.HostCoherentBit | MemoryPropertyFlags.HostCoherentBit |
MemoryPropertyFlags.HostCachedBit; MemoryPropertyFlags.HostCachedBit;
// Some drivers don't expose a "HostCached" memory type,
// so we need those alternative flags for the allocation to succeed there.
private const MemoryPropertyFlags DefaultBufferMemoryAltFlags =
MemoryPropertyFlags.HostVisibleBit |
MemoryPropertyFlags.HostCoherentBit;
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
MemoryPropertyFlags.DeviceLocalBit; MemoryPropertyFlags.DeviceLocalBit;
@@ -94,9 +100,21 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
var allocateFlags = deviceLocal ? DeviceLocalBufferMemoryFlags : DefaultBufferMemoryFlags; MemoryPropertyFlags allocateFlags;
MemoryPropertyFlags allocateFlagsAlt;
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags); if (deviceLocal)
{
allocateFlags = DeviceLocalBufferMemoryFlags;
allocateFlagsAlt = DeviceLocalBufferMemoryFlags;
}
else
{
allocateFlags = DefaultBufferMemoryFlags;
allocateFlagsAlt = DefaultBufferMemoryAltFlags;
}
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
public int[] AttachmentIndices { get; } public int[] AttachmentIndices { get; }
public int AttachmentsCount { get; } public int AttachmentsCount { get; }
public int MaxColorAttachmentIndex { get; } public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1;
public bool HasDepthStencil { get; } public bool HasDepthStencil { get; }
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
@@ -66,8 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
AttachmentSamples = new uint[count]; AttachmentSamples = new uint[count];
AttachmentFormats = new VkFormat[count]; AttachmentFormats = new VkFormat[count];
AttachmentIndices = new int[count]; AttachmentIndices = new int[colorsCount];
MaxColorAttachmentIndex = colors.Length - 1;
uint width = uint.MaxValue; uint width = uint.MaxValue;
uint height = uint.MaxValue; uint height = uint.MaxValue;

View File

@@ -28,10 +28,11 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsMultiView; public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors; public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPreciseOcclusionQueries;
public readonly bool SupportsPushDescriptors; public readonly bool SupportsPushDescriptors;
public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedback;
public readonly bool SupportsTransformFeedbackQueries; public readonly bool SupportsTransformFeedbackQueries;
public readonly bool SupportsPreciseOcclusionQueries;
public readonly bool SupportsPipelineStatisticsQuery;
public readonly bool SupportsGeometryShader; public readonly bool SupportsGeometryShader;
public readonly uint MinSubgroupSize; public readonly uint MinSubgroupSize;
public readonly uint MaxSubgroupSize; public readonly uint MaxSubgroupSize;
@@ -55,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsTransformFeedback, bool supportsTransformFeedback,
bool supportsTransformFeedbackQueries, bool supportsTransformFeedbackQueries,
bool supportsPreciseOcclusionQueries, bool supportsPreciseOcclusionQueries,
bool supportsPipelineStatisticsQuery,
bool supportsGeometryShader, bool supportsGeometryShader,
uint minSubgroupSize, uint minSubgroupSize,
uint maxSubgroupSize, uint maxSubgroupSize,
@@ -77,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedback = supportsTransformFeedback;
SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries; SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries;
SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries;
SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery;
SupportsGeometryShader = supportsGeometryShader; SupportsGeometryShader = supportsGeometryShader;
MinSubgroupSize = minSubgroupSize; MinSubgroupSize = minSubgroupSize;
MaxSubgroupSize = maxSubgroupSize; MaxSubgroupSize = maxSubgroupSize;

View File

@@ -27,7 +27,16 @@ namespace Ryujinx.Graphics.Vulkan
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags = 0) MemoryPropertyFlags flags = 0)
{ {
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags); return AllocateDeviceMemory(physicalDevice, requirements, flags, flags);
}
public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements,
MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags)
{
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags);
if (memoryTypeIndex < 0) if (memoryTypeIndex < 0)
{ {
return default; return default;
@@ -56,21 +65,35 @@ namespace Ryujinx.Graphics.Vulkan
return newBl.Allocate(size, alignment, map); return newBl.Allocate(size, alignment, map);
} }
private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, MemoryPropertyFlags flags) private static int FindSuitableMemoryTypeIndex(
Vk api,
PhysicalDevice physicalDevice,
uint memoryTypeBits,
MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags)
{ {
int bestCandidateIndex = -1;
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
for (int i = 0; i < properties.MemoryTypeCount; i++) for (int i = 0; i < properties.MemoryTypeCount; i++)
{ {
var type = properties.MemoryTypes[i]; var type = properties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) if ((memoryTypeBits & (1 << i)) != 0)
{ {
return i; if (type.PropertyFlags.HasFlag(flags))
{
return i;
}
else if (type.PropertyFlags.HasFlag(alternativeFlags))
{
bestCandidateIndex = i;
}
} }
} }
return -1; return bestCandidateIndex;
} }
public void Dispose() public void Dispose()

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan
protected readonly Device Device; protected readonly Device Device;
public readonly PipelineCache PipelineCache; public readonly PipelineCache PipelineCache;
protected readonly AutoFlushCounter AutoFlush; public readonly AutoFlushCounter AutoFlush;
protected PipelineDynamicState DynamicState; protected PipelineDynamicState DynamicState;
private PipelineState _newState; private PipelineState _newState;
@@ -1344,8 +1344,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex + (FramebufferParams.HasDepthStencil ? 1 : 0); for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
for (int i = FramebufferParams.AttachmentFormats.Length; i <= maxAttachmentIndex; i++)
{ {
dstAttachmentFormats[i] = 0; dstAttachmentFormats[i] = 0;
} }
@@ -1376,8 +1375,6 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < FramebufferParams.AttachmentsCount; i++) for (int i = 0; i < FramebufferParams.AttachmentsCount; i++)
{ {
int bindIndex = FramebufferParams.AttachmentIndices[i];
attachmentDescs[i] = new AttachmentDescription( attachmentDescs[i] = new AttachmentDescription(
0, 0,
FramebufferParams.AttachmentFormats[i], FramebufferParams.AttachmentFormats[i],

View File

@@ -27,14 +27,11 @@ namespace Ryujinx.Graphics.Vulkan
int attachmentCount = 0; int attachmentCount = 0;
int colorCount = 0; int colorCount = 0;
int maxColorAttachmentIndex = 0;
for (int i = 0; i < state.AttachmentEnable.Length; i++) for (int i = 0; i < state.AttachmentEnable.Length; i++)
{ {
if (state.AttachmentEnable[i]) if (state.AttachmentEnable[i])
{ {
maxColorAttachmentIndex = i;
attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
attachmentIndices[attachmentCount++] = i; attachmentIndices[attachmentCount++] = i;
@@ -270,7 +267,7 @@ namespace Ryujinx.Graphics.Vulkan
// NOTE: Viewports, Scissors are dynamic. // NOTE: Viewports, Scissors are dynamic.
for (int i = 0; i < 8; i++) for (int i = 0; i < Constants.MaxRenderTargets; i++)
{ {
var blend = state.BlendDescriptors[i]; var blend = state.BlendDescriptors[i];
@@ -293,21 +290,24 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
int maxAttachmentIndex = 0; int attachmentCount = 0;
for (int i = 0; i < 8; i++) int maxColorAttachmentIndex = -1;
for (int i = 0; i < Constants.MaxRenderTargets; i++)
{ {
if (state.AttachmentEnable[i]) if (state.AttachmentEnable[i])
{ {
pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
maxColorAttachmentIndex = i;
} }
} }
if (state.DepthStencilEnable) if (state.DepthStencilEnable)
{ {
pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
} }
pipeline.ColorBlendAttachmentStateCount = 8; pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);
pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
return pipeline; return pipeline;

View File

@@ -14,7 +14,6 @@ namespace Ryujinx.Graphics.Vulkan
private CounterQueueEvent _activeConditionalRender; private CounterQueueEvent _activeConditionalRender;
private readonly List<BufferedQuery> _pendingQueryCopies; private readonly List<BufferedQuery> _pendingQueryCopies;
private readonly List<BufferedQuery> _pendingQueryResets;
private ulong _byteWeight; private ulong _byteWeight;
@@ -22,7 +21,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_activeQueries = new List<QueryPool>(); _activeQueries = new List<QueryPool>();
_pendingQueryCopies = new(); _pendingQueryCopies = new();
_pendingQueryResets = new List<BufferedQuery>();
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
} }
@@ -34,16 +32,6 @@ namespace Ryujinx.Graphics.Vulkan
query.PoolCopy(Cbs); query.PoolCopy(Cbs);
} }
lock (_pendingQueryResets)
{
foreach (var query in _pendingQueryResets)
{
query.PoolReset(CommandBuffer);
}
_pendingQueryResets.Clear();
}
_pendingQueryCopies.Clear(); _pendingQueryCopies.Clear();
} }
@@ -238,10 +226,12 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0); Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
} }
Gd.ResetCounterPool();
Restore(); Restore();
} }
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset) public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool)
{ {
if (needsReset) if (needsReset)
{ {
@@ -249,9 +239,11 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdResetQueryPool(CommandBuffer, pool, 0, 1); Gd.Api.CmdResetQueryPool(CommandBuffer, pool, 0, 1);
lock (_pendingQueryResets) if (fromSamplePool)
{ {
_pendingQueryResets.Remove(query); // Might be present on here. // Try reset some additional queries in advance.
Gd.ResetFutureCounters(CommandBuffer, AutoFlush.GetRemainingQueries());
} }
} }
@@ -267,14 +259,6 @@ namespace Ryujinx.Graphics.Vulkan
_activeQueries.Remove(pool); _activeQueries.Remove(pool);
} }
public void ResetQuery(BufferedQuery query)
{
lock (_pendingQueryResets)
{
_pendingQueryResets.Add(query);
}
}
public void CopyQueryResults(BufferedQuery query) public void CopyQueryResults(BufferedQuery query)
{ {
_pendingQueryCopies.Add(query); _pendingQueryCopies.Add(query);

View File

@@ -18,7 +18,6 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private readonly PipelineFull _pipeline; private readonly PipelineFull _pipeline;
private QueryPool _queryPool; private QueryPool _queryPool;
private bool _isReset;
private readonly BufferHolder _buffer; private readonly BufferHolder _buffer;
private readonly IntPtr _bufferMap; private readonly IntPtr _bufferMap;
@@ -27,6 +26,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private bool _isSupported; private bool _isSupported;
private long _defaultValue; private long _defaultValue;
private int? _resetSequence;
public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit) public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit)
{ {
@@ -67,8 +67,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
return type switch return type switch
{ {
CounterType.SamplesPassed => true, CounterType.SamplesPassed => true,
CounterType.PrimitivesGenerated => gd.Capabilities.SupportsPipelineStatisticsQuery,
CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries, CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries,
CounterType.PrimitivesGenerated => gd.Capabilities.SupportsGeometryShader,
_ => false _ => false
}; };
} }
@@ -92,16 +92,17 @@ namespace Ryujinx.Graphics.Vulkan.Queries
public void Reset() public void Reset()
{ {
End(false); End(false);
Begin(); Begin(null);
} }
public void Begin() public void Begin(int? resetSequence)
{ {
if (_isSupported) if (_isSupported)
{ {
_pipeline.BeginQuery(this, _queryPool, !_isReset); bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
_pipeline.BeginQuery(this, _queryPool, needsReset, _type == CounterType.SamplesPassed && resetSequence != null);
} }
_isReset = false; _resetSequence = null;
} }
public unsafe void End(bool withResult) public unsafe void End(bool withResult)
@@ -162,13 +163,14 @@ namespace Ryujinx.Graphics.Vulkan.Queries
return data; return data;
} }
public void PoolReset(CommandBuffer cmd) public void PoolReset(CommandBuffer cmd, int resetSequence)
{ {
if (_isSupported) if (_isSupported)
{ {
_api.CmdResetQueryPool(cmd, _queryPool, 0, 1); _api.CmdResetQueryPool(cmd, _queryPool, 0, 1);
} }
_isReset = true;
_resetSequence = resetSequence;
} }
public void PoolCopy(CommandBufferScoped cbs) public void PoolCopy(CommandBufferScoped cbs)

View File

@@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan.Queries namespace Ryujinx.Graphics.Vulkan.Queries
{ {
@@ -32,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private Thread _consumerThread; private Thread _consumerThread;
public int ResetSequence { get; private set; }
internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type) internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type)
{ {
_gd = gd; _gd = gd;
@@ -53,6 +56,24 @@ namespace Ryujinx.Graphics.Vulkan.Queries
_consumerThread.Start(); _consumerThread.Start();
} }
public void ResetCounterPool()
{
ResetSequence++;
}
public void ResetFutureCounters(CommandBuffer cmd, int count)
{
// Pre-emptively reset queries to avoid render pass splitting.
lock (_queryPool)
{
count = Math.Min(count, _queryPool.Count);
for (int i = 0; i < count; i++)
{
_queryPool.ElementAt(i).PoolReset(cmd, ResetSequence);
}
}
}
private void EventConsumer() private void EventConsumer()
{ {
while (!Disposed) while (!Disposed)
@@ -106,7 +127,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
{ {
lock (_lock) lock (_lock)
{ {
_pipeline.ResetQuery(query); // The query will be reset when it dequeues.
_queryPool.Enqueue(query); _queryPool.Enqueue(query);
} }
} }

View File

@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
DrawIndex = drawIndex; DrawIndex = drawIndex;
_counter.Begin(); _counter.Begin(_queue.ResetSequence);
} }
public Auto<DisposableBuffer> GetBuffer() public Auto<DisposableBuffer> GetBuffer()

View File

@@ -24,6 +24,19 @@ namespace Ryujinx.Graphics.Vulkan.Queries
} }
} }
public void ResetCounterPool()
{
foreach (var queue in _counterQueues)
{
queue.ResetCounterPool();
}
}
public void ResetFutureCounters(CommandBuffer cmd, int count)
{
_counterQueues[(int)CounterType.SamplesPassed].ResetFutureCounters(cmd, count);
}
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved)
{ {
return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved); return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved);

View File

@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Vulkan
Intel, Intel,
Nvidia, Nvidia,
ARM, ARM,
Broadcom,
Qualcomm, Qualcomm,
Apple, Apple,
Unknown Unknown
@@ -28,6 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
0x106B => Vendor.Apple, 0x106B => Vendor.Apple,
0x10DE => Vendor.Nvidia, 0x10DE => Vendor.Nvidia,
0x13B5 => Vendor.ARM, 0x13B5 => Vendor.ARM,
0x14E4 => Vendor.Broadcom,
0x8086 => Vendor.Intel, 0x8086 => Vendor.Intel,
0x5143 => Vendor.Qualcomm, 0x5143 => Vendor.Qualcomm,
_ => Vendor.Unknown _ => Vendor.Unknown
@@ -43,6 +45,7 @@ namespace Ryujinx.Graphics.Vulkan
0x106B => "Apple", 0x106B => "Apple",
0x10DE => "NVIDIA", 0x10DE => "NVIDIA",
0x13B5 => "ARM", 0x13B5 => "ARM",
0x14E4 => "Broadcom",
0x1AE0 => "Google", 0x1AE0 => "Google",
0x5143 => "Qualcomm", 0x5143 => "Qualcomm",
0x8086 => "Intel", 0x8086 => "Intel",

View File

@@ -162,7 +162,6 @@ namespace Ryujinx.Graphics.Vulkan
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
{ {
Logger.Error?.Print(LogClass.Gpu, msg); Logger.Error?.Print(LogClass.Gpu, msg);
//throw new Exception(msg);
} }
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
{ {
@@ -379,14 +378,34 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceFeatures2 SType = StructureType.PhysicalDeviceFeatures2
}; };
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT() PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new PhysicalDeviceVulkan11Features()
{ {
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt SType = StructureType.PhysicalDeviceVulkan11Features,
PNext = features2.PNext
};
features2.PNext = &supportedFeaturesVk11;
PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt,
PNext = features2.PNext
}; };
if (supportedExtensions.Contains("VK_EXT_custom_border_color")) if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
{ {
features2.PNext = &featuresCustomBorderColorSupported; features2.PNext = &supportedFeaturesCustomBorderColor;
}
PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
PNext = features2.PNext
};
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
{
features2.PNext = &supportedFeaturesTransformFeedback;
} }
PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
@@ -408,42 +427,49 @@ namespace Ryujinx.Graphics.Vulkan
var features = new PhysicalDeviceFeatures() var features = new PhysicalDeviceFeatures()
{ {
DepthBiasClamp = true, DepthBiasClamp = true,
DepthClamp = true, DepthClamp = supportedFeatures.DepthClamp,
DualSrcBlend = true, DualSrcBlend = supportedFeatures.DualSrcBlend,
FragmentStoresAndAtomics = true, FragmentStoresAndAtomics = true,
GeometryShader = supportedFeatures.GeometryShader, GeometryShader = supportedFeatures.GeometryShader,
ImageCubeArray = true, ImageCubeArray = true,
IndependentBlend = true, IndependentBlend = true,
LogicOp = supportedFeatures.LogicOp, LogicOp = supportedFeatures.LogicOp,
MultiViewport = true,
OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise,
MultiViewport = supportedFeatures.MultiViewport,
PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery,
SamplerAnisotropy = true, SamplerAnisotropy = true,
ShaderClipDistance = true, ShaderClipDistance = true,
ShaderFloat64 = supportedFeatures.ShaderFloat64, ShaderFloat64 = supportedFeatures.ShaderFloat64,
ShaderImageGatherExtended = true, ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended,
ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample,
// ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageReadWithoutFormat = true,
// ShaderStorageImageWriteWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true,
TessellationShader = true, TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = true, VertexPipelineStoresAndAtomics = true,
RobustBufferAccess = useRobustBufferAccess RobustBufferAccess = useRobustBufferAccess
}; };
void* pExtendedFeatures = null; void* pExtendedFeatures = null;
var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
{
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
PNext = pExtendedFeatures,
TransformFeedback = true
};
pExtendedFeatures = &featuresTransformFeedback; if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
{
featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
PNext = pExtendedFeatures,
TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback
};
pExtendedFeatures = &featuresTransformFeedback;
}
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (supportedExtensions.Contains("VK_EXT_robustness2"))
{ {
var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
{ {
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
@@ -466,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PhysicalDeviceVulkan11Features, SType = StructureType.PhysicalDeviceVulkan11Features,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
ShaderDrawParameters = true ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters
}; };
pExtendedFeatures = &featuresVk11; pExtendedFeatures = &featuresVk11;
@@ -527,8 +553,8 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (supportedExtensions.Contains("VK_EXT_custom_border_color") && if (supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColorSupported.CustomBorderColors && supportedFeaturesCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat) supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
{ {
featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{ {

View File

@@ -271,6 +271,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsTransformFeedback, supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries, propertiesTransformFeedback.TransformFeedbackQueries,
features2.Features.OcclusionQueryPrecise, features2.Features.OcclusionQueryPrecise,
supportedFeatures.PipelineStatisticsQuery,
supportedFeatures.GeometryShader, supportedFeatures.GeometryShader,
propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
@@ -589,9 +590,13 @@ namespace Ryujinx.Graphics.Vulkan
Vendor = VendorUtils.FromId(properties.VendorID); Vendor = VendorUtils.FromId(properties.VendorID);
IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows();
IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows();
IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec; IsTBDR = IsMoltenVk ||
Vendor == Vendor.Qualcomm ||
Vendor == Vendor.ARM ||
Vendor == Vendor.Broadcom ||
Vendor == Vendor.ImgTec;
GpuVendor = vendorName; GpuVendor = vendorName;
GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
@@ -679,6 +684,16 @@ namespace Ryujinx.Graphics.Vulkan
_counters.Update(); _counters.Update();
} }
public void ResetCounterPool()
{
_counters.ResetCounterPool();
}
public void ResetFutureCounters(CommandBuffer cmd, int count)
{
_counters?.ResetFutureCounters(cmd, count);
}
public void BackgroundContextAction(Action action, bool alwaysBackground = false) public void BackgroundContextAction(Action action, bool alwaysBackground = false)
{ {
action(); action();

View File

@@ -120,7 +120,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageSharingMode = SharingMode.Exclusive, ImageSharingMode = SharingMode.Exclusive,
ImageArrayLayers = 1, ImageArrayLayers = 1,
PreTransform = capabilities.CurrentTransform, PreTransform = capabilities.CurrentTransform,
CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
Clipped = true Clipped = true
}; };
@@ -188,6 +188,22 @@ namespace Ryujinx.Graphics.Vulkan
return availableFormats[0]; return availableFormats[0];
} }
private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags)
{
if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr))
{
return CompositeAlphaFlagsKHR.OpaqueBitKhr;
}
else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr))
{
return CompositeAlphaFlagsKHR.PreMultipliedBitKhr;
}
else
{
return CompositeAlphaFlagsKHR.InheritBitKhr;
}
}
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
{ {
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
@@ -198,10 +214,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return PresentModeKHR.MailboxKhr; return PresentModeKHR.MailboxKhr;
} }
else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr))
{
return PresentModeKHR.FifoKhr;
}
else else
{ {
return PresentModeKHR.FifoKhr; return PresentModeKHR.FifoKhr;
@@ -225,6 +237,8 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{ {
_gd.PipelineInternal.AutoFlush.Present();
uint nextImage = 0; uint nextImage = 0;
while (true) while (true)