Skip to content

Commit 47b5d29

Browse files
Merge pull request #12370 from CesiumGS/env-map-memory
Fix memory usage for environment map manager
2 parents 6d02388 + 2464be8 commit 47b5d29

File tree

3 files changed

+183
-124
lines changed

3 files changed

+183
-124
lines changed

packages/engine/Source/Scene/DynamicEnvironmentMapManager.js

+123-66
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import ConvolveSpecularMapVS from "../Shaders/ConvolveSpecularMapVS.js";
3333
* @typedef {object} DynamicEnvironmentMapManager.ConstructorOptions
3434
* Options for the DynamicEnvironmentMapManager constructor
3535
* @property {boolean} [enabled=true] If true, the environment map and related properties will continue to update.
36-
* @property {number} [mipmapLevels=10] The number of mipmap levels to generate for specular maps. More mipmap levels will produce a higher resolution specular reflection.
36+
* @property {number} [mipmapLevels=7] The number of mipmap levels to generate for specular maps. More mipmap levels will produce a higher resolution specular reflection.
3737
* @property {number} [maximumSecondsDifference=3600] The maximum amount of elapsed seconds before a new environment map is created.
3838
* @property {number} [maximumPositionEpsilon=1000] The maximum difference in position before a new environment map is created, in meters. Small differences in position will not visibly affect results.
3939
* @property {number} [atmosphereScatteringIntensity=2.0] The intensity of the scattered light emitted from the atmosphere. This should be adjusted relative to the value of {@link Scene.light} intensity.
@@ -77,11 +77,12 @@ function DynamicEnvironmentMapManager(options) {
7777
this._sphericalHarmonicCoefficientsDirty = false;
7878

7979
this._shouldRegenerateShaders = false;
80+
this._shouldReset = false;
8081

8182
options = defaultValue(options, defaultValue.EMPTY_OBJECT);
8283

8384
const mipmapLevels = Math.min(
84-
defaultValue(options.mipmapLevels, 10),
85+
defaultValue(options.mipmapLevels, 7),
8586
Math.log2(ContextLimits.maximumCubeMapSize),
8687
);
8788

@@ -251,7 +252,7 @@ Object.defineProperties(DynamicEnvironmentMapManager.prototype, {
251252
}
252253

253254
this._position = Cartesian3.clone(value, this._position);
254-
this.reset();
255+
this._shouldReset = true;
255256
},
256257
},
257258

@@ -345,7 +346,7 @@ DynamicEnvironmentMapManager._updateCommandQueue = (frameState) => {
345346
DynamicEnvironmentMapManager._activeComputeCommandCount <
346347
DynamicEnvironmentMapManager._maximumComputeCommandCount
347348
) {
348-
if (command.canceled) {
349+
if (command.owner.isDestroyed() || command.canceled) {
349350
command = DynamicEnvironmentMapManager._nextFrameCommandQueue.shift();
350351
continue;
351352
}
@@ -354,6 +355,10 @@ DynamicEnvironmentMapManager._updateCommandQueue = (frameState) => {
354355
DynamicEnvironmentMapManager._activeComputeCommandCount++;
355356
command = DynamicEnvironmentMapManager._nextFrameCommandQueue.shift();
356357
}
358+
359+
if (defined(command)) {
360+
DynamicEnvironmentMapManager._nextFrameCommandQueue.push(command);
361+
}
357362
}
358363
};
359364

