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

# Conflicts:
#	interfaces/Crafter.Graphics-UI.cppm
#	project.cpp
This commit is contained in:
catbot 2026-06-16 17:09:06 +00:00
commit e8f1c7cff9
15 changed files with 732 additions and 39 deletions

View file

@ -219,7 +219,15 @@ void UIRenderer::DispatchFused(GraphicsCommandBuffer cmd,
pc.outImage = outImageSlot_;
pc.fontTexture = fontAtlasImageSlot_;
pc.fontSampler = fontAtlasSamplerSlot_;
pc.flags = 0;
// Per-category clip-active bits, reusing the same surface-coverage test the
// standard path uses (ClipFlags). A category whose clip already covers the
// surface clears its bit so the kernel skips that category's per-pixel clip
// compares (the common full-surface case) — pixel-identical either way.
auto clipBit = [&](const FusedBatch& b, std::uint32_t bit) -> std::uint32_t {
return (ClipFlags(b.clipRectPx, window_->width, window_->height, 0) & kUIFlagClip) ? bit : 0u;
};
pc.flags = clipBit(quads, 0x1u) | clipBit(circles, 0x2u)
| clipBit(images, 0x4u) | clipBit(text, 0x8u);
pc.surfaceWidth = window_->width;
pc.surfaceHeight = window_->height;
pc.frameIdx = window_->currentBuffer;
@ -239,15 +247,41 @@ void UIRenderer::Dispatch(GraphicsCommandBuffer cmd, const GraphicsComputeShader
const void* push, std::uint32_t pushBytes,
std::uint32_t gx, std::uint32_t gy, std::uint32_t gz) {
if (!firstDispatchThisFrame_) {
VkMemoryBarrier mb {
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
// Every UI pass read-modify-writes the same swapchain storage image
// (later passes overdraw earlier ones), so each dispatch must observe
// the previous dispatch's writes. The only resource the hazard touches
// is that one image, so scope the dependency to its subresource with a
// VkImageMemoryBarrier rather than a queue-wide VkMemoryBarrier: same
// correctness, but only this image's shader caches are flushed —
// unrelated buffers/images are no longer invalidated. The image stays
// in VK_IMAGE_LAYOUT_GENERAL (bound as a storage image), so this is a
// pure memory dependency, no layout transition.
//
// The execution dependency is still COMPUTE->COMPUTE over the whole
// queue, so the passes do NOT overlap — this is a narrower cache flush
// only. Eliminating the barriers entirely requires fusing the passes
// into one dispatch (tracked in #47); do not attempt that here.
VkImageMemoryBarrier ib {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = window_->images[window_->currentBuffer],
.subresourceRange = {
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT,
0, 1, &mb, 0, nullptr, 0, nullptr);
0, 0, nullptr, 0, nullptr, 1, &ib);
}
firstDispatchThisFrame_ = false;
shader.Dispatch(cmd, push, pushBytes, gx, gy, gz);