new UI system

This commit is contained in:
Jorijn van der Graaf 2026-05-01 23:35:37 +02:00
commit 216972e73a
82 changed files with 4837 additions and 3243 deletions

View file

@ -0,0 +1,110 @@
/*
Crafter®.Graphics
Copyright (C) 2026 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#include "vulkan/vulkan.h"
export module Crafter.Graphics:UIRenderer;
import std;
import :Device;
import :Window;
import :RenderPass;
import :DescriptorHeapVulkan;
import :VulkanBuffer;
import :SamplerVulkan;
import :ShaderVulkan;
import :ImageVulkan;
import :UIDrawList;
import :UIAtlas;
export namespace Crafter::UI {
// The compute-pass-side renderer. Owns the compute pipeline, per-frame
// item buffers, the SDF glyph atlas, and the descriptor-heap slot
// allocations. Implements RenderPass so it plugs into Window::passes.
//
// Lifecycle:
// - Initialize(window, shaderPath) — once, after the window has a
// descriptor heap. Allocates slots, creates pipeline, atlas image.
// - SetItems(span<UIItem>) per frame, before Window::Render runs.
// - Record(...) — invoked by Window::Render's pass loop.
class UIRenderer : public RenderPass {
public:
// Defaulted bindless slot capacity — covers most game UIs without
// descriptor heap pressure. Override in Initialize.
static constexpr std::uint16_t kDefaultBindlessImageCount = 256;
FontAtlas atlas;
// Initialize. `initCmd` must be a command buffer in recording
// state — used to transition the atlas image. Window must already
// have a non-null descriptorHeap with enough free slots for
// (numFrames + 1 + bindlessImageCount) images, numFrames buffers,
// and 1 sampler.
void Initialize(Window& window,
VkCommandBuffer initCmd,
const std::filesystem::path& spvPath = "ui.comp.spv",
std::uint16_t bindlessImageCount = kDefaultBindlessImageCount);
// Stage `items` into the next-frame mapped buffer. Must be called
// BEFORE Window::Render so the buffer is flushed before the
// dispatch reads it.
void SetItems(std::span<const UIItem> items);
// RenderPass impl — invoked from Window::Render's pass loop.
void Record(VkCommandBuffer cmd, std::uint32_t frameIdx, Window& window) override;
// Heap slot accessors — UIScene reads these to populate DrawList.
std::uint32_t BindlessBaseHeapIdx() const { return bindlessBase_; }
FontAtlas& Atlas() { return atlas; }
// The frame currently being staged. Window::Render advances
// `currentBuffer` before passes record; SetItems writes to
// (currentBuffer + 1) so the previous frame's buffer is still in
// flight on the GPU. For V1 we ride on Window's currentBuffer
// directly since vkQueueWaitIdle gates each frame.
std::uint32_t pendingItemCount = 0;
private:
Window* window_ = nullptr;
VkPipeline pipeline_ = VK_NULL_HANDLE;
VulkanBuffer<UIItem, true> itemBufs_[Window::numFrames];
std::uint16_t itemCapacity_ = 0;
// Heap slot allocations (resource heap unless noted).
std::uint16_t outImageBase_ = 0; // images[outImageBase_ + frame] = swapchain view
std::uint16_t atlasImageSlot_ = 0; // sampled atlas image slot
std::uint16_t bindlessBase_ = 0; // first user-image slot
std::uint16_t bindlessCount_ = 0; // user-image slot count
std::uint16_t itemBufBase_ = 0; // SSBO slot base; per-frame at base + i
std::uint16_t linearSamplerSlot_ = 0; // sampler heap
// Stable VkImageViewCreateInfo for the atlas — descriptor heap
// writes need a pointer to one, so we keep it on the renderer.
VkImageViewCreateInfo atlasViewCreateInfo_{};
// Helpers.
void GrowItemBuffersIfNeeded(std::uint32_t needed);
void WriteSwapchainDescriptors();
void WriteAtlasDescriptor();
void WriteSamplerDescriptors();
void WriteItemBufferDescriptors();
void CreatePipeline(const std::filesystem::path& spvPath);
void CreateLinearSampler();
};
}