Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throttle total resources used for environment maps #12320

Merged
merged 5 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
##### Fixes :wrench:

- Fix label rendering bug in WebGL1 contexts. [#12301](https://github.com/CesiumGS/cesium/pull/12301)
- Fixed lag or crashes when loading many models in the same frame. [#12320](https://github.com/CesiumGS/cesium/pull/12320)
- Updated WMS example URL in UrlTemplateImageryProvider documentation to use an active service. [#12323](https://github.com/CesiumGS/cesium/pull/12323)
- Fix point cloud filtering performance on certain hardware [#12317](https://github.com/CesiumGS/cesium/pull/12317)

Expand Down
2 changes: 1 addition & 1 deletion packages/engine/Source/Renderer/ContextLimits.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Object.defineProperties(ContextLimits, {
},

/**
* The approximate maximum cube mape width and height supported by this WebGL implementation.
* The approximate maximum cube map width and height supported by this WebGL implementation.
* The minimum is 16, but most desktop and laptop implementations will support much larger sizes like 8,192.
* @memberof ContextLimits
* @type {number}
Expand Down
94 changes: 87 additions & 7 deletions packages/engine/Source/Scene/DynamicEnvironmentMapManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import PixelFormat from "../Core/PixelFormat.js";
import SceneMode from "./SceneMode.js";
import Transforms from "../Core/Transforms.js";
import ComputeCommand from "../Renderer/ComputeCommand.js";
import ContextLimits from "../Renderer/ContextLimits.js";
import CubeMap from "../Renderer/CubeMap.js";
import Framebuffer from "../Renderer/Framebuffer.js";
import Texture from "../Renderer/Texture.js";
Expand Down Expand Up @@ -79,7 +80,11 @@ function DynamicEnvironmentMapManager(options) {

options = defaultValue(options, defaultValue.EMPTY_OBJECT);

const mipmapLevels = defaultValue(options.mipmapLevels, 10);
const mipmapLevels = Math.min(
defaultValue(options.mipmapLevels, 10),
Math.log2(ContextLimits.maximumCubeMapSize),
);

this._mipmapLevels = mipmapLevels;
this._radianceMapComputeCommands = new Array(6);
this._convolutionComputeCommands = new Array((mipmapLevels - 1) * 6);
Expand Down Expand Up @@ -295,6 +300,63 @@ Object.defineProperties(DynamicEnvironmentMapManager.prototype, {
},
});

// Internally manage a queue of commands across all instances to prevent too many commands from being added in a single frame and using too much memory at once.
DynamicEnvironmentMapManager._maximumComputeCommandCount = 8; // This value is updated once a context is created.
DynamicEnvironmentMapManager._activeComputeCommandCount = 0;
DynamicEnvironmentMapManager._nextFrameCommandQueue = [];
/**
* Add a command to the queue. If possible, it will be added to the list of commands for the next frame. Otherwise, it will be added to a backlog
* and attempted next frame.
* @private
* @param {ComputeCommand} command The created command
* @param {FrameState} frameState The current frame state
*/
DynamicEnvironmentMapManager._queueCommand = (command, frameState) => {
if (
DynamicEnvironmentMapManager._activeComputeCommandCount >=
DynamicEnvironmentMapManager._maximumComputeCommandCount
) {
// Command will instead be scheduled next frame
DynamicEnvironmentMapManager._nextFrameCommandQueue.push(command);
return;
}

frameState.commandList.push(command);
DynamicEnvironmentMapManager._activeComputeCommandCount++;
};
/**
* If there are any backlogged commands, queue up as many as possible for the next frame.
* @private
* @param {FrameState} frameState The current frame state
*/
DynamicEnvironmentMapManager._updateCommandQueue = (frameState) => {
DynamicEnvironmentMapManager._maximumComputeCommandCount = Math.log2(
ContextLimits.maximumCubeMapSize,
); // Scale relative to GPU resources available

if (
DynamicEnvironmentMapManager._nextFrameCommandQueue.length > 0 &&
DynamicEnvironmentMapManager._activeComputeCommandCount <
DynamicEnvironmentMapManager._maximumComputeCommandCount
) {
let command = DynamicEnvironmentMapManager._nextFrameCommandQueue.shift();
while (
defined(command) &&
DynamicEnvironmentMapManager._activeComputeCommandCount <
DynamicEnvironmentMapManager._maximumComputeCommandCount
) {
if (command.canceled) {
command = DynamicEnvironmentMapManager._nextFrameCommandQueue.shift();
continue;
}

frameState.commandList.push(command);
DynamicEnvironmentMapManager._activeComputeCommandCount++;
command = DynamicEnvironmentMapManager._nextFrameCommandQueue.shift();
}
}
};

/**
* Sets the owner for the input DynamicEnvironmentMapManager if there wasn't another owner.
* Destroys the owner's previous DynamicEnvironmentMapManager if setting is successful.
Expand Down Expand Up @@ -334,15 +396,25 @@ DynamicEnvironmentMapManager.setOwner = function (
DynamicEnvironmentMapManager.prototype.reset = function () {
let length = this._radianceMapComputeCommands.length;
for (let i = 0; i < length; ++i) {
if (defined(this._radianceMapComputeCommands[i])) {
this._radianceMapComputeCommands[i].canceled = true;
DynamicEnvironmentMapManager._activeComputeCommandCount--;
}
this._radianceMapComputeCommands[i] = undefined;
}

length = this._convolutionComputeCommands.length;
for (let i = 0; i < length; ++i) {
if (defined(this._convolutionComputeCommands[i])) {
this._convolutionComputeCommands[i].canceled = true;
DynamicEnvironmentMapManager._activeComputeCommandCount--;
}
this._convolutionComputeCommands[i] = undefined;
}

if (defined(this._irradianceComputeCommand)) {
this._irradianceComputeCommand.canceled = true;
DynamicEnvironmentMapManager._activeComputeCommandCount--;
this._irradianceComputeCommand = undefined;
}

Expand Down Expand Up @@ -520,14 +592,17 @@ function updateRadianceMap(manager, frameState) {
framebuffer._unBind();
framebuffer.destroy();

DynamicEnvironmentMapManager._activeComputeCommandCount--;

if (!commands.some(defined)) {
manager._convolutionsCommandsDirty = true;
manager._shouldRegenerateShaders = true;
}
},
});
frameState.commandList.push(command);

manager._radianceMapComputeCommands[i] = command;
DynamicEnvironmentMapManager._queueCommand(command, frameState);
i++;
}
manager._radianceCommandsDirty = false;
Expand All @@ -554,14 +629,15 @@ function updateSpecularMaps(manager, frameState) {
const getPostExecute = (index, texture, face, level) => () => {
// Copy output texture to corresponding face and mipmap level
const commands = manager._convolutionComputeCommands;
if (!defined(commands[index])) {
if (!defined(commands[index]) || commands[index].canceled) {
// This command was cancelled
return;
}
commands[index] = undefined;

radianceCubeMap.copyFace(frameState, texture, face, level);
facesCopied++;
DynamicEnvironmentMapManager._activeComputeCommandCount--;

// All faces and levels have been copied
if (facesCopied === manager._specularMapTextures.length) {
Expand Down Expand Up @@ -611,15 +687,15 @@ function updateSpecularMaps(manager, frameState) {
owner: manager,
uniformMap: {
u_roughness: () => level / (mipmapLevels - 1),
u_radianceTexture: () => radianceCubeMap,
u_radianceTexture: () => radianceCubeMap ?? context.defaultTexture,
u_faceDirection: () => {
return CubeMap.getDirection(face, scratchCartesian);
},
},
postExecute: getPostExecute(index, texture, face, level),
});
manager._convolutionComputeCommands[index] = command;
frameState.commandList.push(command);
DynamicEnvironmentMapManager._queueCommand(command, frameState);
++index;
}

Expand Down Expand Up @@ -664,7 +740,7 @@ function updateIrradianceResources(manager, frameState) {
fragmentShaderSource: fs,
outputTexture: texture,
uniformMap: {
u_radianceMap: () => manager._radianceCubeMap,
u_radianceMap: () => manager._radianceCubeMap ?? context.defaultTexture,
},
postExecute: () => {
if (!defined(manager._irradianceComputeCommand)) {
Expand All @@ -674,10 +750,12 @@ function updateIrradianceResources(manager, frameState) {
manager._irradianceTextureDirty = false;
manager._irradianceComputeCommand = undefined;
manager._sphericalHarmonicCoefficientsDirty = true;

DynamicEnvironmentMapManager._activeComputeCommandCount--;
},
});
manager._irradianceComputeCommand = command;
frameState.commandList.push(command);
DynamicEnvironmentMapManager._queueCommand(command, frameState);
manager._irradianceTextureDirty = true;
}

Expand Down Expand Up @@ -744,6 +822,8 @@ DynamicEnvironmentMapManager.prototype.update = function (frameState) {
return;
}

DynamicEnvironmentMapManager._updateCommandQueue(frameState);

const dynamicLighting = frameState.atmosphere.dynamicLighting;
const regenerateEnvironmentMap =
atmosphereNeedsUpdate(this, frameState) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
Cartesian3,
Cartographic,
Color,
ContextLimits,
CubeMap,
DynamicAtmosphereLightingType,
DynamicEnvironmentMapManager,
Expand Down Expand Up @@ -70,16 +71,20 @@ describe("Scene/DynamicEnvironmentMapManager", function () {
() => {
const time = JulianDate.fromIso8601("2024-08-30T10:45:00Z");

let scene;
let scene, orginalMaximumCubeMapSize;

beforeAll(() => {
scene = createScene({
skyBox: false,
});
orginalMaximumCubeMapSize = ContextLimits.maximumCubeMapSize;
// To keep tests fast, don't throttle
ContextLimits._maximumCubeMapSize = Number.POSITIVE_INFINITY;
});

afterAll(() => {
scene.destroyForSpecs();
ContextLimits._maximumCubeMapSize = orginalMaximumCubeMapSize;
});

afterEach(() => {
Expand Down