diff --git a/client/index.js b/client/index.js
index ae9c5ae..6282f7d 100644
--- a/client/index.js
+++ b/client/index.js
@@ -3,9 +3,12 @@
document.addEventListener('DOMContentLoaded', main);
const config = {
- ws_url: 'wss://desk.some.website/ws/',
- ping_url: 'https://desk.some.website/api/ping',
- image_url: 'https://desk.some.website/images/',
+// ws_url: 'wss://desk.some.website/ws/',
+// ping_url: 'https://desk.some.website/api/ping',
+// image_url: 'https://desk.some.website/images/',
+ ws_url: 'wss://192.168.100.2/ws/',
+ ping_url: 'https://192.168.100.2/api/ping',
+ image_url: 'https://192.168.100.2/images/',
sync_timeout: 1000,
ws_reconnect_timeout: 2000,
brush_preview_timeout: 1000,
@@ -22,6 +25,7 @@ const config = {
initial_dynamic_bytes: 4096,
frametime_window_size: 100,
tile_size: 16,
+ clip_zoom_threshold: 0.3,
};
const EVENT = Object.freeze({
@@ -176,13 +180,20 @@ function main() {
'players': {},
'onscreen_segments': null,
+
+ 'debug': {
+ 'red': false,
+ 'do_prepass': true,
+ 'limit_from': false,
+ 'limit_to': false,
+ 'render_from': 0,
+ 'render_to': 0,
+ }
};
const context = {
'canvas': null,
'gl': null,
- 'debug_mode': false,
- 'do_prepass': true,
'frametime_window': [],
'frametime_window_head': 0,
@@ -190,6 +201,9 @@ function main() {
'need_static_allocate': true,
'need_static_upload': true,
'need_dynamic_upload': false,
+ 'need_index_upload': true,
+
+ 'full_index_count': 0,
'programs': {},
'buffers': {},
diff --git a/client/math.js b/client/math.js
index ff41fcd..ef82c92 100644
--- a/client/math.js
+++ b/client/math.js
@@ -215,7 +215,7 @@ function quad_fully_onscreen(screen, bbox) {
return false;
}
-function segments_onscreen(state, context) {
+function segments_onscreen(state, context, do_clip) {
// TODO: handle stroke width
if (state.onscreen_segments === null) {
@@ -251,30 +251,36 @@ function segments_onscreen(state, context) {
let head = 0;
for (let i = 0; i < state.events.length; ++i) {
+ if (state.debug.limit_to && i >= state.debug.render_to) break;
+
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 (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]
- state.onscreen_segments[at + 0] = base + 0;
- state.onscreen_segments[at + 1] = base + 1;
- state.onscreen_segments[at + 2] = base + 2;
- state.onscreen_segments[at + 3] = base + 3;
- state.onscreen_segments[at + 4] = base + 2;
- state.onscreen_segments[at + 5] = base + 1;
-
- at += 6;
+
+ if (!(state.debug.limit_from && i < state.debug.render_from)) {
+ if (event.type === EVENT.STROKE && !event.deleted) {
+ if (!do_clip || quad_onscreen(screen, event.bbox)) {
+ const fully_onscreen = !do_clip || 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 (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]
+ state.onscreen_segments[at + 0] = base + 0;
+ state.onscreen_segments[at + 1] = base + 1;
+ state.onscreen_segments[at + 2] = base + 2;
+ state.onscreen_segments[at + 3] = base + 3;
+ state.onscreen_segments[at + 4] = base + 2;
+ state.onscreen_segments[at + 5] = base + 1;
+
+ at += 6;
+ }
}
}
}
- head += (event.points.length - 1) * 4;
}
+
+ head += (event.points.length - 1) * 4;
}
return at;
diff --git a/client/webgl_draw.js b/client/webgl_draw.js
index defeadd..b97d2a8 100644
--- a/client/webgl_draw.js
+++ b/client/webgl_draw.js
@@ -1,10 +1,6 @@
function schedule_draw(state, context) {
if (!state.timers.raf) {
window.requestAnimationFrame(() => {
- context._DRAW_TO_TEXTURE = true;
- draw(state, context);
-
- context._DRAW_TO_TEXTURE = false;
draw(state, context)
});
state.timers.raf = true;
@@ -58,11 +54,29 @@ function draw(state, context) {
gl.clearDepth(0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
- const before_clip = performance.now();
- const index_count = segments_onscreen(state, context);
- const after_clip = performance.now();
+ let index_count;
+ const do_clip = (state.canvas.zoom > config.clip_zoom_threshold);
+
+ if (do_clip) {
+ context.need_index_upload = true;
+ }
+
+ if (do_clip || context.need_index_upload) {
+ const before_clip = performance.now();
+ index_count = segments_onscreen(state, context, do_clip);
+ const after_clip = performance.now();
+ }
+
+ if (!do_clip && !context.need_index_upload) {
+ index_count = context.full_index_count;
+ }
//console.debug('clip', after_clip - before_clip);
-
+
+ document.getElementById('debug-stats').innerHTML = `
+ Segments onscreen: ${index_count}
+ Canvas offset: (${state.canvas.offset.x}, ${state.canvas.offset.y})
+ Canvas zoom: ${Math.round(state.canvas.zoom * 100) / 100}`;
+
if (index_count > 0) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers['b_packed_static_index']);
@@ -70,12 +84,18 @@ function draw(state, context) {
const static_points = context.static_serializer.offset / config.bytes_per_point;
//const dynamic_points = context.dynamic_serializer.offset / config.bytes_per_point;
+ if (!do_clip) {
+ // Almost everything on screen anyways. Only upload indices once
+ if (context.need_index_upload) {
+ context.full_index_count = index_count;
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_buffer, gl.STATIC_DRAW);
+ context.need_index_upload = false;
+ }
+ }
+
if (static_points > 0) {
// DEPTH PREPASS
-
- index_buffer.reverse();
-
- if (context.do_prepass) {
+ if (state.debug.do_prepass && do_clip) {
gl.drawBuffers([gl.NONE]);
locations = context.locations['sdf'].opaque;
@@ -95,11 +115,14 @@ function draw(state, context) {
gl.vertexAttribPointer(locations['a_line'], 4, gl.FLOAT, false, config.bytes_per_point, 4 * 3);
gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_buffer, gl.DYNAMIC_DRAW);
+ if (do_clip) {
+ index_buffer.reverse();
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_buffer, gl.DYNAMIC_DRAW);
+ }
+
gl.drawElements(gl.TRIANGLES, index_count, gl.UNSIGNED_INT, 0);
}
-
// MAIN PASS
gl.drawBuffers([gl.BACK]);
@@ -111,6 +134,7 @@ function draw(state, context) {
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_stroke_count'], state.stroke_count);
+ gl.uniform1i(locations['u_debug_mode'], state.debug.red);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_line']);
@@ -122,8 +146,13 @@ function draw(state, context) {
gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4);
gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4);
- index_buffer.reverse();
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_buffer, gl.DYNAMIC_DRAW);
+ if (do_clip) {
+ if (state.debug.do_prepass) {
+ index_buffer.reverse();
+ }
+
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, index_buffer, gl.DYNAMIC_DRAW);
+ }
gl.drawElements(gl.TRIANGLES, index_count, gl.UNSIGNED_INT, 0);
}
@@ -162,7 +191,8 @@ function draw(state, context) {
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);
+ //console.debug(timeElapsed / 1000000);
+ document.getElementById('debug-timings').innerHTML = 'Frametime: ' + Math.round(timeElapsed / 10000) / 100 + 'ms';
}
if (available || disjoint) {
diff --git a/client/webgl_listeners.js b/client/webgl_listeners.js
index ad0d5c4..00b0822 100644
--- a/client/webgl_listeners.js
+++ b/client/webgl_listeners.js
@@ -17,6 +17,45 @@ function init_listeners(state, context) {
context.canvas.addEventListener('drop', (e) => on_drop(e, state, context));
context.canvas.addEventListener('dragover', (e) => mousemove(e, state, context));
+
+ debug_panel_init(state, context);
+}
+
+function debug_panel_init(state, context) {
+ document.getElementById('debug-red').checked = state.debug.red;
+ document.getElementById('debug-do-prepass').checked = state.debug.do_prepass;
+ document.getElementById('debug-limit-from').checked = state.debug.limit_from;
+ document.getElementById('debug-limit-to').checked = state.debug.limit_to;
+
+ document.getElementById('debug-red').addEventListener('click', (e) => {
+ state.debug.red = e.target.checked;
+ schedule_draw(state, context);
+ });
+
+ document.getElementById('debug-do-prepass').addEventListener('click', (e) => {
+ state.debug.do_prepass = e.target.checked;
+ schedule_draw(state, context);
+ });
+
+ document.getElementById('debug-limit-from').addEventListener('click', (e) => {
+ state.debug.limit_from = e.target.checked;
+ schedule_draw(state, context);
+ });
+
+ document.getElementById('debug-limit-to').addEventListener('click', (e) => {
+ state.debug.limit_to = e.target.checked;
+ schedule_draw(state, context);
+ });
+
+ document.getElementById('debug-render-from').addEventListener('input', (e) => {
+ state.debug.render_from = parseInt(e.target.value);
+ schedule_draw(state, context);
+ });
+
+ document.getElementById('debug-render-to').addEventListener('input', (e) => {
+ state.debug.render_to = parseInt(e.target.value);
+ schedule_draw(state, context);
+ });
}
function cancel(e) {
@@ -69,11 +108,7 @@ function keydown(e, state, context) {
}
}
} else if (e.code === 'KeyD') {
- context.debug_mode = !context.debug_mode;
- schedule_draw(state, context);
- } else if (e.code === 'KeyP') {
- context.do_prepass = !context.do_prepass;
- schedule_draw(state, context);
+ document.querySelector('.debug-window').classList.toggle('dhide');
}
}
diff --git a/client/webgl_shaders.js b/client/webgl_shaders.js
index 3753292..bac05b2 100644
--- a/client/webgl_shaders.js
+++ b/client/webgl_shaders.js
@@ -122,20 +122,20 @@ const sdf_fs_src = `#version 300 es
out vec4 FragColor;
void main() {
- vec2 a = v_line.xy;
- vec2 b = v_line.zw;
+ if (u_debug_mode == 0) {
+ vec2 a = v_line.xy;
+ vec2 b = v_line.zw;
- vec2 pa = v_texcoord - a.xy, ba = b.xy - a.xy;
- float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
- float dist = length(pa - ba * h) - v_thickness / 2.0;
+ vec2 pa = v_texcoord - a.xy, ba = b.xy - a.xy;
+ float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
+ float dist = length(pa - ba * h) - v_thickness / 2.0;
- float fade = 0.5 * length(fwidth(v_texcoord));
- float alpha = 1.0 - smoothstep(0.0, fade, dist);
+ float fade = 0.5 * length(fwidth(v_texcoord));
+ float alpha = 1.0 - smoothstep(-fade, fade, dist);
- if (u_debug_mode == 1) {
- FragColor = vec4(1.0, 0.0, 0.0, 0.1);
- } else {
FragColor = vec4(v_color * alpha, alpha);
+ } else {
+ FragColor = vec4(1.0, 0.0, 0.0, 1.0 / 32.0);
}
}
`;
@@ -191,6 +191,7 @@ function init_webgl(state, context) {
gl.enable(gl.BLEND);
gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
+ //gl.blendFunc(gl.SRC_ALPHA, gl.DST_ALPHA);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.GEQUAL);