Vulkan: trim disabled draw diagnostics overhead (#307)

This commit is contained in:
kuba
2026-07-17 03:18:06 +02:00
committed by GitHub
parent 28485b60e2
commit dbd74654c4
+241 -125
View File
@@ -2775,6 +2775,11 @@ internal static unsafe class VulkanVideoPresenter
private void LoadDebugUtilsCommands()
{
if (!_vulkanDebugUtilsEnabled)
{
return;
}
var setObjectName = _vk.GetDeviceProcAddr(_device, "vkSetDebugUtilsObjectNameEXT");
var beginLabel = _vk.GetDeviceProcAddr(_device, "vkCmdBeginDebugUtilsLabelEXT");
var endLabel = _vk.GetDeviceProcAddr(_device, "vkCmdEndDebugUtilsLabelEXT");
@@ -2877,10 +2882,48 @@ internal static unsafe class VulkanVideoPresenter
$"SharpEmu texture 0x{texture.Address:X16} {texture.Width}x{texture.Height} " +
$"fmt{texture.Format}/{format}";
private static bool IsTitleDraw(IReadOnlyList<GuestVertexBuffer> vertexBuffers)
{
foreach (var buffer in vertexBuffers)
{
if (buffer.Location == 0 &&
buffer.ComponentCount == 4 &&
buffer.DataFormat == 10 &&
buffer.NumberFormat == 0 &&
buffer.Stride == 16 &&
buffer.OffsetBytes == 12 &&
buffer.Length == 67568)
{
return true;
}
}
return false;
}
private static bool AnyTargetAddressMatches(
IReadOnlyList<GuestImageResource>? targets,
string environmentVariable)
{
if (targets is null)
{
return false;
}
foreach (var target in targets)
{
if (AddressListContains(environmentVariable, target.Address))
{
return true;
}
}
return false;
}
private void CreateInstance()
{
var applicationName = (byte*)SilkMarshal.StringToPtr("SharpEmu");
var enableValidation = Environment.GetEnvironmentVariable("SHARPEMU_VK_VALIDATION") == "1";
byte* validationLayerName = null;
try
@@ -2906,7 +2949,8 @@ internal static unsafe class VulkanVideoPresenter
enabledExtensions[index] = extensions[index];
}
if (IsInstanceExtensionAvailable(DebugUtilsExtensionName))
if (_vulkanDebugUtilsEnabled &&
IsInstanceExtensionAvailable(DebugUtilsExtensionName))
{
debugUtilsExtension = (byte*)SilkMarshal.StringToPtr(DebugUtilsExtensionName);
enabledExtensions[enabledExtensionCount++] = debugUtilsExtension;
@@ -2921,11 +2965,12 @@ internal static unsafe class VulkanVideoPresenter
instanceCreateFlags |= InstanceCreateFlags.EnumeratePortabilityBitKhr;
}
if (enableValidation && IsInstanceLayerAvailable("VK_LAYER_KHRONOS_validation"))
if (_vulkanValidationEnabled &&
IsInstanceLayerAvailable("VK_LAYER_KHRONOS_validation"))
{
validationLayerName = (byte*)SilkMarshal.StringToPtr("VK_LAYER_KHRONOS_validation");
}
else if (enableValidation)
else if (_vulkanValidationEnabled)
{
Console.Error.WriteLine("[LOADER][WARN] SHARPEMU_VK_VALIDATION=1 but VK_LAYER_KHRONOS_validation not found (Vulkan SDK installed?).");
}
@@ -4898,6 +4943,13 @@ internal static unsafe class VulkanVideoPresenter
IReadOnlyList<GuestImageResource>? renderTargets = null,
ulong shaderAddress = 0)
{
if (!_traceGuestImagesEnabled &&
!_traceGuestImageAddressFilterEnabled &&
GuestImageTraceInterval() is null)
{
return Array.Empty<GuestImageResource>();
}
var candidates = new HashSet<GuestImageResource>();
foreach (var renderTarget in renderTargets ?? [])
{
@@ -5140,47 +5192,35 @@ internal static unsafe class VulkanVideoPresenter
bool hasDepthAttachment = false,
GuestDepthResource? feedbackDepth = null)
{
var isTitleDraw = draw.VertexBuffers.Any(buffer =>
buffer.Location == 0 &&
buffer.ComponentCount == 4 &&
buffer.DataFormat == 10 &&
buffer.NumberFormat == 0 &&
buffer.Stride == 16 &&
buffer.OffsetBytes == 12 &&
buffer.Length == 67568);
var forceFullscreenPipeline = Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_FULLSCREEN_PIPELINE") == "1";
var forceFullscreenVertex = forceFullscreenPipeline ||
Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_FULLSCREEN_VERTEX") == "1" ||
isTitleDraw && Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_TITLE_FULLSCREEN_VERTEX") == "1" ||
feedbackTargets?.Any(target => AddressListContains(
"SHARPEMU_FORCE_FULLSCREEN_VERTEX_TARGETS",
target.Address)) == true;
var forceRasterState = forceFullscreenPipeline ||
Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_DEFAULT_RASTER_STATE") == "1" ||
isTitleDraw && Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_TITLE_DEFAULT_RASTER_STATE") == "1" ||
feedbackTargets?.Any(target => AddressListContains(
"SHARPEMU_FORCE_DEFAULT_RASTER_STATE_TARGETS",
target.Address)) == true;
var isTitleDraw = IsTitleDraw(draw.VertexBuffers);
var forceFullscreenVertex = _forceFullscreenPipeline ||
_forceFullscreenVertex ||
isTitleDraw && _forceTitleFullscreenVertex ||
AnyTargetAddressMatches(
feedbackTargets,
"SHARPEMU_FORCE_FULLSCREEN_VERTEX_TARGETS");
var forceRasterState = _forceFullscreenPipeline ||
_forceDefaultRasterState ||
isTitleDraw && _forceTitleDefaultRasterState ||
AnyTargetAddressMatches(
feedbackTargets,
"SHARPEMU_FORCE_DEFAULT_RASTER_STATE_TARGETS");
var forceTitleSolidFragment =
Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_TITLE_SOLID_FRAGMENT") == "1" &&
_forceTitleSolidFragment &&
isTitleDraw;
var forceSolidFragment = forceTitleSolidFragment || forceFullscreenPipeline ||
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_SOLID_FRAGMENT") == "1" ||
feedbackTargets?.Any(target => AddressListContains(
"SHARPEMU_FORCE_SOLID_FRAGMENT_TARGETS",
target.Address)) == true;
var forceAttributeFragment = uint.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_ATTRIBUTE_FRAGMENT"),
out var attributeFragmentLocation) &&
feedbackTargets?.Any(target => AddressListContains(
"SHARPEMU_FORCE_ATTRIBUTE_FRAGMENT_TARGETS",
target.Address)) == true;
var forceSolidFragment = forceTitleSolidFragment ||
_forceFullscreenPipeline ||
_forceSolidFragment ||
AnyTargetAddressMatches(
feedbackTargets,
"SHARPEMU_FORCE_SOLID_FRAGMENT_TARGETS");
var attributeFragmentLocation =
_forceAttributeFragmentLocation.GetValueOrDefault();
var forceAttributeFragment =
_forceAttributeFragmentLocation.HasValue &&
AnyTargetAddressMatches(
feedbackTargets,
"SHARPEMU_FORCE_ATTRIBUTE_FRAGMENT_TARGETS");
var vertexSpirv = forceFullscreenVertex
? SpirvFixedShaders.CreateFullscreenVertex(0)
: draw.VertexSpirv;
@@ -5189,11 +5229,9 @@ internal static unsafe class VulkanVideoPresenter
: forceAttributeFragment
? SpirvFixedShaders.CreateAttributeFragment(attributeFragmentLocation)
: draw.PixelSpirv;
var fixedFragmentDumpPath = Environment.GetEnvironmentVariable(
"SHARPEMU_DUMP_FIXED_SOLID_FRAGMENT");
if (forceSolidFragment && !string.IsNullOrWhiteSpace(fixedFragmentDumpPath))
if (forceSolidFragment && !string.IsNullOrWhiteSpace(_fixedFragmentDumpPath))
{
File.WriteAllBytes(fixedFragmentDumpPath, fragmentSpirv);
File.WriteAllBytes(_fixedFragmentDumpPath, fragmentSpirv);
}
if (draw.RenderState.Blends.Count != renderTargetFormats.Count)
{
@@ -5243,21 +5281,18 @@ internal static unsafe class VulkanVideoPresenter
resources.Raster = GuestRasterState.Default;
resources.Depth = GuestDepthState.Default;
}
if (isTitleDraw && Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_TITLE_DEFAULT_BLEND") == "1")
if (isTitleDraw && _forceTitleDefaultBlend)
{
resources.Blends = Enumerable.Repeat(
GuestBlendState.Default,
renderTargetFormats.Count).ToArray();
}
if (isTitleDraw && Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_TITLE_DEFAULT_VIEWPORT_SCISSOR") == "1")
if (isTitleDraw && _forceTitleDefaultViewportScissor)
{
resources.Scissor = null;
resources.Viewport = null;
}
if (isTitleDraw && Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_TITLE_DISABLE_CULL") == "1")
if (isTitleDraw && _forceTitleDisableCull)
{
resources.Raster = resources.Raster with
{
@@ -5265,13 +5300,11 @@ internal static unsafe class VulkanVideoPresenter
CullBack = false,
};
}
if (isTitleDraw && Environment.GetEnvironmentVariable(
"SHARPEMU_FORCE_TITLE_DISABLE_DEPTH") == "1")
if (isTitleDraw && _forceTitleDisableDepth)
{
resources.Depth = GuestDepthState.Default;
}
if (isTitleDraw && Environment.GetEnvironmentVariable(
"SHARPEMU_TRACE_TITLE_STATE") == "1")
if (isTitleDraw && _traceTitleState)
{
Console.Error.WriteLine(
$"[LOADER][TRACE] vk.title_state " +
@@ -7874,7 +7907,7 @@ internal static unsafe class VulkanVideoPresenter
{
ReadOnlySpan<byte> source = guestBuffer.Data.AsSpan(0, guestBuffer.Length);
byte[]? forcedVertexColors = null;
if (Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_VERTEX_COLOR_WHITE") == "1" &&
if (_forceTitleVertexColorWhite &&
guestBuffer.Location == 0 &&
guestBuffer.ComponentCount == 4 &&
guestBuffer.DataFormat == 10 &&
@@ -9400,37 +9433,67 @@ internal static unsafe class VulkanVideoPresenter
return;
}
if (targetFormats
.Select((format, index) => format.IsInteger && work.Draw.RenderState.Blends[index].Enable)
.Any(invalid => invalid))
for (var index = 0; index < targetFormats.Length; index++)
{
Console.Error.WriteLine(
"[LOADER][WARN] Vulkan skipped MRT draw with blending enabled for an integer attachment.");
ReturnPooledGuestData(work.Draw);
return;
if (targetFormats[index].IsInteger &&
work.Draw.RenderState.Blends[index].Enable)
{
Console.Error.WriteLine(
"[LOADER][WARN] Vulkan skipped MRT draw with blending enabled for an integer attachment.");
ReturnPooledGuestData(work.Draw);
return;
}
}
if (!_supportsIndependentBlend &&
work.Draw.RenderState.Blends.Skip(1).Any(blend => blend != work.Draw.RenderState.Blends[0]))
if (!_supportsIndependentBlend)
{
Console.Error.WriteLine(
"[LOADER][WARN] Vulkan skipped MRT draw requiring unsupported independentBlend.");
ReturnPooledGuestData(work.Draw);
return;
for (var index = 1; index < work.Draw.RenderState.Blends.Count; index++)
{
if (work.Draw.RenderState.Blends[index] !=
work.Draw.RenderState.Blends[0])
{
Console.Error.WriteLine(
"[LOADER][WARN] Vulkan skipped MRT draw requiring unsupported independentBlend.");
ReturnPooledGuestData(work.Draw);
return;
}
}
}
var formats = targetFormats.Select(target => target.Format).ToArray();
var formats = new Format[targetFormats.Length];
for (var index = 0; index < targetFormats.Length; index++)
{
formats[index] = targetFormats[index].Format;
}
var targetAddresses = work.Targets
.Where(target => target.Address != 0)
.Select(target => target.Address)
.ToHashSet();
if (work.Draw.Textures.Any(texture =>
texture.IsStorage && targetAddresses.Contains(texture.Address)))
var hasStorageFeedback = false;
foreach (var texture in work.Draw.Textures)
{
if (!texture.IsStorage || texture.Address == 0)
{
continue;
}
foreach (var target in work.Targets)
{
if (target.Address != 0 && target.Address == texture.Address)
{
hasStorageFeedback = true;
break;
}
}
if (hasStorageFeedback)
{
break;
}
}
if (hasStorageFeedback)
{
Console.Error.WriteLine(
$"[LOADER][WARN] Vulkan skipped storage render-target feedback loop " +
$"targets={string.Join(',', targetAddresses.Select(address => $"0x{address:X16}"))}; " +
$"targets={string.Join(',', work.Targets.Where(target => target.Address != 0).Select(target => $"0x{target.Address:X16}"))}; " +
"sampled aliases use ordered snapshots");
ReturnPooledGuestData(work.Draw);
return;
@@ -9802,64 +9865,34 @@ internal static unsafe class VulkanVideoPresenter
}
}
var guestWritesMode = Environment.GetEnvironmentVariable(
"SHARPEMU_TRACE_GUEST_WRITES");
var traceAddressWriteOrdinal = 0L;
_ = long.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_GUEST_WRITE_ORDINAL"),
out traceAddressWriteOrdinal);
var traceLargeWriteOrdinal = 0L;
if (guestWritesMode is not null &&
guestWritesMode.StartsWith("large@", StringComparison.Ordinal) &&
long.TryParse(
guestWritesMode.AsSpan("large@".Length),
out var parsedTraceLargeWriteOrdinal) &&
parsedTraceLargeWriteOrdinal > 0)
{
traceLargeWriteOrdinal = parsedTraceLargeWriteOrdinal;
}
var tracePixelSpirv = false;
if (int.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_PIXEL_SPIRV_BYTES"),
out var tracePixelSpirvBytes) &&
tracePixelSpirvBytes == work.Draw.PixelSpirv.Length)
if (_tracePixelSpirvBytes > 0 &&
_tracePixelSpirvBytes == work.Draw.PixelSpirv.Length)
{
var pixelWriteCount = _pixelSpirvWriteCounts.TryGetValue(
tracePixelSpirvBytes,
_tracePixelSpirvBytes,
out var previousPixelWriteCount)
? previousPixelWriteCount + 1
: 1;
_pixelSpirvWriteCounts[tracePixelSpirvBytes] = pixelWriteCount;
var tracePixelSpirvOccurrence = 1;
_ = int.TryParse(
Environment.GetEnvironmentVariable(
"SHARPEMU_TRACE_PIXEL_SPIRV_OCCURRENCE"),
out tracePixelSpirvOccurrence);
_pixelSpirvWriteCounts[_tracePixelSpirvBytes] = pixelWriteCount;
tracePixelSpirv =
pixelWriteCount == Math.Max(tracePixelSpirvOccurrence, 1);
pixelWriteCount == _tracePixelSpirvOccurrence;
}
var traceTitleDraw =
!_tracedTitleDraw &&
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_TITLE_DRAW") == "1" &&
work.Draw.VertexBuffers.Any(buffer =>
buffer.Location == 0 &&
buffer.ComponentCount == 4 &&
buffer.DataFormat == 10 &&
buffer.NumberFormat == 0 &&
buffer.Stride == 16 &&
buffer.OffsetBytes == 12 &&
buffer.Length == 67568);
_traceTitleDrawEnabled &&
IsTitleDraw(work.Draw.VertexBuffers);
_tracedTitleDraw |= traceTitleDraw;
foreach (var target in targets)
{
var traceAddressWrite =
ShouldTraceGuestImageWriteForDiagnostics(target.Address);
var traceSmallWrites = guestWritesMode == "small" &&
var traceSmallWrites = _traceGuestWritesMode == "small" &&
target.Width <= 512 && target.Height <= 256;
var traceLargeWrites =
(guestWritesMode == "large" || traceLargeWriteOrdinal != 0) &&
(_traceGuestWritesMode == "large" ||
_traceLargeGuestWriteOrdinal != 0) &&
target.Width >= 2560 && target.Height >= 1440;
if (traceAddressWrite || traceSmallWrites ||
traceLargeWrites || tracePixelSpirv || traceTitleDraw)
@@ -9872,10 +9905,10 @@ internal static unsafe class VulkanVideoPresenter
_tracedGuestWriteCounts[target.Address] = writeCount;
var shouldTraceWrite = tracePixelSpirv || traceTitleDraw
? true
: traceAddressWrite && traceAddressWriteOrdinal > 0
? writeCount == traceAddressWriteOrdinal
: traceLargeWriteOrdinal != 0
? writeCount == traceLargeWriteOrdinal
: traceAddressWrite && _traceGuestWriteOrdinal > 0
? writeCount == _traceGuestWriteOrdinal
: _traceLargeGuestWriteOrdinal != 0
? writeCount == _traceLargeGuestWriteOrdinal
: writeCount <=
(traceLargeWrites ? 2 : traceSmallWrites ? 48 : 3);
if (traceAddressWrite || shouldTraceWrite)
@@ -12849,6 +12882,20 @@ internal static unsafe class VulkanVideoPresenter
// Diagnostics toggles are read once: these run per draw / per cached
// texture hit, and env lookups plus string parsing are far too
// expensive there (and non-trivially so under Rosetta 2).
private static readonly bool _vulkanValidationEnabled =
string.Equals(
Environment.GetEnvironmentVariable("SHARPEMU_VK_VALIDATION"),
"1",
StringComparison.Ordinal);
// Object names and command labels are useful in RenderDoc and validation
// captures, but formatting them and calling the debug-utils driver hooks
// for every draw is measurable overhead in normal gameplay.
private static readonly bool _vulkanDebugUtilsEnabled =
_vulkanValidationEnabled ||
string.Equals(
Environment.GetEnvironmentVariable("SHARPEMU_VK_DEBUG_LABELS"),
"1",
StringComparison.Ordinal);
private static readonly string? _traceGuestImagesMode =
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_GUEST_IMAGES");
private static readonly bool _traceGuestImagesEnabled =
@@ -12912,10 +12959,80 @@ internal static unsafe class VulkanVideoPresenter
!string.IsNullOrWhiteSpace(
Environment.GetEnvironmentVariable(
"SHARPEMU_TRACE_GUEST_IMAGE_ADDRS"));
private static readonly bool _forceFullscreenPipeline =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_FULLSCREEN_PIPELINE") == "1";
private static readonly bool _forceFullscreenVertex =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_FULLSCREEN_VERTEX") == "1";
private static readonly bool _forceTitleFullscreenVertex =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_FULLSCREEN_VERTEX") == "1";
private static readonly bool _forceDefaultRasterState =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_DEFAULT_RASTER_STATE") == "1";
private static readonly bool _forceTitleDefaultRasterState =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_DEFAULT_RASTER_STATE") == "1";
private static readonly bool _forceTitleSolidFragment =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_SOLID_FRAGMENT") == "1";
private static readonly bool _forceSolidFragment =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_SOLID_FRAGMENT") == "1";
private static readonly uint? _forceAttributeFragmentLocation =
uint.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_ATTRIBUTE_FRAGMENT"),
out var forceAttributeFragmentLocation)
? forceAttributeFragmentLocation
: null;
private static readonly string? _fixedFragmentDumpPath =
Environment.GetEnvironmentVariable("SHARPEMU_DUMP_FIXED_SOLID_FRAGMENT");
private static readonly bool _forceTitleDefaultBlend =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_DEFAULT_BLEND") == "1";
private static readonly bool _forceTitleDefaultViewportScissor =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_DEFAULT_VIEWPORT_SCISSOR") == "1";
private static readonly bool _forceTitleDisableCull =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_DISABLE_CULL") == "1";
private static readonly bool _forceTitleDisableDepth =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_DISABLE_DEPTH") == "1";
private static readonly bool _traceTitleState =
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_TITLE_STATE") == "1";
private static readonly bool _forceTitleVertexColorWhite =
Environment.GetEnvironmentVariable("SHARPEMU_FORCE_TITLE_VERTEX_COLOR_WHITE") == "1";
private static readonly bool _chunkedDrawsEnabled =
Environment.GetEnvironmentVariable("SHARPEMU_ENABLE_CHUNKED_DRAWS") == "1";
private static readonly string? _traceGuestWritesMode =
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_GUEST_WRITES");
private static readonly long _traceGuestWriteOrdinal =
long.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_GUEST_WRITE_ORDINAL"),
out var traceGuestWriteOrdinal)
? traceGuestWriteOrdinal
: 0;
private static readonly long _traceLargeGuestWriteOrdinal =
ParseTraceLargeGuestWriteOrdinal(_traceGuestWritesMode);
private static readonly int _tracePixelSpirvBytes =
int.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_PIXEL_SPIRV_BYTES"),
out var tracePixelSpirvBytes)
? tracePixelSpirvBytes
: 0;
private static readonly int _tracePixelSpirvOccurrence =
int.TryParse(
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_PIXEL_SPIRV_OCCURRENCE"),
out var tracePixelSpirvOccurrence)
? Math.Max(tracePixelSpirvOccurrence, 1)
: 1;
private static readonly bool _traceTitleDrawEnabled =
Environment.GetEnvironmentVariable("SHARPEMU_TRACE_TITLE_DRAW") == "1";
private static readonly System.Collections.Concurrent.ConcurrentDictionary<
string,
(bool Wildcard, ulong[] Addresses)> _cachedAddressLists = new();
private static long ParseTraceLargeGuestWriteOrdinal(string? mode)
{
return mode is not null &&
mode.StartsWith("large@", StringComparison.Ordinal) &&
long.TryParse(mode.AsSpan("large@".Length), out var ordinal) &&
ordinal > 0
? ordinal
: 0;
}
private static bool ShouldTraceGuestImageContentsForDiagnostics() =>
_traceGuestImagesEnabled;
@@ -13113,8 +13230,7 @@ internal static unsafe class VulkanVideoPresenter
// MoltenVK this starves the render thread and makes the guest fall
// behind its own flip queue. Vulkan clips a normal fullscreen draw
// efficiently; keep tiling only as an explicit driver diagnostic.
var maxPixelsPerDraw = Environment.GetEnvironmentVariable(
"SHARPEMU_ENABLE_CHUNKED_DRAWS") == "1"
var maxPixelsPerDraw = _chunkedDrawsEnabled
? 512u * 512u
: uint.MaxValue;
var rowsPerDraw = Math.Max(