From 5f1bd5a77c6f820d3d495d05be2cdf8dcfe74a6f Mon Sep 17 00:00:00 2001 From: Astell <34811047+Astellou@users.noreply.github.com> Date: Thu, 30 Jul 2026 11:58:43 +0200 Subject: [PATCH] fix vulkan error from astrobot (#689) * fix vulkan error from astrobot * a bit of a clean * a bit of a clean --- .../VideoOut/VulkanVideoPresenter.cs | 116 +++++++++++++++--- 1 file changed, 102 insertions(+), 14 deletions(-) diff --git a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs index 5a1b3943..6de2f2a3 100644 --- a/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs +++ b/src/SharpEmu.Libs/VideoOut/VulkanVideoPresenter.cs @@ -9271,6 +9271,27 @@ internal static unsafe class VulkanVideoPresenter !_guestImages.ContainsKey(texture.Address)) { var guestFormat = GetGuestTextureFormat(texture.Format, texture.NumberType); + + var canonicalView = view; + if (texture.DstSelect != 0xFAC) + { + var identityViewInfo = new ImageViewCreateInfo + { + SType = StructureType.ImageViewCreateInfo, + Image = image, + ViewType = GetGuestTextureViewType( + texture.Type, + texture.ArrayedView), + Format = vkFormat, + Components = ToVkComponentMapping(0xFAC), + SubresourceRange = ColorSubresourceRange(layerCount: layers), + }; + Check( + _vk.CreateImageView(_device, &identityViewInfo, null, out canonicalView), + "vkCreateImageView(texture identity)"); + SetDebugName(ObjectType.ImageView, canonicalView.Handle, $"{debugName} identity view"); + } + var guestImage = new GuestImageResource { Address = texture.Address, @@ -9286,7 +9307,7 @@ internal static unsafe class VulkanVideoPresenter Format = vkFormat, Image = image, Memory = imageMemory, - View = view, + View = canonicalView, InitialUploadPending = true, IsCpuBacked = true, CpuContentFingerprint = contentFingerprint, @@ -11186,6 +11207,8 @@ internal static unsafe class VulkanVideoPresenter EnsureGuestSubmissionCapacity(); resources = CreateComputeDispatchResources(work); + FlushBatchedGuestCommands(); + var batchCount = Math.Max( 1u, (uint)Math.Ceiling(work.GroupCountZ / (double)MaxComputeZSlicesPerSubmission)); @@ -14338,11 +14361,6 @@ internal static unsafe class VulkanVideoPresenter pendingGuestWork.Queue.Name, StringComparison.Ordinal)) { - // 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(); } @@ -14382,14 +14400,7 @@ internal static unsafe class VulkanVideoPresenter } try { - // A host-decoded movie only overrides which image gets - // presented (see the presentation selection below); the - // guest's own command stream keeps draining normally. - // Silently discarding these instead of executing them - // leaves guest-visible completion state (labels, buffers, - // job results) permanently unwritten, which desyncs the - // engine's own job system and previously crashed it - // shortly after the movie finished. + switch (work) { case VulkanOffscreenGuestDraw offscreenDraw: @@ -14711,6 +14722,8 @@ internal static unsafe class VulkanVideoPresenter $"[LOADER][ERROR] Vulkan VideoOut translated draw setup failed: {exception.Message}"); return; } + + FlushBatchedGuestCommands(); } uint imageIndex; @@ -15322,6 +15335,11 @@ internal static unsafe class VulkanVideoPresenter RecordGuestDepthForSampling(depth, shaderStage); } + if (!texture.IsStorage && texture.GuestImage is { } sampledGuestImage) + { + RecordGuestImageForSampling(sampledGuestImage, shaderStage); + } + if (!texture.NeedsUpload) { continue; @@ -15529,6 +15547,76 @@ internal static unsafe class VulkanVideoPresenter depth.Layout = ImageLayout.ShaderReadOnlyOptimal; } + + private void RecordGuestImageForSampling( + GuestImageResource guestImage, + PipelineStageFlags shaderStage) + { + if (guestImage.Initialized || guestImage.InitialUploadPending) + { + return; + } + + var range = ColorSubresourceRange(0, Math.Max(guestImage.MipLevels, 1)); + var toTransfer = new ImageMemoryBarrier + { + SType = StructureType.ImageMemoryBarrier, + DstAccessMask = AccessFlags.TransferWriteBit, + OldLayout = ImageLayout.Undefined, + NewLayout = ImageLayout.TransferDstOptimal, + SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, + DstQueueFamilyIndex = Vk.QueueFamilyIgnored, + Image = guestImage.Image, + SubresourceRange = range, + }; + _vk.CmdPipelineBarrier( + _commandBuffer, + PipelineStageFlags.TopOfPipeBit, + PipelineStageFlags.TransferBit, + 0, + 0, + null, + 0, + null, + 1, + &toTransfer); + + var clearValue = new ClearColorValue(0f, 0f, 0f, 0f); + _vk.CmdClearColorImage( + _commandBuffer, + guestImage.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 = guestImage.Image, + SubresourceRange = range, + }; + _vk.CmdPipelineBarrier( + _commandBuffer, + PipelineStageFlags.TransferBit, + shaderStage, + 0, + 0, + null, + 0, + null, + 1, + &toShaderRead); + + guestImage.Initialized = true; + } + private void RecordStandaloneGuestDepthClear(GuestDepthResource depth) { var depthRange = new ImageSubresourceRange(