@luma.gl/core
Version:
The luma.gl core Device API
188 lines (168 loc) • 6.1 kB
text/typescript
// luma.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import type {
TextureFormatColor,
TextureFormatDepthStencil,
TextureFormat
} from '../../shadertypes/texture-types/texture-formats';
import type {Device} from '../device';
import {Resource, ResourceProps} from './resource';
import {Texture} from './texture';
import {TextureView} from './texture-view';
import {log} from '../../utils/log';
export type FramebufferProps = ResourceProps & {
width?: number;
height?: number;
colorAttachments?: (TextureView | Texture | TextureFormatColor)[];
depthStencilAttachment?: (TextureView | Texture | TextureFormatDepthStencil) | null;
};
/**
* Create new textures with correct size for all attachments.
* @note resize() destroys existing textures (if size has changed).
*/
export abstract class Framebuffer extends Resource<FramebufferProps> {
override get [Symbol.toStringTag](): string {
return 'Framebuffer';
}
/** Width of all attachments in this framebuffer */
width: number;
/** Height of all attachments in this framebuffer */
height: number;
/** Color attachments */
abstract colorAttachments: TextureView[];
/** Depth-stencil attachment, if provided */
abstract depthStencilAttachment: TextureView | null;
constructor(device: Device, props: FramebufferProps = {}) {
super(device, props, Framebuffer.defaultProps);
this.width = this.props.width;
this.height = this.props.height;
}
/**
* Create a copy of this framebuffer with new attached textures, with same props but of the specified size.
* @note Does not copy contents of the attached textures.
*/
clone(size?: {width: number; height: number}): Framebuffer {
const colorAttachments = this.colorAttachments.map(colorAttachment =>
colorAttachment.texture.clone(size)
);
const depthStencilAttachment =
this.depthStencilAttachment && this.depthStencilAttachment.texture.clone(size);
return this.device.createFramebuffer({
...this.props,
...size,
colorAttachments,
depthStencilAttachment
});
}
/**
* Resizes all attachments
* @note resize() destroys existing textures (if size has changed).
* @deprecated Use framebuffer.clone()
*/
resize(size: {width: number; height: number}): void;
resize(size: [width: number, height: number]): void;
resize(): void;
resize(size?: {width: number; height: number} | [width: number, height: number]): void {
let updateSize: boolean = !size;
if (size) {
const [width, height] = Array.isArray(size) ? size : [size.width, size.height];
updateSize = updateSize || height !== this.height || width !== this.width;
this.width = width;
this.height = height;
}
if (updateSize) {
log.log(2, `Resizing framebuffer ${this.id} to ${this.width}x${this.height}`)();
this.resizeAttachments(this.width, this.height);
}
}
/** Auto creates any textures */
protected autoCreateAttachmentTextures(): void {
if (this.props.colorAttachments.length === 0 && !this.props.depthStencilAttachment) {
throw new Error('Framebuffer has noattachments');
}
this.colorAttachments = this.props.colorAttachments.map((attachment, index) => {
if (typeof attachment === 'string') {
const texture = this.createColorTexture(attachment, index);
this.attachResource(texture);
return texture.view;
}
if (attachment instanceof Texture) {
return attachment.view;
}
return attachment;
});
const attachment = this.props.depthStencilAttachment;
if (attachment) {
if (typeof attachment === 'string') {
const texture = this.createDepthStencilTexture(attachment);
this.attachResource(texture);
this.depthStencilAttachment = texture.view;
} else if (attachment instanceof Texture) {
this.depthStencilAttachment = attachment.view;
} else {
this.depthStencilAttachment = attachment;
}
}
}
/** Create a color texture */
protected createColorTexture(format: TextureFormat, index: number): Texture {
return this.device.createTexture({
id: `${this.id}-color-attachment-${index}`,
usage: Texture.RENDER_ATTACHMENT,
format,
width: this.width,
height: this.height,
// TODO deprecated? - luma.gl v8 compatibility
sampler: {
magFilter: 'linear',
minFilter: 'linear'
}
});
}
/** Create depth stencil texture */
protected createDepthStencilTexture(format: TextureFormat): Texture {
return this.device.createTexture({
id: `${this.id}-depth-stencil-attachment`,
usage: Texture.RENDER_ATTACHMENT,
format,
width: this.width,
height: this.height
});
}
/**
* Default implementation of resize
* Creates new textures with correct size for all attachments.
* and destroys existing textures if owned
*/
protected resizeAttachments(width: number, height: number): void {
this.colorAttachments.forEach((colorAttachment, i) => {
const resizedTexture = colorAttachment.texture.clone({
width,
height
});
this.destroyAttachedResource(colorAttachment);
this.colorAttachments[i] = resizedTexture.view;
this.attachResource(resizedTexture.view);
});
if (this.depthStencilAttachment) {
const resizedTexture = this.depthStencilAttachment.texture.clone({
width,
height
});
this.destroyAttachedResource(this.depthStencilAttachment);
this.depthStencilAttachment = resizedTexture.view;
this.attachResource(resizedTexture);
}
this.updateAttachments();
}
/** Implementation is expected to update any underlying binding (WebGL framebuffer attachment) */
protected abstract updateAttachments(): void;
static override defaultProps: Required<FramebufferProps> = {
...Resource.defaultProps,
width: 1,
height: 1,
colorAttachments: [], // ['rgba8unorm'],
depthStencilAttachment: null // 'depth24plus-stencil8'
};
}