UIRendererVk.h#

Parent directory (Vulkan)

Contains implementations of functions for the classes in UIRenderer.h.

Includes#

  • PVRUtils/Vulkan/HelperVk.h

  • PVRUtils/Vulkan/MemoryAllocator.h

  • PVRUtils/Vulkan/SpriteVk.h

  • PVRVk/ApiObjectsVk.h

  • PVRVk/CommandBufferVk.h

  • PVRVk/RenderPassVk.h

Included By#

Namespaces#

Classes#

Source Code#

#pragma once
#include "PVRUtils/Vulkan/SpriteVk.h"
#include "PVRUtils/Vulkan/HelperVk.h"
#include "PVRVk/CommandBufferVk.h"
#include "PVRVk/RenderPassVk.h"
#include "PVRVk/ApiObjectsVk.h"
#include "PVRUtils/Vulkan/MemoryAllocator.h"

namespace pvr {
namespace ui {
class UIRenderer
{
public:
    struct ProgramData
    {
        enum Uniform
        {
            UniformMVPmtx,
            UniformFontTexture,
            UniformColor,
            UniformAlphaMode,
            UniformUVmtx,
            NumUniform
        };
        enum Attribute
        {
            AttributeVertex,
            AttributeUV,
            NumAttribute
        };
        int32_t uniforms[NumUniform];
        int32_t attributes[NumAttribute];
    };

    const pvrvk::Buffer& getFontIbo()
    {
        if (!_fontIbo)
        {
            // create the FontIBO
            std::vector<uint16_t> fontFaces;
            fontFaces.resize(impl::Font_::FontElement);

            for (uint32_t i = 0; i < impl::Font_::MaxRenderableLetters; ++i)
            {
                fontFaces[i * 6] = static_cast<uint16_t>(0 + i * 4);
                fontFaces[i * 6 + 1] = static_cast<uint16_t>(3 + i * 4);
                fontFaces[i * 6 + 2] = static_cast<uint16_t>(1 + i * 4);

                fontFaces[i * 6 + 3] = static_cast<uint16_t>(3 + i * 4);
                fontFaces[i * 6 + 4] = static_cast<uint16_t>(0 + i * 4);
                fontFaces[i * 6 + 5] = static_cast<uint16_t>(2 + i * 4);
            }

            _fontIbo = utils::createBuffer(getDevice().lock(), pvrvk::BufferCreateInfo(sizeof(fontFaces[0]) * impl::Font_::FontElement, pvrvk::BufferUsageFlags::e_INDEX_BUFFER_BIT),
                pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT, pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT, _vmaAllocator, pvr::utils::vma::AllocationCreateFlags::e_MAPPED_BIT);
            pvr::utils::updateHostVisibleBuffer(_fontIbo, &fontFaces[0], 0, static_cast<uint32_t>(sizeof(fontFaces[0]) * fontFaces.size()), true);
        }
        return _fontIbo;
    }

    const pvrvk::Buffer& getImageVbo()
    {
        if (!_imageVbo)
        {
            // create the image vbo
            const float verts[] = {
                /*    Position  */
                -1.f, 1.f, 0.f, 1.0f, 0.0f, 1.0f, // upper left
                -1.f, -1.f, 0.f, 1.0f, 0.f, 0.0f, // lower left
                1.f, 1.f, 0.f, 1.0f, 1.f, 1.f, // upper right
                -1.f, -1.f, 0.f, 1.0f, 0.f, 0.0f, // lower left
                1.f, -1.f, 0.f, 1.0f, 1.f, 0.0f, // lower right
                1.f, 1.f, 0.f, 1.0f, 1.f, 1.f, // upper right
            };
            _imageVbo = utils::createBuffer(getDevice().lock(), pvrvk::BufferCreateInfo(sizeof(verts), pvrvk::BufferUsageFlags::e_VERTEX_BUFFER_BIT),
                pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT, pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT, _vmaAllocator, pvr::utils::vma::AllocationCreateFlags::e_MAPPED_BIT);
            pvr::utils::updateHostVisibleBuffer(_imageVbo, static_cast<const void*>(verts), 0, sizeof(verts), true);
        }
        return _imageVbo;
    }
    UIRenderer() : _screenRotation(.0f), _numSprites(0) {}

