Browse Source

Limit max points per quad, fix wrong rounding of texture size

ssao
A.Olokhtonov 1 year ago
parent
commit
46587068e6
  1. 24
      client/index.html
  2. 3
      client/index.js
  3. 5
      client/webgl_draw.js
  4. 104
      client/webgl_geometry.js
  5. 25
      client/webgl_listeners.js
  6. 33
      client/webgl_shaders.js

24
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=28">
<link rel="stylesheet" type="text/css" href="default.css?v=38">
<script type="text/javascript" src="aux.js?v=28"></script>
<script type="text/javascript" src="math.js?v=28"></script>
<script type="text/javascript" src="tools.js?v=28"></script>
<script type="text/javascript" src="webgl_geometry.js?v=28"></script>
<script type="text/javascript" src="webgl_shaders.js?v=28"></script>
<script type="text/javascript" src="webgl_listeners.js?v=28"></script>
<script type="text/javascript" src="webgl_draw.js?v=28"></script>
<script type="text/javascript" src="index.js?v=28"></script>
<script type="text/javascript" src="aux.js?v=38"></script>
<script type="text/javascript" src="math.js?v=38"></script>
<script type="text/javascript" src="tools.js?v=38"></script>
<script type="text/javascript" src="webgl_geometry.js?v=38"></script>
<script type="text/javascript" src="webgl_shaders.js?v=38"></script>
<script type="text/javascript" src="webgl_listeners.js?v=38"></script>
<script type="text/javascript" src="webgl_draw.js?v=38"></script>
<script type="text/javascript" src="index.js?v=38"></script>
<script type="text/javascript" src="client_send.js?v=28"></script>
<script type="text/javascript" src="client_recv.js?v=28"></script>
<script type="text/javascript" src="websocket.js?v=28"></script>
<script type="text/javascript" src="client_send.js?v=38"></script>
<script type="text/javascript" src="client_recv.js?v=38"></script>
<script type="text/javascript" src="websocket.js?v=38"></script>
</head>
<body>
<div class="main">

3
client/index.js

