Skip to content

Commit

Permalink
Merge pull request #57 from martinlaxenaire/develop
Browse files Browse the repository at this point in the history
Added support for offscreen canvas
  • Loading branch information
martinlaxenaire committed Mar 26, 2024
2 parents 896a6ad + 2e92650 commit 8f43632
Show file tree
Hide file tree
Showing 343 changed files with 4,032 additions and 3,623 deletions.
9 changes: 5 additions & 4 deletions dist/esm/core/renderers/GPUCameraRenderer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class GPUCameraRenderer extends GPURenderer {
*/
constructor({
deviceManager,
label,
container,
pixelRatio = 1,
autoResize = true,
Expand All @@ -21,6 +22,7 @@ class GPUCameraRenderer extends GPURenderer {
}) {
super({
deviceManager,
label,
container,
pixelRatio,
autoResize,
Expand Down Expand Up @@ -59,8 +61,7 @@ class GPUCameraRenderer extends GPURenderer {
* @param cameraParameters - {@link CameraBasePerspectiveOptions | parameters} used to create the {@link camera}
*/
setCamera(cameraParameters) {
const width = this.size ? this.size.width : 1;
const height = this.size ? this.size.height : 1;
const { width, height } = this.rectBBox;
this.camera = new Camera({
fov: cameraParameters.fov,
near: cameraParameters.near,
Expand Down Expand Up @@ -159,8 +160,8 @@ class GPUCameraRenderer extends GPURenderer {
fov,
near,
far,
width: this.size.width,
height: this.size.height,
width: this.rectBBox.width,
height: this.rectBBox.height,
pixelRatio: this.pixelRatio
});
}
Expand Down
122 changes: 81 additions & 41 deletions dist/esm/core/renderers/GPURenderer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class GPURenderer {
*/
constructor({
deviceManager,
label = "Main renderer",
container,
pixelRatio = 1,
autoResize = true,
Expand All @@ -31,14 +32,15 @@ class GPURenderer {
this.type = "GPURenderer";
this.uuid = generateUUID();
if (!deviceManager) {
throwError(`GPURenderer: no device manager provided: ${deviceManager}`);
throwError(`GPURenderer (${label}): no device manager provided: ${deviceManager}`);
}
this.deviceManager = deviceManager;
this.deviceManager.addRenderer(this);
renderPass = { ...{ useDepth: true, sampleCount: 4, clearValue: [0, 0, 0, 0] }, ...renderPass };
preferredFormat = preferredFormat ?? this.deviceManager.gpu?.getPreferredCanvasFormat();
this.options = {
deviceManager,
label,
container,
pixelRatio,
autoResize,
Expand All @@ -48,59 +50,80 @@ class GPURenderer {
};
this.pixelRatio = pixelRatio ?? window.devicePixelRatio ?? 1;
this.alphaMode = alphaMode;
const isOffscreenCanvas = container instanceof OffscreenCanvas;
const isContainerCanvas = isOffscreenCanvas || container instanceof HTMLCanvasElement;
this.canvas = isContainerCanvas ? container : document.createElement("canvas");
const { width, height } = this.canvas;
this.rectBBox = {
width,
height,
top: 0,
left: 0
};
this.setTasksQueues();
this.setRendererObjects();
const isContainerCanvas = container instanceof HTMLCanvasElement;
this.canvas = isContainerCanvas ? container : document.createElement("canvas");
this.domElement = new DOMElement({
element: container,
priority: 5,
// renderer callback need to be called first
onSizeChanged: () => {
if (this.options.autoResize)
this.resize();
if (!isOffscreenCanvas) {
this.domElement = new DOMElement({
element: container,
priority: 5,
// renderer callback need to be called first
onSizeChanged: () => {
if (this.options.autoResize)
this.resize();
}
});
this.resize();
if (!isContainerCanvas) {
this.domElement.element.appendChild(this.canvas);
}
});
this.resize();
if (!isContainerCanvas) {
this.domElement.element.appendChild(this.canvas);
}
if (this.deviceManager.device) {
this.setContext();
}
}
/**
* Set the renderer and canvas {@link size | size}
* @param size - the optional new {@link canvas} size to set
* Set the renderer {@link RectBBox} and canvas sizes
* @param rectBBox - the optional new {@link canvas} {@link RectBBox} to set
*/
setSize(size = null) {
size = { ...{ width: this.boundingRect.width, height: this.boundingRect.height }, ...size };
this.size = size;
const renderingSize = { ...size };
setSize(rectBBox = null) {
rectBBox = {
...{
width: this.boundingRect.width,
height: this.boundingRect.height,
top: this.boundingRect.top,
left: this.boundingRect.left
},
...rectBBox
};
this.rectBBox = rectBBox;
const renderingSize = {
width: this.rectBBox.width,
height: this.rectBBox.height
};
renderingSize.width *= this.pixelRatio;
renderingSize.height *= this.pixelRatio;
this.clampToMaxDimension(renderingSize);
this.canvas.width = Math.floor(renderingSize.width);
this.canvas.height = Math.floor(renderingSize.height);
this.canvas.style.width = this.size.width + "px";
this.canvas.style.height = this.size.height + "px";
if (this.canvas.style) {
this.canvas.style.width = this.rectBBox.width + "px";
this.canvas.style.height = this.rectBBox.height + "px";
}
}
/**
* Set the renderer {@link pixelRatio | pixel ratio} and {@link resize} it
* @param pixelRatio - new pixel ratio to use
*/
setPixelRatio(pixelRatio = 1) {
this.pixelRatio = pixelRatio;
this.resize(this.size);
this.resize(this.rectBBox);
}
/**
* Resize our {@link GPURenderer}
* @param size - the optional new {@link canvas} size to set
* @param rectBBox - the optional new {@link canvas} {@link RectBBox} to set
*/
resize(size = null) {
if (!this.domElement)
return;
this.setSize(size);
resize(rectBBox = null) {
this.setSize(rectBBox);
this.onResize();
this._onAfterResizeCallback && this._onAfterResizeCallback();
}
Expand Down Expand Up @@ -133,12 +156,12 @@ class GPURenderer {
});
}
/**
* Get our {@link domElement | DOM Element} {@link DOMElement#boundingRect | bounding rectangle}
* Get our {@link domElement | DOM Element} {@link DOMElement#boundingRect | bounding rectangle}. If there's no {@link domElement | DOM Element} (like when using an offscreen canvas for example), the {@link rectBBox} values are used.
*/
get boundingRect() {
if (!!this.domElement.boundingRect) {
if (!!this.domElement && !!this.domElement.boundingRect) {
return this.domElement.boundingRect;
} else {
} else if (!!this.domElement) {
const boundingRect = this.domElement.element?.getBoundingClientRect();
return {
top: boundingRect.top,
Expand All @@ -150,6 +173,17 @@ class GPURenderer {
x: boundingRect.x,
y: boundingRect.y
};
} else {
return {
top: this.rectBBox.top,
right: this.rectBBox.left + this.rectBBox.width,
bottom: this.rectBBox.top + this.rectBBox.height,
left: this.rectBBox.left,
width: this.rectBBox.width,
height: this.rectBBox.height,
x: this.rectBBox.left,
y: this.rectBBox.top
};
}
}
/**
Expand Down Expand Up @@ -264,11 +298,11 @@ class GPURenderer {
*/
setMainRenderPasses() {
this.renderPass = new RenderPass(this, {
label: "Main render pass",
label: this.options.label + " render pass",
...this.options.renderPass
});
this.postProcessingPass = new RenderPass(this, {
label: "Post processing render pass",
label: this.options.label + " post processing render pass",
// no need to handle depth or perform MSAA on a fullscreen quad
useDepth: false,
sampleCount: 1
Expand Down Expand Up @@ -322,28 +356,32 @@ class GPURenderer {
commandEncoder
}) {
if (!srcBuffer) {
throwWarning(`${this.type}: cannot copy to buffer because the source buffer has not been provided`);
throwWarning(
`${this.type} (${this.options.label}): cannot copy to buffer because the source buffer has not been provided`
);
return null;
}
if (!dstBuffer) {
dstBuffer = this.createBuffer({
label: this.type + ": destination copy buffer from: " + srcBuffer.label,
label: `GPURenderer (${this.options.label}): destination copy buffer from: ${srcBuffer.label}`,
size: srcBuffer.size,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
}
if (srcBuffer.mapState !== "unmapped") {
throwWarning(`${this.type}: Cannot copy from ${srcBuffer} because it is currently mapped`);
throwWarning(`${this.type} (${this.options.label}): Cannot copy from ${srcBuffer} because it is currently mapped`);
return;
}
if (dstBuffer.mapState !== "unmapped") {
throwWarning(`${this.type}: Cannot copy from ${dstBuffer} because it is currently mapped`);
throwWarning(`${this.type} (${this.options.label}): Cannot copy from ${dstBuffer} because it is currently mapped`);
return;
}
const hasCommandEncoder = !!commandEncoder;
if (!hasCommandEncoder) {
commandEncoder = this.device?.createCommandEncoder({ label: "Copy buffer command encoder" });
!this.production && commandEncoder.pushDebugGroup("Copy buffer command encoder");
commandEncoder = this.device?.createCommandEncoder({
label: `${this.type} (${this.options.label}): Copy buffer command encoder`
});
!this.production && commandEncoder.pushDebugGroup(`${this.type} (${this.options.label}): Copy buffer command encoder`);
}
commandEncoder.copyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, dstBuffer.size);
if (!hasCommandEncoder) {
Expand Down Expand Up @@ -671,8 +709,10 @@ class GPURenderer {
forceClear(commandEncoder) {
const hasCommandEncoder = !!commandEncoder;
if (!hasCommandEncoder) {
commandEncoder = this.device?.createCommandEncoder({ label: "Force clear command encoder" });
!this.production && commandEncoder.pushDebugGroup("Force clear command encoder");
commandEncoder = this.device?.createCommandEncoder({
label: `${this.type} (${this.options.label}): Force clear command encoder`
});
!this.production && commandEncoder.pushDebugGroup(`${this.type} (${this.options.label}): Force clear command encoder`);
}
this.renderPass.updateView();
const pass = commandEncoder.beginRenderPass(this.renderPass.descriptor);
Expand Down
2 changes: 2 additions & 0 deletions dist/esm/core/textures/RenderTexture.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class RenderTexture {
* Create the {@link GPUTexture | texture} (or copy it from source) and update the {@link TextureBinding#resource | binding resource}
*/
createTexture() {
if (!this.size.width || !this.size.height)
return;
if (this.options.fromTexture) {
this.copyGPUTexture(this.options.fromTexture.texture);
return;
Expand Down
3 changes: 3 additions & 0 deletions dist/esm/curtains/GPUCurtains.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class GPUCurtains {
*/
constructor({
container,
label,
pixelRatio = window.devicePixelRatio ?? 1,
preferredFormat,
alphaMode = "premultiplied",
Expand Down Expand Up @@ -41,6 +42,7 @@ class GPUCurtains {
this.type = "CurtainsGPU";
this.options = {
container,
label,
pixelRatio,
camera,
production,
Expand Down Expand Up @@ -92,6 +94,7 @@ class GPUCurtains {
this.createCurtainsRenderer({
deviceManager: this.deviceManager,
// TODO ...this.options?
label: this.options.label,
container: this.options.container,
pixelRatio: this.options.pixelRatio,
autoResize: this.options.autoResize,
Expand Down
2 changes: 2 additions & 0 deletions dist/esm/curtains/renderers/GPUCurtainsRenderer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class GPUCurtainsRenderer extends GPUCameraRenderer {
*/
constructor({
deviceManager,
label,
container,
pixelRatio = 1,
autoResize = true,
Expand All @@ -17,6 +18,7 @@ class GPUCurtainsRenderer extends GPUCameraRenderer {
}) {
super({
deviceManager,
label,
container,
pixelRatio,
autoResize,
Expand Down
26 changes: 14 additions & 12 deletions dist/esm/utils/ResizeManager.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,18 @@ class ResizeManager {
constructor() {
this.shouldWatch = true;
this.entries = [];
this.resizeObserver = new ResizeObserver((observedEntries) => {
const allEntries = observedEntries.map((observedEntry) => {
return this.entries.filter((e) => e.element.isSameNode(observedEntry.target));
}).flat().sort((a, b) => b.priority - a.priority);
allEntries?.forEach((entry) => {
if (entry && entry.callback) {
entry.callback();
}
if (typeof window === "object" && "ResizeObserver" in window) {
this.resizeObserver = new ResizeObserver((observedEntries) => {
const allEntries = observedEntries.map((observedEntry) => {
return this.entries.filter((e) => e.element.isSameNode(observedEntry.target));
}).flat().sort((a, b) => b.priority - a.priority);
allEntries?.forEach((entry) => {
if (entry && entry.callback) {
entry.callback();
}
});
});
});
}
}
/**
* Set {@link shouldWatch}
Expand All @@ -30,7 +32,7 @@ class ResizeManager {
observe({ element, priority, callback }) {
if (!element || !this.shouldWatch)
return;
this.resizeObserver.observe(element);
this.resizeObserver?.observe(element);
const entry = {
element,
priority,
Expand All @@ -43,14 +45,14 @@ class ResizeManager {
* @param element - {@link HTMLElement} to unobserve
*/
unobserve(element) {
this.resizeObserver.unobserve(element);
this.resizeObserver?.unobserve(element);
this.entries = this.entries.filter((e) => !e.element.isSameNode(element));
}
/**
* Destroy our {@link ResizeManager}
*/
destroy() {
this.resizeObserver.disconnect();
this.resizeObserver?.disconnect();
}
}
const resizeManager = new ResizeManager();
Expand Down
Loading

0 comments on commit 8f43632

Please sign in to comment.