    UIRenderer(UIRenderer&& rhs)
        : _renderpass(std::move(rhs._renderpass)), _subpass(std::move(rhs._subpass)), _programData(std::move(rhs._programData)), _defaultFont(std::move(rhs._defaultFont)),
          _sdkLogo(std::move(rhs._sdkLogo)), _defaultTitle(std::move(rhs._defaultTitle)), _defaultDescription(std::move(rhs._defaultDescription)),
          _defaultControls(std::move(rhs._defaultControls)), _device(std::move(rhs._device)), _pipelineLayout(std::move(rhs._pipelineLayout)), _pipeline(std::move(rhs._pipeline)),
          _texDescLayout(std::move(rhs._texDescLayout)), _uboMvpDescLayout(std::move(rhs._uboMvpDescLayout)), _uboMaterialLayout(std::move(rhs._uboMaterialLayout)),
          _samplerBilinear(std::move(rhs._samplerBilinear)), _samplerTrilinear(std::move(rhs._samplerTrilinear)), _descPool(std::move(rhs._descPool)),
          _activeCommandBuffer(std::move(rhs._activeCommandBuffer)), _mustEndCommandBuffer(std::move(rhs._mustEndCommandBuffer)), _fontIbo(std::move(rhs._fontIbo)),
          _imageVbo(std::move(rhs._imageVbo)), _screenDimensions(std::move(rhs._screenDimensions)), _screenRotation(std::move(rhs._screenRotation)),
          _groupId(std::move(rhs._groupId)), _uboMvp(std::move(rhs._uboMvp)), _uboMaterial(std::move(rhs._uboMaterial)), _numSprites(std::move(rhs._numSprites)),
          _sprites(std::move(rhs._sprites)), _textElements(std::move(rhs._textElements)), _fonts(std::move(rhs._fonts))
    {
        updateResourceOwnsership();
    }

    UIRenderer& operator=(UIRenderer&& rhs)
    {
        if (this == &rhs) { return *this; }
        _renderpass = std::move(rhs._renderpass);
        _subpass = std::move(rhs._subpass);
        _programData = std::move(rhs._programData);
        _defaultFont = std::move(rhs._defaultFont);
        _sdkLogo = std::move(rhs._sdkLogo);
        _defaultTitle = std::move(rhs._defaultTitle);
        _defaultDescription = std::move(rhs._defaultDescription);
        _defaultControls = std::move(rhs._defaultControls);
        _device = std::move(rhs._device);
        _pipelineLayout = std::move(rhs._pipelineLayout);
        _pipeline = std::move(rhs._pipeline);
        _texDescLayout = std::move(rhs._texDescLayout);
        _uboMvpDescLayout = std::move(rhs._uboMvpDescLayout);
        _uboMaterialLayout = std::move(rhs._uboMaterialLayout);
        _samplerBilinear = std::move(rhs._samplerBilinear);
        _samplerTrilinear = std::move(rhs._samplerTrilinear);
        _descPool = std::move(rhs._descPool);
        _activeCommandBuffer = std::move(rhs._activeCommandBuffer);
        _mustEndCommandBuffer = std::move(rhs._mustEndCommandBuffer);
        _fontIbo = std::move(rhs._fontIbo);
        _imageVbo = std::move(rhs._imageVbo);
        _screenDimensions = std::move(rhs._screenDimensions);
        _screenRotation = std::move(rhs._screenRotation);
        _groupId = std::move(rhs._groupId);
        _uboMvp = std::move(rhs._uboMvp);
        _uboMaterial = std::move(rhs._uboMaterial);
        _numSprites = std::move(rhs._numSprites);
        updateResourceOwnsership();
        return *this;
    }

    UIRenderer& operator=(const UIRenderer& rhs) = delete;
    UIRenderer(UIRenderer& rhs) = delete;

    pvrvk::DeviceWeakPtr& getDevice() { return _device; }

    const pvrvk::DeviceWeakPtr& getDevice() const { return _device; }

    const ProgramData& getProgramData() { return _programData; }

    pvrvk::GraphicsPipeline getPipeline() { return _pipeline; }

    pvr::utils::vma::Allocator& getMemoryAllocator() { return _vmaAllocator; }

    const pvr::utils::vma::Allocator& getMemoryAllocator() const { return _vmaAllocator; }

    bool isRendering() { return _activeCommandBuffer->isRecording(); }

    void init(uint32_t width, uint32_t height, bool fullscreen, const pvrvk::RenderPass& renderpass, uint32_t subpass, bool isFrameBufferSrgb, pvrvk::CommandPool& commandPool,
        pvrvk::Queue& queue, bool createDefaultLogo = true, bool createDefaultTitle = true, bool createDefaultFont = true, uint32_t maxNumInstances = 64, uint32_t maxNumSprites = 64);

