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'] = {