Combining OpenGL and XAML together in a Universal Windows App (UWP)

Visual Studio 2015 allows easily combine OpenGL graphics with XAML controls in a single window. To accomplish this task we can create a new project based on “XAML App for OpenGL ES (Universal Windows)” template:

XAML App for OpenGL ES (Universal Windows)

The main page of the application shows XAML TextBlock control over a rotating cube drawn with OpenGL:

XAML App for OpenGL ES

My first impression is that this application works in the same way as DirectX and XAML App, because its main page uses the same SwapChainPanel control:


  <SwapChainPanel x:Name="swapChainPanel">
    <TextBlock Text="OpenGL ES and XAML" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="30" />

The only thing you need to do to make this all work is to ensure do you really have “XAML App for OpenGL ES (Universal Windows)” template installed in your Visual Studio 2015 or not. If you do not have it, it means that you did not built ANGLE project yet.

Now if you succeeded with building ANGLE project, you need to run install.bat having angle\templates\ as the current directory. It will output something like this:

Check that this script is in the correct location
Script has been launched from a directory called templates.
Setting the ‘AngleRootPath’ system environment variable
Setting AngleRootPath to “d:\Repos\angle\templates\..”
Successfully set AngleRootPath system environment variable.
Installing ANGLE’s Visual Studio 2013 templates
Visual Studio 2013 templates directory found
Successfully installed latest Visual Studio 2013 templates.
Installing ANGLE’s Visual Studio 2015 templates
Visual Studio 2015 templates directory found
Successfully installed latest Visual Studio 2015 templates.
Script complete.

How the application works

The application includes the following headers from ANGLE project:

// Enable function definitions in the GL headers below

// OpenGL ES includes
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

// EGL includes
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>

#include <angle_windowsstore.h>

and obtains OpenGL surface from SwapChainPanel declared in XAML:

void OpenGLESPage::CreateRenderSurface()
    if (mOpenGLES && mRenderSurface == EGL_NO_SURFACE)
        // The app can configure the the SwapChainPanel which may boost performance.
        // By default, this template uses the default configuration.
        mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, nullptr);
        // You can configure the SwapChainPanel to render at a lower resolution and be scaled up to
        // the swapchain panel size. This scaling is often free on mobile hardware.
        // One way to configure the SwapChainPanel is to specify precisely which resolution it should render at.
        // Size customRenderSurfaceSize = Size(800, 600);
        // mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, &customRenderSurfaceSize, nullptr);
        // Another way is to tell the SwapChainPanel to render at a certain scale factor compared to its size.
        // e.g. if the SwapChainPanel is 1920x1280 then setting a factor of 0.5f will make the app render at 960x640
        // float customResolutionScale = 0.5f;
        // mRenderSurface = mOpenGLES->CreateSurface(swapChainPanel, nullptr, &customResolutionScale);

and then performs OpenGL rendering using this mRenderSurface, for example the main render loop looks like this:

void OpenGLESPage::StartRenderLoop()
    // If the render loop is already running then do not start another thread.
    if (mRenderLoopWorker != nullptr && mRenderLoopWorker->Status == Windows::Foundation::AsyncStatus::Started)

    // Create a task for rendering that will be run on a background thread.
    auto workItemHandler = ref new Windows::System::Threading::WorkItemHandler([this](Windows::Foundation::IAsyncAction ^ action)
        critical_section::scoped_lock lock(mRenderSurfaceCriticalSection);

        SimpleRenderer renderer;

        while (action->Status == Windows::Foundation::AsyncStatus::Started)
            EGLint panelWidth = 0;
            EGLint panelHeight = 0;
            mOpenGLES->GetSurfaceDimensions(mRenderSurface, &panelWidth, &panelHeight);
            // Logic to update the scene could go here
            renderer.UpdateWindowSize(panelWidth, panelHeight);

            // The call to eglSwapBuffers might not be successful (i.e. due to Device Lost)
            // If the call fails, then we must reinitialize EGL and the GL resources.
            if (mOpenGLES->SwapBuffers(mRenderSurface) != GL_TRUE)
                // XAML objects like the SwapChainPanel must only be manipulated on the UI thread.
                swapChainPanel->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([=]()
                }, CallbackContext::Any));


    // Run task on a dedicated high priority background thread.
    mRenderLoopWorker = Windows::System::Threading::ThreadPool::RunAsync(workItemHandler, Windows::System::Threading::WorkItemPriority::High, Windows::System::Threading::WorkItemOptions::TimeSliced);

Useful links

See the following links for more information:

Leave a Reply

Your email address will not be published. Required fields are marked *