Skip to content
SiteEmail

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.

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);
}
}

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.

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.

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.

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

Section titled “Support for Unity's Next Gen Graphics Context for PlayStation 5”

In this release, Gameface 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.

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 Gameface 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 Gameface Unreal Engine documentation for more details.

CohtmlCanvasRenderer component removed from Unity3D integration

Section titled “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.


API

Removed support for Arm architecture on the Microsoft Universal Windows Platform

API Unity

Added a WebSocket state changed event.

API Unity

Added a WebSocket state changed callback

Feature

Added support the CSS Typed Object Model

Feature

Added support for custom media features

Feature Unity

Added support for NGGC for Sony PlayStation 5

Feature Unreal Engine

Introduced a way to run update the views on a worker thread

Enhancement

Added support for animating stroke-dasharray

Enhancement

Added a warning when setting properties without units

Enhancement

Improved 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

Enhancement Unity

Improved support for CohtmlView inside a UICanvas hierarchy

Enhancement Unity

Added support for adding CohtmlView with the context menu when an object is selected and that object has a Canvas as a parent

Fix

Fixed a memory leak when animating SVG attribute values

Fix

Fixed a floating point precision error causing texts to wrap spuriously

Fix

Fixed issuing out of bounds HTTP requests for videos and added documentation about handling end of stream

Fix

Fixed Touch Up/End event being fired with the wrong target element in some cases

Fix

Fixed history.replaceState failing with relative URLs throwing an origin error

Fix

Fixed an incorrect message about unsupported box-sizing CSS property with value border-box

Fix

Added a missing changelog entry for flex longhand support in version 1.48

Fix

Fixed a crash when SVG elements are appended to the DOM in event handling functions

Fix

Fixed createElementNS to correctly work with a null namespace argument

Fix

Fixed generating wrong URLs for the views in the inspector with multiple views

Fix

Fixed a crash during the mouse event firing when GC collects elements that are part of the event path

Fix

Fixed multiple crashes and undefined behaviors related to objects being wrongly collected by GC

Fix

Fixed the !important CSS flag not working in custom expressions and properties with URL

Fix

Fixed custom elements not getting upgraded when define is called after creating the element

Fix

Fixed an infinite recursion with mask and clipPath SVG elements which are self-referencing themselves

Fix

Fixed aliasing on the border edges of transformed elements

Fix

Fixed applying rules from @media rules that fail to be parsed

Fix

Fixed setting textContent or innerHtml to null or undefined not clearing the content of the element

Fix

Fixed parsing HTML comments properly with innerHtml

Fix

Fixed Element.closest method failing to return the element on which was called as a match

Fix

Fixed an uninitialized value for angle in linear gradients which was causing the comparison of two gradients to always return different

Fix

Fixed Player crashing on Mac OS X when allocating executable memory for V8

Fix

Fixed support for Mac OS 10.12 in the samples

Fix

Fixed HDR sample being darker in evaluation packages

Fix Unity

Worked around Unity restriction to let textures be reused with different sampler states in the same frame for all graphics APIs except OpenGL

Fix Unity

Removing the mouse event skip logic causing lag in the Cohtml API

Fix Unreal Engine

Fix path rendering with RHI implementations which don't support first instance

Fix Unreal Engine

Fix crash on Android with GLES3 in UE