diff --git a/client/config.js b/client/config.js index 7417bbf..298f146 100644 --- a/client/config.js +++ b/client/config.js @@ -33,5 +33,10 @@ const config = { offset: { x: 654, y: 372 }, frames: 500, }, + + /* + * points of interest (desk/zoomlevel/x/y + * 1/32/-2075/1020 + */ }; diff --git a/client/wasm/lod.c b/client/wasm/lod.c index f4bde15..f2d7534 100644 --- a/client/wasm/lod.c +++ b/client/wasm/lod.c @@ -83,23 +83,25 @@ rdp_find_max(float *xs, float *ys, unsigned char *pressures, float zoom, int coo float dir_ny = -dx / dist_ab * 255.0f; #if 0 - for (int i = segment_start + 1; i < segment_end; ++i) { - float px = xs[coords_from + i]; - float py = ys[coords_from + i]; + // Scalar version preserved for reference - unsigned char pp = pressures[coords_from + i]; + for (int i = segment_start + 1; i < segment_end; ++i) { + float px = xs[coords_from + i]; + float py = ys[coords_from + i]; - float apx = px - ax; - float apy = py - ay; + unsigned char pp = pressures[coords_from + i]; - float dist = __builtin_fabsf(apx * dir_nx + apy * dir_ny) - + __builtin_abs(pp - ap) + __builtin_abs(pp - bp); + float apx = px - ax; + float apy = py - ay; - if (dist > EPS && dist > max_dist) { - result = i; - max_dist = dist; - } + float dist = __builtin_fabsf(apx * dir_nx + apy * dir_ny) + + __builtin_abs(pp - ap) + __builtin_abs(pp - bp); + + if (dist > EPS && dist > max_dist) { + result = i; + max_dist = dist; } + } #else v128_t ax_x4 = wasm_f32x4_splat(ax); v128_t ay_x4 = wasm_f32x4_splat(ay); diff --git a/client/webgl_draw.js b/client/webgl_draw.js index b7f1e74..b0d05c5 100644 --- a/client/webgl_draw.js +++ b/client/webgl_draw.js @@ -275,7 +275,21 @@ async function draw(state, context, animate, ts) { // TODO: what do we do with this const circle_lod = Math.round(Math.min(7, 3 * Math.sqrt(state.canvas.zoom))); - const circle_data = geometry_good_circle_and_dummy(circle_lod); + const lod_levels = []; + let total_lod_floats = 0; + let total_lod_indices = 0; + let stat_total_vertices = 0; + + for (let i = 0; i <= 7; ++i) { + const d = geometry_good_circle_and_dummy(i); + lod_levels.push({ + 'data': d, + 'vertices_offset': total_lod_floats * 4, + 'indices_offset': total_lod_indices * 4, + }); + total_lod_floats += d.points.size; + total_lod_indices += d.indices.size; + } // "Static" data upload if (segment_count > 0) { @@ -285,9 +299,16 @@ async function draw(state, context, animate, ts) { const batches = []; for (let i = 0; i < nbatches; ++i) { - batches.push(Math.floor(segment_count / nbatches * i)); + batches.push({ + 'index': Math.floor(segment_count / nbatches * i), + 'lod': circle_lod, + }); + + if (i % 2 == 1) { + batches[batches.length - 1].lod = Math.max(0, batches[batches.length - 1].lod - 4); + } } - batches.push(segment_count); + batches.push({'index': segment_count, 'lod': -1}); // lod unused gl.clear(gl.DEPTH_BUFFER_BIT); // draw strokes above the images gl.useProgram(pr.program); @@ -295,17 +316,29 @@ async function draw(state, context, animate, ts) { const total_static_size = context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + round_to_pow2(context.instance_data_pressures.size, 4) + - circle_data.points.size * 4; + total_lod_floats * 4; gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_strokes_static']); gl.bufferData(gl.ARRAY_BUFFER, total_static_size, gl.STREAM_DRAW); + + // Segment points, segment stroke ids, segment pressures gl.bufferSubData(gl.ARRAY_BUFFER, 0, tv_data(context.instance_data_points)); gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4, tv_data(context.instance_data_ids)); gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4, tv_data(context.instance_data_pressures)); - gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + round_to_pow2(context.instance_data_pressures.size, 4), tv_data(circle_data.points)); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers['i_strokes_static']); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, tv_data(circle_data.indices), gl.STREAM_DRAW); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, total_lod_indices * 4, gl.STREAM_DRAW); + + // Upload all variants of LOD vertices/indices + const base_lod_points_offset = context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + round_to_pow2(context.instance_data_pressures.size, 4); + + for (const level of lod_levels) { + gl.bufferSubData(gl.ARRAY_BUFFER, base_lod_points_offset + level.vertices_offset, tv_data(level.data.points)); + gl.bufferSubData(gl.ELEMENT_ARRAY_BUFFER, level.indices_offset, tv_data(level.data.indices)); + } + + // Per-stroke data (base width, color) gl.bindTexture(gl.TEXTURE_2D, textures['stroke_data']); upload_square_rgba16ui_texture(gl, context.stroke_data, config.stroke_texture_size); @@ -316,7 +349,6 @@ async function draw(state, context, animate, ts) { gl.uniform1i(pr.locations['u_debug_mode'], state.debug.red); gl.uniform1i(pr.locations['u_stroke_data'], 0); gl.uniform1i(pr.locations['u_stroke_texture_size'], config.stroke_texture_size); - gl.uniform1i(pr.locations['u_circle_points'], circle_data.points.size / 2 - 4); gl.enableVertexAttribArray(pr.locations['a_pos']); gl.enableVertexAttribArray(pr.locations['a_a']); @@ -324,9 +356,6 @@ async function draw(state, context, animate, ts) { gl.enableVertexAttribArray(pr.locations['a_stroke_id']); gl.enableVertexAttribArray(pr.locations['a_pressure']); - // Circle meshes (shared for all instances) - gl.vertexAttribPointer(pr.locations['a_pos'], 2, gl.FLOAT, false, 2 * 4, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + round_to_pow2(context.instance_data_pressures.size, 4)); - gl.vertexAttribDivisor(pr.locations['a_pos'], 0); gl.vertexAttribDivisor(pr.locations['a_a'], 1); @@ -335,16 +364,26 @@ async function draw(state, context, animate, ts) { gl.vertexAttribDivisor(pr.locations['a_pressure'], 1); for (let b = 0; b < batches.length - 1; ++b) { - const batch_from = batches[b]; - const batch_size = batches[b + 1] - batch_from; + const batch = batches[b]; + const batch_from = batches[b].index; + const batch_size = batches[b + 1].index - batch_from; + const level = lod_levels[batch.lod]; + + if (batch_size > 0) { + stat_total_vertices += batch_size * level.data.indices.size; - // Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values) - gl.vertexAttribPointer(pr.locations['a_a'], 2, gl.FLOAT, false, 2 * 4, batch_from * 2 * 4); - gl.vertexAttribPointer(pr.locations['a_b'], 2, gl.FLOAT, false, 2 * 4, batch_from * 2 * 4 + 2 * 4); - gl.vertexAttribIPointer(pr.locations['a_stroke_id'], 1, gl.INT, 4, context.instance_data_points.size * 4 + batch_from * 4); - gl.vertexAttribPointer(pr.locations['a_pressure'], 2, gl.UNSIGNED_BYTE, true, 1, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + batch_from); + gl.uniform1i(pr.locations['u_circle_points'], level.data.points.size / 2 - 4); - gl.drawElementsInstanced(gl.TRIANGLES, circle_data.indices.size, gl.UNSIGNED_INT, 0, batch_size); + // Points (a, b) and stroke ids are stored in separate cpu buffers so that points can be reused (look at stride and offset values) + gl.vertexAttribPointer(pr.locations['a_a'], 2, gl.FLOAT, false, 2 * 4, batch_from * 2 * 4); + gl.vertexAttribPointer(pr.locations['a_b'], 2, gl.FLOAT, false, 2 * 4, batch_from * 2 * 4 + 2 * 4); + gl.vertexAttribIPointer(pr.locations['a_stroke_id'], 1, gl.INT, 4, context.instance_data_points.size * 4 + batch_from * 4); + gl.vertexAttribPointer(pr.locations['a_pressure'], 2, gl.UNSIGNED_BYTE, true, 1, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4 + batch_from); + + gl.vertexAttribPointer(pr.locations['a_pos'], 2, gl.FLOAT, false, 2 * 4, base_lod_points_offset + level.vertices_offset, 4); + + gl.drawElementsInstanced(gl.TRIANGLES, level.data.indices.size, gl.UNSIGNED_INT, level.indices_offset, batch_size); + } } // I don't really know why I need to do this, but it @@ -553,7 +592,7 @@ async function draw(state, context, animate, ts) { document.getElementById('debug-stats').innerHTML = ` Strokes onscreen: ${context.clipped_indices.size} Segments onscreen: ${segment_count} - Total vertices: ${segment_count * circle_data.indices.size} + Total vertices: ${stat_total_vertices} Circle LOD: ${circle_lod} Canvas offset: (${Math.round(state.canvas.offset.x * 100) / 100}, ${Math.round(state.canvas.offset.y * 100) / 100}) Canvas zoom level: ${state.canvas.zoom_level} diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js index c543f71..27ff5dc 100644 --- a/client/webgl_shaders.js +++ b/client/webgl_shaders.js @@ -322,10 +322,8 @@ function init_webgl(state, context) { gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); - /* gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.NOTEQUAL); - */ context.gpu_timer_ext = gl.getExtension('EXT_disjoint_timer_query_webgl2'); if (context.gpu_timer_ext === null) {