Release notes

What’s new

Core

CSS Typed Object Model

The CSS Typed Object Model is part of the Houdini APIs. It allows the manipulation of CSS styles through objects, reducing the number of strings and strings concatenations required.

This can lead to improved performance, lower memory cost and less time spent on GC, especially when used with properties requiring lots of string concatenation like transform.

We’ve made a partial implementation of the current draft that allows for manipulation of transform and all numeric CSS properties without using strings.

Using CSSTOM with numeric properties

Properties that can be numbers with a unit can be represented as CSSUnitValues.

After creating a CSSTOM object we can use attributeStyleMap to set the inline style of the element.

Before we would do:

element.style.left = updateValue + "px";

Now with types:

unitValueObj.value = updateValue;
element.attributeStyleMap.set("left", unitValueObj);

But to update with types we first need to create a CSSTOM object. There are many ways to do this, for example:

Creating a CSSUnitValue

let unitValueObj = CSSStyleValue.parse("left", "50vw");
// or
let unitValueObj = CSSStyleValue.parse("left", CSS.vw(50));
// or
let unitValueObj = CSS.vw(50);
// or if the element already has an inline style for "left"
let unitValueObj = element.attributeStyleMap.get("left");

Creating a CSSTransformValue

let transf = new CSSTransformValue([
    new CSSScale(2,1),
    new CSSTranslate(CSS.px(100), CSS.px(100)),
    new CSSRotate(CSS.deg(5))
]);
// Or, since creating should happen rarely
// and we will mostly reuse the same object we can use the parse function
let transf = new CSSStyleValue.parse("transform",
                                    "scale(2,1)
                                     translate(100px, 100px)
                                     rotate(5deg)");

When using CSSOM for a complex transform many strings and string concatenations could be required. Now updating a transform value can happen with 0 concatenations.

// `updateValues` contains just floats of the new values that we are going to set
// `transform` is a CSSTransformValue object that is created beforehand.
function UpdateFunction(updateValues) {
    for(let i = 0; i < attributeStyleMaps.length; i++) {
        // This would require 10 string concatenations if done with regular CSSOM
        transform[0].x.value = updateValues[i][0];
        transform[0].y.value = updateValues[i][1];
        transform[1].x.value = updateValues[i][2];
        transform[1].y.value = updateValues[i][3];
        transform[2].angle.value = updateValues[i][4];
        // attrStyleMaps is an array of the attributeStyleMap for the elements
        // that we will be updating often
        attrStyleMaps[i].set("transform", transform);
    }
}

Performance gains

The performance gains are most meaningful in the cases where there was a lot of strings.

For example in a test where we update the transform of 10000 elements every frame these are the results:

The JS updates are nearly five times faster!

Since in CSSTOM we can avoid making garbage there will be less GC pauses and less pressure on JS memory.

Here is the memory from the 10000 element test.

CSSTOM only uses the memory for the elements and the maps while CSSOM creates strings that keep the memory around 30% higher.

There are performance gains with properties that can be represented as CSSUnitValue as well, however the difference isn’t nearly as striking. We also generally improved style setting slightly so the new version with CSSTOM is about 60% faster than before.

Supported subset

The following parts of the CSSTOM draft are currently supported:

  • StylePropertyMap - With the following functions - set, get, delete, clear, has.
  • CSSStyleValue.parse
  • CSSKeywordValue
  • CSSUnitValue
  • CSSTransformValue with its CSSTransformComponent members:
    • CSSTranslate, CSSScale, CSSRotate, CSSSkewX, CSSSkewY.

StylePropertyMap that is used through attributeStyleMap only works on element for inline styles, there is no support for using CSSStyleRules through CSSTOM yet.

There is a page in the documentation with the full list of supported properties tips on how to use CSSTOM in a performant way.

Custom Media Features

We’ve added an easy way to control styling directly from C++/C#. This is accomplished by using custom media features like:

@media (myFeature: myValue) {
	div {
		background-color: green;
	}
}

Controlling what features are in effect is achieved by calling View::SetCustomMediaFeature(const char* featureName, const char* featureValue).

Example:

View->SetCustomMediaFeature("language", "en");

To disable a feature you can do so like:

SetCustomMediaFeature("language", nullptr);

The method returns a flag to indicate if the operation was successful.

For more information refer to the dedicated section for Media Queries.

Rendering

Support for Unity's Next Gen Graphics Context for PlayStation 5

In this release, Prysm introduces support for Unity’s NGGC, Next Gen Graphics Context, which is a graphics implementation for PlayStation 5. NGGC is available on all Unity3D editor versions starting from 2021.3.21. This allows developers to switch to a graphics integration that is more performant in comparison with the only, until recently, PS5 graphics implementation on Unity.

Unreal Engine

Concurrent Advance

The “Concurrent Advance” feature allows cohtml::View::Advance function calls to be executed on a worker thread, improving the overall project performance by reducing game lag instances. This is a significant advancement, as it delegates the animation updates, model synchronization, and JavaScript execution, among other things, away from the Game Thread. Note that this does not mean that different Views will be updated concurrently to one another.

Pros:

  • Enhances project performance by offloading tasks to a worker thread.
  • Reduces game lag by ensuring smoother operations on the Game Thread.

Cons:

  • Limited interaction with View operations during Advance calls, requiring synchronization rules to avoid crashes.
  • Callbacks from Prysm are executed on the worker thread, posing challenges for operations limited to the Game Thread in Unreal Engine.
  • The Game Thread must wait for Advance calls to complete to maintain game-UI sync, potentially blocking the thread if Advance calls are lengthy.
  • Undefined timing of Advance calls require careful synchronization, especially when the UI depends on the game state.

This update introduces a powerful tool for developers to optimize their projects, albeit with a need for careful integration due to its operational intricacies and limitations.

Please check the Prysm Unreal Engine documentation for more details.

Migration guide

CohtmlCanvasRenderer component removed from Unity3D integration

In the previous implementation, to render a CohtmlView into a RawImage UI component, placed within a Unity3D canvas, you had to utilize the CohtmlCanvasRenderer component. To achieve this, you had to create a game object in the scene that holds your CohtmlView and CohtmlCanvasRenderer components and another game object within the Canvas UI holding the RawImage component which we will render the UI into. We believe that this approach wasn’t optimal, so we’ve removed the CohtmlCanvasRenderer component and modified the implementation in a way that the CohtmlView component handles everything internally.

The most significant changes that require your attention when migrating from the old approach are:

  1. The CohtmlCanvasRenderer component is labeled as deprecated. It will be removed from the code base in one of the future releases.
  2. The CohtmlCanvasRenderer component should be removed from the scenes that use it.
  3. All CohtmlView components have to be placed in the same game object as your RawImage or Camera components (e.g. depending if you have InWorld or OnScreen UI).

To avoid applying these changes manually, we have implemented a script to perform this automatically when entering a scene containing CohtmlCanvasRenderer components. When such components are found, a dialog window will appear, and clicking “Yes” will delete the CohtmlCanvasRenderer component and move the CohtmlView that was used by the component. To avoid unwanted changes to the scene, you can also use the “Undo” operation in the editor. If you click “Cancel” it won’t change anything on the scene.

With the new approach, when you create a CohtmlView in a Unity3D Canvas, it will create a game object with a RawImage component and automatically place the CohtmlView in that game object. Alternatively, you could also create a RawImage and add the CohtmlView component to that object.

Changelog

Version 1.51.0


Released 07 Mar 2024
APIRemoved support for Arm architecture on the Microsoft Universal Windows Platform
APIUnityAdded a WebSocket state changed event.
APIUnityAdded a WebSocket state changed callback
FeatureAdded support the CSS Typed Object Model
FeatureAdded support for custom media features
FeatureUnityAdded support for NGGC for Sony PlayStation 5
FeatureUnreal EngineIntroduced a way to run update the views on a worker thread
EnhancementAdded support for animating stroke-dasharray
EnhancementAdded a warning when setting properties without units
EnhancementImproved the inspector performance recording capture by allowing the selection of subsystem to be traced and a giving control over the amount of traced markers through a trace level selection
EnhancementUnityImproved support for CohtmlView inside a UICanvas hierarchy
EnhancementUnityAdded support for adding CohtmlView with the context menu when an object is selected and that object has a Canvas as a parent
FixFixed a memory leak when animating SVG attribute values
FixFixed a floating point precision error causing texts to wrap spuriously
FixFixed issuing out of bounds HTTP requests for videos and added documentation about handling end of stream
FixFixed Touch Up/End event being fired with the wrong target element in some cases
FixFixed history.replaceState failing with relative URLs throwing an origin error
FixFixed an incorrect message about unsupported box-sizing CSS property with value border-box
FixAdded a missing changelog entry for flex longhand support in version 1.48
FixFixed a crash when SVG elements are appended to the DOM in event handling functions
FixFixed createElementNS to correctly work with a null namespace argument
FixFixed generating wrong URLs for the views in the inspector with multiple views
FixFixed a crash during the mouse event firing when GC collects elements that are part of the event path
FixFixed multiple crashes and undefined behaviors related to objects being wrongly collected by GC
FixFixed the !important CSS flag not working in custom expressions and properties with URL
FixFixed custom elements not getting upgraded when define is called after creating the element
FixFixed an infinite recursion with mask and clipPath SVG elements which are self-referencing themselves
FixFixed aliasing on the border edges of transformed elements
FixFixed applying rules from @media rules that fail to be parsed
FixFixed setting textContent or innerHtml to null or undefined not clearing the content of the element
FixFixed parsing HTML comments properly with innerHtml
FixFixed Element.closest method failing to return the element on which was called as a match
FixFixed an uninitialized value for angle in linear gradients which was causing the comparison of two gradients to always return different
FixFixed Player crashing on Mac OS X when allocating executable memory for V8
FixFixed support for Mac OS 10.12 in the samples
FixFixed HDR sample being darker in evaluation packages
FixUnityWorked around Unity restriction to let textures be reused with different sampler states in the same frame for all graphics APIs except OpenGL
FixUnityRemoving the mouse event skip logic causing lag in the Cohtml API
FixUnreal EngineFix path rendering with RHI implementations which don’t support first instance
FixUnreal EngineFix crash on Android with GLES3 in UE