rendering is out of order. How to wait for rendering to finish?

I've created a prototype for a time series application that I am working on. I create a circle and then move it by 0.01 degrees to the east with each time step.

I have this part working. However, the ordering of Cesium's event handling seems to be slightly off. Ideally, I should see a smooth transition where the circle moves to the right over a period of 5 seconds. What I actually see, is the circle generally moves to the right, but jumps backward every now and then.

I have a hunch that the problem is that Cesium renders frames 1-4, gets stuck on frame 5, finishes preparing 6 before 5 is ready and so goes ahead with rendering frame 6.

Anyone else seen this type of behavior?

Just as a note - I found that the "jumping" that I see between frames is not uniform. And if I replay the animation, the jumps do not always occur at the same point. This leads me to believe it is indeed an issue with the timing of the events.

I'm fairly convinced this is a bug in Cesium. I added a boolean mutex in the preRender and postRender event to ensure that I don't load the next time step of the animation until the previous time step's postRender has fired. However, I'm still seeing the jumping back and forth! That really shouldn't be possible!

Hello,

Can you paste some code to reproduce this? How are you adding your circle?

It sounds like an asynchronous issue to me. Our geometry types render asynchronously by default so they don’t hold up the rest of the application. For time dynamic geometry, it’s better to force them to render synchronously to avoid flickering or other types of asynch issues.

Best,

Hannah

Here are the applicable parts of the prototype. Just for a general overview:
load() - adds the event handling for pre/post rendering. Grabs the JSON encoded data, which is just a single data point moving east by a fraction of a degree in each time step.
displayTimeMoment() - performs the swapping in/out of time frames
storeTimeMoment() - actually builds the primitive to display a time frame.

Currently I'm sending the data to Cesium using JSON, but I think I'm going to move to directly importing it with a .Net->JavaScript conversion function I found.

I'm still just prototyping and trying to figure out what Cesium's best practices are now while I'm using a very small and simple example. (so I'm more than happy to listen to suggestions in general)

The big issue in the code below that I'm really confused with is that the rendering can display time frames out of order even though I set asynchronous to false and then even put a mutex (named 'canModifyData') in the pre/post rendering events to make sure I couldnt load time frame 'x+1' until 'x' finishes.

////////////////////////////////////////////////////////////

var load = function (url) {
        if (!Cesium.defined(url)) {
            throw new Cesium.DeveloperError('url is required.');
        }

        //Use 'when' to load the URL into a json object
        //and then process is with the `load` function.
        return Cesium.when(Cesium.loadJson(url), function (json)
        {
            timeSeriesData = json.data;
            
            for (var i = 0; i < timeSeriesData.length; i++) {

                var timeMomentData = timeSeriesData[i].attributes;
                var timeMoment = timeMomentData.momentData;
                var time = timeMomentData.Time;
                storeTimeMoment(time, timeMoment, scene);
            }

            startAnimation();

            //Subscribe to the preRender. Remove old frames, Set new pending frames to current.
            scene.preRender.addEventListener(function () {
                canModifyData = false;

                //Remove the old cylinder once the new one is ready.
                if (Cesium.defined(newPrimitive) && newPrimitive.length > 0)
                {
                    var newPrimitiveReady = true;

                    for (var i = 0; i < newPrimitive.length; i++)
                    {
                        if (!newPrimitive._state === Cesium.PrimitiveState.COMPLETE)
                        {
                            newPrimitiveReady = false;
                            break;
                        }
                    }

                    //Remove old primitives if we can replace with new ones.
                    if (Cesium.defined(currentPrimitive) && newPrimitiveReady)
                    {
                        scene.primitives.remove(currentPrimitive);
                    }

                    currentPrimitive = newPrimitive;
                    newPrimitive = undefined;
                }
            });

            //Subscribe to the postRender. Stop new rendering until this has fired.
            scene.postRender.addEventListener(function () {
                canModifyData = true;
            });

        }).otherwise(function (error) {

            return Cesium.when.reject(error);
        });
    };

    //Handles one moment of time series data.
    var storeTimeMoment = function (time, timeMoment)
    {
        var instances = ;

        //Add each data point to the time moment
        for (var j = 0; j < timeMoment.length; j++) {
            var point3d = timeMoment[j];

            circleInstance = new Cesium.GeometryInstance({
                geometry: new Cesium.CircleGeometry({
                    center: Cesium.Cartesian3.fromDegrees(point3d.x, point3d.y),
                    radius: 2500.0,
                    vertexFormat: Cesium.PerInstanceColorAppearance.VERTEX_FORMAT
                }),
                attributes: {
                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(new Cesium.Color(1.0, 0.0, 0.0, 0.5))
                },
                id: 'circle'
            });
            
            instances.push(circleInstance);

        }

        primitive = new Cesium.Primitive({
            geometryInstances: instances,
            appearance: new Cesium.PerInstanceColorAppearance({
                translucent: false,
                closed: true
            }),
            vertexCacheOptimize: true,
            allowPicking: true,
            releaseGeometryInstances: false,
            asynchronous: false
        });

        bufferedTimes.push(time);
        bufferedTimeSeries.push(primitive);
    };

    //Removes all "non current" (pending) primitives. Adds new time step's primitives to Cesium.
    var displayTimeMoment = function ()
    {
        //Stop if no data or at end of data.
        if (bufferedTimeSeries.length == 0 || animationIndex >= bufferedTimeSeries.length)
        {
            stopAnimation();
            return;
        }

        //remove any currently pending primitive display
        if (Cesium.defined(newPrimitive))
        {
            scene.primitives.remove(newPrimitive);
        }

        //Add new primitives to cesium.
        newPrimitive = bufferedTimeSeries[animationIndex];
        scene.primitives.add(newPrimitive);

        animationIndex++;
    };

    var startAnimation = function () {
        if (!running) {
            animationIndex = 0;

            canModifyData = true;
            clock.shouldAnimate = true;
            running = true;
            removeCallback = eventHelper.add(clock.onTick, onTick);
        }
    };

    var stopAnimation = function () {
        if (running) {
            canModifyData = false;
            removeCallback();
            clock.shouldAnimate = false;
            running = false;
        }
    };

    var onTick = function () {
        if (canModifyData) {
            displayTimeMoment();
        }
    };