@@ -398,7 +403,6 @@ DynamicEnvironmentMapManager.prototype.reset = function () {
398403
for (let i = 0; i < length; ++i) {
399404
if (defined(this._radianceMapComputeCommands[i])) {
400405
this._radianceMapComputeCommands[i].canceled = true;
401-
DynamicEnvironmentMapManager._activeComputeCommandCount--;
402406
}
403407
this._radianceMapComputeCommands[i] = undefined;
404408
}
@@ -407,19 +411,19 @@ DynamicEnvironmentMapManager.prototype.reset = function () {
407411
for (let i = 0; i < length; ++i) {
408412
if (defined(this._convolutionComputeCommands[i])) {
409413
this._convolutionComputeCommands[i].canceled = true;
410-
DynamicEnvironmentMapManager._activeComputeCommandCount--;
411414
}
412415
this._convolutionComputeCommands[i] = undefined;
413416
}
414417

415418
if (defined(this._irradianceComputeCommand)) {
416419
this._irradianceComputeCommand.canceled = true;
417-
DynamicEnvironmentMapManager._activeComputeCommandCount--;
418420
this._irradianceComputeCommand = undefined;
419421
}
420422

421423
this._radianceMapDirty = true;
422424
this._radianceCommandsDirty = true;
425+
this._convolutionsCommandsDirty = false;
426+
this._irradianceCommandDirty = false;
423427
};
424428

425429
const scratchPackedAtmosphere = new Cartesian3();
@@ -539,7 +543,8 @@ function updateRadianceMap(manager, frameState) {
539543
let i = 0;
540544
for (const face of CubeMap.faceNames()) {
541545
let texture = manager._radianceMapTextures[i];
542-
if (defined(texture)) {
546+
// Destroy any existing textures that have no yet been cleaned up
547+
if (defined(texture) && !texture.isDestroyed()) {
543548
texture.destroy();
544549
}
545550

@@ -570,36 +575,35 @@ function updateRadianceMap(manager, frameState) {
570575
);
571576
},
572577
},
573-
persists: true,
574578
owner: manager,
575-
postExecute: () => {
576-
const commands = manager._radianceMapComputeCommands;
577-
if (!defined(commands[index])) {
578-
// This command was cancelled
579-
return;
580-
}
581-
commands[index] = undefined;
582-
583-
const framebuffer = new Framebuffer({
584-
context: context,
585-
colorTextures: [manager._radianceMapTextures[index]],
586-
destroyAttachments: false,
587-
});
588-
589-
// Copy the output texture into the corresponding cubemap face
590-
framebuffer._bind();
591-
manager._radianceCubeMap[face].copyFromFramebuffer();
592-
framebuffer._unBind();
593-
framebuffer.destroy();
594-
579+
});
580+
command.postExecute = () => {
581+
if (manager.isDestroyed() || command.canceled) {
595582
DynamicEnvironmentMapManager._activeComputeCommandCount--;
583+
return;
584+
}
596585

597-
if (!commands.some(defined)) {
598-
manager._convolutionsCommandsDirty = true;
599-
manager._shouldRegenerateShaders = true;
600-
}
601-
},
602-
});
586+
const commands = manager._radianceMapComputeCommands;
587+
commands[index] = undefined;
588+
589+
const framebuffer = new Framebuffer({
590+
context: context,
591+
colorTextures: [manager._radianceMapTextures[index]],
592+
});
593+
594+
// Copy the output texture into the corresponding cubemap face
595+
framebuffer._bind();
596+
manager._radianceCubeMap[face].copyFromFramebuffer();
597+
framebuffer._unBind();
598+
framebuffer.destroy();
599+
600+
DynamicEnvironmentMapManager._activeComputeCommandCount--;
601+
602+
if (!commands.some(defined)) {
603+
manager._convolutionsCommandsDirty = true;
604+
manager._shouldRegenerateShaders = true;
605+
}
606+
};
603607