    void init(uint32_t width, uint32_t height, bool fullscreen, const pvrvk::RenderPass& renderpass, uint32_t subpass, bool isFrameBufferSrgb, pvrvk::CommandPool& commandPool,
        pvrvk::Queue& queue, const pvrvk::ImageView& fontView, const pvr::TextureHeader& textureHeader , const pvrvk::Sampler& fontSampler = pvrvk::Sampler(), bool createDefaultLogo = true, bool createDefaultTitle = true,
        uint32_t maxNumInstances = 64, uint32_t maxNumSprites = 64);

    ~UIRenderer()
    {
        _defaultFont.reset();
        _defaultTitle.reset();
        _defaultDescription.reset();
        _defaultControls.reset();
        _sdkLogo.reset();
        _uboMaterial.reset();
        _uboMvp.reset();
        _texDescLayout.reset();
        _uboMvpDescLayout.reset();
        _uboMaterialLayout.reset();
        _pipelineLayout.reset();
        _pipeline.reset();
        _pipelineCache.reset();
        _samplerBilinear.reset();
        _samplerTrilinear.reset();
        _activeCommandBuffer.reset();
        _fontIbo.reset();
        _imageVbo.reset();
        _sprites.clear();
        _fonts.clear();
        _textElements.clear();
        _screenRotation = .0f;
        _numSprites = 0;
        _renderpass.reset();
        _vmaAllocator.reset();
        _descPool.reset();
        _device.reset();
    }

    TextElement createTextElement(const std::string& text, uint32_t maxLength) { return createTextElement(text, _defaultFont, maxLength); }

    TextElement createTextElement(const std::string& text, const Font& font, uint32_t maxLength);

    TextElement createTextElement(const Font& font, uint32_t maxLength) { return createTextElement(std::string(""), font, maxLength); }

    TextElement createTextElement(const std::wstring& text, uint32_t maxLength) { return createTextElement(text, _defaultFont, maxLength); }

    TextElement createTextElement(const std::wstring& text, const Font& font, uint32_t maxLength);

    Text createText(const TextElement& textElement);

    Text createText(uint32_t maxLength = 255) { return createText(createTextElement("", maxLength)); }

    Text createText(const std::string& text, uint32_t maxLength = 0) { return createText(createTextElement(text, maxLength)); }

    Text createText(const Font& font, const std::string& text, uint32_t maxLength = 0) { return createText(createTextElement(text, font, maxLength)); }

    Text createText(const Font& font, uint32_t maxLength = 255) { return createText(createTextElement("", font, maxLength)); }

    Text createText(const std::wstring& text, uint32_t maxLength = 0) { return createText(createTextElement(text, maxLength)); }

    Text createText(const Font& font, const std::wstring& text) { return createText(createTextElement(text, font, static_cast<uint32_t>(text.length()))); }

    Text createText(const Font& font, const std::wstring& text, uint32_t maxLength = 0) { return createText(createTextElement(text, font, maxLength)); }

    float getRenderingDimX() const { return _screenDimensions.x; }

    float getRenderingDimY() const { return _screenDimensions.y; }

    glm::vec2 getRenderingDim() const { return _screenDimensions; }

    pvrvk::Rect2D getViewport() const
    {
        return pvrvk::Rect2D(pvrvk::Offset2D(0, 0), pvrvk::Extent2D(static_cast<int32_t>(getRenderingDimX()), static_cast<uint32_t>(getRenderingDimY())));
    }

    void setRenderingDimX(float value) { _screenDimensions.x = value; }

    void setRenderingDimY(float value) { _screenDimensions.y = value; }

    Font createFont(const pvrvk::ImageView& image, const TextureHeader& textureHeader, const pvrvk::Sampler& sampler = pvrvk::Sampler());

    Image createImage(const pvrvk::ImageView& image, const pvrvk::Sampler& sampler = pvrvk::Sampler());

    Image createImageFromAtlas(const pvrvk::ImageView& image, const pvrvk::Rect2Df& uv, const pvrvk::Sampler& sampler = pvrvk::Sampler());

    MatrixGroup createMatrixGroup();

    PixelGroup createPixelGroup();

    void beginRendering(pvrvk::SecondaryCommandBuffer& commandBuffer) { beginRendering(commandBuffer, pvrvk::Framebuffer(), true); }

