/**************************************************************************** ** ** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** 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. ** ** BSD License Usage ** Alternatively, you may use this file under the terms of the BSD license ** as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of The Qt Company Ltd nor the names of its ** contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // An example exercising more than a single feature. Enables profiling // (resource logging to a file) and inserts debug markers and sets some // object names. Can also be used to test MSAA swapchains, swapchain image // readback, requesting an sRGB swapchain, and some texture features. #define EXAMPLEFW_PREINIT #include "../shared/examplefw.h" #include "trianglerenderer.h" #include "quadrenderer.h" #include "texturedcuberenderer.h" #include "triangleoncuberenderer.h" #include #include #include #define PROFILE_TO_FILE //#define SKIP_PRESENT //#define USE_MSAA //#define USE_SRGB_SWAPCHAIN //#define READBACK_SWAPCHAIN //#define NO_VSYNC //#define USE_MIN_SWAPCHAIN_BUFFERS //#define DECLARE_EXT_CONTENTS struct { TriangleRenderer triRenderer; QuadRenderer quadRenderer; TexturedCubeRenderer cubeRenderer; TriangleOnCubeRenderer liveTexCubeRenderer; bool onScreenOnly = false; bool triangleOnly = false; QSize lastOutputSize; int frameCount = 0; QFile profOut; } d; void preInit() { #ifdef PROFILE_TO_FILE rhiFlags |= QRhi::EnableProfiling; const QString profFn = QFileInfo(QLatin1String("rhiprof.txt")).absoluteFilePath(); qDebug("Writing profiling output to %s", qPrintable(profFn)); d.profOut.setFileName(profFn); d.profOut.open(QIODevice::WriteOnly); #endif #ifdef SKIP_PRESENT endFrameFlags |= QRhi::SkipPresent; #endif #ifdef USE_MSAA sampleCount = 4; // enable 4x MSAA (except for the render-to-texture pass) #endif #ifdef READBACK_SWAPCHAIN scFlags |= QRhiSwapChain::UsedAsTransferSource; #endif #ifdef USE_SRGB_SWAPCHAIN scFlags |= QRhiSwapChain::sRGB; #endif #ifdef NO_VSYNC scFlags |= QRhiSwapChain::NoVSync; #endif #ifdef USE_MIN_SWAPCHAIN_BUFFERS scFlags |= QRhiSwapChain::MinimalBufferCount; #endif #ifdef DECLARE_EXT_CONTENTS beginFrameFlags |= QRhi::ExternalContentsInPass; #endif // For OpenGL some of these are incorporated into the QSurfaceFormat by // examplefw.h after returning from here as that is out of the RHI's control. } void Window::customInit() { #ifdef PROFILE_TO_FILE m_r->profiler()->setDevice(&d.profOut); #endif d.triRenderer.setRhi(m_r); d.triRenderer.setSampleCount(sampleCount); d.triRenderer.initResources(m_rp); if (!d.triangleOnly) { d.triRenderer.setTranslation(QVector3D(0, 0.5f, 0)); d.quadRenderer.setRhi(m_r); d.quadRenderer.setSampleCount(sampleCount); d.quadRenderer.setPipeline(d.triRenderer.pipeline()); d.quadRenderer.initResources(m_rp); d.quadRenderer.setTranslation(QVector3D(1.5f, -0.5f, 0)); d.cubeRenderer.setRhi(m_r); d.cubeRenderer.setSampleCount(sampleCount); d.cubeRenderer.initResources(m_rp); d.cubeRenderer.setTranslation(QVector3D(0, -0.5f, 0)); } if (!d.onScreenOnly) { d.liveTexCubeRenderer.setRhi(m_r); d.liveTexCubeRenderer.setSampleCount(sampleCount); d.liveTexCubeRenderer.initResources(m_rp); d.liveTexCubeRenderer.setTranslation(QVector3D(-2.0f, 0, 0)); } // Put the gpu mem allocator statistics to the profiling stream after doing // all the init. (where applicable) m_r->profiler()->addVMemAllocatorStats(); // Check some features/limits. qDebug("isFeatureSupported(MultisampleTexture): %d", m_r->isFeatureSupported(QRhi::MultisampleTexture)); qDebug("isFeatureSupported(MultisampleRenderBuffer): %d", m_r->isFeatureSupported(QRhi::MultisampleRenderBuffer)); qDebug("isFeatureSupported(DebugMarkers): %d", m_r->isFeatureSupported(QRhi::DebugMarkers)); qDebug("isFeatureSupported(Timestamps): %d", m_r->isFeatureSupported(QRhi::Timestamps)); qDebug("isFeatureSupported(Instancing): %d", m_r->isFeatureSupported(QRhi::Instancing)); qDebug("isFeatureSupported(CustomInstanceStepRate): %d", m_r->isFeatureSupported(QRhi::CustomInstanceStepRate)); qDebug("isFeatureSupported(PrimitiveRestart): %d", m_r->isFeatureSupported(QRhi::PrimitiveRestart)); qDebug("isFeatureSupported(NonDynamicUniformBuffers): %d", m_r->isFeatureSupported(QRhi::NonDynamicUniformBuffers)); qDebug("isFeatureSupported(NonFourAlignedEffectiveIndexBufferOffset): %d", m_r->isFeatureSupported(QRhi::NonFourAlignedEffectiveIndexBufferOffset)); qDebug("isFeatureSupported(NPOTTextureRepeat): %d", m_r->isFeatureSupported(QRhi::NPOTTextureRepeat)); qDebug("isFeatureSupported(RedOrAlpha8IsRed): %d", m_r->isFeatureSupported(QRhi::RedOrAlpha8IsRed)); qDebug("isFeatureSupported(ElementIndexUint): %d", m_r->isFeatureSupported(QRhi::ElementIndexUint)); qDebug("isFeatureSupported(Compute): %d", m_r->isFeatureSupported(QRhi::Compute)); qDebug("isFeatureSupported(WideLines): %d", m_r->isFeatureSupported(QRhi::WideLines)); qDebug("isFeatureSupported(VertexShaderPointSize): %d", m_r->isFeatureSupported(QRhi::VertexShaderPointSize)); qDebug("isFeatureSupported(BaseVertex): %d", m_r->isFeatureSupported(QRhi::BaseVertex)); qDebug("isFeatureSupported(BaseInstance): %d", m_r->isFeatureSupported(QRhi::BaseInstance)); qDebug("Min 2D texture width/height: %d", m_r->resourceLimit(QRhi::TextureSizeMin)); qDebug("Max 2D texture width/height: %d", m_r->resourceLimit(QRhi::TextureSizeMax)); qDebug("Max color attachment count: %d", m_r->resourceLimit(QRhi::MaxColorAttachments)); } void Window::customRelease() { d.triRenderer.releaseResources(); if (!d.triangleOnly) { d.quadRenderer.releaseResources(); d.cubeRenderer.releaseResources(); } if (!d.onScreenOnly) d.liveTexCubeRenderer.releaseResources(); } void Window::customRender() { const QSize outputSize = m_sc->currentPixelSize(); QRhiCommandBuffer *cb = m_sc->currentFrameCommandBuffer(); if (outputSize != d.lastOutputSize) { d.triRenderer.resize(outputSize); if (!d.triangleOnly) { d.quadRenderer.resize(outputSize); d.cubeRenderer.resize(outputSize); } if (!d.onScreenOnly) d.liveTexCubeRenderer.resize(outputSize); d.lastOutputSize = outputSize; } if (!d.onScreenOnly) { cb->debugMarkBegin("Offscreen triangle pass"); d.liveTexCubeRenderer.queueOffscreenPass(cb); cb->debugMarkEnd(); } QRhiResourceUpdateBatch *u = m_r->nextResourceUpdateBatch(); d.triRenderer.queueResourceUpdates(u); if (!d.triangleOnly) { d.quadRenderer.queueResourceUpdates(u); d.cubeRenderer.queueResourceUpdates(u); } if (!d.onScreenOnly) d.liveTexCubeRenderer.queueResourceUpdates(u); cb->beginPass(m_sc->currentFrameRenderTarget(), m_clearColor, { 1.0f, 0 }, u); cb->debugMarkBegin(QByteArrayLiteral("Triangle")); d.triRenderer.queueDraw(cb, outputSize); cb->debugMarkEnd(); if (!d.triangleOnly) { cb->debugMarkMsg(QByteArrayLiteral("More stuff")); cb->debugMarkBegin(QByteArrayLiteral("Quad")); d.quadRenderer.queueDraw(cb, outputSize); cb->debugMarkEnd(); cb->debugMarkBegin(QByteArrayLiteral("Cube")); d.cubeRenderer.queueDraw(cb, outputSize); cb->debugMarkEnd(); } if (!d.onScreenOnly) { cb->debugMarkMsg(QByteArrayLiteral("Even more stuff")); cb->debugMarkBegin(QByteArrayLiteral("Cube with offscreen triangle")); d.liveTexCubeRenderer.queueDraw(cb, outputSize); cb->debugMarkEnd(); } QRhiResourceUpdateBatch *passEndUpdates = nullptr; #ifdef READBACK_SWAPCHAIN passEndUpdates = m_r->nextResourceUpdateBatch(); QRhiReadbackDescription rb; // no texture given -> backbuffer QRhiReadbackResult *rbResult = new QRhiReadbackResult; int frameNo = d.frameCount; rbResult->completed = [this, rbResult, frameNo] { { QImage::Format fmt = rbResult->format == QRhiTexture::BGRA8 ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGBA8888_Premultiplied; const uchar *p = reinterpret_cast(rbResult->data.constData()); QImage image(p, rbResult->pixelSize.width(), rbResult->pixelSize.height(), fmt); QString fn = QString::asprintf("frame%d.png", frameNo); fn = QFileInfo(fn).absoluteFilePath(); qDebug("Saving into %s", qPrintable(fn)); if (m_r->isYUpInFramebuffer()) image.mirrored().save(fn); else image.save(fn); } delete rbResult; }; passEndUpdates->readBackTexture(rb, rbResult); #endif cb->endPass(passEndUpdates); d.frameCount += 1; }