A.Olokhtonov
4 months ago
7 changed files with 427 additions and 18 deletions
@ -0,0 +1,65 @@
@@ -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 @@
@@ -1,5 +1,8 @@
|
||||
document.addEventListener('DOMContentLoaded', main); |
||||
|
||||
let traces = {}; |
||||
|
||||
function main() { |
||||
init_webgl(); |
||||
init_listeners(); |
||||
} |
||||
|
@ -0,0 +1,250 @@
@@ -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