    void beginRendering(pvrvk::SecondaryCommandBuffer& commandBuffer, const pvrvk::Framebuffer& framebuffer, bool useRenderPass = false)
    {
        if (!commandBuffer->isRecording())
        {
            if (useRenderPass) { commandBuffer->begin(_renderpass, _subpass); }
            else
            {
                commandBuffer->begin(framebuffer, _subpass);
            }
            _mustEndCommandBuffer = true;
        }
        else
        {
            _mustEndCommandBuffer = false;
        }
        pvr::utils::beginCommandBufferDebugLabel(commandBuffer, pvrvk::DebugUtilsLabel("PVRUtilsVk::UIRenderer::Rendering"));
        commandBuffer->bindPipeline(getPipeline()); // bind the uirenderer pipeline
        _activeCommandBuffer = commandBuffer;
    }

    void beginRendering(pvrvk::CommandBuffer& commandBuffer)
    {
        debug_assertion(commandBuffer->isRecording(),
            "UIRenderer: If a Primary command buffer is passed to the UIRenderer,"
            " it must be in the Recording state");
        pvr::utils::beginCommandBufferDebugLabel(commandBuffer, pvrvk::DebugUtilsLabel("PVRUtilsVk::UIRenderer::Rendering"));
        _mustEndCommandBuffer = false;
        commandBuffer->bindPipeline(getPipeline()); // bind the uirenderer pipeline
        _activeCommandBuffer = commandBuffer;
    }

    void beginRendering(pvrvk::SecondaryCommandBuffer commandBuffer, pvrvk::GraphicsPipeline& pipe) { beginRendering(commandBuffer, pipe, pvrvk::Framebuffer(), true); }

    void beginRendering(pvrvk::SecondaryCommandBuffer commandBuffer, pvrvk::GraphicsPipeline& pipe, const pvrvk::Framebuffer& framebuffer, bool useRenderPass = false)
    {
        if (!commandBuffer->isRecording())
        {
            if (useRenderPass) { commandBuffer->begin(_renderpass, _subpass); }
            else
            {
                commandBuffer->begin(framebuffer, _subpass);
            }
            _mustEndCommandBuffer = true;
        }
        else
        {
            _mustEndCommandBuffer = false;
        }
        pvr::utils::beginCommandBufferDebugLabel(commandBuffer, pvrvk::DebugUtilsLabel("PVRUtilsVk::UIRenderer::Rendering"));
        commandBuffer->bindPipeline(pipe);
        _activeCommandBuffer = commandBuffer;
    }

    void beginRendering(pvrvk::CommandBuffer commandBuffer, pvrvk::GraphicsPipeline& pipe)
    {
        debug_assertion(commandBuffer->isRecording(),
            "UIRenderer: If a Primary command buffer is passed to the UIRenderer,"
            " it must be in the Recording state");
        pvr::utils::beginCommandBufferDebugLabel(commandBuffer, pvrvk::DebugUtilsLabel("PVRUtilsVk::UIRenderer::Rendering"));
        _mustEndCommandBuffer = false;
        commandBuffer->bindPipeline(pipe);
        _activeCommandBuffer = commandBuffer;
    }

    void endRendering()
    {
        if (_activeCommandBuffer)
        {
            pvr::utils::endCommandBufferDebugLabel(_activeCommandBuffer);
            if (_mustEndCommandBuffer)
            {
                _mustEndCommandBuffer = false;
                _activeCommandBuffer->end();
            }
            _activeCommandBuffer.reset();
        }
    }

    pvrvk::CommandBufferBase& getActiveCommandBuffer() { return _activeCommandBuffer; }

    const Font& getDefaultFont() const { return _defaultFont; }

    Font& getDefaultFont() { return _defaultFont; }

    const Image& getSdkLogo() const { return _sdkLogo; }

    Image& getSdkLogo() { return _sdkLogo; }

    const Text& getDefaultTitle() const { return _defaultTitle; }

    Text& getDefaultTitle() { return _defaultTitle; }

    const Text& getDefaultDescription() const { return _defaultDescription; }

    Text& getDefaultDescription() { return _defaultDescription; }

    const Text& getDefaultControls() const { return _defaultControls; }

    Text& getDefaultControls() { return _defaultControls; }

    pvrvk::PipelineLayout getPipelineLayout() { return _pipelineLayout; }

    glm::mat4 getProjection() const { return pvr::math::ortho(Api::Vulkan, 0.0, getRenderingDimX(), 0.0f, getRenderingDimY()); }

