You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
465 lines
13 KiB
JavaScript
465 lines
13 KiB
JavaScript
import Color from "../Core/Color.js";
|
|
import defined from "../Core/defined.js";
|
|
import destroyObject from "../Core/destroyObject.js";
|
|
import CesiumMath from "../Core/Math.js";
|
|
import ClearCommand from "../Renderer/ClearCommand.js";
|
|
import Framebuffer from "../Renderer/Framebuffer.js";
|
|
import Texture from "../Renderer/Texture.js";
|
|
|
|
/**
|
|
* Creates a minimal amount of textures and framebuffers.
|
|
*
|
|
* @alias PostProcessStageTextureCache
|
|
* @constructor
|
|
*
|
|
* @param {PostProcessStageCollection} postProcessStageCollection The post process collection.
|
|
*
|
|
* @private
|
|
*/
|
|
function PostProcessStageTextureCache(postProcessStageCollection) {
|
|
this._collection = postProcessStageCollection;
|
|
|
|
this._framebuffers = [];
|
|
this._stageNameToFramebuffer = {};
|
|
|
|
this._width = undefined;
|
|
this._height = undefined;
|
|
this._updateDependencies = false;
|
|
}
|
|
|
|
function getLastStageName(stage) {
|
|
while (defined(stage.length)) {
|
|
stage = stage.get(stage.length - 1);
|
|
}
|
|
return stage.name;
|
|
}
|
|
|
|
function getStageDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
stage,
|
|
previousName
|
|
) {
|
|
if (!stage.enabled || !stage._isSupported(context)) {
|
|
return previousName;
|
|
}
|
|
|
|
var stageDependencies = (dependencies[stage.name] = {});
|
|
if (defined(previousName)) {
|
|
var previous = collection.getStageByName(previousName);
|
|
stageDependencies[getLastStageName(previous)] = true;
|
|
}
|
|
var uniforms = stage.uniforms;
|
|
if (defined(uniforms)) {
|
|
var uniformNames = Object.getOwnPropertyNames(uniforms);
|
|
var uniformNamesLength = uniformNames.length;
|
|
for (var i = 0; i < uniformNamesLength; ++i) {
|
|
var value = uniforms[uniformNames[i]];
|
|
if (typeof value === "string") {
|
|
var dependent = collection.getStageByName(value);
|
|
if (defined(dependent)) {
|
|
stageDependencies[getLastStageName(dependent)] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return stage.name;
|
|
}
|
|
|
|
function getCompositeDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
composite,
|
|
previousName
|
|
) {
|
|
if (
|
|
(defined(composite.enabled) && !composite.enabled) ||
|
|
(defined(composite._isSupported) && !composite._isSupported(context))
|
|
) {
|
|
return previousName;
|
|
}
|
|
|
|
var originalDependency = previousName;
|
|
|
|
var inSeries =
|
|
!defined(composite.inputPreviousStageTexture) ||
|
|
composite.inputPreviousStageTexture;
|
|
var currentName = previousName;
|
|
var length = composite.length;
|
|
for (var i = 0; i < length; ++i) {
|
|
var stage = composite.get(i);
|
|
if (defined(stage.length)) {
|
|
currentName = getCompositeDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
stage,
|
|
previousName
|
|
);
|
|
} else {
|
|
currentName = getStageDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
stage,
|
|
previousName
|
|
);
|
|
}
|
|
// Stages in a series only depend on the previous stage
|
|
if (inSeries) {
|
|
previousName = currentName;
|
|
}
|
|
}
|
|
|
|
// Stages not in a series depend on every stage executed before it since it could reference it as a uniform.
|
|
// This prevents looking at the dependencies of each stage in the composite, but might create more framebuffers than necessary.
|
|
// In practice, there are only 2-3 stages in these composites.
|
|
var j;
|
|
var name;
|
|
if (!inSeries) {
|
|
for (j = 1; j < length; ++j) {
|
|
name = getLastStageName(composite.get(j));
|
|
var currentDependencies = dependencies[name];
|
|
for (var k = 0; k < j; ++k) {
|
|
currentDependencies[getLastStageName(composite.get(k))] = true;
|
|
}
|
|
}
|
|
} else {
|
|
for (j = 1; j < length; ++j) {
|
|
name = getLastStageName(composite.get(j));
|
|
if (!defined(dependencies[name])) {
|
|
dependencies[name] = {};
|
|
}
|
|
dependencies[name][originalDependency] = true;
|
|
}
|
|
}
|
|
|
|
return currentName;
|
|
}
|
|
|
|
function getDependencies(collection, context) {
|
|
var dependencies = {};
|
|
|
|
if (defined(collection.ambientOcclusion)) {
|
|
var ao = collection.ambientOcclusion;
|
|
var bloom = collection.bloom;
|
|
var tonemapping = collection._tonemapping;
|
|
var fxaa = collection.fxaa;
|
|
|
|
var previousName = getCompositeDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
ao,
|
|
undefined
|
|
);
|
|
previousName = getCompositeDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
bloom,
|
|
previousName
|
|
);
|
|
previousName = getStageDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
tonemapping,
|
|
previousName
|
|
);
|
|
previousName = getCompositeDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
collection,
|
|
previousName
|
|
);
|
|
getStageDependencies(collection, context, dependencies, fxaa, previousName);
|
|
} else {
|
|
getCompositeDependencies(
|
|
collection,
|
|
context,
|
|
dependencies,
|
|
collection,
|
|
undefined
|
|
);
|
|
}
|
|
|
|
return dependencies;
|
|
}
|
|
|
|
function getFramebuffer(cache, stageName, dependencies) {
|
|
var collection = cache._collection;
|
|
var stage = collection.getStageByName(stageName);
|
|
|
|
var textureScale = stage._textureScale;
|
|
var forcePowerOfTwo = stage._forcePowerOfTwo;
|
|
var pixelFormat = stage._pixelFormat;
|
|
var pixelDatatype = stage._pixelDatatype;
|
|
var clearColor = stage._clearColor;
|
|
|
|
var i;
|
|
var framebuffer;
|
|
var framebuffers = cache._framebuffers;
|
|
var length = framebuffers.length;
|
|
for (i = 0; i < length; ++i) {
|
|
framebuffer = framebuffers[i];
|
|
|
|
if (
|
|
textureScale !== framebuffer.textureScale ||
|
|
forcePowerOfTwo !== framebuffer.forcePowerOfTwo ||
|
|
pixelFormat !== framebuffer.pixelFormat ||
|
|
pixelDatatype !== framebuffer.pixelDatatype ||
|
|
!Color.equals(clearColor, framebuffer.clearColor)
|
|
) {
|
|
continue;
|
|
}
|
|
|
|
var stageNames = framebuffer.stages;
|
|
var stagesLength = stageNames.length;
|
|
var foundConflict = false;
|
|
for (var j = 0; j < stagesLength; ++j) {
|
|
if (dependencies[stageNames[j]]) {
|
|
foundConflict = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundConflict) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (defined(framebuffer) && i < length) {
|
|
framebuffer.stages.push(stageName);
|
|
return framebuffer;
|
|
}
|
|
|
|
framebuffer = {
|
|
textureScale: textureScale,
|
|
forcePowerOfTwo: forcePowerOfTwo,
|
|
pixelFormat: pixelFormat,
|
|
pixelDatatype: pixelDatatype,
|
|
clearColor: clearColor,
|
|
stages: [stageName],
|
|
buffer: undefined,
|
|
clear: undefined,
|
|
};
|
|
|
|
framebuffers.push(framebuffer);
|
|
return framebuffer;
|
|
}
|
|
|
|
function createFramebuffers(cache, context) {
|
|
var dependencies = getDependencies(cache._collection, context);
|
|
for (var stageName in dependencies) {
|
|
if (dependencies.hasOwnProperty(stageName)) {
|
|
cache._stageNameToFramebuffer[stageName] = getFramebuffer(
|
|
cache,
|
|
stageName,
|
|
dependencies[stageName]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
function releaseResources(cache) {
|
|
var framebuffers = cache._framebuffers;
|
|
var length = framebuffers.length;
|
|
for (var i = 0; i < length; ++i) {
|
|
var framebuffer = framebuffers[i];
|
|
framebuffer.buffer = framebuffer.buffer && framebuffer.buffer.destroy();
|
|
framebuffer.buffer = undefined;
|
|
}
|
|
}
|
|
|
|
function updateFramebuffers(cache, context) {
|
|
var width = cache._width;
|
|
var height = cache._height;
|
|
|
|
var framebuffers = cache._framebuffers;
|
|
var length = framebuffers.length;
|
|
for (var i = 0; i < length; ++i) {
|
|
var framebuffer = framebuffers[i];
|
|
|
|
var scale = framebuffer.textureScale;
|
|
var textureWidth = Math.ceil(width * scale);
|
|
var textureHeight = Math.ceil(height * scale);
|
|
|
|
var size = Math.min(textureWidth, textureHeight);
|
|
if (framebuffer.forcePowerOfTwo) {
|
|
if (!CesiumMath.isPowerOfTwo(size)) {
|
|
size = CesiumMath.nextPowerOfTwo(size);
|
|
}
|
|
textureWidth = size;
|
|
textureHeight = size;
|
|
}
|
|
|
|
framebuffer.buffer = new Framebuffer({
|
|
context: context,
|
|
colorTextures: [
|
|
new Texture({
|
|
context: context,
|
|
width: textureWidth,
|
|
height: textureHeight,
|
|
pixelFormat: framebuffer.pixelFormat,
|
|
pixelDatatype: framebuffer.pixelDatatype,
|
|
}),
|
|
],
|
|
});
|
|
framebuffer.clear = new ClearCommand({
|
|
color: framebuffer.clearColor,
|
|
framebuffer: framebuffer.buffer,
|
|
});
|
|
}
|
|
}
|
|
|
|
PostProcessStageTextureCache.prototype.updateDependencies = function () {
|
|
this._updateDependencies = true;
|
|
};
|
|
|
|
/**
|
|
* Called before the stages in the collection are executed. Creates the minimum amount of framebuffers for a post-process collection.
|
|
*
|
|
* @param {Context} context The context.
|
|
*/
|
|
PostProcessStageTextureCache.prototype.update = function (context) {
|
|
var collection = this._collection;
|
|
var updateDependencies = this._updateDependencies;
|
|
var aoEnabled =
|
|
defined(collection.ambientOcclusion) &&
|
|
collection.ambientOcclusion.enabled &&
|
|
collection.ambientOcclusion._isSupported(context);
|
|
var bloomEnabled =
|
|
defined(collection.bloom) &&
|
|
collection.bloom.enabled &&
|
|
collection.bloom._isSupported(context);
|
|
var tonemappingEnabled =
|
|
defined(collection._tonemapping) &&
|
|
collection._tonemapping.enabled &&
|
|
collection._tonemapping._isSupported(context);
|
|
var fxaaEnabled =
|
|
defined(collection.fxaa) &&
|
|
collection.fxaa.enabled &&
|
|
collection.fxaa._isSupported(context);
|
|
var needsCheckDimensionsUpdate =
|
|
!defined(collection._activeStages) ||
|
|
collection._activeStages.length > 0 ||
|
|
aoEnabled ||
|
|
bloomEnabled ||
|
|
tonemappingEnabled ||
|
|
fxaaEnabled;
|
|
if (
|
|
updateDependencies ||
|
|
(!needsCheckDimensionsUpdate && this._framebuffers.length > 0)
|
|
) {
|
|
releaseResources(this);
|
|
this._framebuffers.length = 0;
|
|
this._stageNameToFramebuffer = {};
|
|
this._width = undefined;
|
|
this._height = undefined;
|
|
}
|
|
|
|
if (!updateDependencies && !needsCheckDimensionsUpdate) {
|
|
return;
|
|
}
|
|
|
|
if (this._framebuffers.length === 0) {
|
|
createFramebuffers(this, context);
|
|
}
|
|
|
|
var width = context.drawingBufferWidth;
|
|
var height = context.drawingBufferHeight;
|
|
var dimensionsChanged = this._width !== width || this._height !== height;
|
|
if (!updateDependencies && !dimensionsChanged) {
|
|
return;
|
|
}
|
|
|
|
this._width = width;
|
|
this._height = height;
|
|
this._updateDependencies = false;
|
|
releaseResources(this);
|
|
updateFramebuffers(this, context);
|
|
};
|
|
|
|
/**
|
|
* Clears all of the framebuffers.
|
|
*
|
|
* @param {Context} context The context.
|
|
*/
|
|
PostProcessStageTextureCache.prototype.clear = function (context) {
|
|
var framebuffers = this._framebuffers;
|
|
for (var i = 0; i < framebuffers.length; ++i) {
|
|
framebuffers[i].clear.execute(context);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Gets the stage with the given name.
|
|
* @param {String} name The name of the stage.
|
|
* @return {PostProcessStage|PostProcessStageComposite}
|
|
*/
|
|
PostProcessStageTextureCache.prototype.getStageByName = function (name) {
|
|
return this._collection.getStageByName(name);
|
|
};
|
|
|
|
/**
|
|
* Gets the output texture for a stage with the given name.
|
|
* @param {String} name The name of the stage.
|
|
* @return {Texture|undefined} The output texture of the stage with the given name.
|
|
*/
|
|
PostProcessStageTextureCache.prototype.getOutputTexture = function (name) {
|
|
return this._collection.getOutputTexture(name);
|
|
};
|
|
|
|
/**
|
|
* Gets the framebuffer for a stage with the given name.
|
|
*
|
|
* @param {String} name The name of the stage.
|
|
* @return {Framebuffer|undefined} The framebuffer for the stage with the given name.
|
|
*/
|
|
PostProcessStageTextureCache.prototype.getFramebuffer = function (name) {
|
|
var framebuffer = this._stageNameToFramebuffer[name];
|
|
if (!defined(framebuffer)) {
|
|
return undefined;
|
|
}
|
|
return framebuffer.buffer;
|
|
};
|
|
|
|
/**
|
|
* Returns true if this object was destroyed; otherwise, false.
|
|
* <p>
|
|
* If this object was destroyed, it should not be used; calling any function other than
|
|
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
|
|
* </p>
|
|
*
|
|
* @returns {Boolean} <code>true</code> if this object was destroyed; otherwise, <code>false</code>.
|
|
*
|
|
* @see PostProcessStageTextureCache#destroy
|
|
*/
|
|
PostProcessStageTextureCache.prototype.isDestroyed = function () {
|
|
return false;
|
|
};
|
|
|
|
/**
|
|
* Destroys the WebGL resources held by this object. Destroying an object allows for deterministic
|
|
* release of WebGL resources, instead of relying on the garbage collector to destroy this object.
|
|
* <p>
|
|
* Once an object is destroyed, it should not be used; calling any function other than
|
|
* <code>isDestroyed</code> will result in a {@link DeveloperError} exception. Therefore,
|
|
* assign the return value (<code>undefined</code>) to the object as done in the example.
|
|
* </p>
|
|
*
|
|
* @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
|
|
*
|
|
* @see PostProcessStageTextureCache#isDestroyed
|
|
*/
|
|
PostProcessStageTextureCache.prototype.destroy = function () {
|
|
releaseResources(this);
|
|
return destroyObject(this);
|
|
};
|
|
export default PostProcessStageTextureCache;
|