Skip to main content

forge-vertex-pulling

Add vertex pulling (programmable vertex fetch) to an SDL GPU project. Replace fixed-function vertex input with storage buffer reads in the vertex shader using SV_VertexID and StructuredBuffer.

Zur Installation springen

Quellinformationen

Repository
Nebulavenus/forge-gpu
Letzte Quellaktivität
10. März 2026 um 06:38
Erkannte Sprache von SKILL.md
Englisch
Sterne
38
Forks
7

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
forge-vertex-pulling
description
Add vertex pulling (programmable vertex fetch) to an SDL GPU project. Replace fixed-function vertex input with storage buffer reads in the vertex shader using SV_VertexID and StructuredBuffer.
triggers
["vertex pulling","storage buffer vertex","programmable vertex fetch","bindless vertex","SV_VertexID fetch","remove vertex attributes","flexible vertex format"]
# Vertex Pulling with SDL3 GPU Replace the fixed-function vertex input assembler with manual storage buffer reads in the vertex shader. The pipeline declares zero vertex attributes and the shader fetches vertex data from a `StructuredBuffer` using `SV_VertexID`. Based on [GPU Lesson 33 — Vertex Pulling](../../../lessons/gpu/33-vertex-pulling/). ## When to use - Meshes with different vertex layouts sharing one pipeline - Compute shaders writing vertex data for the vertex shader to read - Compressed or packed vertex formats decoded in the shader - GPU-driven rendering with indirect draw calls - Reducing pipeline state complexity and pipeline object count ## HLSL pattern ### Vertex shader (pulled) ```hlsl struct PulledVertex { float3 position; float3 normal; float2 uv; }; /* Vertex storage buffer: register(t0, space0) for DXIL vertex shaders. * Slot index follows sampled textures and storage textures (if any). */ StructuredBuffer<PulledVertex> vertex_buffer : register(t0, space0); cbuffer SceneUniforms : register(b0, space1) { column_major float4x4 mvp; column_major float4x4 model; }; struct VSOutput { float4 clip_pos : SV_Position; float3 world_pos : TEXCOORD0; float3 world_nrm : TEXCOORD1; float2 uv : TEXCOORD2; }; VSOutput main(uint vertex_id : SV_VertexID) { VSOutput output; PulledVertex v = vertex_buffer[vertex_id]; float4 world = mul(model, float4(v.position, 1.0)); output.clip_pos = mul(mvp, float4(v.position, 1.0)); output.world_pos = world.xyz; output.world_nrm = normalize(mul((float3x3)model, v.normal)); output.uv = v.uv; return output; } ``` ### Fragment shader No changes needed — fragment shaders work identically regardless of whether vertex data came from the input assembler or a storage buffer. ## C-side pattern ### 1. Define the vertex struct (must match HLSL) ```c typedef struct PulledVertex { vec3 position; /* 12 bytes */ vec3 normal; /* 12 bytes */ vec2 uv; /* 8 bytes */ } PulledVertex; /* 32 bytes */ ``` ### 2. Upload as storage buffer (not vertex buffer) ```c /* The only CPU-side difference: buffer usage flag */ SDL_GPUBuffer *storage_buf = upload_gpu_buffer( device, SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ, /* NOT VERTEX */ vertex_data, vertex_count * sizeof(PulledVertex)); ``` ### 3. Create shader with storage buffer count ```c /* Vertex shader: declare 1 storage buffer */ SDL_GPUShaderCreateInfo info; SDL_zero(info); info.stage = SDL_GPU_SHADERSTAGE_VERTEX; info.num_samplers = 0; info.num_storage_buffers = 1; /* <-- vertex data storage buffer */ info.num_uniform_buffers = 1; /* ... format, code, entrypoint ... */ ``` ### 4. Create pipeline with empty vertex input ```c /* No vertex buffer descriptions, no vertex attributes */ SDL_GPUVertexInputState vis; SDL_zero(vis); /* vis.num_vertex_buffers = 0; already zero */ /* vis.num_vertex_attributes = 0; already zero */ SDL_GPUGraphicsPipelineCreateInfo pi; SDL_zero(pi); pi.vertex_input_state = vis; /* ... rest of pipeline setup ... */ ``` ### 5. Bind and draw ```c /* Bind storage buffer instead of vertex buffer */ SDL_GPUBuffer *bufs[1] = { storage_buf }; SDL_BindGPUVertexStorageBuffers(pass, 0, bufs, 1); /* Index buffer binding is unchanged */ SDL_GPUBufferBinding ib = { index_buffer, 0 }; SDL_BindGPUIndexBuffer(pass, &ib, index_type); SDL_DrawGPUIndexedPrimitives(pass, index_count, 1, 0, 0, 0); ``` ## SDL GPU register mapping For **DXIL** vertex shaders, resources are bound in this order at `space0`: 1. Sampled textures (`t0..tN`) 2. Storage textures (`tN+1..`) 3. Storage buffers (`tN+M+1..`) With zero sampled/storage textures, the first storage buffer is `t0, space0`. For **SPIR-V** vertex shaders, storage buffers go in descriptor set 0 after sampled and storage textures. ## Combining with other techniques - **Compute → Vertex**: Use `SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE` so a compute shader can write the buffer and the vertex shader can read it. - **Indirect drawing** (Lesson 38): Vertex pulling pairs naturally with indirect draws for fully GPU-driven rendering. - **Multiple vertex formats**: One pipeline can render meshes with different vertex layouts by defining multiple `StructuredBuffer` structs or using a `ByteAddressBuffer` with manual offset calculations. ## Common mistakes | Mistake | Fix | |---|---| | Using `SDL_GPU_BUFFERUSAGE_VERTEX` | Use `SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ` | | Forgetting `num_storage_buffers = 1` on shader | Set it in `SDL_GPUShaderCreateInfo` | | Calling `SDL_BindGPUVertexBuffers` | Use `SDL_BindGPUVertexStorageBuffers` | | Mismatched C/HLSL struct layout | Ensure identical field order and sizes | | Leaving vertex attributes in pipeline | Set `num_vertex_buffers = 0`, `num_vertex_attributes = 0` |
Auf GitHub ansehen