    void rotateScreen90degreeCCW()
    {
        _screenRotation += glm::pi<float>() * .5f;
        std::swap(_screenDimensions.x, _screenDimensions.y);
    }

    void rotateScreen90degreeCW()
    {
        _screenRotation -= glm::pi<float>() * .5f;
        std::swap(_screenDimensions.x, _screenDimensions.y);
    }

    glm::mat4 getScreenRotation() const { return glm::rotate(_screenRotation, glm::vec3(0.0f, 0.0f, 1.f)); }

    const pvrvk::DescriptorSetLayout& getTexDescriptorSetLayout() const { return _texDescLayout; }

    const pvrvk::DescriptorSetLayout& getUboDescSetLayout() const { return _uboMvpDescLayout; }

    uint32_t getMaxRenderableSprites() const { return _uboMaterial._numArrayId; }

    uint32_t getMaxInstances() const { return _uboMvp._numArrayId; }

    uint32_t getNumAvailableSprites() const { return _uboMaterial.getNumAvailableBufferArrays(); }

    uint32_t getNumAvailableInstances() const { return _uboMvp.getNumAvailableBufferArrays(); }

    pvrvk::DescriptorPool& getDescriptorPool() { return _descPool; }

    pvrvk::Sampler& getSamplerBilinear() { return _samplerBilinear; }

    pvrvk::Sampler& getSamplerTrilinear() { return _samplerTrilinear; }

private:
    void updateResourceOwnsership()
    {
        std::for_each(_sprites.begin(), _sprites.end(), [this](SpriteWeakRef& sprite) { sprite.lock()->setUIRenderer(this); });

        std::for_each(_fonts.begin(), _fonts.end(), [this](FontWeakRef& font) { font.lock()->setUIRenderer(this); });

        std::for_each(_textElements.begin(), _textElements.end(), [this](TextElementWeakRef& textElement) { textElement.lock()->setUIRenderer(this); });
    }

    friend class pvr::ui::impl::Image_;
    friend class pvr::ui::impl::Text_;
    friend class pvr::ui::impl::Group_;
    friend class pvr::ui::impl::Sprite_;
    friend class pvr::ui::impl::Font_;

    uint64_t generateGroupId() { return _groupId++; }

    struct UboMvp
    {
        friend class ::pvr::ui::UIRenderer;
        UboMvp() : _freeArrayId(0) {}
        void init(pvrvk::Device& device, pvrvk::DescriptorSetLayout& descLayout, pvrvk::DescriptorPool& pool, UIRenderer& uirenderer);
        void initLayout(pvrvk::Device& device, uint32_t numElements);

        void reset()
        {
            _buffer.reset();
            _uboDescSetSet.reset();
        }

        void updateMvp(uint32_t bufferArrayId, const glm::mat4x4& mvp);

        int32_t getNewBufferSlice()
        {
            if (_freeArrayIds.size())
            {
                const uint32_t id = _freeArrayIds.back();
                _freeArrayIds.pop_back();
                return static_cast<int32_t>(id);
            }
            return (_freeArrayId < _numArrayId ? static_cast<int32_t>(_freeArrayId++) : -1);
        }

        void releaseBufferSlice(uint32_t id)
        {
            debug_assertion(id < _numArrayId, "Invalid id");
            _freeArrayIds.emplace_back(id);
        }

        void bindUboDynamic(pvrvk::CommandBufferBase& cb, const pvrvk::PipelineLayout& pipelayout, uint32_t mvpBufferSlice)
        {
            uint32_t dynamicOffsets[] = { static_cast<uint32_t>(_structuredBufferView.getDynamicSliceOffset(mvpBufferSlice)) };
            cb->bindDescriptorSet(pvrvk::PipelineBindPoint::e_GRAPHICS, pipelayout, 1, _uboDescSetSet, dynamicOffsets, ARRAY_SIZE(dynamicOffsets));
        }

        uint32_t getNumAvailableBufferArrays() const { return static_cast<uint32_t>((_numArrayId - _freeArrayId) + _freeArrayIds.size()); }

    private:
        uint32_t _freeArrayId;
        utils::StructuredBufferView _structuredBufferView;
        pvrvk::Buffer _buffer;
        pvr::utils::vma::Allocation _memAllocation;
        pvrvk::DescriptorSet _uboDescSetSet;
        uint32_t _numArrayId;
        std::vector<uint32_t> _freeArrayIds;
    };

    struct UboMaterial
    {
    public:
        friend class ::pvr::ui::UIRenderer;
        UboMaterial() : _freeArrayId(0) {}

