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 itsCSSTransformComponent
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, 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.
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 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.
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:
- The
CohtmlCanvasRenderer
component is labeled as deprecated. It will be removed from the code base in one of the future releases. - The
CohtmlCanvasRenderer
component should be removed from the scenes that use it. - All
CohtmlView
components have to be placed in the same game object as yourRawImage
orCamera
components (e.g. depending if you haveInWorld
orOnScreen
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
API | Removed support for Arm architecture on the Microsoft Universal Windows Platform |
APIUnity | Added a WebSocket state changed event. |
APIUnity | Added a WebSocket state changed callback |
Feature | Added support the CSS Typed Object Model |
Feature | Added support for custom media features |
FeatureUnity | Added support for NGGC for Sony PlayStation 5 |
FeatureUnreal 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 |
EnhancementUnity | Improved support for CohtmlView inside a UICanvas hierarchy |
EnhancementUnity | 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 |
FixUnity | Worked around Unity restriction to let textures be reused with different sampler states in the same frame for all graphics APIs except OpenGL |
FixUnity | Removing the mouse event skip logic causing lag in the Cohtml API |
FixUnreal Engine | Fix path rendering with RHI implementations which don’t support first instance |
FixUnreal Engine | Fix crash on Android with GLES3 in UE |