604608
manager._radianceMapComputeCommands[i] = command;
605609
DynamicEnvironmentMapManager._queueCommand(command, frameState);
@@ -626,32 +630,48 @@ function updateSpecularMaps(manager, frameState) {
626630
const context = frameState.context;
627631

628632
let facesCopied = 0;
629-
const getPostExecute = (index, texture, face, level) => () => {
630-
// Copy output texture to corresponding face and mipmap level
631-
const commands = manager._convolutionComputeCommands;
632-
if (!defined(commands[index]) || commands[index].canceled) {
633-
// This command was cancelled
633+
const getPostExecute = (command, index, texture, face, level) => () => {
634+
if (manager.isDestroyed() || command.canceled) {
635+
DynamicEnvironmentMapManager._activeComputeCommandCount--;
634636
return;
635637
}
638+
639+
// Copy output texture to corresponding face and mipmap level
640+
const commands = manager._convolutionComputeCommands;
636641
commands[index] = undefined;
637642

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

642-
// All faces and levels have been copied
643-
if (facesCopied === manager._specularMapTextures.length) {
647+
texture.destroy();
648+
manager._specularMapTextures[index] = undefined;
649+
650+
// All faces for each mipmap level have been copied
651+
const length = manager._specularMapTextures.length;
652+
if (facesCopied >= length) {
644653
manager._irradianceCommandDirty = true;
645654
radianceCubeMap.sampler = new Sampler({
646655
minificationFilter: TextureMinificationFilter.LINEAR_MIPMAP_LINEAR,
647656
});
657+
648658
manager._shouldRegenerateShaders = true;
659+
660+
// Cleanup shared resources
661+
manager._va.destroy();
662+
manager._va = undefined;
663+
manager._convolveSP.destroy();
664+
manager._convolveSP = undefined;
649665
}
650666
};
651667

652668
let index = 0;
653669
for (let level = 1; level < mipmapLevels; ++level) {
654670
for (const face of CubeMap.faceNames()) {
671+
if (defined(manager._specularMapTextures[index])) {
672+
manager._specularMapTextures[index].destroy();
673+
}
674+
655675
const texture = (manager._specularMapTextures[index] = new Texture({
656676
context: context,
657677
width: width,
@@ -683,6 +703,8 @@ function updateSpecularMaps(manager, frameState) {
683703
shaderProgram: shaderProgram,
684704
vertexArray: vertexArray,
685705
outputTexture: texture,
706+
// Persist so we can use a shared shader progam and vertex array across all commands
707+
// Shared resources are instead destroyed in postExecute
686708
persists: true,
687709
owner: manager,
688710
uniformMap: {
@@ -692,8 +714,14 @@ function updateSpecularMaps(manager, frameState) {
692714
return CubeMap.getDirection(face, scratchCartesian);
693715
},
694716
},
695-
postExecute: getPostExecute(index, texture, face, level),
696717
});
718+
command.postExecute = getPostExecute(
719+
command,
720+
index,
721+
texture,
722+
face,
723+
level,
724+
);
697725
manager._convolutionComputeCommands[index] = command;
698726
DynamicEnvironmentMapManager._queueCommand(command, frameState);
699727
++index;
@@ -717,17 +745,19 @@ function updateIrradianceResources(manager, frameState) {
717745
const dimensions = irradianceTextureDimensions;
718746

719747
let texture = manager._irradianceMapTexture;
720-
if (!defined(texture)) {
721-
texture = new Texture({
722-
context: context,
723-
width: dimensions.x,
724-
height: dimensions.y,
725-
pixelDatatype: PixelDatatype.FLOAT,
726-
pixelFormat: PixelFormat.RGBA,
727-
});
728-
manager._irradianceMapTexture = texture;
748+
if (defined(texture) && !texture.isDestroyed()) {
749+
texture.destroy();
729750
}
730751

752+
texture = new Texture({
753+
context: context,
754+
width: dimensions.x,
755+
height: dimensions.y,
756+
pixelDatatype: PixelDatatype.FLOAT,
757+
pixelFormat: PixelFormat.RGBA,
758+
});
759+
manager._irradianceMapTexture = texture;
760+
731761
let fs = manager._irradianceMapFS;
732762
if (!defined(fs)) {
733763
fs = new ShaderSource({
@@ -739,21 +769,25 @@ function updateIrradianceResources(manager, frameState) {
739769
const command = new ComputeCommand({
740770
fragmentShaderSource: fs,
741771
outputTexture: texture,
772+
owner: manager,
742773
uniformMap: {
743774
u_radianceMap: () => manager._radianceCubeMap ?? context.defaultTexture,
744775
},
745-
postExecute: () => {
746-
if (!defined(manager._irradianceComputeCommand)) {
747-
// This command was cancelled
748-
return;
749-
}
750-
manager._irradianceTextureDirty = false;
751-
manager._irradianceComputeCommand = undefined;
752-
manager._sphericalHarmonicCoefficientsDirty = true;
776+
});
753777

778+
command.postExecute = () => {
779+
if (manager.isDestroyed() || command.canceled) {
754780
DynamicEnvironmentMapManager._activeComputeCommandCount--;
755-
},
756-
});
781+
return;
782+
}
783+
manager._irradianceTextureDirty = false;
784+
manager._irradianceComputeCommand = undefined;
785+
manager._sphericalHarmonicCoefficientsDirty = true;
786+
manager._irradianceMapFS = undefined;
787+
788+
DynamicEnvironmentMapManager._activeComputeCommandCount--;
789+
};
790+
757791
manager._irradianceComputeCommand = command;
758792
DynamicEnvironmentMapManager._queueCommand(command, frameState);
759793
manager._irradianceTextureDirty = true;
@@ -768,6 +802,11 @@ function updateIrradianceResources(manager, frameState) {
768802
function updateSphericalHarmonicCoefficients(manager, frameState) {
769803
const context = frameState.context;
770804

805+
if (!defined(manager._irradianceMapTexture)) {
806+
// Operation was canceled
807+
return;
808+
}
809+
771810
const framebuffer = new Framebuffer({
772811
context: context,
773812
colorTextures: [manager._irradianceMapTexture],
@@ -793,6 +832,8 @@ function updateSphericalHarmonicCoefficients(manager, frameState) {
793832
}
794833

795834
framebuffer.destroy();
835+
manager._irradianceMapTexture.destroy();
836+
manager._irradianceMapTexture = undefined;
796837
manager._shouldRegenerateShaders = true;
797838
}
798839

@@ -834,9 +875,11 @@ DynamicEnvironmentMapManager.prototype.update = function (frameState) {
834875
this.maximumSecondsDifference,
835876
));
836877

837-
if (regenerateEnvironmentMap) {
878+
if (this._shouldReset || regenerateEnvironmentMap) {
838879
this.reset();
880+
this._shouldReset = false;
839881
this._lastTime = JulianDate.clone(frameState.time, this._lastTime);
882+
return;
840883
}
841884

842885
if (this._radianceMapDirty) {
@@ -910,19 +953,33 @@ DynamicEnvironmentMapManager.prototype.destroy = function () {
910953
length = this._radianceMapTextures.length;
911954
for (let i = 0; i < length; ++i) {
912955
this._radianceMapTextures[i] =
913-
this._radianceMapTextures[i] && this._radianceMapTextures[i].destroy();
956+
this._radianceMapTextures[i] &&
957+
!this._radianceMapTextures[i].isDestroyed() &&
958+
this._radianceMapTextures[i].destroy();
914959
}
915960

916961
length = this._specularMapTextures.length;
917962
for (let i = 0; i < length; ++i) {
918963
this._specularMapTextures[i] =
919-
this._specularMapTextures[i] && this._specularMapTextures[i].destroy();
964+
this._specularMapTextures[i] &&
965+
!this._specularMapTextures[i].isDestroyed() &&
966+
this._specularMapTextures[i].destroy();
920967
}
921968

922969
this._radianceCubeMap =
923970
this._radianceCubeMap && this._radianceCubeMap.destroy();
924971
this._irradianceMapTexture =
925-
this._irradianceMapTexture && this._irradianceMapTexture.destroy();
972+
this._irradianceMapTexture &&
973+
!this._irradianceMapTexture.isDestroyed() &&
974+
this._irradianceMapTexture.destroy();
975+
976+
if (defined(this._va)) {
977+
this._va.destroy();
978+
}
979+
980+
if (defined(this._convolveSP)) {
981+
this._convolveSP.destroy();
982+
}
926983

927984
return destroyObject(this);
928985
};

packages/engine/Specs/Scene/Cesium3DTilesetSpec.js

+2
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,8 @@ describe(
487487
}),
488488
);
489489
expect(root.contentFailed).toBeTrue();
490+
491+
await Cesium3DTilesTester.waitForTilesLoaded(scene, tileset);
490492
});
491493

492494
it("handles failed tile requests", async function () {

0 commit comments

Comments
 (0)