let colors = {}; let rasterized = {}; // https://stackoverflow.com/a/17243070 function hsv_to_rgb(h, s, v) { let r, g, b; const i = Math.floor(h * 6); const f = h * 6 - i; const p = v * (1 - s); const q = v * (1 - f * s); const t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v, g = t, b = p; break; case 1: r = q, g = v, b = p; break; case 2: r = p, g = v, b = t; break; case 3: r = p, g = q, b = v; break; case 4: r = t, g = p, b = v; break; case 5: r = v, g = p, b = q; break; } return [ Math.round(r * 255), Math.round(g * 255), Math.round(b * 255) ]; } function get_color(stage_name) { if (stage_name in config.predefined_colors) { return config.predefined_colors[stage_name]; } if (stage_name in colors) { return colors[stage_name]; } colors[stage_name] = hsv_to_rgb(Math.random(), 0.56, 0.56); return colors[stage_name]; } function rasterize_and_pack(text, cycles) { // TODO: handle texture is full or stuff don't fit (unlikely) const key = text; if (key in rasterized) { return rasterized[key]; } const u = raster_tile.x * config.w / config.raster_texture_size; const v = raster_tile.y * config.h / config.raster_texture_size; rasterize(text); gl.bindTexture(gl.TEXTURE_2D, textures['raster']); gl.texSubImage2D(gl.TEXTURE_2D, 0, raster_tile.x * config.w, raster_tile.y * config.h, config.w, config.h, gl.RGBA, gl.UNSIGNED_BYTE, c2d.canvas ); raster_tile.x += 1; if (raster_tile.x === config.raster_texture_size / config.w) { raster_tile.x = 0; raster_tile.y += 1; } rasterized[key] = [u, v]; if (cycles > 1) { gl.bindTexture(gl.TEXTURE_2D, textures['numbers']); for (let i = numbers_rasterized + 1; i <= cycles; ++i) { const u = number_tile.x * config.w / config.raster_texture_size; const v = number_tile.y * config.h / config.raster_texture_size; rasterize(i); gl.texSubImage2D(gl.TEXTURE_2D, 0, number_tile.x * config.w, number_tile.y * config.h, config.w, config.h, gl.RGBA, gl.UNSIGNED_BYTE, c2d.canvas ); number_tile.x += 1; if (number_tile.x === config.raster_texture_size / config.w) { number_tile.x = 0; number_tile.y += 1; } rasterized[i] = [u, v]; } if (cycles > numbers_rasterized) { numbers_rasterized = cycles; } } 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) { a = 80; } 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) { const before = performance.now(); 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]; // TODO: make all quads same size, abuse this fact 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 (config.show_texture) { 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(); console.log(`Generated geometry in ${Math.round(after - before)}ms`); return result; } function clip(quads) { if (config.show_texture) { return 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; }