On this page

C

Car Rendering Service Example

Demonstrates how to render multiple Qt Quick3D views in a service and simultaneously display them from surfaces managed by multiple activities.

Two separate surfaces bound to different QML Items

Building and deploying the example

See specific steps relating to building and deploying Qt for Android Automotive examples.

Overview

The Car Rendering Service example shows how to integrate a QtRaaSApplication rendering engine into an Android Service that can render individual QML items onto surfaces provided by Android activities.

Each activity hosts one or more ObservableSurfaceView objects that wrap and simplify the management of native Surface lifecycles. These surfaces are sent through an AIDL interface to the background RenderingService, which associates them with specific QML items identified by their IDs.

In this example, each Item, hosts a View3D. Each View3D object renders a shared standalone 3D scene, from POV of a separate Camera. This reference architecture demonstrates how to save system resources by sharing the same graphical resources in a single process while the results can be displayed by external applications.

The service renders those items offscreen using QAndroidSurfaceRenderEngine and composes the results directly onto the provided Android surfaces. Android surfaces can also send the touch input events that they receive to the service, which will be separately dispatched to the Quick items. This enables complex multi-surface, multi-display rendering scenarios suitable for automotive display systems and multi-window Android environments.

How It Works

The architecture consists of:

  • Activities: Create and manage ObservableSurfaceView instances, which are lightweight wrappers around SurfaceView that simplify handling surface lifecycle callbacks. When a surface becomes available, it is registered with the rendering service using the AIDL interface.
  • Rendering Service: To simplify this example, runs in the same process and owns the Qt rendering engine. It maintains a mapping between Android surfaces and corresponding QML item IDs. When notified, it starts pushing the rendered frames of an item to the associated surface.
  • Qt Quick Item: Defined in Main.qml. The root object is an Item, and only its **direct children** can be referenced by ID from the Android side. Each registered surface is associated with one of these direct child items for rendering.
  • AIDL Interface: Facilitates communication between the RenderingService and client Activities. Activities bind to the service and call remote methods such as setSurface() and sendTouchEvent() to connect or disconnect rendering targets and send touch input events to the Quick Items.

C++ Entry Point

The C++ side initializes Qt, sets up the QAndroidSurfaceRenderEngine, and loads the Main.qml of the Quick application.

int main(int argc, char *argv[])
{
    QAndroidRaaSApplication app(argc, argv);

    QAndroidSurfaceRenderEngine *renderEngine = new QAndroidSurfaceRenderEngine();
    renderEngine->load(QLatin1String(":/qt/qml/car_rendering_service_module/Main.qml"));

    return app.exec();
}

QML Scene

The QML scene is defined in Main.qml and uses an Item as its root object. Only direct children of this root Item can be referenced by their IDs from the Android side. Each surface registered through the AIDL interface is associated with one of these direct child items.

    Item {
        id: orbitingCameraView

        View3D {
            id: orbitingView3d
            anchors.fill: parent
            importScene: standAloneScene
            camera: cameraPerspectiveOrb

        }
    }

Note: The QAndroidSurfaceRenderEngine manages the position and size of the referenced items. This means that if you set any anchors or sizing properties on an Item that is bound to a surface, those properties will be ignored. The item's geometry is automatically adjusted to match the size of the bound surface.

Android Integration

The Android service is implemented in Java and defined in the manifest as a service. It instantiates a QtRaaSApplication this loads the Qt runtime and starts the Qt Quick application. It implements the AIDL-defined IRenderingService interface to bridge the bound clients to the rendering engine.

interface IRenderingService {

    void setSurface(in Surface surface, String itemId);
    void unsetSurface(String itemId);
    void motionEvent(in MotionEvent event, String itemId);
}

The service class implements and instantiates the binder and returns it when clients bind.

public class RenderingService extends Service {

    private static final String TAG = "RenderService";
    private QtRaaSApplication qtService;

    // Single Binder instance exposed to all clients
    private final IRenderingService.Stub binder = new IRenderingService.Stub() {
        @Override
        public void setSurface(Surface surface, String itemId) {
            qtService.setSurface(surface, itemId);
        }

        @Override
        public void unsetSurface(String itemId) {
            qtService.unsetSurface(itemId);
        }

        @Override
        public void motionEvent(MotionEvent event, String itemId) {
            qtService.sendTouchEvent(event, itemId);
        }
    };

}

Each activity creates its own ObservableSurfaceView instances and binds to the service. When the surface becomes available, the activity passes it together with a QML item ID to the service. When the surface becomes invalid or is destroyed, it notifies the service to unset the surface for that particular item, so that the QtRaaSApplication stops rendering that item.

    private void updateSurface(Surface surface, String qmlItemId)
    {
        if (m_renderingService == null)
            return;

        try {
            if (surface != null)
                m_renderingService.setSurface(surface, qmlItemId);
            else
                m_renderingService.unsetSurface(qmlItemId);
        } catch (RemoteException ignored) {
        }
    }

Available under certain Qt licenses.
Find out more.