With MSAA, a framebuffer textured as CLUT indices was read resolved, so the
indices along edges were averages of the samples and looked up wrong colors.
Dragon Ball Z: Tenkaichi Tag Team draws its outlines from a CLUT32 lookup of
its framebuffer, and they came out noisy and miscolored. Copy each pixel's
first sample with texelFetch into a temporary buffer and look up that.
This needs the multisampled color images to be sampled, and transitioned
back to attachment layout before rendering to them again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The GE's blend terms are floor((2c+1)(2f+1)/1024), which truncates, while the
hardware multiplies by f/255 and rounds, so every pass of a fixed color blend
chain came out up to a level brighter. God Eater Burst's bloom is many such
passes and ended up far too bright.
For additive FIXA/FIXB blends that aren't just 0 and 0xFFFFFF, the shader
now computes the src term exactly and lowers it by 0.5 - (2B+1)/1024, and the
blend constant is the GE's dst factor (2B+1)/512, so the hardware's rounding
of the sum lands on the GE's floor (except where the src term would go below
zero). Blend constants are passed as floats to make that possible.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pipelines still in the compile queue weren't in flight yet, so the
shader cache load could stop waiting (and the spinner) early.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
These own GPU objects, memory or refcounts in their destructors (or assert
there that they were torn down), so a copy would double-free. Nothing copies
them today; this keeps it that way. The manager base classes cover every
backend's subclass.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
pipelineLayouts_ was mutated from the main thread (CreatePipelineLayout, and the
deferred callback queued by DestroyPipelineLayout) while the render thread walked
it every frame in FlushDescriptors. Exiting a game in Vulkan mode hits this
reliably: ~GPU_Vulkan stops the render thread and destroys the draw engine's
layout, but the destruction is deferred onto the delete list and doesn't actually
run until a BeginFrame two frames later, with the render thread running again.
Guard the list, and the lifetime of the layouts in it, with a mutex.
The global delete list had the same problem - VulkanDescSetPool::Recreate queues
the old pool from FlushDescSets on the render thread, which happens for real once
a game goes past the initial 1024 descriptors, while the main thread moves the
list into the current frame's list in EndFrame(). Lock the queueing functions and
Take's source list.
While in there:
* Take() didn't move queryPools_, so query pools queued for deletion sat on the
global list until device teardown instead of being deleted a few frames later.
* PerformDeletes now drains into a local list before destroying anything. A
callback is allowed to queue further deletes (~VKFramebuffer's does, via
~VKRFramebuffer), which used to append to the very vector being iterated.
They now get the normal deferral instead of running in the same pass.
* Missing semicolon in BeginFrame that only compiles because VLOG is empty.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vd8ntC2brCUtCrDJMqLbs8
We can't create a swapchain in this case but we still want emulation to
keep running. So we ditch the last renderpass in VulkanQueueRunner but
run all the rest (if PauseWhenMinimized isn't set).
Now works without any validation errors or hangs.