diff options
Diffstat (limited to 'src/Runtime/Source/runtimerender/Qt3DSRenderPlugin.cpp')
-rw-r--r-- | src/Runtime/Source/runtimerender/Qt3DSRenderPlugin.cpp | 936 |
1 files changed, 936 insertions, 0 deletions
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderPlugin.cpp b/src/Runtime/Source/runtimerender/Qt3DSRenderPlugin.cpp new file mode 100644 index 00000000..046f1b1b --- /dev/null +++ b/src/Runtime/Source/runtimerender/Qt3DSRenderPlugin.cpp @@ -0,0 +1,936 @@ +/**************************************************************************** +** +** 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 "Qt3DSRenderPlugin.h" +#include "foundation/Qt3DSAtomic.h" +#include "foundation/Qt3DSFoundation.h" +#include "foundation/Qt3DSBroadcastingAllocator.h" +#include "foundation/SerializationTypes.h" +#include "foundation/IOStreams.h" +#include "foundation/Qt3DSSystem.h" +#include "foundation/FileTools.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderString.h" +#include "Qt3DSRenderPluginPropertyValue.h" +#include "Qt3DSRenderInputStreamFactory.h" + +#if defined(QT3DS_WINDOWS) +#include "windows/DynamicLibLoader.h" +#elif defined(QT3DS_ANDROID) +#include "android/DynamicLibLoader.h" +#elif defined(QT3DS_LINUX) +#include "linux/DynamicLibLoader.h" +#elif defined(QT3DS_APPLE) +#include "macos/DynamicLibLoader.h" +#elif defined(QT3DS_QNX) +#include "qnx/DynamicLibLoader.h" +#else +#error "Must define an operating system type (QT3DS_WINDOWS, QT3DS_ANDROID, QT3DS_LINUX, QT3DS_APPLE, QT3DS_QNX)" +#endif + +using namespace qt3ds::render; + +namespace { +// Legacy definitions... +// API version 1 definitions +typedef struct _RenderPluginSurfaceDescriptionV1 +{ + long m_Width; + long m_Height; + enum QT3DSRenderPluginDepthTypes m_DepthBuffer; + enum QT3DSRenderPluginTextureTypes m_ColorBuffer; + TBool m_HasStencilBuffer; +} TRenderPluginSurfaceDescriptionV1; + +typedef TNeedsRenderResult (*TNeedsRenderFunctionV1)(TRenderPluginClassPtr cls, + TRenderPluginInstancePtr instance, + TRenderPluginSurfaceDescriptionV1 surface, + TVec2 presScaleFactor); + +typedef void (*TRenderFunctionV1)(TRenderPluginClassPtr cls, TRenderPluginInstancePtr instance, + TRenderPluginSurfaceDescriptionV1 surface, + TVec2 presScaleFactor, + QT3DSRenderPluginColorClearState inClearColorBuffer); + +// End API version 1 definitions + +TRenderPluginSurfaceDescription ToCInterface(const SOffscreenRendererEnvironment &env) +{ + TRenderPluginSurfaceDescription retval; + retval.m_Width = (long)env.m_Width; + retval.m_Height = (long)env.m_Height; + retval.m_ColorBuffer = static_cast<QT3DSRenderPluginTextureTypes>(env.m_Format); + retval.m_DepthBuffer = static_cast<QT3DSRenderPluginDepthTypes>(env.m_Depth); + retval.m_HasStencilBuffer = env.m_Stencil ? TTRUE : TFALSE; + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelNoMSAA; + // note no supersampling AA support for plugins + // we fall back to 4xMSAA + switch (env.m_MSAAMode) { + case AAModeValues::X2: + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelTwo; + break; + case AAModeValues::SSAA: + case AAModeValues::X4: + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelFour; + break; + case AAModeValues::X8: + retval.m_MSAALevel = QT3DSRenderPluginMSAALevelEight; + break; + default: + QT3DS_ASSERT(false); + // fallthrough intentional. + case AAModeValues::NoAA: + break; + }; + return retval; +} + +TRenderPluginSurfaceDescriptionV1 ToCInterfaceV1(const SOffscreenRendererEnvironment &env) +{ + TRenderPluginSurfaceDescriptionV1 retval; + retval.m_Width = (long)env.m_Width; + retval.m_Height = (long)env.m_Height; + retval.m_ColorBuffer = static_cast<QT3DSRenderPluginTextureTypes>(env.m_Format); + retval.m_DepthBuffer = static_cast<QT3DSRenderPluginDepthTypes>(env.m_Depth); + retval.m_HasStencilBuffer = env.m_Stencil ? TTRUE : TFALSE; + return retval; +} + +TVec2 ToCInterface(const QT3DSVec2 &item) +{ + TVec2 retval = { item.x, item.y }; + return retval; +} + +QT3DSRenderPluginColorClearState ToCInterface(SScene::RenderClearCommand inClearCommand) +{ + switch (inClearCommand) { + case SScene::DoNotClear: + return QT3DSRenderPluginColorClearStateDoNotClear; + case SScene::AlwaysClear: + return QT3DSRenderPluginColorClearStateAlwaysClear; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case SScene::ClearIsOptional: + return QT3DSRenderPluginColorClearStateClearIsOptional; + }; +} + +class SRenderPluginPropertyData +{ + SRenderPluginPropertyValue m_Value; + bool m_Dirty; + +public: + SRenderPluginPropertyData() + : m_Dirty(false) + { + } + SRenderPluginPropertyData(const SRenderPluginPropertyData &other) + : m_Value(other.m_Value) + , m_Dirty(other.m_Dirty) + { + } + SRenderPluginPropertyData &operator=(const SRenderPluginPropertyData &other) + { + m_Value = other.m_Value; + m_Dirty = other.m_Dirty; + return *this; + } + + bool IsDirty() const + { + return m_Value.getType() != RenderPluginPropertyValueTypes::NoRenderPluginPropertyValue + && m_Dirty; + } + void SetValue(const SRenderPluginPropertyValue &value) + { + m_Value = value; + m_Dirty = true; + } + + TRenderPluginPropertyUpdate ClearDirty(CRegisteredString inPropName) + { + m_Dirty = false; + TRenderPluginPropertyUpdate retval; + memset(&retval, 0, sizeof(TRenderPluginPropertyUpdate)); + retval.m_PropName = inPropName.c_str(); + switch (m_Value.getType()) { + case RenderPluginPropertyValueTypes::Long: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeLong; + long temp = (long)m_Value.getData<QT3DSI32>(); + retval.m_PropertyValue = *reinterpret_cast<void **>(&temp); + } break; + case RenderPluginPropertyValueTypes::Float: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeFloat; + float temp = m_Value.getData<QT3DSF32>(); + retval.m_PropertyValue = *reinterpret_cast<void **>(&temp); + } break; + case RenderPluginPropertyValueTypes::Boolean: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeLong; + long temp = m_Value.getData<bool>() ? TTRUE : TFALSE; + retval.m_PropertyValue = *reinterpret_cast<void **>(&temp); + } break; + case RenderPluginPropertyValueTypes::String: { + retval.m_PropertyType = QT3DSRenderPluginPropertyTypeCharPtr; + const char *temp = m_Value.getData<CRegisteredString>().c_str(); + retval.m_PropertyValue = reinterpret_cast<void *>(const_cast<char *>(temp)); + } break; + default: + QT3DS_ASSERT(false); + } + return retval; + } +}; + +typedef nvvector<SRenderPluginPropertyData> TPropertyValueList; + +struct IInternalPluginClass : public IRenderPluginClass +{ + virtual void PushUpdates(TRenderPluginInstancePtr instance, + TPropertyValueList &propertyValues) = 0; + virtual void Update(NVConstDataRef<SRenderPropertyValueUpdate> updateBuffer, + TPropertyValueList &propertyValues) = 0; + virtual QT3DSI32 GetAPIVersion() = 0; +}; + +static NVRenderTextureFormats::Enum ToTextureFormat(QT3DSRenderPluginTextureTypes inTextureType) +{ + switch (inTextureType) { + default: + case QT3DSRenderPluginTextureTypeRGBA8: + return NVRenderTextureFormats::RGBA8; + case QT3DSRenderPluginTextureTypeRGB8: + return NVRenderTextureFormats::RGB8; + case QT3DSRenderPluginTextureTypeRGB565: + return NVRenderTextureFormats::RGB565; + case QT3DSRenderPluginTextureTypeRGBA5551: + return NVRenderTextureFormats::RGBA5551; + } +} + +static OffscreenRendererDepthValues::Enum ToDepthValue(QT3DSRenderPluginDepthTypes inType) +{ + switch (inType) { + default: + case QT3DSRenderPluginDepthTypeDepth16: + return OffscreenRendererDepthValues::Depth16; + case QT3DSRenderPluginDepthTypeDepth24: + return OffscreenRendererDepthValues::Depth24; + case QT3DSRenderPluginDepthTypeDepth32: + return OffscreenRendererDepthValues::Depth32; + } +} + +static AAModeValues::Enum ToAAMode(QT3DSRenderPluginMSAALevel inMode) +{ + switch (inMode) { + case QT3DSRenderPluginMSAALevelTwo: + return AAModeValues::X2; + case QT3DSRenderPluginMSAALevelFour: + return AAModeValues::X4; + case QT3DSRenderPluginMSAALevelEight: + return AAModeValues::X8; + default: + QT3DS_ASSERT(false); // fallthrough intentional + case QT3DSRenderPluginMSAALevelNoMSAA: + return AAModeValues::NoAA; + } +} + +struct InstanceImpl : public IRenderPluginInstance +{ + NVFoundationBase &m_Foundation; + TRenderPluginInstancePtr m_Instance; + TRenderPluginClass m_Class; + NVScopedRefCounted<IInternalPluginClass> m_Owner; + CRegisteredString m_RendererType; + // Backing store of property values + nvvector<SRenderPluginPropertyData> m_PropertyValues; + bool m_Dirty; + NVRenderContext *m_RenderContext; + QT3DSI32 mRefCount; + + InstanceImpl(NVFoundationBase &fnd, TRenderPluginInstancePtr instance, TRenderPluginClass cls, + IInternalPluginClass &owner, IStringTable &strTable) + : m_Foundation(fnd) + , m_Instance(instance) + , m_Class(cls) + , m_Owner(owner) + , m_RendererType( + strTable.RegisterStr(IRenderPluginInstance::IRenderPluginOffscreenRendererType())) + , m_PropertyValues(m_Foundation.getAllocator(), "InstanceImpl::m_PropertyValues") + , m_Dirty(false) + , m_RenderContext(NULL) + , mRefCount(0) + { + } + + virtual ~InstanceImpl() { m_Class.ReleaseInstance(m_Class.m_Class, m_Instance); } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + void addCallback(IOffscreenRendererCallback *cb) override + { + + } + void CreateScriptProxy(script_State *state) override + { + if (m_Class.CreateInstanceScriptProxy) + m_Class.CreateInstanceScriptProxy(m_Class.m_Class, m_Instance, state); + } + + // Arbitrary const char* returned to indicate the type of this renderer + // Can be overloaded to form the basis of an RTTI type system. + // Not currently used by the rendering system. + CRegisteredString GetOffscreenRendererType() override { return m_RendererType; } + + SOffscreenRendererEnvironment GetDesiredEnvironment(QT3DSVec2 inPresentationScaleFactor) override + { + if (m_Class.QueryInstanceRenderSurface) { + QT3DSRenderPluginMSAALevel theLevel = QT3DSRenderPluginMSAALevelNoMSAA; + TRenderPluginSurfaceDescription desc = m_Class.QueryInstanceRenderSurface( + m_Class.m_Class, m_Instance, ToCInterface(inPresentationScaleFactor)); + if (m_Owner->GetAPIVersion() > 1) + theLevel = desc.m_MSAALevel; + + return SOffscreenRendererEnvironment( + (QT3DSU32)desc.m_Width, (QT3DSU32)desc.m_Height, ToTextureFormat(desc.m_ColorBuffer), + ToDepthValue(desc.m_DepthBuffer), desc.m_HasStencilBuffer ? true : false, + ToAAMode(theLevel)); + } else { + QT3DS_ASSERT(false); + } + return SOffscreenRendererEnvironment(); + } + + // Returns true of this object needs to be rendered, false if this object is not dirty + SOffscreenRenderFlags NeedsRender(const SOffscreenRendererEnvironment &inEnvironment, + QT3DSVec2 inPresentationScaleFactor, + const SRenderInstanceId instanceId) override + { + if (m_Dirty) { + m_Dirty = false; + m_Owner->PushUpdates(m_Instance, m_PropertyValues); + } + if (m_Class.NeedsRenderFunction) { + if (m_Owner->GetAPIVersion() > 1) { + TNeedsRenderResult result = m_Class.NeedsRenderFunction( + m_Class.m_Class, m_Instance, ToCInterface(inEnvironment), + ToCInterface(inPresentationScaleFactor)); + return SOffscreenRenderFlags(result.HasTransparency ? true : false, + result.HasChangedSinceLastFrame ? true : false); + } else { + TNeedsRenderFunctionV1 theV1Function = + reinterpret_cast<TNeedsRenderFunctionV1>(m_Class.NeedsRenderFunction); + + TNeedsRenderResult result = + theV1Function(m_Class.m_Class, m_Instance, ToCInterfaceV1(inEnvironment), + ToCInterface(inPresentationScaleFactor)); + return SOffscreenRenderFlags(result.HasTransparency ? true : false, + result.HasChangedSinceLastFrame ? true : false); + } + } + return SOffscreenRenderFlags(true, true); + } + // Returns true if the rendered result image has transparency, or false + // if it should be treated as a completely opaque image. + // It is the IOffscreenRenderer's job to clear any buffers (color, depth, stencil) that it + // needs to. It should not assume that it's buffers are clear; + // Sometimes we scale the width and height of the main presentation in order to fit a window. + // If we do so, the scale factor tells the subpresentation renderer how much the system has + // scaled. + void Render(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresentationScaleFactor, + SScene::RenderClearCommand inColorBufferNeedsClear, + const SRenderInstanceId instanceId) override + { + m_RenderContext = &inRenderContext; + if (m_Class.RenderInstance) { + inRenderContext.PushPropertySet(); + if (m_Owner->GetAPIVersion() > 1) { + m_Class.RenderInstance(m_Class.m_Class, m_Instance, ToCInterface(inEnvironment), + ToCInterface(inPresentationScaleFactor), + ToCInterface(inColorBufferNeedsClear)); + } else { + TRenderFunctionV1 theV1Function = + reinterpret_cast<TRenderFunctionV1>(m_Class.RenderInstance); + theV1Function(m_Class.m_Class, m_Instance, ToCInterfaceV1(inEnvironment), + ToCInterface(inPresentationScaleFactor), + ToCInterface(inColorBufferNeedsClear)); + } + + inRenderContext.PopPropertySet(true); + } + } + + void RenderWithClear(const SOffscreenRendererEnvironment &inEnvironment, + NVRenderContext &inRenderContext, QT3DSVec2 inPresScale, + SScene::RenderClearCommand inClearBuffer, QT3DSVec3 inClearColor, + const SRenderInstanceId id) + { + Q_ASSERT(false); + } + + // Implementors should implement one of the two interfaces below. + + // If this renderer supports picking that can return graph objects + // then return an interface here. + IGraphObjectPickQuery *GetGraphObjectPickQuery(const SRenderInstanceId) override { return NULL; } + + // If you *don't* support the GraphObjectPickIterator interface, then you should implement this + // interface + // The system will just ask you to pick. + // If you return true, then we will assume that you swallowed the pick and will continue no + // further. + // else we will assume you did not and will continue the picking algorithm. + bool Pick(const QT3DSVec2 &inMouseCoords, const QT3DSVec2 &inViewportDimensions, + const SRenderInstanceId instanceId) override + { + if (m_Class.Pick) { + if (m_RenderContext) { + m_RenderContext->PushPropertySet(); + bool retval = m_Class.Pick(m_Class.m_Class, m_Instance, ToCInterface(inMouseCoords), + ToCInterface(inViewportDimensions)) + ? true + : false; + m_RenderContext->PopPropertySet(true); + return retval; + } + } + return false; + } + + TRenderPluginInstancePtr GetRenderPluginInstance() override { return m_Instance; } + void Update(NVConstDataRef<SRenderPropertyValueUpdate> updateBuffer) override + { + m_Dirty = true; + m_Owner->Update(updateBuffer, m_PropertyValues); + } + IRenderPluginClass &GetPluginClass() override { return *m_Owner; } +}; + +typedef eastl::pair<CRegisteredString, RenderPluginPropertyValueTypes::Enum> TStringTypePair; + +struct PluginClassImpl : public IInternalPluginClass +{ + typedef nvhash_map<CRegisteredString, QT3DSU32> TStringIndexMap; + NVFoundationBase &m_Foundation; + IStringTable &m_StringTable; + TRenderPluginClass m_Class; + CRegisteredString m_Type; + CLoadedDynamicLibrary *m_DynamicLibrary; + nvvector<SRenderPluginPropertyDeclaration> m_RegisteredProperties; + TStringIndexMap m_ComponentNameToComponentIndexMap; + nvvector<TStringTypePair> m_FullPropertyList; + nvvector<TRenderPluginPropertyUpdate> m_UpdateBuffer; + CRenderString m_TempString; + QT3DSI32 m_APIVersion; + + QT3DSI32 mRefCount; + + PluginClassImpl(NVFoundationBase &fnd, IStringTable &strTable, TRenderPluginClass inClass, + CRegisteredString inType, CLoadedDynamicLibrary *inLibrary) + : m_Foundation(fnd) + , m_StringTable(strTable) + , m_Class(inClass) + , m_Type(inType) + , m_DynamicLibrary(inLibrary) + , m_RegisteredProperties(m_Foundation.getAllocator(), + "PluginClassImpl::m_RegisteredProperties") + , m_ComponentNameToComponentIndexMap(m_Foundation.getAllocator(), + "PluginClassImpl::m_ComponentNameToComponentIndexMap") + , m_FullPropertyList(m_Foundation.getAllocator(), "PluginClassImpl::m_FullPropertyList") + , m_UpdateBuffer(m_Foundation.getAllocator(), "PluginClassImpl::m_UpdateBuffer") + , m_APIVersion(m_Class.GetRenderPluginAPIVersion(m_Class.m_Class)) + , mRefCount(0) + { + } + ~PluginClassImpl() + { + if (m_Class.ReleaseClass) + m_Class.ReleaseClass(m_Class.m_Class); + if (m_DynamicLibrary) + NVDelete(m_Foundation.getAllocator(), m_DynamicLibrary); + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + NVScopedRefCounted<IRenderPluginInstance> CreateInstance() override + { + if (m_Class.CreateInstance) { + TRenderPluginInstancePtr instance = + m_Class.CreateInstance(m_Class.m_Class, m_Type.c_str()); + if (instance) { + InstanceImpl *retval = QT3DS_NEW(m_Foundation.getAllocator(), InstanceImpl)( + m_Foundation, instance, m_Class, *this, m_StringTable); + return retval; + } + } + return NVScopedRefCounted<IRenderPluginInstance>(); + } + + QT3DSI32 GetAPIVersion() override { return m_APIVersion; } + + void AddFullPropertyType(const char *name, RenderPluginPropertyValueTypes::Enum inType) + { + QT3DSU32 itemIndex = (QT3DSU32)m_FullPropertyList.size(); + CRegisteredString regName = m_StringTable.RegisterStr(name); + bool inserted = + m_ComponentNameToComponentIndexMap.insert(eastl::make_pair(regName, itemIndex)).second; + if (inserted) { + m_FullPropertyList.push_back(eastl::make_pair(regName, inType)); + } else { + // Duplicate property declaration. + QT3DS_ASSERT(false); + } + } + + void AddFullPropertyType(const char *name, const char *extension, + RenderPluginPropertyValueTypes::Enum inType) + { + m_TempString.assign(name); + if (!isTrivial(extension)) { + m_TempString.append("."); + m_TempString.append(extension); + } + AddFullPropertyType(m_TempString.c_str(), inType); + } + + void RegisterProperty(const SRenderPluginPropertyDeclaration &dec) override + { + QT3DSU32 startOffset = (QT3DSU32)m_FullPropertyList.size(); + + switch (dec.m_Type) { + + case SRenderPluginPropertyTypes::Vector2: + AddFullPropertyType(dec.m_Name, "x", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "y", RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Color: + AddFullPropertyType(dec.m_Name, "r", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "g", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "b", RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Vector3: + AddFullPropertyType(dec.m_Name, "x", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "y", RenderPluginPropertyValueTypes::Float); + AddFullPropertyType(dec.m_Name, "z", RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Boolean: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::Boolean); + break; + case SRenderPluginPropertyTypes::Float: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::Float); + break; + case SRenderPluginPropertyTypes::Long: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::Long); + break; + case SRenderPluginPropertyTypes::String: + AddFullPropertyType(dec.m_Name, RenderPluginPropertyValueTypes::String); + break; + default: + QT3DS_ASSERT(false); + break; + } + m_RegisteredProperties.push_back(dec); + m_RegisteredProperties.back().m_StartOffset = startOffset; + } + + NVConstDataRef<SRenderPluginPropertyDeclaration> GetRegisteredProperties() override + { + return m_RegisteredProperties; + } + + SRenderPluginPropertyDeclaration GetPropertyDeclaration(CRegisteredString inPropName) override + { + for (QT3DSU32 idx = 0, end = m_RegisteredProperties.size(); idx < end; ++idx) { + if (m_RegisteredProperties[idx].m_Name == inPropName) + return m_RegisteredProperties[idx]; + } + QT3DS_ASSERT(false); + return SRenderPluginPropertyDeclaration(); + } + + // From which you can get the property name breakdown + virtual eastl::pair<CRegisteredString, RenderPluginPropertyValueTypes::Enum> + GetPropertyValueInfo(QT3DSU32 inIndex) override + { + if (inIndex < m_FullPropertyList.size()) + return m_FullPropertyList[inIndex]; + QT3DS_ASSERT(false); + return eastl::pair<CRegisteredString, RenderPluginPropertyValueTypes::Enum>( + CRegisteredString(), RenderPluginPropertyValueTypes::NoRenderPluginPropertyValue); + } + + void PushUpdates(TRenderPluginInstancePtr instance, TPropertyValueList &propertyValues) override + { + m_UpdateBuffer.clear(); + for (QT3DSU32 idx = 0, end = propertyValues.size(); idx < end; ++idx) { + SRenderPluginPropertyData &theData(propertyValues[idx]); + if (theData.IsDirty()) + m_UpdateBuffer.push_back(theData.ClearDirty(m_FullPropertyList[idx].first)); + } + if (m_Class.UpdateInstance) + m_Class.UpdateInstance(m_Class.m_Class, instance, m_UpdateBuffer.data(), + (long)m_UpdateBuffer.size()); + } + + void Update(NVConstDataRef<SRenderPropertyValueUpdate> updateBuffer, + TPropertyValueList &propertyValues) override + { + for (QT3DSU32 idx = 0, end = updateBuffer.size(); idx < end; ++idx) { + const SRenderPropertyValueUpdate &update = updateBuffer[idx]; + TStringIndexMap::iterator iter = + m_ComponentNameToComponentIndexMap.find(update.m_PropertyName); + if (iter == m_ComponentNameToComponentIndexMap.end()) { + QT3DS_ASSERT(false); + continue; + } + + QT3DSU32 propIndex = iter->second; + if (update.m_Value.getType() != m_FullPropertyList[propIndex].second) { + QT3DS_ASSERT(false); + continue; + } + if (propIndex >= propertyValues.size()) + propertyValues.resize(propIndex + 1); + propertyValues[propIndex].SetValue(update.m_Value); + } + } +}; + +struct PluginInstanceKey +{ + CRegisteredString m_Path; + void *m_InstanceKey; + PluginInstanceKey(CRegisteredString p, void *ik) + : m_Path(p) + , m_InstanceKey(ik) + { + } + bool operator==(const PluginInstanceKey &rhs) const + { + return m_Path == rhs.m_Path && m_InstanceKey == rhs.m_InstanceKey; + } +}; +} + +namespace eastl { +template <> +struct hash<PluginInstanceKey> +{ + size_t operator()(const PluginInstanceKey &k) const + { + return hash<CRegisteredString>()(k.m_Path) + ^ hash<size_t>()(reinterpret_cast<size_t>(k.m_InstanceKey)); + } + bool operator()(const PluginInstanceKey &lhs, const PluginInstanceKey &rhs) const + { + return lhs.m_Path == rhs.m_Path && lhs.m_InstanceKey == rhs.m_InstanceKey; + } +}; +} + +namespace { + +struct SLoadedPluginData +{ + CRegisteredString m_PluginPath; + eastl::vector<SRenderPluginPropertyDeclaration> m_Properties; +}; + +typedef eastl::vector<SLoadedPluginData> TLoadedPluginDataList; + +struct PluginManagerImpl : public IRenderPluginManager, public IRenderPluginManagerCore +{ + typedef nvhash_map<CRegisteredString, NVScopedRefCounted<IRenderPluginClass>> TLoadedClassMap; + typedef nvhash_map<PluginInstanceKey, NVScopedRefCounted<IRenderPluginInstance>> TInstanceMap; + NVFoundationBase &m_Foundation; + IStringTable &m_StringTable; + TLoadedClassMap m_LoadedClasses; + TInstanceMap m_Instances; + NVScopedRefCounted<NVRenderContext> m_RenderContext; + IInputStreamFactory &m_InputStreamFactory; + QT3DSI32 mRefCount; + TStr m_DllDir; + TLoadedPluginDataList m_LoadedPluginData; + + PluginManagerImpl(NVFoundationBase &fnd, IStringTable &st, IInputStreamFactory &inFactory) + : m_Foundation(fnd) + , m_StringTable(st) + , m_LoadedClasses(fnd.getAllocator(), "PluginManagerImpl::m_LoadedClasses") + , m_Instances(fnd.getAllocator(), "PluginManagerImpl::m_Instances") + , m_InputStreamFactory(inFactory) + , mRefCount(0) + , m_DllDir(ForwardingAllocator(fnd.getAllocator(), "PluginManagerImpl::m_DllDir")) + { + } + + QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator()) + + IRenderPluginClass *GetRenderPlugin(CRegisteredString inRelativePath) override + { + TLoadedClassMap::iterator iter = m_LoadedClasses.find(inRelativePath); + if (iter != m_LoadedClasses.end()) + return iter->second; + + return NVScopedRefCounted<IRenderPluginClass>(); + } + + IRenderPluginClass *GetOrCreateRenderPlugin(CRegisteredString inRelativePath) override + { + TLoadedClassMap::iterator iter = m_LoadedClasses.find(inRelativePath); + if (iter != m_LoadedClasses.end()) { + return iter->second; + } + + // We insert right here to keep us from going down this path potentially for every instance. + iter = + m_LoadedClasses + .insert(eastl::make_pair(inRelativePath, NVScopedRefCounted<IRenderPluginClass>())) + .first; + eastl::string xmlDir, fname, extension; + + CFileTools::Split(inRelativePath.c_str(), xmlDir, fname, extension); + + eastl::string sharedLibrary(xmlDir); + eastl::string subdir(qt3ds::foundation::System::getPlatformGLStr()); + eastl::string libdir; + eastl::string libpath; + + CFileTools::CombineBaseAndRelative(xmlDir.c_str(), subdir.c_str(), libdir); + CFileTools::CombineBaseAndRelative(libdir.c_str(), fname.c_str(), libpath); +#ifdef _DEBUG + libpath.append("d"); +#endif + libpath.append(qt3ds::foundation::System::g_DLLExtension); + eastl::string loadPath; + if (m_DllDir.size()) { + // Then we have to copy the dll to the dll directory before loading because the + // filesystem + // the plugin is on may not be executable. + eastl::string targetFile; + CFileTools::CombineBaseAndRelative(m_DllDir.c_str(), fname.c_str(), targetFile); +#ifdef _DEBUG + targetFile.append("d"); +#endif + targetFile.append(qt3ds::foundation::System::g_DLLExtension); + + qCInfo(TRACE_INFO, "Copying plugin shared library from %s to %s", + libpath.c_str(), targetFile.c_str()); + + // try to open the library. + NVScopedRefCounted<IRefCountedInputStream> theStream = + m_InputStreamFactory.GetStreamForFile(libpath.c_str()); + if (!theStream) { + qCCritical(INVALID_OPERATION, "Failed to load render plugin %s", + libpath.c_str()); + return NULL; + } + CFileSeekableIOStream outStream(targetFile.c_str(), FileWriteFlags()); + if (!outStream.IsOpen()) { + qCCritical(INVALID_OPERATION, "Failed to load render plugin %s", + targetFile.c_str()); + return NULL; + } + + QT3DSU8 buf[1024] = { 0 }; + for (QT3DSU32 len = theStream->Read(toDataRef(buf, 1024)); len; + len = theStream->Read(toDataRef(buf, 1024))) { + outStream.Write(toDataRef(buf, len)); + } + loadPath = targetFile; + } else { + QString path; + m_InputStreamFactory.GetPathForFile(libpath.c_str(), path); + loadPath = path.toUtf8().data(); + } + CLoadedDynamicLibrary *library = NULL; + TRenderPluginClass newPluginClass; + memSet(&newPluginClass, 0, sizeof(newPluginClass)); + + // Do not load plugin dlls during compilation steps or when we don't have a valid render + // context. + // They may try opengl access at some point and that would end in disaster during binary + // save steps. + if ((QT3DSU32)m_RenderContext->GetRenderContextType() != NVRenderContextValues::NullContext) { + library = CLoadedDynamicLibrary::Create(loadPath.c_str(), m_Foundation); + if (!library) { + // try loading it from the system instead of from this specific path. This means do + // not use any extensions or any special + // sauce. + loadPath = fname; +#ifdef _DEBUG + loadPath.append("d"); +#endif + library = CLoadedDynamicLibrary::Create(loadPath.c_str(), m_Foundation); + } + } + + if (library) { + TCreateRenderPluginClassFunction CreateClass = + reinterpret_cast<TCreateRenderPluginClassFunction>( + library->FindFunction("CreateRenderPlugin")); + if (CreateClass) { + newPluginClass = CreateClass(fname.c_str()); + if (newPluginClass.m_Class) { + // Check that the required functions are there. + if (newPluginClass.CreateInstance == NULL + || newPluginClass.QueryInstanceRenderSurface == NULL + || newPluginClass.RenderInstance == NULL + || newPluginClass.ReleaseInstance == NULL + || newPluginClass.ReleaseClass == NULL) { + if (newPluginClass.ReleaseClass) + newPluginClass.ReleaseClass(newPluginClass.m_Class); + qCCritical(INVALID_OPERATION, + "Failed to load render plugin: %s, required functions " + "missing. Required functions are:" + "CreateInstance, QueryInstanceRenderSurface, " + "RenderInstance, ReleaseInstance, ReleaseClass", + inRelativePath.c_str()); + NVDelete(m_Foundation.getAllocator(), library); + memSet(&newPluginClass, 0, sizeof(newPluginClass)); + } + } + } + } + if (newPluginClass.m_Class) { + PluginClassImpl *retval = QT3DS_NEW(m_Foundation.getAllocator(), PluginClassImpl)( + m_Foundation, m_StringTable, newPluginClass, + m_StringTable.RegisterStr(fname.c_str()), library); + + iter->second = retval; + if (newPluginClass.InitializeClassGLResources) { + m_RenderContext->PushPropertySet(); + newPluginClass.InitializeClassGLResources(newPluginClass.m_Class, loadPath.c_str()); + m_RenderContext->PopPropertySet(true); + } + return iter->second; + } + return NULL; + } + + void SetDllDir(const char *inDllDir) override { m_DllDir.assign(nonNull(inDllDir)); } + + IRenderPluginInstance *GetOrCreateRenderPluginInstance(CRegisteredString inRelativePath, + void *inKey) override + { + PluginInstanceKey theKey(inRelativePath, inKey); + TInstanceMap::iterator iter = m_Instances.find(theKey); + if (iter == m_Instances.end()) { + IRenderPluginClass *theClass = GetOrCreateRenderPlugin(inRelativePath); + NVScopedRefCounted<IRenderPluginInstance> theInstance; + if (theClass) + theInstance = theClass->CreateInstance(); + + iter = m_Instances.insert(eastl::make_pair(theKey, theInstance)).first; + } + return iter->second.mPtr; + } + + void Save(qt3ds::render::SWriteBuffer &ioBuffer, + const qt3ds::render::SStrRemapMap &inRemapMap, + const char8_t * /*inProjectDir*/) const override + { + QT3DSU32 numClasses = m_LoadedClasses.size(); + ioBuffer.write(numClasses); + for (TLoadedClassMap::const_iterator iter = m_LoadedClasses.begin(), + end = m_LoadedClasses.end(); + iter != end; ++iter) { + CRegisteredString saveStr = iter->first; + saveStr.Remap(inRemapMap); + ioBuffer.write(saveStr); + if (iter->second) { + NVConstDataRef<SRenderPluginPropertyDeclaration> theProperties = + const_cast<IRenderPluginClass &>((*iter->second)).GetRegisteredProperties(); + ioBuffer.write(theProperties.size()); + for (QT3DSU32 idx = 0, end = theProperties.size(); idx < end; ++idx) { + SRenderPluginPropertyDeclaration theDec(theProperties[idx]); + theDec.m_Name.Remap(inRemapMap); + ioBuffer.write(theDec); + } + } else + ioBuffer.write((QT3DSU32)0); + } + } + + void Load(NVDataRef<QT3DSU8> inData, CStrTableOrDataRef inStrDataBlock, + const char8_t * /*inProjectDir*/) override + { + qt3ds::render::SDataReader theReader(inData.begin(), inData.end()); + QT3DSU32 numClasses = theReader.LoadRef<QT3DSU32>(); + ForwardingAllocator alloc(m_Foundation.getAllocator(), "tempstrings"); + qt3ds::foundation::TStr workStr(alloc); + nvvector<SRenderPluginPropertyDeclaration> propertyBuffer(m_Foundation.getAllocator(), + "tempprops"); + for (QT3DSU32 classIdx = 0; classIdx < numClasses; ++classIdx) { + CRegisteredString classPath = theReader.LoadRef<CRegisteredString>(); + classPath.Remap(inStrDataBlock); + QT3DSU32 numProperties = theReader.LoadRef<QT3DSU32>(); + propertyBuffer.clear(); + for (QT3DSU32 propIdx = 0; propIdx < numProperties; ++propIdx) { + propertyBuffer.push_back(theReader.LoadRef<SRenderPluginPropertyDeclaration>()); + propertyBuffer.back().m_Name.Remap(inStrDataBlock); + } + m_LoadedPluginData.push_back(SLoadedPluginData()); + m_LoadedPluginData.back().m_PluginPath = classPath; + m_LoadedPluginData.back().m_Properties.assign(propertyBuffer.begin(), + propertyBuffer.end()); + } + } + IRenderPluginManager &GetRenderPluginManager(NVRenderContext &rc) override + { + m_RenderContext = rc; + for (QT3DSU32 idx = 0, end = m_LoadedPluginData.size(); idx < end; ++idx) { + // Now we can attempt to load the class. + IRenderPluginClass *theClass = + GetOrCreateRenderPlugin(m_LoadedPluginData[idx].m_PluginPath); + if (theClass) { + eastl::vector<SRenderPluginPropertyDeclaration> &propertyBuffer( + m_LoadedPluginData[idx].m_Properties); + for (QT3DSU32 propIdx = 0, propEnd = propertyBuffer.size(); propIdx < propEnd; + ++propIdx) { + theClass->RegisterProperty(propertyBuffer[propIdx]); + } + } + } + m_LoadedPluginData.clear(); + return *this; + } +}; +} + +IRenderPluginManagerCore &IRenderPluginManagerCore::Create(NVFoundationBase &inFoundation, + IStringTable &strTable, + IInputStreamFactory &inFactory) +{ + return *QT3DS_NEW(inFoundation.getAllocator(), PluginManagerImpl)(inFoundation, strTable, + inFactory); +} |