@ -16,7 +16,7 @@ const config = { @@ -16,7 +16,7 @@ const config = {
default_color: 0x00,
default_width: 8,
bytes_per_point: 8,
initial_static_bytes: 4096,
initial_static_bytes: 4096 * 16,
};
const EVENT = Object.freeze({
@ -108,6 +108,7 @@ function main() { @@ -108,6 +108,7 @@ function main() {
const context = {
'canvas': null,
'gl': null,
'debug_mode': false,
'programs': {},
'buffers': {},

5
client/webgl_draw.js

@ -42,10 +42,12 @@ function draw(state, context) { @@ -42,10 +42,12 @@ function draw(state, context) {
const npoints = context.point_serializer.offset / (4 * 2);
const nstrokes = context.quad_serializer.offset / (6 * 4 * 4);
ser_align(context.point_serializer, 8192);
if (npoints > 0) {
// TOOD: if points changed
if (true) {
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, npoints, 1, 0, gl.RG, gl.FLOAT, new Float32Array(context.point_serializer.buffer, 0, context.point_serializer.offset / 4));
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, Math.min(npoints, 8192), Math.max(1, Math.ceil(npoints / 8192)), 0, gl.RG, gl.FLOAT, new Float32Array(context.point_serializer.buffer, 0, context.point_serializer.offset / 4));
}
gl.activeTexture(gl.TEXTURE0 + 1);
@ -63,6 +65,7 @@ function draw(state, context) { @@ -63,6 +65,7 @@ function draw(state, context) {
gl.uniform2f(locations['u_translation'], state.canvas.offset.x, state.canvas.offset.y);
gl.uniform1i(locations['u_texture_points'], 0);
gl.uniform1i(locations['u_texture_indices'], 1);
gl.uniform1i(locations['u_debug_mode'], context.debug_mode ? 1 : 0);
gl.bufferData(gl.ARRAY_BUFFER, new Uint8Array(context.quad_serializer.buffer, 0, context.quad_serializer.offset), gl.STATIC_DRAW);
gl.drawArrays(gl.TRIANGLES, 0, nstrokes * 6);

104
client/webgl_geometry.js

@ -23,7 +23,23 @@ function push_quad_xyzrgb(s, p1x, p1y, p4x, p4y, z, r, g, b) { @@ -23,7 +23,23 @@ function push_quad_xyzrgb(s, p1x, p1y, p4x, p4y, z, r, g, b) {
push_point_xyzrgb(s, p4x, p1y, z, r, g, b);
}
const MAX_POINTS_PER_QUAD = 10;
const MAX_QUAD_SIDE = 256;
function count_stroke_quads(points) {
let min_x, min_y, max_x, max_y;
let points_per_quad = 0;
// TODO
return Math.ceil(points.length / MAX_POINTS_PER_QUAD);
}
function push_stroke(context, stroke) {
// if (stroke.stroke_id !== 1123776468) {
// return;
// }
const stroke_width = stroke.width;
const points = stroke.points;
const color_u32 = stroke.color;
@ -36,11 +52,8 @@ function push_stroke(context, stroke) { @@ -36,11 +52,8 @@ function push_stroke(context, stroke) {
return;
}
const points_from = context.point_serializer.offset / (4 * 2); // 4 is sizeof(f32) btw, just sain'
const points_to = points_from + points.length;
ser_u32(context.index_serializer, points_from);
ser_u32(context.index_serializer, points_to);
let points_from = context.point_serializer.offset / (4 * 2); // 4 is sizeof(f32) btw, just sain'
const points_start = points_from;
let min_x, min_y, max_x, max_y;
@ -50,15 +63,58 @@ function push_stroke(context, stroke) { @@ -50,15 +63,58 @@ function push_stroke(context, stroke) {
min_y = Math.floor(points[0].y - stroke_width / 2);
max_y = Math.ceil(points[0].y + stroke_width / 2);
for (const p of points) {
let points_per_quad = 0;
for (let i = 0; i < points.length; ++i) {
const p = points[i];
min_x = Math.min(min_x, Math.floor(p.x - stroke_width / 2));
min_y = Math.min(min_y, Math.floor(p.y - stroke_width / 2));
max_x = Math.max(max_x, Math.ceil(p.x + stroke_width / 2));
max_y = Math.max(max_y, Math.ceil(p.y + stroke_width / 2));
push_point_xy(context.point_serializer, p.x, p.y);
points_per_quad++;
if (points_per_quad == MAX_POINTS_PER_QUAD) {
let points_to = points_from + MAX_POINTS_PER_QUAD;
if (points_from > points_start) {
// 1 point overlap to prevent gaps
ser_u32(context.index_serializer, points_from - 1);
} else {
ser_u32(context.index_serializer, points_from);
}
ser_u32(context.index_serializer, points_to);
push_quad_xyzrgb(context.quad_serializer, min_x, min_y, max_x, max_y, stroke_width / 2, r, g, b);
min_x = Math.floor(p.x - stroke_width / 2);
max_x = Math.ceil(p.x + stroke_width / 2);
min_y = Math.floor(p.y - stroke_width / 2);
max_y = Math.ceil(p.y + stroke_width / 2);
points_from = points_to;
points_per_quad = 0;
}
}
push_quad_xyzrgb(context.quad_serializer, min_x, min_y, max_x, max_y, stroke_width / 2, r, g, b);
if (points_per_quad > 0) {
const points_to = points_from + points_per_quad;
if (points_from > points_start) {
ser_u32(context.index_serializer, points_from - 1);
} else {
ser_u32(context.index_serializer, points_from);
}
ser_u32(context.index_serializer, points_to);
push_quad_xyzrgb(context.quad_serializer, min_x, min_y, max_x, max_y, stroke_width / 2, r, g, b);
}
}
function geometry_prepare_stroke(state) {
@ -76,17 +132,35 @@ function geometry_prepare_stroke(state) { @@ -76,17 +132,35 @@ function geometry_prepare_stroke(state) {
function geometry_add_stroke(state, context, stroke) {
if (!state.online || !stroke) return;
if (stroke.points.length < 2) return;
const bytes_left = context.point_serializer.size - context.point_serializer.offset;
const bytes_needed = stroke.points.length * config.bytes_per_point;
const stroke_quads = count_stroke_quads(stroke.points);
// const stroke_quads = Math.ceil(stroke.points.length / MAX_POINTS_PER_QUAD);
if (bytes_left < bytes_needed) {
const extend_points_by = Math.ceil((context.point_serializer.size + bytes_needed) * 1.62);
const extend_indices_by = Math.ceil((context.index_serializer.size + stroke.points.length * 4 * 2) * 1.62);
const extend_quads_by = Math.ceil((context.quad_serializer.size + 6 * (4 * 3)) * 1.62);
// Points
const point_bytes_left = context.point_serializer.size - context.point_serializer.offset;
const point_bytes_needed = stroke.points.length * config.bytes_per_point;
if (point_bytes_left < point_bytes_needed) {
const extend_points_by = Math.ceil((context.point_serializer.size + point_bytes_needed) * 1.62);
context.point_serializer = ser_extend(context.point_serializer, extend_points_by);
}
// Indices
const index_bytes_left = context.index_serializer.size - context.index_serializer.offset;
const index_bytes_needed = stroke_quads * (4 * 2);
if (index_bytes_left < index_bytes_needed) {
const extend_indices_by = Math.ceil((context.index_serializer.size + index_bytes_needed) * 1.62);
context.index_serializer = ser_extend(context.index_serializer, extend_indices_by);
}
// Quads
const quad_bytes_left = context.quad_serializer.size - context.quad_serializer.offset;
const quad_bytes_needed = stroke_quads * 6 * (4 * 4);
if (quad_bytes_left < quad_bytes_needed) {
const extend_quads_by = Math.ceil((context.quad_serializer.size + quad_bytes_needed) * 1.62);
context.quad_serializer = ser_extend(context.quad_serializer, extend_quads_by);
}
@ -151,6 +225,8 @@ function geometry_clear_player(state, context, player_id) { @@ -151,6 +225,8 @@ function geometry_clear_player(state, context, player_id) {
}
function add_image(context, image_id, bitmap, p) {
return; // TODO
const x = p.x;
const y = p.y;
const gl = context.gl;
@ -161,7 +237,7 @@ function add_image(context, image_id, bitmap, p) { @@ -161,7 +237,7 @@ function add_image(context, image_id, bitmap, p) {
'image_id': image_id
};
gl.bindTexture(gl.TEXTURE_2D, context.textures[id].texture);
gl.bindTexture(gl.TEXTURE_2D, context.textures['image'][id].texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bitmap);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);

25
client/webgl_listeners.js

@ -46,6 +46,31 @@ function keydown(e, state, context) { @@ -46,6 +46,31 @@ function keydown(e, state, context) {
} else if (e.code === 'Tab') {
e.preventDefault();
zenmode();
} else if (e.code === 'KeyZ') {
const topleft = screen_to_canvas(state, {'x': 0, 'y': 0});
const bottomright = screen_to_canvas(state, {'x': context.canvas.width, 'y': context.canvas.height});
for (let i = 0; i < state.events.length; ++i) {
const event = state.events[i];
if (event.type === EVENT.STROKE) {
let on_screen = false;
for (const p of event.points) {
if (topleft.x <= p.x && p.x <= bottomright.x && topleft.y <= p.y && p.y <= bottomright.y) {
on_screen = true;
break;
}
}
if (on_screen) {
console.log(i);
}
}
}
} else if (e.code === 'KeyD') {
context.debug_mode = !context.debug_mode;
schedule_draw(state, context);
}
}

33
client/webgl_shaders.js

@ -22,15 +22,16 @@ const sdf_vs_src = `#version 300 es @@ -22,15 +22,16 @@ const sdf_vs_src = `#version 300 es
v_color = a_color;
v_thickness = a_pos.z;
gl_Position = vec4(screen02 - 1.0, 0, 1);
gl_Position = vec4(screen02 - 1.0, 0.0, 1);
}
`;
const sdf_fs_src = `#version 300 es
precision mediump float;
precision highp float;
uniform sampler2D u_texture_points;
uniform highp usampler2D u_texture_indices;
uniform int u_debug_mode;
in vec2 v_texcoord;
in vec3 v_color;
@ -41,7 +42,7 @@ const sdf_fs_src = `#version 300 es @@ -41,7 +42,7 @@ const sdf_fs_src = `#version 300 es
out vec4 FragColor;
void main() {
float mindist = 99999.9;
float mindist = 99999999.9;
uvec4 indices = texelFetch(u_texture_indices, ivec2(v_vertexid / 6, 0), 0);
@ -49,8 +50,14 @@ const sdf_fs_src = `#version 300 es @@ -49,8 +50,14 @@ const sdf_fs_src = `#version 300 es
uint v_to = indices.y;
for (uint i = v_from; i < v_to - uint(1); ++i) {
vec4 a = texelFetch(u_texture_points, ivec2(i, 0), 0);
vec4 b = texelFetch(u_texture_points, ivec2(i + uint(1), 0), 0);
uint x1 = i % uint(8192);
uint y1 = i / uint(8192);
uint x2 = (i + uint(1)) % uint(8192);
uint y2 = (i + uint(1)) / uint(8192);
vec4 a = texelFetch(u_texture_points, ivec2(x1, y1), 0);
vec4 b = texelFetch(u_texture_points, ivec2(x2, y2), 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);
@ -62,8 +69,15 @@ const sdf_fs_src = `#version 300 es @@ -62,8 +69,15 @@ const sdf_fs_src = `#version 300 es
float fade = 0.5 * length(fwidth(v_texcoord));
float alpha = 1.0 - smoothstep(-fade, fade, mindist);
FragColor = vec4(v_color * alpha, alpha);
// FragColor = vec4(v_color, 1.0);
if (u_debug_mode == 1) {
uint x1 = v_from % uint(8192);
uint y1 = v_from / uint(8192);
vec4 a = texelFetch(u_texture_points, ivec2(x1, y1), 0);
FragColor = vec4(a.xy, 0.0, 1.0);
} else {
FragColor = vec4(v_color * alpha, alpha);
// FragColor = vec4(v_color * alpha, 0.1 + alpha);
}
}
`;
@ -88,7 +102,7 @@ const tquad_vs_src = `#version 300 es @@ -88,7 +102,7 @@ const tquad_vs_src = `#version 300 es
`;
const tquad_fs_src = `#version 300 es
precision mediump float;
precision highp float;
in vec2 v_texcoord;
@ -148,6 +162,7 @@ function init_webgl(state, context) { @@ -148,6 +162,7 @@ function init_webgl(state, context) {
'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')
};
context.buffers['image'] = {
@ -164,6 +179,8 @@ function init_webgl(state, context) { @@ -164,6 +179,8 @@ function init_webgl(state, context) {
'indices': gl.createTexture()
};
context.textures['image'] = {};
const resize_canvas = (entries) => {
// https://www.khronos.org/webgl/wiki/HandlingHighDPI
const entry = entries[0];

Loading…
Cancel
Save