Commit graph Crafter.Graphics/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm
Author SHA1 Message Date
079b6b0e1f
feat: windows on their own clock, deferred deletion for every handle owner, staged re-uploads, and RTPass push data
Window
- updateInterval runs Update on the window's own clock while the
  compositor sends no frame callbacks (hidden, minimized): the Wayland loop
  is a poll() loop, and the Win32 loop sleeps until the next update. Both
  read the interval each pass.
- SetPresentMode (FIFO, Mailbox, Immediate). A mode the surface lacks, or
  whose swapchain has other than numFrames images (Mesa's Mailbox has four),
  falls back to FIFO until the mode is set again.
- AddPollFd / RemovePollFd wake the Wayland loop on the app's descriptors.
- The swapchain has TRANSFER_DST where the surface allows it.
- CaptureNextFrame replaces SaveFrame: it records the copy in the frame,
  before the present, where SaveFrame read an image already presented. A
  frame unwound past before the write still frees the readback buffer.
- Inter-pass barriers on the swapchain image carry the access masks of the
  stages the passes use.

Deferred deletion (issue #101)
- VulkanBuffer, Mesh, VulkanShader, ComputeShader and PipelineRTVulkan hand
  their handles to the deletion queue from their destructors and moves, as
  a frame in flight may still use them; a static owner goes after the
  driver has torn down. VulkanShader and PipelineRTVulkan move and can't be
  copied; re-Load and re-Init queue the old pipeline.
- The queue and its mutex are never destroyed, and the queue takes handles
  from any thread; Device::frameCounter is atomic. Both the queue and the
  staging rings count frames as one window renders them (documented).
- DrainDeletions also lets every buffer's staging ring start over: without
  a Window nothing else reclaims either, so an app without one calls it
  after waiting for its work.

Meshes and buffers
- A Mesh rebuild queues the old acceleration structure and builds into the
  same storage after a barrier, so the BLAS address stays put when it fits.
  The refit and rebuild barriers cover compute ray queries too.
- RTBuildOptions::noDuplicateAnyHit. Mesh::geometryFlags replaces opaque,
  and the refit and rebuild fallbacks keep the flags.
- A re-upload into a buffer a frame in flight may read (UploadDeviceLocal
  of the same size, UploadDeviceLocalRange) copies on the GPU, after those
  reads, through a per-frame staging ring; on ReBAR it used to write the
  buffer in place. Empty uploads record nothing.
- A compressed Mesh::Build that keeps its buffers decompresses into them
  after the reads of frames in flight, and the hit shaders and ray queries
  wait for the decompress as well as the BLAS build. Its vertex buffer
  takes a ranged refit's copies.
- Mesh move-assigns (queueing the BLAS it replaces), so it can be replaced
  in place or erased from a vector.
- VulkanBuffer::Create asks for a device address only for buffers with
  SHADER_DEVICE_ADDRESS usage, as vkGetBufferDeviceAddress requires; the
  others have address 0. UIRenderer::RegisterBuffer refuses those.
- Build inputs are synchronized as shader reads.

Other
- RTPass pushes the app's own push data and traces at an explicit size.
- DescriptorHeapVulkan hands freed slot ranges out again (they leaked), and
  keeps its free lists sorted.
- ImageVulkan's mip chain clamps each level to 1 texel: a non-square
  image's shorter side went to 0.
- VulkanShader throws on a missing file instead of failing in bad_alloc.
- CRAFTER_GRAPHICS_DEVICE picks the GPU by its number among the distinct
  GPUs Vulkan lists; anything but such a number is an error.
- The debug messenger prints info messages only with
  CRAFTER_GRAPHICS_VALIDATION set.
- Windows: the WinRT gamepad code moved to a C file (mingw-w64's WinRT
  headers don't compile as C++), -lcombase dropped, a reference leak fixed.
- project.cpp honours --local for Crafter.Event, Math and Asset, as Asset
  does for Math, and builds every test through one helper, projectArgs
  included.
- Tests: DebugMessengerSeverity, DeletionQueueThreads, DeviceSelection,
  HeapSlotReuse, MeshLifetime, MipChainNonSquare, PipelineLifetime,
  RTPassPushData, SwapchainTransferDst, UploadInFlight, VulkanBufferAddress,
  VulkanShaderLifetime, WindowUpdateClock; others extended.

Breaking: SaveFrame is CaptureNextFrame; Mesh::opaque is geometryFlags; a
buffer without SHADER_DEVICE_ADDRESS usage has no address; frameCounter is
std::atomic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 20:20:29 +02:00
48aa9be5ce feat(vulkan): run the compute/UI path without VK_EXT_descriptor_heap
Device::Initialize required twelve device extensions with no capability
check. Mesa Turnip (Adreno) lacks six of them: VK_EXT_descriptor_heap,
VK_KHR_shader_untyped_pointers, VK_EXT_device_fault and the three ray
tracing extensions. vkCreateDevice failed there, so not even a UI-only
program could start, although the UI path is compute-only and needs
nothing else that Turnip is missing.

The descriptor heap, ray tracing and device fault are now optional
groups, detected at start-up from the extension list and the feature
bits (Device::descriptorHeapSupported, rayTracingSupported,
deviceFaultSupported). Ray tracing needs the heap; on a device without
it the ray tracing entry points throw a message naming the cause.

Without the heap the same bindless slots live in one update-after-bind
descriptor set per frame, owned by DescriptorHeapVulkan: binding 0
storage images, 1 sampled images, 2 samplers, 3 storage buffers. Slot
allocation, the slot handles, UIDispatchHeader and the UIRenderer API
are unchanged; buffer slots simply stop being offset. Window::Render
binds that frame's set where it binds the heap, every compute pipeline
shares the heap object's pipeline layout, and push data travels as push
constants.

Each UI kernel is built twice from one source. ui-shared.glsl declares
its resources through UI_HEAP_* macros that expand to descriptor_heap
or, with CRAFTER_GRAPHICS_DESCRIPTOR_SETS defined, to set/binding.
<name>.comp.spv and <name>.comp.sets.spv ship side by side and
ComputeShader::Load picks one. The heap build of every shader is
byte-identical to before.

Breaking:
- ComputeShader::Load takes the heap as a second argument.
- DescriptorHeapVulkan is no longer copyable or movable.
- Custom UI shaders declare heap resources with the UI_HEAP_* macros
  and are registered twice in project.cpp (see the README).
- project.cpp uses Shader::defines and Shader::variant, so it needs a
  crafter-build that has them (Crafter.Build master, fe38cc1 or later).

Also:
- maintenance5 is enabled as a feature, not only as an extension:
  every VulkanBuffer already relied on VkBufferUsageFlags2CreateInfo.
- Barrier stage masks drop RAY_TRACING_SHADER on devices without ray
  tracing, and ImageVulkan::Create resolves its default consumer stage
  to COMPUTE_SHADER there.
- Window refuses a swapchain whose image count is not numFrames
  instead of writing past images[]. The limitation itself is #160.
- CRAFTER_GRAPHICS_NO_DESCRIPTOR_HEAP forces the fallback on a driver
  that has the heap, so it can be exercised on a desktop GPU.

Verified on an RTX 4090 in both modes under the validation layer, and
on a Fairphone 6 (Turnip, Adreno 810, postmarketOS), where the fallback
is the only path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 17:28:46 +02:00
42a479572d SPDX license update 2026-07-22 18:09:06 +02:00
catbot
b410f2d5aa perf(rt): place SBT in device-local memory via upload-strategy helper (#72)
The shader binding table is written once at pipeline creation and read by
the GPU on every vkCmdTraceRaysKHR thereafter, yet it was allocated plain
HOST_VISIBLE (system RAM) — so every trace dispatch read raygen/miss/hit
records over PCIe.

Route the SBT allocation through Device::PreferDirectDeviceWrite (#89):
when a DEVICE_LOCAL|HOST_VISIBLE type exists, prefer DEVICE_LOCAL on top
of the required HOST_VISIBLE so the buffer lands in mappable device
memory (GPU reads hit local VRAM; the one-time memcpy goes write-combined
over PCIe). When no combined type exists, the preferred hint is dropped
and GetMemoryType (#59) falls back to plain HOST_VISIBLE — current
behaviour. The SBT is tiny so a small BAR window's budget is never the
constraint.

The buffer stays mapped and the existing FlushDevice gates on the chosen
memory type's coherency flags (#60), so a direct-write type lacking
HOST_COHERENT is still flushed correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 17:41:34 +00:00
catbot
a3a6dd2006 perf(pipeline): shared, disk-persisted VkPipelineCache (#69)
vkCreateComputePipelines and vkCreateRayTracingPipelinesKHR were passed
VK_NULL_HANDLE, so the driver couldn't reuse compiled shader binaries
across the pipelines built at startup (4 UI shaders + user/RT pipelines)
or across runs.

Add one process-wide Device::pipelineCache. LoadPipelineCache (called
from Initialize) seeds it from pipeline_cache.bin when the file's
VkPipelineCacheHeaderVersionOne header matches this device's
vendorID / deviceID / pipelineCacheUUID; a stale or foreign blob is
discarded so the driver never rejects it. SavePipelineCache is registered
with std::atexit, serialising the cache at process shutdown without any
explicit teardown hook. Both create sites now pass the cache; a null
handle stays valid, so no create-site null check is needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:23:25 +00:00
catbot
1c310762a7 fix(vulkan-rt): configurable recursion depth + per-shader TLAS push for compute (#21)
Two gaps in the Vulkan RT path that fault the device on the NVIDIA
proprietary driver with a non-trivial pipeline (simple VulkanTriangle
never hit them):

1. maxPipelineRayRecursionDepth was hardcoded to 1, so any closest-hit
   shader that traces a secondary ray (shadow ray — a very common
   pattern) recursed past the pipeline limit (UB → device fault).
   PipelineRTVulkan::Init now takes a maxRecursionDepth parameter
   (default 1, clamped to the device's maxRayRecursionDepth).

2. The NVIDIA descriptor-heap AS-read workaround rewrites every shader
   that reads an accelerationStructureEXT from the heap — including
   compute shaders — to read the TLAS device address from a push
   constant, but only RTPass pushed that address. A compute shader that
   ray-queries the TLAS (rayQueryEXT) therefore ran against an unwritten
   push slot → garbage AS handle → VK_ERROR_DEVICE_LOST.

   WorkaroundNvidiaAS::Patch now returns a per-shader PatchResult
   {patched, tlasPushOffset} instead of writing the clobber-prone global
   Device::workaroundTlasPushOffset (removed). VulkanShader stores it;
   ShaderBindingTableVulkan/PipelineRTVulkan carry it for RTPass, and
   ComputeShader tracks its own offset and pushes the caller-supplied
   TLAS address in Dispatch (new defaulted tlasAddress parameter),
   mirroring RTPass::Record.

The PushConstantRewrite regression test now asserts Patch's returned
patched/offset and adds two ray-querying compute-shader cases, proving
the rewrite is stage-agnostic and the per-shader offset is correct.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 18:35:39 +00:00
5352ef69a2 browser DOM support 2026-05-18 02:07:48 +02:00
216972e73a new UI system 2026-05-01 23:35:37 +02:00
3fcea6a3d7 writing ui descriptors 2026-04-10 22:26:15 +02:00
92dfe16dac pipeline destructor 2026-04-10 20:51:16 +02:00
f8e142fb06 descriptor heap rewrite 2026-04-05 22:53:59 +02:00
d661c88ee2 cleaned up renderer 2026-03-09 20:10:19 +01:00
72bf6c83e1 windows build fix 2026-03-02 23:53:13 +01:00
b5e69c90fa Win32 vulkan window 2026-02-24 02:32:37 +01:00
415e523a45 runtime pipeline 2026-02-22 00:46:38 +01:00
74832c6824 templated pipeline 2026-01-31 21:08:42 +01:00
8275e01b6c fixes 2026-01-30 07:19:59 +01:00
5e3a7738ed fixed descriptors 2026-01-29 23:31:56 +01:00
96b5d1a299 vulkan triangle 2026-01-29 19:18:47 +01:00
e4e7c66808 RT descriptors 2026-01-29 01:31:17 +01:00
7b24f52764 RT pipeline 2026-01-29 00:45:02 +01:00