@babylonjs/core
Version:
Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
148 lines • 6.19 kB
JavaScript
import { Observable } from "./observable.js";
import { EffectWrapper } from "../Materials/effectRenderer.js";
import { Engine } from "../Engines/engine.js";
/**
* @internal
*/
export var DepthTextureType;
(function (DepthTextureType) {
DepthTextureType[DepthTextureType["NormalizedViewDepth"] = 0] = "NormalizedViewDepth";
DepthTextureType[DepthTextureType["ViewDepth"] = 1] = "ViewDepth";
DepthTextureType[DepthTextureType["ScreenDepth"] = 2] = "ScreenDepth";
})(DepthTextureType || (DepthTextureType = {}));
/**
* @internal
*/
export class ThinMinMaxReducerPostProcess extends EffectWrapper {
_gatherImports(useWebGPU, list) {
if (useWebGPU) {
this._webGPUReady = true;
list.push(import("../ShadersWGSL/minmaxRedux.fragment.js"));
}
else {
list.push(import("../Shaders/minmaxRedux.fragment.js"));
}
}
constructor(name, engine = null, defines = "", options) {
super({
...options,
name,
engine: engine || Engine.LastCreatedEngine,
useShaderStore: true,
useAsPostProcess: true,
fragmentShader: ThinMinMaxReducerPostProcess.FragmentUrl,
uniforms: ThinMinMaxReducerPostProcess.Uniforms,
defines,
});
this.textureWidth = 0;
this.textureHeight = 0;
}
bind(noDefaultBindings = false) {
super.bind(noDefaultBindings);
const effect = this.drawWrapper.effect;
if (this.textureWidth === 1 || this.textureHeight === 1) {
effect.setInt2("texSize", this.textureWidth, this.textureHeight);
}
else {
effect.setFloat2("texSize", this.textureWidth, this.textureHeight);
}
}
}
ThinMinMaxReducerPostProcess.FragmentUrl = "minmaxRedux";
ThinMinMaxReducerPostProcess.Uniforms = ["texSize"];
const BufferFloat = new Float32Array(4 * 1 * 1);
const BufferUint8 = new Uint8Array(4 * 1 * 1);
const MinMax = { min: 0, max: 0 };
/**
* @internal
*/
export class ThinMinMaxReducer {
get depthRedux() {
return this._depthRedux;
}
set depthRedux(value) {
if (this._depthRedux === value) {
return;
}
this._depthRedux = value;
this._recreatePostProcesses();
}
get textureWidth() {
return this._textureWidth;
}
get textureHeight() {
return this._textureHeight;
}
constructor(scene, depthRedux = true) {
this.onAfterReductionPerformed = new Observable();
this._textureWidth = 0;
this._textureHeight = 0;
this._scene = scene;
this._depthRedux = depthRedux;
this.reductionSteps = [];
}
setTextureDimensions(width, height, depthTextureType = 0 /* DepthTextureType.NormalizedViewDepth */) {
if (width === this._textureWidth && height === this._textureHeight && depthTextureType === this._depthTextureType) {
return false;
}
this._textureWidth = width;
this._textureHeight = height;
this._depthTextureType = depthTextureType;
this._recreatePostProcesses();
return true;
}
readMinMax(texture) {
// Note that we should normally await the call to _readTexturePixels!
// But because WebGL does the read synchronously, we know the values will be updated without waiting for the promise to be resolved, which will let us get the updated values
// in the current frame, whereas in WebGPU, the read is asynchronous and we should normally wait for the promise to be resolved to get the updated values.
// However, it's safe to avoid waiting for the promise to be resolved in WebGPU as well, because we will simply use the current values until "buffer" is updated later on.
// Note that it means we can suffer some rendering artifacts in WebGPU because we may use previous min/max values for the current frame.
const isFloat = texture.type === Engine.TEXTURETYPE_FLOAT || texture.type === Engine.TEXTURETYPE_HALF_FLOAT;
const buffer = isFloat ? BufferFloat : BufferUint8;
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this._scene.getEngine()._readTexturePixels(texture, 1, 1, -1, 0, buffer, false);
MinMax.min = buffer[0];
MinMax.max = buffer[1];
if (!isFloat) {
MinMax.min = MinMax.min / 255.0;
MinMax.max = MinMax.max / 255.0;
}
if (MinMax.min >= MinMax.max) {
MinMax.min = 0;
MinMax.max = 1;
}
this.onAfterReductionPerformed.notifyObservers(MinMax);
}
dispose(disposeAll = true) {
if (disposeAll) {
this.onAfterReductionPerformed.clear();
this._textureWidth = 0;
this._textureHeight = 0;
}
for (let i = 0; i < this.reductionSteps.length; ++i) {
this.reductionSteps[i].dispose();
}
this.reductionSteps.length = 0;
}
_recreatePostProcesses() {
this.dispose(false);
const scene = this._scene;
let w = this.textureWidth, h = this.textureHeight;
const reductionInitial = new ThinMinMaxReducerPostProcess("Initial reduction phase", scene.getEngine(), "#define INITIAL" + (this._depthRedux ? "\n#define DEPTH_REDUX" : "") + (this._depthTextureType === 1 /* DepthTextureType.ViewDepth */ ? "\n#define VIEW_DEPTH" : ""));
reductionInitial.textureWidth = w;
reductionInitial.textureHeight = h;
this.reductionSteps.push(reductionInitial);
let index = 1;
// create the additional steps
while (w > 1 || h > 1) {
w = Math.max(Math.round(w / 2), 1);
h = Math.max(Math.round(h / 2), 1);
const reduction = new ThinMinMaxReducerPostProcess("Reduction phase " + index, scene.getEngine(), "#define " + (w == 1 && h == 1 ? "LAST" : w == 1 || h == 1 ? "ONEBEFORELAST" : "MAIN"));
reduction.textureWidth = w;
reduction.textureHeight = h;
this.reductionSteps.push(reduction);
index++;
}
}
}
//# sourceMappingURL=thinMinMaxReducer.js.map