Browse Source

No clipping at zoom < 0.3. Really fast

ssao
A.Olokhtonov 12 months ago
parent
commit
1bc6f2c3fe
  1. 3
      client/client_recv.js
  2. 35
      client/default.css
  3. 45
      client/index.html
  4. 24
      client/index.js
  5. 46
      client/math.js
  6. 64
      client/webgl_draw.js
  7. 45
      client/webgl_listeners.js
  8. 21
      client/webgl_shaders.js

3
client/client_recv.js

@ -179,6 +179,9 @@ function handle_event(state, context, event) { @@ -179,6 +179,9 @@ function handle_event(state, context, event) {
state.stroke_count++;
document.getElementById('debug-render-from').max = state.stroke_count;
document.getElementById('debug-render-to').max = state.stroke_count;
break;
}

35
client/default.css

@ -27,6 +27,11 @@ body.offline .main { @@ -27,6 +27,11 @@ body.offline .main {
display: none !important;
}
.flexcol {
display: flex;
flex-direction: column;
}
canvas {
width: 100%;
height: 100%;
@ -171,17 +176,17 @@ canvas.movemode.moving { @@ -171,17 +176,17 @@ canvas.movemode.moving {
filter: invert(100%);
}
input[type=range] {
.sizer input[type=range] {
-webkit-appearance: none;
width: 200px;
background: transparent;
}
input[type=range]:focus {
.sizer input[type=range]:focus {
outline: none;
}
input[type=range]::-webkit-slider-thumb {
.sizer input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
border: none;
background: white;
@ -193,7 +198,7 @@ input[type=range]::-webkit-slider-thumb { @@ -193,7 +198,7 @@ input[type=range]::-webkit-slider-thumb {
margin-top: -6px; /* You need to specify a margin in Chrome, but in Firefox and IE it is automatic */
}
input[type=range]::-moz-range-thumb {
.sizer input[type=range]::-moz-range-thumb {
border: none;
background: white;
height: 16px;
@ -203,7 +208,7 @@ input[type=range]::-moz-range-thumb { @@ -203,7 +208,7 @@ input[type=range]::-moz-range-thumb {
border: 2px solid var(--dark-blue);
}
input[type=range]::-webkit-slider-runnable-track {
.sizer input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 8px;
cursor: pointer;
@ -212,7 +217,7 @@ input[type=range]::-webkit-slider-runnable-track { @@ -212,7 +217,7 @@ input[type=range]::-webkit-slider-runnable-track {
border: none;
}
input[type=range]:focus::-webkit-slider-runnable-track {
.sizer input[type=range]:focus::-webkit-slider-runnable-track {
width: 100%;
height: 8px;
cursor: pointer;
@ -221,7 +226,7 @@ input[type=range]:focus::-webkit-slider-runnable-track { @@ -221,7 +226,7 @@ input[type=range]:focus::-webkit-slider-runnable-track {
border: none;
}
input[type=range]::-moz-range-track {
.sizer input[type=range]::-moz-range-track {
width: 100%;
height: 8px;
cursor: pointer;
@ -311,4 +316,18 @@ body.offline * { @@ -311,4 +316,18 @@ body.offline * {
.loader.hidden {
opacity: 0;
}
}
.debug-window {
position: absolute;
min-width: 256px;
top: 20px;
right: 20px;
display: flex;
flex-direction: column;
gap: 10px;
user-select: none;
padding: 5px;
background: white;
border: 1px solid var(--dark-blue);
}

45
client/index.html

@ -7,20 +7,20 @@ @@ -7,20 +7,20 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="shortcut icon" href="icons/favicon.svg" id="favicon">
<link rel="stylesheet" type="text/css" href="default.css?v=65">
<script type="text/javascript" src="aux.js?v=65"></script>
<script type="text/javascript" src="math.js?v=65"></script>
<script type="text/javascript" src="tools.js?v=65"></script>
<script type="text/javascript" src="webgl_geometry.js?v=65"></script>
<script type="text/javascript" src="webgl_shaders.js?v=65"></script>
<script type="text/javascript" src="webgl_listeners.js?v=65"></script>
<script type="text/javascript" src="webgl_draw.js?v=65"></script>
<script type="text/javascript" src="index.js?v=65"></script>
<script type="text/javascript" src="client_send.js?v=65"></script>
<script type="text/javascript" src="client_recv.js?v=65"></script>
<script type="text/javascript" src="websocket.js?v=65"></script>
<link rel="stylesheet" type="text/css" href="default.css?v=66">
<script type="text/javascript" src="aux.js?v=66"></script>
<script type="text/javascript" src="math.js?v=66"></script>
<script type="text/javascript" src="tools.js?v=66"></script>
<script type="text/javascript" src="webgl_geometry.js?v=66"></script>
<script type="text/javascript" src="webgl_shaders.js?v=66"></script>
<script type="text/javascript" src="webgl_listeners.js?v=66"></script>
<script type="text/javascript" src="webgl_draw.js?v=66"></script>
<script type="text/javascript" src="index.js?v=66"></script>
<script type="text/javascript" src="client_send.js?v=66"></script>
<script type="text/javascript" src="client_recv.js?v=66"></script>
<script type="text/javascript" src="websocket.js?v=66"></script>
</head>
<body>
<div class="main">
@ -30,6 +30,23 @@ @@ -30,6 +30,23 @@
<polyline points="150,150 225,230 280,120" / fill="none" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="5">
</svg> -->
<div class="debug-window dhide">
<div id="debug-stats" class="flexcol"></div>
<div id="debug-timings" class="flexcol"></div>
<label><input type="checkbox" id="debug-red">Simple shader</label>
<label><input type="checkbox" id="debug-do-prepass">Depth prepass</label>
<div class="flexcol">
<label><input type="checkbox" id="debug-limit-from">Limit events from</label>
<input type="range" min="0" max="0" value="0" id="debug-render-from">
</div>
<div class="flexcol">
<label><input type="checkbox" id="debug-limit-to">Limit events to</label>
<input type="range" min="0" max="0" value="0" id="debug-render-to">
</div>
</div>
<div class="sizer-wrapper">
<div class="sizer">

24
client/index.js

@ -3,9 +3,12 @@ @@ -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 = { @@ -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() { @@ -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() { @@ -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': {},

46
client/math.js

@ -215,7 +215,7 @@ function quad_fully_onscreen(screen, bbox) { @@ -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) { @@ -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;

64
client/webgl_draw.js

@ -1,10 +1,6 @@ @@ -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) { @@ -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 = `
<span>Segments onscreen: ${index_count}</span>
<span>Canvas offset: (${state.canvas.offset.x}, ${state.canvas.offset.y})</span>
<span>Canvas zoom: ${Math.round(state.canvas.zoom * 100) / 100}</span>`;
if (index_count > 0) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers['b_packed_static_index']);
@ -70,12 +84,18 @@ function draw(state, context) { @@ -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) { @@ -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) { @@ -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) { @@ -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) { @@ -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) {

45
client/webgl_listeners.js

@ -17,6 +17,45 @@ function init_listeners(state, context) { @@ -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) { @@ -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');
}
}

21
client/webgl_shaders.js

@ -122,20 +122,20 @@ const sdf_fs_src = `#version 300 es @@ -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) { @@ -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);

Loading…
Cancel
Save