KDGpu Handle-Based Ownership Model¶
Introduction¶
KDGpu uses a handle-based resource management system that provides safe, efficient access to GPU resources while preventing common errors like use-after-free and resource leaks. This system is inspired by modern entity-component-system (ECS) architectures.
Handle Design¶
Handle Structure¶
Every KDGpu resource (Buffer, Texture, Pipeline, etc.) contains an opaque Handle<T> that uniquely identifies the underlying Vulkan resource:
1 2 3 4 5 6 7 8 9 | |
Key components:
- Index: Slot in the resource pool where this resource lives
- Generation: Counter that increments when the slot is reused, allowing detection of stale handles
Handle Types¶
KDGpu provides two handle variants:
RequiredHandle<T>: A handle that MUST be valid (wrapper around Handle) OptionalHandle<T>: An alias for Handlethat MAY be invalid
1 2 3 4 5 | |
Implicit Conversion to Handles¶
KDGpu resources provide implicit conversion operators to their handle types, making code more readable by allowing resources to be passed directly where handles are expected:
1 | |
Filename: kdgpu_doc_snippets.cpp
This implicit conversion:
- Reduces boilerplate code and improves readability
- Is type-safe (only converts to the correct handle type)
- Does not affect performance (zero-cost abstraction)
- Still allows explicit
.handle()calls when needed for clarity
Resource Ownership¶
Ownership Model¶
KDGpu Resources follow the expected Vulkan ownership:
- Instance owns: Adapters
- Adapter owns: Nothing (references Instance resources)
- Device owns: All created resources (Buffers, Textures, Pipelines, CommandRecorders, etc.)
This means that you must be careful about destruction order. Textures, Buffers, Pipelines ... must be released prior to the device.
Lifetime rules:
- Resources live until their owner is destroyed or the resource is explicitly moved
- Resources use RAII - destruction is automatic
- Resources cannot be copied, only moved (enforced by deleted copy constructors)
Resource Creation¶
Resources are always created through factory methods on their owner:
1 2 3 | |
Filename: kdgpu_doc_snippets.cpp
Each create*() method:
- Allocates a slot in the internal resource pool
- Creates the underlying Vulkan resource
- Returns a KDGpu wrapper object containing the handle
- Registers cleanup for automatic destruction
Handle Validity¶
Checking Validity¶
Every KDGpu resource has an isValid() method:
1 2 3 4 5 6 7 8 9 10 | |
Filename: kdgpu_doc_snippets.cpp
A resource becomes invalid when:
- Default constructed (never initialized)
- Moved from (ownership transferred)
- Underlying resource was destroyed
Stale Handle Detection¶
The generation counter prevents use of stale handles:
1 2 3 4 | |
Filename: kdgpu_doc_snippets.cpp
Move Semantics¶
Move-Only Resources¶
All KDGpu resources are move-only:
1 2 3 4 | |
Filename: kdgpu_doc_snippets.cpp
1 2 3 | |
Filename: kdgpu_doc_snippets.cpp
Storing Resources¶
Use std::vector or other STL containers for collections:
1 2 3 4 5 | |
Filename: kdgpu_doc_snippets.cpp
Resource Pools¶
Pool Architecture¶
Internally, KDGpu uses a pool allocator pattern:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Benefits:
- O(1) allocation and deallocation
- Automatic handle validation via generation counters
- Cache-friendly memory layout
- Minimal memory overhead per resource
Vulkan Resource Mapping¶
Vulkan Lifecycle¶
When you create a KDGpu resource, here's what happens:
1 2 3 4 5 6 7 8 9 10 | |
When the KDGpu resource is destroyed:
1 2 3 4 5 | |
Filename: kdgpu_doc_snippets.cpp
Accessing Vulkan Handles¶
For advanced use cases, KDGpu provides access to the underlying Vulkan resources through the GraphicsApi:
1 2 3 4 5 6 7 8 9 10 11 12 | |
Filename: kdgpu_doc_snippets.cpp
Warning: Direct access to Vulkan resources bypasses KDGpu's safety mechanisms. Only use this for:
- Interoperability with other Vulkan libraries
- Performance-critical paths after profiling
- Debugging and introspection
Best Practices¶
Do's¶
✓ Store resources in class members or STL containers
1 2 3 4 5 6 7 8 9 10 11 | |
Filename: kdgpu_doc_snippets.cpp
✓ Check isValid() after default construction
1 2 3 4 5 | |
Filename: kdgpu_doc_snippets.cpp
✓ Use move semantics for resource transfer
1 2 3 4 5 | |
Filename: kdgpu_doc_snippets.cpp
✓ Let resources destruct automatically via RAII
1 2 3 4 5 6 | |
Filename: kdgpu_doc_snippets.cpp
Don'ts¶
1 2 3 4 5 6 | |
Filename: kdgpu_doc_snippets.cpp
Anti-Pattern Examples¶
✗ Don't cache bare handles without the owning object
1 2 3 4 5 6 7 8 9 10 | |
✗ Don't use handles after moving the resource
1 2 3 4 5 6 7 8 | |
✗ Don't manually delete or free resources
1 2 3 4 5 6 7 8 | |
Comparison with Other APIs¶
vs. Raw Vulkan¶
| Aspect | Vulkan | KDGpu |
|---|---|---|
| Resource Lifetime | Need to keep VkHandle around and call vkDestroy* functions | Move-only RAII objects with handle-based ref |
| Handle Validation | VK_NULL_HANDLE check only | Generation-based stale handle detection |
| Memory Management | Manual (or use VMA separately) | Integrated with pool allocation |
| API Simplicity | (verbose) | Higher-level abstractions |
vs. Vulkan-Hpp¶
| Aspect | Vulkan-Hpp | KDGpu |
|---|---|---|
| Resource Lifetime | vk::UniqueHandle with std::unique_ptr semantics | Move-only RAII objects with handle-based ref |
| Handle Validation | nullptr check only | Generation-based stale handle detection |
| Memory Management | Manual (or use VMA separately) | Integrated with pool allocation |
| API Simplicity | 1:1 with Vulkan (verbose) | Higher-level abstractions |
See Also¶
- Handle
- Core handle template - KDGpu API Overview - KDGpu API overview
- KDGpu to Vulkan API Mapping - Vulkan mapping reference
Updated on 2026-11-07 at 00:01:27 +0000