Giter VIP home page Giter VIP logo

greenfield's Introduction

Greenfield

The in-browser wayland compositor

Greenfield is a Wayland compositor written entirely in TypeScript while utilizing WebAssembly and WebGL for the performance critical parts. It can run native Wayland applications remotely and display them directly in your browser.

Greenfield running DOOM3 remotely 1920x1080@60FPS (16 Mar 2023):

Fosdem presentation + demo

For more information, visit the website, or have a look on how it all began

Modular Compositor

Greenfield consists of 3 separate parts.

Running

To run, you will need 2 parts to work together.

  • The Greenfield Compositor Proxy that runs remotely and forwards the native Wayland applications to the browser. You need at least one, but can run as many as you require e.g. to access different machines.
  • An implementation of the Greenfield Compositor Module that runs in your browser and receives the forwarded signals of potential multiple Greenfield Compositor Proxies.

Greenfield Compositor Module

The Greenfield Compositor Module comes with a simple demo implementation.

In the compositor-module directory run

  • yarn install
  • yarn generate
  • yarn start

to start the compositor demo module.

Note that the compositor-module built uses Open-API to generate client code to talk to the compositor-proxy and requires java to be present on your PATH during build.

Go to http://localhost:8080 and be greeted with a nice white compositor. It has 2 URL input fields that can be used. The first input field connects to the provided remote compositor and accepts input in the form of host:port. The second input field launches and runs Wayland applications in an iframe (experimental) and accepts input in the form of http(s)://host:port/path.

Greenfield Compositor Proxy

To build, you need a set of native dependencies. You can look at the Docker image to see which ones you need to build and run respectively, or if you're running a Debian based distro you can run:

sudo apt install cmake build-essential ninja-build pkg-config libffi-dev libudev-dev libgbm-dev libdrm-dev libegl-dev \ 
 libwayland-dev libglib2.0-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev libgraphene-1.0-dev gstreamer1.0-plugins-base \ 
 gstreamer1.0-plugins-good gstreamer1.0-plugins-ugly gstreamer1.0-gl xwayland

More dependencies may be required depending on your GPU eg. for nvidia based cards.

Next, inside compositor-proxy, run:

  • yarn install
  • yarn generate
  • yarn build:native
  • cp dist/encoding/proxy-encoding-addon.node src/encoding/proxy-encoding-addon.node
  • cp dist/proxy-poll-addon.node src/proxy-poll-addon.node

For XWayland support a few extra steps may be needed, this is optional and only required if you don't already hava an X server running eg. when running on a server:

  • export XAUTHORITY=.Xauthority
  • touch "$HOME/$XAUTHORITY"
  • xauth add "${HOST}":1 . "$(xxd -l 16 -p /dev/urandom)"

and finally

  • yarn start

NOTE: Firefox needs to be at least at version 113 and dom.workers.modules.enabled preference needs to be set to true. To change preferences in Firefox, visit about:config.

This will start a development build+run. You should now see something that says Compositor proxy started. Listening on port 8081. You can also adjust some things in src/config.yaml.

In our demo compositor we can now input the url localhost:8081 to make a connection to the Greenfield Compositor Proxy. You should see a message appear in the log output of the compositor-proxy that we started earlier: New websocket connected..

You should now have a Wayland compositor running on your system, so let's start some applications. Most recent GTK3/4 applications (like gnome-terminal) should auto-detect the compositor-proxy and simply connect without issues or extra setup. QT applications often require an extra -platform wayland parameter. If your application can't connect, try setting the WAYLAND_DISPLAY environment variable to the value that was printed by compositor-proxy. ie if you see Listening on: WAYLAND_DISPLAY=\"wayland-0\". then set the environment variable export WAYLAND_DISPLAY=wayland-0.

Packaged build

It's also possible to build a distributable release.

  • yarn install
  • yarn generate
  • yarn build
  • yarn package

This creates a set of files in the package directory. The run.sh script accepts several options:

        Usage
          $ compositor-proxy <options>

        Options
          --help, Print this help text.
          --static-session-id=...,  Mandatory. Only use and accept this session id when communicating.
          --config-path=...,  Use a custom configuration file located at this path.

        Examples
          $ compositor-proxy --static-session-id=test123 --config-path=./config.yaml

Below is an example config file (the default config). It can be copy-pasted and used with the --config-path=... option. You will at least need to set the public.baseURL when not running locally.

server:
  http:
    # Hostname argument.
    bindIP: 0.0.0.0
    # Port argument.
    bindPort: 8081
    # CORS allowed origins, used when doing cross-origin requests. Value can be * or comma seperated domains.
    allowOrigin: '*'
public:
  # The base url to use when connecting to this endpoint. This is the publicly reachable address of the compositor proxy.
  baseURL: http://localhost:8081
encoder:
  # Path of the render device that should be used for hardware acceleration. e.g. /dev/dri/renderD128
  renderDevice: /dev/dri/renderD128
  # The gstreamer h264 encoder to use. 'x264' and 'nvh264' are supported ('vaapih264' is currently broken). 'x264'
  # is a pure software encoder. While 'nvh264' is a hw accelerated encoder for Nvidia based GPUs.
  # see https://gstreamer.freedesktop.org/documentation/x264/index.html
  # see https://gstreamer.freedesktop.org/documentation/nvenc/nvh264enc.html
  h264Encoder: x264
