Dynamically Creating and Destroying LiveView RTs
In most situations, when LiveViews are utilized in the UI, they are often left alive during the run of the game, which can lead to a small, but persistent performance and memory overhead. For example, we might have a separate level or scene for rendering LiveViews kept alive at all times, or models constantly being rendered offscreen should the user decide to see a particular LiveView. In this post, we’ll go over how we can dynamically create and destroy our LiveViews only when we need them, thus helping us optimize memory usage and overall performance.
Here’s a quick overview of what we’ll cover:
- Basic Show/Hide Logic for LiveViews
- Ensuring Proper Initialization (Manual Frame Delays vs. Event-Based Notifications)
- Handling Cached V8 Resources
- Conclusion & Key Takeaways
1. Basic Show/Hide LiveView Logic
A straightforward way to manage LiveViews is to toggle the creation and destruction of a LiveView using JavaScript. In the snippet below, we create an <img> tag for the LiveView or remove it, depending on our isShown flag:
let isShown = false;
// This is called when the LiveView needs to be updated
function UpdateLiveView() {
if (!isShown) ShowLiveView();
else HideLiveView();
isShown = !isShown;
}
function ShowLiveView() {
engine.trigger("CreateRT");
const img = document.createElement('img');
img.className = 'liveview';
img.src = '<LiveViewResource>';
img.id = "liveView";
document.getElementById("parent").appendChild(img);
}
function HideLiveView() {
const liveView = document.getElementById("liveView");
document.getElementById("parent").removeChild(liveView);
engine.trigger("HideView");
}
- Pros: Quick and straightforward logic to implement
- Cons: If the '<img>' is created too soon (i.e., before the back-end finishes preparing the RT), the LiveView may fail to render.
2. Ensuring Proper Initialization
When creating a new '<img>' we should keep in mind that this is an extremely fast operation, and could result in the FrontEnd requesting the LiveView resource before it’s prepared by the engine. This leads to a rendering error or a blank image. Below are two methods we can use to handle this delay:
- Manual Frame Delay
- Event-Based Notification
Option A: Manual Frame Delay
We can delay the creation of the '<img>' element by a few frames by invoking the `resuestAnimationFrame` on the JS side, or another similar call in the backend:
function ShowLiveView() {
engine.trigger("CreateRT");
// Wait two frames before creating the <img> tag
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const img = document.createElement('img');
img.className = 'liveview';
img.src = '<LiveViewResource>';
img.id = "liveView";
document.getElementById("parent").appendChild(img);
});
});
}
Option B: Event-Based Notification
Alternatively, we can have the back-end signal that the LiveView resource is fully prepared so the '<img>' is never created prematurely.
function ShowLiveView() {
engine.trigger("CreateRT");
}
// Called by the engine once the LiveView RT is ready
engine.on("OnLiveViewResourceCreated", () => {
const img = document.createElement('img');
img.className = 'liveview';
img.src = '<LiveViewResource>';
img.id = "liveView";
document.getElementById("parent").appendChild(img);
});
3. Handling the V8 Resource Cache
That said, even if we remove the `<img>` element from the DOM and call engine methods to destroy it in the backend, V8 (the JavaScript engine) may retain an internal reference until the next garbage collection (GC) cycle occurs. To better showcase the potential problem, please have in mind the following codeflow:
- First Creation: We create the LiveView, so the LiveViewresource is cached on the V8 side.
- Destruction: We remove the LiveView from the DOM and destroy it on the back-end, but V8’s cached reference still lingers, even though there are no living references to it.
- Recreation: When we recreate the LiveView, V8 might reuse the invalid cached reference, resulting in a broken or missing texture.
Forcing a Garbage Collection
Since this behavior is caused by the caching or resources, we can resolve this by:
- Invoking the 'LibraryParams::ExposeGC' Cohtml API
- Exposing and invoking the 'gc()' JavaScript API
function ShowLiveView() {
window.gc(); // Attempt to force a GC cycle
requestAnimationFrame(() => {
requestAnimationFrame(() => {
const img = document.createElement('img');
img.className = 'liveview';
img.src = '<LiveViewResource>';
img.id = "liveView";
document.getElementById("parent").appendChild(img);
});
});
}
We hope that the provided information grants you better insight on how to work with the LiveViews and possibly optimize how they are utilized.
Please sign in to leave a comment.
Comments
0 comments