732 lines · 25.6 KB · C++
RawDownload
////  Created by Bradley Austin Davis on 2018/10/21//  Copyright 2014 High Fidelity, Inc.//  Copyright 2024 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "RenderThread.h"#include <QtGui/QWindow>#include <QtCore/QThreadPool>#ifdef Q_OS_LINUX#include <QtX11Extras/QX11Info>#endif#ifdef USE_GL#include <gl/OffscreenGLCanvas.h>#include <gl/QOpenGLContextWrapper.h>#else#include <gpu/vk/VKFramebuffer.h>#endif //#define FRAME_PLAYER_ENABLE_RENDERDOC_CAPTURE #ifdef FRAME_PLAYER_ENABLE_RENDERDOC_CAPTURE// VKTODO: this is Linux-specific#include <dlfcn.h>// VKTODO: get a proper way to add renderdoc include path#include "renderdoc_1.35/include/renderdoc_app.h"RENDERDOC_API_1_1_2 *rdoc_api = NULL;#endif void RenderThread::submitFrame(const gpu::FramePointer& frame) {    std::unique_lock<std::mutex> lock(_frameLock);    _pendingFrames.push(frame);} void RenderThread::resize(const QSize& newSize) {    std::unique_lock<std::mutex> lock(_frameLock);    _pendingSize.push(newSize);} void RenderThread::move(const glm::vec3& v) {    std::unique_lock<std::mutex> lock(_frameLock);    _correction = glm::inverse(glm::translate(mat4(), v)) * _correction;} void RenderThread::initialize(QWindow* window) {#ifdef FRAME_PLAYER_ENABLE_RENDERDOC_CAPTURE    if(void *mod = dlopen("/home/ksuprynowicz/programy/renderdoc_1.35/lib/librenderdoc.so", RTLD_NOW | RTLD_NOLOAD))    {        pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");        int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api);        assert(ret == 1);    }#endif     std::unique_lock<std::mutex> lock(_frameLock);    setObjectName("RenderThread");    Parent::initialize();     _window = window; #ifdef USE_GL    //_vkcontext.createInstance();    //_vkcontext.createDevice();    //_vkstagingBuffer = _vkcontext.createStagingBuffer(1024 * 1024 * 512, nullptr);     _window->setFormat(getDefaultOpenGLSurfaceFormat());    _context.setWindow(window);    _context.create();    if (!_context.makeCurrent()) {        qFatal("Unable to make context current");    }    QOpenGLContextWrapper(_context.qglContext()).makeCurrent(_window);    glGenTextures(1, &_externalTexture);    glBindTexture(GL_TEXTURE_2D, _externalTexture);    static const glm::u8vec4 color{ 0 };    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &color);    gl::setSwapInterval(0);    // GPU library init    gpu::Context::init<gpu::gl::GLBackend>();    _context.makeCurrent();    _gpuContext = std::make_shared<gpu::Context>();    _backend = _gpuContext->getBackend();    _context.doneCurrent();    _context.moveToThread(_thread);        if (!_presentPipeline) {        gpu::ShaderPointer program = gpu::Shader::createProgram(shader::gpu::program::DrawTexture);        gpu::StatePointer state = std::make_shared<gpu::State>();        _presentPipeline = gpu::Pipeline::create(program, state);    }#else    _vkcontext.setValidationEnabled(true);    auto size = window->size();    _extent = VkExtent2D{ (uint32_t)size.width(), (uint32_t)size.height() };     _vkcontext.setValidationEnabled(true);    _vkcontext.requireExtensions({        std::string{ VK_KHR_SURFACE_EXTENSION_NAME },#ifdef WIN32        std::string{ VK_KHR_WIN32_SURFACE_EXTENSION_NAME },#else        std::string{ VK_KHR_XCB_SURFACE_EXTENSION_NAME },#endif    });    _vkcontext.requireDeviceExtensions({ VK_KHR_SWAPCHAIN_EXTENSION_NAME });    _vkcontext.createInstance();#ifdef WIN32    _surface = _vkcontext.instance.createWin32SurfaceKHR({ {}, GetModuleHandle(NULL), (HWND)window->winId() });#else    _vkcontext.createDevice();    _swapchain.setContext(&_vkcontext);    _swapchain.initSurface(QX11Info::connection(), (xcb_window_t)(window->winId()));#endif    _swapchain.create(&_extent.width, &_extent.height, true);     setupRenderPass();    setupFramebuffers();     VkSemaphoreCreateInfo semaphoreCI = vks::initializers::semaphoreCreateInfo();    VK_CHECK_RESULT(vkCreateSemaphore(_vkcontext.device->logicalDevice, &semaphoreCI, nullptr, &acquireComplete));    VK_CHECK_RESULT(vkCreateSemaphore(_vkcontext.device->logicalDevice, &semaphoreCI, nullptr, &renderComplete));     // GPU library init    gpu::Context::init<gpu::vk::VKBackend>();    _gpuContext = std::make_shared<gpu::Context>();    _backend = _gpuContext->getBackend();    auto vkBackend = std::dynamic_pointer_cast<gpu::vk::VKBackend>(_gpuContext->getBackend());    vkBackend->setIsFramePlayer(true);#endif} void RenderThread::setup() {    // Wait until the context has been moved to this thread    { std::unique_lock<std::mutex> lock(_frameLock); }    _gpuContext->beginFrame();    _gpuContext->endFrame(); #ifdef USE_GL    _context.makeCurrent();    glViewport(0, 0, 800, 600);    (void)CHECK_GL_ERROR();#endif    _elapsed.start();} void RenderThread::shutdown() {    _activeFrame.reset();    while (!_pendingFrames.empty()) {        _gpuContext->consumeFrameUpdates(_pendingFrames.front());        _pendingFrames.pop();    }    _gpuContext->shutdown();    _gpuContext.reset();} void RenderThread::renderFrame(gpu::FramePointer& frame) {    if (frame && !frame->batches.empty()) {#ifdef FRAME_PLAYER_ENABLE_RENDERDOC_CAPTURE        if (rdoc_api)            rdoc_api->StartFrameCapture(NULL, NULL);#endif    }#ifdef USE_GL    PROFILE_RANGE(render_gpu_gl, __FUNCTION__);#else    PROFILE_RANGE(render_gpu, __FUNCTION__);#endif    ++_presentCount;#ifdef USE_GL    _context.makeCurrent();#else    auto vkBackend = std::dynamic_pointer_cast<gpu::vk::VKBackend>(_gpuContext->getBackend());#endif     {       std::unique_lock<std::mutex> lock(_frameLock);       if (_correction != glm::mat4()) {           _backend->updatePresentFrame(_correction);#ifndef USE_GL            vkBackend->enableContextViewCorrectionForFramePlayer();#endif       }    }    _backend->recycle();    _backend->syncCache();     auto windowSize = _window->size(); #ifndef USE_GL    auto windowExtent = VkExtent2D{ (uint32_t)windowSize.width(), (uint32_t)windowSize.height() };    if (windowExtent.width != _extent.width || windowExtent.height != _extent.height) {        return;    }     if (_extent.width != _swapchain.extent.width || _extent.width != _swapchain.extent.width) {        _swapchain.create(&_extent.width, &_extent.height, false, false);        setupFramebuffers();        return;    }     static const VkOffset2D offset{0, 0};    static const std::array<VkClearValue, 2> clearValues{        VkClearValue{color: VkClearColorValue{float32: { 0.2f, 0.2f, 0.2f, 0.2f }}},        VkClearValue{depthStencil: VkClearDepthStencilValue{ 1.0f, 0 }},    };     uint32_t swapchainIndex;    VK_CHECK_RESULT(_swapchain.acquireNextImage(acquireComplete, &swapchainIndex));    auto framebuffer = _framebuffers[swapchainIndex];    const auto& commandBuffer = _vkcontext.createCommandBuffer(_vkcontext.device->graphicsCommandPool);    //auto vkBackend = dynamic_pointer_cast<gpu::vulkan::VKBackend>(getBackend());    //Q_ASSERT(vkBackend);     auto rect = VkRect2D{ offset, _extent };    VkRenderPassBeginInfo beginInfo = vks::initializers::renderPassBeginInfo();    beginInfo.renderPass = _renderPass;    beginInfo.framebuffer = framebuffer;    beginInfo.renderArea = rect;    beginInfo.clearValueCount = (uint32_t)clearValues.size();    beginInfo.pClearValues = clearValues.data();        //_renderPass, framebuffer, rect, (uint32_t)clearValues.size(), clearValues.data() };    VkCommandBufferBeginInfo commandBufferBeginInfo = vks::initializers::commandBufferBeginInfo();    commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;    VK_CHECK_RESULT(vkBeginCommandBuffer(commandBuffer, &commandBufferBeginInfo));     using namespace vks::debugutils;    cmdBeginLabel(commandBuffer, "executeFrame", glm::vec4{ 1, 1, 1, 1 });#endif    glm::uvec2 fboSize{ frame->framebuffer->getWidth(), frame->framebuffer->getHeight() };#ifdef USE_GL    auto& glbackend = (gpu::gl::GLBackend&)(*_backend);    auto fbo = glbackend.getFramebufferID(frame->framebuffer);    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);    glClearColor(0, 0, 0, 1);    glClearDepth(0);    glClear(GL_DEPTH_BUFFER_BIT);    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);#else     //_gpuContext->enableStereo(true);    //auto vkBackend = std::dynamic_pointer_cast<gpu::vk::VKBackend>(_gpuContext->getBackend());    vkBackend->setDrawCommandBuffer(commandBuffer);#endif     if (frame && !frame->batches.empty()) {        // VKTODO: this is a temporary workaround, until render passes are selected inside Vulkan backend        //vkCmdBeginRenderPass(commandBuffer, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);        _gpuContext->executeFrame(frame);        _renderedFrameCount++;        qDebug() << "Frame rendered: " << _renderedFrameCount;        //vkCmdEndRenderPass(commandBuffer);    } #ifdef USE_GL    static gpu::BatchPointer batch = nullptr;    if (!batch) {        batch = std::make_shared<gpu::Batch>();        batch->setPipeline(_presentPipeline);        batch->setFramebuffer(nullptr);        batch->setResourceTexture(0, frame->framebuffer->getRenderBuffer(0));        batch->setViewportTransform(ivec4(uvec2(0), ivec2(windowSize.width(), windowSize.height())));        batch->draw(gpu::TRIANGLE_STRIP, 4);    }    glDisable(GL_FRAMEBUFFER_SRGB);    _gpuContext->executeBatch(*batch);        // Keep this raw gl code here for reference    //glDisable(GL_FRAMEBUFFER_SRGB);    //glClear(GL_COLOR_BUFFER_BIT);  /*  glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);    glBlitFramebuffer(        0, 0, fboSize.x, fboSize.y,         0, 0, windowSize.width(), windowSize.height(),        GL_COLOR_BUFFER_BIT, GL_NEAREST);*/    (void)CHECK_GL_ERROR();    _context.swapBuffers();    _context.doneCurrent();#else    cmdEndLabel(commandBuffer);    cmdBeginLabel(commandBuffer, "renderpass:testClear", glm::vec4{ 0, 1, 1, 1 });    vkCmdBeginRenderPass(commandBuffer, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);    vkCmdEndRenderPass(commandBuffer);     // Blit the image into the swapchain.    // is vks::tools::insertImageMemoryBarrier needed?    VkImageBlit imageBlit{};    imageBlit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;    imageBlit.srcSubresource.layerCount = 1;    imageBlit.srcSubresource.mipLevel = 0;    imageBlit.srcOffsets[1].x = vkBackend->_outputTexture->_gpuObject.getWidth();    imageBlit.srcOffsets[1].y = vkBackend->_outputTexture->_gpuObject.getHeight();    imageBlit.srcOffsets[1].z = 1;     imageBlit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;    imageBlit.dstSubresource.layerCount = 1;    imageBlit.dstSubresource.mipLevel = 0;    imageBlit.dstOffsets[1].x = _swapchain.extent.width;    imageBlit.dstOffsets[1].y = _swapchain.extent.height;    imageBlit.dstOffsets[1].z = 1;     VkImageSubresourceRange mipSubRange = {};    mipSubRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;    mipSubRange.baseMipLevel = 0;    mipSubRange.levelCount = 1;    mipSubRange.layerCount = 1;     vks::tools::insertImageMemoryBarrier(        commandBuffer,        vkBackend->_outputTexture->attachments[0].image,        VK_ACCESS_TRANSFER_READ_BIT,        0,        VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,        VK_PIPELINE_STAGE_TRANSFER_BIT,        VK_PIPELINE_STAGE_TRANSFER_BIT,        mipSubRange);     vks::tools::insertImageMemoryBarrier(        commandBuffer,        _swapchain.images[swapchainIndex],        0,        VK_ACCESS_TRANSFER_WRITE_BIT,        VK_IMAGE_LAYOUT_UNDEFINED,        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,        VK_PIPELINE_STAGE_TRANSFER_BIT,        VK_PIPELINE_STAGE_TRANSFER_BIT,        mipSubRange);     vkCmdBlitImage(        commandBuffer,        vkBackend->_outputTexture->attachments[0].image,        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,        _swapchain.images[swapchainIndex],        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,        1,        &imageBlit,        VK_FILTER_LINEAR);     vks::tools::insertImageMemoryBarrier(        commandBuffer,        _swapchain.images[swapchainIndex],        0,        VK_ACCESS_TRANSFER_WRITE_BIT,        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,        VK_PIPELINE_STAGE_TRANSFER_BIT,        VK_PIPELINE_STAGE_TRANSFER_BIT,        mipSubRange);     cmdEndLabel(commandBuffer);    VK_CHECK_RESULT(vkEndCommandBuffer(commandBuffer));     static const VkPipelineStageFlags waitFlags{ VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT };    VkSubmitInfo submitInfo = vks::initializers::submitInfo();    submitInfo.waitSemaphoreCount = 1;    submitInfo.pWaitSemaphores = &acquireComplete;    submitInfo.pWaitDstStageMask = &waitFlags;    submitInfo.commandBufferCount = 1;    submitInfo.pCommandBuffers = &commandBuffer;    submitInfo.signalSemaphoreCount = 1;    submitInfo.pSignalSemaphores = &renderComplete;    submitInfo.commandBufferCount = 1;    VkFenceCreateInfo fenceCI = vks::initializers::fenceCreateInfo();    VkFence frameFence;    vkCreateFence(_vkcontext.device->logicalDevice, &fenceCI, nullptr, &frameFence);    vkQueueSubmit(_vkcontext.graphicsQueue, 1, &submitInfo, frameFence);    _swapchain.queuePresent(_vkcontext.graphicsQueue, swapchainIndex, renderComplete);    //VKTODO _vkcontext.trashCommandBuffers({ commandBuffer });    vkBackend->waitForGPU();    vkBackend->recyclePreviousFrame();    if (frame && !frame->batches.empty()) {#ifdef FRAME_PLAYER_ENABLE_RENDERDOC_CAPTURE        if (rdoc_api)            rdoc_api->EndFrameCapture(NULL, NULL);#endif    } #endif} bool RenderThread::process() {    std::queue<gpu::FramePointer> pendingFrames;    std::queue<QSize> pendingSize;     {        std::unique_lock<std::mutex> lock(_frameLock);        pendingFrames.swap(_pendingFrames);        pendingSize.swap(_pendingSize);    }     while (!pendingFrames.empty()) {        _activeFrame = pendingFrames.front();        pendingFrames.pop();        _gpuContext->consumeFrameUpdates(_activeFrame);    }     while (!pendingSize.empty()) {#ifndef USE_GL        const auto& size = pendingSize.front();        _extent = VkExtent2D{ (uint32_t)size.width(), (uint32_t)size.height() };#endif        pendingSize.pop();    }     if (!_activeFrame) {        QThread::msleep(1);        return true;    }     renderFrame(_activeFrame);    /*if (_renderedFrameCount == 1) {        return false;    }*/    return true;} #ifndef USE_GL void RenderThread::setupFramebuffers() {    // Recreate the frame buffers    for (auto framebuffer : _framebuffers) {        _vkcontext.recycler.trashVkFramebuffer(framebuffer);    }    _framebuffers.clear();     std::vector<VkImageView> attachments;    attachments.resize(1);    VkFramebufferCreateInfo framebufferCreateInfo{};    framebufferCreateInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;    framebufferCreateInfo.pNext = NULL;    framebufferCreateInfo.renderPass = _renderPass;    framebufferCreateInfo.attachmentCount = 1;    framebufferCreateInfo.pAttachments = attachments.data();    framebufferCreateInfo.width = _extent.width;    framebufferCreateInfo.height = _extent.height;    framebufferCreateInfo.layers = 1;     // Create frame buffers for every swap chain image    _framebuffers.resize(_swapchain.imageCount);    for (uint32_t i = 0; i < _swapchain.imageCount; i++) {        attachments[0] = _swapchain.buffers[i].view;        VK_CHECK_RESULT(vkCreateFramebuffer(_vkcontext.device->logicalDevice, &framebufferCreateInfo, nullptr, &_framebuffers[i]));    }} void RenderThread::setupRenderPass() {    if (_renderPass) {        _vkcontext.recycler.trashVkRenderPass(_renderPass);    }     VkAttachmentDescription attachment{};    // Color attachment    attachment.format = _swapchain.colorFormat;    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;    attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;    attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;    attachment.samples = VK_SAMPLE_COUNT_1_BIT;     VkAttachmentReference colorAttachmentReference{};    colorAttachmentReference.attachment = 0;    colorAttachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;     VkSubpassDescription subpass{};    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;    subpass.colorAttachmentCount = 1;    subpass.pColorAttachments = &colorAttachmentReference;    /*VkSubpassDependency subpassDependency{};    subpassDependency.srcSubpass = 0;    subpassDependency.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;    subpassDependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;    subpassDependency.dstSubpass = VK_SUBPASS_EXTERNAL;    subpassDependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;    subpassDependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;*/     VkRenderPassCreateInfo renderPassInfo{};    renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;    renderPassInfo.attachmentCount = 1;    renderPassInfo.pAttachments = &attachment;    renderPassInfo.subpassCount = 1;    renderPassInfo.pSubpasses = &subpass;    // VKTODO    // renderPassInfo.dependencyCount = 1;    // renderPassInfo.pDependencies = &subpassDependency;    renderPassInfo.dependencyCount = 0;    VK_CHECK_RESULT(vkCreateRenderPass(_vkcontext.device->logicalDevice, &renderPassInfo, nullptr, &_renderPass));}#endif class QLambdaRunnable : public QRunnable {public:    QLambdaRunnable(const std::function<void()>& f) : _f(f) {}     void run() override { _f(); } 

Lines 1–500 of 732.