Optimize renderer

This commit is contained in:
2026-09-26 11:38:32 -03:00
parent 8b549162c4
commit 58afaac821
8 changed files with 424 additions and 72 deletions
+36 -13
View File
@@ -1,7 +1,7 @@
import type { Camera } from "../camera.ts";
import { mat4 } from "gl-matrix";
import type { RenderLayer } from "$/common/everything_registry.ts";
import { TERRAIN_VERTEX_FLOATS } from "../workers/chunk_messages.ts";
import { TERRAIN_VERTEX_BYTES } from "../workers/chunk_messages.ts";
export let device: GPUDevice;
export let canvas: HTMLCanvasElement;
@@ -375,10 +375,16 @@ export function flush_batch() {
vert_index = 0;
}
// draws a chunk mesh made of quads (4 vertices each). without an index buffer the quads are drawn in order
// what's bound for terrain draws in the current pass, so drawing hundreds of chunks doesn't bind the same things
// again for each one. cleared whenever the pipeline changes
let terrain_binds: { slot: number; texture: GPUTexture; index_buffer: GPUBuffer } | undefined;
// draws quads first to first + quad_count of a chunk mesh (4 vertices each). without an index buffer the quads are
// drawn in order
export function draw_terrain(
layer: RenderLayer,
vertex_buffer: GPUBuffer,
first: number,
quad_count: number,
index_buffer?: GPUBuffer,
) {
@@ -386,7 +392,7 @@ export function draw_terrain(
return;
}
if (!index_buffer) {
ensure_quad_indices(quad_count);
ensure_quad_indices(first + quad_count);
index_buffer = quad_index_buffer!;
}
@@ -395,17 +401,33 @@ export function draw_terrain(
if (current_pipeline !== pipeline) {
render_pass.setPipeline(pipeline);
current_pipeline = pipeline;
terrain_binds = undefined;
}
render_pass.setBindGroup(0, uniform_bind_group, [uniform_slot * UNIFORM_SLOT_SIZE]);
render_pass.setBindGroup(1, get_texture_bind_group(current_texture));
render_pass.setBindGroup(2, lightmap_bind_group);
if (!terrain_binds) {
render_pass.setBindGroup(2, lightmap_bind_group);
}
if (terrain_binds?.slot !== uniform_slot) {
render_pass.setBindGroup(0, uniform_bind_group, [uniform_slot * UNIFORM_SLOT_SIZE]);
}
if (terrain_binds?.texture !== current_texture) {
render_pass.setBindGroup(1, get_texture_bind_group(current_texture));
}
if (terrain_binds?.index_buffer !== index_buffer) {
render_pass.setIndexBuffer(index_buffer, "uint32");
}
terrain_binds = { slot: uniform_slot, texture: current_texture, index_buffer };
render_pass.setVertexBuffer(0, vertex_buffer);
render_pass.setIndexBuffer(index_buffer, "uint32");
render_pass.drawIndexed(quad_count * 6);
render_pass.drawIndexed(quad_count * 6, 1, first * 6);
}
export function create_vertex_buffer(vertices: Float32Array): GPUBuffer {
// the camera's view and projection, for frustum culling. only meaningful in 3d mode
export function view_projection(): Readonly<Float32Array> {
return mvp as Float32Array;
}
export function create_vertex_buffer(vertices: Float32Array<ArrayBuffer> | Uint8Array<ArrayBuffer>): GPUBuffer {
const buffer = device.createBuffer({
size: Math.max(4, vertices.byteLength),
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
@@ -598,6 +620,7 @@ function ensure_pass(): GPURenderPassEncoder {
pending_color_clear = false;
pending_depth_clear = false;
current_pipeline = undefined;
terrain_binds = undefined;
apply_scissor(pass);
return pass;
@@ -762,12 +785,12 @@ function create_pipelines() {
module: terrain_module,
entryPoint: "vs_terrain",
buffers: [{
arrayStride: TERRAIN_VERTEX_FLOATS * 4,
arrayStride: TERRAIN_VERTEX_BYTES,
attributes: [
{ shaderLocation: 0, offset: 0, format: "float32x3" },
{ shaderLocation: 1, offset: 12, format: "float32x2" },
{ shaderLocation: 2, offset: 20, format: "float32x4" },
{ shaderLocation: 3, offset: 36, format: "float32x2" },
{ shaderLocation: 1, offset: 12, format: "unorm16x2" },
{ shaderLocation: 2, offset: 16, format: "unorm8x4" },
{ shaderLocation: 3, offset: 20, format: "unorm8x2" },
],
}],
};