Skip to main content

forge-debug-lines

Add immediate-mode debug line drawing to an SDL GPU project. Render colored lines, grids, axes, circles, and wireframe boxes with world-space (depth-tested) and overlay (always-on-top) modes. Use when someone needs diagnostic visualization, debug drawing, or wireframe rendering in SDL3 GPU.

インストールへ移動

ソース情報

リポジトリ
Nebulavenus/forge-gpu
ソースの最終更新活動
2026年3月1日 21:31
検出された SKILL.md の言語
英語
スター
38
フォーク
7

インストール方法

デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。

ソースファイルを確認

インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。

SKILL.md を表示中

SKILL.md
ソースの指示 · 読み取り専用プレビュー
name
forge-debug-lines
description
Add immediate-mode debug line drawing to an SDL GPU project. Render colored lines, grids, axes, circles, and wireframe boxes with world-space (depth-tested) and overlay (always-on-top) modes. Use when someone needs diagnostic visualization, debug drawing, or wireframe rendering in SDL3 GPU.
# Debug Lines — Immediate-Mode Debug Drawing This skill adds a lightweight debug drawing system to any SDL GPU project. It uses `SDL_GPU_PRIMITIVETYPE_LINELIST` with a dynamic vertex buffer updated every frame via the immediate-mode pattern: reset, accumulate, upload, draw. ## When to use - Visualizing positions, bounds, directions, or other spatial data - Drawing coordinate-axis gizmos (RGB = XYZ) - Rendering wireframe bounding boxes for physics or collision debugging - Drawing circles to show radii, influence zones, or trigger areas - Adding an always-visible overlay for diagnostic indicators - Any per-frame debug visualization that changes based on game state ## Key API calls (ordered) 1. `SDL_CreateGPUGraphicsPipeline` — create two pipelines (depth ON / depth OFF) with `SDL_GPU_PRIMITIVETYPE_LINELIST` and `SDL_GPU_CULLMODE_NONE` 2. `SDL_CreateGPUBuffer` — pre-allocate GPU vertex buffer for max vertices 3. `SDL_CreateGPUTransferBuffer` — pre-allocate upload staging buffer 4. `SDL_MapGPUTransferBuffer` / `SDL_UnmapGPUTransferBuffer` — map, copy, unmap each frame 5. `SDL_UploadToGPUBuffer` — copy pass to transfer data to GPU 6. `SDL_PushGPUVertexUniformData` — push VP matrix 7. `SDL_DrawGPUPrimitives` — draw world lines, then overlay lines with first_vertex offset ## Common mistakes - **Forgetting to reset vertex counts each frame.** The immediate-mode pattern requires `world_count = 0; overlay_count = 0;` at the start of every frame, otherwise vertices accumulate and the buffer overflows. - **Using TRIANGLELIST primitive type.** Debug lines use `LINELIST` — every pair of vertices is a separate line segment. There are no faces, no indices. - **Enabling back-face culling.** Lines have no face orientation. Always use `SDL_GPU_CULLMODE_NONE` for line pipelines. - **Creating separate GPU buffers for world and overlay.** Both can share one buffer — use `first_vertex` offset in the draw call to select the region. - **Forgetting the copy pass before drawing.** The transfer buffer upload requires `SDL_BeginGPUCopyPass` / `SDL_UploadToGPUBuffer` / `SDL_EndGPUCopyPass` / `SDL_SubmitGPUCommandBuffer` before the render pass. ## Code template ### Vertex format ```c typedef struct DebugVertex { vec3 position; /* 12 bytes — world-space position */ vec4 color; /* 16 bytes — RGBA color */ } DebugVertex; /* 28 bytes total */ #define DEBUG_VERTEX_PITCH 28 #define MAX_DEBUG_VERTICES 65536 ``` ### Pipeline creation ```c /* Vertex layout: position (float3) + color (float4) */ SDL_GPUVertexAttribute attrs[2]; SDL_zero(attrs); attrs[0].location = 0; attrs[0].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT3; attrs[0].offset = offsetof(DebugVertex, position); attrs[1].location = 1; attrs[1].format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT4; attrs[1].offset = offsetof(DebugVertex, color); SDL_GPUGraphicsPipelineCreateInfo pipe; SDL_zero(pipe); pipe.primitive_type = SDL_GPU_PRIMITIVETYPE_LINELIST; pipe.rasterizer_state.cull_mode = SDL_GPU_CULLMODE_NONE; /* World-space pipeline: depth test ON */ pipe.depth_stencil_state.enable_depth_test = true; pipe.depth_stencil_state.enable_depth_write = true; pipe.depth_stencil_state.compare_op = SDL_GPU_COMPAREOP_LESS_OR_EQUAL; line_pipeline = SDL_CreateGPUGraphicsPipeline(device, &pipe); /* Overlay pipeline: depth test OFF */ pipe.depth_stencil_state.enable_depth_test = false; pipe.depth_stencil_state.enable_depth_write = false; overlay_pipeline = SDL_CreateGPUGraphicsPipeline(device, &pipe); ``` ### Per-frame upload and draw ```c /* 1. Reset counts */ state->world_count = 0; state->overlay_count = 0; /* 2. Accumulate debug shapes */ debug_grid(state, 20, 1.0f, gray); debug_axes(state, origin, 2.0f, true); /* overlay */ debug_box_wireframe(state, min, max, orange, false); /* 3. Upload (map → copy → unmap → copy pass) */ void *mapped = SDL_MapGPUTransferBuffer(device, transfer_buf, true); SDL_memcpy(mapped, vertices, total * sizeof(DebugVertex)); SDL_UnmapGPUTransferBuffer(device, transfer_buf); /* ... copy pass to GPU buffer ... */ /* 4. Draw */ if (world_count > 0) { SDL_BindGPUGraphicsPipeline(pass, line_pipeline); SDL_DrawGPUPrimitives(pass, world_count, 1, 0, 0); } if (overlay_count > 0) { SDL_BindGPUGraphicsPipeline(pass, overlay_pipeline); SDL_DrawGPUPrimitives(pass, overlay_count, 1, world_count, 0); } ``` ### Debug shape helpers ```c /* Single line segment (2 vertices) */ static void debug_line(state, vec3 start, vec3 end, vec4 color, bool overlay); /* Ground grid on XZ plane */ static void debug_grid(state, int half_size, float spacing, vec4 color); /* Coordinate axes: red=X, green=Y, blue=Z (6 vertices) */ static void debug_axes(state, vec3 origin, float size, bool overlay); /* Circle from line segments (2 × segments vertices) */ static void debug_circle(state, vec3 center, float radius, vec3 normal, vec4 color, int segments, bool overlay); /* Wireframe AABB (24 vertices = 12 edges) */ static void debug_box_wireframe(state, vec3 min_pt, vec3 max_pt, vec4 color, bool overlay); ``` ### Shader (HLSL) ```hlsl /* Vertex shader — transform position, pass through color */ cbuffer DebugUniforms : register(b0, space1) { column_major float4x4 view_projection; }; struct VSInput { float3 position : TEXCOORD0; float4 color : TEXCOORD1; }; struct VSOutput { float4 clip_pos : SV_Position; float4 color : TEXCOORD0; }; VSOutput main(VSInput input) { VSOutput output; output.clip_pos = mul(view_projection, float4(input.position, 1.0)); output.color = input.color; return output; } /* Fragment shader — pass-through color */ float4 main(PSInput input) : SV_Target { return input.color; } ``` ## Lesson reference See [Lesson 19 — Debug Lines](../../../lessons/gpu/19-debug-lines/) for the full implementation with detailed comments explaining every step.
GitHubで見る