Files
cocos-engine/cocos/core/root.ts
2022-05-17 11:56:06 +08:00

595 lines
16 KiB
TypeScript

/*
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
https://www.cocos.com/
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated engine source code (the "Software"), a limited,
worldwide, royalty-free, non-assignable, revocable and non-exclusive license
to use Cocos Creator solely to develop games on your target platforms. You shall
not use Cocos Creator software for developing other software or tools that's
used for developing games. You are not granted to publish, distribute,
sublicense, and/or sell copies of Cocos Creator.
The software or tools in this License Agreement are licensed, not sold.
Xiamen Yaji Software Co., Ltd. reserves all rights not expressly granted to you.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
import { JSB } from 'internal:constants';
import { builtinResMgr } from './builtin';
import { Pool } from './memop';
import { RenderPipeline, createDefaultPipeline, DeferredPipeline } from './pipeline';
import { Camera, Light, Model } from './renderer/scene';
import { NativeRoot } from './renderer/native-scene';
import type { DataPoolManager } from '../3d/skeletal-animation/data-pool-manager';
import { LightType } from './renderer/scene/light';
import { IRenderSceneInfo, RenderScene } from './renderer/core/render-scene';
import { SphereLight } from './renderer/scene/sphere-light';
import { SpotLight } from './renderer/scene/spot-light';
import { legacyCC } from './global-exports';
import { RenderWindow, IRenderWindowInfo } from './renderer/core/render-window';
import { ColorAttachment, DepthStencilAttachment, RenderPassInfo, StoreOp, Device, Swapchain, Feature } from './gfx';
import { warnID } from './platform/debug';
import { Batcher2D } from '../2d/renderer/batcher-2d';
/**
* @zh
* Root描述信息
*/
export interface IRootInfo {
enableHDR?: boolean;
}
/**
* @zh
* 场景描述信息
*/
export interface ISceneInfo {
name: string;
}
/**
* @zh
* Root类
*/
export class Root {
private _init (): void {
if (JSB) {
this._naitveObj = new NativeRoot();
}
}
private _destroy (): void {
if (JSB) {
this._naitveObj = null;
}
}
private _setCumulativeTime (deltaTime: number): void {
this._cumulativeTime += deltaTime;
if (JSB) {
this._naitveObj.cumulativeTime = this._cumulativeTime;
}
}
private _setFrameTime (deltaTime: number): void {
this._frameTime = deltaTime;
if (JSB) {
this._naitveObj.frameTime = deltaTime;
}
}
/**
* @zh
* GFX 设备
*/
public get device (): Device {
return this._device;
}
/**
* @zh
* 主窗口
*/
public get mainWindow (): RenderWindow | null {
return this._mainWindow;
}
/**
* @zh
* 当前窗口
*/
public set curWindow (window: RenderWindow | null) {
this._curWindow = window;
}
public get curWindow (): RenderWindow | null {
return this._curWindow;
}
/**
* @zh
* 临时窗口(用于数据传输)
*/
public set tempWindow (window: RenderWindow | null) {
this._tempWindow = window;
}
public get tempWindow (): RenderWindow | null {
return this._tempWindow;
}
/**
* @zh
* 窗口列表
*/
public get windows (): RenderWindow[] {
return this._windows;
}
/**
* @zh
* 渲染管线
*/
public get pipeline (): RenderPipeline {
return this._pipeline!;
}
/**
* @zh
* UI实例
* 引擎内部使用,用户无需调用此接口
*/
public get batcher2D (): Batcher2D {
return this._batcher as Batcher2D;
}
/**
* @zh
* 场景列表
*/
public get scenes (): RenderScene[] {
return this._scenes;
}
/**
* @zh
* 累计时间(秒)
*/
public get cumulativeTime (): number {
return this._cumulativeTime;
}
/**
* @zh
* 帧时间(秒)
*/
public get frameTime (): number {
return this._frameTime;
}
/**
* @zh
* 一秒内的累计帧数
*/
public get frameCount (): number {
return this._frameCount;
}
/**
* @zh
* 每秒帧率
*/
public get fps (): number {
return this._fps;
}
/**
* @zh
* 每秒固定帧率
*/
public set fixedFPS (fps: number) {
if (fps > 0) {
this._fixedFPS = fps;
this._fixedFPSFrameTime = 1000.0 / fps;
} else {
this._fixedFPSFrameTime = 0;
}
}
public get fixedFPS (): number {
return this._fixedFPS;
}
public get dataPoolManager () {
return this._dataPoolMgr;
}
get useDeferredPipeline () : boolean {
return this._useDeferredPipeline;
}
/**
* @deprecated since v3.5.0, this is an engine private interface that will be removed in the future.
*/
public _createSceneFun: (root: Root) => RenderScene = null!;
/**
* @deprecated since v3.5.0, this is an engine private interface that will be removed in the future.
*/
public _createWindowFun: (root: Root) => RenderWindow = null!;
private _device: Device;
private _windows: RenderWindow[] = [];
private _mainWindow: RenderWindow | null = null;
private _curWindow: RenderWindow | null = null;
private _tempWindow: RenderWindow | null = null;
private _pipeline: RenderPipeline | null = null;
private _batcher: Batcher2D | null = null;
private _dataPoolMgr: DataPoolManager;
private _scenes: RenderScene[] = [];
private _modelPools = new Map<Constructor<Model>, Pool<Model>>();
private _cameraPool: Pool<Camera> | null = null;
private _lightPools = new Map<Constructor<Light>, Pool<Light>>();
private _fpsTime = 0;
private _frameCount = 0;
private _fps = 0;
private _fixedFPS = 0;
private _useDeferredPipeline = false;
private _fixedFPSFrameTime = 0;
private _cumulativeTime = 0;
private _frameTime = 0;
private declare _naitveObj: any;
/**
* 构造函数
* @param device GFX 设备
*/
constructor (device: Device) {
this._device = device;
this._dataPoolMgr = legacyCC.internal.DataPoolManager && new legacyCC.internal.DataPoolManager(device) as DataPoolManager;
RenderScene.registerCreateFunc(this);
RenderWindow.registerCreateFunc(this);
this._cameraPool = new Pool(() => new Camera(this._device), 4, (cam) => cam.destroy());
}
/**
* @zh
* 初始化函数
* @param info Root描述信息
*/
public initialize (info: IRootInfo): Promise<void> {
this._init();
const swapchain: Swapchain = legacyCC.game._swapchain;
const colorAttachment = new ColorAttachment();
colorAttachment.format = swapchain.colorTexture.format;
const depthStencilAttachment = new DepthStencilAttachment();
depthStencilAttachment.format = swapchain.depthStencilTexture.format;
depthStencilAttachment.depthStoreOp = StoreOp.DISCARD;
depthStencilAttachment.stencilStoreOp = StoreOp.DISCARD;
const renderPassInfo = new RenderPassInfo([colorAttachment], depthStencilAttachment);
this._mainWindow = this.createWindow({
title: 'rootMainWindow',
width: swapchain.width,
height: swapchain.height,
renderPassInfo,
swapchain,
});
this._curWindow = this._mainWindow;
return Promise.resolve(builtinResMgr.initBuiltinRes(this._device));
}
public destroy () {
this.destroyScenes();
if (this._pipeline) {
this._pipeline.destroy();
this._pipeline = null;
}
if (this._batcher) {
this._batcher.destroy();
this._batcher = null;
}
this._curWindow = null;
this._mainWindow = null;
this.dataPoolManager.clear();
this._destroy();
}
/**
* @zh
* 重置大小
* @param width 屏幕宽度
* @param height 屏幕高度
*/
public resize (width: number, height: number) {
for (const window of this._windows) {
if (window.swapchain) {
window.resize(width, height);
}
}
}
public setRenderPipeline (rppl: RenderPipeline): boolean {
if (rppl instanceof DeferredPipeline) {
this._useDeferredPipeline = true;
}
let isCreateDefaultPipeline = false;
if (!rppl) {
rppl = createDefaultPipeline();
isCreateDefaultPipeline = true;
}
this._pipeline = rppl;
// now cluster just enabled in deferred pipeline
if (!this._useDeferredPipeline || !this.device.hasFeature(Feature.COMPUTE_SHADER)) {
// disable cluster
this._pipeline.clusterEnabled = false;
}
this._pipeline.bloomEnabled = false;
if (!this._pipeline.activate(this._mainWindow!.swapchain)) {
if (isCreateDefaultPipeline) {
this._pipeline.destroy();
}
this._pipeline = null;
return false;
}
const scene = legacyCC.director.getScene();
if (scene) {
scene.globals.activate();
}
this.onGlobalPipelineStateChanged();
if (!this._batcher && legacyCC.internal.Batcher2D) {
this._batcher = new legacyCC.internal.Batcher2D(this);
if (!this._batcher!.initialize()) {
this.destroy();
return false;
}
}
return true;
}
public onGlobalPipelineStateChanged () {
for (let i = 0; i < this._scenes.length; i++) {
this._scenes[i].onGlobalPipelineStateChanged();
}
this._pipeline!.pipelineSceneData.onGlobalPipelineStateChanged();
}
/**
* @zh
* 激活指定窗口为当前窗口
* @param window GFX 窗口
*/
public activeWindow (window: RenderWindow) {
this._curWindow = window;
}
/**
* @zh
* 重置累计时间
*/
public resetCumulativeTime () {
this._setCumulativeTime(0);
}
/**
* @zh
* 每帧执行函数
* @param deltaTime 间隔时间
*/
public frameMove (deltaTime: number) {
this._setFrameTime(deltaTime);
/*
if (this._fixedFPSFrameTime > 0) {
const elapsed = this._frameTime * 1000.0;
if (this._fixedFPSFrameTime > elapsed) {
setTimeout(function () {}, this._fixedFPSFrameTime - elapsed);
}
}
*/
++this._frameCount;
this._setCumulativeTime(deltaTime);
this._fpsTime += deltaTime;
if (this._fpsTime > 1.0) {
this._fps = this._frameCount;
this._frameCount = 0;
this._fpsTime = 0.0;
}
for (let i = 0; i < this._scenes.length; ++i) {
this._scenes[i].removeBatches();
}
const windows = this._windows;
const cameraList: Camera[] = [];
for (let i = 0; i < windows.length; i++) {
const window = windows[i];
window.extractRenderCameras(cameraList);
}
if (this._pipeline && cameraList.length > 0) {
this._device.acquire([legacyCC.game._swapchain]);
const scenes = this._scenes;
const stamp = legacyCC.director.getTotalFrames();
if (this._batcher) {
this._batcher.update();
this._batcher.uploadBuffers();
}
for (let i = 0; i < scenes.length; i++) {
scenes[i].update(stamp);
}
legacyCC.director.emit(legacyCC.Director.EVENT_BEFORE_COMMIT);
cameraList.sort((a: Camera, b: Camera) => a.priority - b.priority);
this._pipeline.render(cameraList);
this._device.present();
}
if (this._batcher) this._batcher.reset();
}
/**
* @zh
* 创建窗口
* @param info GFX 窗口描述信息
*/
public createWindow (info: IRenderWindowInfo): RenderWindow | null {
const window = this._createWindowFun(this);
window.initialize(this.device, info);
this._windows.push(window);
return window;
}
/**
* @zh
* 销毁指定的窗口
* @param window GFX 窗口
*/
public destroyWindow (window: RenderWindow) {
for (let i = 0; i < this._windows.length; ++i) {
if (this._windows[i] === window) {
window.destroy();
this._windows.splice(i, 1);
return;
}
}
}
/**
* @zh
* 销毁全部窗口
*/
public destroyWindows () {
for (const window of this._windows) {
window.destroy();
}
this._windows.length = 0;
}
/**
* @zh
* 创建渲染场景
* @param info 渲染场景描述信息
*/
public createScene (info: IRenderSceneInfo): RenderScene {
const scene: RenderScene = this._createSceneFun(this);
scene.initialize(info);
this._scenes.push(scene);
return scene;
}
/**
* @zh
* 销毁指定的渲染场景
* @param scene 渲染场景
*/
public destroyScene (scene: RenderScene) {
for (let i = 0; i < this._scenes.length; ++i) {
if (this._scenes[i] === scene) {
scene.destroy();
this._scenes.splice(i, 1);
return;
}
}
}
/**
* @zh
* 销毁全部场景
*/
public destroyScenes () {
for (const scene of this._scenes) {
scene.destroy();
}
this._scenes.length = 0;
}
public createModel<T extends Model> (ModelCtor: typeof Model): T {
let p = this._modelPools.get(ModelCtor);
if (!p) {
this._modelPools.set(ModelCtor, new Pool(() => new ModelCtor(), 10, (obj) => obj.destroy()));
p = this._modelPools.get(ModelCtor)!;
}
const model = p.alloc() as T;
model.initialize();
return model;
}
public destroyModel (m: Model) {
const p = this._modelPools.get(m.constructor as Constructor<Model>);
if (p) {
p.free(m);
if (m.scene) {
m.scene.removeModel(m);
}
} else {
warnID(1300, m.constructor.name);
}
m.destroy();
}
public createCamera (): Camera {
return this._cameraPool!.alloc();
}
public createLight<T extends Light> (LightCtor: new () => T): T {
let l = this._lightPools.get(LightCtor);
if (!l) {
this._lightPools.set(LightCtor, new Pool<Light>(() => new LightCtor(), 4, (obj) => obj.destroy()));
l = this._lightPools.get(LightCtor)!;
}
const light = l.alloc() as T;
light.initialize();
return light;
}
public destroyLight (l: Light) {
const p = this._lightPools.get(l.constructor as Constructor<Light>);
if (p) {
p.free(l);
if (l.scene) {
switch (l.type) {
case LightType.SPHERE:
l.scene.removeSphereLight(l as SphereLight);
break;
case LightType.SPOT:
l.scene.removeSpotLight(l as SpotLight);
break;
default:
break;
}
}
}
l.destroy();
}
}
legacyCC.Root = Root;