//// 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. Load more
// // 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(); }