In this blog series we’re going to explain how to create cross-platform, cross-API graphics applications using the PowerVR Framework – all from one set of source code files!

The series will be split into the following parts:

  • PowerVR Framework: Getting started with the PVRApi Vulkan and OpenGL ES abstraction
  • PowerVR Framework: Writing portable Vulkan and OpenGL ES 3.0/3.1 apps with PVRApi
  • PowerVR Framework: Loading scenes, textures and shaders with PVRAssets
  • PowerVR Framework: Understanding image barriers for render to texture effects

Let’s get started with part one!

The aim of part one is the cleared screen below:

Intro1ClearApi

Whilst not the most advanced example, it introduces the first concepts required to write a framework application.

First, let’s talk a bit about the motivation and aim of the PowerVR framework and why you would want to use it.

The new framework is designed to:

  • Support new explicit APIs such as Vulkan – exposing their power whilst hiding their verbosity
  • Continue to support OpenGL ES 3.x – allowing use of fast paths that mimic explicit APIs where possible
  • Support all major platforms with one source base – Android, iOS, Windows, Linux and OS X

Most of all, the framework should allow us to focus on high level design removing as much boiler plate code as possible

Please see An overview of the new PowerVR Framework for more information and Learn more about the Vulkan API if unfamiliar with Vulkan.

Now we’re going to create the first example – Intro1ClearApi.

To build it, you will require git, CMake 3.1+ and a supported IDE/platform.

The following platforms have been tested using CMake 3.3.1 and where applicable PVRVFrame libraries from the SDK 16.1 release were used:

  • Visual Studio 14
  • XCode – iOS and OS X
  • Android Ninja and Visual Studio 14
  • Ubuntu 14.04 Make

All examples and CMake files are hosted on GitHub in the following repository:

PVRApiIntro

Please see the GitHub page for how to build and run Intro1ClearApi for supported platforms. Detailed instructions are provided on the page describing the CMake build process. The CMake files are a great base for your own Framework based projects. If you have any problems building the examples please contact us on PowerVR Support or on the PowerVR Forum.

Let’s go over the first concepts of writing an application for the framework by going through Intro1ClearApi. All the source code is contained in ClearAPI.cpp.

To write an application which targets the framework we extend pvr::Shell and implement the following entry points –

class MyApp : public pvr
{
public:

     virtual pvr::Result::Enum initApplication(); // Perform non-graphics set-up - audio initialization etc
     virtual pvr::Result::Enum initView(); // Graphics set-up - load textures, create command buffers etc
     virtual pvr::Result::Enum renderFrame(); // Render frame called at VSync rate - submit command buffers etc
     virtual pvr::Result::Enum releaseView(); // Graphics tear down - delete textures, command buffers etc
     virtual pvr::Result::Enum quitApplication(); // Non-graphics tear down - close audio devices etc
};

initApplication() and quitApplication() – For Intro1ClearAPI we return pvr::Result::Success in both. There is no non-graphics set-up to be done. initView() – This forms the core of Intro1ClearAPI. In modern APIs such as Vulkan we want to create CommandBuffers outside of our main rendering thread (renderFrame() function). We can use a second thread to do this or with static requirements on CommandBuffers we can do this entirely in initView(). For Intro1ClearAPI we prepare all CommandBuffers in initView() creating one for each frame buffer in the swap chain. We submit these CommandBuffers in renderFrame().

pvr::Result::Enum OGLESIntroducingPVRApi::initView() {
    onscreenFB = getGraphicsContext()->createOnScreenFboSet(); // This returns an array of our on-screen frame buffers. In OpenGL ES we just receive one.
    // We then create a command buffer for each frame buffer
    for (int i = 0 ; i < getSwapChainLength(); i++){
        onscreenCBArray.add(getGraphicsContext()->createCommandBufferOnDefaultPool());
    auto & cb = onscreenCBArray[i];
    auto & frameBuffer = onscreenFB[i];
    // We record a command buffer with a render pass that clears the screen for each frame buffer
    cb->beginRecording();
    cb->beginRenderPass(frameBuffer, pvr::Rectanglei(0, 0, getWidth(), getHeight()),false,
    glm::vec4(123.0 / 255.0, 172.0 / 255.0, 189.0 / 255.0, 1.0));
    cb->endRenderPass();
    cb->endRecording();
    }
return pvr::Result::Success;
};

renderFrame() – At this point we have our CommandBuffers created for each frame buffer by initView(). renderFrame() is called after initView() at VSync rate to … render our frame. By calling getSwapChainIndex() we can determine which frame buffer is next in the swap chain. We then submit the corresponding CommandBuffer for the frame buffer – rendering our frame to the screen.

pvr::Result::Enum OGLESIntroducingPVRApi::renderFrame(){

    onscreenCBArray[getSwapChainIndex()]->submit();

    return pvr::Result::Success;

}

The submission of CommandBuffers has a very low cost in Vulkan – by only submitting CommmandBuffers on the rendering thread we ensure a stable frame rate. If a new CommandBuffer is required we should create this in a background thread – taking advantage of modern multi-core processors.

releaseView() – This is the last function to cover and we again return pvr::Result::Success.

In this introduction we have gone over the simplest principles required to render to the screen using the PowerVR Framework.

Compared to raw Vulkan calls, the reduction in verbosity is considerable. For comparison, here is the API stream – including structure parameters – generated by the PowerVR Framework to render this basic scene Intro1ClearApi Vulkan API calls.

The framework also simplifiers OpenGL ES rendering by abstracting all EGL/window set-up Intro1ClearApi OpenGL ES calls.

Importantly this same code covers all platforms – both OpenGL ES and Vulkan, providing an abstraction that works for both APIs.

In the next part we go through the process of drawing a triangle which exposes the first differences between Vulkan and OpenGL ES when using PVRApi.

See you there!

 

About the author: Dev Tech

Profile photo of Devlin