Files
nothoughts/node_modules/pixi.js/lib/rendering/renderers/shared/renderTarget/RenderTargetSystem.js
2025-08-04 18:57:35 +02:00

275 lines
11 KiB
JavaScript

'use strict';
var Matrix = require('../../../../maths/matrix/Matrix.js');
var Rectangle = require('../../../../maths/shapes/Rectangle.js');
var _const = require('../../gl/const.js');
var calculateProjection = require('../../gpu/renderTarget/calculateProjection.js');
var SystemRunner = require('../system/SystemRunner.js');
var CanvasSource = require('../texture/sources/CanvasSource.js');
var TextureSource = require('../texture/sources/TextureSource.js');
var Texture = require('../texture/Texture.js');
var getCanvasTexture = require('../texture/utils/getCanvasTexture.js');
var isRenderingToScreen = require('./isRenderingToScreen.js');
var RenderTarget = require('./RenderTarget.js');
"use strict";
class RenderTargetSystem {
constructor(renderer) {
/** This is the root viewport for the render pass*/
this.rootViewPort = new Rectangle.Rectangle();
/** the current viewport that the gpu is using */
this.viewport = new Rectangle.Rectangle();
/**
* a runner that lets systems know if the active render target has changed.
* Eg the Stencil System needs to know so it can manage the stencil buffer
*/
this.onRenderTargetChange = new SystemRunner.SystemRunner("onRenderTargetChange");
/** the projection matrix that is used by the shaders based on the active render target and the viewport */
this.projectionMatrix = new Matrix.Matrix();
/** the default clear color for render targets */
this.defaultClearColor = [0, 0, 0, 0];
/**
* a hash that stores the render target for a given render surface. When you pass in a texture source,
* a render target is created for it. This map stores and makes it easy to retrieve the render target
*/
this._renderSurfaceToRenderTargetHash = /* @__PURE__ */ new Map();
/** A hash that stores a gpu render target for a given render target. */
this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null);
/**
* A stack that stores the render target and frame that is currently being rendered to.
* When push is called, the current render target is stored in this stack.
* When pop is called, the previous render target is restored.
*/
this._renderTargetStack = [];
this._renderer = renderer;
}
/** called when dev wants to finish a render pass */
finishRenderPass() {
this.adaptor.finishRenderPass(this.renderTarget);
}
/**
* called when the renderer starts to render a scene.
* @param options
* @param options.target - the render target to render to
* @param options.clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111
* @param options.clearColor - the color to clear to
* @param options.frame - the frame to render to
*/
renderStart({
target,
clear,
clearColor,
frame
}) {
this._renderTargetStack.length = 0;
this.push(
target,
clear,
clearColor,
frame
);
this.rootViewPort.copyFrom(this.viewport);
this.rootRenderTarget = this.renderTarget;
this.renderingToScreen = isRenderingToScreen.isRenderingToScreen(this.rootRenderTarget);
}
postrender() {
this.adaptor.postrender?.(this.rootRenderTarget);
}
/**
* Binding a render surface! This is the main function of the render target system.
* It will take the RenderSurface (which can be a texture, canvas, or render target) and bind it to the renderer.
* Once bound all draw calls will be rendered to the render surface.
*
* If a frame is not provide and the render surface is a texture, the frame of the texture will be used.
* @param renderSurface - the render surface to bind
* @param clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111
* @param clearColor - the color to clear to
* @param frame - the frame to render to
* @returns the render target that was bound
*/
bind(renderSurface, clear = true, clearColor, frame) {
const renderTarget = this.getRenderTarget(renderSurface);
const didChange = this.renderTarget !== renderTarget;
this.renderTarget = renderTarget;
this.renderSurface = renderSurface;
const gpuRenderTarget = this.getGpuRenderTarget(renderTarget);
if (renderTarget.pixelWidth !== gpuRenderTarget.width || renderTarget.pixelHeight !== gpuRenderTarget.height) {
this.adaptor.resizeGpuRenderTarget(renderTarget);
gpuRenderTarget.width = renderTarget.pixelWidth;
gpuRenderTarget.height = renderTarget.pixelHeight;
}
const source = renderTarget.colorTexture;
const viewport = this.viewport;
const pixelWidth = source.pixelWidth;
const pixelHeight = source.pixelHeight;
if (!frame && renderSurface instanceof Texture.Texture) {
frame = renderSurface.frame;
}
if (frame) {
const resolution = source._resolution;
viewport.x = frame.x * resolution + 0.5 | 0;
viewport.y = frame.y * resolution + 0.5 | 0;
viewport.width = frame.width * resolution + 0.5 | 0;
viewport.height = frame.height * resolution + 0.5 | 0;
} else {
viewport.x = 0;
viewport.y = 0;
viewport.width = pixelWidth;
viewport.height = pixelHeight;
}
calculateProjection.calculateProjection(
this.projectionMatrix,
0,
0,
viewport.width / source.resolution,
viewport.height / source.resolution,
!renderTarget.isRoot
);
this.adaptor.startRenderPass(renderTarget, clear, clearColor, viewport);
if (didChange) {
this.onRenderTargetChange.emit(renderTarget);
}
return renderTarget;
}
clear(target, clear = _const.CLEAR.ALL, clearColor) {
if (!clear)
return;
if (target) {
target = this.getRenderTarget(target);
}
this.adaptor.clear(
target || this.renderTarget,
clear,
clearColor,
this.viewport
);
}
contextChange() {
this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null);
}
/**
* Push a render surface to the renderer. This will bind the render surface to the renderer,
* @param renderSurface - the render surface to push
* @param clear - the clear mode to use. Can be true or a CLEAR number 'COLOR | DEPTH | STENCIL' 0b111
* @param clearColor - the color to clear to
* @param frame - the frame to use when rendering to the render surface
*/
push(renderSurface, clear = _const.CLEAR.ALL, clearColor, frame) {
const renderTarget = this.bind(renderSurface, clear, clearColor, frame);
this._renderTargetStack.push({
renderTarget,
frame
});
return renderTarget;
}
/** Pops the current render target from the renderer and restores the previous render target. */
pop() {
this._renderTargetStack.pop();
const currentRenderTargetData = this._renderTargetStack[this._renderTargetStack.length - 1];
this.bind(currentRenderTargetData.renderTarget, false, null, currentRenderTargetData.frame);
}
/**
* Gets the render target from the provide render surface. Eg if its a texture,
* it will return the render target for the texture.
* If its a render target, it will return the same render target.
* @param renderSurface - the render surface to get the render target for
* @returns the render target for the render surface
*/
getRenderTarget(renderSurface) {
if (renderSurface.isTexture) {
renderSurface = renderSurface.source;
}
return this._renderSurfaceToRenderTargetHash.get(renderSurface) ?? this._initRenderTarget(renderSurface);
}
/**
* Copies a render surface to another texture
* @param sourceRenderSurfaceTexture - the render surface to copy from
* @param destinationTexture - the texture to copy to
* @param originSrc - the origin of the copy
* @param originSrc.x - the x origin of the copy
* @param originSrc.y - the y origin of the copy
* @param size - the size of the copy
* @param size.width - the width of the copy
* @param size.height - the height of the copy
* @param originDest - the destination origin (top left to paste from!)
* @param originDest.x - the x origin of the paste
* @param originDest.y - the y origin of the paste
*/
copyToTexture(sourceRenderSurfaceTexture, destinationTexture, originSrc, size, originDest) {
if (originSrc.x < 0) {
size.width += originSrc.x;
originDest.x -= originSrc.x;
originSrc.x = 0;
}
if (originSrc.y < 0) {
size.height += originSrc.y;
originDest.y -= originSrc.y;
originSrc.y = 0;
}
const { pixelWidth, pixelHeight } = sourceRenderSurfaceTexture;
size.width = Math.min(size.width, pixelWidth - originSrc.x);
size.height = Math.min(size.height, pixelHeight - originSrc.y);
return this.adaptor.copyToTexture(
sourceRenderSurfaceTexture,
destinationTexture,
originSrc,
size,
originDest
);
}
/**
* ensures that we have a depth stencil buffer available to render to
* This is used by the mask system to make sure we have a stencil buffer.
*/
ensureDepthStencil() {
if (!this.renderTarget.stencil) {
this.renderTarget.stencil = true;
this.adaptor.startRenderPass(this.renderTarget, false, null, this.viewport);
}
}
/** nukes the render target system */
destroy() {
this._renderer = null;
this._renderSurfaceToRenderTargetHash.forEach((renderTarget, key) => {
if (renderTarget !== key) {
renderTarget.destroy();
}
});
this._renderSurfaceToRenderTargetHash.clear();
this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null);
}
_initRenderTarget(renderSurface) {
let renderTarget = null;
if (CanvasSource.CanvasSource.test(renderSurface)) {
renderSurface = getCanvasTexture.getCanvasTexture(renderSurface).source;
}
if (renderSurface instanceof RenderTarget.RenderTarget) {
renderTarget = renderSurface;
} else if (renderSurface instanceof TextureSource.TextureSource) {
renderTarget = new RenderTarget.RenderTarget({
colorTextures: [renderSurface]
});
if (CanvasSource.CanvasSource.test(renderSurface.source.resource)) {
renderTarget.isRoot = true;
}
renderSurface.once("destroy", () => {
renderTarget.destroy();
this._renderSurfaceToRenderTargetHash.delete(renderSurface);
const gpuRenderTarget = this._gpuRenderTargetHash[renderTarget.uid];
if (gpuRenderTarget) {
this._gpuRenderTargetHash[renderTarget.uid] = null;
this.adaptor.destroyGpuRenderTarget(gpuRenderTarget);
}
});
}
this._renderSurfaceToRenderTargetHash.set(renderSurface, renderTarget);
return renderTarget;
}
getGpuRenderTarget(renderTarget) {
return this._gpuRenderTargetHash[renderTarget.uid] || (this._gpuRenderTargetHash[renderTarget.uid] = this.adaptor.initGpuRenderTarget(renderTarget));
}
}
exports.RenderTargetSystem = RenderTargetSystem;
//# sourceMappingURL=RenderTargetSystem.js.map