diff --git a/Caddyfile b/Caddyfile index 7e46fe3..9d42ea1 100644 --- a/Caddyfile +++ b/Caddyfile @@ -1,4 +1,9 @@ -http://192.168.100.2 { +desk.local { + header { + Cross-Origin-Opener-Policy same-origin + Cross-Origin-Embedder-Policy require-corp + } + redir /ws /ws/ redir /desk /desk/ @@ -16,7 +21,7 @@ http://192.168.100.2 { } handle_path /desk/* { - root * /code/desk2/client + root * /code/desk2/client try_files {path} /index.html file_server } diff --git a/client/index.html b/client/index.html index 5d6f2fd..d2db833 100644 --- a/client/index.html +++ b/client/index.html @@ -7,20 +7,20 @@ - + - - - - - - - - + + + + + + + + - - - + + +
diff --git a/client/index.js b/client/index.js index 7cd68a4..fa1207a 100644 --- a/client/index.js +++ b/client/index.js @@ -1,9 +1,9 @@ document.addEventListener('DOMContentLoaded', main); const config = { - ws_url: 'ws://192.168.100.2/ws/', - ping_url: 'http://192.168.100.2/api/ping', - image_url: 'http://192.168.100.2/images/', + ws_url: 'wss://desk.local/ws/', + ping_url: 'https://desk.local/api/ping', + image_url: 'https://desk.local/images/', sync_timeout: 1000, ws_reconnect_timeout: 2000, brush_preview_timeout: 1000, @@ -17,6 +17,8 @@ const config = { default_width: 8, bytes_per_point: 8 * 4, initial_static_bytes: 4096 * 16, + initial_dynamic_bytes: 4096, + frametime_window_size: 100, }; const EVENT = Object.freeze({ @@ -109,6 +111,11 @@ function main() { 'canvas': null, 'gl': null, 'debug_mode': false, + 'frametime_window': [], + 'frametime_window_head': 0, + + 'need_static_upload': true, + 'need_dynamic_upload': false, 'programs': {}, 'buffers': {}, @@ -116,6 +123,7 @@ function main() { 'textures': {}, 'static_serializer': serializer_create(config.initial_static_bytes), + 'dynamic_serializer': serializer_create(config.initial_dynamic_bytes), 'bgcolor': {'r': 1.0, 'g': 1.0, 'b': 1.0}, diff --git a/client/math.js b/client/math.js index f005882..6e8f0bc 100644 --- a/client/math.js +++ b/client/math.js @@ -7,7 +7,7 @@ function screen_to_canvas(state, p) { } function rdp_find_max(state, points, start, end) { - const EPS = 0.5 / Math.pow(state.canvas.zoom, 0.5); + const EPS = 0.5 / Math.pow(state.canvas.zoom, 0.25); // const EPS = 10.0; let result = -1; diff --git a/client/webgl_draw.js b/client/webgl_draw.js index 055ba8f..135ff21 100644 --- a/client/webgl_draw.js +++ b/client/webgl_draw.js @@ -12,6 +12,9 @@ function draw(state, context) { const width = window.innerWidth; const height = window.innerHeight; + const frame_start = performance.now(); + const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0); + let locations; let buffers; @@ -25,31 +28,68 @@ function draw(state, context) { gl.useProgram(context.programs['sdf']); - gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed']); + gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height); + gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom); + gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y); + gl.uniform1i(locations['u_debug_mode'], context.debug_mode ? 1 : 0); + + const static_points = context.static_serializer.offset / config.bytes_per_point; + const dynamic_points = context.dynamic_serializer.offset / config.bytes_per_point; + + if (static_points > 0) { + gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed_static']); + + gl.enableVertexAttribArray(locations['a_pos']); + gl.enableVertexAttribArray(locations['a_line']); + gl.enableVertexAttribArray(locations['a_color']); + + gl.vertexAttribPointer(locations['a_pos'], 3, gl.FLOAT, false, config.bytes_per_point, 0); + gl.vertexAttribPointer(locations['a_line'], 4, gl.FLOAT, false, config.bytes_per_point, 4 * 3); + gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4); - gl.enableVertexAttribArray(locations['a_pos']); - gl.enableVertexAttribArray(locations['a_line']); - gl.enableVertexAttribArray(locations['a_color']); + if (context.need_static_upload) { + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.static_serializer.buffer, 0, context.static_serializer.offset), gl.STATIC_DRAW); + context.need_static_upload = false; + } - gl.vertexAttribPointer(locations['a_pos'], 3, gl.FLOAT, false, config.bytes_per_point, 0); - gl.vertexAttribPointer(locations['a_line'], 4, gl.FLOAT, false, config.bytes_per_point, 4 * 3); - gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4); + gl.drawArrays(gl.TRIANGLES, 0, static_points); + } + + if (dynamic_points > 0) { + gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed_dynamic']); - const npoints = context.static_serializer.offset / config.bytes_per_point; + gl.enableVertexAttribArray(locations['a_pos']); + gl.enableVertexAttribArray(locations['a_line']); + gl.enableVertexAttribArray(locations['a_color']); - // TODO: if changed + gl.vertexAttribPointer(locations['a_pos'], 3, gl.FLOAT, false, config.bytes_per_point, 0); + gl.vertexAttribPointer(locations['a_line'], 4, gl.FLOAT, false, config.bytes_per_point, 4 * 3); + gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4); - if (npoints > 0) { - gl.uniform2f(locations['u_res'], context.canvas.width, context.canvas.height); - gl.uniform2f(locations['u_scale'], state.canvas.zoom, state.canvas.zoom); - gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y); - gl.uniform1i(locations['u_debug_mode'], context.debug_mode ? 1 : 0); + if (context.need_dynamic_upload) { + gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.dynamic_serializer.buffer, 0, context.dynamic_serializer.offset), gl.STATIC_DRAW); + context.need_dynamic_upload = false; + } - gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.static_serializer.buffer, 0, context.static_serializer.offset), gl.STATIC_DRAW); + gl.drawArrays(gl.TRIANGLES, 0, dynamic_points); + } - gl.drawArrays(gl.TRIANGLES, 0, npoints); + const next_tick = () => { + const wait_status = gl.clientWaitSync(sync, 0, 0); + const frame_end = performance.now(); + + if (wait_status === gl.ALREADY_SIGNALED || wait_status === gl.CONDITION_SATISFIED) { + const frametime_ms = frame_end - frame_start; + gl.deleteSync(sync); + console.debug(frametime_ms); + } else { + setTimeout(next_tick, 0); + } } + setTimeout(next_tick, 0); + + // Images // locations = context.locations['image']; // buffers = context.buffers['image']; diff --git a/client/webgl_geometry.js b/client/webgl_geometry.js index c329b7a..48f0e13 100644 --- a/client/webgl_geometry.js +++ b/client/webgl_geometry.js @@ -22,7 +22,7 @@ function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, ax, ay, bx, by, th push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b); } -function push_stroke(context, stroke) { +function push_stroke(s, stroke) { // if (stroke.stroke_id !== 1123776468) { // return; // } @@ -66,7 +66,7 @@ function push_stroke(context, stroke) { let p4_x = to.x + (-up_x + dir1_x) * radius; let p4_y = to.y + (-up_y + dir1_y) * radius; - push_quad(context.static_serializer, + push_quad(s, p1_x, p1_y, p2_x, p2_y, p3_x, p3_y, @@ -103,7 +103,9 @@ function geometry_add_stroke(state, context, stroke) { context.static_serializer = ser_extend(context.static_serializer, extend_to); } - push_stroke(context, stroke); + push_stroke(context.static_serializer, stroke); + + context.need_static_upload = true; } function geometry_delete_stroke(state, context, stroke_index) { @@ -127,28 +129,30 @@ function geometry_delete_stroke(state, context, stroke_index) { } function recompute_dynamic_data(state, context) { - // let bytes_needed = 0; + let bytes_needed = 0; - // for (const player_id in state.players) { - // const player = state.players[player_id]; - // if (player.points.length > 0) { - // bytes_needed += player.points.length * config.bytes_per_point; - // } - // } + for (const player_id in state.players) { + const player = state.players[player_id]; + if (player.points.length > 0) { + bytes_needed += player.points.length * 6 * config.bytes_per_point; + } + } - // if (bytes_needed > context.dynamic_stroke_serializer.size) { - // context.dynamic_stroke_serializer = serializer_create(Math.ceil(bytes_needed * 1.62)); - // } else { - // context.dynamic_stroke_serializer.offset = 0; - // } + if (bytes_needed > context.dynamic_serializer.size) { + context.dynamic_serializer = serializer_create(Math.ceil(bytes_needed * 1.62)); + } else { + context.dynamic_serializer.offset = 0; + } - // for (const player_id in state.players) { - // // player has the same data as their current stroke: points, color, width - // const player = state.players[player_id]; - // if (player.points.length > 0) { - // push_stroke(context.dynamic_stroke_serializer, player); - // } - // } + for (const player_id in state.players) { + // player has the same data as their current stroke: points, color, width + const player = state.players[player_id]; + if (player.points.length > 0) { + push_stroke(context.dynamic_serializer, player); + } + } + + context.need_dynamic_upload = true; } function geometry_add_point(state, context, player_id, point) { diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js index 066940a..a8727b3 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, 1.0); + FragColor = vec4(1.0, 0.0, 0.0, 0.5); } else { FragColor = vec4(v_color * alpha, alpha); // FragColor = vec4(v_color * alpha, 0.1 + alpha); @@ -179,7 +179,8 @@ function init_webgl(state, context) { }; context.buffers['sdf'] = { - 'b_packed': context.gl.createBuffer(), + 'b_packed_static': context.gl.createBuffer(), + 'b_packed_dynamic': context.gl.createBuffer(), }; context.textures['sdf'] = {