# 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 public buffer, image, memory, pipeline, utility, and descriptor modules. - `buffers.rs`: `BufferDesc` and `Buffer`; allocated and unallocated Vulkan buffer creation, memory requirement query/binding, raw access, and host mapping. - `images.rs`: `ImageDesc`, `Image`, `ImageViewDesc`, `ImageView`, subresource ranges, extents, offsets, and queue ownership. `ImageInner` distinguishes allocated/unallocated images from borrowed swapchain images. - `memory.rs`: re-exports device `MemoryRequirements`; exposes memory validation errors, `MemoryMember`, and `MemoryGroup` for aligned shared buffer/image allocations. - `pipeline.rs`: shader-stage and graphics/compute pipeline descriptions; `DescriptorPool`, `DescriptorSetLayout`, `PipelineLayout`, `Sampler`, `ShaderModule`, and `Pipeline`. - `descriptors.rs`: managed descriptor binding/resource metadata, validation plans, errors, and pool-generation helpers; it remains independent of render-graph types. - `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, `renderer_device::device::MemoryLocation`, allocation strategy, and optional debug name. `Buffer::new` requests a VMA allocation through `DeviceInner::allocate_for_buffer` and binds it through `bind_buffer_memory`; `Buffer::new_unallocated` creates only the Vulkan handle. Its `memory_requirements` includes dedicated-allocation hints, and `bind_allocation(Arc>, offset)` validates then binds shared 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 VMA allocation block size. CPU-mapped locations select host-visible, host-coherent memory. - `ImageDesc` configures image type, format, extent, layers/mips, samples, tiling, usage, initial layout, queue sharing, and allocation strategy/location. `Image::new` uses the device's VMA allocation and image-binding helpers. - `Image::new` creates an allocated image; `Image::new_unallocated` creates an unbound image. `memory_requirements` includes dedicated-allocation hints, and `bind_allocation(Arc>, offset)` validates then binds shared memory. `Image::new_with_allocation` accepts an explicit `renderer_device::device::Allocation` and validates size and memory-type compatibility. - `MemoryGroup::new(memory_type_index, property_flags, size, alignment)` fixes the shared pool's memory type and capacity. `add(MemoryMember::Buffer(&mut buffer))` / `add(MemoryMember::Image(&mut image))` queries requirements, checks that the selected type is allowed for every member, and packs each at an aligned offset; `allocate(&device)` creates aliasable memory, then consuming `bind_all()` transfers allocation references into the resources. The group does not retain its own allocation after binding. - `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`. - `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_untracked` returns raw descriptor-set handles without retaining layouts or bound resources. `allocate_managed` returns a generation-checked `DescriptorSet` that retains its pool, layout facts, and written resource owners. - A managed `DescriptorSet` retains its pool-allocation token, layout facts, and the `Arc`, `Arc`, `Arc`, or `Arc` owners supplied for its written descriptors. Image views in turn retain their parent images. - `DescriptorSet::raw()` is fallible and checks the pool generation before exposing the Vulkan handle. Generation checks also apply to binding metadata and writer operations; they detect sets invalidated by reset but cannot revoke escaped raw handles or make concurrent reset/use safe. - `DescriptorSet::writer` stages buffer, image, sampler, and texel-buffer updates. `DescriptorWriter` validates the descriptor type, slot bounds, ranges, resource facts, and required enabled features before Vulkan updates; retained owners are replaced only after the Vulkan writes succeed. Partially-bound bindings may keep unwritten slots; other bindings must be complete at commit. - Managed writes support `UNIFORM_BUFFER`, `UNIFORM_BUFFER_DYNAMIC`, `STORAGE_BUFFER`, `STORAGE_BUFFER_DYNAMIC`, `SAMPLED_IMAGE`, `STORAGE_IMAGE`, `COMBINED_IMAGE_SAMPLER`, `SAMPLER`, `UNIFORM_TEXEL_BUFFER`, and `STORAGE_TEXEL_BUFFER`. Input attachments, acceleration structures, inline uniform blocks, and all other descriptor types are rejected until explicitly implemented. - Managed image writes reject swapchain-backed images (including combined image samplers) before updating Vulkan descriptors, because retaining an `ImageView` does not retain its swapchain. Use the untracked route only when the caller separately guarantees the external image/swapchain lifetime. - `DescriptorSetLayout::new` chains per-binding descriptor flags when the device API/extension supports the Vulkan structure and validates requested flags against enabled descriptor-indexing features. - `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` stores `Weak` 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. - Unallocated resources use `Allocation::Unmanaged` until bound. Shared bind APIs are one-shot; they validate offset alignment, allowed memory type, and allocation bounds before calling the device layer. Keep `DeviceObject` on the resource after a successful bind so Vulkan handles are destroyed before their allocation reference. - `MemoryGroup` only accepts members whose `memory_type_bits` include its selected type (the intersection across all members); it rejects dedicated-required resources. Group offsets use aligned bump packing, and allocation ownership transfers into each bound member at `bind_all`. - `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` so the source image outlives the view. `Image::create_view` returns `ManuallyDrop`; callers must transfer/drop it deliberately. - `Image` tracks views through `WeakVec` 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` must remain alive through view destruction. - `Image::create_view` returns `ManuallyDrop`; when a managed descriptor needs an owned view, deliberately transfer it with `ManuallyDrop::into_inner` and place it in an `Arc`. - `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. - Descriptor indexing validation uses `Device::enabled_features()` (the feature snapshot requested for logical-device creation), not `Device::features()` (adapter-supported features). Managed layouts validate binding-flag and descriptor-type-specific feature requirements, and managed writes validate descriptor requirements against the same enabled snapshot. - `DescriptorPool::allocate_untracked` (and its backwards-compatible `allocate` alias) is a raw escape hatch: it retains no layout or binding owners, owns no managed-set token, and is not eligible for transitive managed import. Any raw handles are the caller's responsibility. - Managed pool `reset` and `destroy` are unsafe caller-synchronized operations. Before reset/destruction, callers must ensure relevant GPU work has completed and exclude all managed-set use, including allocation, writes, graph recording/import, and command-buffer recording/binding. Final pool destruction/drop has the same synchronization requirement. Generation checks are defense-in-depth and do not close concurrent reset/use races; wrapper drops do not prove GPU retirement. Any raw handle returned by `raw()` escapes these checks and remains the caller's responsibility. Raw/untracked reset and use are likewise caller-synchronized. - 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.