Browse Source

A half-baked CSR that kinda works to clip kinda quickly

master
Aleksey Olokhtonov 4 months ago
parent
commit
798dd5072d
  1. 8
      Caddyfile
  2. 9
      default.css
  3. 132
      geometry.js
  4. 7
      index.html
  5. 59
      input.js
  6. 22
      parse.js
  7. 29
      render.js

8
Caddyfile

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
localhost {
header {
Cross-Origin-Opener-Policy same-origin
Cross-Origin-Embedder-Policy require-corp
}
root * /home/aolo2/code/nitka
file_server
}

9
default.css

@ -24,3 +24,12 @@ body .main { @@ -24,3 +24,12 @@ body .main {
height: 128px;
z-index: 1;
}
.main .sidepanel {
position: absolute;
height: 100%;
width: 300px;
left: 0;
top: 0;
background: white;
}

132
geometry.js

@ -31,7 +31,7 @@ function rasterize_and_pack(text, cycles) { @@ -31,7 +31,7 @@ function rasterize_and_pack(text, cycles) {
let cycles_total_padding = (cycles - 1) * config.padding;
let bonus_cells = Math.ceil(cycles_total_padding / config.w);
const tiles_needed = cycles - 1 + 1 + bonus_cells; // stage name + count cycles from one
const tiles_needed = 1; // cycles - 1 + 1 + bonus_cells; // stage name + count cycles from one
if (tiles_needed > config.raster_texture_size / config.w - raster_tile.x) {
raster_tile.x = 0;
raster_tile.y += 1;
@ -50,40 +50,24 @@ function rasterize_and_pack(text, cycles) { @@ -50,40 +50,24 @@ function rasterize_and_pack(text, cycles) {
);
raster_tile.x += 1;
/*
raster_tile.x += cycles + bonus_cells;
if (raster_tile.x === config.raster_texture_size / config.w) {
raster_tile.x = 0;
raster_tile.y += 1;
}
*/
rasterized[key] = [u, v];
return [u, v];
}
function generate(trace_id) {
const before = performance.now();
const result = {
'count': 0,
};
const positions = [];
const sizes = [];
const colors = [];
const uvs = [];
let instructions = {};
if (trace_id in traces) {
instructions = traces[trace_id].raw;
}
let y = 0;
function pack_instruction(instruction, positions, sizes, colors, uvs, starts, y) {
starts.push(positions.length);
for (const id in instructions) {
const instruction = instructions[id];
for (let i = 0; i < instruction.lanes['0'].length; ++i) {
const stage = instruction.lanes['0'][i];
let stage_cycles;
@ -111,28 +95,59 @@ function generate(trace_id) { @@ -111,28 +95,59 @@ function generate(trace_id) {
positions.push(config.w * stage.c + config.padding * (stage.c - 1), config.h * y + config.padding * (y - 1));
colors.push(r, g, b, a);
uvs.push(u, v);
}
return instruction.lanes['0'].length;
}
function generate(trace_id) {
const before = performance.now();
result.count++;
const result = {
'count': 0,
};
const positions = [];
const sizes = [];
const colors = [];
const uvs = [];
const starts = [];
let instructions = {};
if (trace_id in traces) {
instructions = traces[trace_id].raw;
}
let y = 0;
for (let i = 0; i < instructions.length; ++i) {
const instruction = instructions[i];
result.count += pack_instruction(instruction, positions, sizes, colors, uvs, starts, y);
if (config.limit > 0 && result.count >= config.limit) {
break;
}
}
++y;
}
starts.push(positions.length);
if (false) {
result.pos = new Float32Array([0, 0]);
result.size = new Float32Array([config.raster_texture_size, config.raster_texture_size]);
result.color = new Uint8Array([0, 0, 0, 255]);
result.uv = new Float32Array([0, 0]);
result.count = 1;
result.trace_id = trace_id;
result.uploaded = false;
} else {
result.pos = new Float32Array(positions);
result.size = new Float32Array(sizes);
result.color = new Uint8Array(colors);
result.uv = new Float32Array(uvs);
result.trace_id = trace_id;
result.uploaded = false;
result.start = new Int32Array(starts);
}
const after = performance.now();
@ -141,3 +156,70 @@ function generate(trace_id) { @@ -141,3 +156,70 @@ function generate(trace_id) {
return result;
}
function clip(quads) {
const tl = screen_to_canvas({'x': 0, 'y': 0});
const br = screen_to_canvas({'x': 0, 'y': canvas.height});
const x1 = tl.x;
const y1 = tl.y;
const x2 = br.x;
const y2 = br.y;
const result = {
'count': 0,
};
if (quads.count === 0) {
return result;
}
let i0 = -1;
let i1 = -1;
for (let i = 0; i < quads.start.length - 1; ++i) {
const index = quads.start[i];
const next = quads.start[i + 1];
const left = quads.pos[index];
const top = quads.pos[index + 1];
const bottom = top + quads.size[index + 1];
const right = quads.pos[next - 2] + quads.size[next - 2];
if (bottom < y1) {
if (i < quads.start.length / 2 - 2) {
const index_ahead = quads.start[i * 2];
const top_ahead = quads.pos[index_ahead + 1];
const bottom_ahead = top_ahead + quads.size[index_ahead + 1];
if (bottom_ahead < y1) {
i *= 2;
continue;
}
}
}
if (bottom < y1 || right < x1) {
continue;
}
if (top > y2) {
i1 = quads.start[i + 1];
break;
}
if (i0 === -1) {
i0 = index;
}
}
result.pos = quads.pos.subarray(i0, i1);
result.size = quads.size.subarray(i0, i1);
result.color = quads.color.subarray(i0 * 2, i1 * 2);
result.uv = quads.uv.subarray(i0, i1);
result.count = (i1 - i0) / 2;
result.uploaded = false;
return result;
}

7
index.html

@ -17,9 +17,16 @@ @@ -17,9 +17,16 @@
<script type="text/javascript" src="input.js"></script>
<script type="text/javascript" src="geometry.js"></script>
<script type="text/javascript" src="rasterizer.js"></script>
<script type="text/javascript" src="math.js"></script>
</head>
<body>
<div class="main">
<div class="sidepanel">
<label>
Upload trace
<input type="file" id="file-input">
</label>
</div>
<canvas id="c"></canvas>
<canvas id="offscreen" width="32" height="32"></canvas>
</div>

59
input.js

@ -9,6 +9,8 @@ function init_listeners() { @@ -9,6 +9,8 @@ function init_listeners() {
document.querySelector('#c').addEventListener('mouseleave', mouseleave);
document.querySelector('#c').addEventListener('wheel', wheel);
document.querySelector('#file-input').addEventListener('change', form_upload);
window.addEventListener('keydown', keydown);
window.addEventListener('keyup', keyup);
}
@ -23,7 +25,7 @@ function cancel(e) { @@ -23,7 +25,7 @@ function cancel(e) {
}
function mousedown(e) {
if (e.button === 1) {
if (e.button === 1 || (e.button === 0 && spacedown)) {
moving = true;
return;
}
@ -50,7 +52,7 @@ function mouseleave(e) { @@ -50,7 +52,7 @@ function mouseleave(e) {
}
function mouseup(e) {
if (e.button === 1 && moving) {
if ((e.button === 1 || e.button === 0 && spacedown) && moving) {
moving = false;
}
}
@ -58,9 +60,13 @@ function mouseup(e) { @@ -58,9 +60,13 @@ function mouseup(e) {
function wheel(e) {
const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY};
const zooming_in = e.deltaY < 0;
const level = zooming_in ? zoom_level + 2 : zoom_level - 2;
let level = zooming_in ? zoom_level + 2 : zoom_level - 2;
const dz = (level > 0 ? config.zoom_delta : -config.zoom_delta);
if (level > config.max_zoom_level) {
level = config.max_zoom_level;
}
zoom_level = level;
zoom_target = Math.pow(1.0 + dz, Math.abs(zoom_level))
zoom_screenp = screenp;
@ -70,36 +76,33 @@ function wheel(e) { @@ -70,36 +76,33 @@ function wheel(e) {
function jump_to_first_instruction() {
if (Object.keys(traces).length > 0) {
const trace = traces[Object.keys(traces)[0]].raw;
if (Object.keys(trace).length > 0) {
const first_instruction = trace[Object.keys(trace)[0]];
offset.x = -first_instruction.cycle * config.w + config.padding * 2;
offset.y = config.padding * 2;
const quads = traces[Object.keys(traces)[0]].geo;
offset.x = -quads.pos[0] + config.padding * 2;
offset.y = quads.pos[1] + config.padding * 2;
zoom_target = 1;
zoom = 1;
schedule_draw();
}
}
}
function keydown(e) {
if (e.code === 'Digit0') {
jump_to_first_instruction();
} else if (e.code === 'Space') {
spacedown = true;
}
}
function keyup(e) {
}
function drop(e) {
e.preventDefault();
if (e.dataTransfer.files.length !== 1) {
console.error('Only one file at once, please!');
return false;
if (e.code === 'Space') {
spacedown = false;
if (moving) {
moving = false;
}
}
}
const file = e.dataTransfer.files[0];
function upload_file(file) {
const fr = new FileReader();
const upload_failed = () => {
@ -136,3 +139,23 @@ function drop(e) { @@ -136,3 +139,23 @@ function drop(e) {
fr.readAsText(file);
}
function form_upload(e) {
if (e.target.files.length === 1) {
upload_file(e.target.files[0]);
} else if (e.target.files > 1) {
console.error('Only one file at a time for now!');
}
}
function drop(e) {
e.preventDefault();
if (e.dataTransfer.files.length !== 1) {
console.error('Only one file at once, please!');
return false;
}
const file = e.dataTransfer.files[0];
upload_file(file);
}

22
parse.js

@ -13,19 +13,12 @@ function parse(text) { @@ -13,19 +13,12 @@ function parse(text) {
for (let i = 0; i < text.length; ++i) {
if (text[i] === '\n') {
if (line_index === 0) {
line_start = i + 1;
line_index += 1;
continue;
}
// TODO: speed
const line_copy = text.substring(line_start, i);
const line_parts = line_copy.split(/\t/);
if (line_parts.length === 0) {
console.error('Parser error: empty line');
return false;
}
const command = line_parts[0];
@ -64,7 +57,7 @@ function parse(text) { @@ -64,7 +57,7 @@ function parse(text) {
'thread_id': thread_id,
'text': '',
'popover_text': '',
'lanes': {},
'lanes': {'0': [], '1': []},
};
break;
@ -85,7 +78,6 @@ function parse(text) { @@ -85,7 +78,6 @@ function parse(text) {
}
} else {
console.error('Parser error: label for an instruction that does not exist');
return false;
}
break;
@ -110,7 +102,6 @@ function parse(text) { @@ -110,7 +102,6 @@ function parse(text) {
});
} else {
console.error('Parser error: pipeline start for an instruction that does not exist');
return false;
}
@ -145,7 +136,6 @@ function parse(text) { @@ -145,7 +136,6 @@ function parse(text) {
instructions[id].retcyc = c;
} else {
console.error('Parser error: retire for an instruction that does not exist');
return false;
}
break;
@ -165,7 +155,6 @@ function parse(text) { @@ -165,7 +155,6 @@ function parse(text) {
default: {
console.error('Parser error: unexpected command', command);
return false;
}
}
@ -174,17 +163,18 @@ function parse(text) { @@ -174,17 +163,18 @@ function parse(text) {
}
}
const after = performance.now();
console.log(`Parsed in ${Math.round(after - before)}ms`);
traces['0'] = {
'raw': instructions,
// The fact these are sorted is used extensively over the code
'raw': Object.values(instructions).sort((a, b) => a.cycle - b.cycle),
};
traces['0'].geo = generate('0');
const after = performance.now();
console.log(`Parsed in ${Math.round(after - before)}ms`);
return true;
}

29
render.js

@ -28,6 +28,7 @@ let config = { @@ -28,6 +28,7 @@ let config = {
limit: -1,
zoom_delta: 0.05,
raster_texture_size: 4096,
max_zoom_level: 0,
};
let canvas = null;
@ -42,6 +43,7 @@ let zoom_screenp = { 'x': 0, 'y': 0 }; @@ -42,6 +43,7 @@ let zoom_screenp = { 'x': 0, 'y': 0 };
let last_frame_dt = 0;
let last_frame_ts = 0;
let raster_tile = { 'x': 0, 'y': 0 };
let spacedown = false;
const tquad_vs_src = `#version 300 es
in vec2 a_pos;
@ -149,17 +151,24 @@ function draw(ts, animation) { @@ -149,17 +151,24 @@ function draw(ts, animation) {
quads = traces['0'].geo;
}
if (quads.count > 0) {
const clipped = clip(quads);
if (clipped.count > 0) {
const program = programs['quad'];
const fade = Math.max(0, Math.min(1.25 * zoom - 0.25, 1));
gl.useProgram(program.program);
gl.bindBuffer(gl.ARRAY_BUFFER, buffers['b_packed']);
gl.bufferData(gl.ARRAY_BUFFER, quads.count * config.bytes_per_quad, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, quads.pos);
gl.bufferSubData(gl.ARRAY_BUFFER, quads.pos.byteLength, quads.size);
gl.bufferSubData(gl.ARRAY_BUFFER, quads.pos.byteLength + quads.size.byteLength, quads.color);
gl.bufferSubData(gl.ARRAY_BUFFER, quads.pos.byteLength + quads.size.byteLength + quads.color.byteLength, quads.uv);
if (!clipped.uploaded) {
gl.bufferData(gl.ARRAY_BUFFER, clipped.count * config.bytes_per_quad, gl.STATIC_DRAW);
gl.bufferSubData(gl.ARRAY_BUFFER, 0, clipped.pos);
gl.bufferSubData(gl.ARRAY_BUFFER, clipped.pos.byteLength, clipped.size);
gl.bufferSubData(gl.ARRAY_BUFFER, clipped.pos.byteLength + clipped.size.byteLength, clipped.color);
gl.bufferSubData(gl.ARRAY_BUFFER, clipped.pos.byteLength + clipped.size.byteLength + clipped.color.byteLength, clipped.uv);
clipped.uploaded = true;
}
gl.uniform2f(program.locations['u_res'], canvas.width, canvas.height);
gl.uniform2f(program.locations['u_translation'], offset.x, offset.y);
@ -175,9 +184,9 @@ function draw(ts, animation) { @@ -175,9 +184,9 @@ function draw(ts, animation) {
gl.enableVertexAttribArray(program.locations['a_uv']);
gl.vertexAttribPointer(program.locations['a_pos'], 2, gl.FLOAT, false, 2 * 4, 0);
gl.vertexAttribPointer(program.locations['a_size'], 2, gl.FLOAT, false, 2 * 4, quads.pos.byteLength);
gl.vertexAttribPointer(program.locations['a_color'], 4, gl.UNSIGNED_BYTE, true, 4 * 1, quads.pos.byteLength + quads.size.byteLength);
gl.vertexAttribPointer(program.locations['a_uv'], 2, gl.FLOAT, false, 2 * 4, quads.pos.byteLength + quads.size.byteLength + quads.color.byteLength);
gl.vertexAttribPointer(program.locations['a_size'], 2, gl.FLOAT, false, 2 * 4, clipped.pos.byteLength);
gl.vertexAttribPointer(program.locations['a_color'], 4, gl.UNSIGNED_BYTE, true, 4 * 1, clipped.pos.byteLength + clipped.size.byteLength);
gl.vertexAttribPointer(program.locations['a_uv'], 2, gl.FLOAT, false, 2 * 4, clipped.pos.byteLength + clipped.size.byteLength + clipped.color.byteLength);
gl.vertexAttribDivisor(program.locations['a_pos'], 1);
gl.vertexAttribDivisor(program.locations['a_size'], 1);
@ -185,7 +194,7 @@ function draw(ts, animation) { @@ -185,7 +194,7 @@ function draw(ts, animation) {
gl.vertexAttribDivisor(program.locations['a_uv'], 1);
gl.bindTexture(gl.TEXTURE_2D, textures['raster']);
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, quads.count);
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, clipped.count);
gl.vertexAttribDivisor(program.locations['a_pos'], 0);
gl.vertexAttribDivisor(program.locations['a_size'], 0);

Loading…
Cancel
Save