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