agent files
This commit is contained in:
parent
a173cf18ff
commit
885f3ca492
41
AGENTS.md
Normal file
41
AGENTS.md
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
# Workspace guide
|
||||
|
||||
## Workspace
|
||||
- Rust workspace uses the nightly toolchain (`rust-toolchain`), edition 2024, resolver 2.
|
||||
- Members: `crates/renderer`, `crates/renderer/renderer_macros`, `crates/renderer_device`, `crates/renderer_resources`, `crates/window`, `crates/game`, `crates/text`, and `crates/bevy_vulkan_render`.
|
||||
- Dependency direction for Vulkan rendering: `bevy_vulkan_render` → `renderer` → `renderer_resources` → `renderer_device`. `renderer_macros` supplies `DeviceRef` derives to `renderer` and is a dev-dependency for `renderer_device` tests.
|
||||
- `renderer` re-exports device, instance, queue, and sync modules and many root device types. Prefer its paths for downstream rendering code; its resource-module aliases remain crate-private.
|
||||
- `crates/renderer/renderer_macros` is the proc-macro crate used by `renderer` for `DeviceRef` derives.
|
||||
- `crates/renderer_device` owns Vulkan instance/device setup, queues, sync primitives, and handle lifetimes.
|
||||
- `crates/renderer_resources` owns public buffer, image, view, descriptor, sampler, shader-module, and pipeline resource APIs on `renderer_device`.
|
||||
- `crates/renderer` owns rendering orchestration, commands, swapchains, and render graph APIs; resource module aliases are internal, not `renderer` public modules.
|
||||
- `crates/bevy_vulkan_render` integrates the renderer with Bevy. `crates/window`, `crates/game`, and `crates/text` are separate workspace crates.
|
||||
- The workspace root package is `vidya`; it also contains examples and depends on `bevy_vulkan_render`.
|
||||
- `renderer_resources` depends on `renderer_device`, not `renderer`; do not introduce a dependency cycle.
|
||||
- `renderer` uses `renderer_device` for device/instance ownership and accesses resource modules through crate-private aliases.
|
||||
- `renderer_macros` is nested under the renderer package directory but is a distinct workspace package.
|
||||
|
||||
## Layout
|
||||
- Workspace and shared dependency versions: root `Cargo.toml`.
|
||||
- Per-crate manifests live beside crate source under `crates/`.
|
||||
- Rendering shaders and assets are under `crates/renderer/shaders` and `assets/`.
|
||||
- Root-level demos and examples are under `examples/`; add shared dependencies under `[workspace.dependencies]` where appropriate.
|
||||
- The workspace uses Rust 2024 syntax; `renderer` and `renderer_device` use nightly features. `renderer_resources` has no crate-level nightly feature attributes.
|
||||
|
||||
## Conventions
|
||||
- Keep changes local to the owning crate; preserve the device/rendering layer boundary.
|
||||
- Follow existing Rust style and use the workspace dependency declarations in the root manifest when applicable.
|
||||
- Keep the device/resource/rendering dependency boundaries described above; use `renderer`'s re-exported API in Bevy integration where possible.
|
||||
- Do not commit changes; the user owns commits.
|
||||
- Read the crate-level instructions before changing rendering/device/resource boundaries:
|
||||
- [`crates/renderer_device/AGENTS.md`](crates/renderer_device/AGENTS.md)
|
||||
- [`crates/renderer_resources/AGENTS.md`](crates/renderer_resources/AGENTS.md)
|
||||
- [`crates/renderer/AGENTS.md`](crates/renderer/AGENTS.md)
|
||||
|
||||
## Verification
|
||||
- `cargo check --workspace` checks all workspace crates.
|
||||
- `cargo test -p renderer_device` runs the device-layer tests.
|
||||
- Rendering code uses nightly features; use the pinned workspace toolchain for all Cargo commands.
|
||||
- Run Cargo commands from the workspace root to use the pinned toolchain and shared workspace configuration.
|
||||
- Use crate-scoped `cargo check -p <crate>` or `cargo test -p <crate>` for focused checks, then workspace check for cross-crate API changes.
|
||||
- Review `git status` and `git diff` before reporting changes; preserve unrelated user modifications.
|
||||
130
crates/renderer/AGENTS.md
Normal file
130
crates/renderer/AGENTS.md
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
# renderer
|
||||
|
||||
## Purpose
|
||||
`renderer` builds rendering orchestration, command submission, swapchains, and render-graph APIs on `renderer_resources` and the lower-level `renderer_device` layer. It re-exports selected `renderer_device` APIs and uses crate-private aliases to the resource crate. Follow [`../renderer_device/AGENTS.md`](../renderer_device/AGENTS.md) for device ownership internals and [`../renderer_resources/AGENTS.md`](../renderer_resources/AGENTS.md) for resource construction and ownership.
|
||||
|
||||
## Module map
|
||||
- `lib.rs`: public root API, `Renderer2`, sampler cache, egui state, texture manager, and `renderer_device` re-exports.
|
||||
- `memory.rs` (not declared by `lib.rs`): empty; not part of the compiled crate module tree.
|
||||
- `renderer_resources::{buffers, images, pipeline}`: public resource-crate modules aliased through `pub(crate) use` in this crate root. These aliases preserve internal `crate::buffers::X`, `crate::images::X`, and `crate::pipeline::X` paths without exposing public renderer modules.
|
||||
- `commands.rs` (public): command pools/buffers, command recording helpers, queue submission, and command futures.
|
||||
- `swapchain.rs` (public): `Surface`, swapchain configuration, acquired images/frames, and presentation.
|
||||
- `render_graph/mod.rs` (public): re-exports the legacy graph API from `legacy.rs`; sibling `commands.rs`, `graph_builder.rs`, `recorder.rs`, and `resources.rs` are private modules.
|
||||
- `render_graph/legacy.rs`: public resource/pass descriptions, graph construction/resolution, resource barriers, and `RenderContext`.
|
||||
- `render_graph/commands.rs`: private typed render-graph commands and resource-access declarations; not wired into the legacy graph.
|
||||
- `render_graph/graph_builder.rs`: private newer graph-builder scaffolding and API example.
|
||||
- `render_graph/recorder.rs`: private typed command-list and side-effect tracking scaffolding.
|
||||
- `render_graph/resources.rs`: graph resource IDs, buffer/texture ranges and access scopes; private.
|
||||
- `rendering/mod.rs` (public): `Wireframe` helper and re-exports `egui_pass` / `egui_pre_pass`.
|
||||
- `egui.rs` is included as private `egui_pass`: egui texture upload/pre-pass and drawing integration.
|
||||
- `egui_pass` is a private module, but `rendering` publicly re-exports its `egui_pass` and `egui_pre_pass` functions.
|
||||
- `text.rs` (private): font storage, glyph rasterization, and CPU-side glyph-atlas data; largely internal/dead-code-allowed.
|
||||
- `util.rs` (public): renderer helpers and stable re-exports of `eq_f32`, `ord_f32`, `hash_f32`, and `F32` from `renderer_resources::util`; `weak_vec` moved there and is not re-exported from renderer.
|
||||
- `texture` (private inline module in `lib.rs`): `TextureManager` and `TextureId` around shared images.
|
||||
|
||||
## Key types and API surface
|
||||
- Public modules are `commands`, `render_graph`, `rendering`, `swapchain`, and `util`; `text`, `egui_pass`, and `texture` are private. `buffers`, `images`, and `pipeline` are crate-private aliases to public `renderer_resources` modules, not public paths through `renderer`.
|
||||
- `lib.rs` re-exports `ash` and the principal `renderer_device` APIs at crate root, including `device`, `instance`, `queue`, `sync`, `Device`, `DeviceOwned`, `DeviceRef`, `Instance`, `Error`, and `Result`.
|
||||
- Root entry point: `Renderer2` owns a `Device`, `SamplerCache`, `TextureManager`, and display handle. Its API creates surfaces and builds/submits a `RenderGraph` in `draw_graph`.
|
||||
- Root helper types include `EguiState`, `EguiTextureInfo`, and `SamplerCache`. `TextureManager` and `TextureId` are used by these internals but are not re-exported at crate root.
|
||||
- `SamplerCache` caches `renderer_resources::pipeline::Sampler` objects by `SamplerDesc`; `EguiState` keeps texture mappings and descriptor/pipeline resources for egui.
|
||||
- `commands::CommandPools`, `CommandPool`, `CommandBuffer`, and `CommandBufferFuture` provide per-thread command-pool allocation and submission. `SingleUseCommandPool`/`SingleUseCommand` support one-shot operations.
|
||||
- `commands::CommandBufferExt` adds recording helpers for barriers, copies, rendering, and pipeline state. `CommandList<T: HasQueue>` submits command buffers that share a queue.
|
||||
- `swapchain::Surface` owns the surface and current configured swapchain; `SwapchainConfiguration` describes image format, extent, present mode, usage, and image count. Acquired image values must be presented.
|
||||
- Public `render_graph` items come from `legacy.rs`: `RenderGraph`, `GraphResourceId`, `GraphResourceDesc`, `PassDesc`, `RenderContext`, `Access`, `LoadOp`, and `StoreOp` provide pass/resource declaration and resolution.
|
||||
- `RenderGraph::add_resource` / `import_image` / `import_buffer` / `import_framebuffer` register graph resources; `add_pass` records read/write declarations and a callback; `resolve` creates internal resources and returns a command list with graph transitions.
|
||||
- `rendering::Wireframe` is an example higher-level renderer; egui pass functions are re-exported from `rendering` rather than the private `egui_pass` module.
|
||||
- Resource types are defined in `renderer_resources`: `Buffer`/`BufferDesc`; `Image`/`ImageDesc`, `ImageView`/`ImageViewDesc`, `MipRange`, `Extent`, and `Offset`; and pipeline, descriptor, shader, and sampler types.
|
||||
- Buffer/image descriptors carry Vulkan usage, memory location, queue-sharing flags, and allocation strategy; `Buffer::map`/`map_mut` expose mapped memory only when the allocator returns a mapped allocation.
|
||||
- The egui integration uploads texture deltas to GPU images and records passes through the legacy `RenderGraph`; shader binaries remain under `crates/renderer/shaders`, and renderer's hardcoded shader paths are unchanged.
|
||||
- `ImageDesc` includes image shape, format, layout, usage, queue family selection, and allocation options. The resource crate's `Image::new_with_allocation` checks allocation size and memory-type compatibility before binding.
|
||||
- `ImageViewDesc` selects view type, format, aspects, mip range, and array layers; the resource crate's `Image::create_view` normalizes `REMAINING_MIP_LEVELS` / `REMAINING_ARRAY_LAYERS` and checks image/view compatibility.
|
||||
- `BufferDesc` and `ImageDesc` use `renderer_device::device::QueueFlags` to select concurrent sharing families. Empty/single-family selections use exclusive sharing.
|
||||
- `Pipeline` retains its `PipelineLayout` via `Arc`; `DescriptorPool` serializes descriptor allocation/reset using a mutex. Pipeline creation uses the device pipeline cache.
|
||||
- `Surface::configure` skips recreating an equivalent swapchain for the same device; configuration validation falls back for unsupported present mode/format and rejects zero or oversized extents.
|
||||
- `SwapchainImage` dereferences to an `ImageView`; `SwapchainFrame` exposes the swapchain, image, format, view, and acquire/release semaphores used by frame submission.
|
||||
- `render_graph::legacy::RenderGraph` stores resource descriptions until `resolve`; framebuffer imports are marked as present outputs.
|
||||
- `render_graph::legacy::PassDesc` lists per-pass `reads`, `writes`, and an optional `RecordFn`; the graph resolver builds dependencies and transitions between `Access` states.
|
||||
- `render_graph::commands::Command` distinguishes side-effect declaration from GPU command application, with commands marking resource ranges/stages/accesses for barriers.
|
||||
- `render_graph::recorder::CommandList` type-erases recorded commands into bytes and tracks per-resource side effects.
|
||||
- Internal text support defines `FontId` and a private font atlas store; it rasterizes glyphs with `cosmic-text` into CPU image data but is not a public renderer API.
|
||||
- `util::DropGuard`, `MutexExt`, `FormatExt`, `Rgba`/`Rgba8`, and `def_monotonic_id!` support lifetime callbacks, lock closures, format conversion, color values, and IDs.
|
||||
- `render_graph::legacy::RenderContext` exposes the selected device, one-shot command buffer, graph resources, and optional framebuffer to each pass record callback.
|
||||
- Graph `Access` includes stage, access mask, and image layout. Imported resources require callers to describe their initial access; framebuffer import marks presentation as an output.
|
||||
- `DeviceObject<vk::Buffer>`, `DeviceObject<vk::Image>`, `DeviceObject<vk::ImageView>`, and `DeviceObject<vk::SwapchainKHR>` wrap device-owned Vulkan handles. Resource wrappers retain allocations/devices as needed; see the device-layer guide for lifecycle rules.
|
||||
- `renderer::device::Allocation` (from `renderer_device`) distinguishes owned, shared, and unmanaged GPU allocations; the resource image implementation uses it when constructing allocated images.
|
||||
- `GraphResource` can hold imported resources, owned buffers/images, framebuffers, or resource descriptions awaiting resolution. `RenderGraph::resolve` creates described resources and records `SingleUseCommand`s for its passes.
|
||||
- Descriptor, pipeline, shader, and sampler abstractions are implemented in `renderer_resources::pipeline`. Root `SamplerCache` uses them through the private alias, not a public renderer pipeline module.
|
||||
|
||||
## Invariants and gotchas
|
||||
- Device ownership is delegated to `renderer_device`: wrappers here strongly retain `Device` through its `DeviceObject` or explicit fields. Do not place a device-owning wrapper back inside `DeviceInner`.
|
||||
- `Image::from_swapchain_raw(image, device, format, extent, usage)` wraps a swapchain-owned raw image without taking image ownership. `Swapchain::acquire_image` passes the swapchain device, configured format/usage, and configured extent with depth 1. Its descriptor remains TYPE_2D, one mip/layer, TYPE_1 samples, OPTIMAL tiling, PRESENT queue flags, UNDEFINED layout, and GpuOnly memory. Do not destroy that image as an independently allocated image.
|
||||
- `ImageView` retains an `Arc<Image>` so its source image outlives the Vulkan view. `Image::create_view` in `renderer_resources` returns `ManuallyDrop<ImageView>`; renderer call sites transfer it deliberately (the swapchain path uses `ManuallyDrop::into_inner`).
|
||||
- For allocated images, `Allocation::Owned` frees allocation memory with its `DeviceObject`; `Allocation::Shared` permits shared ownership and only exposes mutable access when uniquely held; `Unmanaged` carries no allocator allocation.
|
||||
- `Swapchain` owns raw swapchain images and manually created acquire/release semaphores. Its drop path waits idle and destroys those semaphores; the `DeviceObject` destroys the swapchain handle.
|
||||
- `SwapchainImage` / `SwapchainFrame` are `#[must_use]` acquired-frame values. Presentation consumes the frame; preserve its `Arc<Swapchain>` so image ownership stays valid until presentation.
|
||||
- `Surface` uses a `DropGuard` for surface destruction and keeps its `Instance`; field order drops the swapchain lock first, then the guard, then the instance.
|
||||
- `SingleUseCommand` and `CommandBuffer` use `DeviceObject<vk::CommandBuffer>`, whose device-layer destroy implementation is intentionally a no-op. Their own drops free command buffers through their pools. `SingleUseCommandPool` owns a raw mutex-protected pool and destroys it itself.
|
||||
- `CommandList::submit` requires all commands to come from the same queue; `CommandBuffer::submit` checks queue-family compatibility.
|
||||
- `CommandPool` is explicitly `!Sync`; `CommandBuffer` is explicitly `!Send` and `!Sync`. `SingleUseCommand` is explicitly `!Sync`. Preserve these thread-affinity constraints when changing allocation/submission wrappers.
|
||||
- `CommandPool::alloc` currently calls `free_command_buffers()` before allocating, so its free-list is not an enduring reusable buffer cache. `CommandBufferFuture` frees the submitted buffer when the future is dropped.
|
||||
- `SingleUseCommand` begins recording with `ONE_TIME_SUBMIT`; callers must end/submit according to its state and must not access it concurrently (it is `!Sync`).
|
||||
- `CommandPools` is a thread-local registry sharded by thread and queue family; its unsafe `Send` implementation documents an assumption that shards are unused during drop/movement.
|
||||
- `Surface::new_from_raw_window_handle` is unsafe: the caller keeps display/window handles valid while surface use continues.
|
||||
- `Swapchain::release_resources` is an unsafe once-only pre-destruction operation; `Drop` invokes it and currently ignores the result of its internal idle wait.
|
||||
- `Swapchain` uses separate synchronization semaphore lifetimes: acquire semaphores are sized for frames in flight, release semaphores are per swapchain image. Preserve their indexing, explicit destruction, and uniqueness; they are manually managed, not entries in `DevicePools`.
|
||||
- Resource implementation details are owned by `renderer_resources`; `crate::buffers`, `crate::images`, and `crate::pipeline` resolve through crate-private aliases. Do not expose these aliases as renderer public modules.
|
||||
- `Allocation::Shared` only permits `allocation_mut()` through `Arc::get_mut`; callers must not assume shared allocation contents can always be mutated.
|
||||
- `ImageDesc` and `BufferDesc` omit `alloc_scheme` from both `Hash` and `Eq`; `Hash` omits `name`, and `Eq` also omits `name`. Check descriptor-cache assumptions before relying on either as a complete semantic identity.
|
||||
- `Image::new_with_allocation` in `renderer_resources` validates memory size/type but still creates the raw image before validation; failure paths should account for cleaning up handles if this function is revised.
|
||||
- `Surface::configure` replaces the current `Arc<Swapchain>` after creating the new one; in-flight tokens retain old swapchains, so preserve that overlap behavior on reconfiguration.
|
||||
- `Swapchain::acquire_image` moves the potentially blocking acquire call to `smol::unblock`, waits on a pooled fence, and constructs the acquired view before returning the frame.
|
||||
- `Renderer2::draw_graph` acquires a frame, imports its image, resolves/submits the graph with acquire/release semaphores, awaits completion, and then presents.
|
||||
- The active public graph API is `render_graph/legacy.rs`. Other graph siblings are private experimental/incomplete implementation, not a replacement API; avoid relying on TODO scaffolding.
|
||||
- `render_graph/legacy.rs` has partial/unfinished behavior in some paths; inspect the exact pass/resource path being changed.
|
||||
- `GraphResource::simple_hash` is used as an import deduplication key, not a collision-proof identity or equality check. `resolve` appends an output-read pass.
|
||||
- `RenderGraph::resolve` creates a `SingleUseCommandPool` on the device main queue and records pass callbacks into command buffers. The graph's task scheduling remains single-queue in this path.
|
||||
- Some legacy graph commands (`clear_pass`, `present_pass`) are convenience helpers; `present_pass` is deprecated in favor of marking outputs with `Access::present()`.
|
||||
- The newer `render_graph/commands.rs` and `recorder.rs` use additional `ResourceId`/typed resource wrappers, distinct from legacy `GraphResourceId` and `GraphResource`.
|
||||
- The crate root also exposes `Extent2D` and `utils` alongside renderer modules and device re-exports.
|
||||
- `SamplerDesc` implements float-aware equality and hashing with utility helpers; keep those implementations consistent if adding sampler fields.
|
||||
- `Pipeline::new_graphics` supports dynamic rendering state through `RenderingState`; its render-pass and rendering choices are supplied in `GraphicsPipelineDesc`.
|
||||
- `Buffer` and `Image` creation in `renderer_resources` allocates/binds via `DeviceInner.alloc2`; device ownership and final allocation release are handled by the device-layer wrappers.
|
||||
- Resource types live in `renderer_resources`; the `renderer` aliases are crate-private and are not externally reachable as `renderer::buffers::...` etc.
|
||||
- `Renderer2::new` selects an adapter with `None` for the surface, then creates the logical device using its retained display handle. `choose_adapter_default` does not evaluate raw surface compatibility.
|
||||
- `render_graph/legacy.rs` implements the active public graph resolver. Its resources are ordered by `GraphResourceId`, and passes declare reads/writes through `PassDesc` so the resolver can derive dependencies and transitions.
|
||||
|
||||
## Conventions
|
||||
- Nightly crate features in `lib.rs`: `negative_impls`, `map_try_insert`, `debug_closure_helpers`.
|
||||
- Use `renderer`'s re-exported `Device`, `DeviceOwned`, and `DeviceRef` APIs rather than adding a parallel device ownership model.
|
||||
- Do not commit changes. Verify with `cargo check --workspace`; device-layer tests run with `cargo test -p renderer_device`.
|
||||
- Run focused crate checks with `cargo check -p renderer`; use `cargo check --workspace` for changes crossing crate boundaries.
|
||||
- Resource APIs live in the public `renderer_resources` crate. Renderer aliases are deliberately crate-private; keep the renderer-facing API intentional when adding re-exports.
|
||||
|
||||
## Change guidance
|
||||
- Add buffer/image/pipeline resource constructors in `renderer_resources`, using its `renderer_device` re-exports and retaining the correct device/handle owner. Do not add a dependency from `renderer_resources` back to `renderer`.
|
||||
- For every resource, identify whether it owns, shares, or merely borrows Vulkan handles and memory before implementing `Drop` or changing field order.
|
||||
- Keep image-view format, aspect, mip, layer, and view-kind validation in `renderer_resources`; Vulkan view compatibility depends on the backing image description and create flags.
|
||||
- A swapchain image is not an independently destroyable `Image`; retain its swapchain and do not attach a GPU allocation to it.
|
||||
- Keep allocation freeing and Vulkan handle destruction in `renderer_resources` in a safe drop order: image/buffer before bound allocation memory.
|
||||
- Use the correct queue family set in buffer/image descriptions when resources are accessed from more than one queue; `QueueFlags` here is the device-layer selection type.
|
||||
- Command buffer recording and queue submission have separate synchronization requirements. Preserve queue locks and ensure an in-flight command buffer/pool is not freed before completion.
|
||||
- `CommandBufferFuture` owns the submitted command buffer until the future is dropped; callers that discard it early change its lifetime and must ensure GPU work is complete.
|
||||
- Surface/window raw-handle validity is the caller's obligation; retain the originating instance until surface teardown.
|
||||
- `SwapchainImage` and `SwapchainFrame` are must-use ownership tokens; keep presentation consuming the token to prevent duplicate presentation.
|
||||
- Graph callbacks in `PassDesc` capture resources across graph resolution. Preserve their lifetimes and correctly declare resource reads/writes for dependency/barrier generation.
|
||||
- When adding a public graph API, check whether it belongs to the legacy `render_graph` re-export or an unexposed experimental sibling; do not accidentally promise incomplete internals.
|
||||
- The newer typed graph modules have stubs and `todo!()` paths; add tests and complete the actual resolve/record flow before routing runtime rendering through them.
|
||||
- Egui texture IDs map separately between `egui::TextureId` and internal `TextureId`; updates and frees must maintain both maps and texture manager state.
|
||||
- Shader paths are currently relative to the workspace working directory; preserve or intentionally redesign that runtime asset lookup contract.
|
||||
- `renderer_resources::util` owns the float helpers and `F32`; renderer re-exports those names through `renderer::util`. Keep `SamplerDesc` float equality/hash helpers in sync for every field so `SamplerCache` map keys remain valid.
|
||||
- For pipeline changes, account for both legacy render-pass and dynamic-rendering descriptions and the shared `device.pools.pipeline_cache.raw` handle.
|
||||
- `util::DropGuard` invokes its closure at drop; do not allow a closure to outlive raw Vulkan dependencies it captures.
|
||||
- The `egui` pre-pass currently packs texture updates into a staging image/buffer and registers graph work; texture-free deltas and upload ordering deserve explicit coverage when changing the integration.
|
||||
- The private font-atlas image is CPU memory, not a Vulkan `images::Image`; do not conflate the similarly named `Image` types when editing `text.rs`.
|
||||
- `weak_vec` moved to `renderer_resources::util` and is not re-exported by `renderer`; it prunes dead weak references on capacity pressure. Image-side view tracking should remain weak to avoid ownership cycles.
|
||||
- Render graph IDs are distinct: legacy `GraphResourceId` wraps `u32`; the newer internal `resources::ResourceId` wraps `u64`.
|
||||
- Public `commands::traits::CommandBufferExt` contains raw recording convenience methods; those methods assume valid command-buffer state and do not all enforce it at runtime.
|
||||
- `SwapchainConfiguration.image_count` is treated as the additional desired in-flight count during validation, while creation asks for minimum surface image count plus that configured value. The fallback-configuration helper assigns a total count.
|
||||
- The supported surface capability extension query can filter present modes based on the selected mode; its behavior depends on optional `InstanceExtensions` support.
|
||||
- `rendering::Wireframe` allocates GPU buffers and submits a one-shot upload before returning, then records its rendering pass through the legacy graph.
|
||||
119
crates/renderer_device/AGENTS.md
Normal file
119
crates/renderer_device/AGENTS.md
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
# renderer_device
|
||||
|
||||
## Purpose
|
||||
`renderer_device` is the Vulkan instance, logical-device, queue, synchronization, and device-owned-object layer extracted from `renderer`. It owns Vulkan device lifecycle and shared device services; higher-level resource and rendering abstractions belong in `renderer`.
|
||||
|
||||
## Module map
|
||||
- `lib.rs`: crate error/result types, physical-device feature/property descriptions and queries, surface capabilities, extension helpers, and public module exports. `extern crate self as renderer_device` supports generated derive paths.
|
||||
- `device.rs`: `Device`/`DeviceInner`, logical-device creation, pools, ownership traits/wrappers, extension and allocation helpers.
|
||||
- `instance.rs`: `Instance`/`InstanceInner`, instance setup, adapter discovery/selection, surface capability queries, debug messenger lifecycle.
|
||||
- `queue.rs`: queue-family selection, `Queue`, queue synchronization, and `DeviceQueues`.
|
||||
- `sync.rs`: fence/semaphore wrappers and pools, `FenceFuture`, and background fence waiters.
|
||||
- `pipeline_cache.rs`: keyed Vulkan pipeline cache with `pipeline_cache.bin` load/export.
|
||||
- `debug.rs`: Vulkan debug callback forwarding messages to tracing.
|
||||
- `renderer_macros/src/lib.rs`: separate workspace proc-macro source; derives `DeviceRef` for a struct field marked `#[device]`.
|
||||
|
||||
## Key types and ownership
|
||||
- `Device(Arc<DeviceInner>)` is the cloneable owning handle. It dereferences to `DeviceInner`; equality is `Arc::ptr_eq`.
|
||||
- `DeviceInner` keeps the raw `ash::Device`, allocator mutex, adapter and instance, queues, synchronization threadpool, extension loaders, and inline `DevicePools`.
|
||||
- `PhysicalDeviceInfo::create_logical_device` selects queues, enables requested/supported extensions and features, initializes the allocator/cache, and constructs `DeviceInner` with `Arc::new_cyclic`.
|
||||
- `DeviceInner::dev`, `instance`, `queues`, `physical_device`, `main_queue`, `compute_queue`, and `transfer_queue` are the main convenience accessors for downstream code.
|
||||
- `DevicePools` contains a `PipelineCache` wrapper plus pools for raw fences and binary/timeline semaphore newtypes. A `Pool<T>` stores bare handles in `Mutex<Vec<T>>` and a `Weak<DeviceInner>`; `get()` reuses a handle or creates one through `Pooled`.
|
||||
- `PoolObject<T: Recycle>` strongly owns a `Device`; dropping it returns its handle to the relevant pool. The pool's weak reference prevents a `DeviceInner` ownership cycle.
|
||||
- `asdf::DeviceObject<T>` strongly owns its `Device`, dereferences to its handle, and destroys that handle through `asdf::traits::ExternallyManagedObject<Device>` on drop. `DeviceOwned<T>` exposes the owning device and raw handle.
|
||||
- `DeviceRef` supplies `device()` for `Device`, Arc/reference forms, and structs supported by the `DeviceRef` derive (`#[device]` identifies the field). `Pooled` creates pooled handles; `Recycle` returns them.
|
||||
- The `DeviceRef` proc macro is implemented in the separate `renderer_macros` package and emits paths rooted at `::renderer_device::`.
|
||||
- `sync::Fence` and `sync::Semaphore` distinguish `Dedicated` (`DeviceObject`) from `Pooled` (`PoolObject`) ownership. `SemaphoreInner`, `BinarySemaphore`, and `TimelineSemaphore` represent semaphore-kind handles.
|
||||
- `PhysicalDeviceInfo`, `PhysicalDeviceFeatures`, `PhysicalDeviceProperties`, and `SurfaceCapabilities` carry adapter/capability data. `Instance::choose_adapter_default` accepts `Option<vk::SurfaceKHR>` (a raw handle), not a swapchain/surface wrapper.
|
||||
- `Error` and `Result<T>` are the crate-wide error types; `Error` covers Vulkan, loading, allocator, I/O, and renderer-facing resource/capability failures.
|
||||
- `InstanceDesc` configures borrowed instance extensions/layers, optional app metadata, and a raw display handle; its default requests surface and validation layers, with validation settings in debug builds. `Instance::new` creates a shared `Instance` and requests debug-utils plus optional capability-query extensions alongside requested/platform extensions.
|
||||
- `Instance::expose_adapter` queries one adapter. `choose_adapter_default` ranks enumerated adapters against requested extensions/features and prefers discrete devices, then newer APIs, device-local memory, and texture size.
|
||||
- `PhysicalDeviceFeatures` stores core, Vulkan 1.1/1.2/1.3, and optional mesh-shader feature structures; `push_to_device_create_info` chains enabled feature structs.
|
||||
- `PhysicalDeviceProperties` stores supported extensions, core/versioned properties, optional mesh-shader properties, memory properties, and API version; `supports_extension` checks name and minimum version.
|
||||
- `get_physical_device_features` and `get_physical_device_properties` query the feature/property chains. Extension query results are consumed by the instance and logical-device setup helpers.
|
||||
- `DeviceExtensions` holds the debug-utils device loader and optional swapchain/mesh-shader loaders. `DeviceInner.enabled_extensions` records enabled extension names.
|
||||
- `AllocationStrategy` selects allocator-managed blocks or a dedicated allocation. `Allocation` represents `Owned`, `Shared`, or `Unmanaged` GPU allocations.
|
||||
- `get_extensions` returns supported/enabled and unsupported requested extension sets; `get_layers` returns available requested layers or the supported/unsupported split.
|
||||
- `Queue` shares a `QueueInner` through `Arc`; `with_locked` serializes operations on the Vulkan queue. `QueueFamily` records index, count, flags, granularity, and timestamp bits.
|
||||
- `DeviceQueues` provides graphics, compute, and transfer queues; absent compute/transfer queues alias the graphics queue. Queue selection requires the selected graphics family to support presentation.
|
||||
- `DeviceQueueInfos` chooses queue families and produces `vk::DeviceQueueCreateInfo` values before `retrieve_queues` obtains the raw Vulkan queues.
|
||||
- `QueueFlags` in `device.rs` describes requested graphics/async-compute/transfer/present families; `queue::QueueFlags` describes discovered Vulkan family capabilities. They are distinct types.
|
||||
- `FenceFuture` waits and resets a fence, and `SyncThreadpool` schedules blocking fence waits for async wakers. `Fence::from_pool` and `Semaphore::from_pool` wrap recycled pool entries.
|
||||
- `SemaphoreType` selects binary or timeline construction. `SemaphoreInner` wraps the common Vulkan handle while `BinarySemaphore` and `TimelineSemaphore` tag their respective pools.
|
||||
- `Fence::wait_on`, `is_signaled`, and `reset` operate on either dedicated or pooled fence handles through their owning device.
|
||||
- Pooled timeline semaphores are created at value zero; `Semaphore::from_pool` signals the requested initial value when it obtains one.
|
||||
- `PipelineCache::new` computes its key from adapter vendor/API/device/driver properties and tries to load compatible data; cache persistence uses a magic and format version.
|
||||
- `DebugName` is the wrapper for Vulkan object debug names.
|
||||
- `Extension` is the name/version pair used by extension helpers and adapter creation. `make_extension!`, `get_extensions`, `get_layers`, and the support/intersection functions centralize Vulkan extension discovery.
|
||||
- `SurfaceCapabilities` returns raw `SurfaceCapabilitiesKHR` plus the effective minimum image count, surface formats, and present modes queried for the raw surface.
|
||||
- `DebugUtilsCreateInfo` is the small severity/message-type configuration record used with debug utilities; `DebugUtils` itself owns the instance loader and messenger handle.
|
||||
- `InstanceExtensions` stores optional surface-capability-query extension loaders and an optional surface-maintenance marker.
|
||||
- `DeviceHandle` is a separate unsafe mutable-handle destruction trait used by `vk::DescriptorPool`; the normal `DeviceObject` destruction path uses `ExternallyManagedObject` instead.
|
||||
- `asdf::ExternallyManagedObject<T, O>` is the generic owner/inner wrapper: it uses `ManuallyDrop`, invokes `destroy` exactly once, and retains the owner until that call.
|
||||
- `DeviceObject<T>` exposes `device()`, `handle()` for `Copy` handles, and optional `name()`. It only implements `DeviceOwned<T>` when `T` is a Vulkan handle.
|
||||
- `Queue::with_locked` serializes calls on a queue; `DeviceQueues::family_indices` deduplicates family IDs for resource-sharing modes.
|
||||
- `Fence::new_dedicated` and `Semaphore::new_dedicated` create per-object device-owned primitives; `from_pool` constructors create pooled sync objects.
|
||||
- `FenceFuture::block` waits then resets. Polling an unsignaled future schedules a blocking waiter through `SyncThreadpool`; the pool's worker count is bounded by its configured maximum.
|
||||
|
||||
## Invariants and gotchas
|
||||
- `asdf` is the literal legacy module name for `DeviceObject` and its traits. Do not silently rename paths; generated/client code refers to these APIs.
|
||||
- Vulkan child handles left bare in pools at device teardown are implicitly released by `vkDestroyDevice`; pool entries must not independently retain device-owning wrappers.
|
||||
- `_drop: DeviceDrop` is the private shutdown path: it waits for the device to idle, exports and destroys the pipeline cache to `pipeline_cache.bin`, then destroys the Vulkan device. The cache is also exposed as a copyable wrapper through `DevicePools` for pipeline creation.
|
||||
- `Device` cloning is cheap and prolongs the full device lifetime; any live `DeviceObject`, `PoolObject`, sync wrapper, or other owner retaining a `Device` can delay shutdown.
|
||||
- Device-owned wrapper structs live outside `DeviceInner` and its pools. Pools hold bare handles, never `DeviceObject` or `PoolObject` wrappers.
|
||||
- `Pool::device()` upgrades its `Weak` and panics if the pool outlives its device; don't retain pools independently.
|
||||
- `PoolObject` uses `ManuallyDrop` to transfer its inner handle to `Recycle` exactly once. New `Recycle` implementations must route to the matching pool and handle type.
|
||||
- The derive parser requires a named-field struct with exactly one `#[device]` field and does not add generic bounds itself; generic field types must satisfy `DeviceRef` through the input declaration. Its generated paths require the crate self-alias in `lib.rs`.
|
||||
- `vk::Semaphore` and `SemaphoreInner` have destruction implementations for dedicated ownership; pooled semaphore ownership instead recycles `SemaphoreInner` into a kind-specific pool.
|
||||
- `vk::Buffer`, `vk::Image`, `vk::ImageView`, descriptor/pipeline objects, swapchains, semaphores, and GPU allocations have concrete destruction paths; inspect `device.rs` and `sync.rs` before introducing another handle wrapper.
|
||||
- Queue `lock()`/`unlock()` and `DeviceInner::lock_queues`/`unlock_queues` are unsafe paired operations. Prefer `Queue::with_locked` or `wait_idle` unless the caller can uphold their contracts.
|
||||
- `Instance` shares its raw Vulkan instance through `Arc`; `DebugUtils` owns and destroys its messenger. Device references retain their `Instance`.
|
||||
- `superset_of` compares requested-true flags with `a != b`, which looks inverted for a superset test — verify before relying on adapter feature selection.
|
||||
- Physical-device feature/property queries chain Vulkan 1.1/1.2/1.3 structures and optional mesh-shader data without checking each version's availability; property querying marks beyond-1.0 probing as not yet fallible.
|
||||
- `choose_adapter_default` ignores its optional raw surface argument (`TODO`); it does not test surface compatibility.
|
||||
- Instance/queue selection accepts `RawDisplayHandle`; surface capability lookup uses a raw `vk::SurfaceKHR`. This crate has no `renderer::swapchain::Surface` dependency.
|
||||
- `queue::QueueFlags` and `device::QueueFlags` intentionally serve different purposes. Keep imports qualified when adding queue-family selection logic.
|
||||
- `PipelineCache` persistence uses a single working-directory path and one cache record. Changes to the key or file header should account for compatibility with existing cache files.
|
||||
- Logical-device setup always includes the swapchain extension in its required extension list, regardless of the caller's extra requested extensions.
|
||||
- Queue-family selection uses the optional display handle to query presentation support and currently asserts that its chosen graphics family is present-capable.
|
||||
- Extension selectors and feature checks drive selection; do not infer that every optional extension is enabled just because a loader type exists.
|
||||
- `Instance::query_presentation_support` handles platform-specific display variants and has an unimplemented XCB branch.
|
||||
- `DebugUtils` is stored inside `InstanceInner`; its destructor destroys the messenger, so retain the loader/messenger for the instance lifetime.
|
||||
- `DeviceObject`'s concrete destruction impls are split between `device.rs` and `sync.rs`; search both before adding a duplicate or assuming a type has an implementation.
|
||||
- `PhysicalDeviceInfo` construction can discard adapters that fail property queries; `Instance::choose_adapter` filters failed exposures before sorting.
|
||||
- `DeviceQueueInfos::retrieve_queues` reuses the graphics queue handle for absent compute or transfer roles. These aliases share the same `QueueInner` lock.
|
||||
- `QueueFamily::granularity` uses the source spelling `TransferGranuality`; avoid renaming it as unrelated cleanup.
|
||||
- `sync::Semaphore::from_pool` signals pooled timeline semaphores to the provided value but does not reset them to zero first; ensure requested timeline value semantics remain valid on reuse.
|
||||
- Fence waiters wake on successful waits, log and stop on non-timeout errors, and can time out of the worker pool after its idle duration.
|
||||
- `PipelineCache` uses MD5 only to derive a local adapter key; this is a cache identity, not a security mechanism.
|
||||
- Device-level extension loaders are constructed only for extensions recorded as enabled; callers should check optional loaders such as `device_extensions.swapchain` before use.
|
||||
- `Device::PartialEq` compares `Arc` identity, not raw Vulkan handles or physical-device identity.
|
||||
- `Instance::get_adapter_surface_capabilities` receives the surface loader separately from the raw handle and returns format/mode data alongside generic capabilities.
|
||||
- In debug builds `DebugName` stores its string; in non-debug builds its dereference returns a placeholder and does not retain the supplied name. `debug_name_object` assigns names through debug utils.
|
||||
- GPU allocation free is routed through `DeviceObject<GpuAllocation>` into the allocator mutex. Do not call allocator free on an allocation after ownership has moved or dropped.
|
||||
- `ExternallyManagedObject<Device>` must destroy each owned handle once. The `vk::CommandBuffer` implementation is intentionally a no-op: its outer command-buffer wrapper frees it through its command pool. `vk::CommandPool` itself is really destroyed.
|
||||
- Device handle types are hand-written around `DeviceObject` with `DeviceOwned` implementations where needed; the old `define_device_owned_handle!` macro no longer exists.
|
||||
|
||||
## Conventions
|
||||
- Nightly crate features: `debug_closure_helpers`, `slice_partition_dedup`.
|
||||
- Keep unsafe Vulkan destruction and queue locking contracts explicit; inspect each trait's safety requirements when adding implementations.
|
||||
- Do not commit changes. Verify with `cargo check --workspace` and `cargo test -p renderer_device` from the workspace root.
|
||||
- Keep `renderer_device` independent of higher-level render resource/swapchain wrapper types; public raw Vulkan handles and capability records form this crate's boundary.
|
||||
|
||||
## Change guidance
|
||||
- A new device-owned Vulkan handle needs a concrete `ExternallyManagedObject<Device>` implementation before it can be wrapped in `DeviceObject<T>`.
|
||||
- Keep `DeviceOwned<T>` aligned with its wrapper: `device()` returns the strong owner used for destruction, and `handle()` returns the wrapped raw handle.
|
||||
- A pooled type needs both `Pooled::create_from_pool` and `Recycle::recycle`, a matching inline entry in `DevicePools`, and initialization from its `Weak<DeviceInner>`.
|
||||
- Keep dedicated and pooled sync paths semantically separate. Reused fences may require reset; timeline semaphore reuse must honor counter values.
|
||||
- `DeviceObject::new_debug_named` requires `DebugNameable`; use `DeviceObject::new` when no valid debug naming route exists.
|
||||
- Never put `Device` or `Arc<Device>` inside a value stored inline in `DeviceInner` unless the ownership cycle is deliberately redesigned.
|
||||
- Raw Vulkan calls must use the instance/device associated with their handle. Document synchronization and lifetime obligations when exposing raw handles.
|
||||
- New queue operations must use `Queue::with_locked` or explicitly document their external-synchronization safety contract.
|
||||
- When adding required features/extensions, update adapter support checks, physical-device queries, and logical-device enablement together.
|
||||
- `required_extensions` warns when an additional requested device extension is unsupported and always includes swapchain; warnings are not a hard-failure policy.
|
||||
- Keep platform surface creation out of adapter APIs; selection/capability methods use raw Vulkan surface and display handles.
|
||||
- Pipeline cache data is an optimization: load failures fall back to an empty cache; export/write failures are logged during teardown.
|
||||
- Preserve `extern crate self as renderer_device`: proc-macro expansion uses this absolute path inside crate tests as well as downstream code.
|
||||
- The derive parser's diagnostics require named-field structs and exactly one `#[device]` field. Do not rely on the incomplete tuple-field branch.
|
||||
- `get_available_extensions` ignores failed per-layer extension enumeration results; extension compatibility otherwise compares names and minimum spec versions.
|
||||
- On `Instance`/`Device` teardown changes, inspect explicit `Drop` implementations and field drop order, not only local wrapper drops.
|
||||
72
crates/renderer_resources/AGENTS.md
Normal file
72
crates/renderer_resources/AGENTS.md
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# renderer_resources
|
||||
|
||||
## Purpose
|
||||
`renderer_resources` owns Vulkan buffer, image, view, descriptor, sampler, shader-module, and pipeline construction on the `renderer_device` layer. It handles resource memory and handle lifetimes, but contains no window, swapchain, or presentation orchestration; those remain in `renderer`. It must not depend on `renderer`.
|
||||
|
||||
## Module map
|
||||
- `lib.rs`: re-exports `Device`, `Error`, `Result`, `device`, and `make_extension` from `renderer_device`; declares four public resource modules.
|
||||
- `buffers.rs`: `BufferDesc` and `Buffer`; Vulkan buffer creation, GPU allocation/binding, raw access, and host mapping.
|
||||
- `images.rs`: `ImageDesc`, `Image`, `ImageViewDesc`, `ImageView`, subresource ranges, extents, offsets, and queue ownership. `ImageInner` distinguishes allocated images from borrowed swapchain images.
|
||||
- `pipeline.rs`: shader-stage and graphics/compute pipeline descriptions; `DescriptorPool`, `DescriptorSetLayout`, `PipelineLayout`, `Sampler`, `ShaderModule`, and `Pipeline`.
|
||||
- `util.rs`: `eq_f32`, `ord_f32`, `hash_f32`, `F32`, and the crate-private `weak_vec` module / `WeakVec` used by image view tracking.
|
||||
|
||||
## Key types and API
|
||||
- These modules are public from `renderer_resources`; the crate root re-exports the core device types and `device` module from `renderer_device` for resource construction.
|
||||
- `BufferDesc` configures Vulkan buffer flags, size, usage, queue families, memory location, allocation strategy, and optional debug name. `Buffer::new` allocates/binds memory; `map` and `map_mut` return mapped slices when available.
|
||||
- `Buffer::raw` returns the Vulkan handle and `len` returns the configured size. Mapping is bounded to the descriptor size, not the allocator block size.
|
||||
- `ImageDesc` configures image type, format, extent, layers/mips, samples, tiling, usage, initial layout, queue sharing, and allocation.
|
||||
- `Image::new` creates an allocated image. `Image::new_with_allocation` accepts an explicit `renderer_device::device::Allocation` and validates size and memory-type compatibility.
|
||||
- `Image::from_swapchain_raw(image, device, format, extent, usage)` builds an image wrapper for an image owned by a swapchain; renderer calls it from `Swapchain::acquire_image`.
|
||||
- `ImageViewDesc` describes a view's type, format, component mapping, aspect, mip range, and layer range. `Image::create_view` validates compatibility and returns `ManuallyDrop<ImageView>`.
|
||||
- `Image::create_view` resolves remaining mip/layer sentinels against the parent image, defaults an undefined view format to the image format, and checks image/view kind and format compatibility.
|
||||
- `Extent`, `Offset`, and `MipRange` represent image dimensions, offsets, and mip/layer ranges. `QueueOwnership` carries source and destination family indices.
|
||||
- `Image` exposes its raw handle, format, dimensions, and optional allocation; swapchain-backed images have no allocation.
|
||||
- Pipeline configuration records include `ShaderStageDesc`, `GraphicsPipelineDesc`, `ComputePipelineDesc`, `PipelineLayoutDesc`, `DescriptorSetLayoutDesc`, `DescriptorSetLayoutBindingDesc`, `DescriptorPoolDesc`, `DescriptorSetAllocDesc`, `SamplerDesc`, and fixed-function state descriptions.
|
||||
- Fixed-function state records are `VertexInputState`, `TessellationState`, `InputAssemblyState`, `ViewportState`, `DepthBiasState`, `RasterizationState`, `MultisampleState`, `DepthBounds`, `DepthState`, `StencilState`, `DepthStencilState`, `ColorBlendState`, `RenderingState`, and `DynamicState`.
|
||||
- `Pipeline` wraps graphics or compute Vulkan pipelines and retains its `PipelineLayout` in an `Arc`. Creation uses `device.pools.pipeline_cache.raw`.
|
||||
- `DescriptorPool` serializes allocation/reset with a mutex; descriptor set layouts and pipeline layouts own their Vulkan handles.
|
||||
- `DescriptorPool::allocate` returns raw descriptor-set handles from the provided `DescriptorSetLayout` values and optionally assigns names from `DescriptorSetAllocDesc`.
|
||||
- `DescriptorSetLayout::new` applies per-binding descriptor flags when the device supports Vulkan 1.2 or `VK_EXT_descriptor_indexing`.
|
||||
- `PipelineLayoutDesc` references descriptor-set layouts and push-constant ranges; `PipelineLayout` owns the created Vulkan layout.
|
||||
- `SamplerDesc` describes filtering, addressing, anisotropy, compare, LOD, and border settings; `Sampler::new` creates the Vulkan sampler.
|
||||
- `ShaderModule::new_from_path` reads a shader file relative to the process working directory; file-open failures map to `Error::AssetMissing`, while read/Vulkan failures use their corresponding `Error` conversions. `new_from_memory` accepts SPIR-V words.
|
||||
- `GraphicsPipelineDesc` supports render-pass/subpass inputs or dynamic `RenderingState`; optional fixed-function states are converted during pipeline creation.
|
||||
- `SamplerDesc` uses `eq_f32` / `hash_f32` for float-valued fields. `F32` provides `Eq`/`Ord`/`Hash` semantics using the same helpers.
|
||||
- `WeakVec<T>` stores `Weak<T>` references and prunes entries whose strong count is zero when growing; it is crate-private, not public API.
|
||||
|
||||
## Invariants and gotchas
|
||||
- This crate consumes the `renderer_device` ownership types and re-exports; do not add a parallel device model or a dependency on `renderer`.
|
||||
- `Image::from_swapchain_raw` stores the raw image in the `ImageInner::Swapchain` variant with its device. The swapchain owns that Vulkan image; never destroy it as an independently allocated image.
|
||||
- `from_swapchain_raw` records a 2D, one-mip, one-layer, single-sample, optimal-tiled image with PRESENT queue sharing and UNDEFINED initial layout; the caller supplies format, extent, and usage.
|
||||
- `from_swapchain_raw` retains a `Device` for views and device queries, but not ownership of the image handle or swapchain lifetime; the renderer's frame/swapchain token must keep the swapchain alive.
|
||||
- Allocated images and buffers use `DeviceObject` for Vulkan handles and allocation ownership. Keep resource destruction ordered before freeing bound memory.
|
||||
- `Buffer` equality compares the raw buffer handle; `Image` equality compares the raw image handle. Descriptor equality is separate from resource object identity.
|
||||
- `Allocation::Owned` owns a device-managed allocation; `Shared` shares it through `Arc` and permits mutable access only when uniquely held; `Unmanaged` carries no allocation.
|
||||
- `ImageView` retains `Arc<Image>` so the source image outlives the view. `Image::create_view` returns `ManuallyDrop<ImageView>`; callers must transfer/drop it deliberately.
|
||||
- `Image` tracks views through `WeakVec<ImageView>` to avoid a strong-reference cycle. The weak list does not keep views alive.
|
||||
- `ImageView`'s `DeviceObject` owns and destroys the Vulkan view; its parent `Arc<Image>` must remain alive through view destruction.
|
||||
- `ImageDesc` and `BufferDesc` omit `alloc_scheme` from both `Hash` and `Eq`; their `Hash` implementations omit `name`, and `Eq` also omits `name`. Do not treat these descriptors as complete resource identities without accounting for these fields.
|
||||
- Descriptor hashing/equality covers resource shape, usage, sharing, and memory location but not every construction input. Update both implementations together if adding descriptor-keyed resource deduplication.
|
||||
- `Pipeline` retains its layout with `Arc`; descriptor-pool operations are mutex-serialized. Keep these lifetime and synchronization guarantees when changing constructors or wrappers.
|
||||
- `DescriptorPool::allocate` returns raw `vk::DescriptorSet` handles; keep the owning pool alive while those sets are in use. Descriptor sets are not wrapped in owning resource objects here.
|
||||
- Descriptor-set layout binding flags are chained into layout creation when the device API version or descriptor-indexing extension supports them.
|
||||
- Pipeline cache lifetime and teardown belong to `renderer_device`; use the device's existing cache rather than adding a resource-crate cache.
|
||||
- Renderer shader assets remain under `crates/renderer/shaders`; this crate does not own those assets.
|
||||
- `renderer` accesses these modules through crate-private aliases. Making a resource type available in `renderer`'s public API requires an explicit API/re-export decision there.
|
||||
|
||||
## Conventions
|
||||
- `renderer_resources` has no crate-level nightly feature attributes; use APIs available under the workspace's pinned nightly toolchain and edition 2024.
|
||||
- Depend on `renderer_device` paths re-exported at this crate root (`crate::Device`, `crate::device::...`, and `crate::make_extension`) rather than introducing a dependency on `renderer`.
|
||||
- Do not commit changes. Verify with `cargo check -p renderer_resources` and `cargo check --workspace` from the workspace root.
|
||||
|
||||
## Change guidance
|
||||
- Add buffer, image, view, descriptor, sampler, shader, and pipeline constructors in this crate, not in `renderer`'s orchestration layer.
|
||||
- For each Vulkan handle, preserve the distinction between resource-owned handles and externally owned handles such as raw swapchain images.
|
||||
- Keep image-view range/format/kind validation close to image/view construction. Cover `REMAINING_MIP_LEVELS` and `REMAINING_ARRAY_LAYERS` normalization when changing range handling.
|
||||
- When modifying descriptor equality/hash behavior, update both together and account for `name` and `alloc_scheme` deliberately.
|
||||
- Preserve `renderer_device::device::Allocation` ownership semantics when accepting shared or unmanaged memory; ensure bound resources outlive their allocation use.
|
||||
- Keep `SamplerDesc` float equality and hashing consistent across every float field.
|
||||
- `renderer_resources::util::weak_vec` is internal support for images; do not expose or move it back into `renderer` without reviewing view ownership cycles.
|
||||
- Keep shader loading errors compatible with `renderer_device::Error`, and test path-based loading relative to the workspace runtime working directory.
|
||||
- Keep this crate independent of swapchain/window wrappers. Integrate swapchain images through the raw image plus explicit device, format, extent, and usage, not renderer types.
|
||||
- Keep pipeline descriptors, Vulkan state conversion, and handle ownership in this crate rather than moving resource ownership back into renderer orchestration.
|
||||
Loading…
Reference in a new issue