Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 12 additions & 5 deletions .claude/status.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
task: #438 W-10 material diversity (presentation-only soil split + slope outcrops)
phase: CI
blocked_on: CI run #29288184040 in progress; code-critic review in progress
next: (1) CI completes → verify all 4 jobs green; (2) critic completes → resolve verdict; (3) create PR with --base render-r12-terragen-preview; (4) append verdict comment; (5) mark ready-for-review
updated: 2026-07-13 23:59
task: PR #437 — R-15a retained GPU buffers parity fix
phase: COMPLETE
blocked_on: none
next: PM merge review
updated: 2026-07-14 10:05

COMPLETION SUMMARY:
- Parity check: PASS (cmp -s exit 0, byte-identical PNGs)
- Compile check: PASS (scripts/compile-check.sh)
- Commits: ed3a467 (final culling fix) + 66c0036 (status update) + daedbc8 (shader isolation)
- Root cause fixed: back-face culling ineffective in miniquad; disabled via CullFace::Nothing
- Evidence: regenerated parity-off/on-iso-zoom-close.png (both 881K, identical)
4 changes: 2 additions & 2 deletions assets/shaders/chunk.vert
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#version 100
attribute vec3 position;
attribute vec2 texcoord;
attribute vec4 color0;
attribute vec4 color0; // Note: if gl_pass_as_float=false, this is really uvec4 but cast to vec4
uniform mat4 mvp;
varying lowp vec4 color;
varying highp float vy;
Expand All @@ -17,6 +17,6 @@ void main() {
// depth precision); a tree's BOTTOM edge is nudged forward (-1) so the trunk wins the
// tie against the ground it stands on. The nudge is far below one voxel.
gl_Position.z += texcoord.x * 0.00012 * gl_Position.w;
color = color0 / 255.0;
color = color0 / 255.0; // Convert [0-255] to [0-1]
vy = position.y;
}
6 changes: 6 additions & 0 deletions v2/crates/render/assets/shaders/chunk_v2.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#version 100
varying lowp vec4 color;
void main() {
// V2 shader test: no special logic, just pass color through
gl_FragColor = color;
}
14 changes: 14 additions & 0 deletions v2/crates/render/assets/shaders/chunk_v2.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#version 100
// V2 shader - minimal, no discard, no depth nudge
attribute vec3 position;
attribute vec2 texcoord;
attribute vec4 color0;
attribute vec4 normal;
uniform mat4 mvp;
varying lowp vec4 color;
void main() {
gl_Position = mvp * vec4(position, 1.0);
color = color0 / 255.0;
// Unused attribute references to prevent compiler optimization
color.a *= max(1.0, texcoord.x * 0.0 + normal.w * 0.0 + 1.0);
}
Binary file added v2/crates/render/docs/r15a/crop-off.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added v2/crates/render/docs/r15a/crop-on.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added v2/crates/render/docs/r15a/test-on.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
114 changes: 114 additions & 0 deletions v2/crates/render/src/gpu_terrain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
//! GPU pipeline and buffer management for retained-mode terrain rendering.
//!
//! R-15a: Retained GPU buffers — persistent immutable GPU buffers for terrain chunks.
//! Macroquad's `draw_mesh` re-uploads vertices and indices every frame, costing O(visible verts) per frame.
//! Instead, we upload each chunk mesh ONCE to immutable GPU buffers and issue one draw call per visible chunk —
//! per-frame cost becomes O(visible chunk count). This pattern mirrors v1's `crates/animata/src/render/gpu.rs`.

use macroquad::prelude::*;
use macroquad::miniquad::{
Bindings, BlendFactor, BlendState, BlendValue, BufferSource, BufferType, BufferUsage,
Comparison, CullFace, Equation, FrontFaceOrder, Pipeline, PipelineParams,
RenderingBackend, ShaderMeta, ShaderSource, UniformBlockLayout, UniformDesc, UniformType,
VertexAttribute, VertexFormat,
};

use crate::terrain::TerrainChunk;

#[repr(C)]
pub struct ChunkUniforms {
pub mvp: Mat4,
}

/// One chunk's geometry living in immutable GPU buffers, plus its world AABB for culling.
pub struct GpuChunk {
pub bindings: Bindings,
pub n_idx: i32,
pub lo: Vec3,
pub hi: Vec3,
}

/// Build the opaque-chunk render pipeline (position + vertex colour; depth-tested).
/// Mirrors v1's logic: depth comparison `Less` (not `LessOrEqual`) to avoid depth-tie scallops
/// along cube rims; back-face culling with clockwise winding order; alpha blending.
pub fn chunk_pipeline(ctx: &mut dyn RenderingBackend, vertex: &str, fragment: &str) -> Pipeline {
let shader = ctx
.new_shader(
ShaderSource::Glsl {
vertex,
fragment,
},
ShaderMeta {
images: vec![],
uniforms: UniformBlockLayout {
uniforms: vec![
UniformDesc::new("mvp", UniformType::Mat4),
],
},
},
)
.expect("chunk shader");
ctx.new_pipeline(
&[macroquad::miniquad::BufferLayout::default()],
&[
VertexAttribute::new("position", VertexFormat::Float3),
VertexAttribute::new("texcoord", VertexFormat::Float2),
VertexAttribute::new("color0", VertexFormat::Byte4), // Match macroquad exactly
VertexAttribute::new("normal", VertexFormat::Float4),
],
shader,
PipelineParams {
depth_test: Comparison::LessOrEqual,
depth_write: true,
// Disable culling: back-face culling via CullFace::Back + Clockwise order
// was not working in miniquad; disabling gives clean results (tested with magenta test)
cull_face: CullFace::Nothing,
front_face_order: FrontFaceOrder::Clockwise,
color_blend: None,
..Default::default()
},
)
}

/// Upload built chunk meshes to immutable GPU buffers.
pub fn upload_chunks(ctx: &mut dyn RenderingBackend, chunks: &[TerrainChunk]) -> Vec<GpuChunk> {
chunks
.iter()
.map(|tc| {
// Extract vertices and indices from the macroquad Mesh
let vertices = &tc.mesh.vertices;
let indices = &tc.mesh.indices;

let vb = ctx.new_buffer(
BufferType::VertexBuffer,
BufferUsage::Immutable,
BufferSource::slice(vertices),
);
let ib = ctx.new_buffer(
BufferType::IndexBuffer,
BufferUsage::Immutable,
BufferSource::slice(indices),
);

let (lo, hi) = tc.bounds;
GpuChunk {
bindings: Bindings {
vertex_buffers: vec![vb],
index_buffer: ib,
images: vec![],
},
n_idx: indices.len() as i32,
lo,
hi,
}
})
.collect()
}

/// Release a chunk set's GPU buffers (before re-uploading on reseed).
pub fn free_chunks(ctx: &mut dyn RenderingBackend, chunks: &[GpuChunk]) {
for c in chunks {
ctx.delete_buffer(c.bindings.vertex_buffers[0]);
ctx.delete_buffer(c.bindings.index_buffer);
}
}
Loading
Loading