diff --git a/CHANGES.md b/CHANGES.md
index 0afa5f0dbc3..538c9b575a8 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -9,6 +9,7 @@
- Materials loaded from type now respect submaterials present in the referenced material type. [#10566](https://github.com/CesiumGS/cesium/issues/10566)
- Reverts `createImageBitmap` options update to continue support for older browsers [#12846](https://github.com/CesiumGS/cesium/issues/12846)
- Fix flickering artifact in Gaussian splat models caused by incorrect sorting results. [#12662](https://github.com/CesiumGS/cesium/issues/12662)
+- Fix rendering for geometry entities when `requestRenderMode` is enabled. [#12841](https://github.com/CesiumGS/cesium/pull/12841)
- Improved performance and reduced memory usage of `Event` class. [#12896](https://github.com/CesiumGS/cesium/pull/12896)
- Fixes vertical misalignment of glyphs in labels with small fonts [#8474](https://github.com/CesiumGS/cesium/issues/8474)
- Prevent runtime errors for certain forms of invalid PNTS files [#12872](https://github.com/CesiumGS/cesium/issues/12872)
diff --git a/packages/engine/Source/Scene/Primitive.js b/packages/engine/Source/Scene/Primitive.js
index d2fab03f88d..6486bff22a8 100644
--- a/packages/engine/Source/Scene/Primitive.js
+++ b/packages/engine/Source/Scene/Primitive.js
@@ -2489,10 +2489,16 @@ Primitive.prototype.destroy = function () {
function setReady(primitive, frameState, state, error) {
primitive._error = error;
primitive._state = state;
+
frameState.afterRender.push(function () {
primitive._ready =
primitive._state === PrimitiveState.COMPLETE ||
primitive._state === PrimitiveState.FAILED;
+
+ // Returning 'true' here will ensure that another rendering pass is
+ // triggered after the primitive actually became ready, to make sure
+ // that it is in fact rendered even in "request render mode"
+ return true;
});
}
export default Primitive;
diff --git a/packages/engine/Source/Scene/PrimitiveState.js b/packages/engine/Source/Scene/PrimitiveState.js
index 08e862951b4..6420317b8f4 100644
--- a/packages/engine/Source/Scene/PrimitiveState.js
+++ b/packages/engine/Source/Scene/PrimitiveState.js
@@ -1,13 +1,136 @@
/**
+ * The states that describe the lifecycle of a Primitive, as
+ * represented by the primitive._state.
+ *
+ * The state transitions are triggered by calls to the update
+ * function, but the actual state changes may happen asynchronously if the
+ * asynchronous flag of the primitive was set to
+ * true.
+ *
* @private
*/
const PrimitiveState = {
+ /**
+ * The initial state of a primitive.
+ *
+ * Note that this does NOT mean that the primitive is "ready", as indicated
+ * by the _ready property. It means the opposite: Nothing was
+ * done with the primitive at all.
+ *
+ * For primitives that are created with the asynchronous:true
+ * setting and that are in this state, the update call starts
+ * the creation of the geometry using web workers, and the primitive goes
+ * into the CREATING state.
+ *
+ * For synchronously created primitives, this state never matters. They will
+ * go into the COMBINED (or FAILED) state directly due to a call to the
+ * update function, if they are not yet FAILED, COMBINED,
+ * or COMPLETE.
+ */
READY: 0,
+
+ /**
+ * The process of creating the primitive geometry is ongoing.
+ *
+ * A primitive can only ever be in this state when it was created
+ * with the asynchronous:true setting.
+ *
+ * It means that web workers are currently creating the geometry
+ * of the primitive.
+ *
+ * When the geometry creation succeeds, then the primitive will go
+ * into the CREATED state. Otherwise, it will go into the FAILED
+ * state. Both will happen asynchronously.
+ *
+ * The update function has to be called regularly
+ * until either of these states is reached.
+ */
CREATING: 1,
+
+ /**
+ * The geometry for the primitive has been created.
+ *
+ * A primitive can only ever be in this state when it was created
+ * with the asynchronous:true setting.
+ *
+ * It means that web workers have (asynchronously) finished the
+ * creation of the geometry, but further (asynchronous) processing
+ * is necessary: If a primitive is determined to be in this state
+ * during a call to update, an asynchronous process
+ * is triggered to "combine" the geometry, meaning that the primitive
+ * will go into the COMBINING state.
+ */
CREATED: 2,
+
+ /**
+ * The asynchronous creation of the geometry has been finished, but the
+ * asynchronous process of combining the geometry has not finished yet.
+ *
+ * A primitive can only ever be in this state when it was created
+ * with the asynchronous:true setting.
+ *
+ * It means that whatever is done with
+ * PrimitivePipeline.packCombineGeometryParameters has
+ * not finished yet. When combining the geometry succeeds, the
+ * primitive will go into the COMBINED state. Otherwise, it will
+ * go into the FAILED state.
+ */
COMBINING: 3,
+
+ /**
+ * The geometry data is in a form that can be uploaded to the GPU.
+ *
+ * For synchronous primitives, this means that the geometry
+ * has been created (synchronously) due to the first call to the
+ * update function.
+ *
+ * For asynchronous primitives, this means that the asynchronous
+ * creation of the geometry and the asynchronous combination of the
+ * geometry have both finished.
+ *
+ * The update function has to be called regularly until
+ * this state is reached. When it is reached, the update
+ * call will cause the transition into the COMPLETE state.
+ */
COMBINED: 4,
+
+ /**
+ * The geometry has been created and uploaded to the GPU.
+ *
+ * When this state is reached, it eventually causes the _ready
+ * flag of the primitive to become true.
+ *
+ * Note: Setting the ready flag does NOT happen in the
+ * update call: It only happens after rendering the next
+ * frame!
+ *
+ * Note: This state does not mean that nothing has to be done
+ * anymore (so the work is not "complete"). When the primitive is in
+ * this state, the update function still has to be
+ * called regularly.
+ */
COMPLETE: 5,
+
+ /**
+ * The creation of the primitive failed.
+ *
+ * When this state is reached, it eventually causes the _ready
+ * flag of the primitive to become true.
+ *
+ * Note: Setting the ready flag does NOT happen in the
+ * update call: It only happens after rendering the next
+ * frame!
+ *
+ * This state can be reached when the (synchronous or asynchronous)
+ * creation of the geometry, or the (asynchronous) combination of
+ * the geometry caused any form of error.
+ *
+ * It may or may not imply the presence of the _error property.
+ * When the _error property is present on a FAILED primitive,
+ * this error will be thrown during the update call. When it
+ * is not present for a FAILED primitive, then the update call
+ * will do nothing.
+ */
FAILED: 6,
};
export default Object.freeze(PrimitiveState);