remove tiny-skia and replace chunk renderer with a GPU-based one
This commit is contained in:
parent
39632f56a7
commit
b4c3260f49
10 changed files with 253 additions and 434 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue