RHI vulkan updates
This commit is contained in:
@@ -16,6 +16,21 @@ Use this when working on any file in `src/prism/rhi/`, or when creating a new ba
|
||||
|
||||
## Conventions
|
||||
|
||||
### Global context
|
||||
|
||||
RHI functions do **not** take allocator parameters. A global `PrRhiContext` provides two allocators:
|
||||
- `allocator` — for user-facing objects (buffers, textures, pipelines, etc.)
|
||||
- `tmp` — for short-lived internal temporaries
|
||||
|
||||
```c
|
||||
extern PrRhiContext _G_RHI_CONTEXT;
|
||||
|
||||
void prRhiInit(void); // sets up both allocators
|
||||
void prRhiDestroy(void); // tears down context
|
||||
```
|
||||
|
||||
All RHI functions access `_G_RHI_CONTEXT` directly. Do not pass allocators to RHI API calls.
|
||||
|
||||
### Backend dispatch
|
||||
|
||||
Backend selection is compile-time via `-D PR_RHI_VULKAN` / `-D PR_RHI_D3D12` / `-D PR_RHI_METAL`. The umbrella header `pr_rhi.h` includes the appropriate alias file:
|
||||
@@ -49,22 +64,95 @@ All descriptor structs are passed **by value**, not `const *`:
|
||||
|
||||
```c
|
||||
// correct
|
||||
PrRhiDevice *prRhiCreateDevice(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface,
|
||||
PrRhiDeviceDesc desc, WpAllocator *alloc);
|
||||
PrRhiDevice *prRhiCreateDevice(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface, PrRhiDeviceDesc desc);
|
||||
|
||||
// wrong
|
||||
PrRhiDevice *prRhiCreateDevice(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface,
|
||||
const PrRhiDeviceDesc *desc, WpAllocator *alloc);
|
||||
PrRhiDevice *prRhiCreateDevice(PrRhiPhysicalDevice *pdev, PrRhiSurface *surface, const PrRhiDeviceDesc *desc);
|
||||
```
|
||||
|
||||
### Frame-by-frame command batching
|
||||
|
||||
Commands that run every frame must avoid arena allocation. Use stack arrays with a while-loop to batch operations:
|
||||
|
||||
```c
|
||||
// correct — stack array, batched submission
|
||||
void prRhiCmdBindDescriptorSetsVk(PrRhiCommandBuffer *cb, PrRhiPipelineBindPoint bind_point,
|
||||
PrRhiPipelineLayout *layout, u32 first_set,
|
||||
PrRhiDescriptorSetArray sets) {
|
||||
u32 set_count = sets ? (u32)wpArrayCount(sets) : 0;
|
||||
while (set_count > 0) {
|
||||
VkDescriptorSetArray vk_sets = wpArrayWithCapacity(VkDescriptorSet, 16, WP_ARRAY_INIT_FILLED);
|
||||
u32 total_capacity = (u32)wpArrayCapacity(vk_sets);
|
||||
u32 real_count = set_count < total_capacity ? set_count : total_capacity;
|
||||
for (u32 i = 0; i < real_count; ++i) {
|
||||
vk_sets[i] = sets[i]->handle;
|
||||
}
|
||||
vkCmdBindDescriptorSets(cb->handle, vk_bp, vk_layout, first_set, real_count, vk_sets, 0, NULL);
|
||||
set_count -= real_count;
|
||||
first_set += real_count;
|
||||
}
|
||||
}
|
||||
|
||||
// wrong — allocates from arena on every call
|
||||
void prRhiCmdBindDescriptorSetsBad(PrRhiCommandBuffer *cb, ...) {
|
||||
VkDescriptorSetArray vk_sets = wpArrayAllocCapacity(VkDescriptorSet, &_G_RHI_CONTEXT.allocator, count, ...);
|
||||
// ... this leaks every frame
|
||||
}
|
||||
```
|
||||
|
||||
Apply this pattern to: `prRhiCmdBindDescriptorSets`, `prRhiCmdBindVertexBuffers`, `prRhiCmdCopyBufferToImage`, and any other command that processes user-provided arrays.
|
||||
|
||||
### Opaque struct handles — no casts
|
||||
|
||||
Handle types in opaque structs are already the correct Vulkan type. Do not cast:
|
||||
|
||||
```c
|
||||
// correct
|
||||
vk_device = device->handle;
|
||||
vk_buffer = buffer->handle;
|
||||
|
||||
// wrong
|
||||
vk_device = (VkDevice)device->handle;
|
||||
vk_buffer = (VkBuffer)buffer->handle;
|
||||
```
|
||||
|
||||
### Vulkan struct initialisation — designated initializers
|
||||
|
||||
Always use C99 designated initializers for Vulkan info structs:
|
||||
|
||||
```c
|
||||
// correct
|
||||
VkBufferCreateInfo buf_info = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = desc.size,
|
||||
.usage = _toVkBufferUsage(desc.usage),
|
||||
};
|
||||
|
||||
// wrong
|
||||
VkBufferCreateInfo buf_info = {};
|
||||
buf_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
buf_info.size = desc.size;
|
||||
buf_info.usage = _toVkBufferUsage(desc.usage);
|
||||
```
|
||||
|
||||
### API patterns
|
||||
|
||||
- **`prRhiCreateCommandPool`**: Takes only `PrRhiDevice *device` (uses `device->queue_family_index` internally)
|
||||
- **`prRhiFreeCommandBuffers`**: Takes `PrRhiCommandBufferArray buffers` (count derived from `wpArrayCount`)
|
||||
- **`prRhiAllocateDescriptorSet`**: Takes `WpU32Array variable_descriptor_counts` for variable descriptor support
|
||||
- **`prRhiCmdBindVertexBuffers`**: Takes `WpU64Array offsets` (count matched to buffers internally)
|
||||
- **Shader entry points**: Configurable via `vertex_shader_entry_point` / `fragment_shader_entry_point` in pipeline desc (not hardcoded to "main")
|
||||
|
||||
### File layout
|
||||
|
||||
```
|
||||
src/prism/rhi/
|
||||
├── pr_rhi.h ← umbrella header (API declarations + backend dispatch)
|
||||
├── pr_rhi.c ← global context definition (prRhiInit, prRhiDestroy)
|
||||
├── pr_rhi_types.h ← shared types (enums, element types, array aliases, desc structs, opaque handles)
|
||||
├── vulkan/
|
||||
│ ├── pr_rhi_vk.h ← Vulkan backend header (opaque struct defs + Vk-suffixed decls)
|
||||
│ ├── pr_rhi_vk.c ← Vulkan backend implementation
|
||||
│ ├── pr_rhi_vk_aliases.h ← #define alias mapping
|
||||
│ └── profiles/ ← generated Vulkan Profiles library
|
||||
├── d3d12/
|
||||
|
||||
Reference in New Issue
Block a user