Skip to main content

forge-mipmaps

Create mipmapped textures with auto-generated mip levels, configure sampler mipmap modes (trilinear, bilinear, none), and generate procedural textures. Use when someone needs mipmaps, trilinear filtering, LOD control, or procedural texture generation in SDL3 GPU.

Aller à l'installation

Informations de source

Dépôt
Nebulavenus/forge-gpu
Dernière activité de la source
1 mars 2026 à 21:31
Langue détectée de SKILL.md
anglais
Étoiles
38
Forks
7

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
forge-mipmaps
description
Create mipmapped textures with auto-generated mip levels, configure sampler mipmap modes (trilinear, bilinear, none), and generate procedural textures. Use when someone needs mipmaps, trilinear filtering, LOD control, or procedural texture generation in SDL3 GPU.
# Mipmaps — Mip Chain Generation, Trilinear Filtering, and LOD Control This skill teaches how to create textures with multiple mip levels, auto-generate mipmaps with `SDL_GenerateMipmapsForGPUTexture`, and configure sampler mipmap modes for different quality/performance trade-offs. It builds on the `textures-and-samplers` skill (texture creation, sampler binding). ## When to use - Creating textures that look correct at any distance (no aliasing) - Setting up trilinear filtering for smooth mip level transitions - Generating procedural textures without loading image files - Controlling LOD behavior with `min_lod`, `max_lod`, and `mip_lod_bias` - Comparing sampler mipmap modes (trilinear vs bilinear+nearest vs none) ## Key API calls (ordered) 1. `SDL_CreateGPUTexture` — create texture with `num_levels > 1` and `SAMPLER | COLOR_TARGET` usage 2. `SDL_CreateGPUTransferBuffer` — staging buffer for CPU → GPU upload 3. `SDL_MapGPUTransferBuffer` / `SDL_UnmapGPUTransferBuffer` — write pixel data 4. `SDL_BeginGPUCopyPass` / `SDL_UploadToGPUTexture` / `SDL_EndGPUCopyPass` — upload base mip level 5. `SDL_GenerateMipmapsForGPUTexture` — auto-generate all smaller mip levels (outside any pass) 6. `SDL_SubmitGPUCommandBuffer` — submit the upload + mipgen work 7. `SDL_CreateGPUSampler` — configure mipmap filtering mode (trilinear, bilinear+nearest, none) 8. `SDL_BindGPUFragmentSamplers` — bind texture + sampler for drawing ## Code template Minimal end-to-end sequence: create a mipmapped texture, generate mips, set up a trilinear sampler, and bind for rendering. ```c #include "math/forge_math.h" #define TEX_SIZE 256 #define BYTES_PER_PX 4 #define MAX_LOD 1000.0f /* allow all mip levels */ /* ── 1. Create texture with mip levels ──────────────────────────────── */ int num_levels = (int)forge_log2f((float)TEX_SIZE) + 1; SDL_GPUTextureCreateInfo tex_info = { 0 }; tex_info.type = SDL_GPU_TEXTURETYPE_2D; tex_info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB; tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; tex_info.width = TEX_SIZE; tex_info.height = TEX_SIZE; tex_info.layer_count_or_depth = 1; tex_info.num_levels = num_levels; SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &tex_info); /* ── 2. Generate pixel data (procedural or loaded from file) ────────── */ Uint32 total_bytes = TEX_SIZE * TEX_SIZE * BYTES_PER_PX; Uint8 *pixels = (Uint8 *)SDL_malloc(total_bytes); /* ... fill pixels ... */ /* ── 3. Upload base level via transfer buffer ───────────────────────── */ SDL_GPUTransferBufferCreateInfo xfer_info = { 0 }; xfer_info.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; xfer_info.size = total_bytes; SDL_GPUTransferBuffer *xfer = SDL_CreateGPUTransferBuffer(device, &xfer_info); void *mapped = SDL_MapGPUTransferBuffer(device, xfer, false); SDL_memcpy(mapped, pixels, total_bytes); SDL_UnmapGPUTransferBuffer(device, xfer); SDL_free(pixels); SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(device); SDL_GPUCopyPass *copy = SDL_BeginGPUCopyPass(cmd); SDL_GPUTextureTransferInfo src = { 0 }; src.transfer_buffer = xfer; src.pixels_per_row = TEX_SIZE; src.rows_per_layer = TEX_SIZE; SDL_GPUTextureRegion dst = { 0 }; dst.texture = texture; dst.mip_level = 0; dst.w = TEX_SIZE; dst.h = TEX_SIZE; dst.d = 1; SDL_UploadToGPUTexture(copy, &src, &dst, false); SDL_EndGPUCopyPass(copy); /* ── 4. Generate mipmaps — MUST be outside any pass ─────────────────── */ SDL_GenerateMipmapsForGPUTexture(cmd, texture); SDL_SubmitGPUCommandBuffer(cmd); SDL_ReleaseGPUTransferBuffer(device, xfer); /* ── 5. Create trilinear sampler ────────────────────────────────────── */ SDL_GPUSamplerCreateInfo samp_info = { 0 }; samp_info.min_filter = SDL_GPU_FILTER_LINEAR; samp_info.mag_filter = SDL_GPU_FILTER_LINEAR; samp_info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; samp_info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; samp_info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; samp_info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; samp_info.min_lod = 0.0f; samp_info.max_lod = MAX_LOD; SDL_GPUSampler *sampler = SDL_CreateGPUSampler(device, &samp_info); /* ── 6. Bind for rendering (inside a render pass) ───────────────────── */ SDL_GPUTextureSamplerBinding binding = { 0 }; binding.texture = texture; binding.sampler = sampler; SDL_BindGPUFragmentSamplers(pass, 0, &binding, 1); SDL_DrawGPUIndexedPrimitives(pass, index_count, 1, 0, 0, 0); ``` ## Creating a mipmapped texture ### Key differences from a non-mipmapped texture ```c SDL_GPUTextureCreateInfo tex_info = { 0 }; tex_info.type = SDL_GPU_TEXTURETYPE_2D; tex_info.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM_SRGB; tex_info.width = 256; tex_info.height = 256; tex_info.layer_count_or_depth = 1; /* ── Two critical differences from non-mipmapped textures ──────────── */ /* 1. Usage MUST include COLOR_TARGET for mipmap generation. * The GPU generates mips by rendering into each level. */ tex_info.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_COLOR_TARGET; /* 2. Multiple mip levels instead of 1. * log2(256) + 1 = 9 levels: 256, 128, 64, 32, 16, 8, 4, 2, 1 */ tex_info.num_levels = (int)forge_log2f((float)tex_info.width) + 1; SDL_GPUTexture *texture = SDL_CreateGPUTexture(device, &tex_info); ``` ### Mip level count formula ```c #include "math/forge_math.h" int num_levels = (int)forge_log2f((float)max_dimension) + 1; /* Examples: * 256×256 → 9 levels * 512×512 → 10 levels * 1024×1024 → 11 levels * 2048×2048 → 12 levels */ ``` For non-square textures, use the larger dimension: ```c int max_dim = (width > height) ? width : height; int num_levels = (int)forge_log2f((float)max_dim) + 1; ``` ## Generating mipmaps After uploading the base level (mip 0), call `SDL_GenerateMipmapsForGPUTexture` **outside any render or copy pass**: ```c SDL_GPUCommandBuffer *cmd = SDL_AcquireGPUCommandBuffer(device); /* Upload base level in a copy pass */ SDL_GPUCopyPass *copy = SDL_BeginGPUCopyPass(cmd); SDL_UploadToGPUTexture(copy, &src, &dst, false); SDL_EndGPUCopyPass(copy); /* Generate all other mip levels — MUST be outside any pass */ SDL_GenerateMipmapsForGPUTexture(cmd, texture); SDL_SubmitGPUCommandBuffer(cmd); ``` The GPU internally renders into each mip level, downsampling from the level above using a blit operation. ## Sampler mipmap modes ### Trilinear (highest quality) Blends between two adjacent mip levels for smooth transitions: ```c SDL_GPUSamplerCreateInfo info = { 0 }; info.min_filter = SDL_GPU_FILTER_LINEAR; info.mag_filter = SDL_GPU_FILTER_LINEAR; info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_LINEAR; /* blend between mips */ info.address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; info.address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; info.address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_REPEAT; info.min_lod = 0.0f; info.max_lod = 1000.0f; /* allow all mip levels */ ``` ### Bilinear + nearest mip (medium quality) Smooth within a level, but "pops" when switching between levels: ```c info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; /* snap to closest mip */ ``` ### No mipmaps (force base level) Causes aliasing at distance — useful for comparison or when you only have one mip level: ```c info.min_filter = SDL_GPU_FILTER_NEAREST; info.mag_filter = SDL_GPU_FILTER_NEAREST; info.mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST; info.max_lod = 0.0f; /* clamp to level 0 only */ ``` ## LOD control | Sampler field | Purpose | Default | |---------------|---------|---------| | `min_lod` | Minimum mip level (0 = base) | 0.0 | | `max_lod` | Maximum mip level | 1000.0 (all) | | `mip_lod_bias` | Added to computed LOD (positive = blurrier) | 0.0 | ```c /* Example: restrict to first 3 mip levels */ info.min_lod = 0.0f; info.max_lod = 2.0f; /* levels 0, 1, 2 only */ /* Example: bias toward blurrier mips (reduces shimmering) */ info.mip_lod_bias = 0.5f; ``` ## Procedural texture generation Generate textures in code instead of loading from files: ```c int size = 256; int tiles = 16; int tile_size = size / tiles; Uint8 *pixels = SDL_malloc(size * size * 4); for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { int tile_x = x / tile_size; int tile_y = y / tile_size; Uint8 color = ((tile_x + tile_y) % 2 == 0) ? 255 : 0; int idx = (y * size + x) * 4; pixels[idx + 0] = color; /* R */ pixels[idx + 1] = color; /* G */ pixels[idx + 2] = color; /* B */ pixels[idx + 3] = 255; /* A */ } } /* Upload pixels via transfer buffer, then SDL_GenerateMipmapsForGPUTexture */ ``` No file I/O, no CMake copy steps, no pixel format conversion. ## UV tiling To make a texture repeat, multiply UVs by a scale factor in the vertex shader: ```hlsl cbuffer Uniforms : register(b0, space1) { float time; float aspect; float uv_scale; float _pad; /* pad to 16-byte alignment */ }; output.uv = input.uv * uv_scale; ``` With `REPEAT` address mode, UVs beyond 0–1 wrap around automatically. ## Uniform struct alignment When adding fields to the uniform buffer, pad to 16-byte alignment: ```c typedef struct Uniforms { float time; /* 4 bytes */ float aspect; /* 4 bytes */ float uv_scale; /* 4 bytes */ float _pad; /* 4 bytes — pad to 16-byte boundary */ } Uniforms; ``` GPU uniform buffers require 16-byte aligned members. Missing padding causes subtle data corruption. ## Common mistakes | Mistake | Fix | |---------|-----| | Missing `COLOR_TARGET` usage | `SDL_GenerateMipmapsForGPUTexture` requires `SAMPLER \| COLOR_TARGET` | | `num_levels = 1` with mipmap sampler | Set `num_levels = (int)forge_log2f(size) + 1` | | Calling `SDL_GenerateMipmapsForGPUTexture` inside a pass | Must be called outside any render or copy pass | | `max_lod = 0` with trilinear sampler | GPU can only use level 0 — set `max_lod = 1000.0f` for all levels | | Uniform buffer not 16-byte aligned | Add `float _pad` fields to fill to 16 bytes | | Forgetting to upload base level before generating mips | Upload mip 0 first, then call generate | ## Cleanup Release samplers in reverse order of creation: ```c for (int i = 0; i < NUM_SAMPLERS; i++) SDL_ReleaseGPUSampler(device, samplers[i]); SDL_ReleaseGPUTexture(device, texture); ``` ## Reference - [GPU Lesson 05 — Mipmaps](../../../lessons/gpu/05-mipmaps/) — full implementation - [Math Lesson 04 — Mipmaps & LOD](../../../lessons/math/04-mipmaps-and-lod/) — theory - [textures-and-samplers skill](../forge-textures-and-samplers/SKILL.md) — prerequisite - `forge_log2f()` in `common/math/forge_math.h` — mip level count
Voir sur GitHub