From e41997563fb101affec0c758406ecddbab62e672 Mon Sep 17 00:00:00 2001 From: "A.Olokhtonov" Date: Tue, 7 Nov 2023 17:59:35 +0300 Subject: [PATCH] Faster clipping --- client/index.js | 1 + client/math.js | 22 ++++++++++++----- client/webgl_draw.js | 52 +++++++++++++++++++++++++++++++++++------ client/webgl_shaders.js | 4 +++- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/client/index.js b/client/index.js index be750f7..7efa149 100644 --- a/client/index.js +++ b/client/index.js @@ -173,6 +173,7 @@ function main() { }, 'players': {}, + 'onscreen_segments': [], }; const context = { diff --git a/client/math.js b/client/math.js index 210c922..39c7d7a 100644 --- a/client/math.js +++ b/client/math.js @@ -207,8 +207,19 @@ function quad_onscreen(screen, bbox) { return false; } +function quad_fully_onscreen(screen, bbox) { + if (screen.x1 < bbox.x1 && screen.x2 > bbox.x2 && screen.y1 < bbox.y1 && screen.y2 > bbox.y2) { + return true; + } + + return false; +} + function segments_onscreen(state, context) { - const result = []; + // TODO: handle stroke width + + state.onscreen_segments.length = 0; + const screen_topleft = screen_to_canvas(state, {'x': 0, 'y': 0}); const screen_bottomright = screen_to_canvas(state, {'x': context.canvas.width, 'y': context.canvas.height}); const screen_topright = { 'x': screen_bottomright.x, 'y': screen_topleft.y }; @@ -221,21 +232,20 @@ function segments_onscreen(state, context) { const event = state.events[i]; if (event.type === EVENT.STROKE && !event.deleted) { if (quad_onscreen(screen, event.bbox)) { + const fully_onscreen = quad_fully_onscreen(screen, event.bbox); for (let j = 0; j < event.points.length - 1; ++j) { const a = event.points[j + 0]; const b = event.points[j + 1]; - if (segment_interesects_quad(a, b, screen_topleft, screen_bottomright, screen_topright, screen_bottomleft)) { + if (fully_onscreen || segment_interesects_quad(a, b, screen_topleft, screen_bottomright, screen_topright, screen_bottomleft)) { let base = head + j * 4; // We draw quads as [1, 2, 3, 4, 3, 2] - result.push(base + 0, base + 1, base + 2); - result.push(base + 3, base + 2, base + 1); + state.onscreen_segments.push(base + 0, base + 1, base + 2); + state.onscreen_segments.push(base + 3, base + 2, base + 1); } } } head += (event.points.length - 1) * 4; } } - - return result; } diff --git a/client/webgl_draw.js b/client/webgl_draw.js index 05203e0..ef87a4c 100644 --- a/client/webgl_draw.js +++ b/client/webgl_draw.js @@ -11,9 +11,13 @@ function draw(state, context) { const gl = context.gl; const width = window.innerWidth; const height = window.innerHeight; + + let query = null; - const frame_start = performance.now(); - const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); + if (context.gpu_timer_ext !== null) { + query = gl.createQuery(); + gl.beginQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT, query); + } let locations; let buffers; @@ -64,11 +68,17 @@ function draw(state, context) { context.static_upload_from = context.static_serializer.offset; } - const indices = segments_onscreen(state, context); + const before_clip = performance.now(); + segments_onscreen(state, context); + const after_clip = performance.now(); + console.debug('clip', after_clip - before_clip); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers['b_packed_static_index']); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indices), gl.DYNAMIC_DRAW); - gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_INT, 0); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(state.onscreen_segments), gl.DYNAMIC_DRAW); + const after_index_uploads = performance.now(); + console.debug('index upload', after_index_uploads - after_clip); + + gl.drawElements(gl.TRIANGLES, state.onscreen_segments.length, gl.UNSIGNED_INT, 0); } if (dynamic_points > 0) { @@ -89,7 +99,7 @@ function draw(state, context) { gl.drawArrays(gl.TRIANGLES, 0, dynamic_points); } - +/* const next_tick = () => { const wait_status = gl.clientWaitSync(sync, 0, 0); const frame_end = performance.now(); @@ -104,8 +114,36 @@ function draw(state, context) { } setTimeout(next_tick, 0); + */ + + if (context.gpu_timer_ext) { + gl.endQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT); + + const next_tick = () => { + if (query) { + // At some point in the future, after returning control to the browser + const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE); + const disjoint = gl.getParameter(context.gpu_timer_ext.GPU_DISJOINT_EXT); + + if (available && !disjoint) { + // See how much time the rendering of the object took in nanoseconds. + const timeElapsed = gl.getQueryParameter(query, gl.QUERY_RESULT); + console.debug(timeElapsed / 1000000); + } + + if (available || disjoint) { + // Clean up the query object. + gl.deleteQuery(query); + // Don't re-enter this polling loop. + query = null; + } else { + setTimeout(next_tick, 0); + } + } + } - + setTimeout(next_tick, 0); + } // Images // locations = context.locations['image']; // buffers = context.buffers['image']; diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js index 7f499ea..37dc93d 100644 --- a/client/webgl_shaders.js +++ b/client/webgl_shaders.js @@ -80,7 +80,7 @@ const sdf_fs_src = `#version 300 es // float alpha = 1.0 - step(0.0, dist); if (u_debug_mode == 1) { - FragColor = vec4(1.0, 0.0, 0.0, 0.5); + FragColor = vec4(1.0, 0.0, 0.0, 0.1); } else { FragColor = vec4(v_color * alpha, alpha); // FragColor = vec4(v_color * alpha, 0.1 + alpha); @@ -140,6 +140,8 @@ function init_webgl(state, context) { gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); + context.gpu_timer_ext = gl.getExtension('EXT_disjoint_timer_query_webgl2'); + const quad_vs = create_shader(gl, gl.VERTEX_SHADER, tquad_vs_src); const quad_fs = create_shader(gl, gl.FRAGMENT_SHADER, tquad_fs_src);