remove tiny-skia and replace chunk renderer with a GPU-based one

This commit is contained in:
りき萌 2025-09-05 17:41:25 +02:00
parent 39632f56a7
commit b4c3260f49
10 changed files with 253 additions and 434 deletions

View file

@ -1,6 +1,7 @@
import { listen, Pool } from "rkgk/framework.js";
import { Viewport } from "rkgk/viewport.js";
import { Wall, chunkKey } from "rkgk/wall.js";
import { AtlasAllocator } from "rkgk/chunk-allocator.js";
class CanvasRenderer extends HTMLElement {
viewport = new Viewport();
@ -135,7 +136,8 @@ class CanvasRenderer extends HTMLElement {
out vec4 f_color;
void main() {
f_color = texture(u_texture, vf_uv);
vec4 color = texture(u_texture, vf_uv);
f_color = color;
}
`,
);
@ -191,8 +193,7 @@ class CanvasRenderer extends HTMLElement {
uboRects: this.uboRects,
});
this.atlasAllocator = new AtlasAllocator(this.wall.chunkSize, 8);
this.chunkAllocations = new Map();
this.atlasAllocator = new AtlasAllocator(this.gl, this.wall.chunkSize, 8);
console.debug("initialized atlas allocator", this.atlasAllocator);
@ -298,15 +299,11 @@ class CanvasRenderer extends HTMLElement {
for (let batch of this.batches) {
for (let [i, chunks] of batch) {
let atlas = this.atlasAllocator.atlases[i];
this.gl.bindTexture(this.gl.TEXTURE_2D, atlas.id);
this.gl.bindTexture(this.gl.TEXTURE_2D, atlas.texture);
this.#resetRectBuffer();
for (let chunk of chunks) {
let { i, allocation } = this.getChunkAllocation(
chunk.layerId,
chunk.x,
chunk.y,
);
let { i, allocation } = chunk.allocation;
let atlas = this.atlasAllocator.atlases[i];
this.#pushRect(
chunk.x * this.wall.chunkSize,
@ -326,42 +323,20 @@ class CanvasRenderer extends HTMLElement {
// TODO: This is a nice debug view.
// There should be a switch to it somewhere in the app.
/*
let x = 0;
let y = 0;
for (let atlas of this.atlasAllocator.atlases) {
this.#resetRectBuffer();
this.gl.bindTexture(this.gl.TEXTURE_2D, atlas.id);
this.#pushRect(x, y, atlas.textureSize, atlas.textureSize, 0, 0, 1, 1);
this.#drawRects();
if (x > atlas.textureSize * 16) {
y += atlas.textureSize;
x = 0;
}
x += atlas.textureSize;
}
*/
}
getChunkAllocation(layerId, chunkX, chunkY) {
let key = `${layerId}/${chunkKey(chunkX, chunkY)}`;
if (this.chunkAllocations.has(key)) {
return this.chunkAllocations.get(key);
} else {
let allocation = this.atlasAllocator.alloc(this.gl);
this.chunkAllocations.set(key, allocation);
return allocation;
}
}
deallocateChunks(layer) {
for (let chunkKey of layer.chunks.keys()) {
let key = `${layer.id}/${chunkKey}`;
if (this.chunkAllocations.has(key)) {
let allocation = this.chunkAllocations.get(key);
this.atlasAllocator.dealloc(allocation);
this.chunkAllocations.delete(key);
let x = 0;
let y = 0;
for (let atlas of this.atlasAllocator.atlases) {
this.#resetRectBuffer();
this.gl.bindTexture(this.gl.TEXTURE_2D, atlas.texture);
this.#pushRect(x, y, atlas.textureSize, atlas.textureSize, 0, 0, 1, 1);
this.#drawRects();
if (x > atlas.textureSize * 16) {
y += atlas.textureSize;
x = 0;
}
x += atlas.textureSize;
}
// */
}
#collectChunksThisFrame() {
@ -383,12 +358,7 @@ class CanvasRenderer extends HTMLElement {
for (let chunkX = left; chunkX < right; ++chunkX) {
let chunk = layer.getChunk(chunkX, chunkY);
if (chunk != null) {
if (chunk.renderDirty) {
this.#updateChunkTexture(layer, chunkX, chunkY);
chunk.renderDirty = false;
}
let allocation = this.getChunkAllocation(layer.id, chunkX, chunkY);
let allocation = chunk.id;
let array = batch.get(allocation.i);
if (array == null) {
@ -396,7 +366,7 @@ class CanvasRenderer extends HTMLElement {
batch.set(allocation.i, array);
}
array.push({ layerId: layer.id, x: chunkX, y: chunkY });
array.push({ layerId: layer.id, x: chunkX, y: chunkY, allocation });
}
}
}
@ -438,12 +408,6 @@ class CanvasRenderer extends HTMLElement {
this.gl.drawArraysInstanced(this.gl.TRIANGLES, 0, 6, this.uboRectsNum);
}
#updateChunkTexture(layer, chunkX, chunkY) {
let allocation = this.getChunkAllocation(layer.id, chunkX, chunkY);
let chunk = layer.getChunk(chunkX, chunkY);
this.atlasAllocator.upload(this.gl, allocation, chunk.pixmap);
}
// Behaviours
sendViewportUpdate() {
@ -584,101 +548,3 @@ class InteractEvent extends Event {
}
}
}
class Atlas {
static getInitBuffer(chunkSize, nChunks) {
let imageSize = chunkSize * nChunks;
return new Uint8Array(imageSize * imageSize * 4);
}
constructor(gl, chunkSize, nChunks, initBuffer) {
this.id = gl.createTexture();
this.chunkSize = chunkSize;
this.nChunks = nChunks;
this.textureSize = chunkSize * nChunks;
this.free = Array(nChunks * nChunks);
for (let y = 0; y < nChunks; ++y) {
for (let x = 0; x < nChunks; ++x) {
this.free[x + y * nChunks] = { x, y };
}
}
gl.bindTexture(gl.TEXTURE_2D, this.id);
gl.texImage2D(
gl.TEXTURE_2D,
0,
gl.RGBA8,
this.textureSize,
this.textureSize,
0,
gl.RGBA,
gl.UNSIGNED_BYTE,
initBuffer,
);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
}
alloc() {
return this.free.pop();
}
dealloc(xy) {
this.free.push(xy);
}
upload(gl, { x, y }, pixmap) {
gl.bindTexture(gl.TEXTURE_2D, this.id);
gl.texSubImage2D(
gl.TEXTURE_2D,
0,
x * this.chunkSize,
y * this.chunkSize,
this.chunkSize,
this.chunkSize,
gl.RGBA,
gl.UNSIGNED_BYTE,
pixmap.getArrayBuffer(),
);
}
}
class AtlasAllocator {
atlases = [];
constructor(chunkSize, nChunks) {
this.chunkSize = chunkSize;
this.nChunks = nChunks;
this.initBuffer = Atlas.getInitBuffer(chunkSize, nChunks);
}
alloc(gl) {
// Right now we do a dumb linear scan through all atlases, but in the future it would be
// really nice to optimize this by storing information about which atlases have free slots
// precisely.
for (let i = 0; i < this.atlases.length; ++i) {
let atlas = this.atlases[i];
let allocation = atlas.alloc();
if (allocation != null) {
return { i, allocation };
}
}
let i = this.atlases.length;
let atlas = new Atlas(gl, this.chunkSize, this.nChunks, this.initBuffer);
let allocation = atlas.alloc();
this.atlases.push(atlas);
return { i, allocation };
}
dealloc({ i, allocation }) {
let atlas = this.atlases[i];
atlas.dealloc(allocation);
}
upload(gl, { i, allocation }, pixmap) {
this.atlases[i].upload(gl, allocation, pixmap);
}
}