A.Olokhtonov
4 months ago
7 changed files with 427 additions and 18 deletions
@ -0,0 +1,65 @@ |
|||||||
|
let colors = {}; |
||||||
|
|
||||||
|
function get_color(stage_name) { |
||||||
|
if (stage_name in colors) { |
||||||
|
return colors[stage_name]; |
||||||
|
} |
||||||
|
|
||||||
|
const r = Math.floor(Math.random() * 155); |
||||||
|
const g = Math.floor(Math.random() * 155); |
||||||
|
const b = Math.floor(Math.random() * 155); |
||||||
|
|
||||||
|
colors[stage_name] = { 'r': 100 + r, 'g': 100 + g, 'b': 100 + b }; |
||||||
|
|
||||||
|
return colors[stage_name]; |
||||||
|
} |
||||||
|
|
||||||
|
function generate(trace_id) { |
||||||
|
const result = { |
||||||
|
'count': 0, |
||||||
|
}; |
||||||
|
|
||||||
|
const positions = []; |
||||||
|
const sizes = []; |
||||||
|
const colors = []; |
||||||
|
|
||||||
|
let instructions = []; |
||||||
|
|
||||||
|
if (trace_id in traces) { |
||||||
|
instructions = traces[trace_id]; |
||||||
|
} |
||||||
|
|
||||||
|
console.log(instructions); |
||||||
|
|
||||||
|
let y = 0; |
||||||
|
|
||||||
|
for (const instruction of instructions) { |
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
const color = get_color(stage.name); |
||||||
|
|
||||||
|
sizes.push(stage_cycles * config.w, 1 * config.h); |
||||||
|
positions.push(config.w * stage.c, config.h * y); |
||||||
|
colors.push(color.r, color.g, color.b, 255); |
||||||
|
|
||||||
|
result.count++; |
||||||
|
} |
||||||
|
|
||||||
|
++y; |
||||||
|
} |
||||||
|
|
||||||
|
result.pos = new Float32Array(positions); |
||||||
|
result.size = new Float32Array(sizes); |
||||||
|
result.color = new Uint8Array(colors); |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
@ -1,5 +1,8 @@ |
|||||||
document.addEventListener('DOMContentLoaded', main); |
document.addEventListener('DOMContentLoaded', main); |
||||||
|
|
||||||
|
let traces = {}; |
||||||
|
|
||||||
function main() { |
function main() { |
||||||
|
init_webgl(); |
||||||
init_listeners(); |
init_listeners(); |
||||||
} |
} |
||||||
|
@ -0,0 +1,250 @@ |
|||||||
|
let programs = {}; |
||||||
|
let buffers = {}; |
||||||
|
let timers = {}; |
||||||
|
let config = { |
||||||
|
bytes_per_quad: 20, |
||||||
|
w: 24, |
||||||
|
h: 24, |
||||||
|
}; |
||||||
|
|
||||||
|
let canvas = null; |
||||||
|
let gl = null; |
||||||
|
let gpu_timer_ext = null; |
||||||
|
let offset = { x: 0, y: 0 }; |
||||||
|
let scale = 1; |
||||||
|
|
||||||
|
const tquad_vs_src = `#version 300 es
|
||||||
|
in vec2 a_pos; |
||||||
|
in vec2 a_size; |
||||||
|
in vec4 a_color; |
||||||
|
|
||||||
|
uniform vec2 u_res; |
||||||
|
uniform vec2 u_translation; |
||||||
|
uniform float u_scale; |
||||||
|
|
||||||
|
out vec4 v_color; |
||||||
|
|
||||||
|
void main() { |
||||||
|
int vertex_index = gl_VertexID % 6; |
||||||
|
vec2 corner; |
||||||
|
|
||||||
|
if (vertex_index == 0) { |
||||||
|
// "top left" aka "p1"
|
||||||
|
corner = a_pos + vec2(1.0); |
||||||
|
} else if (vertex_index == 1 || vertex_index == 5) { |
||||||
|
// "top right" aka "p2"
|
||||||
|
corner = a_pos + vec2(a_size.x - 1.0, 1.0); |
||||||
|
} else if (vertex_index == 2 || vertex_index == 4) { |
||||||
|
// "bottom left" aka "p3"
|
||||||
|
corner = a_pos + vec2(1.0, a_size.y - 1.0); |
||||||
|
} else { |
||||||
|
// "bottom right" aka "p4"
|
||||||
|
corner = a_pos + a_size - vec2(1.0); |
||||||
|
} |
||||||
|
|
||||||
|
vec2 screen02 = (corner.xy * vec2(u_scale) + u_translation) / u_res * 2.0; |
||||||
|
screen02.y = 2.0 - screen02.y; |
||||||
|
v_color = a_color; |
||||||
|
|
||||||
|
gl_Position = vec4(screen02 - 1.0, 1.0, 1.0); |
||||||
|
} |
||||||
|
`;
|
||||||
|
|
||||||
|
const tquad_fs_src = `#version 300 es
|
||||||
|
precision highp float; |
||||||
|
|
||||||
|
in vec4 v_color; |
||||||
|
|
||||||
|
layout(location = 0) out vec4 FragColor; |
||||||
|
|
||||||
|
void main() { |
||||||
|
FragColor = v_color; |
||||||
|
} |
||||||
|
`;
|
||||||
|
|
||||||
|
function schedule_draw() { |
||||||
|
if (!timers.raf) { |
||||||
|
window.requestAnimationFrame(draw); |
||||||
|
timers.raf = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function draw() { |
||||||
|
const cpu_before = performance.now(); |
||||||
|
const width = window.innerWidth; |
||||||
|
const height = window.innerHeight; |
||||||
|
|
||||||
|
let query = null; |
||||||
|
|
||||||
|
if (gpu_timer_ext !== null) { |
||||||
|
query = gl.createQuery(); |
||||||
|
gl.beginQuery(gpu_timer_ext.TIME_ELAPSED_EXT, query); |
||||||
|
} |
||||||
|
|
||||||
|
gl.viewport(0, 0, canvas.width, canvas.height); |
||||||
|
gl.clearColor(0, 0, 0, 1); |
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT); |
||||||
|
|
||||||
|
const quads = generate('0'); |
||||||
|
|
||||||
|
if (quads.count > 0) { |
||||||
|
const program = programs['quad']; |
||||||
|
|
||||||
|
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.uniform2f(program.locations['u_res'], canvas.width, canvas.height); |
||||||
|
gl.uniform2f(program.locations['u_translation'], offset.x, offset.y); |
||||||
|
gl.uniform1f(program.locations['u_scale'], scale); |
||||||
|
|
||||||
|
gl.enableVertexAttribArray(program.locations['a_pos']); |
||||||
|
gl.enableVertexAttribArray(program.locations['a_size']); |
||||||
|
gl.enableVertexAttribArray(program.locations['a_color']); |
||||||
|
|
||||||
|
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.vertexAttribDivisor(program.locations['a_pos'], 1); |
||||||
|
gl.vertexAttribDivisor(program.locations['a_size'], 1); |
||||||
|
gl.vertexAttribDivisor(program.locations['a_color'], 1); |
||||||
|
|
||||||
|
gl.drawArraysInstanced(gl.TRIANGLES, 0, 6, quads.count); |
||||||
|
|
||||||
|
gl.vertexAttribDivisor(program.locations['a_pos'], 0); |
||||||
|
gl.vertexAttribDivisor(program.locations['a_size'], 0); |
||||||
|
gl.vertexAttribDivisor(program.locations['a_color'], 0); |
||||||
|
} |
||||||
|
|
||||||
|
if (gpu_timer_ext) { |
||||||
|
gl.endQuery(gpu_timer_ext.TIME_ELAPSED_EXT); |
||||||
|
|
||||||
|
const next_tick = () => { |
||||||
|
if (query) { |
||||||
|
const available = gl.getQueryParameter(query, gl.QUERY_RESULT_AVAILABLE); |
||||||
|
const disjoint = gl.getParameter(gpu_timer_ext.GPU_DISJOINT_EXT); |
||||||
|
|
||||||
|
if (available && !disjoint) { |
||||||
|
const timeElapsed = gl.getQueryParameter(query, gl.QUERY_RESULT); |
||||||
|
console.log('Last GPU Frametime: ' + Math.round(timeElapsed / 10000) / 100 + 'ms'); |
||||||
|
} |
||||||
|
|
||||||
|
if (available || disjoint) { |
||||||
|
gl.deleteQuery(query); |
||||||
|
query = null; |
||||||
|
} else if (!available) { |
||||||
|
setTimeout(next_tick, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
setTimeout(next_tick, 0); |
||||||
|
} |
||||||
|
|
||||||
|
const cpu_after = performance.now(); |
||||||
|
|
||||||
|
timers.raf = false; |
||||||
|
|
||||||
|
console.log('Last CPU Frametime: ' + Math.round((cpu_after - cpu_before) * 100) / 100 + 'ms'); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
function init_webgl() { |
||||||
|
canvas = document.querySelector('#c'); |
||||||
|
gl = canvas.getContext('webgl2'); |
||||||
|
|
||||||
|
gpu_timer_ext = gl.getExtension('EXT_disjoint_timer_query_webgl2'); |
||||||
|
if (gpu_timer_ext === null) { |
||||||
|
gpu_timer_ext = gl.getExtension('EXT_disjoint_timer_query'); |
||||||
|
} |
||||||
|
|
||||||
|
const quad_vs = create_shader(gl, gl.VERTEX_SHADER, tquad_vs_src); |
||||||
|
const quad_fs = create_shader(gl, gl.FRAGMENT_SHADER, tquad_fs_src); |
||||||
|
|
||||||
|
programs = { |
||||||
|
'quad': create_program(gl, quad_vs, quad_fs), |
||||||
|
}; |
||||||
|
|
||||||
|
buffers = { |
||||||
|
'b_packed': gl.createBuffer(), |
||||||
|
}; |
||||||
|
|
||||||
|
const resize_canvas = (entries) => { |
||||||
|
// https://www.khronos.org/webgl/wiki/HandlingHighDPI
|
||||||
|
const entry = entries[0]; |
||||||
|
|
||||||
|
let width; |
||||||
|
let height; |
||||||
|
|
||||||
|
if (entry.devicePixelContentBoxSize) { |
||||||
|
width = entry.devicePixelContentBoxSize[0].inlineSize; |
||||||
|
height = entry.devicePixelContentBoxSize[0].blockSize; |
||||||
|
} else if (entry.contentBoxSize) { |
||||||
|
// fallback for Safari that will not always be correct
|
||||||
|
width = Math.round(entry.contentBoxSize[0].inlineSize * devicePixelRatio); |
||||||
|
height = Math.round(entry.contentBoxSize[0].blockSize * devicePixelRatio); |
||||||
|
} |
||||||
|
|
||||||
|
canvas.width = width; |
||||||
|
canvas.height = height; |
||||||
|
|
||||||
|
schedule_draw(); |
||||||
|
} |
||||||
|
|
||||||
|
const resize_observer = new ResizeObserver(resize_canvas); |
||||||
|
resize_observer.observe(canvas); |
||||||
|
} |
||||||
|
|
||||||
|
function create_shader(gl, type, source) { |
||||||
|
const shader = gl.createShader(type); |
||||||
|
|
||||||
|
gl.shaderSource(shader, source); |
||||||
|
gl.compileShader(shader); |
||||||
|
|
||||||
|
if (gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { |
||||||
|
return shader; |
||||||
|
} |
||||||
|
|
||||||
|
console.error(type, ':', gl.getShaderInfoLog(shader)); |
||||||
|
|
||||||
|
gl.deleteShader(shader); |
||||||
|
} |
||||||
|
|
||||||
|
function create_program(gl, vs, fs) { |
||||||
|
const program = gl.createProgram(); |
||||||
|
|
||||||
|
gl.attachShader(program, vs); |
||||||
|
gl.attachShader(program, fs); |
||||||
|
gl.linkProgram(program); |
||||||
|
|
||||||
|
if (gl.getProgramParameter(program, gl.LINK_STATUS)) { |
||||||
|
// src: tiny-sdf
|
||||||
|
// https://github.com/mapbox/tiny-sdf
|
||||||
|
|
||||||
|
const wrapper = {program}; |
||||||
|
const num_attrs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); |
||||||
|
const num_uniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); |
||||||
|
|
||||||
|
wrapper.locations = {}; |
||||||
|
|
||||||
|
for (let i = 0; i < num_attrs; i++) { |
||||||
|
const attribute = gl.getActiveAttrib(program, i); |
||||||
|
wrapper.locations[attribute.name] = gl.getAttribLocation(program, attribute.name); |
||||||
|
} |
||||||
|
|
||||||
|
for (let i = 0; i < num_uniforms; i++) { |
||||||
|
const uniform = gl.getActiveUniform(program, i); |
||||||
|
wrapper.locations[uniform.name] = gl.getUniformLocation(program, uniform.name); |
||||||
|
} |
||||||
|
|
||||||
|
return wrapper; |
||||||
|
} |
||||||
|
|
||||||
|
console.error('link:', gl.getProgramInfoLog(program)); |
||||||
|
|
||||||
|
gl.deleteProgram(program); |
||||||
|
} |
Loading…
Reference in new issue