#pragma once #include #include "RenderBackend.h" // Vulkan implementation. Qt Quick creates the VkDevice (on the physical device // SteamVR reports for the HMD, with the device extensions SteamVR requires); // this backend owns the color image Qt renders into and handles the layout // dance SteamVR needs: // // Qt renders -> image in COLOR_ATTACHMENT_OPTIMAL // finishFrame() -> transition to TRANSFER_SRC_OPTIMAL (required by // OpenVR); // the CPU waits for it, the texture is handed over // right after // SetOverlayTexture -> SteamVR copies from the image using our queue // beginFrame() (next) -> transition back to COLOR_ATTACHMENT_OPTIMAL, the // layout Qt's // resource tracking expects; no CPU wait: queue order // puts it before Qt's frame // // Everything runs on the GUI thread; QQuickRenderControl::endFrame() waits for // the GPU (offscreen frames are synchronous). class VulkanRenderBackend final : public RenderBackend { public: VulkanRenderBackend() = default; ~VulkanRenderBackend() override; QString name() const override { return QStringLiteral("Vulkan"); } QSGRendererInterface::GraphicsApi graphicsApi() const override { return QSGRendererInterface::Vulkan; } bool initialize(const RenderInterop& interop, QString* error) override; void configureWindow(QQuickWindow* window) override; bool attachWindow(QQuickWindow* window, QString* error) override; void detachWindow() override; QString deviceName() const override { return m_deviceName; } bool makeCurrent() override { return true; } void doneCurrent() override {} QQuickRenderTarget createRenderTarget(QSize size) override; void releaseRenderTarget() override; void beginFrame() override; void finishFrame() override; void waitForGpu() override; vr::Texture_t vrTexture() override; vr::VRTextureBounds_t vrTextureBounds() const override; QImage readback() override; private: bool findMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties, uint32_t* index) const; // A command buffer with its fence, for small submissions of our own. struct Submission { VkCommandBuffer commandBuffer = VK_NULL_HANDLE; VkFence fence = VK_NULL_HANDLE; bool pending = false; }; // Records `record` into the submission's command buffer (after its // previous use has finished) and submits it; with `wait`, blocks until // the GPU has executed it. bool submit(Submission& submission, const std::function& record, bool wait); bool waitFor(Submission& submission); void transitionImage(Submission& submission, VkImageLayout from, VkImageLayout to, bool wait); QVulkanInstance m_instance; QByteArrayList m_deviceExtensions; VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; VkDevice m_device = VK_NULL_HANDLE; VkQueue m_queue = VK_NULL_HANDLE; uint32_t m_queueFamilyIndex = 0; QVulkanDeviceFunctions* m_df = nullptr; VkCommandPool m_commandPool = VK_NULL_HANDLE; Submission m_restore; // beginFrame(): async Submission m_handoff; // finishFrame() and readback(): synchronous VkImage m_image = VK_NULL_HANDLE; VkDeviceMemory m_imageMemory = VK_NULL_HANDLE; // Actual layout outside of Qt's frame VkImageLayout m_layout = VK_IMAGE_LAYOUT_UNDEFINED; QSize m_size; vr::VRVulkanTextureData_t m_vrTextureData{}; QString m_deviceName; };