logging:
  # "fatal" | "error" | "warn" | "info" | "debug" | "trace"
  level: info

The packaged binary expects the following set of dependencies to be available for mesa & nvidia support, if you're running a Debian based distro you can run:

apt-get install \
    libffi8 \
    libudev1 \
    libgbm1 \
    libgraphene-1.0-0 \
    gstreamer1.0-plugins-base \
    gstreamer1.0-plugins-good \
    gstreamer1.0-plugins-bad \
    gstreamer1.0-plugins-ugly \
    gstreamer1.0-gl \
    libosmesa6 \
    libdrm2 \
    libdrm-intel1 \
    libopengl0 \
    libglvnd0 \
    libglx0 \
    libglapi-mesa \
    libegl1-mesa \
    libglx-mesa0 \
    libnvidia-egl-wayland1 \
    libnvidia-egl-gbm1 \
    xwayland \
    xauth \
    xxd \
    inotify-tools \
    libnode108

Docker

Running the Greenfield Compositor Proxy can also be done using docker-compose (see docker-compose.yml in the compositor-proxy directory), but you will be limited to the applications specified in the docker-compose file. Beware that this docker compose file only provides the Greenfield Compositor Proxy, so you will still need to run a Greenfield Compositor Module implementation yourself.

There is also a short screen-cast, if you're unsure on how to get started:

docker-compose greenfield

The compositor-proxy is also available as a public docker image docker.io/udevbe/compositor-proxy but does not include any config.yaml. This means you'll have to include it yourself using a mount. Have a look at the docker-compose file for inspiration.

High level technical

Client connection

A Greenfield browser compositor uses a native compositor-proxy to talk to native applications. This proxy compositor accepts native Wayland client connections and assigns them to a WebSocket connection as soon as one becomes available. A native client and it's WebSocket connection are bound to each other until either one is closed.

A compositor-proxy proxy can request additional WebSocket connections from an already connected Greenfield browser compositor. This is needed in case a Wayland client spawns a new Wayland client process. If no WebSocket connections already exists, the compositor-proxy will wait until a new WebSocket connection is available. In other words, the first WebSocket connection is always initiated from the browser.

Client frame encoding, or "can it run games?"

Each application's content is encoded to video frames using GStreamer and send to the browser for decoding. In the browser the application is realised by a WebGL texture inside a HTML5 canvas. This canvas is basically what you would call an 'output' in Wayland terminology. The browser compositor is asynchronous, meaning a slow client will not block the processing of another client.

Old implementation

This setup has some performance constraints as a lot of parts still use the CPU to do image encoding and decoding. The proxy compositor can't deal with OpenGL texture coming from the client either, meaning that a client (ie a game) needs to download its entire rendering to RAM before passing it to the proxy-compositor. Another drawback is that we have to work with a dual encoding/decoding solution as the H.264 codec does not support transparency (alpha).

To make this performant, all image processing should be done on the GPU and an image codec that supports alpha (transparency) should be used.

Such a hypothetical perfect pipeline would look something like this:

Here, all heavy operations are done by the GPU. H.264 has been replaced with H.265 which supports transparency, so we can use a single encoding/decoding step. The compositor-proxy supports the Wayland DRM protocol, so it can pass OpenGL applications directly to the encoder without making any copies.

There is however one major problem to this solution: the combination of WebCodecs API and the H.265 codec simply does not exist in any browser.

The current implementation:

Here we've extended the old solution with support for the Wayland DRM protocol (+ DMA_BUF protocol). This allows for a zero-copy transfer of the application pixels to the encoding pipeline. We've also added support for the H.264 WebCodecs API, which allows us to do decoding on the GPU of the receiving browser client if supported. If no usable GPU is available in the Compositor-Proxy, the pipeline falls back to slower software rendering.

The end result is a near ideal solution that is fast enough to support gaming.

WebSockets - WebRTC DataChannels - WebTransport

There is one drawback that currently still remains, and that's the use of WebSockets to deliver data to the browser. WebSockets operate over TCP which is ill-suited for real-time applications like Greenfield. Instead, a UDP based protocol is needed. Browsers today unfortunately have no support for UDP based protocols aside from WebRTC DataChannels. However, we can not use WebRTC DataChannels as the build-in SCTP congestion algorithm is unacceptably slow. A more low level UDP protocol is required and is currently in the works in the form of the WebTransport protocol. Once WebTransport becomes more widely available, we can operate in UDP mode and assure fast end reliable transfers using KCP in combination with forward-error-correction.

Copy-Paste

If both clients are connected to separate compositor-proxy, copy-paste will use a direct peer to peer transfer between compositor-proxies. This avoids the round trip and massive overhead of transferring all content to the browser and back. How this works is illustrated in the image below:

XWayland

Very much beta. Most things are implemented except for fullscreen applications. Please report any bugs or annoyances you find.

Media

Fosdem presentation + demo (2 Feb 2019):

Fosdem presentation + demo

Early tech preview demo (23 Nov 2017):

Early tech preview demo

greenfield's People

Contributors

dependabot[bot] avatar jschueller avatar zubnix avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.