I have a hunch that the problem is that Cesium renders frames 1-4, gets stuck on frame 5, finishes preparing 6 before 5 is ready and so goes ahead with rendering frame 6.

GL is is allowed to be async, but the spec enforces keeping commands ordered from the view of the application.

Manually using setInterval, will cause your update to not align with the renderer. Additionally you have no guarantee of time-sync to czm's clock.
u= update every 2t
r= render every 3t
u u u u u
r r r r r
at 3t has rendered 2, updated 1
at 7t has rendered 4, updated 3

Use a property or event hook instead, and reference cesium's clock for all manual animations.

Oh you are using onPreRender. Just base your position function off the clock then.

Destroying-Creating the primitive each frame is very pessimal, update the transformation matrix if you can.

Eventually I will have various large data sets to display. Basing the position off the currently clock time would probably fix this issue, but the underlying problem would still be there when I switch to using real data in the future. For now I just move a circle eastward for convenience and simplicity. Nice out of the box idea though :slight_smile:

And I'm not 100% tied to sticking with the pre/post render events. I only did it in an attempt to get more control over the out of order rendering issue/bug/problem. It didn't seem to fix it though.

You lost me with this post... I don't think I am using setInterval? I understand the premise of what you are saying and how it could be an issue though. How do you set the rendering and update of the database to be in sync? And how would this result in rendering out of order, not just missing a frame entirely?

Just as a note: In the final application, the data will be rendered using GPU shaded triangles in some cases and by varying the extruded height of polygons in other cases. It depends on the data that we need to show.

First post was based on assumption you were using setInterval, as most people do, ignore it.

If you have realtime data then use a CollecitonProperty; upon receiving data add it to the collection; call the property when rendering.
This abstracts the source of your data from the rendering method. Although you must generate start-end timestamps for your realtime data, which is a bit redundant, but afaik is the best use of the API.

Did some more research after reading this. Should I switch to the Entity API instead of the Primitive API? I think the way it would work is that I would keep updating viewer.entities with the correct values at each clock tick rather than updating scene.primitives.

It looks like an entity can have a TimeIntervalCollection associated with it. So with each clock tick, my idea would be to check entity.availability.findDataForIntervalContainingDate(CURRENT_CLOCK_TIME). If that returns undefined, i would set the entity.show to false for the current clock tick. If it returns a "data" then I would use that data to update the entity's values for the current clock tick.

So the data might be a number and I would adjust the entity's color/height for that particular time frame.

I would highly recommend switching to the Entity API. Even better, if you know all of your data head of time, look into using the CZML format for your JSON. Then you can just load that in without any additional processing.

Best,

Hannah

Hi Hannah,

I think your idea to use CZML could work for us. I'm having a little trouble getting started though. I made this very simple example based off of the CZML sandcastle code. When I run it, I see the timer start and the blue ellipse appear, but I don't see the czml loaded. Am I missing a step somewhere?

Also, now that I'm using the entities instead of the primitives, if I click on the earth, sometimes the ellipse entity just disappears on me. Not really sure why that would happen.

define([
  'viewer'
], function (viewer) {

    var eventHelper = new Cesium.EventHelper();
    var scene = viewer.scene;
    var camera = scene.camera;
    var clock = viewer.clock;
    
    var test = function () {
        var entityJSON = {};

        entityJSON.position = Cesium.Cartesian3.fromDegrees(-75.0, 39.0);
        entityJSON.ellipse = {
            semiMinorAxis: 2500.0,
            semiMajorAxis: 2500.0,
            material: Cesium.Color.BLUE.withAlpha(0.5),
            extrudedHeight: 300000
        };
        viewer.entities.add(entityJSON);

        viewer.dataSources.add(Cesium.CzmlDataSource.load('simple.czml'));

        clock.shouldAnimate = true;
    };

    return { test: test };
});

Oh and I put the simple.czml file as listed on github into the same folder as my JS file.

We added a bunch of CZML examples to Sandcastle. Particularly, take a look at
http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=CZML%20Circles%20and%20Ellipses.html&label=CZML for adding circles and ellipses

http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=CZML%20Path.html&label=CZML for using an array of positions to move an entity (in this case, a billboard)

-Hannah

Oh! Thanks Hannah! These examples are perfect! Not sure how I missed them earlier.