summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source/runtimerender/Qt3DSOffscreenRenderManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/Source/runtimerender/Qt3DSOffscreenRenderManager.cpp')
-rw-r--r--src/Runtime/Source/runtimerender/Qt3DSOffscreenRenderManager.cpp498
1 files changed, 498 insertions, 0 deletions
diff --git a/src/Runtime/Source/runtimerender/Qt3DSOffscreenRenderManager.cpp b/src/Runtime/Source/runtimerender/Qt3DSOffscreenRenderManager.cpp
new file mode 100644
index 00000000..7d66c7a3
--- /dev/null
+++ b/src/Runtime/Source/runtimerender/Qt3DSOffscreenRenderManager.cpp
@@ -0,0 +1,498 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2012 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) 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.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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSOffscreenRenderManager.h"
+#include "foundation/Qt3DSContainers.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "render/Qt3DSRenderBaseTypes.h"
+#include "foundation/StringTable.h"
+#include "render/Qt3DSRenderFrameBuffer.h"
+#include "render/Qt3DSRenderTexture2D.h"
+#include "Qt3DSRenderResourceManager.h"
+#include "render/Qt3DSRenderContext.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "Qt3DSTextRenderer.h"
+#include "Qt3DSRenderContextCore.h"
+#include "Qt3DSOffscreenRenderKey.h"
+#include "foundation/FastAllocator.h"
+#include "Qt3DSRenderRenderList.h"
+#include "Qt3DSRenderResourceTexture2D.h"
+#include "Qt3DSRenderResourceBufferObjects.h"
+#include "Qt3DSRendererUtil.h"
+
+using namespace qt3ds::render;
+
+namespace eastl {
+template <>
+struct hash<SOffscreenRendererKey>
+{
+ size_t operator()(const SOffscreenRendererKey &key) const
+ {
+ switch (key.getType()) {
+ case OffscreenRendererKeyTypes::RegisteredString:
+ return hash<CRegisteredString>()(key.getData<CRegisteredString>());
+ case OffscreenRendererKeyTypes::VoidPtr:
+ return hash<size_t>()(reinterpret_cast<size_t>(key.getData<void *>()));
+ default:
+ break;
+ }
+ QT3DS_ASSERT(false);
+ return 0;
+ }
+ bool operator()(const SOffscreenRendererKey &lhs, const SOffscreenRendererKey &rhs) const
+ {
+ return lhs == rhs;
+ }
+};
+}
+
+namespace {
+
+using eastl::pair;
+using eastl::make_pair;
+
+struct SRendererData : SOffscreenRenderResult
+{
+ NVAllocatorCallback &m_Allocator;
+ IResourceManager &m_ResourceManager;
+ QT3DSU32 m_FrameCount;
+ bool m_Rendering;
+
+ SRendererData(NVAllocatorCallback &inAllocator, IResourceManager &inResourceManager)
+ : m_Allocator(inAllocator)
+ , m_ResourceManager(inResourceManager)
+ , m_FrameCount(QT3DS_MAX_U32)
+ , m_Rendering(false)
+ {
+ }
+ ~SRendererData()
+ {
+ if (m_Texture)
+ m_ResourceManager.Release(*m_Texture);
+ m_Texture = NULL;
+ }
+ void Release() { NVDelete(m_Allocator, this); }
+};
+
+struct SScopedRenderDataRenderMarker
+{
+ SRendererData &m_Data;
+ SScopedRenderDataRenderMarker(SRendererData &d)
+ : m_Data(d)
+ {
+ QT3DS_ASSERT(m_Data.m_Rendering == false);
+ m_Data.m_Rendering = true;
+ }
+ ~SScopedRenderDataRenderMarker() { m_Data.m_Rendering = false; }
+};
+
+struct SRenderDataReleaser
+{
+ SRendererData *mPtr;
+ SRenderDataReleaser(SRendererData *inItem)
+ : mPtr(inItem)
+ {
+ }
+ // Transfer ownership
+ SRenderDataReleaser(const SRenderDataReleaser &inOther)
+ : mPtr(inOther.mPtr)
+ {
+ const_cast<SRenderDataReleaser &>(inOther).mPtr = NULL;
+ }
+
+ ~SRenderDataReleaser()
+ {
+ if (mPtr)
+ mPtr->Release();
+ }
+};
+struct SOffscreenRenderManager;
+
+struct SOffscreenRunnable : public IRenderTask
+{
+ SOffscreenRenderManager &m_RenderManager;
+ SRendererData &m_Data;
+ SOffscreenRendererEnvironment m_DesiredEnvironment;
+ SOffscreenRunnable(SOffscreenRenderManager &rm, SRendererData &data,
+ SOffscreenRendererEnvironment env)
+ : m_RenderManager(rm)
+ , m_Data(data)
+ , m_DesiredEnvironment(env)
+ {
+ }
+ void Run() override;
+};
+
+struct SOffscreenRenderManager : public IOffscreenRenderManager
+{
+ typedef nvhash_map<SOffscreenRendererKey, SRenderDataReleaser> TRendererMap;
+ IQt3DSRenderContext &m_Context;
+ NVAllocatorCallback &m_Allocator;
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ NVScopedRefCounted<IResourceManager> m_ResourceManager;
+ TRendererMap m_Renderers;
+ SFastAllocator<> m_PerFrameAllocator;
+ QT3DSU32 m_FrameCount; // cheap per-
+
+ volatile QT3DSI32 mRefCount;
+
+ SOffscreenRenderManager(NVAllocatorCallback &inCallback, IStringTable &inStringTable,
+ IResourceManager &inManager, IQt3DSRenderContext &inContext)
+ : m_Context(inContext)
+ , m_Allocator(inCallback)
+ , m_StringTable(inStringTable)
+ , m_ResourceManager(inManager)
+ , m_Renderers(inCallback, "SOffscreenRenderManager::m_Renderers")
+ , m_PerFrameAllocator(inCallback, "m_PerFrameAllocator")
+ , m_FrameCount(0)
+ , mRefCount(0)
+ {
+ }
+
+ virtual ~SOffscreenRenderManager() {}
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE_OVERRIDE(m_Allocator)
+
+ Option<bool> MaybeRegisterOffscreenRenderer(const SOffscreenRendererKey &inKey,
+ IOffscreenRenderer &inRenderer) override
+ {
+ TRendererMap::iterator theIter = m_Renderers.find(inKey);
+ if (theIter != m_Renderers.end()) {
+ SRendererData &theData = *(theIter->second.mPtr);
+ if (theData.m_Renderer != &inRenderer) {
+ if (inKey.getType() == OffscreenRendererKeyTypes::RegisteredString) {
+ qCCritical(INVALID_OPERATION, "Different renderers registered under same key: %s",
+ inKey.getData<CRegisteredString>().c_str());
+ }
+ QT3DS_ASSERT(false);
+ return Empty();
+ }
+ return false;
+ }
+ RegisterOffscreenRenderer(inKey, inRenderer);
+ return true;
+ }
+
+ void RegisterOffscreenRenderer(const SOffscreenRendererKey &inKey,
+ IOffscreenRenderer &inRenderer) override
+ {
+ pair<TRendererMap::iterator, bool> theInsert = m_Renderers.insert(
+ make_pair(inKey, QT3DS_NEW(m_Allocator, SRendererData)(m_Allocator, *m_ResourceManager)));
+ QT3DS_ASSERT(theInsert.second);
+ SRendererData &theData = *(theInsert.first->second.mPtr);
+ theData.m_Renderer = &inRenderer;
+ }
+
+ bool HasOffscreenRenderer(const SOffscreenRendererKey &inKey) override
+ {
+ return m_Renderers.find(inKey) != m_Renderers.end();
+ }
+
+ IOffscreenRenderer *GetOffscreenRenderer(const SOffscreenRendererKey &inKey) override
+ {
+ TRendererMap::iterator theRenderer = m_Renderers.find(inKey);
+ if (theRenderer != m_Renderers.end()) {
+ SRendererData &theData = *theRenderer->second.mPtr;
+ return theData.m_Renderer;
+ }
+ return NULL;
+ }
+ void ReleaseOffscreenRenderer(const SOffscreenRendererKey &inKey) override
+ {
+ m_Renderers.erase(inKey);
+ }
+
+ void RenderItem(SRendererData &theData, SOffscreenRendererEnvironment theDesiredEnvironment)
+ {
+ NVRenderContext &theContext = m_ResourceManager->GetRenderContext();
+ QT3DSVec2 thePresScaleFactor = m_Context.GetPresentationScaleFactor();
+ SOffscreenRendererEnvironment theOriginalDesiredEnvironment(theDesiredEnvironment);
+ // Ensure that our overall render context comes back no matter what the client does.
+ qt3ds::render::NVRenderContextScopedProperty<QT3DSVec4> __clearColor(
+ theContext, &NVRenderContext::GetClearColor, &NVRenderContext::SetClearColor,
+ QT3DSVec4(0, 0, 0, 0));
+ qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled(
+ theContext, &NVRenderContext::IsScissorTestEnabled,
+ &NVRenderContext::SetScissorTestEnabled, false);
+ qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __scissorRect(
+ theContext, &NVRenderContext::GetScissorRect, &NVRenderContext::SetScissorRect);
+ qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __viewportRect(
+ theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport);
+ qt3ds::render::NVRenderContextScopedProperty<bool> __depthWrite(
+ theContext, &NVRenderContext::IsDepthWriteEnabled,
+ &NVRenderContext::SetDepthWriteEnabled, false);
+ qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBoolOp::Enum> __depthFunction(
+ theContext, &NVRenderContext::GetDepthFunction, &NVRenderContext::SetDepthFunction,
+ qt3ds::render::NVRenderBoolOp::Less);
+ qt3ds::render::NVRenderContextScopedProperty<bool> __blendEnabled(
+ theContext, &NVRenderContext::IsBlendingEnabled, &NVRenderContext::SetBlendingEnabled,
+ false);
+ qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendFunctionArgument>
+ __blendFunction(theContext, &NVRenderContext::GetBlendFunction,
+ &NVRenderContext::SetBlendFunction,
+ qt3ds::render::NVRenderBlendFunctionArgument());
+ qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderBlendEquationArgument>
+ __blendEquation(theContext, &NVRenderContext::GetBlendEquation,
+ &NVRenderContext::SetBlendEquation,
+ qt3ds::render::NVRenderBlendEquationArgument());
+ qt3ds::render::NVRenderContextScopedProperty<qt3ds::render::NVRenderFrameBufferPtr>
+ __rendertarget(theContext, &NVRenderContext::GetRenderTarget,
+ &NVRenderContext::SetRenderTarget);
+
+ QT3DSU32 theSampleCount = 1;
+ bool isMultisamplePass = false;
+ if (theDesiredEnvironment.m_MSAAMode != AAModeValues::NoAA) {
+ switch (theDesiredEnvironment.m_MSAAMode) {
+ case AAModeValues::SSAA:
+ theSampleCount = 1;
+ isMultisamplePass = true;
+ break;
+ case AAModeValues::X2:
+ theSampleCount = 2;
+ isMultisamplePass = true;
+ break;
+ case AAModeValues::X4:
+ theSampleCount = 4;
+ isMultisamplePass = true;
+ break;
+ case AAModeValues::X8:
+ theSampleCount = 8;
+ isMultisamplePass = true;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ };
+
+ // adjust render size for SSAA
+ if (theDesiredEnvironment.m_MSAAMode == AAModeValues::SSAA) {
+ CRendererUtil::GetSSAARenderSize(
+ theOriginalDesiredEnvironment.m_Width, theOriginalDesiredEnvironment.m_Height,
+ theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height);
+ }
+ }
+ CResourceFrameBuffer theFrameBuffer(*m_ResourceManager);
+ theFrameBuffer.EnsureFrameBuffer();
+ NVRenderTexture2D *renderTargetTexture(theData.m_Texture);
+ qt3ds::render::NVRenderTextureTargetType::Enum fboAttachmentType =
+ qt3ds::render::NVRenderTextureTargetType::Texture2D;
+ if (isMultisamplePass) {
+ renderTargetTexture = NULL;
+ if (theSampleCount > 1)
+ fboAttachmentType = qt3ds::render::NVRenderTextureTargetType::Texture2D_MS;
+ }
+
+ CResourceTexture2D renderColorTexture(*m_ResourceManager, renderTargetTexture);
+
+ CResourceTexture2D renderDepthStencilTexture(*m_ResourceManager);
+
+ if (theSampleCount > 1)
+ m_Context.GetRenderContext().SetMultisampleEnabled(true);
+
+ qt3ds::render::NVRenderClearFlags theClearFlags;
+ NVRenderTextureFormats::Enum theDepthStencilTextureFormat(NVRenderTextureFormats::Unknown);
+ NVRenderFrameBufferAttachments::Enum theAttachmentLocation(
+ NVRenderFrameBufferAttachments::Unknown);
+ if (theDesiredEnvironment.m_Stencil) {
+ theDepthStencilTextureFormat = NVRenderTextureFormats::Depth24Stencil8;
+ theAttachmentLocation = NVRenderFrameBufferAttachments::DepthStencil;
+ } else if (theDesiredEnvironment.m_Depth != OffscreenRendererDepthValues::NoDepthBuffer) {
+ theAttachmentLocation = NVRenderFrameBufferAttachments::Depth;
+ switch (theDesiredEnvironment.m_Depth) {
+ case OffscreenRendererDepthValues::Depth16:
+ theDepthStencilTextureFormat = NVRenderTextureFormats::Depth16;
+ break;
+ case OffscreenRendererDepthValues::Depth24:
+ theDepthStencilTextureFormat = NVRenderTextureFormats::Depth24;
+ break;
+ case OffscreenRendererDepthValues::Depth32:
+ theDepthStencilTextureFormat = NVRenderTextureFormats::Depth32;
+ break;
+ default:
+ theAttachmentLocation = NVRenderFrameBufferAttachments::Unknown;
+ theDepthStencilTextureFormat = NVRenderTextureFormats::Unknown;
+ break;
+ }
+ }
+ renderColorTexture.EnsureTexture(theDesiredEnvironment.m_Width,
+ theDesiredEnvironment.m_Height,
+ theDesiredEnvironment.m_Format, theSampleCount);
+ theFrameBuffer->Attach(NVRenderFrameBufferAttachments::Color0, *renderColorTexture,
+ fboAttachmentType);
+
+ if (theDepthStencilTextureFormat != NVRenderTextureFormats::Unknown) {
+ renderDepthStencilTexture.EnsureTexture(theDesiredEnvironment.m_Width,
+ theDesiredEnvironment.m_Height,
+ theDepthStencilTextureFormat, theSampleCount);
+ theFrameBuffer->Attach(theAttachmentLocation, *renderDepthStencilTexture,
+ fboAttachmentType);
+ }
+ // IsComplete check takes a really long time so I am not going to worry about it for now.
+
+ theContext.SetRenderTarget(theFrameBuffer);
+ theContext.SetViewport(
+ NVRenderRect(0, 0, theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height));
+ theContext.SetScissorTestEnabled(false);
+
+ theContext.SetBlendingEnabled(false);
+ theData.m_Renderer->Render(theDesiredEnvironment, theContext, thePresScaleFactor,
+ SScene::AlwaysClear, this);
+
+ if (theSampleCount > 1) {
+ CResourceTexture2D theResult(*m_ResourceManager, theData.m_Texture);
+
+ if (theDesiredEnvironment.m_MSAAMode != AAModeValues::SSAA) {
+ // Have to downsample the FBO.
+ CRendererUtil::ResolveMutisampleFBOColorOnly(
+ *m_ResourceManager, theResult, m_Context.GetRenderContext(),
+ theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height,
+ theDesiredEnvironment.m_Format, *theFrameBuffer);
+
+ m_Context.GetRenderContext().SetMultisampleEnabled(false);
+ } else {
+ // Resolve the FBO to the layer texture
+ CRendererUtil::ResolveSSAAFBOColorOnly(
+ *m_ResourceManager, theResult, theOriginalDesiredEnvironment.m_Width,
+ theOriginalDesiredEnvironment.m_Height, m_Context.GetRenderContext(),
+ theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height,
+ theDesiredEnvironment.m_Format, *theFrameBuffer);
+ }
+
+ QT3DS_ASSERT(theData.m_Texture == theResult.GetTexture());
+ theResult.ForgetTexture();
+ } else {
+ renderColorTexture.ForgetTexture();
+ }
+ theFrameBuffer->Attach(NVRenderFrameBufferAttachments::Color0,
+ NVRenderTextureOrRenderBuffer(), fboAttachmentType);
+ if (theAttachmentLocation != NVRenderFrameBufferAttachments::Unknown)
+ theFrameBuffer->Attach(theAttachmentLocation, NVRenderTextureOrRenderBuffer(),
+ fboAttachmentType);
+ }
+
+ SOffscreenRenderResult GetRenderedItem(const SOffscreenRendererKey &inKey) override
+ {
+ TRendererMap::iterator theRenderer = m_Renderers.find(inKey);
+ QT3DSVec2 thePresScaleFactor = m_Context.GetPresentationScaleFactor();
+ if (theRenderer != m_Renderers.end() && theRenderer->second.mPtr->m_Rendering == false) {
+ SRendererData &theData = *theRenderer->second.mPtr;
+ SScopedRenderDataRenderMarker __renderMarker(theData);
+
+ bool renderedThisFrame = theData.m_Texture && theData.m_FrameCount == m_FrameCount;
+ theData.m_FrameCount = m_FrameCount;
+ // Two different quick-out pathways.
+ if (renderedThisFrame)
+ return theData;
+
+ SOffscreenRendererEnvironment theDesiredEnvironment =
+ theData.m_Renderer->GetDesiredEnvironment(thePresScaleFactor);
+ // Ensure we get a valid width and height
+ theDesiredEnvironment.m_Width =
+ ITextRenderer::NextMultipleOf4(theDesiredEnvironment.m_Width);
+ theDesiredEnvironment.m_Height =
+ ITextRenderer::NextMultipleOf4(theDesiredEnvironment.m_Height);
+ if (theDesiredEnvironment.m_Width == 0 || theDesiredEnvironment.m_Height == 0) {
+ return SOffscreenRenderResult();
+ }
+
+ NVRenderRect theViewport(0, 0, theDesiredEnvironment.m_Width,
+ theDesiredEnvironment.m_Height);
+ IRenderList &theRenderList(m_Context.GetRenderList());
+ NVRenderContext &theContext(m_Context.GetRenderContext());
+ // This happens here because if there are any fancy render steps
+ SRenderListScopedProperty<bool> _scissor(theRenderList,
+ &IRenderList::IsScissorTestEnabled,
+ &IRenderList::SetScissorTestEnabled, false);
+ SRenderListScopedProperty<NVRenderRect> _viewport(
+ theRenderList, &IRenderList::GetViewport, &IRenderList::SetViewport, theViewport);
+ // Some plugins don't use the render list so they need the actual gl context setup.
+ qt3ds::render::NVRenderContextScopedProperty<bool> __scissorEnabled(
+ theContext, &NVRenderContext::IsScissorTestEnabled,
+ &NVRenderContext::SetScissorTestEnabled, false);
+ qt3ds::render::NVRenderContextScopedProperty<NVRenderRect> __viewportRect(
+ theContext, &NVRenderContext::GetViewport, &NVRenderContext::SetViewport,
+ theViewport);
+
+ QT3DSU32 taskId = m_Context.GetRenderList().AddRenderTask(
+ *QT3DS_NEW(m_Context.GetPerFrameAllocator(),
+ SOffscreenRunnable)(*this, theData, theDesiredEnvironment));
+
+ SOffscreenRenderFlags theFlags =
+ theData.m_Renderer->NeedsRender(theDesiredEnvironment, thePresScaleFactor, this);
+ theData.m_HasTransparency = theFlags.m_HasTransparency;
+ theData.m_HasChangedSinceLastFrame = theFlags.m_HasChangedSinceLastFrame;
+ if (theData.m_Texture) {
+ // Quick-out if the renderer doesn't need to render itself.
+ if (theData.m_HasChangedSinceLastFrame == false) {
+ m_Context.GetRenderList().DiscardRenderTask(taskId);
+ return theData;
+ }
+ } else
+ theData.m_HasChangedSinceLastFrame = true;
+
+ // Release existing texture if it doesn't match latest environment request.
+ if (theData.m_Texture) {
+ STextureDetails theDetails = theData.m_Texture->GetTextureDetails();
+ if (theDesiredEnvironment.m_Width != theDetails.m_Width
+ || theDesiredEnvironment.m_Height != theDetails.m_Height
+ || theDesiredEnvironment.m_Format != theDetails.m_Format) {
+ m_ResourceManager->Release(*theData.m_Texture);
+ theData.m_Texture = NULL;
+ }
+ }
+
+ if (theData.m_Texture == NULL)
+ theData.m_Texture = m_ResourceManager->AllocateTexture2D(
+ theDesiredEnvironment.m_Width, theDesiredEnvironment.m_Height,
+ theDesiredEnvironment.m_Format);
+
+ // Add the node to the graph and get on with it.
+
+ return theData;
+ }
+ return SOffscreenRenderResult();
+ }
+
+ void BeginFrame() override { m_PerFrameAllocator.reset(); }
+ void EndFrame() override { ++m_FrameCount; }
+};
+
+void SOffscreenRunnable::Run()
+{
+ m_RenderManager.RenderItem(m_Data, m_DesiredEnvironment);
+}
+}
+
+IOffscreenRenderManager &IOffscreenRenderManager::CreateOffscreenRenderManager(
+ NVAllocatorCallback &inCallback, IStringTable &inStringTable, IResourceManager &inManager,
+ IQt3DSRenderContext &inContext)
+{
+ return *QT3DS_NEW(inCallback, SOffscreenRenderManager)(inCallback, inStringTable, inManager,
+ inContext);
+}