mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-31 06:59:45 +08:00
Astro Bot stack: VEH/TBB, title clear, swapchain fallback, Psml MFSR (#528)
* Cpu/Kernel: harden VEH trampoline and keep TBB on native workers Route FastFail/CLR/stack-overflow around managed VEH, serialize managed entry with a recursive spinlock, require native workers for tbb_thead, and abandon pthread mutexes when a guest thread is torn down by worker abort so splash waiters are not left holding locks forever. * Cpu: soft-fail TBB native worker storms and cap concurrent Runs Throwing on worker/prologue faults killed the process mid tbb_thead burst (FailFast 0xC0000409). Soft-return 0x80020012, limit in-flight native Runs (default 2), and keep prewarm small so back-to-back boots do not need an artificial settle delay. * Agc/VideoOut: poison-only empty-SRT reject and clear procedural ES/PS Skip QueueSubmit only when Address-0 image slots remain; run the Astro title clear pair via CmdClearColorImage so the pass executes without descriptors that lose the device. * VideoOut: recreate swapchain with fallback extent on 0x0 surface Minimized Win32 surfaces report 0x0 / MaxImageExtent=0; deferring recreate forever left an OutOfDate swapchain with no presents. Clamp to last/default size and recreate instead of early-return. * Psml: stub MFSR init/shared/context and dispatch packet size Astro Bot asserts in GfxRenderStagePSSR when scePsmlMfsrInit is unresolved (Mfsr initialized failed). Soft HLE for the MFSR shared-resource and 800M3_2 context path plus dispatch packet size lets boot pass splash to first frame without claiming real upscaling. * Psml: stub MFSR GetDispatchMfsrPacket900 for logo PSSR Astro StartLevel ps_logo asserted GfxRenderStagePSSR.cpp:266 when GetDispatchMfsrPacket900 (RUNLFro+qok) was unresolved. Return SCE_OK from SizeInDwords so the 900 fill runs, soft-clear the guest packet buffer, and register 1000/1100 siblings for the same ABI.
This commit is contained in:
@@ -54,6 +54,14 @@ internal sealed record VulkanOffscreenGuestDraw(
|
||||
bool PublishTarget,
|
||||
ulong ShaderAddress);
|
||||
|
||||
internal sealed record VulkanOffscreenColorClear(
|
||||
IReadOnlyList<GuestRenderTarget> Targets,
|
||||
float Red,
|
||||
float Green,
|
||||
float Blue,
|
||||
float Alpha,
|
||||
ulong ShaderAddress);
|
||||
|
||||
internal sealed record VulkanComputeGuestDispatch(
|
||||
ulong ShaderAddress,
|
||||
byte[] ComputeSpirv,
|
||||
@@ -1250,6 +1258,67 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Apply a solid color clear to offscreen guest render targets without a
|
||||
/// graphics pipeline. Used for empty-SRT procedural clear draws that
|
||||
/// otherwise lose the device on QueueSubmit with Address-0 descriptors.
|
||||
/// </summary>
|
||||
internal static void SubmitOffscreenColorClear(
|
||||
IReadOnlyList<GuestRenderTarget> targets,
|
||||
float red,
|
||||
float green,
|
||||
float blue,
|
||||
float alpha,
|
||||
ulong shaderAddress = 0)
|
||||
{
|
||||
if (targets.Count == 0 ||
|
||||
targets.Count > 8 ||
|
||||
AnyRenderTargetInvalid(targets))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var firstTarget = targets[0];
|
||||
if (RenderTargetsMismatchedOrAliased(targets, firstTarget))
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][WARN] Vulkan skipped MRT color clear with mismatched dimensions or aliased targets.");
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_gate)
|
||||
{
|
||||
if (_closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var target in targets)
|
||||
{
|
||||
var guestTextureFormat = GetGuestTextureFormat(
|
||||
target.Format,
|
||||
target.NumberType);
|
||||
if (guestTextureFormat != 0)
|
||||
{
|
||||
_availableGuestImages[target.Address] = guestTextureFormat;
|
||||
}
|
||||
}
|
||||
|
||||
var workSequence = EnqueueGuestWorkLocked(
|
||||
new VulkanOffscreenColorClear(
|
||||
targets.ToArray(),
|
||||
red,
|
||||
green,
|
||||
blue,
|
||||
alpha,
|
||||
shaderAddress));
|
||||
foreach (var target in targets)
|
||||
{
|
||||
_guestImageWorkSequences[target.Address] = workSequence;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static void SubmitGuestImageWrite(ulong address, byte[] pixels)
|
||||
{
|
||||
lock (_gate)
|
||||
@@ -2874,6 +2943,13 @@ internal static unsafe class VulkanVideoPresenter
|
||||
private readonly System.Collections.Concurrent.ConcurrentQueue<GuestImageResource> _pendingAliasImageDumps = new();
|
||||
private bool _deviceLost;
|
||||
private bool _deviceLostLogged;
|
||||
// Last guest work the render thread entered; included in device-lost
|
||||
// reports so QueueSubmit faults name the offending dispatch/draw.
|
||||
private string _activeGuestWorkLabel = string.Empty;
|
||||
// Survives the per-work finally clear: batched submits often flush
|
||||
// after the label is reset (queue switch / end-of-drain).
|
||||
private string _lastGuestWorkLabel = string.Empty;
|
||||
private string _lastSubmitDebugName = string.Empty;
|
||||
private int _directPresentationCount;
|
||||
private readonly Dictionary<ulong, long> _presentedGuestImageTraceCounts = new();
|
||||
private readonly Dictionary<ulong, GuestImageResource> _guestImages = new();
|
||||
@@ -4884,9 +4960,14 @@ internal static unsafe class VulkanVideoPresenter
|
||||
CommandBufferCount = 1,
|
||||
PCommandBuffers = &commandBuffer,
|
||||
};
|
||||
var submitContext = ResolveGuestSubmitContext(resources);
|
||||
_lastSubmitDebugName = submitContext;
|
||||
var submitLabel = string.IsNullOrEmpty(submitContext)
|
||||
? "vkQueueSubmit(guest)"
|
||||
: $"vkQueueSubmit(guest) during {submitContext}";
|
||||
Check(
|
||||
_vk.QueueSubmit(_queue, 1, &submitInfo, fence),
|
||||
"vkQueueSubmit(guest)");
|
||||
submitLabel);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -5039,6 +5120,17 @@ internal static unsafe class VulkanVideoPresenter
|
||||
if (result == Result.ErrorDeviceLost)
|
||||
{
|
||||
_deviceLost = true;
|
||||
if (!_deviceLostLogged)
|
||||
{
|
||||
_deviceLostLogged = true;
|
||||
var work = !string.IsNullOrEmpty(_lastGuestWorkLabel)
|
||||
? $"last_work={_lastGuestWorkLabel}"
|
||||
: "work=<none>";
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][ERROR] Vulkan device lost; dropping subsequent guest GPU work. " +
|
||||
$"{work} last_submit={oldest.DebugName} " +
|
||||
"vkWaitForFences(guest) failed with ErrorDeviceLost.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -10147,6 +10239,15 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
if (TryMarkDeviceLost(exception))
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][ERROR] Vulkan device lost during compute " +
|
||||
$"cs=0x{work.ShaderAddress:X16} " +
|
||||
$"groups={work.GroupCountX}x{work.GroupCountY}x{work.GroupCountZ} " +
|
||||
$"textures={work.Textures.Count} " +
|
||||
$"globals={work.GlobalMemoryBuffers.Count} " +
|
||||
$"writes_global={(work.WritesGlobalMemory ? 1 : 0)} " +
|
||||
$"indirect={(work.IsIndirect ? 1 : 0)} " +
|
||||
$"spirv={work.ComputeSpirv.Length}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10249,6 +10350,49 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
}
|
||||
|
||||
// Empty resource tables with non-trivial SPIR-V usually means the
|
||||
// SRT/EUD walk failed (scalar_pointer_fallback / srt=0). Binding
|
||||
// nothing while the module still declares descriptors is a common
|
||||
// device-loss trigger on the subsequent QueueSubmit.
|
||||
if (work.Textures.Count == 0 &&
|
||||
work.GlobalMemoryBuffers.Count == 0 &&
|
||||
work.ComputeSpirv.Length > 0)
|
||||
{
|
||||
error = "empty-resources";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Address-0 storage is host scratch for legitimate descriptors, but
|
||||
// after an empty SRT walk every binding can collapse to Address-0
|
||||
// fallbacks with no real globals — that path has lost the device
|
||||
// on Astro Bot right after the first presented frame.
|
||||
var hasUsableStorage = false;
|
||||
for (var i = 0; i < work.Textures.Count; i++)
|
||||
{
|
||||
var texture = work.Textures[i];
|
||||
if (texture.IsStorage && texture.Address != 0)
|
||||
{
|
||||
hasUsableStorage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var hasUsableGlobal = false;
|
||||
for (var i = 0; i < work.GlobalMemoryBuffers.Count; i++)
|
||||
{
|
||||
if (work.GlobalMemoryBuffers[i].BaseAddress != 0)
|
||||
{
|
||||
hasUsableGlobal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasUsableStorage && !hasUsableGlobal)
|
||||
{
|
||||
error = "no-usable-resources";
|
||||
return false;
|
||||
}
|
||||
|
||||
error = string.Empty;
|
||||
return true;
|
||||
}
|
||||
@@ -11056,6 +11200,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
transientFramebuffer = default;
|
||||
resources.DebugName =
|
||||
$"SharpEmu offscreen mrt={targets.Length} " +
|
||||
$"ps=0x{work.ShaderAddress:X16} " +
|
||||
$"first=0x{work.Targets[0].Address:X16} " +
|
||||
$"{firstTarget.Width}x{firstTarget.Height}";
|
||||
|
||||
@@ -11354,6 +11499,12 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
if (TryMarkDeviceLost(exception))
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][ERROR] Vulkan device lost during offscreen " +
|
||||
$"vs=0x{work.ShaderAddress:X16} " +
|
||||
$"mrt={work.Targets.Count} " +
|
||||
$"textures={work.Draw.Textures.Count} " +
|
||||
$"vertices={work.Draw.VertexCount}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -11397,6 +11548,133 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void ExecuteOffscreenColorClear(VulkanOffscreenColorClear work)
|
||||
{
|
||||
if (_deviceLost || work.Targets.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Interlocked.Increment(ref _perfDrawCount);
|
||||
PerfOverlay.RecordDraw();
|
||||
|
||||
var targetFormats = new VulkanRenderTargetFormat[work.Targets.Count];
|
||||
for (var index = 0; index < targetFormats.Length; index++)
|
||||
{
|
||||
var target = work.Targets[index];
|
||||
if (!TryDecodeRenderTargetFormat(target.Format, target.NumberType, out targetFormats[index]) ||
|
||||
!SupportsColorAttachment(targetFormats[index].Format))
|
||||
{
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][WARN] Vulkan skipped color clear for unsupported target " +
|
||||
$"0x{target.Address:X16} format={target.Format} number_type={target.NumberType}.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
EnsureGuestSubmissionCapacity();
|
||||
var commandBuffer = BeginBatchedGuestCommands();
|
||||
CloseOpenTranslatedRenderPass();
|
||||
var clearValue = new ClearColorValue(work.Red, work.Green, work.Blue, work.Alpha);
|
||||
|
||||
for (var index = 0; index < work.Targets.Count; index++)
|
||||
{
|
||||
var targetDescriptor = work.Targets[index];
|
||||
var image = GetOrCreateGuestImage(
|
||||
targetDescriptor,
|
||||
targetFormats[index].Format);
|
||||
if (TakeGuestImageInitialData(targetDescriptor.Address) is { } initialData &&
|
||||
!image.Initialized &&
|
||||
(ulong)initialData.Length ==
|
||||
(ulong)image.Width * image.Height * 4)
|
||||
{
|
||||
UploadGuestImageInitialData(image, initialData);
|
||||
}
|
||||
|
||||
var toTransferDst = new ImageMemoryBarrier
|
||||
{
|
||||
SType = StructureType.ImageMemoryBarrier,
|
||||
SrcAccessMask = image.Initialized ? AccessFlags.ShaderReadBit : 0,
|
||||
DstAccessMask = AccessFlags.TransferWriteBit,
|
||||
OldLayout = image.Initialized
|
||||
? ImageLayout.ShaderReadOnlyOptimal
|
||||
: ImageLayout.Undefined,
|
||||
NewLayout = ImageLayout.TransferDstOptimal,
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Image = image.Image,
|
||||
SubresourceRange = ColorSubresourceRange(0, image.MipLevels),
|
||||
};
|
||||
_vk.CmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
image.Initialized
|
||||
? PipelineStageFlags.FragmentShaderBit
|
||||
: PipelineStageFlags.TopOfPipeBit,
|
||||
PipelineStageFlags.TransferBit,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
&toTransferDst);
|
||||
|
||||
var range = ColorSubresourceRange(0, image.MipLevels);
|
||||
_vk.CmdClearColorImage(
|
||||
commandBuffer,
|
||||
image.Image,
|
||||
ImageLayout.TransferDstOptimal,
|
||||
&clearValue,
|
||||
1,
|
||||
&range);
|
||||
|
||||
var toShaderRead = new ImageMemoryBarrier
|
||||
{
|
||||
SType = StructureType.ImageMemoryBarrier,
|
||||
SrcAccessMask = AccessFlags.TransferWriteBit,
|
||||
DstAccessMask = AccessFlags.ShaderReadBit,
|
||||
OldLayout = ImageLayout.TransferDstOptimal,
|
||||
NewLayout = ImageLayout.ShaderReadOnlyOptimal,
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Image = image.Image,
|
||||
SubresourceRange = ColorSubresourceRange(0, image.MipLevels),
|
||||
};
|
||||
_vk.CmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
PipelineStageFlags.TransferBit,
|
||||
PipelineStageFlags.FragmentShaderBit,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
&toShaderRead);
|
||||
image.Initialized = true;
|
||||
|
||||
var guestTextureFormat = GetGuestTextureFormat(
|
||||
targetDescriptor.Format,
|
||||
targetDescriptor.NumberType);
|
||||
if (guestTextureFormat != 0)
|
||||
{
|
||||
lock (_gate)
|
||||
{
|
||||
_availableGuestImages[image.Address] = guestTextureFormat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_traceVulkanShaderEnabled)
|
||||
{
|
||||
TraceVulkanShader(
|
||||
$"vk.offscreen_color_clear mrt={work.Targets.Count} " +
|
||||
$"ps=0x{work.ShaderAddress:X16} " +
|
||||
$"rgba=({work.Red:0.###},{work.Green:0.###},{work.Blue:0.###},{work.Alpha:0.###})");
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void ExecuteGuestImageWrite(VulkanGuestImageWrite work)
|
||||
{
|
||||
@@ -12891,6 +13169,8 @@ internal static unsafe class VulkanVideoPresenter
|
||||
// A host command buffer must never contain commands from
|
||||
// two independent guest queues: an ordered action fences
|
||||
// only its own queue's predecessor submissions.
|
||||
// Keep the previous work label so a device-lost on this
|
||||
// flush still names the draws that filled the batch.
|
||||
FlushBatchedGuestCommands();
|
||||
}
|
||||
|
||||
@@ -12905,6 +13185,11 @@ internal static unsafe class VulkanVideoPresenter
|
||||
_enqueueAsImmediateQueueFollowup = true;
|
||||
_immediateFollowupTail = null;
|
||||
var work = pendingGuestWork.Work;
|
||||
_activeGuestWorkLabel = DescribeGuestWork(
|
||||
work,
|
||||
pendingGuestWork.Queue,
|
||||
pendingGuestWork.Sequence);
|
||||
_lastGuestWorkLabel = _activeGuestWorkLabel;
|
||||
var deferGuestWork = false;
|
||||
|
||||
var traceWork = ShouldTracePresentedGuestImageContentsForDiagnostics();
|
||||
@@ -12934,6 +13219,9 @@ internal static unsafe class VulkanVideoPresenter
|
||||
case VulkanOffscreenGuestDraw offscreenDraw:
|
||||
ExecuteOffscreenDraw(offscreenDraw);
|
||||
break;
|
||||
case VulkanOffscreenColorClear colorClear:
|
||||
ExecuteOffscreenColorClear(colorClear);
|
||||
break;
|
||||
case VulkanComputeGuestDispatch computeDispatch:
|
||||
ExecuteComputeDispatch(computeDispatch);
|
||||
break;
|
||||
@@ -12959,6 +13247,7 @@ internal static unsafe class VulkanVideoPresenter
|
||||
}
|
||||
_enqueueAsImmediateQueueFollowup = false;
|
||||
_immediateFollowupTail = null;
|
||||
_activeGuestWorkLabel = string.Empty;
|
||||
Volatile.Write(ref _executingGuestWorkSequence, 0);
|
||||
}
|
||||
|
||||
@@ -15929,7 +16218,14 @@ internal static unsafe class VulkanVideoPresenter
|
||||
{
|
||||
value = value <= 1 && fallback > 1 ? fallback : value;
|
||||
minimum = Math.Max(minimum, 1u);
|
||||
maximum = Math.Max(maximum, minimum);
|
||||
// Minimized / unmapped Win32 surfaces often report MaxImageExtent=0.
|
||||
// Clamping the fallback into [1,1] would create a useless 1x1
|
||||
// swapchain; keep the last/default size instead.
|
||||
if (maximum < minimum)
|
||||
{
|
||||
maximum = Math.Max(fallback, minimum);
|
||||
}
|
||||
|
||||
return Math.Clamp(value, minimum, maximum);
|
||||
}
|
||||
|
||||
@@ -16214,15 +16510,16 @@ internal static unsafe class VulkanVideoPresenter
|
||||
: (uint)Math.Max(framebufferSize.Y, 0);
|
||||
if (surfaceWidth <= 1 || surfaceHeight <= 1)
|
||||
{
|
||||
// Minimized / unmapped window reports 0x0. Deferring forever
|
||||
// leaves an OutOfDate swapchain with no presents. ChooseExtent
|
||||
// already clamps 0 to last/default size — recreate with that.
|
||||
if (!_swapchainRecreateDeferred)
|
||||
{
|
||||
_swapchainRecreateDeferred = true;
|
||||
Console.Error.WriteLine(
|
||||
$"[LOADER][INFO] Vulkan VideoOut deferred swapchain recreation: " +
|
||||
$"surface={surfaceWidth}x{surfaceHeight}");
|
||||
"[LOADER][INFO] Vulkan VideoOut swapchain recreate " +
|
||||
$"using fallback extent (window reported {surfaceWidth}x{surfaceHeight})");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
_swapchainRecreateDeferred = false;
|
||||
@@ -16429,14 +16726,86 @@ internal static unsafe class VulkanVideoPresenter
|
||||
if (!_deviceLostLogged)
|
||||
{
|
||||
_deviceLostLogged = true;
|
||||
var work = !string.IsNullOrEmpty(_activeGuestWorkLabel)
|
||||
? $"work={_activeGuestWorkLabel}"
|
||||
: !string.IsNullOrEmpty(_lastGuestWorkLabel)
|
||||
? $"last_work={_lastGuestWorkLabel}"
|
||||
: "work=<none>";
|
||||
var submit = string.IsNullOrEmpty(_lastSubmitDebugName)
|
||||
? string.Empty
|
||||
: $" last_submit={_lastSubmitDebugName}";
|
||||
Console.Error.WriteLine(
|
||||
"[LOADER][ERROR] Vulkan device lost; dropping subsequent guest GPU work. " +
|
||||
exception.Message);
|
||||
$"{work}{submit} {exception.Message}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private string ResolveGuestSubmitContext(
|
||||
IReadOnlyList<TranslatedDrawResources> resources)
|
||||
{
|
||||
var workLabel = !string.IsNullOrEmpty(_activeGuestWorkLabel)
|
||||
? _activeGuestWorkLabel
|
||||
: _lastGuestWorkLabel;
|
||||
var resourceName = resources.Count > 0
|
||||
? resources[0].DebugName
|
||||
: _batchResources.Count > 0
|
||||
? _batchResources[0].DebugName
|
||||
: string.Empty;
|
||||
if (string.IsNullOrEmpty(workLabel))
|
||||
{
|
||||
return string.IsNullOrEmpty(resourceName)
|
||||
? string.Empty
|
||||
: $"batch={resourceName}";
|
||||
}
|
||||
|
||||
return string.IsNullOrEmpty(resourceName)
|
||||
? workLabel
|
||||
: $"{workLabel} batch={resourceName}";
|
||||
}
|
||||
|
||||
private static string DescribeGuestWork(
|
||||
object work,
|
||||
VulkanGuestQueueIdentity queue,
|
||||
long sequence)
|
||||
{
|
||||
var queuePart =
|
||||
$"queue={queue.Name} submission={queue.SubmissionId} sequence={sequence}";
|
||||
return work switch
|
||||
{
|
||||
VulkanComputeGuestDispatch compute =>
|
||||
$"compute cs=0x{compute.ShaderAddress:X16} " +
|
||||
$"groups={compute.GroupCountX}x{compute.GroupCountY}x{compute.GroupCountZ} " +
|
||||
$"textures={compute.Textures.Count} " +
|
||||
$"globals={compute.GlobalMemoryBuffers.Count} " +
|
||||
$"writes_global={(compute.WritesGlobalMemory ? 1 : 0)} " +
|
||||
$"indirect={(compute.IsIndirect ? 1 : 0)} " +
|
||||
$"spirv={compute.ComputeSpirv.Length} {queuePart}",
|
||||
VulkanOffscreenGuestDraw draw =>
|
||||
$"offscreen vs=0x{draw.ShaderAddress:X16} " +
|
||||
$"mrt={draw.Targets.Count} " +
|
||||
$"textures={draw.Draw.Textures.Count} " +
|
||||
$"vertices={draw.Draw.VertexCount} {queuePart}",
|
||||
VulkanOffscreenColorClear clear =>
|
||||
$"offscreen_clear ps=0x{clear.ShaderAddress:X16} " +
|
||||
$"mrt={clear.Targets.Count} " +
|
||||
$"rgba=({clear.Red:0.###},{clear.Green:0.###},{clear.Blue:0.###},{clear.Alpha:0.###}) " +
|
||||
queuePart,
|
||||
VulkanGuestImageWrite imageWrite =>
|
||||
$"image_write addr=0x{imageWrite.Address:X16} {queuePart}",
|
||||
VulkanOrderedGuestAction action =>
|
||||
$"ordered_action name={action.DebugName} {queuePart}",
|
||||
VulkanOrderedGuestFlip flip =>
|
||||
$"ordered_flip version={flip.Version} " +
|
||||
$"buf={flip.DisplayBufferIndex} addr=0x{flip.Address:X16} {queuePart}",
|
||||
VulkanOrderedGuestFlipWait wait =>
|
||||
$"flip_wait version={wait.Version} " +
|
||||
$"buf={wait.DisplayBufferIndex} {queuePart}",
|
||||
_ => $"{work.GetType().Name} {queuePart}",
|
||||
};
|
||||
}
|
||||
|
||||
private static void TraceVulkanShader(string message)
|
||||
{
|
||||
if (!_traceVulkanShaderEnabled)
|
||||
|
||||
Reference in New Issue
Block a user