Browse Source

Try to speed up the LOD calculation with a cache (kinda succesfull, but not really)

ssao
A.Olokhtonov 11 months ago
parent
commit
9bbab32369
  1. 32
      client/index.html
  2. 7
      client/index.js
  3. 67
      client/math.js
  4. 56
      client/webgl_geometry.js

32
client/index.html

@ -7,22 +7,22 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <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="shortcut icon" href="icons/favicon.svg" id="favicon">
<link rel="stylesheet" type="text/css" href="default.css?v=66"> <link rel="stylesheet" type="text/css" href="default.css?v=67">
<script type="text/javascript" src="aux.js?v=66"></script> <script type="text/javascript" src="aux.js?v=67"></script>
<script type="text/javascript" src="heapify.js?v=66"></script> <script type="text/javascript" src="heapify.js?v=67"></script>
<script type="text/javascript" src="bvh.js?v=66"></script> <script type="text/javascript" src="bvh.js?v=67"></script>
<script type="text/javascript" src="math.js?v=66"></script> <script type="text/javascript" src="math.js?v=67"></script>
<script type="text/javascript" src="tools.js?v=66"></script> <script type="text/javascript" src="tools.js?v=67"></script>
<script type="text/javascript" src="webgl_geometry.js?v=66"></script> <script type="text/javascript" src="webgl_geometry.js?v=67"></script>
<script type="text/javascript" src="webgl_shaders.js?v=66"></script> <script type="text/javascript" src="webgl_shaders.js?v=67"></script>
<script type="text/javascript" src="webgl_listeners.js?v=66"></script> <script type="text/javascript" src="webgl_listeners.js?v=67"></script>
<script type="text/javascript" src="webgl_draw.js?v=66"></script> <script type="text/javascript" src="webgl_draw.js?v=67"></script>
<script type="text/javascript" src="index.js?v=66"></script> <script type="text/javascript" src="index.js?v=67"></script>
<script type="text/javascript" src="client_send.js?v=66"></script> <script type="text/javascript" src="client_send.js?v=67"></script>
<script type="text/javascript" src="client_recv.js?v=66"></script> <script type="text/javascript" src="client_recv.js?v=67"></script>
<script type="text/javascript" src="websocket.js?v=66"></script> <script type="text/javascript" src="websocket.js?v=67"></script>
</head> </head>
<body> <body>
<div class="main"> <div class="main">

7
client/index.js

