Coherent UI supports two types of views - buffered and on-demand.
Buffered views are the default view type supported by Coherent UI. They are created by specifying
in their ViewInfo at the time of their creation.
These views are completely automatic. Their rendering is performed in Coherent UI' according to optimal internal rules. When a frame of such view has been completed the OnDraw method of the ViewListener attached to it will be called in the stack frame of the Coherent::UI::ViewContext::FetchSurfaces
method. All rendering of such views is lazy. If nothing visual has changed in the view no rendering will occur and hence the Coherent::UI::ViewListener::OnDraw
callback won't be invoked.
When triggering events on such views the user is not guranteed that the change will be immediately visible in the next received frame. Internal optimizations buffer frames for these views and offer better work balancing and smooth animations, so changes in a view might be user-visible after more than one frame. This slight delay is hardly ever user-noticeble especially for view that show just animations, menus or in-game browsers.
The delay however might be a problem if the game runs with low frame-rates (under 25-30) or if perfect visual synchronization is required between events happening in the game frame and the UI. Such situations might be the nameplates of characters in an RPG where they must always be 'glued' to the game entities and never 'lag' behind them or a real-time strategy where health bars rendered by Coherent UI must always stay at a pixel-perfect relative position to every unit. In such cases the user should use on-demand views.
On-Demand views offer a mechanism to synchronize the game frame and the events it generates with the UI frame. These views are rendered only on-demand by the user via a call to RequestFrame. Coherent UI guarantees that any events triggered prior to the call to Coherent::UI::View::RequestFrame
will be processed in the next frame returned to the user via a call to Coherent::UI::View::FetchSurface
. For on-demand views surfaces should be requested explicitly for every such view as a method of the Coherent::UI::View
class itself. Coherent::UI::View::FetchSurface
will block until the requested frame has been rendered and call Coherent::UI::ViewListener::OnDraw
with the new surface. A non-blocking method Coherent::UI::View::PeekFrameReady
is available to tell the user if the requested frame has been rendered.
On-Demand views require a little more effort from the user but are much more powerful by providing complete scheduling control.
ViewListener::OnFinishLoad
with isMainFrame == true has been received. Otherwise a deadlock is possible where you wait for the rendered surface but Coherent UI needs resources (usually the page itself) to be read via the file handler.On-Demand views allow You to control the way time passes in them. In this way your UI animations and JS timers will always advance as much as your application has advanced between two consecutive frames (or you can make time go faster/slower or pause it altoghther). To create a view where You control time you need to set ViewInfo::ControlTimeOnDemand
to true and request new frames via the View::RequestFrame(double timeSinceArbitraryMoment)
overload. Here the parameter timeSinceArbitraryMoment
must be a monotonically increasing value in seconds. You can use the time since the application started or any other time point in the past.
timeSinceArbitraryMoment
is not a delta value between frames. It must be the time in seconds since a moment in the past. It must be monotonically increasing and you can't make time go backward. Time set this way controls all animations as well as JavaScript intervals and AJAX request timeouts in Your view. To get the 'application time' you can use the Date.appTime()
method in JavaScript which returns the number of milliseconds since the epoch in application time, not 'real' time. You can still get the real computer time by simply creating a normal Date()
object. The raw number of ticks from the epoch corrected by the application time is available via engine.ticks
. Sometimes it might be useful to have Date()
return the application time as well - for instance if you don't want to change pre-existing JavaScript code but you still want to be able to control time. This can be trivially achieved: just pass Date = Date.prototype.appTime
as JavaScript code you want to be executed as soon as a view is created. This is done via the ViewContext::CreateView(const ViewInfo& info, const wchar_t* path, ViewListenerBase* listener, const wchar_t* scriptToExecute)
overload. The last parameter is JavaScript code that will be executed when the view is created. Now even the default 'Date' object will work only in application time.Buffered views should be the default choice when no perfect visual synchronization is required between the game frame and the user interface. They are completely automatic and offer optimal load balance. Usually buffered views should be used for:
Coherent::UI::ViewContext::FetchSurfaces
method. No calls must be made to request new frames.On-demand views are more powerful but require more bookkeeping. They should be used if the user must be sure that some events must be rendered in particular frames. Typical scenarios where on-demand views are a perfect fit are:
The best possible performance with on-demand views is gained when you structure your interaction with the view context as follows:
You should always try to put as much computation between the frame requests and their fetches as possible because Coherent UI renders in multi-threaded way. Thus by drawing the game frame simultaneously with the UI you maximise the chances that the interface will be already rendered when you try to fetch it, leveraging modern multi-core systems.
Buffered views will never block the user frame. On-demand views block in Coherent::UI::View::FetchSurface
until the requested frame is ready, Coherent::UI::View::PeekFrameReady
however allows the user to monitor the completion of the rendering process.
Currently intercepting URL requests and On-demand views should be used carefully together, since they might cause a deadlock in client application. While the client is blocked waiting for a surface from a on-demand view, the view might be blocked waiting for the client to intercept and allow some URL request. This can be avoided by either using a different thread for rendering the view (i.e. calling Coherent::UI::View::FetchSurface
in your renderer thread and Coherent::UI::ViewContext::Update
in your main thread) or by carefully designed main loop. Here the context is updated until the new frame is ready, so calling Coherent::UI::View::FetchSurface
will not block.