mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 01:23:51 +00:00
fix(rendering): wait all frame fences before freeing shared descriptor sets
deferAfterFrameFence only waits for one frame slot's fence, but shared resources (material descriptor sets, vertex/index buffers) are bound by both in-flight frames' command buffers. On AMD RADV this caused vkFreeDescriptorSets errors and eventual SIGSEGV. Add deferAfterAllFrameFences: queues to every frame slot with a shared counter so cleanup runs exactly once, after the last slot is fenced. Use it for WMO, terrain, water, and character model shared resources. Per-frame bone sets keep using deferAfterFrameFence (already correct). Also fix character renderer vertex format: R8G8B8A8_UINT -> _SINT to match shader's ivec4 input (RADV validation rejects the mismatch).
This commit is contained in:
parent
def821055b
commit
3ac8c4d95f
6 changed files with 31 additions and 10 deletions
|
|
@ -246,7 +246,7 @@ bool CharacterRenderer::initialize(VkContext* ctx, VkDescriptorSetLayout perFram
|
|||
std::vector<VkVertexInputAttributeDescription> charAttrs = {
|
||||
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, position))},
|
||||
{1, 0, VK_FORMAT_R8G8B8A8_UNORM, static_cast<uint32_t>(offsetof(CharVertexGPU, boneWeights))},
|
||||
{2, 0, VK_FORMAT_R8G8B8A8_UINT, static_cast<uint32_t>(offsetof(CharVertexGPU, boneIndices))},
|
||||
{2, 0, VK_FORMAT_R8G8B8A8_SINT, static_cast<uint32_t>(offsetof(CharVertexGPU, boneIndices))},
|
||||
{3, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, normal))},
|
||||
{4, 0, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, texCoords))},
|
||||
{5, 0, VK_FORMAT_R32G32B32A32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, tangent))},
|
||||
|
|
@ -492,7 +492,7 @@ void CharacterRenderer::destroyModelGPU(M2ModelGPU& gpuModel, bool defer) {
|
|||
if (ib) vmaDestroyBuffer(alloc, ib, ibAlloc);
|
||||
} else if (vb || ib) {
|
||||
// Streaming path: in-flight command buffers may still reference these
|
||||
vkCtx_->deferAfterFrameFence([alloc, vb, vbAlloc, ib, ibAlloc]() {
|
||||
vkCtx_->deferAfterAllFrameFences([alloc, vb, vbAlloc, ib, ibAlloc]() {
|
||||
if (vb) vmaDestroyBuffer(alloc, vb, vbAlloc);
|
||||
if (ib) vmaDestroyBuffer(alloc, ib, ibAlloc);
|
||||
});
|
||||
|
|
@ -2663,7 +2663,7 @@ bool CharacterRenderer::initializeShadow(VkRenderPass shadowRenderPass) {
|
|||
// Character vertex format (CharVertexGPU): stride = 56 bytes
|
||||
// loc 0: vec3 aPos (R32G32B32_SFLOAT, offset 0)
|
||||
// loc 1: vec4 aBoneWeights (R8G8B8A8_UNORM, offset 12)
|
||||
// loc 2: ivec4 aBoneIndices (R8G8B8A8_UINT, offset 16)
|
||||
// loc 2: ivec4 aBoneIndices (R8G8B8A8_SINT, offset 16)
|
||||
// loc 3: vec2 aTexCoord (R32G32_SFLOAT, offset 32)
|
||||
VkVertexInputBindingDescription vertBind{};
|
||||
vertBind.binding = 0;
|
||||
|
|
@ -2672,7 +2672,7 @@ bool CharacterRenderer::initializeShadow(VkRenderPass shadowRenderPass) {
|
|||
std::vector<VkVertexInputAttributeDescription> vertAttrs = {
|
||||
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, position))},
|
||||
{1, 0, VK_FORMAT_R8G8B8A8_UNORM, static_cast<uint32_t>(offsetof(CharVertexGPU, boneWeights))},
|
||||
{2, 0, VK_FORMAT_R8G8B8A8_UINT, static_cast<uint32_t>(offsetof(CharVertexGPU, boneIndices))},
|
||||
{2, 0, VK_FORMAT_R8G8B8A8_SINT, static_cast<uint32_t>(offsetof(CharVertexGPU, boneIndices))},
|
||||
{3, 0, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, texCoords))},
|
||||
};
|
||||
|
||||
|
|
@ -3336,7 +3336,7 @@ void CharacterRenderer::recreatePipelines() {
|
|||
std::vector<VkVertexInputAttributeDescription> charAttrs = {
|
||||
{0, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, position))},
|
||||
{1, 0, VK_FORMAT_R8G8B8A8_UNORM, static_cast<uint32_t>(offsetof(CharVertexGPU, boneWeights))},
|
||||
{2, 0, VK_FORMAT_R8G8B8A8_UINT, static_cast<uint32_t>(offsetof(CharVertexGPU, boneIndices))},
|
||||
{2, 0, VK_FORMAT_R8G8B8A8_SINT, static_cast<uint32_t>(offsetof(CharVertexGPU, boneIndices))},
|
||||
{3, 0, VK_FORMAT_R32G32B32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, normal))},
|
||||
{4, 0, VK_FORMAT_R32G32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, texCoords))},
|
||||
{5, 0, VK_FORMAT_R32G32B32A32_SFLOAT, static_cast<uint32_t>(offsetof(CharVertexGPU, tangent))},
|
||||
|
|
|
|||
|
|
@ -1061,8 +1061,8 @@ void TerrainRenderer::destroyChunkGPU(TerrainChunkGPU& chunk) {
|
|||
chunk.materialSet = VK_NULL_HANDLE;
|
||||
chunk.ownedAlphaTextures.clear();
|
||||
|
||||
vkCtx->deferAfterFrameFence([device, allocator, vertexBuffer, vertexAlloc, indexBuffer, indexAlloc,
|
||||
paramsUBO, paramsAlloc, pool, materialSet, alphaTextures]() {
|
||||
vkCtx->deferAfterAllFrameFences([device, allocator, vertexBuffer, vertexAlloc, indexBuffer, indexAlloc,
|
||||
paramsUBO, paramsAlloc, pool, materialSet, alphaTextures]() {
|
||||
if (vertexBuffer) {
|
||||
AllocatedBuffer ab{}; ab.buffer = vertexBuffer; ab.allocation = vertexAlloc;
|
||||
destroyBuffer(allocator, ab);
|
||||
|
|
|
|||
|
|
@ -194,6 +194,23 @@ void VkContext::deferAfterFrameFence(std::function<void()>&& fn) {
|
|||
deferredCleanup_[currentFrame].push_back(std::move(fn));
|
||||
}
|
||||
|
||||
void VkContext::deferAfterAllFrameFences(std::function<void()>&& fn) {
|
||||
// Shared resources (material descriptor sets, vertex/index buffers) are
|
||||
// bound by every in-flight frame's command buffer. deferAfterFrameFence
|
||||
// only waits for ONE slot's fence — the other slot may still be executing.
|
||||
// Add to every slot; a shared counter ensures the lambda runs exactly once,
|
||||
// after the LAST slot has been fenced.
|
||||
auto counter = std::make_shared<uint32_t>(MAX_FRAMES_IN_FLIGHT);
|
||||
auto sharedFn = std::make_shared<std::function<void()>>(std::move(fn));
|
||||
for (uint32_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||||
deferredCleanup_[i].push_back([counter, sharedFn]() {
|
||||
if (--(*counter) == 0) {
|
||||
(*sharedFn)();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void VkContext::runDeferredCleanup(uint32_t frameIndex) {
|
||||
auto& q = deferredCleanup_[frameIndex];
|
||||
if (q.empty()) return;
|
||||
|
|
|
|||
|
|
@ -1405,8 +1405,8 @@ void WaterRenderer::destroyWaterMesh(WaterSurface& surface) {
|
|||
surface.materialAlloc = VK_NULL_HANDLE;
|
||||
surface.materialSet = VK_NULL_HANDLE;
|
||||
|
||||
vkCtx->deferAfterFrameFence([device, allocator, vertexBuffer, vertexAlloc, indexBuffer, indexAlloc,
|
||||
materialUBO, materialAlloc, pool, materialSet]() {
|
||||
vkCtx->deferAfterAllFrameFences([device, allocator, vertexBuffer, vertexAlloc, indexBuffer, indexAlloc,
|
||||
materialUBO, materialAlloc, pool, materialSet]() {
|
||||
if (vertexBuffer) {
|
||||
AllocatedBuffer ab{}; ab.buffer = vertexBuffer; ab.allocation = vertexAlloc;
|
||||
destroyBuffer(allocator, ab);
|
||||
|
|
|
|||
|
|
@ -1972,7 +1972,7 @@ void WMORenderer::destroyGroupGPU(GroupResources& group, bool defer) {
|
|||
}
|
||||
|
||||
VkDescriptorPool pool = materialDescPool_;
|
||||
vkCtx_->deferAfterFrameFence([device, allocator, pool, vb, vbAlloc, ib, ibAlloc,
|
||||
vkCtx_->deferAfterAllFrameFences([device, allocator, pool, vb, vbAlloc, ib, ibAlloc,
|
||||
mats = std::move(mats)]() {
|
||||
if (vb) vmaDestroyBuffer(allocator, vb, vbAlloc);
|
||||
if (ib) vmaDestroyBuffer(allocator, ib, ibAlloc);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue