Fix shadow flashing: per-frame shadow depth images and framebuffers

Single shadow depth image shared across MAX_FRAMES=2 in-flight GPU frames
caused a race: frame N's main pass reads shadow map while frame N+1's
shadow pass clears and writes it, producing visible flashing standing
still and while moving.

Fix: give each in-flight frame its own VkImage, VmaAllocation, VkImageView,
and VkFramebuffer for the shadow depth attachment. renderShadowPass() now
indexes all shadow resources by getCurrentFrame(), and layout transitions
track per-frame state in shadowDepthLayout_[frame]. Cleanup loops over
MAX_FRAMES=2. Descriptor sets already written per-frame; updated shadow
image view binding to use the matching per-frame view.
This commit is contained in:
Kelsi 2026-03-09 22:14:32 -07:00
parent d5de031c23
commit edd7e5e591
2 changed files with 53 additions and 39 deletions

View file

@ -241,13 +241,17 @@ private:
std::unique_ptr<game::ZoneManager> zoneManager;
// Shadow mapping (Vulkan)
static constexpr uint32_t SHADOW_MAP_SIZE = 4096;
VkImage shadowDepthImage = VK_NULL_HANDLE;
VmaAllocation shadowDepthAlloc = VK_NULL_HANDLE;
VkImageView shadowDepthView = VK_NULL_HANDLE;
// Per-frame shadow resources: each in-flight frame has its own depth image and
// framebuffer so that frame N's shadow read and frame N+1's shadow write don't
// race on the same image across concurrent GPU submissions.
// Array size must match MAX_FRAMES (= 2, defined in the private section below).
VkImage shadowDepthImage[2] = {};
VmaAllocation shadowDepthAlloc[2] = {};
VkImageView shadowDepthView[2] = {};
VkSampler shadowSampler = VK_NULL_HANDLE;
VkRenderPass shadowRenderPass = VK_NULL_HANDLE;
VkFramebuffer shadowFramebuffer = VK_NULL_HANDLE;
VkImageLayout shadowDepthLayout_ = VK_IMAGE_LAYOUT_UNDEFINED;
VkFramebuffer shadowFramebuffer[2] = {};
VkImageLayout shadowDepthLayout_[2] = {};
glm::mat4 lightSpaceMatrix = glm::mat4(1.0f);
glm::vec3 shadowCenter = glm::vec3(0.0f);
bool shadowCenterInitialized = false;