BufferedQuery.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.GAL;
  3. using Silk.NET.Vulkan;
  4. using System;
  5. using System.Runtime.InteropServices;
  6. using System.Threading;
  7. namespace Ryujinx.Graphics.Vulkan.Queries
  8. {
  9. class BufferedQuery : IDisposable
  10. {
  11. private const int MaxQueryRetries = 5000;
  12. private const long DefaultValue = -1;
  13. private const long DefaultValueInt = 0xFFFFFFFF;
  14. private const ulong HighMask = 0xFFFFFFFF00000000;
  15. private readonly Vk _api;
  16. private readonly Device _device;
  17. private readonly PipelineFull _pipeline;
  18. private QueryPool _queryPool;
  19. private readonly BufferHolder _buffer;
  20. private readonly IntPtr _bufferMap;
  21. private readonly CounterType _type;
  22. private bool _result32Bit;
  23. private bool _isSupported;
  24. private long _defaultValue;
  25. private int? _resetSequence;
  26. public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit)
  27. {
  28. _api = gd.Api;
  29. _device = device;
  30. _pipeline = pipeline;
  31. _type = type;
  32. _result32Bit = result32Bit;
  33. _isSupported = QueryTypeSupported(gd, type);
  34. if (_isSupported)
  35. {
  36. QueryPipelineStatisticFlags flags = type == CounterType.PrimitivesGenerated ?
  37. QueryPipelineStatisticFlags.GeometryShaderPrimitivesBit : 0;
  38. var queryPoolCreateInfo = new QueryPoolCreateInfo()
  39. {
  40. SType = StructureType.QueryPoolCreateInfo,
  41. QueryCount = 1,
  42. QueryType = GetQueryType(type),
  43. PipelineStatistics = flags
  44. };
  45. gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
  46. }
  47. var buffer = gd.BufferManager.Create(gd, sizeof(long), forConditionalRendering: true);
  48. _bufferMap = buffer.Map(0, sizeof(long));
  49. _defaultValue = result32Bit ? DefaultValueInt : DefaultValue;
  50. Marshal.WriteInt64(_bufferMap, _defaultValue);
  51. _buffer = buffer;
  52. }
  53. private bool QueryTypeSupported(VulkanRenderer gd, CounterType type)
  54. {
  55. return type switch
  56. {
  57. CounterType.SamplesPassed => true,
  58. CounterType.PrimitivesGenerated => gd.Capabilities.SupportsPipelineStatisticsQuery,
  59. CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries,
  60. _ => false
  61. };
  62. }
  63. private static QueryType GetQueryType(CounterType type)
  64. {
  65. return type switch
  66. {
  67. CounterType.SamplesPassed => QueryType.Occlusion,
  68. CounterType.PrimitivesGenerated => QueryType.PipelineStatistics,
  69. CounterType.TransformFeedbackPrimitivesWritten => QueryType.TransformFeedbackStreamExt,
  70. _ => QueryType.Occlusion
  71. };
  72. }
  73. public Auto<DisposableBuffer> GetBuffer()
  74. {
  75. return _buffer.GetBuffer();
  76. }
  77. public void Reset()
  78. {
  79. End(false);
  80. Begin(null);
  81. }
  82. public void Begin(int? resetSequence)
  83. {
  84. if (_isSupported)
  85. {
  86. bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
  87. bool isOcclusion = _type == CounterType.SamplesPassed;
  88. _pipeline.BeginQuery(this, _queryPool, needsReset, isOcclusion, isOcclusion && resetSequence != null);
  89. }
  90. _resetSequence = null;
  91. }
  92. public unsafe void End(bool withResult)
  93. {
  94. if (_isSupported)
  95. {
  96. _pipeline.EndQuery(_queryPool);
  97. }
  98. if (withResult && _isSupported)
  99. {
  100. Marshal.WriteInt64(_bufferMap, _defaultValue);
  101. _pipeline.CopyQueryResults(this);
  102. }
  103. else
  104. {
  105. // Dummy result, just return 0.
  106. Marshal.WriteInt64(_bufferMap, 0);
  107. }
  108. }
  109. private bool WaitingForValue(long data)
  110. {
  111. return data == _defaultValue ||
  112. (!_result32Bit && ((ulong)data & HighMask) == ((ulong)_defaultValue & HighMask));
  113. }
  114. public bool TryGetResult(out long result)
  115. {
  116. result = Marshal.ReadInt64(_bufferMap);
  117. return result != _defaultValue;
  118. }
  119. public long AwaitResult(AutoResetEvent wakeSignal = null)
  120. {
  121. long data = _defaultValue;
  122. if (wakeSignal == null)
  123. {
  124. while (WaitingForValue(data))
  125. {
  126. data = Marshal.ReadInt64(_bufferMap);
  127. }
  128. }
  129. else
  130. {
  131. int iterations = 0;
  132. while (WaitingForValue(data) && iterations++ < MaxQueryRetries)
  133. {
  134. data = Marshal.ReadInt64(_bufferMap);
  135. if (WaitingForValue(data))
  136. {
  137. wakeSignal.WaitOne(1);
  138. }
  139. }
  140. if (iterations >= MaxQueryRetries)
  141. {
  142. Logger.Error?.Print(LogClass.Gpu, $"Error: Query result {_type} timed out. Took more than {MaxQueryRetries} tries.");
  143. }
  144. }
  145. return data;
  146. }
  147. public void PoolReset(CommandBuffer cmd, int resetSequence)
  148. {
  149. if (_isSupported)
  150. {
  151. _api.CmdResetQueryPool(cmd, _queryPool, 0, 1);
  152. }
  153. _resetSequence = resetSequence;
  154. }
  155. public void PoolCopy(CommandBufferScoped cbs)
  156. {
  157. var buffer = _buffer.GetBuffer(cbs.CommandBuffer, true).Get(cbs, 0, sizeof(long)).Value;
  158. QueryResultFlags flags = QueryResultFlags.ResultWaitBit;
  159. if (!_result32Bit)
  160. {
  161. flags |= QueryResultFlags.Result64Bit;
  162. }
  163. _api.CmdCopyQueryPoolResults(
  164. cbs.CommandBuffer,
  165. _queryPool,
  166. 0,
  167. 1,
  168. buffer,
  169. 0,
  170. (ulong)(_result32Bit ? sizeof(int) : sizeof(long)),
  171. flags);
  172. }
  173. public unsafe void Dispose()
  174. {
  175. _buffer.Dispose();
  176. if (_isSupported)
  177. {
  178. _api.DestroyQueryPool(_device, _queryPool, null);
  179. }
  180. _queryPool = default;
  181. }
  182. }
  183. }