WebPlatformForEmbedded/WPEUIFramework

Name: WPEUIFramework

Owner: Web Platform for Embedded

Description: WPE UI Framework

Created: 2017-03-08 12:03:41.0

Updated: 2018-05-15 19:42:39.0

Pushed: 2018-05-15 19:42:37.0

Homepage: https://www.npmjs.com/package/wpe-uiframework

Size: 4096

Language: JavaScript

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

WPE UI Framework

npm version Donate

The WPE UI Framework (WUF) enables you to build WebGL-rendered UIs, apps and games. It contains a feature-rich and highly optimized 2D render tree, a flexible animation/transition toolkit and a framework to compose your UI based on UML state charts defined in-code.

Features

High performance

The framework is highly optimized, both in terms of CPU and GPU performance.

WebGL effects

Create cool pixel/lighting/3d/displacement/etc effects with our set of shaders, or implement your own custom shaders.

Memory Management

No memory leaks. Smart GPU memory management.

Installation
Browser

You can download the latest version of wuf.js from the repository here: https://github.com/WebPlatformForEmbedded/WPEUIFramework (dist/wuf.js).

Node.js

For Node.js, this module depends on node-canvas for image loading and text creation, and node-wpe-webgl for providing a WebGL interface to the native hardware. Install the dependencies and follow the installation instructions of node-canvas (https://github.com/Automattic/node-canvas) and node-wpe-webgl (https://github.com/WebPlatformForEmbedded/node-wpe-webgl).

Basic usage
Browser

index.html contents:

CTYPE html>
l lang="en">
d>
<meta charset="UTF-8" />
<script src="wuf.js"></script>
ad>
y>
<script>
    window.onload = function() {
        class YourApp extends wuf.Application {
              static _template() {
        return {texture: {type: wuf.textures.TextTexture, text: ?hello world?}}
            }
        }

        const options = {stage: {w: 900, h: 900, glClearColor: 0xFF000000}}
        const app = new YourApp(options);
        document.body.appendChild(app.stage.getCanvas());
    }
</script>
dy>
ml>

{PROVIDE EXAMPLE}

NodeJS

start.js contents:

t wuf = require('wpe-uiframework')

s YourApp extends wuf.Application {
static _template() {
    return {texture: {type: wuf.textures.TextTexture, text: ?hello world?}}
}


t options = {stage: {w: 900, h: 900, glClearColor: 0xFF000000}}
ons.stage.window = {title: "Border example", fullscreen: false};

t app = new YourApp(options);

Our test application YourApp extends the wuf.Application class. The full framework is bootstrapped by simply creating a new instance of your app. It is possible to run multiple apps in the same browser tab. Every app has it's own WebGL canvas and rendering context.

You can pass several options to an app. In this case, we specify the canvas width and height, and a background color in ARGB format (the glClearColor). Check the API for a list of all initialisation options.

YourApp has a template that allows you to define the layout of your application. In this case, it consists of a single view (a wuf render tree element) that contains a text.

Render Tree

The render tree defines what is being rendered on the screen. It consists out of a tree containing exclusively View (+ subtypes) instances. You can add, remove and change the views in this render tree as you wish, and those changes will be reflected on the screen during the next frame. The Stage manages the render tree and is responsible for texture loading, performing coordinate calculations and performing the required WebGL calls.

Drawing loop

On every requestAnimationFrame call, ideally at 60fps, the render tree is checked for changes, and those changes are rendered to the screen:

Layouting / Positioning

All views are positioned absolutely, relative to the parent view, using x and y. The framework was designed for fixed width/height viewports, but if you need more complex positioning such as floating or relative widths/heights, have a look at the calculation cycle hooks onUpdate, and onAfterUpdate.

A view has dimensions, gettable by the renderWidth and renderHeight properties. They can be set by specifying the w, h properties. If w, h are not set, the renderWidth corresponds to the (displayed) texture width. By default, both w and h are 0. The view dimensions are used for both positioning (mount, pivot) as well as for rendering the texture.

The mount specifies the point within the view dimensions that is specified by the x, y coordinates. Mount 0 corresponds the upper-left corner, 1 to the bottom-right corner. mountX and mountY can also be set separately, so (1,0) corresponds to the upper-right corner and (0,1) to the bottom-right corner.

The pivot (pivot,pivotX,pivotY) specifies the point within the view dimensions that is the origin for rotation and scale transformations. {PROVIDE EXAMPLE}

View properties

Many different propreties can be used to control the positioning, rendering and behavior of views. Some were just mentioned, but please check the API for a complete list of view properties.

Children

Views are part of the render tree, and may contain view 'children'. These can be accessed using the childList property. TODO

Accessing the view tree

{PROVIDE EXAMPLE}

Textures

A view may or may not have a texture to be rendered. TODO

Shaders
RenderToTexture
Filters
Memory management

We can distinguish between CPU memory and GPU memory.

CPU memory

We have tried to set up the framework in such a way that CPU memory memory leaks are avoided. When you remove a branch of views, it will be dereferenced by the framework (including the animation/transition system), so it's up to your own code to also stop referencing it and then it can be cleaned up.

If you are running into memory problems on embedded devices, you could reduce the cpu-to-gpu coordinate buffers (configuration parameter bufferMemory, defaults to 8M). Usually, 1M is already more than enough because that allows you to draw 15K quads per frame.

GPU memory

In terms of GPU memory: a certain amount of reserved GPU memory (in pixels) can pre-specified in the Application configuration parameter textureMemory (defaults to 18M pixels). All textures are uploaded to the GPU only when they are used, and there they take up space. Previously used textures will remain in the GPU memory until the reserved amount is full; then, the textures that are not required for rendering at that moment are all garbage collected from the GPU memory. When they appear on the screen again, they must be reloaded and uploaded to GPU memory again.

If you are using renderToTexture or filters, the created textures are also cached, even when they are no longer used. This cache has a positive impact on the performance because it is expensive to recreate them, and often they can be reused. Again, a configuration parameter controls the reserved amount of pixels in memory: renderTextureMemory (defaults to 12M pixels).

Garbage collection for both caches can be forced using Stage.gcTextureMemory() and Stage.gcRenderTextureMemory(). This is handy on embedded devices where GPU memory is a limited resource, and another application may be brought up in front the UI.

Performance

This chapter describes how the framework tries to improve performance of your application, and what you can do to best utilize these optimizations.

Basic optimizations

Many optimizations have been performed to minimize the work, power consumption and improve performance, both for the CPU and GPU. The most important optimisations are:

To be able to perform these optimizations when possible, the framework keeps a couple of flags per view:

|Mode | Description| |—|—| |Attached | True iff the view is attached of the render tree| |Enabled | True iff attached and visible and alpha > 0| |Active | True iff enabled and withinBounds| |WithinBounds | True iff the (x,y,x+renderWidth,y+renderHeight) area is within visible bounds (viewport and/or clipping area)|

Calculation loop

During the calculations loop, when a view is found to be not withinBounds (out of bounds), and it can be assumed that no other descendant view can possibly be within bounds, the complete branch can be skipped (both for calculations and for rendering), improving performance. This can have a big effect when there are a lot of enabled views in the render tree (such as in an EPG or a side scroller game). The framework is able to optimize it when any of the following properties is enabled:

Clipbox tells the framework that no descendant of this view will extend the view dimensions, without actually clipping it (which, in itself, costs performance). This is the cheapest way to improve performance. You should use it when a view contains a lot of non-protruding descendands and can go out of bounds.

Rendering

Views that have a texture will only be rendered when they are both active and withinBounds.

Texture loading

When a view has an associated texture, that texture will not be loaded until the view is active and withinBounds.

Often, you'd like textures (images etc) to be loaded before they enter a screen to avoid the 'pop in' effect. The boundsMargin view property allows a certain additional margin (can be specified separately for all sides) to be set for a branch of the render tree. This may force additional textures to be loaded (those within the bounds margin) before they enter the screen when scrolling. It does not force them to be rendered though. Setting a boundsMargin activates it for the full branch, and overrides the parent setting. By default, the root is set to a 100px margin on all sides (top, right, bottom, left).

One thing to watch out for is that it the framework usually doesn't know in advance what the dimensions of a texture will be until that texture is loaded. Sometimes these dimensions do affect when the view is considered to be withinBounds. For example, when it is positioned somewhere on the left side of the viewport, it could be either withinBounds (if the texture is wide) or not (if the texture is narrow). Therefor the framework assumes, if dimensions are unknown and unspecified, a maximum 2048x2048 texture size. In some cases, especially when having a lot of stuff on the left or top side of the screen or when using an alternative mount position, this may cause textures to be loaded unnecessarily. There are two ways to fix this:

Batching drawElements calls

Every frame the framework converts the complete render tree into a series of WebGL commands that are pushed to the video card. A video card likes to receive things in a 'batched' form: a single drawElements call that draws many quads (2 polygons) in a single command has much better performance than one individual drawElements call per quad. The difference is not so noticable when you only have a couple of things to draw, but when you need to draw a lot of quads (100+) the difference can be huge for both CPU and GPU (up to more than 10x). Of course the framework tries to batch calls where it can, but certain things may force it to separate the calls, such as:

In practice, the first one (different texture sources) is by far the most likely to cause many batch breaks. You should understand that the render order of the views/texturizes is fully determined by the position in the render tree (top-down first-last) and the z-index, if specified. As an example: you may be able to create a draw batch by z-indexing all views that share the same texture (good candidates are those that have rect enabled). You may want to use the forceZIndexContext on an ancestor to make sure that the batch doesn't interfere with other parts of the render tree.

Code composition

An application can be composed into components. A component extends the wuf.Component class, which itself extends the wuf.View class. In fact, wuf.Application extends wuf.Component, so it is the render tree root. Composition is achieved by simply including them as Views somewhere in the render tree.

Tools
View types

The View class may be subclassed to add additional functionality (BorderView, FastBlurView, SmoothScaleView)


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.