Merge remote-tracking branch 'origin/master' into claude/issue-40

# Conflicts:
#	implementations/Crafter.Graphics-Window.cpp
#	interfaces/Crafter.Graphics-Window.cppm
This commit is contained in:
catbot 2026-06-16 15:41:59 +00:00
commit 2d6052ec67
3 changed files with 195 additions and 126 deletions

View file

@ -164,7 +164,14 @@ export namespace Crafter {
std::chrono::nanoseconds vblank;
std::chrono::nanoseconds totalFrame;
std::chrono::time_point<std::chrono::high_resolution_clock> frameEnd;
std::vector<std::chrono::nanoseconds> frameTimes;
// Fixed-size ring buffer of the most recent frame times. LogTiming does
// order-independent sum/avg/min/max, so head position is irrelevant to
// the reported stats; this avoids the per-frame memmove a vector::erase
// at the front would incur once full.
static constexpr std::size_t frameTimeCapacity = 100;
std::array<std::chrono::nanoseconds, frameTimeCapacity> frameTimes{};
std::size_t frameTimesHead = 0;
std::size_t frameTimesCount = 0;
void LogTiming();
#endif
@ -246,6 +253,11 @@ export namespace Crafter {
VkSwapchainKHR swapChain = VK_NULL_HANDLE;
VkFormat colorFormat;
VkColorSpaceKHR colorSpace;
// Supported composite-alpha mode for vulkanSurface. A surface property
// that does not change across swapchain recreation, so it is selected
// once at construction rather than re-derived on every CreateSwapchain
// (i.e. on every resize configure).
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkImage images[numFrames];
VkImageViewCreateInfo imageViews[numFrames];
// Tracks whether each swapchain image has been rendered (and thus
@ -258,6 +270,20 @@ export namespace Crafter {
std::array<bool, numFrames> imageInitialised{};
std::thread thread;
VkCommandBuffer drawCmdBuffers[numFrames];
// Per-frame Vulkan info structs whose contents are almost entirely
// invariant. Initialised once in the constructor; Render() patches only
// the few fields that change each frame (the two barriers' image and
// the acquire barrier's oldLayout, and — for the multi-frame-in-flight
// sync below — submitInfo/presentInfo's wait/signal semaphores plus
// submitInfo's command buffer, which follow currentBuffer/frameCounter).
// presentInfo.pImageIndices and pSwapchains point at the
// currentBuffer/swapChain members, so they track value changes
// (including swapchain recreation) automatically.
VkSubmitInfo submitInfo;
VkCommandBufferBeginInfo cmdBufInfo;
VkImageMemoryBarrier acquireBarrier;
VkImageMemoryBarrier presentBarrier;
VkPresentInfoKHR presentInfo;
// ── Multi-frame-in-flight synchronisation (issue #40) ──────────────
// The renderer keeps up to numFrames frames in flight, so a single
@ -301,6 +327,18 @@ export namespace Crafter {
VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
std::vector<RenderPass*> passes;
DescriptorHeapVulkan* descriptorHeap = nullptr;
// Cached per-frame heap-bind structs. The heap address/size per slot
// are stable once a heap is assigned; only currentBuffer varies frame
// to frame, so the structs (and the reservedRangeOffset arithmetic)
// are computed once per heap rather than every frame in Render().
// Keyed on the heap pointer: whenever descriptorHeap differs from
// cachedDescriptorHeap the cache is rebuilt. This invalidates on any
// heap (re)assignment — heaps never resize today, so a stable pointer
// means stable ranges. Not tied to onResize (the heap is independent
// of swapchain size).
VkBindHeapInfoEXT resourceHeapInfos[numFrames];
VkBindHeapInfoEXT samplerHeapInfos[numFrames];
DescriptorHeapVulkan* cachedDescriptorHeap = nullptr;
std::optional<std::array<float, 4>> clearColor;
#else
// DOM mode: the page IS the window. WebGPU device and canvas are