|
|
@ -275,7 +275,21 @@ async function draw(state, context, animate, ts) { |
|
|
|
|
|
|
|
|
|
|
|
// TODO: what do we do with this
|
|
|
|
// TODO: what do we do with this
|
|
|
|
const circle_lod = Math.round(Math.min(7, 3 * Math.sqrt(state.canvas.zoom))); |
|
|
|
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
|
|
|
|
// "Static" data upload
|
|
|
|
if (segment_count > 0) { |
|
|
|
if (segment_count > 0) { |
|
|
@ -285,9 +299,16 @@ async function draw(state, context, animate, ts) { |
|
|
|
const batches = []; |
|
|
|
const batches = []; |
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < nbatches; ++i) { |
|
|
|
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.clear(gl.DEPTH_BUFFER_BIT); // draw strokes above the images
|
|
|
|
gl.useProgram(pr.program); |
|
|
|
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 + |
|
|
|
const total_static_size = context.instance_data_points.size * 4 + |
|
|
|
context.instance_data_ids.size * 4 + |
|
|
|
context.instance_data_ids.size * 4 + |
|
|
|
round_to_pow2(context.instance_data_pressures.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.bindBuffer(gl.ARRAY_BUFFER, buffers['b_strokes_static']); |
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, total_static_size, gl.STREAM_DRAW); |
|
|
|
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, 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, tv_data(context.instance_data_ids)); |
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4, |
|
|
|
gl.bufferSubData(gl.ARRAY_BUFFER, context.instance_data_points.size * 4 + context.instance_data_ids.size * 4, |
|
|
|
tv_data(context.instance_data_pressures)); |
|
|
|
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.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']); |
|
|
|
gl.bindTexture(gl.TEXTURE_2D, textures['stroke_data']); |
|
|
|
upload_square_rgba16ui_texture(gl, context.stroke_data, config.stroke_texture_size); |
|
|
|
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_debug_mode'], state.debug.red); |
|
|
|
gl.uniform1i(pr.locations['u_stroke_data'], 0); |
|
|
|
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_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_pos']); |
|
|
|
gl.enableVertexAttribArray(pr.locations['a_a']); |
|
|
|
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_stroke_id']); |
|
|
|
gl.enableVertexAttribArray(pr.locations['a_pressure']); |
|
|
|
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_pos'], 0); |
|
|
|
gl.vertexAttribDivisor(pr.locations['a_a'], 1); |
|
|
|
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); |
|
|
|
gl.vertexAttribDivisor(pr.locations['a_pressure'], 1); |
|
|
|
|
|
|
|
|
|
|
|
for (let b = 0; b < batches.length - 1; ++b) { |
|
|
|
for (let b = 0; b < batches.length - 1; ++b) { |
|
|
|
const batch_from = batches[b]; |
|
|
|
const batch = batches[b]; |
|
|
|
const batch_size = batches[b + 1] - batch_from; |
|
|
|
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.uniform1i(pr.locations['u_circle_points'], level.data.points.size / 2 - 4); |
|
|
|
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.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
|
|
|
|
// 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 = ` |
|
|
|
document.getElementById('debug-stats').innerHTML = ` |
|
|
|
<span>Strokes onscreen: ${context.clipped_indices.size}</span> |
|
|
|
<span>Strokes onscreen: ${context.clipped_indices.size}</span> |
|
|
|
<span>Segments onscreen: ${segment_count}</span> |
|
|
|
<span>Segments onscreen: ${segment_count}</span> |
|
|
|
<span>Total vertices: ${segment_count * circle_data.indices.size}</span> |
|
|
|
<span>Total vertices: ${stat_total_vertices}</span> |
|
|
|
<span>Circle LOD: ${circle_lod}</span> |
|
|
|
<span>Circle LOD: ${circle_lod}</span> |
|
|
|
<span>Canvas offset: (${Math.round(state.canvas.offset.x * 100) / 100}, ${Math.round(state.canvas.offset.y * 100) / 100})</span> |
|
|
|
<span>Canvas offset: (${Math.round(state.canvas.offset.x * 100) / 100}, ${Math.round(state.canvas.offset.y * 100) / 100})</span> |
|
|
|
<span>Canvas zoom level: ${state.canvas.zoom_level}</span> |
|
|
|
<span>Canvas zoom level: ${state.canvas.zoom_level}</span> |
|
|
|