descriptor heap leak fix

This commit is contained in:
Jorijn van der Graaf 2026-05-05 00:02:04 +02:00
commit 825da78f7f
3 changed files with 232 additions and 29 deletions

View file

@ -208,24 +208,27 @@ export namespace Crafter {
// Allocates a heap slot for the buffer and writes its descriptor into
// every per-frame heap. The user's mapped buffer is shared across
// frames — fine because Window::Render currently waits idle before
// submitting the next frame. Returns the slot index for use in headers.
// submitting the next frame. Returns a move-only BufferSlot handle
// whose destructor returns the slot to the heap. Implicitly converts
// to the absolute heap index when passed to FillHeader / Dispatch*.
template<typename T, bool Mapped>
std::uint16_t RegisterBuffer(VulkanBuffer<T, Mapped>& buffer);
BufferSlot RegisterBuffer(VulkanBuffer<T, Mapped>& buffer);
// Same for an ImageVulkan-managed sampled image (e.g. a user texture).
// Caller specifies the layout the image will be sampled in (typically
// VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL).
template<typename Pixel>
std::uint16_t RegisterImage(ImageVulkan<Pixel>& image, VkFormat format,
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
ImageSlot RegisterImage(ImageVulkan<Pixel>& image, VkFormat format,
VkImageLayout layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
// Allocates a sampler slot and writes a VkSamplerCreateInfo into
// every per-frame sampler heap. v1 takes the create-info inline.
std::uint16_t RegisterSampler(const VkSamplerCreateInfo& info);
SamplerSlot RegisterSampler(const VkSamplerCreateInfo& info);
// Convenience: a linear-filter, clamp-to-edge sampler. Returns the
// slot. Useful for the FontAtlas and most plain image sampling.
std::uint16_t RegisterLinearClampSampler();
// Convenience: a linear-filter, clamp-to-edge sampler. Returns a
// SamplerSlot handle. Useful for the FontAtlas and most plain image
// sampling.
SamplerSlot RegisterLinearClampSampler();
// Shapes a UTF-8 string into glyph quads at (x, y) baseline. Calls
// FontAtlas::Ensure for each codepoint (rasterising on first use),
@ -252,14 +255,14 @@ export namespace Crafter {
// heap, that slot points at THAT frame's swapchain image. So the
// shader's `uiImages[hdr.outImage]` is always the current frame's
// swapchain image regardless of which heap is bound.
std::uint16_t outImageSlot_ = 0;
ImageSlot outImageSlot_;
// Stable VkImageViewCreateInfos for the descriptor heap to ingest.
// These must outlive the write call.
VkImageViewCreateInfo atlasViewCreateInfo_{};
std::uint16_t fontAtlasImageSlot_ = 0xFFFF;
std::uint16_t fontAtlasSamplerSlot_ = 0xFFFF;
ImageSlot fontAtlasImageSlot_;
SamplerSlot fontAtlasSamplerSlot_;
bool firstDispatchThisFrame_ = true;
@ -279,19 +282,17 @@ export namespace Crafter {
// ─── template-method implementations ────────────────────────────────
template<typename T, bool Mapped>
std::uint16_t UIRenderer::RegisterBuffer(VulkanBuffer<T, Mapped>& buffer) {
BufferSlot UIRenderer::RegisterBuffer(VulkanBuffer<T, Mapped>& buffer) {
auto range = heap_->AllocateBufferSlots(1);
WriteBufferDescriptor(range.firstElement, buffer.address, buffer.size);
// GLSL `descriptor_heap` indexes buffer-typed views in buffer-descriptor
// units from heap byte 0; the actual buffer region starts past the
// image region at `bufferStartElement`. Return the absolute index so
// the user just hands it to FillHeader without thinking about it.
return static_cast<std::uint16_t>(heap_->bufferStartElement + range.firstElement);
// BufferSlot's operator uint16_t() folds in heap_->bufferStartElement,
// so callers receive the absolute heap index when they convert.
return BufferSlot{heap_, range.firstElement};
}
template<typename Pixel>
std::uint16_t UIRenderer::RegisterImage(ImageVulkan<Pixel>& image, VkFormat format,
VkImageLayout layout) {
ImageSlot UIRenderer::RegisterImage(ImageVulkan<Pixel>& image, VkFormat format,
VkImageLayout layout) {
auto range = heap_->AllocateImageSlots(1);
// Build a stable view-create-info that lives as long as the heap reads
@ -318,6 +319,6 @@ export namespace Crafter {
},
};
WriteSampledImageDescriptor(range.firstElement, info, layout);
return range.firstElement;
return ImageSlot{heap_, range.firstElement};
}
}