@lightningjs/renderer
Version:
Lightning 3 Renderer
234 lines • 9.61 kB
JavaScript
/*
* If not stated otherwise in this file or this component's LICENSE file the
* following copyright and licenses apply:
*
* Copyright 2023 Comcast Cable Communications Management, LLC.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ThreadX, BufferStruct } from '@lightningjs/threadx';
import { NodeStruct } from './NodeStruct.js';
import { ThreadXMainNode } from './ThreadXMainNode.js';
import { assertTruthy } from '../../utils.js';
import { isThreadXRendererMessage, } from './ThreadXRendererMessage.js';
import { TextNodeStruct, } from './TextNodeStruct.js';
import { ThreadXMainTextNode } from './ThreadXMainTextNode.js';
import { ThreadXMainShaderController } from './ThreadXMainShaderController.js';
export class ThreadXCoreDriver {
settings;
threadx;
rendererMain = null;
root = null;
fps = 0;
constructor(settings) {
this.settings = settings;
this.threadx = ThreadX.init({
workerId: 1,
workerName: 'main',
sharedObjectFactory: (buffer) => {
const typeId = BufferStruct.extractTypeId(buffer);
const rendererMain = this.rendererMain;
assertTruthy(rendererMain);
if (typeId === NodeStruct.typeId) {
const nodeStruct = new NodeStruct(buffer);
return nodeStruct.lock(() => {
return new ThreadXMainNode(rendererMain, nodeStruct);
});
}
else if (typeId === TextNodeStruct.typeId) {
const nodeStruct = new TextNodeStruct(buffer);
return nodeStruct.lock(() => {
return new ThreadXMainTextNode(rendererMain, nodeStruct);
});
}
return null;
},
onMessage: async (message) => {
// Forward fpsUpdate events from the renderer worker's Stage to RendererMain
if (isThreadXRendererMessage('fpsUpdate', message)) {
this.onFpsUpdate(message.fpsData);
}
// Forward frameTick events from the renderer worker's Stage to RendererMain
if (isThreadXRendererMessage('frameTick', message)) {
this.onFrameTick(message.frameTickData);
}
},
});
this.threadx.registerWorker('renderer', new Worker(this.settings.coreWorkerUrl, { type: 'module' }));
}
async init(rendererMain, rendererSettings, canvas) {
this.rendererMain = rendererMain;
const offscreenCanvas = canvas.transferControlToOffscreen();
const rootNodeId = (await this.threadx.sendMessageAsync('renderer', {
type: 'init',
canvas: offscreenCanvas,
appWidth: rendererSettings.appWidth,
appHeight: rendererSettings.appHeight,
txMemByteThreshold: rendererSettings.txMemByteThreshold,
boundsMargin: rendererSettings.boundsMargin,
deviceLogicalPixelRatio: rendererSettings.deviceLogicalPixelRatio,
devicePhysicalPixelRatio: rendererSettings.devicePhysicalPixelRatio,
clearColor: rendererSettings.clearColor,
coreExtensionModule: rendererSettings.coreExtensionModule,
fpsUpdateInterval: rendererSettings.fpsUpdateInterval,
enableContextSpy: rendererSettings.enableContextSpy,
numImageWorkers: rendererSettings.numImageWorkers,
}, [offscreenCanvas]));
// The Render worker shares the root node with this worker during the
// 'init' call above. That call returns the ID of the root node, which
// we can use to retrieve it from the shared object store.
const rootNode = this.threadx.getSharedObjectById(rootNodeId);
assertTruthy(rootNode instanceof ThreadXMainNode, 'Unexpected root node type');
this.root = rootNode;
}
getRootNode() {
assertTruthy(this.root, 'Driver not initialized');
return this.root;
}
createNode(props) {
const rendererMain = this.rendererMain;
assertTruthy(rendererMain);
const bufferStruct = new NodeStruct();
Object.assign(bufferStruct, {
// Node specific properties
x: props.x,
y: props.y,
width: props.width,
height: props.height,
parentId: props.parent ? props.parent.id : 0,
autosize: props.autosize,
clipping: props.clipping,
color: props.color,
colorTop: props.colorTop,
colorRight: props.colorBottom,
colorBottom: props.colorBottom,
colorLeft: props.colorLeft,
colorTl: props.colorTl,
colorTr: props.colorTr,
colorBl: props.colorBl,
colorBr: props.colorBr,
alpha: props.alpha,
zIndex: props.zIndex,
zIndexLocked: props.zIndexLocked,
scaleX: props.scaleX,
scaleY: props.scaleY,
mount: props.mount,
mountX: props.mountX,
mountY: props.mountY,
pivot: props.pivot,
pivotX: props.pivotX,
pivotY: props.pivotY,
rotation: props.rotation,
});
const node = new ThreadXMainNode(rendererMain, bufferStruct);
node.once('beforeDestroy', this.onBeforeDestroyNode.bind(this, node));
this.threadx.shareObjects('renderer', [node]).catch(console.error);
node.shader = props.shader ?? null;
node.texture = props.texture ?? null;
node.src = props.src ?? '';
this.onCreateNode(node);
return node;
}
createTextNode(props) {
const rendererMain = this.rendererMain;
assertTruthy(rendererMain);
const bufferStruct = new TextNodeStruct();
Object.assign(bufferStruct, {
// Node specific properties
x: props.x,
y: props.y,
width: props.width,
height: props.height,
parentId: props.parent ? props.parent.id : 0,
clipping: props.clipping,
color: props.color,
colorTop: props.colorTop,
colorRight: props.colorBottom,
colorBottom: props.colorBottom,
colorLeft: props.colorLeft,
colorTl: props.colorTl,
colorTr: props.colorTr,
colorBl: props.colorBl,
colorBr: props.colorBr,
alpha: props.alpha,
autosize: props.autosize,
zIndex: props.zIndex,
zIndexLocked: props.zIndexLocked,
scaleX: props.scaleX,
scaleY: props.scaleY,
mount: props.mount,
mountX: props.mountX,
mountY: props.mountY,
pivot: props.pivot,
pivotX: props.pivotX,
pivotY: props.pivotY,
rotation: props.rotation,
// Text specific properties
text: props.text,
textRendererOverride: props.textRendererOverride,
fontSize: props.fontSize,
fontFamily: props.fontFamily,
fontWeight: props.fontWeight,
fontStretch: props.fontStretch,
fontStyle: props.fontStyle,
lineHeight: props.lineHeight,
maxLines: props.maxLines,
textBaseline: props.textBaseline,
verticalAlign: props.verticalAlign,
contain: props.contain,
letterSpacing: props.letterSpacing,
overflowSuffix: props.overflowSuffix,
offsetY: props.offsetY,
textAlign: props.textAlign,
scrollable: props.scrollable,
scrollY: props.scrollY,
});
const node = new ThreadXMainTextNode(rendererMain, bufferStruct);
node.once('beforeDestroy', this.onBeforeDestroyNode.bind(this, node));
this.threadx.shareObjects('renderer', [node]).catch(console.error);
node.shader = props.shader ?? null;
node.texture = props.texture ?? null;
node.src = props.src ?? '';
node.debug = props.debug ?? {};
this.onCreateNode(node);
return node;
}
createShaderController(shaderRef) {
return new ThreadXMainShaderController(shaderRef);
}
// TODO: Remove?
destroyNode(node) {
node.destroy();
}
releaseTexture(textureDescId) {
this.threadx.sendMessage('renderer', {
type: 'releaseTexture',
textureDescId,
});
}
//#region Event Methods
// The implementations for these event methods are provided by RendererMain
onCreateNode(node) {
throw new Error('Method not implemented.');
}
onBeforeDestroyNode(node) {
throw new Error('Method not implemented.');
}
onFpsUpdate(fps) {
throw new Error('Method not implemented.');
}
onFrameTick(frameTickData) {
throw new Error('Method not implemented.');
}
}
//# sourceMappingURL=ThreadXCoreDriver.js.map