Browse Source

LODs work! Need to fix that memory usage though

ssao
A.Olokhtonov 11 months ago
parent
commit
1438b0ad73
  1. 63
      client/bvh.js
  2. 12
      client/client_recv.js
  3. 39
      client/client_send.js
  4. 36
      client/index.js
  5. 19
      client/math.js
  6. 214
      client/webgl_draw.js
  7. 54
      client/webgl_geometry.js
  8. 14
      client/webgl_shaders.js
  9. 3
      server/config.js
  10. 4
      server/math.js
  11. 1
      server/milton.js
  12. 8
      server/recv.js
  13. 7
      server/send.js
  14. 9
      server/storage.js

63
client/bvh.js

@ -91,10 +91,6 @@ function bvh_find_best_sibling(bvh, leaf_index) { @@ -91,10 +91,6 @@ function bvh_find_best_sibling(bvh, leaf_index) {
return best_index;
}
function bvh_rotate(bvh, index) {
}
function bvh_add_stroke(bvh, index, stroke) {
const leaf_index = bvh_make_leaf(bvh, index, stroke);
@ -147,7 +143,7 @@ function bvh_add_stroke(bvh, index, stroke) { @@ -147,7 +143,7 @@ function bvh_add_stroke(bvh, index, stroke) {
bvh.nodes[new_parent].bbox = new_bbox;
bvh.nodes[new_parent].area = (new_bbox.x2 - new_bbox.x1) * (new_bbox.y2 - new_bbox.y1);
// 3. Refit and rotate
// 3. Refit
let refit_index = bvh.nodes[leaf_index].parent_index;
while (refit_index !== null) {
const child1 = bvh.nodes[refit_index].child1;
@ -155,8 +151,6 @@ function bvh_add_stroke(bvh, index, stroke) { @@ -155,8 +151,6 @@ function bvh_add_stroke(bvh, index, stroke) {
bvh.nodes[refit_index].bbox = quad_union(bvh.nodes[child1].bbox, bvh.nodes[child2].bbox);
bvh_rotate(bvh, refit_index);
refit_index = bvh.nodes[refit_index].parent_index;
}
}
@ -187,48 +181,53 @@ function bvh_intersect_quad(bvh, quad) { @@ -187,48 +181,53 @@ function bvh_intersect_quad(bvh, quad) {
return result;
}
function bvh_clip(state, context) {
if (state.onscreen_segments.length < Math.ceil(state.total_points * 6 * 1.2)) {
state.onscreen_segments = new Uint32Array(state.total_points * 6 * 2);
}
function bvh_clip(state, context, lod_level) {
const lod = context.lods[lod_level];
let at = 0;
lod.indices = ser_ensure(lod.indices, lod.total_points * 6 * 4);
ser_clear(lod.indices);
const screen_topleft = screen_to_canvas(state, {'x': 0, 'y': 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 };
const screen_bottomleft = { 'x': screen_topleft.x, 'y': screen_bottomright.y };
const screen = {'x1': screen_topleft.x, 'y1': screen_topleft.y, 'x2': screen_bottomright.x, 'y2': screen_bottomright.y};
const screen_topright = { 'x': screen_bottomright.x, 'y': screen_topleft.y };
const screen_bottomleft = { 'x': screen_topleft.x, 'y': screen_bottomright.y };
const screen = {
'x1': screen_topleft.x,
'y1': screen_topleft.y,
'x2': screen_bottomright.x,
'y2': screen_bottomright.y
};
const stroke_indices = bvh_intersect_quad(state.bvh, screen);
stroke_indices.sort((a, b) => a - b);
for (const i of stroke_indices) {
if (state.debug.limit_to && i >= state.debug.render_to) break;
const event = state.events[i];
if (!(state.debug.limit_from && i < state.debug.render_from)) {
if (event.type === EVENT.STROKE && !event.deleted && event.points.length > 0) {
for (let j = 0; j < event.points.length - 1; ++j) {
let base = event.starting_index + j * 4;
const points = event.lods[lod_level].points;
for (let j = 0; j < points.length - 1; ++j) {
const base = event.lods[lod_level].starting_index + 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;
ser_u32(lod.indices, base + 0);
ser_u32(lod.indices, base + 1);
ser_u32(lod.indices, base + 2);
ser_u32(lod.indices, base + 3);
ser_u32(lod.indices, base + 2);
ser_u32(lod.indices, base + 1);
}
}
}
}
return at;
return lod.indices.offset / 4;
}
function bvh_construct_rec(bvh, vertical, strokes) {
@ -263,5 +262,7 @@ function bvh_construct_rec(bvh, vertical, strokes) { @@ -263,5 +262,7 @@ function bvh_construct_rec(bvh, vertical, strokes) {
}
function bvh_construct(state) {
state.bvh.root = bvh_construct_rec(state.bvh, true, state.events);
if (state.events.length > 0) {
state.bvh.root = bvh_construct_rec(state.bvh, true, state.events);
}
}

12
client/client_recv.js

@ -80,6 +80,7 @@ function des_event(d) { @@ -80,6 +80,7 @@ function des_event(d) {
event.stroke_id = stroke_id;
event.points = [];
event.lods = [];
for (let i = 0; i < point_count; ++i) {
const x = coords[2 * i + 0];
@ -176,16 +177,7 @@ function handle_event(state, context, event, options = {}) { @@ -176,16 +177,7 @@ function handle_event(state, context, event, options = {}) {
geometry_clear_player(state, context, event.user_id);
need_draw = true;
//}
event.index = state.events.length;
event.starting_index = state.starting_index;
if (event.points.length > 1) {
state.starting_index += (event.points.length - 1) * 4;
}
state.total_points += event.points.length;
geometry_add_stroke(state, context, event, state.events.length, options.skip_bvh === true);
state.stroke_count++;

39
client/client_send.js

@ -6,18 +6,41 @@ function serializer_create(size) { @@ -6,18 +6,41 @@ function serializer_create(size) {
'buffer': buffer,
'view': new DataView(buffer),
'strview': new Uint8Array(buffer),
'need_gpu_allocate': true, // need to call glBufferData to create a GPU buffer of size serializer.size
'gpu_upload_from': 0, // need to call glBufferSubData for bytes in [serializer.gpu_upload_from, serializer.offset)
};
}
function ser_extend(s, by) {
const old_view = s.strview;
const old_offset = s.offset;
const s_copy = serializer_create(s.size + by)
function ser_ensure(s, size) {
if (s.size < size) {
const new_s = serializer_create(Math.ceil(size * 2));
new_s.strview.set(s.strview);
new_s.offset = s.offset;
return new_s;
}
return s;
}
function ser_ensure_by(s, by) {
if (s.offset + by > s.size) {
const new_s = serializer_create(Math.ceil((s.size + by) * 2));
new_s.strview.set(s.strview);
new_s.offset = s.offset;
return new_s;
}
s_copy.strview.set(old_view);
s_copy.offset = old_offset;
return s;
}
return s_copy;
function ser_clear(s) {
s.offset = 0;
s.gpu_upload_from = 0;
}
function ser_u8(s, value) {
@ -294,4 +317,4 @@ function clear_event(state) { @@ -294,4 +317,4 @@ function clear_event(state) {
return {
'type': EVENT.CLEAR
};
}
}

36
client/index.js

@ -19,15 +19,14 @@ const config = { @@ -19,15 +19,14 @@ const config = {
second_finger_timeout: 500,
buffer_first_touchmoves: 5,
debug_print: false,
min_zoom: 0.01,
max_zoom: 1000,
min_zoom: 0.00001,
max_zoom: 1,
initial_offline_timeout: 1000,
default_color: 0x00,
default_width: 8,
bytes_per_point: 9 * 4,
initial_static_bytes: 4096 * 16,
initial_dynamic_bytes: 4096,
frametime_window_size: 100,
tile_size: 16,
clip_zoom_threshold: 0.00003,
};
@ -207,30 +206,20 @@ function main() { @@ -207,30 +206,20 @@ function main() {
const context = {
'canvas': null,
'gl': null,
'frametime_window': [],
'frametime_window_head': 0,
'static_upload_from': 0,
'need_static_allocate': true,
'need_static_upload': true,
'need_dynamic_upload': false,
'need_index_upload': true,
'full_index_count': 0,
'programs': {},
'buffers': {},
'locations': {},
'textures': {},
'framebuffers': {},
'static_serializer': serializer_create(config.initial_static_bytes),
'static_index_serializer': serializer_create(config.initial_static_bytes),
'dynamic_serializer': serializer_create(config.initial_dynamic_bytes),
'dynamic_index_serializer': serializer_create(config.initial_dynamic_bytes),
'lods': [],
'bgcolor': {'r': 1.0, 'g': 1.0, 'b': 1.0},
'gpu_timer_ext': null,
'active_image': null,
};
@ -238,7 +227,20 @@ function main() { @@ -238,7 +227,20 @@ function main() {
const url = new URL(window.location.href);
const parts = url.pathname.split('/');
config.lod_levels = Math.ceil(Math.log2(1.0 / config.min_zoom));
for (let i = 0; i < config.lod_levels; ++i) {
context.lods.push({
'max_zoom': Math.pow(0.5, i), // use this LOD level when current canvas.zoom is less than this value, but not less than the next level max_zoom (or if this is the last zoom level)
'total_points': 0,
'vertices': serializer_create(config.initial_static_bytes),
'indices': serializer_create(config.initial_static_bytes),
'data_buffer': null,
'index_buffer': null,
});
}
state.desk_id = parts.length > 0 ? parts[parts.length - 1] : 0;
init_webgl(state, context);

19
client/math.js

@ -60,6 +60,25 @@ function process_rdp_r(state, points, start, end) { @@ -60,6 +60,25 @@ function process_rdp_r(state, points, start, end) {
return result;
}
function rdp_indices_r(zoom, points, start, end) {
let result = [];
const max = rdp_find_max({'canvas': {'zoom': zoom}}, points, start, end);
if (max !== -1) {
const before = rdp_indices_r(zoom, points, start, max);
const after = rdp_indices_r(zoom, points, max, end);
result = [...before, max, ...after];
}
return result;
}
function rdp_indices(zoom, points) {
const result = [0, ...rdp_indices_r(zoom, points, 0, points.length - 1), points.length - 1];
return result;
}
function process_rdp(state, points) {
const result = process_rdp_r(state, points, 0, points.length - 1);
result.unshift(points[0]);

214
client/webgl_draw.js

@ -7,30 +7,38 @@ function schedule_draw(state, context) { @@ -7,30 +7,38 @@ function schedule_draw(state, context) {
}
}
function upload_if_needed(context) {
const gl = context.gl;
if (context.need_static_allocate) {
if (config.debug_print) console.debug('static allocate');
gl.bufferData(gl.ARRAY_BUFFER, context.static_serializer.size, gl.DYNAMIC_DRAW);
context.need_static_allocate = false;
context.static_upload_from = 0;
context.need_static_upload = true;
function upload_if_needed(gl, buffer_kind, serializer) {
if (serializer.need_gpu_allocate) {
if (config.debug_print) console.debug('gpu allocate');
gl.bufferData(buffer_kind, serializer.size, gl.DYNAMIC_DRAW);
serializer.need_gpu_allocate = false;
serializer.gpu_upload_from = 0;
}
if (context.need_static_upload) {
if (config.debug_print) console.debug('static upload');
const upload_offset = context.static_upload_from;
const upload_size = context.static_serializer.offset - upload_offset;
gl.bufferSubData(gl.ARRAY_BUFFER, upload_offset, new Uint8Array(context.static_serializer.buffer, upload_offset, upload_size));
context.need_static_upload = false;
context.static_upload_from = context.static_serializer.offset;
if (serializer.gpu_upload_from < serializer.offset) {
if (config.debug_print) console.debug('gpu upload');
const upload_offset = serializer.gpu_upload_from;
const upload_size = serializer.offset - upload_offset;
gl.bufferSubData(buffer_kind, upload_offset, new Uint8Array(serializer.buffer, upload_offset, upload_size));
serializer.gpu_upload_from = serializer.offset;
}
}
function draw(state, context) {
const cpu_before = performance.now();
let lod_level = -1;
for (let i = context.lods.length - 1; i >= 0; --i) {
const level = context.lods[i];
if (state.canvas.zoom <= level.max_zoom) {
lod_level = i;
break;
}
}
const lod = context.lods[lod_level];
state.timers.raf = false;
const gl = context.gl;
@ -43,122 +51,81 @@ function draw(state, context) { @@ -43,122 +51,81 @@ function draw(state, context) {
query = gl.createQuery();
gl.beginQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT, query);
}
let locations;
let buffers;
buffers = context.buffers['sdf'];
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed_static']);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers['b_packed_static_index']);
upload_if_needed(context);
gl.viewport(0, 0, context.canvas.width, context.canvas.height);
gl.clearColor(context.bgcolor.r, context.bgcolor.g, context.bgcolor.b, 1);
gl.clearDepth(0.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
let index_count;
const do_clip = true;//(state.canvas.zoom > config.clip_zoom_threshold);
const before_clip = performance.now();
const index_count = bvh_clip(state, context, lod_level);
const after_clip = performance.now();
if (do_clip) {
context.need_index_upload = true;
}
gl.bindBuffer(gl.ARRAY_BUFFER, lod.data_buffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, lod.index_buffer);
if (do_clip || context.need_index_upload) {
const before_clip = performance.now();
index_count = bvh_clip(state, context);
const after_clip = performance.now();
}
if (!do_clip && !context.need_index_upload) {
index_count = context.full_index_count;
}
upload_if_needed(gl, gl.ARRAY_BUFFER, lod.vertices);
upload_if_needed(gl, gl.ELEMENT_ARRAY_BUFFER, lod.indices);
document.getElementById('debug-stats').innerHTML = `
<span>Segments onscreen: ${do_clip ? index_count : '-' }</span>
<span>LOD level: ${lod_level}</span>
<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 * 100000) / 100000}</span>`;
if (index_count > 0) {
const index_buffer = new Uint32Array(state.onscreen_segments.buffer, 0, index_count);
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
if (state.debug.do_prepass && do_clip) {
gl.drawBuffers([gl.NONE]);
locations = context.locations['sdf'].opaque;
gl.useProgram(context.programs['sdf'].opaque);
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_stroke_count'], state.stroke_count);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_line']);
gl.enableVertexAttribArray(locations['a_stroke_id']);
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.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4);
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]);
// DEPTH PREPASS
if (state.debug.do_prepass) {
gl.drawBuffers([gl.NONE]);
locations = context.locations['sdf'].main;
locations = context.locations['sdf'].opaque;
gl.useProgram(context.programs['sdf'].main);
gl.useProgram(context.programs['sdf'].opaque);
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_stroke_count'], state.stroke_count);
gl.uniform1i(locations['u_debug_mode'], state.debug.red);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_line']);
gl.enableVertexAttribArray(locations['a_color']);
gl.enableVertexAttribArray(locations['a_stroke_id']);
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.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4);
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);
}
// MAIN PASS
gl.drawBuffers([gl.BACK]);
locations = context.locations['sdf'].main;
gl.useProgram(context.programs['sdf'].main);
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_stroke_count'], state.stroke_count);
gl.uniform1i(locations['u_debug_mode'], state.debug.red);
gl.enableVertexAttribArray(locations['a_pos']);
gl.enableVertexAttribArray(locations['a_line']);
gl.enableVertexAttribArray(locations['a_color']);
gl.enableVertexAttribArray(locations['a_stroke_id']);
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.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4);
//index_buffer.reverse();
gl.drawElements(gl.TRIANGLES, index_count, gl.UNSIGNED_INT, 0);
}
/*
// Dynamic data (stroke previews that are currently in progress)
const dynamic_points = context.dynamic_serializer.offset / config.bytes_per_point;
@ -219,7 +186,7 @@ function draw(state, context) { @@ -219,7 +186,7 @@ function draw(state, context) {
gl.drawElements(gl.TRIANGLES, dynamic_indices.length, gl.UNSIGNED_INT, 0);
}
*/
if (state.debug.draw_bvh) {
const points = new Float32Array(state.bvh.nodes.length * 6 * 2);
@ -259,7 +226,7 @@ function draw(state, context) { @@ -259,7 +226,7 @@ function draw(state, context) {
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2);
}
if (context.gpu_timer_ext) {
gl.endQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT);
@ -289,52 +256,7 @@ function draw(state, context) { @@ -289,52 +256,7 @@ function draw(state, context) {
setTimeout(next_tick, 0);
}
// Images
// locations = context.locations['image'];
// buffers = context.buffers['image'];
// textures = context.textures['image'];
// gl.useProgram(context.programs['image']);
// gl.enableVertexAttribArray(locations['a_pos']);
// gl.enableVertexAttribArray(locations['a_texcoord']);
// 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_texture'], 0);
// if (context.quad_positions_f32.byteLength > 0) {
// gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_pos']);
// gl.vertexAttribPointer(locations['a_pos'], 2, gl.FLOAT, false, 0, 0);
// gl.bufferData(gl.ARRAY_BUFFER, context.quad_positions_f32, gl.STATIC_DRAW);
// gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_texcoord']);
// gl.vertexAttribPointer(locations['a_texcoord'], 2, gl.FLOAT, false, 0, 0);
// gl.bufferData(gl.ARRAY_BUFFER, context.quad_texcoords_f32, gl.STATIC_DRAW);
// }
// const count = Object.keys(textures).length;
// let active_image_index = -1;
// gl.uniform1i(locations['u_outline'], 0);
// for (let key = 0; key < count; ++key) {
// if (textures[key].image_id === context.active_image) {
// active_image_index = key;
// continue;
// }
// gl.bindTexture(gl.TEXTURE_2D, textures[key].texture);
// gl.drawArrays(gl.TRIANGLES, key * 6, 6);
// }
// if (active_image_index !== -1) {
// gl.uniform1i(locations['u_outline'], 1);
// gl.bindTexture(gl.TEXTURE_2D, textures[active_image_index].texture);
// gl.drawArrays(gl.TRIANGLES, active_image_index * 6, 6);
// }
//
const cpu_after = performance.now();

54
client/webgl_geometry.js

@ -96,22 +96,52 @@ function geometry_prepare_stroke(state) { @@ -96,22 +96,52 @@ function geometry_prepare_stroke(state) {
function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh = false) {
if (!state.online || !stroke || stroke.points.length === 0) return;
stroke.bbox = stroke_bbox(stroke);
stroke.area = (stroke.bbox.x2 - stroke.bbox.x1) * (stroke.bbox.y2 - stroke.bbox.y1);
stroke.index = state.events.length;
for (let i = 0; i < config.lod_levels; ++i) {
// TODO: just pass zoom to process_stroke ?
const saved_zoom = state.canvas.zoom;
state.canvas.zoom = Math.pow(0.5, i);
const points = (i > 0 ? process_stroke(state, stroke.points) : stroke.points);
state.canvas.zoom = saved_zoom;
const vertex_serializer = context.lods[i].vertices = ser_ensure_by(context.lods[i].vertices, points.length * 4 * config.bytes_per_point);
/*
event.index = state.events.length;
event.starting_index = state.starting_index;
if (event.points.length > 1) {
state.starting_index += (event.points.length - 1) * 4;
}
let bytes_left = context.static_serializer.size - context.static_serializer.offset;
let bytes_needed = stroke.points.length * 4 * config.bytes_per_point;
state.total_points += event.points.length;
*/
if (bytes_left < bytes_needed) {
const extend_to = Math.ceil((context.static_serializer.size + bytes_needed) * 1.62 / 4) * 4;
context.static_serializer = ser_extend(context.static_serializer, extend_to);
context.need_static_allocate = true;
}
let starting_index = 0;
push_stroke(context.static_serializer, stroke, stroke_index);
if (!skip_bvh) bvh_add_stroke(state.bvh, stroke_index, stroke);
if (state.events.length > 0) {
const last_stroke = state.events[stroke_index - 1].lods[i];
starting_index = last_stroke.starting_index + (last_stroke.points.length - 1) * 4;
}
stroke.lods.push({
'points': points,
'starting_index': starting_index,
'width': stroke.width,
'color': stroke.color,
});
context.need_static_upload = true;
context.lods[i].total_points += points.length;
push_stroke(vertex_serializer, stroke.lods[stroke.lods.length - 1], stroke_index);
if (i === 0) {
stroke.bbox = stroke_bbox(stroke);
stroke.area = (stroke.bbox.x2 - stroke.bbox.x1) * (stroke.bbox.y2 - stroke.bbox.y1);
}
}
if (!skip_bvh) bvh_add_stroke(state.bvh, stroke_index, stroke);
}
function geometry_delete_stroke(state, context, stroke_index) {

14
client/webgl_shaders.js

@ -187,7 +187,7 @@ const sdf_fs_src = `#version 300 es @@ -187,7 +187,7 @@ const sdf_fs_src = `#version 300 es
FragColor = vec4(v_color * alpha, alpha);
} else {
FragColor = vec4(1.0, 0.0, 0.0, 1.0 / 32.0);
FragColor = vec4(0.2, 0.0, 0.0, 0.2);
}
}
`;
@ -246,12 +246,12 @@ function init_webgl(state, context) { @@ -246,12 +246,12 @@ function init_webgl(state, context) {
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.GEQUAL);
/*
context.gpu_timer_ext = gl.getExtension('EXT_disjoint_timer_query_webgl2');
if (context.gpu_timer_ext === null) {
context.gpu_timer_ext = gl.getExtension('EXT_disjoint_timer_query');
}
*/
const quad_vs = create_shader(gl, gl.VERTEX_SHADER, tquad_vs_src);
const quad_fs = create_shader(gl, gl.FRAGMENT_SHADER, tquad_fs_src);
@ -306,14 +306,18 @@ function init_webgl(state, context) { @@ -306,14 +306,18 @@ function init_webgl(state, context) {
}
};
for (let i = 0; i < context.lods.length; ++i) {
const level = context.lods[i];
level.data_buffer = gl.createBuffer();
level.index_buffer = gl.createBuffer();
}
context.buffers['debug'] = {
'b_packed': gl.createBuffer(),
};
context.buffers['sdf'] = {
'b_packed_static': gl.createBuffer(),
'b_packed_dynamic': gl.createBuffer(),
'b_packed_static_index': gl.createBuffer(),
'b_packed_dynamic_index': gl.createBuffer(),
};

3
server/config.js

@ -2,5 +2,6 @@ export const HOST = '127.0.0.1'; @@ -2,5 +2,6 @@ export const HOST = '127.0.0.1';
export const PORT = 3003;
export const DATADIR = 'data';
export const SYNC_TIMEOUT = 1000;
export const SYNC_MAX_ATTEMPTS = 3;
export const IMAGEDIR = 'images';
export const DEBUG_PRINT = true;
export const DEBUG_PRINT = true;

4
server/math.js

@ -8,7 +8,3 @@ export function crypto_random32() { @@ -8,7 +8,3 @@ export function crypto_random32() {
return dataview.getUint32(0);
}
export function fast_random32() {
return Math.floor(Math.random() * 4294967296);
}

1
server/milton.js

@ -7,7 +7,6 @@ let first_point_x = null; @@ -7,7 +7,6 @@ let first_point_x = null;
let first_point_y = null;
function parse_and_insert_stroke(desk_id, line) {
const stroke_id = math.fast_random32();
const words = line.split(' ');
const width = parseInt(words.shift());
const points = new Float32Array(words.map(i => parseFloat(i)));

8
server/recv.js

@ -13,6 +13,7 @@ function recv_ack(d, session) { @@ -13,6 +13,7 @@ function recv_ack(d, session) {
session.state = SESSION.READY;
session.sn = sn;
session.sync_attempts = 0;
if (config.DEBUG_PRINT) console.log(`ack ${sn} in`);
}
@ -108,15 +109,14 @@ function recv_fire(d, session) { @@ -108,15 +109,14 @@ function recv_fire(d, session) {
function handle_event(session, event) {
switch (event.type) {
case EVENT.STROKE: {
event.stroke_id = math.fast_random32();
storage.queries.insert_stroke.run({
'$id': event.stroke_id,
const stroke_result = storage.queries.insert_stroke.get({
'$width': event.width,
'$color': event.color,
'$points': event.points
});
event.stroke_id = stroke_result.id;
storage.queries.insert_event.run({
'$type': event.type,
'$desk_id': session.desk_id,

7
server/send.js

@ -232,8 +232,11 @@ async function sync_session(session_id) { @@ -232,8 +232,11 @@ async function sync_session(session_id) {
if (config.DEBUG_PRINT) console.log(`syn ${desk.sn} out`);
await session.ws.send(s.buffer);
session.sync_timer = setTimeout(() => sync_session(session_id), config.SYNC_TIMEOUT);
if (session.sync_attempts < config.SYNC_MAX_ATTEMPTS) {
session.sync_attempts += 1;
session.sync_timer = setTimeout(() => sync_session(session_id), config.SYNC_TIMEOUT);
}
}
export function sync_desk(desk_id) {

9
server/storage.js

@ -68,10 +68,10 @@ export function startup() { @@ -68,10 +68,10 @@ export function startup() {
);`).run();
// INSERT
queries.insert_desk = db.query('INSERT INTO desks (id, title, sn) VALUES ($id, $title, 0)');
queries.insert_stroke = db.query('INSERT INTO strokes (id, width, color, points) VALUES ($id, $width, $color, $points) RETURNING id');
queries.insert_session = db.query('INSERT INTO sessions (id, desk_id, lsn) VALUES ($id, $desk_id, 0)');
queries.insert_event = db.query('INSERT INTO events (type, desk_id, session_id, stroke_id, image_id, x, y) VALUES ($type, $desk_id, $session_id, $stroke_id, $image_id, $x, $y)');
queries.insert_desk = db.query('INSERT INTO desks (id, title, sn) VALUES ($id, $title, 0) RETURNING id');
queries.insert_stroke = db.query('INSERT INTO strokes (width, color, points) VALUES ($width, $color, $points) RETURNING id');
queries.insert_session = db.query('INSERT INTO sessions (id, desk_id, lsn) VALUES ($id, $desk_id, 0) RETURNING id');
queries.insert_event = db.query('INSERT INTO events (type, desk_id, session_id, stroke_id, image_id, x, y) VALUES ($type, $desk_id, $session_id, $stroke_id, $image_id, $x, $y) RETURNING id');
// UPDATE
queries.update_desk_sn = db.query('UPDATE desks SET sn = $sn WHERE id = $id');
@ -124,6 +124,7 @@ export function startup() { @@ -124,6 +124,7 @@ export function startup() {
for (const session of stored_sessions) {
session.state = SESSION.CLOSED;
session.ws = null;
session.sync_attempts = 0;
sessions[session.id] = session;
}
}

Loading…
Cancel
Save