@ -31,6 +31,7 @@ const config = {
tile_size: 16, tile_size: 16,
clip_zoom_threshold: 0.00003, clip_zoom_threshold: 0.00003,
stroke_texture_size: 1024, stroke_texture_size: 1024,
rdp_cache_threshold: 100,
}; };
const EVENT = Object.freeze({ const EVENT = Object.freeze({
@ -209,7 +210,11 @@ function main() {
'render_from': 0, 'render_from': 0,
'render_to': 0, 'render_to': 0,
'draw_bvh': false, 'draw_bvh': false,
} },
'rdp_cache': {},
'stats': {},
}; };
const context = { const context = {

67
client/math.js

@ -11,8 +11,16 @@ function screen_to_canvas(state, p) {
} }
function rdp_find_max(state, zoom, stroke, start, end) { function rdp_find_max(state, zoom, stroke, start, end) {
// Finds a point from the range [start, end) with the maximum distance from the line (start--end)
const EPS = 1.0 / zoom; const EPS = 1.0 / zoom;
// const EPS = 10.0; let cache_key = null;
if (end - start > config.rdp_cache_threshold) {
cache_key = stroke.index + '-' + zoom + '-' + start + '-' + end;
if (cache_key in state.rdp_cache) {
return state.rdp_cache[cache_key];
}
}
let result = -1; let result = -1;
let max_dist = 0; let max_dist = 0;
@ -26,23 +34,17 @@ function rdp_find_max(state, zoom, stroke, start, end) {
const dy = by - ay; const dy = by - ay;
const dist_ab = Math.sqrt(dx * dx + dy * dy); const dist_ab = Math.sqrt(dx * dx + dy * dy);
const sin_theta = dy / dist_ab; const dir_nx = dy / dist_ab;
const cos_theta = dx / dist_ab; const dir_ny = -dx / dist_ab;
for (let i = start; i < end; ++i) { for (let i = start + 1; i < end; ++i) {
const px = state.coordinates.data[stroke.coords_from + i * 2 + 0]; const px = state.coordinates.data[stroke.coords_from + i * 2 + 0];
const py = state.coordinates.data[stroke.coords_from + i * 2 + 1]; const py = state.coordinates.data[stroke.coords_from + i * 2 + 1];
const ox = px - ax; const apx = px - ax;
const oy = py - ay; const apy = py - ay;
const rx = cos_theta * ox + sin_theta * oy;
const ry = -sin_theta * ox + cos_theta * oy;
const x = rx + ax; const dist = Math.abs(apx * dir_nx + apy * dir_ny);
const y = ry + ay;
const dist = Math.abs(y - ay);
if (dist > EPS && dist > max_dist) { if (dist > EPS && dist > max_dist) {
result = i; result = i;
@ -50,19 +52,36 @@ function rdp_find_max(state, zoom, stroke, start, end) {
} }
} }
state.stats.rdp_max_count++;
state.stats.rdp_segments += end - start - 1;
if (end - start > config.rdp_cache_threshold) {
state.rdp_cache[cache_key] = result;
}
return result; return result;
} }
function process_rdp_indices_r(state, zoom, mask, stroke, start, end) { function process_rdp_indices_r(state, zoom, mask, stroke, start, end) {
// Looks like the recursive implementation spends most of its time in the function call overhead
// Let's try to use an explicit stack instead to give the js engine more room to play with
// Update: it's not faster. But it gives more sensible source-line samples in chrome profiler, so I'll leave it
let result = 0; let result = 0;
const stack = [];
stack.push({'start': start, 'end': end});
const max = rdp_find_max(state, zoom, stroke, start, end); while (stack.length > 0) {
const region = stack.pop();
const max = rdp_find_max(state, zoom, stroke, region.start, region.end);
if (max !== -1) { if (max !== -1) {
mask[max] = 1; mask[max] = 1;
result += 1; result += 1;
result += process_rdp_indices_r(state, zoom, mask, stroke, start, max); stack.push({'start': region.start, 'end': max});
result += process_rdp_indices_r(state, zoom, mask, stroke, max, end); stack.push({'start': max, 'end': region.end});
}
} }
return result; return result;
@ -103,8 +122,18 @@ function process_ewmv(points, round = false) {
} }
function process_stroke(state, zoom, stroke) { function process_stroke(state, zoom, stroke) {
// Try caching the highest zoom level that only returns the endpoints
if (zoom <= stroke.turns_into_straight_line_zoom) {
return 2;
}
// const result0 = process_ewmv(points); // const result0 = process_ewmv(points);
const result1 = process_rdp_indices(state, zoom, stroke, true); const result1 = process_rdp_indices(state, zoom, stroke, true);
if (result1 === 2 && zoom > stroke.turns_into_straight_line_zoom) {
stroke.turns_into_straight_line_zoom = zoom;
}
return result1; return result1;
} }

56
client/webgl_geometry.js

@ -40,8 +40,14 @@ function geometry_write_instances(state, context) {
context.instance_data = ser_ensure(context.instance_data, state.coordinates.count / 2 * config.bytes_per_instance); context.instance_data = ser_ensure(context.instance_data, state.coordinates.count / 2 * config.bytes_per_instance);
ser_clear(context.instance_data); ser_clear(context.instance_data);
state.stats.rdp_max_count = 0;
state.stats.rdp_segments = 0;
let segment_count = 0; let segment_count = 0;
let fast_path_count = 0;
let slow_path_count = 0;
for (let i = 0; i < context.clipped_indices.count; ++i) { for (let i = 0; i < context.clipped_indices.count; ++i) {
const stroke_index = context.clipped_indices.data[i]; const stroke_index = context.clipped_indices.data[i];
const stroke = state.events[stroke_index]; const stroke = state.events[stroke_index];
@ -49,29 +55,48 @@ function geometry_write_instances(state, context) {
segment_count += lod_indices_count - 1; segment_count += lod_indices_count - 1;
let base_this = 0; if (lod_indices_count === 2) {
let base_next = 0; fast_path_count++;
// Fast path
for (let j = 0; j < lod_indices_count - 1; ++j) { const ax = state.coordinates.data[stroke.coords_from + 0];
while (state.rdp_mask[base_this] == 0) base_this++; const ay = state.coordinates.data[stroke.coords_from + 1];
base_next = base_this + 1; const bx = state.coordinates.data[stroke.coords_to - 2];
while (state.rdp_mask[base_next] == 0) base_next++; const by = state.coordinates.data[stroke.coords_to - 1];
const ax = state.coordinates.data[stroke.coords_from + base_this * 2 + 0];
const ay = state.coordinates.data[stroke.coords_from + base_this * 2 + 1];
const bx = state.coordinates.data[stroke.coords_from + base_next * 2 + 0];
const by = state.coordinates.data[stroke.coords_from + base_next * 2 + 1];
ser_f32(context.instance_data, ax); ser_f32(context.instance_data, ax);
ser_f32(context.instance_data, ay); ser_f32(context.instance_data, ay);
ser_f32(context.instance_data, bx); ser_f32(context.instance_data, bx);
ser_f32(context.instance_data, by); ser_f32(context.instance_data, by);
ser_u32(context.instance_data, stroke_index); ser_u32(context.instance_data, stroke_index);
} else {
base_this = base_next; slow_path_count++;
let base_this = 0;
let base_next = 0;
for (let j = 0; j < lod_indices_count - 1; ++j) {
while (state.rdp_mask[base_this] == 0) base_this++;
base_next = base_this + 1;
while (state.rdp_mask[base_next] == 0) base_next++;
const ax = state.coordinates.data[stroke.coords_from + base_this * 2 + 0];
const ay = state.coordinates.data[stroke.coords_from + base_this * 2 + 1];
const bx = state.coordinates.data[stroke.coords_from + base_next * 2 + 0];
const by = state.coordinates.data[stroke.coords_from + base_next * 2 + 1];
ser_f32(context.instance_data, ax);
ser_f32(context.instance_data, ay);
ser_f32(context.instance_data, bx);
ser_f32(context.instance_data, by);
ser_u32(context.instance_data, stroke_index);
base_this = base_next;
}
} }
} }
console.debug('fast:', fast_path_count, 'slow:', slow_path_count);
console.debug('rdp max:', state.stats.rdp_max_count, 'rdp segments:', state.stats.rdp_segments);
return segment_count; return segment_count;
} }
@ -80,7 +105,8 @@ function geometry_add_stroke(state, context, stroke, stroke_index, skip_bvh = fa
stroke.bbox = stroke_bbox(state, stroke); stroke.bbox = stroke_bbox(state, stroke);
stroke.area = box_area(stroke.bbox); stroke.area = box_area(stroke.bbox);
stroke.turns_into_straight_line_zoom = -1;
context.stroke_data = ser_ensure_by(context.stroke_data, config.bytes_per_stroke); context.stroke_data = ser_ensure_by(context.stroke_data, config.bytes_per_stroke);
const color_u32 = stroke.color; const color_u32 = stroke.color;

Loading…
Cancel
Save