RHI vulkan updates

This commit is contained in:
2026-07-12 13:43:59 +01:00
parent bffe9b8174
commit 45a34bb151
10 changed files with 1361 additions and 1082 deletions
+92 -4
View File
@@ -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/
+28
View File
@@ -36,6 +36,16 @@ All code follows the patterns established in `src/wapp/`. The project prefix is
- **Tabs for indentation**, 8-column tab width.
- **Braces** on the same line as control statements (Attach style).
- **Braces on single-line if statements**: Always use braces, even for single-line bodies:
```c
// correct
if (!buffer) { return; }
if (!texture) { _abort("alloc failed"); }
// wrong
if (!buffer) return;
if (!texture) _abort("alloc failed");
```
- **Pointers**: `*` against the name, not the type (`PrRhiBuffer *buf`, not `PrRhiBuffer* buf`).
- **Line width**: 120 columns.
- **Continuation lines** align to the opening parenthesis.
@@ -117,6 +127,24 @@ prefer SoA layouts, batch processing, and minimise pointer chasing. Keep
the DAG in contiguous arrays (e.g. adjacency lists packed in flat buffers)
rather than individually allocated linked structures.
### Frame-by-frame command batching
Commands that execute every frame must avoid arena allocation. Use stack
arrays with a while-loop to batch operations in fixed-size chunks:
```c
while (count > 0) {
VkTypeArray batch = wpArrayWithCapacity(VkType, 16, WP_ARRAY_INIT_FILLED);
u32 batch_size = count < wpArrayCapacity(batch) ? count : (u32)wpArrayCapacity(batch);
// ... process batch ...
vkCmd*(cb->handle, ...);
count -= batch_size;
first += batch_size;
}
```
This avoids per-frame arena churn while handling arbitrarily large inputs.
### Graph / adjacency lists
Adjacency list nodes must be **separately allocated from the vertex array**.
+4 -1
View File
@@ -43,7 +43,7 @@ build: vendor
bear -a -- {{CC}} -g -c {{VK_FLAGS}} src/prism/rhi/pr_rhi.c -o {{BUILDDIR}}/pr_rhi.o
bear -a -- {{CC}} -g -c {{VK_FLAGS}} src/prism/rhi/vulkan/pr_rhi_vk.c -o {{BUILDDIR}}/pr_rhi_vk.o
bear -a -- {{CC}} -g -c src/wapp/wapp.c -o {{BUILDDIR}}/wapp.o
bear -a -- {{CXX}} -g -c -Wno-nullability-completeness -DVK_NO_PROTOTYPES \
bear -a -- {{CXX}} -g -c {{VK_FLAGS}} -Wno-nullability-completeness -DVK_NO_PROTOTYPES \
{{APP_INC}} \
src/main.cpp \
-o {{BUILDDIR}}/main.o
@@ -55,6 +55,9 @@ build: vendor
-o {{BUILDDIR}}/prism
@echo "--- build done: {{BUILDDIR}}/prism ---"
run:
./{{BUILDDIR}}/prism
# Clean
clean:
rm -rf {{BUILDDIR}}
+109 -114
View File
@@ -3,8 +3,7 @@
// Prism port of how-to-vulkan's main.cpp — uses the RHI API instead of
// direct Vulkan calls.
#define PR_RHI_VULKAN
#include "prism/rhi/pr_rhi_types.h"
#include "prism/rhi/pr_rhi.h"
#include <SDL3/SDL_timer.h>
#include <glm/ext/matrix_clip_space.hpp>
@@ -95,6 +94,8 @@ struct AppState {
PrRhiSurface *surface;
PrRhiSwapchain *swapchain;
PrRhiFormat swapchain_format;
PrRhiBuffer *vert_index_buf;
u64 vertex_buf_size;
u64 index_count;
@@ -136,9 +137,10 @@ struct AppState {
int main() {
AppState app = {};
WpAllocator arena = wpMemArenaAllocatorInitZero(MiB(128));
prRhiInit();
// {{{ Initialisation
prRhiInit();
check(SDL_Init(SDL_INIT_VIDEO), EXIT_CODE_SDL_INIT_FAILED);
f32 display_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
@@ -149,8 +151,7 @@ int main() {
// }}}
// {{{ Instance creation
PrRhiInstanceDesc inst_desc = {};
app.inst = prRhiCreateInstance(inst_desc);
app.inst = prRhiCreateInstance(PrRhiInstanceDesc{});
// }}}
// {{{ Physical device selection
@@ -159,8 +160,7 @@ int main() {
i32 selected = -1;
for (u32 i = 0; i < wpArrayCount(pdevs); ++i) {
PrRhiPhysicalDeviceProperties props;
prRhiGetPhysicalDeviceProperties(pdevs[i], &props);
PrRhiPhysicalDeviceProperties props = prRhiGetPhysicalDeviceProperties(pdevs[i]);
switch (props.device_type) {
case PR_RHI_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
selected = (i32)i;
@@ -176,7 +176,8 @@ int main() {
app.pdev = pdevs[selected];
// Print device info
WpStr8 dev_name, driver_info;
WpStr8 dev_name = wpStr8Buf(512);
WpStr8 driver_info = wpStr8Buf(512);
prRhiGetPhysicalDeviceName(app.pdev, &dev_name);
prRhiGetPhysicalDeviceDriverInfo(app.pdev, &driver_info);
std::cout << "Selected GPU: " << std::string_view((const char *)dev_name.buf, dev_name.size) << '\n'
@@ -186,14 +187,14 @@ int main() {
// {{{ Surface creation
check(SDL_GetWindowSize(app.window, &app.window_size.x, &app.window_size.y),
EXIT_CODE_GET_WINDOW_SIZE_FAILED);
app.surface = prRhiCreateSurfaceFromWindow(app.inst, (void *)app.window);
app.surface = prRhiCreateSurfaceFromWindow(app.inst, app.window);
// }}}
// {{{ Device creation
PrRhiDeviceDesc dev_desc = {};
dev_desc.present_mode = PR_RHI_PRESENT_MODE_FIFO;
app.device = prRhiCreateDevice(app.pdev, app.surface, dev_desc);
u32 qfi = prRhiGetQueueFamilyIndex(app.device);
// }}}
// {{{ Swapchain creation
@@ -203,9 +204,9 @@ int main() {
swap_desc.height = (u32)app.window_size.y;
swap_desc.has_depth = true;
swap_desc.depth_format = PR_RHI_FORMAT_D24_UNORM_S8_UINT;
app.swapchain = prRhiCreateSwapchain(app.device, swap_desc);
PrRhiFormat swapchain_format = prRhiGetSwapchainFormat(app.swapchain);
app.swapchain = prRhiCreateSwapchain(app.device, swap_desc);
app.swapchain_format = prRhiGetSwapchainFormat(app.swapchain);
// }}}
// {{{ Vertex/Index buffers
@@ -252,6 +253,7 @@ int main() {
vert_desc.size = app.vertex_buf_size + index_buf_size;
vert_desc.usage = (PrRhiBufferUsage)(PR_RHI_BUFFER_USAGE_VERTEX | PR_RHI_BUFFER_USAGE_INDEX);
vert_desc.memory = PR_RHI_MEMORY_CPU_TO_GPU;
app.vert_index_buf = prRhiCreateBuffer(app.device, vert_desc);
void *mapped = prRhiBufferMap(app.device, app.vert_index_buf);
@@ -271,6 +273,7 @@ int main() {
buf_desc.usage = (PrRhiBufferUsage)(PR_RHI_BUFFER_USAGE_STORAGE |
PR_RHI_BUFFER_USAGE_SHADER_DEVICE_ADDRESS);
buf_desc.memory = PR_RHI_MEMORY_CPU_TO_GPU;
app.shader_data_bufs[i] = prRhiCreateBuffer(app.device, buf_desc);
}
// }}}
@@ -294,7 +297,7 @@ int main() {
}
// Render completed semaphores (per swapchain image)
u32 swapchain_image_count = app.swapchain->image_count;
u32 swapchain_image_count = prRhiGetSwapchainImageCount(app.swapchain);
app.render_completed_semaphores = wpArrayAllocCapacity(PrRhiSemaphore *, &arena,
swapchain_image_count,
WP_ARRAY_INIT_FILLED);
@@ -304,10 +307,7 @@ int main() {
// }}}
// {{{ Command pool and buffers
PrRhiCommandPoolDesc pool_desc = {};
pool_desc.queue_family_index = qfi;
app.cmd_pool = prRhiCreateCommandPool(app.device, pool_desc);
app.cmd_pool = prRhiCreateCommandPool(app.device);
app.cmd_buffers = prRhiAllocateCommandBuffers(app.device, app.cmd_pool,
AppState::max_frames_in_flight);
// }}}
@@ -331,27 +331,28 @@ int main() {
samp_desc.mipmap_mode = PR_RHI_MIPMAP_MODE_LINEAR;
samp_desc.max_anisotropy = 8.0f;
samp_desc.max_lod = PR_RHI_LOD_CLAMP_NONE;
PrRhiSampler *sampler = prRhiCreateSampler(app.device, samp_desc);
app.textures[i].texture = tex;
app.textures[i].sampler = sampler;
}
prRhiFreeCommandBuffers(app.device, app.cmd_pool, 1, upload_cbs);
wpArrayDealloc(PrRhiCommandBuffer *, &arena, &upload_cbs);
prRhiFreeCommandBuffers(app.device, app.cmd_pool, upload_cbs);
// }}}
// {{{ Descriptor set layout, pool, set
PrRhiDescriptorSetLayoutBinding bindings[1] = {};
bindings[0].type = PR_RHI_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
bindings[0].descriptor_count = AppState::texture_count;
bindings[0].stage_flags = PR_RHI_SHADER_STAGE_FRAGMENT;
bindings[0].binding_flags = PR_RHI_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT;
PrRhiDescriptorSetLayoutBindingArray ds_layouts = wpArray(
PrRhiDescriptorSetLayoutBinding,
PrRhiDescriptorSetLayoutBinding{
PR_RHI_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
AppState::texture_count,
PR_RHI_SHADER_STAGE_FRAGMENT,
PR_RHI_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT,
}
);
PrRhiDescriptorSetLayoutBindingArray ds_layouts = wpArray(PrRhiDescriptorSetLayoutBinding, bindings[0]);
PrRhiDescriptorSetLayoutDesc layout_desc = {};
layout_desc.bindings = ds_layouts;
PrRhiDescriptorSetLayoutDesc layout_desc = { ds_layouts };
app.desc_set_layout = prRhiCreateDescriptorSetLayout(app.device, layout_desc);
// Pool
@@ -359,15 +360,16 @@ int main() {
pool_size.type = PR_RHI_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
pool_size.descriptor_count = AppState::texture_count;
PrRhiDescriptorPoolDesc pool_desc2 = {};
pool_desc2.max_sets = 1;
pool_desc2.pool_sizes = wpArray(PrRhiDescriptorPoolSize, pool_size);
app.desc_pool = prRhiCreateDescriptorPool(app.device, pool_desc2);
PrRhiDescriptorPoolDesc ds_pool_desc = {};
ds_pool_desc.max_sets = 1;
ds_pool_desc.pool_sizes = wpArray(PrRhiDescriptorPoolSize, pool_size);
app.desc_pool = prRhiCreateDescriptorPool(app.device, ds_pool_desc);
// Allocate descriptor set (variable count)
app.desc_set = prRhiAllocateDescriptorSet(app.device, app.desc_pool,
app.desc_set_layout,
AppState::texture_count);
WpU32Array var_counts = wpArray(u32, AppState::texture_count);
app.desc_set = prRhiAllocateDescriptorSet(app.device, app.desc_pool, app.desc_set_layout,
var_counts);
// Write descriptor set
PrRhiDescriptorImageInfoArray img_infos = wpArrayAllocCapacity(PrRhiDescriptorImageInfo,
@@ -417,6 +419,7 @@ int main() {
PrRhiShaderDesc shader_desc = {};
shader_desc.spirv_code = spirv->getBufferPointer();
shader_desc.spirv_size = spirv->getBufferSize();
app.shader = prRhiCreateShader(app.device, shader_desc);
// }}}
@@ -425,56 +428,56 @@ int main() {
pc_range.stage_flags = PR_RHI_SHADER_STAGE_VERTEX;
pc_range.size = sizeof(u64);
PrRhiDescriptorSetLayout *set_layouts_pl[] = { app.desc_set_layout };
PrRhiDescriptorSetLayoutArray pl_layouts = wpArray(PrRhiDescriptorSetLayout *, set_layouts_pl[0]);
PrRhiDescriptorSetLayoutArray pl_layouts = wpArray(PrRhiDescriptorSetLayout *, app.desc_set_layout);
PrRhiPipelineLayoutDesc pl_desc = {};
pl_desc.set_layouts = pl_layouts;
pl_desc.push_constant_ranges = wpArray(PrRhiPushConstantRange, pc_range);
app.pipeline_layout = prRhiCreatePipelineLayout(app.device, pl_desc);
// }}}
// {{{ Graphics pipeline
PrRhiVertexInputBinding vertex_binding = {};
vertex_binding.binding = 0;
vertex_binding.stride = sizeof(Vertex);
PrRhiVertexInputBindingArray vertex_bindings = wpArray(
PrRhiVertexInputBinding,
PrRhiVertexInputBinding{ 0, sizeof(Vertex) }
);
PrRhiVertexAttribute vertex_attrs[3] = {};
vertex_attrs[0].location = 0;
vertex_attrs[0].binding = 0;
vertex_attrs[0].format = PR_RHI_FORMAT_R32G32B32_SFLOAT;
vertex_attrs[0].offset = 0;
vertex_attrs[1].location = 1;
vertex_attrs[1].binding = 0;
vertex_attrs[1].format = PR_RHI_FORMAT_R32G32B32_SFLOAT;
vertex_attrs[1].offset = offsetof(Vertex, normal);
vertex_attrs[2].location = 2;
vertex_attrs[2].binding = 0;
vertex_attrs[2].format = PR_RHI_FORMAT_R32G32_SFLOAT;
vertex_attrs[2].offset = offsetof(Vertex, uv);
PrRhiVertexAttributeArray vertex_attrs = wpArray(
PrRhiVertexAttribute,
PrRhiVertexAttribute{ 0, 0, PR_RHI_FORMAT_R32G32B32_SFLOAT, 0 },
PrRhiVertexAttribute{ 1, 0, PR_RHI_FORMAT_R32G32B32_SFLOAT, offsetof(Vertex, normal) },
PrRhiVertexAttribute{ 2, 0, PR_RHI_FORMAT_R32G32_SFLOAT, offsetof(Vertex, uv) }
);
PrRhiColorBlendAttachment blend_attachment = {};
blend_attachment.color_write_mask = 0xf;
PrRhiColorBlendAttachmentArray blend_attachments = wpArray(
PrRhiColorBlendAttachment,
PrRhiColorBlendAttachment{ 0xf }
);
PrRhiFormat color_fmts[] = { swapchain_format };
PrRhiFormatArray color_fmt_array = wpArray(PrRhiFormat, color_fmts[0]);
PrRhiFormatArray color_fmt_array = wpArray(PrRhiFormat, app.swapchain_format);
PrRhiGraphicsPipelineDesc pipe_desc = {};
pipe_desc.vertex_shader = app.shader;
pipe_desc.vertex_shader_entry_point = "main";
pipe_desc.fragment_shader = app.shader;
pipe_desc.vertex_bindings = wpArray(PrRhiVertexInputBinding, vertex_binding);
pipe_desc.vertex_attributes = wpArray(PrRhiVertexAttribute, vertex_attrs[0],
vertex_attrs[1], vertex_attrs[2]);
pipe_desc.fragment_shader_entry_point = "main";
pipe_desc.vertex_bindings = vertex_bindings;
pipe_desc.vertex_attributes = vertex_attrs;
pipe_desc.topology = PR_RHI_TOPOLOGY_TRIANGLE_LIST;
pipe_desc.color_attachment_formats = color_fmt_array;
pipe_desc.depth_attachment_format = swap_desc.depth_format;
pipe_desc.depth_test_enable = true;
pipe_desc.depth_write_enable = true;
pipe_desc.depth_compare_op = PR_RHI_COMPARE_OP_LESS_OR_EQUAL;
pipe_desc.blend_attachments = wpArray(PrRhiColorBlendAttachment, blend_attachment);
pipe_desc.blend_attachments = blend_attachments;
pipe_desc.dynamic_viewport = true;
pipe_desc.dynamic_scissor = true;
pipe_desc.cull_mode = PR_RHI_CULL_MODE_BACK;
pipe_desc.front_face = PR_RHI_FRONT_FACE_COUNTER_CLOCKWISE;
pipe_desc.line_width = 1.0f;
pipe_desc.layout = app.pipeline_layout;
app.pipeline = prRhiCreateGraphicsPipeline(app.device, pipe_desc);
// }}}
@@ -516,8 +519,7 @@ int main() {
glm::mat4_cast(glm::quat(app.object_rotations[i]));
}
void *shader_data_ptr = prRhiBufferMap(app.device,
app.shader_data_bufs[app.frame_index]);
void *shader_data_ptr = prRhiBufferMap(app.device, app.shader_data_bufs[app.frame_index]);
memcpy(shader_data_ptr, &app.shader_data, sizeof(ShaderData));
prRhiBufferUnmap(app.device, app.shader_data_bufs[app.frame_index]);
// }}}
@@ -529,43 +531,43 @@ int main() {
// Transition images to attachment optimal
{
PrRhiImageMemoryBarrier img_barriers[2] = {};
PrRhiImageMemoryBarrierArray barriers_arr =
wpArrayWithCapacity(PrRhiImageMemoryBarrier, 2, WP_ARRAY_INIT_FILLED);
// Color attachment
PrRhiTexture *color_tex = prRhiGetSwapchainTexture(app.swapchain, image_index);
img_barriers[0].texture = color_tex;
img_barriers[0].old_layout = PR_RHI_LAYOUT_UNDEFINED;
img_barriers[0].new_layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
img_barriers[0].src_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
img_barriers[0].src_access_mask = PR_RHI_ACCESS_NONE;
img_barriers[0].dst_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
img_barriers[0].dst_access_mask = (PrRhiAccess)(PR_RHI_ACCESS_COLOR_ATTACHMENT_READ | PR_RHI_ACCESS_COLOR_ATTACHMENT_WRITE);
barriers_arr[0].texture = color_tex;
barriers_arr[0].old_layout = PR_RHI_LAYOUT_UNDEFINED;
barriers_arr[0].new_layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
barriers_arr[0].src_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
barriers_arr[0].src_access_mask = PR_RHI_ACCESS_NONE;
barriers_arr[0].dst_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
barriers_arr[0].dst_access_mask = (PrRhiAccess)(PR_RHI_ACCESS_COLOR_ATTACHMENT_READ | PR_RHI_ACCESS_COLOR_ATTACHMENT_WRITE);
// Depth attachment
PrRhiTexture *depth_tex = prRhiGetSwapchainDepthTexture(app.swapchain);
img_barriers[1].texture = depth_tex;
img_barriers[1].old_layout = PR_RHI_LAYOUT_UNDEFINED;
img_barriers[1].new_layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
img_barriers[1].src_stage_mask = PR_RHI_PIPELINE_STAGE_LATE_FRAGMENT_TESTS;
img_barriers[1].src_access_mask = PR_RHI_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE;
img_barriers[1].dst_stage_mask = PR_RHI_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS;
img_barriers[1].dst_access_mask = PR_RHI_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE;
barriers_arr[1].texture = depth_tex;
barriers_arr[1].old_layout = PR_RHI_LAYOUT_UNDEFINED;
barriers_arr[1].new_layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
barriers_arr[1].src_stage_mask = PR_RHI_PIPELINE_STAGE_LATE_FRAGMENT_TESTS;
barriers_arr[1].src_access_mask = PR_RHI_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE;
barriers_arr[1].dst_stage_mask = PR_RHI_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS;
barriers_arr[1].dst_access_mask = PR_RHI_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE;
PrRhiImageMemoryBarrierArray barriers_arr = wpArray(PrRhiImageMemoryBarrier,
img_barriers[0], img_barriers[1]);
prRhiCmdPipelineBarrier(cb, barriers_arr, NULL);
}
// Rendering
{
PrRhiColorAttachment color_att = {};
color_att.texture = prRhiGetSwapchainTexture(app.swapchain, image_index);
color_att.layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
color_att.clear = true;
color_att.clear_color[0] = 0.0f;
color_att.clear_color[1] = 0.0f;
color_att.clear_color[2] = 0.0f;
color_att.clear_color[3] = 0.0f;
PrRhiColorAttachmentArray color_arr =
wpArrayWithCapacity(PrRhiColorAttachment, 1, WP_ARRAY_INIT_FILLED);
color_arr[0].texture = prRhiGetSwapchainTexture(app.swapchain, image_index);
color_arr[0].layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
color_arr[0].clear = true;
color_arr[0].clear_color[0] = 0.0f;
color_arr[0].clear_color[1] = 0.0f;
color_arr[0].clear_color[2] = 0.0f;
color_arr[0].clear_color[3] = 0.0f;
PrRhiDepthAttachment depth_att = {};
depth_att.texture = prRhiGetSwapchainDepthTexture(app.swapchain);
@@ -573,7 +575,6 @@ int main() {
depth_att.clear = true;
depth_att.clear_depth = 1.0f;
PrRhiColorAttachmentArray color_arr = wpArray(PrRhiColorAttachment, color_att);
prRhiCmdBeginRendering(cb, color_arr, &depth_att);
}
@@ -586,44 +587,39 @@ int main() {
prRhiCmdBindDescriptorSets(cb, PR_RHI_PIPELINE_BIND_POINT_GRAPHICS,
app.pipeline_layout, 0, sets);
PrRhiBuffer *vert_bufs[] = { app.vert_index_buf };
u64 vert_offsets[] = { 0 };
PrRhiBufferArray vert_buf_arr = wpArray(PrRhiBuffer *, vert_bufs[0]);
prRhiCmdBindVertexBuffers(cb, 0, vert_buf_arr, vert_offsets, 1);
prRhiCmdBindIndexBuffer(cb, app.vert_index_buf, app.vertex_buf_size,
PR_RHI_INDEX_TYPE_UINT16);
PrRhiBufferArray vert_buf_arr = wpArray(PrRhiBuffer *, app.vert_index_buf);
WpU64Array vert_offsets = wpArray(u64, 0);
prRhiCmdBindVertexBuffers(cb, 0, vert_buf_arr, vert_offsets);
prRhiCmdBindIndexBuffer(cb, app.vert_index_buf, app.vertex_buf_size, PR_RHI_INDEX_TYPE_UINT16);
// Push shader data buffer device address
u64 buf_addr = prRhiGetBufferDeviceAddress(app.device,
app.shader_data_bufs[app.frame_index]);
prRhiCmdPushConstants(cb, app.pipeline_layout, PR_RHI_SHADER_STAGE_VERTEX,
0, sizeof(u64), &buf_addr);
u64 buf_addr = prRhiGetBufferDeviceAddress(app.device, app.shader_data_bufs[app.frame_index]);
prRhiCmdPushConstants(cb, app.pipeline_layout, PR_RHI_SHADER_STAGE_VERTEX, 0, sizeof(u64), &buf_addr);
prRhiCmdDrawIndexed(cb, (u32)app.index_count, AppState::instance_count, 0, 0, 0);
prRhiCmdEndRendering(cb);
// Transition to present
{
PrRhiImageMemoryBarrier present_barrier = {};
present_barrier.texture = prRhiGetSwapchainTexture(app.swapchain, image_index);
present_barrier.old_layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
present_barrier.new_layout = PR_RHI_LAYOUT_PRESENT_SRC;
present_barrier.src_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
present_barrier.src_access_mask = PR_RHI_ACCESS_COLOR_ATTACHMENT_WRITE;
present_barrier.dst_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
present_barrier.dst_access_mask = PR_RHI_ACCESS_NONE;
PrRhiImageMemoryBarrierArray present_barriers =
wpArrayWithCapacity(PrRhiImageMemoryBarrier, 1, WP_ARRAY_INIT_FILLED);
present_barriers[0].texture = prRhiGetSwapchainTexture(app.swapchain, image_index);
present_barriers[0].old_layout = PR_RHI_LAYOUT_ATTACHMENT_OPTIMAL;
present_barriers[0].new_layout = PR_RHI_LAYOUT_PRESENT_SRC;
present_barriers[0].src_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
present_barriers[0].src_access_mask = PR_RHI_ACCESS_COLOR_ATTACHMENT_WRITE;
present_barriers[0].dst_stage_mask = PR_RHI_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT;
present_barriers[0].dst_access_mask = PR_RHI_ACCESS_NONE;
prRhiCmdPipelineBarrier(cb, wpArray(PrRhiImageMemoryBarrier, present_barrier), NULL);
prRhiCmdPipelineBarrier(cb, present_barriers, NULL);
}
prRhiEndCommandBuffer(cb);
// }}}
// {{{ Submit
prRhiQueueSubmit(app.device, cb,
app.image_acquired_semaphores[app.frame_index],
app.render_completed_semaphores[image_index],
app.fences[app.frame_index]);
prRhiQueueSubmit(app.device, cb, app.image_acquired_semaphores[app.frame_index],
app.render_completed_semaphores[image_index], app.fences[app.frame_index]);
// }}}
// {{{ Present
@@ -709,8 +705,7 @@ int main() {
prRhiDestroyTexture(app.device, app.textures[i].texture);
}
prRhiFreeCommandBuffers(app.device, app.cmd_pool, AppState::max_frames_in_flight,
app.cmd_buffers);
prRhiFreeCommandBuffers(app.device, app.cmd_pool, app.cmd_buffers);
prRhiDestroyCommandPool(app.device, app.cmd_pool);
for (u32 i = 0; i < swapchain_image_count; ++i) {
+2 -4
View File
@@ -7,11 +7,9 @@
PrRhiContext _G_RHI_CONTEXT;
void prRhiInit(void) {
_G_RHI_CONTEXT.main = wpMemArenaAllocatorInit(MiB(64));
_G_RHI_CONTEXT.scratch = wpMemArenaAllocatorInit(MiB(32));
_G_RHI_CONTEXT.allocator = wpMemArenaAllocatorInit(MiB(64));
}
void prRhiDestroy(void) {
wpMemArenaAllocatorDestroy(&_G_RHI_CONTEXT.scratch);
wpMemArenaAllocatorDestroy(&_G_RHI_CONTEXT.main);
wpMemArenaAllocatorDestroy(&_G_RHI_CONTEXT.allocator);
}
+10 -8
View File
@@ -24,6 +24,7 @@
#define PR_RHI_H
#include "pr_rhi_types.h"
#include <SDL3/SDL_video.h>
#ifdef __cplusplus
extern "C" {
@@ -48,14 +49,13 @@ void prRhiDestroyInstance(PrRhiInstance *inst);
PrRhiPhysicalDeviceArray prRhiGetPhysicalDevices(PrRhiInstance *inst);
void prRhiGetPhysicalDeviceName(PrRhiPhysicalDevice *pdev, WpStr8 *out);
void prRhiGetPhysicalDeviceDriverInfo(PrRhiPhysicalDevice *pdev, WpStr8 *out);
void prRhiGetPhysicalDeviceProperties(PrRhiPhysicalDevice *pdev,
PrRhiPhysicalDeviceProperties *out);
PrRhiPhysicalDeviceProperties prRhiGetPhysicalDeviceProperties(PrRhiPhysicalDevice *pdev);
// ======================================================================
// Surface (platform-specific)
// ======================================================================
PrRhiSurface *prRhiCreateSurfaceFromWindow(PrRhiInstance *inst, void *window_handle);
PrRhiSurface *prRhiCreateSurfaceFromWindow(PrRhiInstance *inst, SDL_Window *window);
void prRhiDestroySurface(PrRhiInstance *inst, PrRhiSurface *surface);
PrRhiSurfaceCapabilities prRhiGetSurfaceCapabilities(PrRhiPhysicalDevice *pdev,
PrRhiSurface *surface);
@@ -77,6 +77,7 @@ u32 prRhiGetQueueFamilyIndex(PrRhiDevice *device);
PrRhiSwapchain *prRhiCreateSwapchain(PrRhiDevice *device, PrRhiSwapchainDesc desc);
void prRhiDestroySwapchain(PrRhiDevice *device, PrRhiSwapchain *swapchain);
u32 prRhiGetSwapchainImageCount(PrRhiSwapchain *swapchain);
PrRhiSwapchainResult prRhiAcquireNextImage(PrRhiDevice *device, PrRhiSwapchain *swapchain,
PrRhiSemaphore *signal_semaphore, u32 *out_image_index);
@@ -168,7 +169,7 @@ void prRhiDestroyDescriptorPool(PrRhiDevice *device,
PrRhiDescriptorSet *prRhiAllocateDescriptorSet(PrRhiDevice *device, PrRhiDescriptorPool *pool,
PrRhiDescriptorSetLayout *layout,
u32 variable_count);
WpU32Array variable_descriptor_counts);
void prRhiFreeDescriptorSet(PrRhiDevice *device, PrRhiDescriptorPool *pool,
PrRhiDescriptorSet *set);
void prRhiUpdateDescriptorSet(PrRhiDevice *device, PrRhiWriteDescriptorSetArray writes);
@@ -191,13 +192,14 @@ void prRhiDestroySemaphore(PrRhiDevice *device, PrRhiSemaphore *semap
// Command pools and command buffers
// ======================================================================
PrRhiCommandPool *prRhiCreateCommandPool(PrRhiDevice *device, PrRhiCommandPoolDesc desc);
PrRhiCommandPool *prRhiCreateCommandPool(PrRhiDevice *device);
void prRhiDestroyCommandPool(PrRhiDevice *device, PrRhiCommandPool *pool);
PrRhiCommandBufferArray prRhiAllocateCommandBuffers(PrRhiDevice *device, PrRhiCommandPool *pool,
u32 count);
void prRhiFreeCommandBuffers(PrRhiDevice *device, PrRhiCommandPool *pool,
u32 count, PrRhiCommandBufferArray buffers);
PrRhiCommandBufferArray buffers);
// ======================================================================
// Command buffer recording
@@ -238,8 +240,8 @@ void prRhiCmdPushConstants(PrRhiCommandBuffer *cb, PrRhiPipelineLayout *layout,
// --- Vertex / index buffers ---
void prRhiCmdBindVertexBuffers(PrRhiCommandBuffer *cb, u32 first_binding,
PrRhiBufferArray buffers, const u64 *offsets, u32 count);
void prRhiCmdBindVertexBuffers(PrRhiCommandBuffer *cb, u32 first_binding, PrRhiBufferArray buffers,
WpU64Array offsets);
void prRhiCmdBindIndexBuffer(PrRhiCommandBuffer *cb, PrRhiBuffer *buffer, u64 offset,
PrRhiIndexType index_type);
+43 -6
View File
@@ -357,9 +357,40 @@ typedef struct PrRhiPipelineLayoutDesc {
PrRhiPushConstantRangeArray push_constant_ranges;
} PrRhiPipelineLayoutDesc;
typedef enum PrRhiPolygonMode {
PR_RHI_POLYGON_MODE_FILL = 0,
PR_RHI_POLYGON_MODE_LINE = 1,
PR_RHI_POLYGON_MODE_POINT = 2,
} PrRhiPolygonMode;
typedef enum PrRhiCullMode {
PR_RHI_CULL_MODE_NONE = 0,
PR_RHI_CULL_MODE_FRONT = 0x00000001,
PR_RHI_CULL_MODE_BACK = 0x00000002,
PR_RHI_CULL_MODE_FRONT_AND_BACK = 0x00000003,
} PrRhiCullMode;
typedef enum PrRhiFrontFace {
PR_RHI_FRONT_FACE_COUNTER_CLOCKWISE = 0,
PR_RHI_FRONT_FACE_CLOCKWISE = 1,
} PrRhiFrontFace;
typedef enum PrRhiMultisampleCount {
PR_RHI_SAMPLE_COUNT_1 = 0x00000001,
PR_RHI_SAMPLE_COUNT_2 = 0x00000002,
PR_RHI_SAMPLE_COUNT_4 = 0x00000004,
PR_RHI_SAMPLE_COUNT_8 = 0x00000008,
PR_RHI_SAMPLE_COUNT_16 = 0x00000010,
PR_RHI_SAMPLE_COUNT_32 = 0x00000020,
PR_RHI_SAMPLE_COUNT_64 = 0x00000040,
} PrRhiMultisampleCount;
typedef struct PrRhiGraphicsPipelineDesc {
PrRhiShader *vertex_shader;
const char *vertex_shader_entry_point;
PrRhiShader *fragment_shader;
const char *fragment_shader_entry_point;
PrRhiVertexInputBindingArray vertex_bindings;
PrRhiVertexAttributeArray vertex_attributes;
@@ -378,11 +409,22 @@ typedef struct PrRhiGraphicsPipelineDesc {
b8 dynamic_viewport;
b8 dynamic_scissor;
PrRhiPolygonMode polygon_mode;
PrRhiCullMode cull_mode;
PrRhiFrontFace front_face;
f32 line_width;
PrRhiMultisampleCount multisample_count;
PrRhiPipelineLayout *layout;
} PrRhiGraphicsPipelineDesc;
typedef struct PrRhiComputePipelineDesc {
PrRhiShader *shader;
const char *shader_entry_point;
PrRhiPipelineLayout *layout;
} PrRhiComputePipelineDesc;
@@ -408,10 +450,6 @@ typedef struct PrRhiFenceDesc {
b8 signaled;
} PrRhiFenceDesc;
typedef struct PrRhiCommandPoolDesc {
u32 queue_family_index;
} PrRhiCommandPoolDesc;
typedef struct PrRhiSwapchainDesc {
PrRhiSurface *surface;
u32 width;
@@ -443,8 +481,7 @@ typedef u64 PrRhiDeviceAddress;
// ============================================================================
typedef struct PrRhiContext {
WpAllocator main;
WpAllocator scratch;
WpAllocator allocator;
} PrRhiContext;
// --- Command buffer types ---
File diff suppressed because it is too large Load Diff
+10 -10
View File
@@ -8,6 +8,7 @@
#define PR_RHI_VK_H
#include "../pr_rhi_types.h"
#include <SDL3/SDL_video.h>
#include <volk/volk.h>
#include <vk_mem_alloc.h>
@@ -128,10 +129,9 @@ void prRhiDestroyInstanceVk(PrRhiInstance *inst);
PrRhiPhysicalDeviceArray prRhiGetPhysicalDevicesVk(PrRhiInstance *inst);
void prRhiGetPhysicalDeviceNameVk(PrRhiPhysicalDevice *pdev, WpStr8 *out);
void prRhiGetPhysicalDeviceDriverInfoVk(PrRhiPhysicalDevice *pdev, WpStr8 *out);
void prRhiGetPhysicalDevicePropertiesVk(PrRhiPhysicalDevice *pdev,
PrRhiPhysicalDeviceProperties *out);
PrRhiPhysicalDeviceProperties prRhiGetPhysicalDevicePropertiesVk(PrRhiPhysicalDevice *pdev);
PrRhiSurface *prRhiCreateSurfaceFromWindowVk(PrRhiInstance *inst, void *window_handle);
PrRhiSurface *prRhiCreateSurfaceFromWindowVk(PrRhiInstance *inst, SDL_Window *window);
void prRhiDestroySurfaceVk(PrRhiInstance *inst, PrRhiSurface *surface);
PrRhiSurfaceCapabilities prRhiGetSurfaceCapabilitiesVk(PrRhiPhysicalDevice *pdev,
@@ -145,6 +145,7 @@ u32 prRhiGetQueueFamilyIndexVk(PrRhiDevice *device);
PrRhiSwapchain *prRhiCreateSwapchainVk(PrRhiDevice *device, PrRhiSwapchainDesc desc);
void prRhiDestroySwapchainVk(PrRhiDevice *device, PrRhiSwapchain *swapchain);
u32 prRhiGetSwapchainImageCountVk(PrRhiSwapchain *swapchain);
PrRhiSwapchainResult prRhiAcquireNextImageVk(PrRhiDevice *device, PrRhiSwapchain *swapchain,
PrRhiSemaphore *signal_semaphore, u32 *out_image_index);
PrRhiSwapchainResult prRhiPresentVk(PrRhiDevice *device, PrRhiSwapchain *swapchain,
@@ -196,7 +197,7 @@ void prRhiDestroyDescriptorPoolVk(PrRhiDevice *device,
PrRhiDescriptorSet *prRhiAllocateDescriptorSetVk(PrRhiDevice *device,
PrRhiDescriptorPool *pool,
PrRhiDescriptorSetLayout *layout,
u32 variable_count);
WpU32Array variable_descriptor_counts);
void prRhiFreeDescriptorSetVk(PrRhiDevice *device, PrRhiDescriptorPool *pool,
PrRhiDescriptorSet *set);
void prRhiUpdateDescriptorSetVk(PrRhiDevice *device, PrRhiWriteDescriptorSetArray writes);
@@ -211,13 +212,12 @@ void prRhiResetFencesVk(PrRhiDevice *device, PrRhiFenceArray fences, u32 count);
PrRhiSemaphore *prRhiCreateSemaphoreVk(PrRhiDevice *device);
void prRhiDestroySemaphoreVk(PrRhiDevice *device, PrRhiSemaphore *semaphore);
PrRhiCommandPool *prRhiCreateCommandPoolVk(PrRhiDevice *device,
PrRhiCommandPoolDesc desc);
PrRhiCommandPool *prRhiCreateCommandPoolVk(PrRhiDevice *device);
void prRhiDestroyCommandPoolVk(PrRhiDevice *device, PrRhiCommandPool *pool);
PrRhiCommandBufferArray prRhiAllocateCommandBuffersVk(PrRhiDevice *device, PrRhiCommandPool *pool,
u32 count);
void prRhiFreeCommandBuffersVk(PrRhiDevice *device, PrRhiCommandPool *pool,
u32 count, PrRhiCommandBufferArray buffers);
void prRhiFreeCommandBuffersVk(PrRhiDevice *device, PrRhiCommandPool *pool, PrRhiCommandBufferArray buffers);
void prRhiBeginCommandBufferVk(PrRhiCommandBuffer *cb);
void prRhiEndCommandBufferVk(PrRhiCommandBuffer *cb);
@@ -243,8 +243,8 @@ void prRhiCmdBindDescriptorSetsVk(PrRhiCommandBuffer *cb, PrRhiPipelineBindPoint
void prRhiCmdPushConstantsVk(PrRhiCommandBuffer *cb, PrRhiPipelineLayout *layout,
PrRhiShaderStage stage_flags, u32 offset, u32 size,
const void *data);
void prRhiCmdBindVertexBuffersVk(PrRhiCommandBuffer *cb, u32 first_binding,
PrRhiBufferArray buffers, const u64 *offsets, u32 count);
void prRhiCmdBindVertexBuffersVk(PrRhiCommandBuffer *cb, u32 first_binding, PrRhiBufferArray buffers,
WpU64Array offsets);
void prRhiCmdBindIndexBufferVk(PrRhiCommandBuffer *cb, PrRhiBuffer *buffer, u64 offset,
PrRhiIndexType index_type);
+1
View File
@@ -20,6 +20,7 @@
#define prRhiGetQueueFamilyIndex prRhiGetQueueFamilyIndexVk
#define prRhiCreateSwapchain prRhiCreateSwapchainVk
#define prRhiDestroySwapchain prRhiDestroySwapchainVk
#define prRhiGetSwapchainImageCount prRhiGetSwapchainImageCountVk
#define prRhiAcquireNextImage prRhiAcquireNextImageVk
#define prRhiPresent prRhiPresentVk
#define prRhiRecreateSwapchain prRhiRecreateSwapchainVk