From 4d3cb0012c1c80520f87a8ca84b7f66484178a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Micha=C3=ABl=20Celerier?= Date: Fri, 17 Apr 2020 15:32:44 +0200 Subject: rhi: Work needed for PBR material support in RHI Change-Id: Id0e7d343083afcefd93f9d581917e14baa994b7f Reviewed-by: Paul Lemire --- src/extras/defaults/qmetalroughmaterial.cpp | 5 + .../rhi/graphicshelpers/submissioncontext.cpp | 252 ++++++++++++++------- .../rhi/graphicshelpers/submissioncontext_p.h | 26 +-- .../renderers/rhi/renderer/rendercommand.cpp | 6 + .../renderers/rhi/renderer/rendercommand_p.h | 2 + src/plugins/renderers/rhi/renderer/renderer.cpp | 81 ++++--- src/plugins/renderers/rhi/renderer/renderview.cpp | 42 +++- src/plugins/renderers/rhi/renderer/renderview_p.h | 12 +- src/plugins/renderers/rhi/textures/texture.cpp | 91 ++++++-- src/render/render.pro | 1 + src/render/shadergraph/qshadergenerator.cpp | 2 +- src/render/surfaces/surfaces.pri | 9 + src/render/surfaces/vulkaninstance.cpp | 75 ++++++ src/render/surfaces/vulkaninstance_p.h | 54 +++++ 14 files changed, 501 insertions(+), 157 deletions(-) create mode 100644 src/render/surfaces/surfaces.pri create mode 100644 src/render/surfaces/vulkaninstance.cpp create mode 100644 src/render/surfaces/vulkaninstance_p.h (limited to 'src') diff --git a/src/extras/defaults/qmetalroughmaterial.cpp b/src/extras/defaults/qmetalroughmaterial.cpp index 7aecfa65d..78e544ead 100644 --- a/src/extras/defaults/qmetalroughmaterial.cpp +++ b/src/extras/defaults/qmetalroughmaterial.cpp @@ -377,6 +377,7 @@ void QMetalRoughMaterial::setBaseColor(const QVariant &baseColor) } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers); + d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers); } void QMetalRoughMaterial::setMetalness(const QVariant &metalness) @@ -401,6 +402,7 @@ void QMetalRoughMaterial::setMetalness(const QVariant &metalness) } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers); + d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers); } void QMetalRoughMaterial::setRoughness(const QVariant &roughness) @@ -425,6 +427,7 @@ void QMetalRoughMaterial::setRoughness(const QVariant &roughness) } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers); + d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers); } void QMetalRoughMaterial::setAmbientOcclusion(const QVariant &ambientOcclusion) @@ -445,6 +448,7 @@ void QMetalRoughMaterial::setAmbientOcclusion(const QVariant &ambientOcclusion) } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers); + d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers); } void QMetalRoughMaterial::setNormal(const QVariant &normal) @@ -465,6 +469,7 @@ void QMetalRoughMaterial::setNormal(const QVariant &normal) } d->m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers); d->m_metalRoughES3ShaderBuilder->setEnabledLayers(layers); + d->m_metalRoughRHIShaderBuilder->setEnabledLayers(layers); } void QMetalRoughMaterial::setTextureScale(float textureScale) diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp index eac49f01a..748138540 100644 --- a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp +++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext.cpp @@ -62,6 +62,8 @@ #include #include #include +#include +#include #include #include #include @@ -91,6 +93,7 @@ #include #include #endif +#include QT_BEGIN_NAMESPACE @@ -489,9 +492,6 @@ SubmissionContext::SubmissionContext() , m_rhi(nullptr) , m_currentSwapChain(nullptr) , m_currentRenderPassDescriptor(nullptr) -#if QT_CONFIG(vulkan) - , m_vkInstance(nullptr) -#endif #ifndef QT_NO_OPENGL , m_fallbackSurface(nullptr) #endif @@ -537,34 +537,9 @@ void SubmissionContext::initialize() #if QT_CONFIG(vulkan) if (requestedApi == Qt3DRender::API::Vulkan) { - if (!m_vkInstance) { - m_vkInstance = new QVulkanInstance(); - m_ownsVkInstance = true; - -#ifndef Q_OS_ANDROID - m_vkInstance->setLayers({ "VK_LAYER_LUNARG_standard_validation" }); -#else - m_vkInstance->setLayers(QByteArrayList() - << "VK_LAYER_GOOGLE_threading" - << "VK_LAYER_LUNARG_parameter_validation" - << "VK_LAYER_LUNARG_object_tracker" - << "VK_LAYER_LUNARG_core_validation" - << "VK_LAYER_LUNARG_image" - << "VK_LAYER_LUNARG_swapchain" - << "VK_LAYER_GOOGLE_unique_objects"); -#endif - - if (!m_vkInstance->create()) { - qWarning("Failed to create Vulkan instance"); - } - } - - Q_ASSERT(m_vkInstance); - if (m_vkInstance->isValid()) { - QRhiVulkanInitParams params; - params.inst = m_vkInstance; - m_rhi = QRhi::create(QRhi::Vulkan, ¶ms, rhiFlags); - } + QRhiVulkanInitParams params; + params.inst = &Qt3DRender::staticVulkanInstance(); + m_rhi = QRhi::create(QRhi::Vulkan, ¶ms, rhiFlags); } #endif @@ -674,63 +649,38 @@ bool SubmissionContext::beginDrawing(QSurface *surface) } // Check if we have a swapchain for the Window, if not create one - SwapChainInfo &swapChainInfo = m_swapChains[surface]; - QRhiSwapChain *swapChain = swapChainInfo.swapChain; - - if (swapChain == nullptr) { - swapChain = m_rhi->newSwapChain(); - Q_ASSERT(surface->surfaceClass() == QSurface::Window); - QWindow *window = static_cast(surface); - Q_ASSERT(window != nullptr); - const int samples = format().samples(); - - swapChain->setWindow(window); - swapChain->setFlags(QRhiSwapChain::Flags {}); - swapChain->setSampleCount(samples); - - QRhiRenderBuffer *renderBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, - QSize(), - samples, - QRhiRenderBuffer::UsedWithSwapChainOnly); - swapChain->setDepthStencil(renderBuffer); - - QRhiRenderPassDescriptor *renderPassDescriptor = swapChain->newCompatibleRenderPassDescriptor(); - swapChain->setRenderPassDescriptor(renderPassDescriptor); - - // Build swapChain the first time - swapChain->buildOrResize(); - - swapChainInfo.swapChain = swapChain; - swapChainInfo.renderBuffer = renderBuffer; - swapChainInfo.renderPassDescriptor = renderPassDescriptor; - } + SwapChainInfo *swapChainInfo = swapChainForSurface(surface); + QRhiSwapChain *swapChain = swapChainInfo->swapChain; // TO DO: Check if that's required all the time { // Rebuild RenderPassDescriptor - swapChainInfo.renderBuffer->setPixelSize(surface->size()); - swapChainInfo.renderBuffer->build(); + // TODO -> this is not necessary, swapChain->buildOrResize already does it + // swapChainInfo->renderBuffer->setPixelSize(surface->size()); + // swapChainInfo->renderBuffer->build(); // Resize swapchain if needed if (m_surface->size() != swapChain->surfacePixelSize()) - swapChain->buildOrResize(); + { + bool couldRebuild = swapChain->buildOrResize(); + if (!couldRebuild) + return false; + } } m_currentSwapChain = swapChain; - m_currentRenderPassDescriptor = swapChainInfo.renderPassDescriptor; + m_currentRenderPassDescriptor = swapChainInfo->renderPassDescriptor; // Begin Frame - m_rhi->beginFrame(m_currentSwapChain); + const auto success = m_rhi->beginFrame(m_currentSwapChain); - // Update Resource Update Batch - m_currentUpdates = m_rhi->nextResourceUpdateBatch(); - - return true; + return success == QRhi::FrameOpSuccess; } void SubmissionContext::endDrawing(bool swapBuffers) { m_rhi->endFrame(m_currentSwapChain, {}); + m_currentUpdates = nullptr; RHI_UNIMPLEMENTED; //* if (swapBuffers) @@ -1031,12 +981,6 @@ void SubmissionContext::releaseResources() delete m_rhi; m_rhi = nullptr; -#if QT_CONFIG(vulkan) - if (m_ownsVkInstance) { - delete m_vkInstance; - m_vkInstance = nullptr; - } -#endif #ifndef QT_NO_OPENGL delete m_fallbackSurface; m_fallbackSurface = nullptr; @@ -1276,6 +1220,50 @@ StateVariant *SubmissionContext::getState(RenderStateSet *ss, StateMask type) co return nullptr; } + +SubmissionContext::SwapChainInfo *SubmissionContext::swapChainForSurface(QSurface *surface) noexcept +{ + SwapChainInfo &swapChainInfo = m_swapChains[surface]; + auto& swapChain = swapChainInfo.swapChain; + + if (swapChain == nullptr) { + swapChain = m_rhi->newSwapChain(); + Q_ASSERT(surface->surfaceClass() == QSurface::Window); + QWindow *window = static_cast(surface); + Q_ASSERT(window != nullptr); + const int samples = format().samples(); + + + swapChain->setWindow(window); + swapChain->setFlags(QRhiSwapChain::Flags {}); + swapChain->setSampleCount(samples); + + QRhiRenderBuffer *renderBuffer = m_rhi->newRenderBuffer(QRhiRenderBuffer::DepthStencil, + QSize(), + samples, + QRhiRenderBuffer::UsedWithSwapChainOnly); + swapChain->setDepthStencil(renderBuffer); + + QRhiRenderPassDescriptor *renderPassDescriptor = swapChain->newCompatibleRenderPassDescriptor(); + swapChain->setRenderPassDescriptor(renderPassDescriptor); + + // Build swapChain the first time + if (swapChain->buildOrResize()) + { + swapChainInfo.swapChain = swapChain; + swapChainInfo.renderBuffer = renderBuffer; + swapChainInfo.renderPassDescriptor = renderPassDescriptor; + } + else + { + swapChain->releaseAndDestroyLater(); + m_swapChains.remove(surface); + return nullptr; + } + } + return &swapChainInfo; +} + QRhiCommandBuffer *SubmissionContext::currentFrameCommandBuffer() const { return m_currentSwapChain->currentFrameCommandBuffer(); @@ -1286,6 +1274,11 @@ QRhiRenderTarget *SubmissionContext::currentFrameRenderTarget() const return m_currentSwapChain->currentFrameRenderTarget(); } +QRhiSwapChain *SubmissionContext::currentSwapChain() const +{ + return m_currentSwapChain; +} + QRhiRenderPassDescriptor *SubmissionContext::currentRenderPassDescriptor() const { return m_currentRenderPassDescriptor; @@ -1305,8 +1298,8 @@ QSurfaceFormat SubmissionContext::format() const noexcept // than the other way around bool SubmissionContext::setParameters(ShaderParameterPack ¶meterPack) { - static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight.irradiance")); - static const int specularId = StringToInt::lookupId(QLatin1String("envLight.specular")); + static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight_irradiance")); + static const int specularId = StringToInt::lookupId(QLatin1String("envLight_specular")); // Activate textures and update TextureUniform in the pack // with the correct textureUnit @@ -1600,6 +1593,101 @@ void SubmissionContext::blitFramebuffer(Qt3DCore::QNodeId inputRenderTargetId, //* } } +namespace +{ +template +constexpr int getFirstAvailableBit(const std::bitset& bits) +{ + for (std::size_t i = 0; i < N; i++) + { + if (!bits.test(i)) + return i; + } + return -1; +} +// This function ensures that the shader stages all have the same bindings +void preprocessRHIShader(QVector& shaderCodes) +{ + // Map the variable names to bindings + std::map bindings; + bindings["qt3d_render_view_uniforms"] = 0; + bindings["qt3d_command_uniforms"] = 1; + std::bitset<512> assignedBindings; + assignedBindings.set(0); + assignedBindings.set(1); + + thread_local const QRegularExpression samplerRegex( + QStringLiteral("binding\\s*=\\s*([0-9]+).*\\)\\s*uniform\\s*[ui]?sampler[a-zA-Z0-9]+\\s*([a-zA-Z0-9_]+)\\s*;")); + thread_local const QRegularExpression uboRegex( + QStringLiteral("(?:std140\\s*,\\s*binding\\s*=\\s*([0-9]+).*|binding\\s*=\\s*([0-9]+)\\s*,\\s*std140.*)\\)\\s*uniform\\s*([a-zA-Z0-9_]+)")); + + auto replaceBinding = [&bindings, &assignedBindings] (int& offset, QRegularExpressionMatch& match, QByteArray& code, int indexCapture, int variableCapture) noexcept + { + int index = match.captured(indexCapture).toInt(); + QByteArray variable = match.captured(variableCapture).toUtf8(); + + auto it = bindings.find(variable); + if (it == bindings.end()) + { + // 1. Check if the index is already used + if (assignedBindings.test(index)) + { + index = getFirstAvailableBit(assignedBindings); + if (index == -1) { + return; + } + + const int indexStartOffset = match.capturedStart(indexCapture); + const int indexEndOffset = match.capturedEnd(indexCapture); + const int indexLength = indexEndOffset - indexStartOffset; + code.replace(indexStartOffset, indexLength, QByteArray::number(index)); + } + + assignedBindings.set(index); + bindings.emplace(std::move(variable), index); + } + else + { + int indexToUse = it->second; + const int indexStartOffset = match.capturedStart(indexCapture); + const int indexEndOffset = match.capturedEnd(indexCapture); + const int indexLength = indexEndOffset - indexStartOffset; + code.replace(indexStartOffset, indexLength, QByteArray::number(indexToUse)); + } + // This may fail in the case where the replaced offset is an incredibly long number, + // which seems quite unlikely + offset = match.capturedEnd(0); + }; + + for (QByteArray& shaderCode : shaderCodes) + { + // Regex for the sampler variables + int offset = 0; + auto match = samplerRegex.match(shaderCode, offset); + while (match.hasMatch()) + { + const int indexCapture = 1; + const int variableCapture = 2; + replaceBinding(offset, match, shaderCode, indexCapture, variableCapture); + + match = samplerRegex.match(shaderCode, offset); + } + + // Regex for the UBOs + offset = 0; + match = uboRegex.match(shaderCode, offset); + while (match.hasMatch()) + { + const int indexCapture = !match.capturedView(1).isEmpty() ? 1 : 2; + const int variableCapture = 3; + replaceBinding(offset, match, shaderCode, indexCapture, variableCapture); + + match = uboRegex.match(shaderCode, offset); + } + } +} +} + // Called by GL Command Thread SubmissionContext::ShaderCreationInfo SubmissionContext::createShaderProgram(RHIShader *shader) { @@ -1665,7 +1753,8 @@ SubmissionContext::ShaderCreationInfo SubmissionContext::createShaderProgram(RHI } // Perform shader introspection - shader->introspect(); + if (success) + shader->introspect(); return {success, logs}; } @@ -1690,7 +1779,10 @@ void SubmissionContext::loadShader(Shader *shaderNode, const QVector sharedShaderIds = rhiShaderManager->shaderIdsForProgram(rhiShader); if (sharedShaderIds.size() == 1) { // Shader in the cache hasn't been loaded yet - rhiShader->setShaderCode(shaderNode->shaderCode()); + QVector shaderCodes = shaderNode->shaderCode(); + preprocessRHIShader(shaderCodes); + rhiShader->setShaderCode(shaderCodes); + const ShaderCreationInfo loadResult = createShaderProgram(rhiShader); shaderNode->setStatus(loadResult.linkSucceeded ? QShaderProgram::Ready : QShaderProgram::Error); shaderNode->setLog(loadResult.logs); diff --git a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h index dba99e505..6a1091e51 100644 --- a/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h +++ b/src/plugins/renderers/rhi/graphicshelpers/submissioncontext_p.h @@ -191,6 +191,17 @@ public: StateVariant* getState(RenderStateSet *ss, StateMask type) const; + // Swap chain + + + struct SwapChainInfo + { + QRhiSwapChain *swapChain = nullptr; + QRhiRenderBuffer *renderBuffer = nullptr; + QRhiRenderPassDescriptor *renderPassDescriptor = nullptr; + }; + SwapChainInfo* swapChainForSurface(QSurface* surface) noexcept; + QRhiResourceUpdateBatch *m_currentUpdates{}; @@ -198,10 +209,10 @@ public: QRhiCommandBuffer *currentFrameCommandBuffer() const; QRhiRenderTarget *currentFrameRenderTarget() const; QRhiRenderPassDescriptor *currentRenderPassDescriptor() const; + QRhiSwapChain *currentSwapChain() const; QSurfaceFormat format() const noexcept; private: - // Material Material* activeMaterial() const { return m_material; } void setActiveMaterial(Material* rmat); @@ -223,6 +234,7 @@ private: void applyState(const StateVariant &state, QRhiGraphicsPipeline *graphicsPipeline); + bool m_ownCurrent; const unsigned int m_id; QSurface *m_surface; @@ -249,22 +261,10 @@ private: GraphicsApiFilterData m_contextInfo; QRhi* m_rhi; - - struct SwapChainInfo - { - QRhiSwapChain *swapChain = nullptr; - QRhiRenderBuffer *renderBuffer = nullptr; - QRhiRenderPassDescriptor *renderPassDescriptor = nullptr; - }; - QHash m_swapChains; QRhiSwapChain *m_currentSwapChain; QRhiRenderPassDescriptor *m_currentRenderPassDescriptor; -#if QT_CONFIG(vulkan) - QVulkanInstance *m_vkInstance; - bool m_ownsVkInstance{}; -#endif #ifndef QT_NO_OPENGL QOffscreenSurface *m_fallbackSurface; #endif diff --git a/src/plugins/renderers/rhi/renderer/rendercommand.cpp b/src/plugins/renderers/rhi/renderer/rendercommand.cpp index 5d311480a..ee1b76cef 100644 --- a/src/plugins/renderers/rhi/renderer/rendercommand.cpp +++ b/src/plugins/renderers/rhi/renderer/rendercommand.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "rendercommand_p.h" +#include "renderer/rhigraphicspipeline_p.h" QT_BEGIN_NAMESPACE @@ -78,6 +79,11 @@ RenderCommand::RenderCommand() m_workGroups[2] = 0; } +bool RenderCommand::isValid() const noexcept +{ + return m_rhiShader && pipeline && pipeline->pipeline(); +} + bool operator==(const RenderCommand &a, const RenderCommand &b) noexcept { return (a.m_rhiShader == b.m_rhiShader && a.m_material == b.m_material && diff --git a/src/plugins/renderers/rhi/renderer/rendercommand_p.h b/src/plugins/renderers/rhi/renderer/rendercommand_p.h index f4031b711..0a3d03e93 100644 --- a/src/plugins/renderers/rhi/renderer/rendercommand_p.h +++ b/src/plugins/renderers/rhi/renderer/rendercommand_p.h @@ -97,6 +97,8 @@ class Q_AUTOTEST_EXPORT RenderCommand public: RenderCommand(); + bool isValid() const noexcept; + HMaterial m_material; // Purely used to ease sorting (minimize stage changes, binding changes ....) RHIShader *m_rhiShader; // GL Shader to be used at render time Qt3DCore::QNodeId m_shaderId; // Shader for given pass and mesh diff --git a/src/plugins/renderers/rhi/renderer/renderer.cpp b/src/plugins/renderers/rhi/renderer/renderer.cpp index 03d72e010..34ae18b5d 100644 --- a/src/plugins/renderers/rhi/renderer/renderer.cpp +++ b/src/plugins/renderers/rhi/renderer/renderer.cpp @@ -123,7 +123,6 @@ #include #include -#include QT_BEGIN_NAMESPACE @@ -679,15 +678,6 @@ void Renderer::render() } } -QShader getShader(const QString &name) -{ - QFile f(name); - if (f.open(QIODevice::ReadOnly)) - return QShader::fromSerialized(f.readAll()); - - return QShader(); -} - // Either called by render if Qt3D is in charge of the RenderThread // or by QRenderAspectPrivate::renderSynchronous (for Scene3D) void Renderer::doRender(bool swapBuffers) @@ -728,25 +718,47 @@ void Renderer::doRender(bool swapBuffers) QVector rhiPassesInfo; if (canRender()) { - { // Scoped to destroy surfaceLock - QSurface *surface = nullptr; - for (const RenderView *rv: renderViews) { - surface = rv->surface(); - if (surface) - break; - } + QSurface *surface = nullptr; + for (const RenderView *rv: renderViews) { + surface = rv->surface(); + if (surface) + break; + } + + // In case we did not draw because e.g. there wase no swapchain, + // we keep the resource updates from the previous frame. + if (!m_submissionContext->m_currentUpdates) + { + m_submissionContext->m_currentUpdates = m_submissionContext->rhi()->nextResourceUpdateBatch(); + } + + // 1) Execute commands for buffer uploads, texture updates, shader loading first + updateResources(); + + rhiPassesInfo = prepareCommandsSubmission(renderViews); + // 2) Update Pipelines and copy data into commands to allow concurrent submission + preprocessingComplete = true; + + bool hasCommands = false; + for (const RenderView *rv: renderViews) { + const auto& commands = rv->commands(); + hasCommands = std::any_of( + commands.begin(), + commands.end(), + [] (const RenderCommand& cmd) { + return cmd.isValid(); + }); + if (hasCommands) + break; + } + if (hasCommands) { + // Scoped to destroy surfaceLock SurfaceLocker surfaceLock(surface); const bool surfaceIsValid = (surface && surfaceLock.isSurfaceValid()); if (surfaceIsValid) { beganDrawing = m_submissionContext->beginDrawing(surface); if (beganDrawing) { - // 1) Execute commands for buffer uploads, texture updates, shader loading first - updateResources(); - // 2) Update Pipelines and copy data into commands to allow concurrent submission - rhiPassesInfo = prepareCommandsSubmission(renderViews); - preprocessingComplete = true; - // Purge shader which aren't used any longer static int callCount = 0; ++callCount; @@ -764,7 +776,7 @@ void Renderer::doRender(bool swapBuffers) // Only try to submit the RenderViews if the preprocessing was successful // This part of the submission is happening in parallel to the RV building for the next frame - if (preprocessingComplete) { + if (beganDrawing) { submissionStatsPart1.end(submissionStatsPart2.restart()); // 3) Submit the render commands for frame n (making sure we never reference something that could be changing) @@ -900,7 +912,10 @@ QSurfaceFormat Renderer::format() void Renderer::updateGraphicsPipeline(RenderCommand& cmd, RenderView *rv, int renderViewIndex) { if (!cmd.m_rhiShader) + { + qDebug() << "Warning: command has no shader"; return; + } // The Graphics Pipeline defines // - Render State (Depth, Culling, Stencil, Blending) @@ -934,7 +949,6 @@ void Renderer::updateGraphicsPipeline(RenderCommand& cmd, RenderView *rv, int re if (!graphicsPipeline) { qDebug() << "Warning : could not create a graphics pipeline"; - RHIGraphicsPipeline *graphicsPipeline = m_RHIResourceManagers->rhiGraphicsPipelineManager()->getOrCreateResource(pipelineKey); return; } @@ -951,6 +965,11 @@ void Renderer::updateGraphicsPipeline(RenderCommand& cmd, RenderView *rv, int re if (graphicsPipeline->pipeline() == nullptr || requiredRebuild) { bool ok = true; + const SubmissionContext::SwapChainInfo* swapchain = + m_submissionContext->swapChainForSurface(rv->surface()); + if (!swapchain || !swapchain->swapChain || !swapchain->renderPassDescriptor) + return; + // TO DO: Find a way to recycle those // Create Per Command UBO QRhiBuffer *commandUBO = m_submissionContext->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, sizeof(CommandUBO)); @@ -1124,11 +1143,12 @@ void Renderer::updateGraphicsPipeline(RenderCommand& cmd, RenderView *rv, int re inputBindings.resize(uniqueBindings.size()); for (int i = 0, m = uniqueBindings.size(); i < m; ++i) { const BufferBinding binding = uniqueBindings.at(i); + /* qDebug() << binding.bufferId << binding.stride << binding.classification << binding.attributeDivisor; - + */ inputBindings[i] = QRhiVertexInputBinding{binding.stride, binding.classification, int(binding.attributeDivisor) }; } @@ -1138,14 +1158,15 @@ void Renderer::updateGraphicsPipeline(RenderCommand& cmd, RenderView *rv, int re pipeline->setVertexInputLayout(inputLayout); pipeline->setShaderResourceBindings(shaderResourceBindings); - pipeline->setRenderPassDescriptor(m_submissionContext->currentRenderPassDescriptor()); - graphicsPipeline->setAttributesToBindingHash(attributeNameToBinding); + pipeline->setRenderPassDescriptor(swapchain->renderPassDescriptor); + graphicsPipeline->setAttributesToBindingHash(attributeNameToBinding); // Render States m_submissionContext->applyStateSet(renderState, pipeline); + ok = pipeline->build(); assert(ok); } @@ -1201,7 +1222,10 @@ QVector Renderer::prepareCommandsSubmission(const QVector // By this time shaders should have been loaded RHIShader *shader = m_RHIResourceManagers->rhiShaderManager()->lookupResource(command.m_shaderId); if (!shader) + { + qDebug() << "Warning: could not find shader"; continue; + } // We should never have inserted a command for which these are null // in the first place @@ -1497,7 +1521,6 @@ void Renderer::updateResources() { const QVector activeTextureHandles = std::move(m_dirtyTextures); for (const HTexture &handle: activeTextureHandles) { - RHI_UNIMPLEMENTED; Texture *texture = m_nodesManager->textureManager()->data(handle); // Can be null when using Scene3D rendering diff --git a/src/plugins/renderers/rhi/renderer/renderview.cpp b/src/plugins/renderers/rhi/renderer/renderview.cpp index 49a7c2d9e..70020093a 100644 --- a/src/plugins/renderers/rhi/renderer/renderview.cpp +++ b/src/plugins/renderers/rhi/renderer/renderview.cpp @@ -1157,19 +1157,45 @@ void RenderView::setShaderAndUniforms(RenderCommand *command, // Environment Light int envLightCount = 0; if (environmentLight && environmentLight->isEnabled()) { + static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight_irradiance")); + static const int specularId = StringToInt::lookupId(QLatin1String("envLight_specular")); ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); if (shaderData) { - setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); + //setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); envLightCount = 1; + + // ("specularSize", "irradiance", "irradianceSize", "specular") + auto irr = shaderData->properties()["irradiance"].value.value(); + auto spec = shaderData->properties()["specular"].value.value(); + + setUniformValue(command->m_parameterPack, irradianceId, irr); + setUniformValue(command->m_parameterPack, specularId, spec); } - } else { - // with some drivers, samplers (like the envbox sampler) need to be bound even though - // they may not be actually used, otherwise draw calls can fail - static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight_irradiance")); - static const int specularId = StringToInt::lookupId(QLatin1String("envLight_specular")); -// setUniformValue(command->m_parameterPack, irradianceId, m_renderer->submissionContext()->maxTextureUnitsCount()); -// setUniformValue(command->m_parameterPack, specularId, m_renderer->submissionContext()->maxTextureUnitsCount()); } + //if (environmentLight && environmentLight->isEnabled()) { + // ShaderData *shaderData = m_manager->shaderDataManager()->lookupResource(environmentLight->shaderData()); + // if (shaderData) { + // setDefaultUniformBlockShaderDataValue(command->m_parameterPack, shader, shaderData, QStringLiteral("envLight")); + // envLightCount = 1; + // } + //} else { + // // with some drivers, samplers (like the envbox sampler) need to be bound even though + // // they may not be actually used, otherwise draw calls can fail + // static const int irradianceId = StringToInt::lookupId(QLatin1String("envLight_irradiance")); + // static const int specularId = StringToInt::lookupId(QLatin1String("envLight_specular")); + // + // // for (const auto& sampler : shader->samplers()) + // // { + // // if (sampler.m_nameId == irradianceId) + // // { + // // setUniformValue(command->m_parameterPack, irradianceId, sampler.m_location); + // // } + // // else if (sampler.m_nameId == specularId) + // // { + // // setUniformValue(command->m_parameterPack, specularId, sampler.m_location); + // // } + // // } + //} setUniformValue(command->m_parameterPack, StringToInt::lookupId(QStringLiteral("envLightCount")), envLightCount); } } diff --git a/src/plugins/renderers/rhi/renderer/renderview_p.h b/src/plugins/renderers/rhi/renderer/renderview_p.h index c770eaaec..cceefe5e4 100644 --- a/src/plugins/renderers/rhi/renderer/renderview_p.h +++ b/src/plugins/renderers/rhi/renderer/renderview_p.h @@ -133,13 +133,13 @@ struct RenderViewUBO float viewportMatrix[16]; float inverseViewportMatrix[16]; float textureTransformMatrix[4]; - float eyePosition[3]; float pad1; - float aspectRatio; float pad2[3]; - float gamma; float pad3[3]; - float exposure; float pad4[3]; - float time; float pad5[3]; + float eyePosition[3]; + float aspectRatio; + float gamma; + float exposure; + float time; }; -static_assert(sizeof(RenderViewUBO) == sizeof(float) * (8 * 16 + 6 * 4), "UBO doesn't match std140"); +static_assert(sizeof(RenderViewUBO) == sizeof(float) * (8 * 16 + 1 * 4 + 1 * 3 + 4 * 1), "UBO doesn't match std140"); class Q_AUTOTEST_EXPORT RenderView { diff --git a/src/plugins/renderers/rhi/textures/texture.cpp b/src/plugins/renderers/rhi/textures/texture.cpp index 47a2d8025..c1a1bc19b 100644 --- a/src/plugins/renderers/rhi/textures/texture.cpp +++ b/src/plugins/renderers/rhi/textures/texture.cpp @@ -113,6 +113,7 @@ QRhiTexture::Format rhiFormatFromTextureFormat(QAbstractTexture::TextureFormat f case QAbstractTexture::RGBA8_ETC2_EAC: return QRhiTexture::ETC2_RGBA8; default: + Q_UNREACHABLE(); return QRhiTexture::UnknownFormat; } } @@ -145,6 +146,7 @@ QRhiSampler::Filter rhiMipMapFilterFromTextureFilter(QAbstractTexture::Filter fi case QAbstractTexture::LinearMipMapLinear: return QRhiSampler::Linear; default: + Q_UNREACHABLE(); return QRhiSampler::None; } } @@ -165,6 +167,8 @@ rhiWrapModeFromTextureWrapMode(QTextureWrapMode::WrapMode x, return QRhiSampler::ClampToEdge; case Qt3DRender::QTextureWrapMode::MirroredRepeat: return QRhiSampler::Mirror; + default: + Q_UNREACHABLE(); } }; @@ -197,19 +201,23 @@ QRhiSampler::CompareOp rhiCompareOpFromTextureCompareOp(QAbstractTexture::Compar // This uploadGLData where the data is a fullsize subimage // as QOpenGLTexture doesn't allow partial subimage uploads -QRhiTextureUploadEntry uploadRhiData(int level, int layer, QOpenGLTexture::CubeMapFace face, +QRhiTextureUploadEntry uploadRhiData(int level, int layer, const QByteArray &bytes, const QTextureImageDataPtr &data) noexcept { + qDebug() << "Case 1: " << level << layer ; QRhiTextureSubresourceUploadDescription description; description.setData(bytes); + description.setSourceSize({data->width(), data->height()}); + return QRhiTextureUploadEntry(layer, level, description); } // For partial sub image uploads -QRhiTextureUploadEntry uploadRhiData(int mipLevel, int layer, QOpenGLTexture::CubeMapFace cubeFace, +QRhiTextureUploadEntry uploadRhiData(int mipLevel, int layer, int xOffset, int yOffset, int zOffset, const QByteArray &bytes, const QTextureImageDataPtr &data) noexcept { + qDebug() << "Case 2: " << mipLevel << layer << xOffset << yOffset << zOffset; QRhiTextureSubresourceUploadDescription description; description.setData(bytes); description.setSourceTopLeft(QPoint(xOffset, yOffset)); @@ -580,14 +588,6 @@ QRhiTexture *RHITexture::buildRhiTexture(SubmissionContext *ctx) if (issRGB8Format) rhiFlags |= QRhiTexture::sRGB; -// // Set layers count if texture array -// if (actualTarget == QAbstractTexture::Target1DArray || -// actualTarget == QAbstractTexture::Target2DArray || -// actualTarget == QAbstractTexture::Target2DMultisampleArray || -// actualTarget == QAbstractTexture::TargetCubeMapArray) { -// glTex->setLayers(m_properties.layers); -// } - if (actualTarget == QAbstractTexture::Target2DMultisample || actualTarget == QAbstractTexture::Target2DMultisampleArray) { // Set samples count if multisampled texture @@ -616,24 +616,75 @@ void RHITexture::uploadRhiTextureData(SubmissionContext *ctx) if (m_textureData) { const QVector imgData = m_textureData->imageData(); - for (const QTextureImageDataPtr &data : imgData) { - const int mipLevels = m_properties.generateMipMaps ? 1 : data->mipLevels(); + if (m_properties.samples == 1) + { + for (const QTextureImageDataPtr &data : imgData) { + const int mipLevels = m_properties.generateMipMaps ? 1 : data->mipLevels(); + Q_ASSERT(mipLevels <= ctx->rhi()->mipLevelsForSize({data->width(), data->height()})); - for (int layer = 0; layer < data->layers(); layer++) { - for (int face = 0; face < data->faces(); face++) { + const int maxLevels = (data->layers() == 1 && data->faces() == 1) ? 1 + : (data->layers() > 1 && data->faces() == 1) ? data->layers() + : (data->faces() > 1 && data->layers() == 1) ? data->faces() + : 0; + + if (data->layers() == 1 && data->faces() == 1) + { for (int level = 0; level < mipLevels; level++) { // ensure we don't accidentally cause a detach / copy of the raw bytes - const QByteArray bytes(data->data(layer, face, level)); - const QRhiTextureUploadEntry entry = uploadRhiData(level, layer, - static_cast(QOpenGLTexture::CubeMapPositiveX + face), - bytes, data); + const QByteArray bytes(data->data(0, 0, level)); + const QRhiTextureUploadEntry entry = uploadRhiData(level, 0, bytes, data); uploadEntries.push_back(entry); } } + else if (data->layers() > 1 && data->faces() == 1) + { + for (int layer = 0; layer < data->layers(); layer++) { + for (int level = 0; level < mipLevels; level++) { + // ensure we don't accidentally cause a detach / copy of the raw bytes + const QByteArray bytes(data->data(layer, 0, level)); + const QRhiTextureUploadEntry entry = uploadRhiData(level, layer, bytes, data); + uploadEntries.push_back(entry); + } + } + } + else if (data->faces() > 1 && data->layers() == 1) + { + for (int face = 0; face < data->faces(); face++) { + for (int level = 0; level < mipLevels; level++) { + // ensure we don't accidentally cause a detach / copy of the raw bytes + const QByteArray bytes(data->data(0, face, level)); + const QRhiTextureUploadEntry entry = uploadRhiData(level, face, bytes, data); + uploadEntries.push_back(entry); + } + } + } + else + { + qDebug() << "Unsupported case"; + } } } - } + else + { + for (const QTextureImageDataPtr &data : imgData) { + const int mipLevels = m_properties.generateMipMaps ? 1 : data->mipLevels(); + + for (int layer = 0; layer < data->layers(); layer++) { + for (int face = 0; face < data->faces(); face++) { + for (int level = 0; level < mipLevels; level++) { + // ensure we don't accidentally cause a detach / copy of the raw bytes + const QByteArray bytes(data->data(layer, face, level)); + const QRhiTextureUploadEntry entry = uploadRhiData(level, layer, + bytes, data); + uploadEntries.push_back(entry); + } + } + } + } + } + } +/* // Upload all QTexImageData references by the TextureImages for (int i = 0; i < std::min(m_images.size(), m_imageData.size()); i++) { const QTextureImageDataPtr &imgData = m_imageData.at(i); @@ -686,7 +737,7 @@ void RHITexture::uploadRhiTextureData(SubmissionContext *ctx) bytes, imgData); uploadEntries.push_back(entry); } - +*/ QRhiTextureUploadDescription uploadDescription; uploadDescription.setEntries(uploadEntries.begin(), uploadEntries.end()); diff --git a/src/render/render.pro b/src/render/render.pro index 528546230..ef5b154ff 100644 --- a/src/render/render.pro +++ b/src/render/render.pro @@ -19,6 +19,7 @@ include (raycasting/raycasting.pri) include (services/services.pri) include (shadergraph/shadergraph.pri) include (texture/texture.pri) +include (surfaces/surfaces.pri) gcov { QMAKE_CXXFLAGS += -fprofile-arcs -ftest-coverage diff --git a/src/render/shadergraph/qshadergenerator.cpp b/src/render/shadergraph/qshadergenerator.cpp index 15178fc71..92c69be72 100644 --- a/src/render/shadergraph/qshadergenerator.cpp +++ b/src/render/shadergraph/qshadergenerator.cpp @@ -473,7 +473,7 @@ namespace int currentInputLocation { 0 }; int currentOutputLocation { 0 }; - int currentBinding { 0 }; + int currentBinding { 2 }; QByteArrayList ubo; }; diff --git a/src/render/surfaces/surfaces.pri b/src/render/surfaces/surfaces.pri new file mode 100644 index 000000000..0d25a36c6 --- /dev/null +++ b/src/render/surfaces/surfaces.pri @@ -0,0 +1,9 @@ +INCLUDEPATH += $$PWD + +qtConfig(vulkan) { +HEADERS += \ + $$PWD/vulkaninstance_p.h \ + +SOURCES += \ + $$PWD/vulkaninstance.cpp +} diff --git a/src/render/surfaces/vulkaninstance.cpp b/src/render/surfaces/vulkaninstance.cpp new file mode 100644 index 000000000..adbeb3074 --- /dev/null +++ b/src/render/surfaces/vulkaninstance.cpp @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "vulkaninstance_p.h" +#include + +QT_BEGIN_NAMESPACE + +namespace Qt3DRender { + +QVulkanInstance &staticVulkanInstance() noexcept +{ + static QVulkanInstance* vkInstance = [] + { + QVulkanInstance* v = new QVulkanInstance; +#ifndef Q_OS_ANDROID + v->setLayers({ "VK_LAYER_LUNARG_standard_validation" }); +#else + v->setLayers(QByteArrayList() + << "VK_LAYER_GOOGLE_threading" + << "VK_LAYER_LUNARG_parameter_validation" + << "VK_LAYER_LUNARG_object_tracker" + << "VK_LAYER_LUNARG_core_validation" + << "VK_LAYER_LUNARG_image" + << "VK_LAYER_LUNARG_swapchain" + << "VK_LAYER_GOOGLE_unique_objects"); +#endif + + if (!v->create()) { + qWarning("Failed to create Vulkan instance"); + } + return v; + }(); + return *vkInstance; +} + +} // Qt3DRender + +QT_END_NAMESPACE diff --git a/src/render/surfaces/vulkaninstance_p.h b/src/render/surfaces/vulkaninstance_p.h new file mode 100644 index 000000000..ca6e2124f --- /dev/null +++ b/src/render/surfaces/vulkaninstance_p.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2020 Klaralvdalens Datakonsult AB (KDAB). +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT3DRENDER_VULKANINSTANCE_P_H +#define QT3DRENDER_VULKANINSTANCE_P_H + +#include +QT_BEGIN_NAMESPACE +#if QT_CONFIG(vulkan) +class QVulkanInstance; +namespace Qt3DRender { +Q_3DRENDERSHARED_PRIVATE_EXPORT +QVulkanInstance& staticVulkanInstance() noexcept; +} // Qt3DRender +#endif +QT_END_NAMESPACE + +#endif // QT3DRENDER_VULKANINSTANCE_P_H -- cgit v1.2.3