Only recreate buffers when their contents change

This commit is contained in:
Aaron Fenyes 2025-04-22 12:03:04 -07:00
parent 23f395331a
commit 5a1d8bc201

View file

@ -8,6 +8,7 @@ use web_sys::{
KeyboardEvent,
MouseEvent,
WebGl2RenderingContext,
WebGlBuffer,
WebGlProgram,
WebGlShader,
WebGlUniformLocation,
@ -81,22 +82,51 @@ fn get_uniform_array_locations<const N: usize>(
})
}
// load the given data into the vertex input of the given name
fn bind_vertex_attrib(
// find the vertex attribute called `attr_name` in the given program. enable it
// and return its index
fn find_and_enable_attribute(
context: &WebGl2RenderingContext,
index: u32,
size: i32,
data: &[f32]
program: &WebGlProgram,
attr_name: &str
) -> u32 {
let attr_index = context.get_attrib_location(program, attr_name) as u32;
context.enable_vertex_attrib_array(attr_index);
attr_index
}
// bind the given vertex buffer object to the given vertex attribute
fn bind_to_attribute(
context: &WebGl2RenderingContext,
attr_index: u32,
attr_size: i32,
buffer: &Option<WebGlBuffer>
) {
// create a data buffer and bind it to ARRAY_BUFFER
let buffer = context.create_buffer().unwrap();
context.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&buffer));
context.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, buffer.as_ref());
context.vertex_attrib_pointer_with_i32(
attr_index,
attr_size,
WebGl2RenderingContext::FLOAT,
false, // don't normalize
0, // zero stride
0, // zero offset
);
}
// load the given data into a new vertex buffer object
fn load_new_buffer(
context: &WebGl2RenderingContext,
data: &[f32]
) -> Option<WebGlBuffer> {
// create a buffer and bind it to ARRAY_BUFFER
let buffer = context.create_buffer();
context.bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, buffer.as_ref());
// load the given data into the buffer. the function `Float32Array::view`
// creates a raw view into our module's `WebAssembly.Memory` buffer.
// allocating more memory will change the buffer, invalidating the view.
// that means we have to make sure we don't allocate any memory until the
// view is dropped
// load the given data into the buffer. this block is unsafe because
// `Float32Array::view` creates a raw view into our module's
// `WebAssembly.Memory` buffer. allocating more memory will change the
// buffer, invalidating the view, so we have to make sure we don't allocate
// any memory until the view is dropped. we're okay here because the view is
// used as soon as it's created
unsafe {
context.buffer_data_with_array_buffer_view(
WebGl2RenderingContext::ARRAY_BUFFER,
@ -105,22 +135,7 @@ fn bind_vertex_attrib(
);
}
// allow the target attribute to be used
context.enable_vertex_attrib_array(index);
// take whatever's bound to ARRAY_BUFFER---here, the data buffer created
// above---and bind it to the target attribute
//
// https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/vertexAttribPointer
//
context.vertex_attrib_pointer_with_i32(
index,
size,
WebGl2RenderingContext::FLOAT,
false, // don't normalize
0, // zero stride
0, // zero offset
);
buffer
}
// the direction in camera space that a mouse event is pointing along
@ -260,9 +275,11 @@ pub fn Display() -> View {
&JsValue::from("uniform vectors available")
);
// find indices of sphere vertex attributes and uniforms
// find and enable the sphere program's sole vertex attribute
let viewport_position_attr = find_and_enable_attribute(&ctx, &sphere_program, "position");
// find the sphere program's uniforms
const SPHERE_MAX: usize = 200;
let viewport_position_index = ctx.get_attrib_location(&sphere_program, "position") as u32;
let sphere_cnt_loc = ctx.get_uniform_location(&sphere_program, "sphere_cnt");
let sphere_sp_locs = get_uniform_array_locations::<SPHERE_MAX>(
&ctx, &sphere_program, "sphere_list", Some("sp")
@ -282,7 +299,7 @@ pub fn Display() -> View {
let layer_threshold_loc = ctx.get_uniform_location(&sphere_program, "layer_threshold");
let debug_mode_loc = ctx.get_uniform_location(&sphere_program, "debug_mode");
// set the viewport vertex positions
// load the viewport vertex positions into a new vertex buffer object
const VERTEX_CNT: usize = 6;
let viewport_positions: [f32; 3*VERTEX_CNT] = [
// northwest triangle
@ -294,9 +311,10 @@ pub fn Display() -> View {
1.0, 1.0, 0.0,
1.0, -1.0, 0.0
];
let viewport_position_buffer = load_new_buffer(&ctx, &viewport_positions);
// find indices of point vertex attributes and uniforms
let point_position_index = ctx.get_attrib_location(&point_program, "position") as u32;
// find and enable the point program's sole vertex attribute
let point_position_attr = find_and_enable_attribute(&ctx, &point_program, "position");
// set up a repainting routine
let (_, start_animation_loop, _) = create_raf(move || {
@ -512,8 +530,9 @@ pub fn Display() -> View {
ctx.uniform1i(layer_threshold_loc.as_ref(), LAYER_THRESHOLD);
ctx.uniform1i(debug_mode_loc.as_ref(), DEBUG_MODE);
// pass the viewport vertex positions
bind_vertex_attrib(&ctx, viewport_position_index, 3, &viewport_positions);
// bind the viewport vertex position buffer to the position
// attribute in the vertex shader
bind_to_attribute(&ctx, viewport_position_attr, 3, &viewport_position_buffer);
// draw the scene
ctx.draw_arrays(WebGl2RenderingContext::TRIANGLES, 0, VERTEX_CNT as i32);
@ -523,8 +542,10 @@ pub fn Display() -> View {
// use the point rendering program
ctx.use_program(Some(&point_program));
// pass the point vertex positions
bind_vertex_attrib(&ctx, point_position_index, 3, point_positions.as_slice());
// load the point positions into a new buffer and bind it to the
// position attribute in the vertex shader
let point_position_buffer = load_new_buffer(&ctx, point_positions.as_slice());
bind_to_attribute(&ctx, point_position_attr, 3, &point_position_buffer);
// draw the scene
ctx.draw_arrays(WebGl2RenderingContext::POINTS, 0, point_positions.ncols() as i32);