        void reset()
        {
            _buffer.reset();
            _uboDescSetSet.reset();
        }

        void init(pvrvk::Device& device, pvrvk::DescriptorSetLayout& descLayout, pvrvk::DescriptorPool& pool, UIRenderer& uirenderer);
        void initLayout(pvrvk::Device& device, uint32_t numArrayId);

        void updateMaterial(uint32_t arrayIndex, const glm::vec4& color, int32_t alphaMode, const glm::mat4& uv);

        int32_t getNewBufferArray()
        {
            if (_freeArrayIds.size())
            {
                const uint32_t id = _freeArrayIds.back();
                _freeArrayIds.pop_back();
                return static_cast<int32_t>(id);
            }
            return (_freeArrayId < _numArrayId ? static_cast<int32_t>(_freeArrayId++) : -1);
        }

        void releaseBufferArray(uint32_t id)
        {
            debug_assertion(id < _numArrayId, "Invalid id");
            _freeArrayIds.emplace_back(id);
        }

        void bindUboDynamic(pvrvk::CommandBufferBase& cb, const pvrvk::PipelineLayout& pipelayout, uint32_t bufferSlice)
        {
            uint32_t dynamicOffsets[] = { static_cast<uint32_t>(_structuredBufferView.getDynamicSliceOffset(bufferSlice)) };
            cb->bindDescriptorSet(pvrvk::PipelineBindPoint::e_GRAPHICS, pipelayout, 2, _uboDescSetSet, dynamicOffsets, ARRAY_SIZE(dynamicOffsets));
        }

        uint32_t getNumAvailableBufferArrays() const { return static_cast<uint32_t>(((_numArrayId - _freeArrayId) + _freeArrayIds.size())); }

    private:
        pvrvk::DescriptorSet _uboDescSetSet;
        uint32_t _freeArrayId;
        uint32_t _numArrayId;
        utils::StructuredBufferView _structuredBufferView;
        pvrvk::Buffer _buffer;
        std::vector<uint32_t> _freeArrayIds;
    };

    UboMvp& getUbo() { return _uboMvp; }

    UboMaterial& getMaterial() { return _uboMaterial; }

    void setUpUboPoolLayouts(uint32_t numInstances, uint32_t numSprites);
    void setUpUboPools(uint32_t numInstances, uint32_t numSprites);

    void initCreateDefaultFont(pvrvk::CommandBuffer& cmdBuffer);
    void initCreateDefaultSdkLogo(pvrvk::CommandBuffer& cmdBuffer);
    void initCreateDefaultSampler();
    void initCreateDefaultTitle();
    void initCreatePipeline(bool isFramebufferSrgb);
    void initCreateDescriptorSetLayout();

    pvr::utils::vma::Allocator _vmaAllocator;
    std::vector<SpriteWeakRef> _sprites;
    std::vector<TextElementWeakRef> _textElements;
    std::vector<FontWeakRef> _fonts;

    pvrvk::RenderPass _renderpass;
    uint32_t _subpass;
    ProgramData _programData;
    Font _defaultFont;
    Image _sdkLogo;
    Text _defaultTitle;
    Text _defaultDescription;
    Text _defaultControls;
    pvrvk::DeviceWeakPtr _device;

    pvrvk::PipelineLayout _pipelineLayout;
    pvrvk::GraphicsPipeline _pipeline;
    pvrvk::PipelineCache _pipelineCache;
    pvrvk::DescriptorSetLayout _texDescLayout;
    pvrvk::DescriptorSetLayout _uboMvpDescLayout;
    pvrvk::DescriptorSetLayout _uboMaterialLayout;
    pvrvk::Sampler _samplerBilinear;
    pvrvk::Sampler _samplerTrilinear;
    pvrvk::DescriptorPool _descPool;
    pvrvk::CommandBufferBase _activeCommandBuffer;
    bool _mustEndCommandBuffer;
    pvrvk::Buffer _fontIbo;
    pvrvk::Buffer _imageVbo;
    glm::vec2 _screenDimensions;
    float _screenRotation;
    uint64_t _groupId = 1;
    UboMvp _uboMvp;
    UboMaterial _uboMaterial;
    uint32_t _numSprites;

    // Methods and Members related to MoltenVK support.
#ifdef VK_USE_PLATFORM_MACOS_MVK

    MVKConfiguration mvkConfig;
    size_t sizeOfMVK = 0;
    bool isFullImageViewSwizzleMVK = false;

#endif
};
} // namespace ui
} // namespace pvr