initial implementation of WebGL-based brush renderer
This commit is contained in:
parent
b4c3260f49
commit
bb55e23979
14 changed files with 385 additions and 247 deletions
|
@ -1,7 +1,7 @@
|
|||
class Atlas {
|
||||
static getInitBuffer(chunkSize, nChunks) {
|
||||
let imageSize = chunkSize * nChunks;
|
||||
return new Uint8Array(imageSize * imageSize * 4).fill(0xaa);
|
||||
return new Uint8Array(imageSize * imageSize * 4).fill(0x00);
|
||||
}
|
||||
|
||||
constructor(gl, chunkSize, nChunks, initBuffer) {
|
||||
|
@ -44,8 +44,12 @@ class Atlas {
|
|||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
}
|
||||
|
||||
alloc() {
|
||||
return this.free.pop();
|
||||
alloc(gl, initBuffer) {
|
||||
let xy = this.free.pop();
|
||||
if (xy != null) {
|
||||
this.upload(gl, xy, initBuffer);
|
||||
}
|
||||
return xy;
|
||||
}
|
||||
|
||||
dealloc(xy) {
|
||||
|
@ -81,11 +85,24 @@ class Atlas {
|
|||
);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
}
|
||||
|
||||
getFramebufferRect({ x, y }) {
|
||||
return {
|
||||
x: x * this.chunkSize,
|
||||
y: y * this.chunkSize,
|
||||
width: this.chunkSize,
|
||||
height: this.chunkSize,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class AtlasAllocator {
|
||||
atlases = [];
|
||||
|
||||
// Allocation names
|
||||
#ids = new Map();
|
||||
#idCounter = 1;
|
||||
|
||||
// Download buffers
|
||||
#pboPool = [];
|
||||
#downloadBufferPool = [];
|
||||
|
@ -98,6 +115,28 @@ export class AtlasAllocator {
|
|||
this.initBuffer = Atlas.getInitBuffer(chunkSize, nChunks);
|
||||
}
|
||||
|
||||
#obtainId(allocInfo) {
|
||||
let id = this.#idCounter++;
|
||||
this.#ids.set(id, allocInfo);
|
||||
return id;
|
||||
}
|
||||
|
||||
#releaseId(id) {
|
||||
this.#ids.delete(id);
|
||||
}
|
||||
|
||||
#getAllocInfo(id) {
|
||||
return this.#ids.get(id);
|
||||
}
|
||||
|
||||
getAtlasIndex(id) {
|
||||
return this.#getAllocInfo(id).i;
|
||||
}
|
||||
|
||||
getAllocation(id) {
|
||||
return this.#getAllocInfo(id).allocation;
|
||||
}
|
||||
|
||||
alloc() {
|
||||
// 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
|
||||
|
@ -105,33 +144,35 @@ export class AtlasAllocator {
|
|||
|
||||
for (let i = 0; i < this.atlases.length; ++i) {
|
||||
let atlas = this.atlases[i];
|
||||
let allocation = atlas.alloc();
|
||||
let allocation = atlas.alloc(this.gl, this.initBuffer);
|
||||
if (allocation != null) {
|
||||
return { i, allocation };
|
||||
return this.#obtainId({ i, allocation });
|
||||
}
|
||||
}
|
||||
|
||||
let i = this.atlases.length;
|
||||
let atlas = new Atlas(this.gl, this.chunkSize, this.nChunks, this.initBuffer);
|
||||
let allocation = atlas.alloc();
|
||||
let allocation = atlas.alloc(this.gl, this.initBuffer);
|
||||
this.atlases.push(atlas);
|
||||
|
||||
return { i, allocation };
|
||||
return this.#obtainId({ i, allocation });
|
||||
}
|
||||
|
||||
dealloc(id) {
|
||||
let { i, allocation } = id;
|
||||
let { i, allocation } = this.#getAllocInfo(id);
|
||||
let atlas = this.atlases[i];
|
||||
atlas.dealloc(allocation);
|
||||
this.#releaseId(id);
|
||||
}
|
||||
|
||||
upload(id, source) {
|
||||
let { i, allocation } = id;
|
||||
let { i, allocation } = this.#getAllocInfo(id);
|
||||
this.atlases[i].upload(this.gl, allocation, source);
|
||||
}
|
||||
|
||||
async download(id) {
|
||||
let gl = this.gl;
|
||||
let allocInfo = this.#getAllocInfo(id);
|
||||
|
||||
// Get PBO
|
||||
|
||||
|
@ -146,7 +187,7 @@ export class AtlasAllocator {
|
|||
// Initiate download
|
||||
|
||||
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
|
||||
this.atlases[id.i].download(gl, id);
|
||||
this.atlases[allocInfo.i].download(gl, allocInfo.allocation);
|
||||
let fence = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
gl.bindBuffer(gl.PIXEL_PACK_BUFFER, 0);
|
||||
|
@ -191,4 +232,30 @@ export class AtlasAllocator {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvasSource() {
|
||||
let useCanvas = (gl, id) => {
|
||||
let allocInfo = this.#getAllocInfo(id);
|
||||
let atlas = this.atlases[allocInfo.i];
|
||||
|
||||
let viewport = atlas.getFramebufferRect(allocInfo.allocation);
|
||||
|
||||
gl.enable(gl.SCISSOR_TEST);
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, atlas.framebuffer);
|
||||
gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height);
|
||||
gl.scissor(viewport.x, viewport.y, viewport.width, viewport.height);
|
||||
|
||||
return viewport;
|
||||
};
|
||||
|
||||
let resetCanvas = (gl) => {
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
gl.disable(gl.SCISSOR_TEST);
|
||||
};
|
||||
|
||||
return {
|
||||
useCanvas,
|
||||
resetCanvas,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue