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. 156
      geometry.js
  4. 7
      index.html
  5. 65
      input.js
  6. 22
      parse.js
  7. 29
      render.js

8
Caddyfile

@ -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 {
height: 128px; height: 128px;
z-index: 1; z-index: 1;
} }
.main .sidepanel {
position: absolute;
height: 100%;
width: 300px;
left: 0;
top: 0;
background: white;
}

156
geometry.js

@ -31,7 +31,7 @@ function rasterize_and_pack(text, cycles) {
let cycles_total_padding = (cycles - 1) * config.padding; let cycles_total_padding = (cycles - 1) * config.padding;
let bonus_cells = Math.ceil(cycles_total_padding / config.w); 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) { if (tiles_needed > config.raster_texture_size / config.w - raster_tile.x) {
raster_tile.x = 0; raster_tile.x = 0;
raster_tile.y += 1; raster_tile.y += 1;
@ -50,18 +50,56 @@ function rasterize_and_pack(text, cycles) {
); );
raster_tile.x += 1; raster_tile.x += 1;
/*
raster_tile.x += cycles + bonus_cells; raster_tile.x += cycles + bonus_cells;
if (raster_tile.x === config.raster_texture_size / config.w) { if (raster_tile.x === config.raster_texture_size / config.w) {
raster_tile.x = 0; raster_tile.x = 0;
raster_tile.y += 1; raster_tile.y += 1;
} }
*/
rasterized[key] = [u, v]; rasterized[key] = [u, v];
return [u, v]; return [u, v];
} }
function pack_instruction(instruction, positions, sizes, colors, uvs, starts, y) {
starts.push(positions.length);
for (let i = 0; i < instruction.lanes['0'].length; ++i) {
const stage = instruction.lanes['0'][i];
let stage_cycles;
if (i < instruction.lanes['0'].length - 1) {
const next_stage = instruction.lanes['0'][i + 1];
stage_cycles = next_stage.c - stage.c;
} else {
stage_cycles = instruction.retcyc - stage.c;
}
let [r, g, b] = get_color(stage.name);
let a = 255;
if (!instruction.retired) {
r = Math.max(50, r - 50);
g = Math.max(50, g - 50);
b = Math.max(50, b - 50);
a = 100;
}
const [u, v] = rasterize_and_pack(stage.name, stage_cycles);
sizes.push(stage_cycles * config.w + (stage_cycles - 1) * config.padding, 1 * config.h);
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) { function generate(trace_id) {
const before = performance.now(); const before = performance.now();
@ -73,6 +111,7 @@ function generate(trace_id) {
const sizes = []; const sizes = [];
const colors = []; const colors = [];
const uvs = []; const uvs = [];
const starts = [];
let instructions = {}; let instructions = {};
@ -82,57 +121,33 @@ function generate(trace_id) {
let y = 0; let y = 0;
for (const id in instructions) { for (let i = 0; i < instructions.length; ++i) {
const instruction = instructions[id]; const instruction = instructions[i];
for (let i = 0; i < instruction.lanes['0'].length; ++i) { result.count += pack_instruction(instruction, positions, sizes, colors, uvs, starts, y);
const stage = instruction.lanes['0'][i]; if (config.limit > 0 && result.count >= config.limit) {
let stage_cycles; break;
if (i < instruction.lanes['0'].length - 1) {
const next_stage = instruction.lanes['0'][i + 1];
stage_cycles = next_stage.c - stage.c;
} else {
stage_cycles = instruction.retcyc - stage.c;
}
let [r, g, b] = get_color(stage.name);
let a = 255;
if (!instruction.retired) {
r = Math.max(50, r - 50);
g = Math.max(50, g - 50);
b = Math.max(50, b - 50);
a = 100;
}
const [u, v] = rasterize_and_pack(stage.name, stage_cycles);
sizes.push(stage_cycles * config.w + (stage_cycles - 1) * config.padding, 1 * config.h);
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);
result.count++;
if (config.limit > 0 && result.count >= config.limit) {
break;
}
} }
++y; ++y;
} }
starts.push(positions.length);
if (false) { if (false) {
result.pos = new Float32Array([0, 0]); result.pos = new Float32Array([0, 0]);
result.size = new Float32Array([config.raster_texture_size, config.raster_texture_size]); result.size = new Float32Array([config.raster_texture_size, config.raster_texture_size]);
result.color = new Uint8Array([0, 0, 0, 255]); result.color = new Uint8Array([0, 0, 0, 255]);
result.uv = new Float32Array([0, 0]); result.uv = new Float32Array([0, 0]);
result.count = 1; result.count = 1;
result.trace_id = trace_id;
result.uploaded = false;
} else { } else {
result.pos = new Float32Array(positions); result.pos = new Float32Array(positions);
result.size = new Float32Array(sizes); result.size = new Float32Array(sizes);
result.color = new Uint8Array(colors); result.color = new Uint8Array(colors);
result.uv = new Float32Array(uvs); result.uv = new Float32Array(uvs);
result.trace_id = trace_id;
result.uploaded = false;
result.start = new Int32Array(starts);
} }
const after = performance.now(); const after = performance.now();
@ -141,3 +156,70 @@ function generate(trace_id) {
return result; 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 @@
<script type="text/javascript" src="input.js"></script> <script type="text/javascript" src="input.js"></script>
<script type="text/javascript" src="geometry.js"></script> <script type="text/javascript" src="geometry.js"></script>
<script type="text/javascript" src="rasterizer.js"></script> <script type="text/javascript" src="rasterizer.js"></script>
<script type="text/javascript" src="math.js"></script>
</head> </head>
<body> <body>
<div class="main"> <div class="main">
<div class="sidepanel">
<label>
Upload trace
<input type="file" id="file-input">
</label>
</div>
<canvas id="c"></canvas> <canvas id="c"></canvas>
<canvas id="offscreen" width="32" height="32"></canvas> <canvas id="offscreen" width="32" height="32"></canvas>
</div> </div>

65
input.js

@ -9,6 +9,8 @@ function init_listeners() {
document.querySelector('#c').addEventListener('mouseleave', mouseleave); document.querySelector('#c').addEventListener('mouseleave', mouseleave);
document.querySelector('#c').addEventListener('wheel', wheel); document.querySelector('#c').addEventListener('wheel', wheel);
document.querySelector('#file-input').addEventListener('change', form_upload);
window.addEventListener('keydown', keydown); window.addEventListener('keydown', keydown);
window.addEventListener('keyup', keyup); window.addEventListener('keyup', keyup);
} }
@ -23,7 +25,7 @@ function cancel(e) {
} }
function mousedown(e) { function mousedown(e) {
if (e.button === 1) { if (e.button === 1 || (e.button === 0 && spacedown)) {
moving = true; moving = true;
return; return;
} }
@ -50,7 +52,7 @@ function mouseleave(e) {
} }
function mouseup(e) { function mouseup(e) {
if (e.button === 1 && moving) { if ((e.button === 1 || e.button === 0 && spacedown) && moving) {
moving = false; moving = false;
} }
} }
@ -58,9 +60,13 @@ function mouseup(e) {
function wheel(e) { function wheel(e) {
const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY}; const screenp = {'x': window.devicePixelRatio * e.clientX, 'y': window.devicePixelRatio * e.clientY};
const zooming_in = e.deltaY < 0; 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); 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_level = level;
zoom_target = Math.pow(1.0 + dz, Math.abs(zoom_level)) zoom_target = Math.pow(1.0 + dz, Math.abs(zoom_level))
zoom_screenp = screenp; zoom_screenp = screenp;
@ -70,36 +76,33 @@ function wheel(e) {
function jump_to_first_instruction() { function jump_to_first_instruction() {
if (Object.keys(traces).length > 0) { if (Object.keys(traces).length > 0) {
const trace = traces[Object.keys(traces)[0]].raw; const quads = traces[Object.keys(traces)[0]].geo;
if (Object.keys(trace).length > 0) { offset.x = -quads.pos[0] + config.padding * 2;
const first_instruction = trace[Object.keys(trace)[0]]; offset.y = quads.pos[1] + config.padding * 2;
offset.x = -first_instruction.cycle * config.w + config.padding * 2; zoom_target = 1;
offset.y = config.padding * 2; zoom = 1;
zoom_target = 1; schedule_draw();
zoom = 1;
schedule_draw();
}
} }
} }
function keydown(e) { function keydown(e) {
if (e.code === 'Digit0') { if (e.code === 'Digit0') {
jump_to_first_instruction(); jump_to_first_instruction();
} else if (e.code === 'Space') {
spacedown = true;
} }
} }
function keyup(e) { function keyup(e) {
} if (e.code === 'Space') {
spacedown = false;
function drop(e) { if (moving) {
e.preventDefault(); moving = false;
}
if (e.dataTransfer.files.length !== 1) {
console.error('Only one file at once, please!');
return false;
} }
}
const file = e.dataTransfer.files[0]; function upload_file(file) {
const fr = new FileReader(); const fr = new FileReader();
const upload_failed = () => { const upload_failed = () => {
@ -136,3 +139,23 @@ function drop(e) {
fr.readAsText(file); 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) {
for (let i = 0; i < text.length; ++i) { for (let i = 0; i < text.length; ++i) {
if (text[i] === '\n') { if (text[i] === '\n') {
if (line_index === 0) {
line_start = i + 1;
line_index += 1;
continue;
}
// TODO: speed // TODO: speed
const line_copy = text.substring(line_start, i); const line_copy = text.substring(line_start, i);
const line_parts = line_copy.split(/\t/); const line_parts = line_copy.split(/\t/);
if (line_parts.length === 0) { if (line_parts.length === 0) {
console.error('Parser error: empty line'); console.error('Parser error: empty line');
return false;
} }
const command = line_parts[0]; const command = line_parts[0];
@ -64,7 +57,7 @@ function parse(text) {
'thread_id': thread_id, 'thread_id': thread_id,
'text': '', 'text': '',
'popover_text': '', 'popover_text': '',
'lanes': {}, 'lanes': {'0': [], '1': []},
}; };
break; break;
@ -85,7 +78,6 @@ function parse(text) {
} }
} else { } else {
console.error('Parser error: label for an instruction that does not exist'); console.error('Parser error: label for an instruction that does not exist');
return false;
} }
break; break;
@ -110,7 +102,6 @@ function parse(text) {
}); });
} else { } else {
console.error('Parser error: pipeline start for an instruction that does not exist'); console.error('Parser error: pipeline start for an instruction that does not exist');
return false;
} }
@ -145,7 +136,6 @@ function parse(text) {
instructions[id].retcyc = c; instructions[id].retcyc = c;
} else { } else {
console.error('Parser error: retire for an instruction that does not exist'); console.error('Parser error: retire for an instruction that does not exist');
return false;
} }
break; break;
@ -165,7 +155,6 @@ function parse(text) {
default: { default: {
console.error('Parser error: unexpected command', command); console.error('Parser error: unexpected command', command);
return false;
} }
} }
@ -174,17 +163,18 @@ function parse(text) {
} }
} }
const after = performance.now();
console.log(`Parsed in ${Math.round(after - before)}ms`);
traces['0'] = { 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'); traces['0'].geo = generate('0');
const after = performance.now();
console.log(`Parsed in ${Math.round(after - before)}ms`);
return true; return true;
} }

29
render.js

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

Loading…
Cancel
Save