Browse Source

Render stroke_id to a small texture

ssao
A.Olokhtonov 1 year ago
parent
commit
7e3b6156c0
  1. 2
      client/client_recv.js
  2. 6
      client/index.js
  3. 8
      client/math.js
  4. 68
      client/webgl_draw.js
  5. 26
      client/webgl_geometry.js
  6. 4
      client/webgl_listeners.js
  7. 93
      client/webgl_shaders.js

2
client/client_recv.js

@ -175,7 +175,7 @@ function handle_event(state, context, event) { @@ -175,7 +175,7 @@ function handle_event(state, context, event) {
need_draw = true;
}
geometry_add_stroke(state, context, event);
geometry_add_stroke(state, context, event, state.events.length);
break;
}

6
client/index.js

@ -17,10 +17,11 @@ const config = { @@ -17,10 +17,11 @@ const config = {
initial_offline_timeout: 1000,
default_color: 0x00,
default_width: 8,
bytes_per_point: 8 * 4,
bytes_per_point: 9 * 4,
initial_static_bytes: 4096 * 16,
initial_dynamic_bytes: 4096,
frametime_window_size: 100,
tile_size: 16,
};
const EVENT = Object.freeze({
@ -192,7 +193,8 @@ function main() { @@ -192,7 +193,8 @@ function main() {
'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),

8
client/math.js

@ -234,6 +234,14 @@ function segments_onscreen(state, context) { @@ -234,6 +234,14 @@ function segments_onscreen(state, context) {
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});
/*
screen_topleft.x += 300;
screen_topleft.y += 300;
screen_bottomright.x -= 300;
screen_bottomright.y -= 300;
*/
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};

68
client/webgl_draw.js

@ -1,6 +1,12 @@ @@ -1,6 +1,12 @@
function schedule_draw(state, context) {
if (!state.timers.raf) {
window.requestAnimationFrame(() => draw(state, context));
window.requestAnimationFrame(() => {
context._DRAW_TO_TEXTURE = true;
draw(state, context);
context._DRAW_TO_TEXTURE = false;
draw(state, context)
});
state.timers.raf = true;
}
}
@ -14,6 +20,12 @@ function draw(state, context) { @@ -14,6 +20,12 @@ function draw(state, context) {
let query = null;
if (context._DRAW_TO_TEXTURE) {
gl.bindFramebuffer(gl.FRAMEBUFFER, context.framebuffers['sdf'].tiles);
} else {
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
if (context.gpu_timer_ext !== null) {
query = gl.createQuery();
gl.beginQuery(context.gpu_timer_ext.TIME_ELAPSED_EXT, query);
@ -22,20 +34,36 @@ function draw(state, context) { @@ -22,20 +34,36 @@ function draw(state, context) {
let locations;
let buffers;
gl.viewport(0, 0, context.canvas.width, context.canvas.height);
gl.clearColor(context.bgcolor.r, context.bgcolor.g, context.bgcolor.b, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
if (!context._DRAW_TO_TEXTURE) {
gl.viewport(0, 0, context.canvas.width, context.canvas.height);
gl.clearColor(context.bgcolor.r, context.bgcolor.g, context.bgcolor.b, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
} else {
//gl.clearBufferuiv(gl.COLOR, 0, new Uint8Array([0, 0, 0, 1]));
gl.viewport(0, 0, Math.ceil(context.canvas.width / config.tile_size), Math.ceil(context.canvas.height / config.tile_size));
gl.clearColor(context.bgcolor.r, context.bgcolor.g, context.bgcolor.b, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
}
// SDF
locations = context.locations['sdf'];
buffers = context.buffers['sdf'];
gl.useProgram(context.programs['sdf']);
if (!context._DRAW_TO_TEXTURE) {
locations = context.locations['sdf'].main;
gl.useProgram(context.programs['sdf'].main);
} else {
locations = context.locations['sdf'].tiles;
gl.useProgram(context.programs['sdf'].tiles);
}
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._DRAW_TO_TEXTURE) {
gl.uniform1i(locations['u_debugmode'], 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;
@ -45,11 +73,25 @@ function draw(state, context) { @@ -45,11 +73,25 @@ function draw(state, context) {
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);
if (!context._DRAW_TO_TEXTURE) {
gl.enableVertexAttribArray(locations['a_color']);
}
if (context._DRAW_TO_TEXTURE) {
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);
if (!context._DRAW_TO_TEXTURE) {
gl.vertexAttribPointer(locations['a_color'], 3, gl.UNSIGNED_BYTE, true, config.bytes_per_point, 4 * 3 + 4 * 4);
}
if (context._DRAW_TO_TEXTURE) {
gl.vertexAttribIPointer(locations['a_stroke_id'], 1, gl.UNSIGNED_INT, config.bytes_per_point, 4 * 3 + 4 * 4 + 4);
}
if (context.need_static_allocate) {
if (config.debug_print) console.debug('static allocate');
@ -71,7 +113,7 @@ function draw(state, context) { @@ -71,7 +113,7 @@ function draw(state, context) {
const before_clip = performance.now();
const index_count = segments_onscreen(state, context);
const after_clip = performance.now();
console.debug('clip', after_clip - before_clip);
//console.debug('clip', after_clip - before_clip);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers['b_packed_static_index']);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(state.onscreen_segments.buffer, 0, index_count), gl.DYNAMIC_DRAW);
@ -81,6 +123,7 @@ function draw(state, context) { @@ -81,6 +123,7 @@ function draw(state, context) {
gl.drawElements(gl.TRIANGLES, index_count, gl.UNSIGNED_INT, 0);
}
/*
if (dynamic_points > 0) {
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed_dynamic']);
@ -99,6 +142,7 @@ function draw(state, context) { @@ -99,6 +142,7 @@ function draw(state, context) {
gl.drawArrays(gl.TRIANGLES, 0, dynamic_points);
}
*/
/*
const next_tick = () => {
const wait_status = gl.clientWaitSync(sync, 0, 0);

26
client/webgl_geometry.js

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
function push_point(s, x, y, ax, ay, bx, by, thickness, r, g, b) {
function push_point(s, x, y, ax, ay, bx, by, thickness, r, g, b, stroke_id) {
ser_f32(s, x);
ser_f32(s, y);
ser_f32(s, thickness);
@ -10,16 +10,17 @@ function push_point(s, x, y, ax, ay, bx, by, thickness, r, g, b) { @@ -10,16 +10,17 @@ function push_point(s, x, y, ax, ay, bx, by, thickness, r, g, b) {
ser_u8(s, g);
ser_u8(s, b);
ser_align(s, 4);
ser_u32(s, stroke_id);
}
function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, ax, ay, bx, by, thickness, r, g, b) {
push_point(s, p1x, p1y, ax, ay, bx, by, thickness, r, g, b);
push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b);
push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b);
push_point(s, p4x, p4y, ax, ay, bx, by, thickness, r, g, b);
function push_quad(s, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id) {
push_point(s, p1x, p1y, ax, ay, bx, by, thickness, r, g, b, stroke_id);
push_point(s, p2x, p2y, ax, ay, bx, by, thickness, r, g, b, stroke_id);
push_point(s, p3x, p3y, ax, ay, bx, by, thickness, r, g, b, stroke_id);
push_point(s, p4x, p4y, ax, ay, bx, by, thickness, r, g, b, stroke_id);
}
function push_stroke(s, stroke) {
function push_stroke(s, stroke, stroke_index) {
// if (stroke.stroke_id !== 1123776468) {
// return;
// }
@ -71,7 +72,8 @@ function push_stroke(s, stroke) { @@ -71,7 +72,8 @@ function push_stroke(s, stroke) {
from.x, from.y,
to.x, to.y,
stroke_width,
r, g, b
r, g, b,
stroke_index
);
}
}
@ -95,7 +97,7 @@ function geometry_prepare_stroke(state) { @@ -95,7 +97,7 @@ function geometry_prepare_stroke(state) {
};
}
function geometry_add_stroke(state, context, stroke) {
function geometry_add_stroke(state, context, stroke, stroke_index) {
if (!state.online || !stroke) return;
stroke.bbox = stroke_bbox(stroke.points);
@ -104,12 +106,12 @@ function geometry_add_stroke(state, context, stroke) { @@ -104,12 +106,12 @@ function geometry_add_stroke(state, context, stroke) {
let bytes_needed = stroke.points.length * 4 * config.bytes_per_point;
if (bytes_left < bytes_needed) {
const extend_to = Math.ceil((context.static_serializer.size + bytes_needed) * 1.62);
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;
}
push_stroke(context.static_serializer, stroke);
push_stroke(context.static_serializer, stroke, stroke_index);
context.need_static_upload = true;
}
@ -153,7 +155,7 @@ function recompute_dynamic_data(state, context) { @@ -153,7 +155,7 @@ function recompute_dynamic_data(state, context) {
// 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);
push_stroke(context.dynamic_serializer, player, 0); // TODO: stroke index ??
}
}

4
client/webgl_listeners.js

@ -207,7 +207,7 @@ function mouseup(e, state, context) { @@ -207,7 +207,7 @@ function mouseup(e, state, context) {
const stroke = geometry_prepare_stroke(state);
if (stroke) {
geometry_add_stroke(state, context, stroke);
geometry_add_stroke(state, context, stroke, 0); // TODO: stroke index?
queue_event(state, stroke_event(state));
geometry_clear_player(state, context, state.me);
schedule_draw(state, context);
@ -408,7 +408,7 @@ function touchend(e, state, context) { @@ -408,7 +408,7 @@ function touchend(e, state, context) {
const stroke = geometry_prepare_stroke(state);
if (stroke) {
geometry_add_stroke(state, context, stroke);
geometry_add_stroke(state, context, stroke, 0); // TODO: stroke index
queue_event(state, stroke_event(state));
geometry_clear_player(state, context, state.me);
schedule_draw(state, context);

93
client/webgl_shaders.js

@ -3,6 +3,8 @@ const sdf_vs_src = `#version 300 es @@ -3,6 +3,8 @@ const sdf_vs_src = `#version 300 es
in vec4 a_line;
in vec3 a_color;
in uint a_stroke_id;
uniform vec2 u_scale;
uniform vec2 u_res;
uniform vec2 u_translation;
@ -11,6 +13,7 @@ const sdf_vs_src = `#version 300 es @@ -11,6 +13,7 @@ const sdf_vs_src = `#version 300 es
out vec2 v_texcoord;
out vec3 v_color;
flat out uint v_stroke_id;
flat out float v_thickness;
void main() {
@ -48,6 +51,7 @@ const sdf_vs_src = `#version 300 es @@ -48,6 +51,7 @@ const sdf_vs_src = `#version 300 es
v_line = a_line;
v_color = a_color;
v_thickness = a_pos.z;
v_stroke_id = a_stroke_id;
gl_Position = vec4(screen02 - 1.0, 0.0, 1);
}
@ -88,6 +92,28 @@ const sdf_fs_src = `#version 300 es @@ -88,6 +92,28 @@ const sdf_fs_src = `#version 300 es
}
`;
const tiles_fs_src = `#version 300 es
precision highp float;
uniform int u_debug_mode;
in vec4 v_line;
in vec2 v_texcoord;
in vec3 v_color;
flat in uint v_stroke_id;
flat in float v_thickness;
//out uint TileId;
out vec4 FragColor;
void main() {
//TileId = uint(1);
vec3 color = vec3(float(v_stroke_id * 3245u % 255u) / 255.0, float(v_stroke_id * 7343u % 255u) / 255.0, float(v_stroke_id * 5528u % 255u) / 255.0);
FragColor = vec4(color, 1);
}
`;
const tquad_vs_src = `#version 300 es
in vec2 a_pos;
in vec2 a_texcoord;
@ -148,8 +174,13 @@ function init_webgl(state, context) { @@ -148,8 +174,13 @@ function init_webgl(state, context) {
const sdf_vs = create_shader(gl, gl.VERTEX_SHADER, sdf_vs_src);
const sdf_fs = create_shader(gl, gl.FRAGMENT_SHADER, sdf_fs_src);
const tiles_fs = create_shader(gl, gl.FRAGMENT_SHADER, tiles_fs_src);
context.programs['image'] = create_program(gl, quad_vs, quad_fs);
context.programs['sdf'] = create_program(gl, sdf_vs, sdf_fs);
context.programs['sdf'] = {
'main': create_program(gl, sdf_vs, sdf_fs),
'tiles': create_program(gl, sdf_vs, tiles_fs), // same vertex shader
};
context.locations['image'] = {
'a_pos': gl.getAttribLocation(context.programs['image'], 'a_pos'),
@ -163,16 +194,33 @@ function init_webgl(state, context) { @@ -163,16 +194,33 @@ function init_webgl(state, context) {
};
context.locations['sdf'] = {
'a_pos': gl.getAttribLocation(context.programs['sdf'], 'a_pos'),
'a_line': gl.getAttribLocation(context.programs['sdf'], 'a_line'),
'a_color': gl.getAttribLocation(context.programs['sdf'], 'a_color'),
'u_res': gl.getUniformLocation(context.programs['sdf'], 'u_res'),
'u_scale': gl.getUniformLocation(context.programs['sdf'], 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['sdf'], 'u_translation'),
'u_texture_points': gl.getUniformLocation(context.programs['sdf'], 'u_texture_points'),
'u_texture_indices': gl.getUniformLocation(context.programs['sdf'], 'u_texture_indices'),
'u_debug_mode': gl.getUniformLocation(context.programs['sdf'], 'u_debug_mode')
'main': {
'a_pos': gl.getAttribLocation(context.programs['sdf'].main, 'a_pos'),
'a_line': gl.getAttribLocation(context.programs['sdf'].main, 'a_line'),
'a_color': gl.getAttribLocation(context.programs['sdf'].main, 'a_color'),
'a_stroke_id': gl.getAttribLocation(context.programs['sdf'].main, 'a_stroke_id'),
'u_res': gl.getUniformLocation(context.programs['sdf'].main, 'u_res'),
'u_scale': gl.getUniformLocation(context.programs['sdf'].main, 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['sdf'].main, 'u_translation'),
'u_texture_points': gl.getUniformLocation(context.programs['sdf'].main, 'u_texture_points'),
'u_texture_indices': gl.getUniformLocation(context.programs['sdf'].main, 'u_texture_indices'),
'u_debug_mode': gl.getUniformLocation(context.programs['sdf'].main, 'u_debug_mode'),
},
'tiles': {
'a_pos': gl.getAttribLocation(context.programs['sdf'].tiles, 'a_pos'),
'a_line': gl.getAttribLocation(context.programs['sdf'].tiles, 'a_line'),
'a_color': gl.getAttribLocation(context.programs['sdf'].tiles, 'a_color'),
'a_stroke_id': gl.getAttribLocation(context.programs['sdf'].tiles, 'a_stroke_id'),
'u_res': gl.getUniformLocation(context.programs['sdf'].tiles, 'u_res'),
'u_scale': gl.getUniformLocation(context.programs['sdf'].tiles, 'u_scale'),
'u_translation': gl.getUniformLocation(context.programs['sdf'].tiles, 'u_translation'),
'u_texture_points': gl.getUniformLocation(context.programs['sdf'].tiles, 'u_texture_points'),
'u_texture_indices': gl.getUniformLocation(context.programs['sdf'].tiles, 'u_texture_indices'),
'u_debug_mode': gl.getUniformLocation(context.programs['sdf'].tiles, 'u_debug_mode'),
}
};
context.buffers['image'] = {
@ -187,7 +235,25 @@ function init_webgl(state, context) { @@ -187,7 +235,25 @@ function init_webgl(state, context) {
'b_packed_dynamic_index': gl.createBuffer(),
};
context.textures['sdf'] = {};
context.textures['sdf'] = {
'tiles': gl.createTexture(),
};
context.framebuffers['sdf'] = {
'tiles': gl.createFramebuffer(),
};
gl.bindTexture(gl.TEXTURE_2D, context.textures['sdf'].tiles);
//gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32UI, context.canvas.width, context.canvas.height, 0, gl.RED_INTEGER, gl.UNSIGNED_INT, null);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, Math.ceil(context.canvas.width / config.tile_size), Math.ceil(context.canvas.height / config.tile_size), 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.bindFramebuffer(gl.FRAMEBUFFER, context.framebuffers['sdf'].tiles);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, context.textures['sdf'].tiles, 0);
// gl.bindFramebuffer(gl.FRAMEBUFFER, null);
context.textures['image'] = {};
const resize_canvas = (entries) => {
@ -209,6 +275,9 @@ function init_webgl(state, context) { @@ -209,6 +275,9 @@ function init_webgl(state, context) {
context.canvas.width = width;
context.canvas.height = height;
gl.bindTexture(gl.TEXTURE_2D, context.textures['sdf'].tiles);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, Math.ceil(context.canvas.width / config.tile_size), Math.ceil(context.canvas.height / config.tile_size), 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
schedule_draw(state, context);
}

Loading…
Cancel
Save