color-synth

a synth that generates colors instead of sounds
Log | Files | Refs | README

commit bb77179c2d64ecda140cc065647ea5c610f55b68
parent 305848b3f608989ba3289a49e5911aa375e2319f
Author: massi <mdsiboldi@gmail.com>
Date:   Sat, 22 Jul 2023 12:26:48 -0700

perf testing, oh also perf

Diffstat:
Msrc/lib/engine.worker.ts | 94+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/lib/types.ts | 10+---------
Msrc/routes/+page.svelte | 31+++++++++++++++++--------------
3 files changed, 76 insertions(+), 59 deletions(-)

diff --git a/src/lib/engine.worker.ts b/src/lib/engine.worker.ts @@ -8,6 +8,8 @@ import type { UnitStateMap, } from "$lib/types"; import { + ROWS, + COLS, getUnit, LOOP_CYCLES, range, @@ -81,11 +83,33 @@ function vUnit(x: { id: UnitId }): number { } } +const perf = {} + +const p = { + start: (tag: string) => { + if (!perf[tag]) perf[tag] = { + n: 0, + cum: 0, + avg: 0 + } + perf[tag]._t = performance.now(); + }, + end: (tag: string) => { + const r = perf[tag] + r.n++; + r.cum += performance.now() - r._t; + r.avg = r.cum / r.n; + } +} + +setInterval(() => console.log(perf), 5000) + function v(input: Input): number { if (typeof input === "number") { return input; } - return input.reduce((a, b) => a + vUnit(b), 0); + const result = input.reduce((a, b) => a + vUnit(b), 0); + return result; } function update() { @@ -120,42 +144,40 @@ function update() { function drawSquares() { if (config) { - const ctx = canvas.getContext("2d"); - if (!ctx) throw new Error("could not get 2d rendering context...") - const cols = 100; - const rows = 50; - const color = [0, 0, 0]; - const width = canvas.width; - const height = canvas.height; - const paneWidth = Math.ceil(width / cols); - const paneHeight = Math.ceil(height / rows); - - for (let row = 0; row < rows; row++) { - for (let col = 0; col < cols; col++) { - const l = config.sinks.l == null ? 0 : v(config.sinks.l); - const c = config.sinks.c == null ? 0 : v(config.sinks.c); - const h = config.sinks.h == null ? 0 : v(config.sinks.h); - color[0] = wrangle(l, range.signal, { - min: 0, - max: 100, - }); - color[1] = wrangle(c, range.signal, { - min: 0, - max: 0.5, - }); - color[2] = wrangle(h, range.signal, { - min: 0, - max: 360, - }); - ctx.fillStyle = `oklch(${color[0]}% ${color[1]} ${color[2]}deg)`; - const x = col * paneWidth; - const y = row * paneHeight; - ctx.fillRect(x, y, paneWidth, paneHeight); - update(); - } + p.start("drawSquares"); + const ctx = canvas.getContext("2d", { + antialias: false, + alpha: false, + }); + if (!ctx) { + throw new Error("couldnt get ctx"); + } + + // 4 = R G B A + const data = new Uint8ClampedArray(ROWS * COLS * 4); + let di = 0; + + for (let i = 0; i < ROWS * COLS; i++) { + const l = config.sinks.l == null ? 0 : v(config.sinks.l); + const c = config.sinks.c == null ? 0 : v(config.sinks.c); + const h = config.sinks.h == null ? 0 : v(config.sinks.h); + data[di++] = wrangle(l, range.signal, { + min: 0, + max: 255, + }); + data[di++] = wrangle(c, range.signal, { + min: 0, + max: 255, + }); + data[di++] = wrangle(h, range.signal, { + min: 0, + max: 255, + }); + data[di++] = 255; + update(); } - const bmp = canvas.transferToImageBitmap(); - postMessage({ kind: "bmp", content: bmp }, [bmp]); + postMessage({ kind: "buf", content: data.buffer }, [data.buffer]); + p.end("drawSquares"); } requestAnimationFrame(drawSquares); } diff --git a/src/lib/types.ts b/src/lib/types.ts @@ -1,7 +1,7 @@ export const LOOP_CYCLES = 100_000_000; export const INPUT_RANGE = 100; export const ROWS = 100; -export const COLS = 50; +export const COLS = 100; export const CELLS = ROWS * COLS; export type WithTarget<E, T> = E & { currentTarget: T }; @@ -185,14 +185,6 @@ export function getUnit(units: UnitMap, id: UnitId): Unit { return result; } -export function getHigherUnit(units: UnitMap, id: UnitId): HigherUnit { - const result = getUnit(units, id); - if (result.kind === "const") { - throw new Error("lower unit found"); - } - return result; -} - export const is = { input: (i: any): i is Input => { return i === Object(i) && i.id; diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte @@ -4,11 +4,10 @@ import Sink from '$lib/Sink.svelte'; import Osc from '$lib/Osc.svelte'; import Slider from '$lib/Slider.svelte'; - import { inp, rescale, range, is, getUnit as _getUnit } from '$lib/types'; + import { COLS, ROWS, inp, rescale, range, is, getUnit as _getUnit } from '$lib/types'; import type { Output, Input, Unit, UnitId, ConstUnit, UnitMap, Sinks } from '$lib/types'; let cvs: HTMLCanvasElement | undefined; - let offscreenCanvas: OffscreenCanvas | undefined; // initialize worker let engineWorker: Worker | undefined = undefined; @@ -16,13 +15,6 @@ const EngineWorker = await import('$lib/engine.worker?worker'); engineWorker = new EngineWorker.default(); engineWorker.postMessage({ kind: 'config', content: { units, sinks } }); - if (cvs && engineWorker && !offscreenCanvas) { - const { innerHeight: height, innerWidth: width } = window; - console.log('sending canvas', { height, width }); - setTimeout(() => { - engineWorker?.postMessage({ kind: 'window', content: { width, height } }); - }, 0); - } }; onMount(loadWorker); @@ -31,10 +23,22 @@ $: { if (engineWorker) { engineWorker.onmessage = (msg) => { - if (cvs) { - const ctx = cvs.getContext('bitmaprenderer'); - ctx?.transferFromImageBitmap(msg.data.content); - } + requestAnimationFrame(() => { + if (cvs) { + const ctx = cvs.getContext('2d'); + if (!ctx) return; + ctx.imageSmoothingEnabled = false; + + const offscreen = new OffscreenCanvas(COLS, ROWS); + const offscreenCtx = offscreen.getContext('2d'); + if (!offscreenCtx) throw new Error('cant get 2d context for offscreen canvas'); + + const arr = new Uint8ClampedArray(msg.data.content); + const img = new ImageData(arr, COLS); + offscreenCtx.putImageData(img, 0, 0); + ctx.drawImage(offscreen, 0, 0, cvs.width, cvs.height); + } + }); }; } } @@ -91,7 +95,6 @@ } function _updateUrl() { - console.log('updating url'); const url = new URL(String(location)); url.searchParams.set('z', toUrl()); history.pushState({}, '', url);