Lifetimes derive from the real dependency ordering (retained DAG + compile-time happens-before closure), not command-index intervals. Aliasing is restricted to the single-queue contract; alias reuse is synchronized with memory/image barriers anchored at first-use commands. - renderer_device: aliasable shared allocations (CAN_ALIAS), memory- requirements queries incl. dedicated hints, offset-aware bind_buffer/image_memory2, grouped bind under one allocator lock - renderer_resources: unallocated resource creation, requirements cached in Allocation::Unmanaged (OnceLock), MemoryGroup pooling with intersecting memoryTypeBits, bind-time validation - renderer_rendergraph: retained dependency DAG, happens_before closure, batch lifetimes, Placement type, plan_placements with pooled aliasing (first-fit over offset-sorted ranges, pool classes split by bufferImageGranularity), alias-barrier construction, placement/bind error variants - perf: quadratic compile/resolve paths eliminated (per-resource indexing, frontier caching, backward-scan prune, cached recording index, recorder scratch reuse); single requirements query per resource through plan/validate/bind - design plan + GPU-gated validation tests
15 KiB
15 KiB
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-exportsDevice,Error,Result,device, andmake_extensionfromrenderer_device; declares public buffer, image, memory, pipeline, utility, and descriptor modules.buffers.rs:BufferDescandBuffer; 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.ImageInnerdistinguishes allocated/unallocated images from borrowed swapchain images.memory.rs: re-exports deviceMemoryRequirements; exposes memory validation errors,MemoryMember, andMemoryGroupfor aligned shared buffer/image allocations.pipeline.rs: shader-stage and graphics/compute pipeline descriptions;DescriptorPool,DescriptorSetLayout,PipelineLayout,Sampler,ShaderModule, andPipeline.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-privateweak_vecmodule /WeakVecused by image view tracking.
Key types and API
- These modules are public from
renderer_resources; the crate root re-exports the core device types anddevicemodule fromrenderer_devicefor resource construction. BufferDescconfigures Vulkan buffer flags, size, usage, queue families,renderer_device::device::MemoryLocation, allocation strategy, and optional debug name.Buffer::newrequests a VMA allocation throughDeviceInner::allocate_for_bufferand binds it throughbind_buffer_memory;Buffer::new_unallocatedcreates only the Vulkan handle. Itsmemory_requirementsincludes dedicated-allocation hints, andbind_allocation(Arc<DeviceObject<GpuAllocation>>, offset)validates then binds shared memory.mapandmap_mutreturn mapped slices when available.Buffer::rawreturns the Vulkan handle andlenreturns 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.ImageDescconfigures image type, format, extent, layers/mips, samples, tiling, usage, initial layout, queue sharing, and allocation strategy/location.Image::newuses the device's VMA allocation and image-binding helpers.Image::newcreates an allocated image;Image::new_unallocatedcreates an unbound image.memory_requirementsincludes dedicated-allocation hints, andbind_allocation(Arc<DeviceObject<GpuAllocation>>, offset)validates then binds shared memory.Image::new_with_allocationaccepts an explicitrenderer_device::device::Allocationand 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 consumingbind_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 fromSwapchain::acquire_image.ImageViewDescdescribes a view's type, format, component mapping, aspect, mip range, and layer range.Image::create_viewvalidates compatibility and returnsManuallyDrop<ImageView>.Image::create_viewresolves 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, andMipRangerepresent image dimensions, offsets, and mip/layer ranges.QueueOwnershipcarries source and destination family indices.Imageexposes 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, andDynamicState. Pipelinewraps graphics or compute Vulkan pipelines and retains itsPipelineLayoutin anArc. Creation usesdevice.pools.pipeline_cache.raw.DescriptorPoolserializes allocation/reset with a mutex; descriptor set layouts and pipeline layouts own their Vulkan handles.DescriptorPool::allocate_untrackedreturns raw descriptor-set handles without retaining layouts or bound resources.allocate_managedreturns a generation-checkedDescriptorSetthat retains its pool, layout facts, and written resource owners.- A managed
DescriptorSetretains its pool-allocation token, layout facts, and theArc<Buffer>,Arc<ImageView>,Arc<Sampler>, orArc<BufferView>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::writerstages buffer, image, sampler, and texel-buffer updates.DescriptorWritervalidates 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, andSTORAGE_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
ImageViewdoes not retain its swapchain. Use the untracked route only when the caller separately guarantees the external image/swapchain lifetime. DescriptorSetLayout::newchains per-binding descriptor flags when the device API/extension supports the Vulkan structure and validates requested flags against enabled descriptor-indexing features.PipelineLayoutDescreferences descriptor-set layouts and push-constant ranges;PipelineLayoutowns the created Vulkan layout.SamplerDescdescribes filtering, addressing, anisotropy, compare, LOD, and border settings;Sampler::newcreates the Vulkan sampler.ShaderModule::new_from_pathreads a shader file relative to the process working directory; file-open failures map toError::AssetMissing, while read/Vulkan failures use their correspondingErrorconversions.new_from_memoryaccepts SPIR-V words.GraphicsPipelineDescsupports render-pass/subpass inputs or dynamicRenderingState; optional fixed-function states are converted during pipeline creation.SamplerDescuseseq_f32/hash_f32for float-valued fields.F32providesEq/Ord/Hashsemantics using the same helpers.WeakVec<T>storesWeak<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_deviceownership types and re-exports; do not add a parallel device model or a dependency onrenderer. Image::from_swapchain_rawstores the raw image in theImageInner::Swapchainvariant with its device. The swapchain owns that Vulkan image; never destroy it as an independently allocated image.from_swapchain_rawrecords 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_rawretains aDevicefor 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
DeviceObjectfor Vulkan handles and allocation ownership. Keep resource destruction ordered before freeing bound memory. - Unallocated resources use
Allocation::Unmanageduntil bound. Shared bind APIs are one-shot; they validate offset alignment, allowed memory type, and allocation bounds before calling the device layer. KeepDeviceObject<GpuAllocation>on the resource after a successful bind so Vulkan handles are destroyed before their allocation reference. MemoryGrouponly accepts members whosememory_type_bitsinclude 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 atbind_all.Bufferequality compares the raw buffer handle;Imageequality compares the raw image handle. Descriptor equality is separate from resource object identity.Allocation::Ownedowns a device-managed allocation;Sharedshares it throughArcand permits mutable access only when uniquely held;Unmanagedcarries no allocation.ImageViewretainsArc<Image>so the source image outlives the view.Image::create_viewreturnsManuallyDrop<ImageView>; callers must transfer/drop it deliberately.Imagetracks views throughWeakVec<ImageView>to avoid a strong-reference cycle. The weak list does not keep views alive.ImageView'sDeviceObjectowns and destroys the Vulkan view; its parentArc<Image>must remain alive through view destruction.Image::create_viewreturnsManuallyDrop<ImageView>; when a managed descriptor needs an owned view, deliberately transfer it withManuallyDrop::into_innerand place it in anArc.ImageDescandBufferDescomitalloc_schemefrom bothHashandEq; theirHashimplementations omitname, andEqalso omitsname. 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.
Pipelineretains its layout withArc; 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), notDevice::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-compatibleallocatealias) 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
resetanddestroyare 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 byraw()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. rendereraccesses these modules through crate-private aliases. Making a resource type available inrenderer's public API requires an explicit API/re-export decision there.
Conventions
renderer_resourceshas no crate-level nightly feature attributes; use APIs available under the workspace's pinned nightly toolchain and edition 2024.- Depend on
renderer_devicepaths re-exported at this crate root (crate::Device,crate::device::..., andcrate::make_extension) rather than introducing a dependency onrenderer. - Do not commit changes. Verify with
cargo check -p renderer_resourcesandcargo check --workspacefrom 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_LEVELSandREMAINING_ARRAY_LAYERSnormalization when changing range handling. - When modifying descriptor equality/hash behavior, update both together and account for
nameandalloc_schemedeliberately. - Preserve
renderer_device::device::Allocationownership semantics when accepting shared or unmanaged memory; ensure bound resources outlive their allocation use. - Keep
SamplerDescfloat equality and hashing consistent across every float field. renderer_resources::util::weak_vecis internal support for images; do not expose or move it back intorendererwithout 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.