summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source/stateapplication/debugger
diff options
context:
space:
mode:
authorPasi Keränen <pasi.keranen@qt.io>2019-04-12 12:47:08 +0300
committerPasi Keränen <pasi.keranen@qt.io>2019-04-15 07:10:26 +0000
commitcef006904e875b7424b7fa13ea3dc2f9762081d9 (patch)
tree1e1cb4f9e58c9e692cf5c2f5fc5687db4a313d2a /src/Runtime/Source/stateapplication/debugger
parent46000b6fb0fca6594827875695c564cf5d7d070c (diff)
Flatten OpenGL source directory hierarchy
Flatten Source,Include and PrivateInclude directories away. Replace camelcase directory names with lowercase names. Remove random Qt3DS prefixes from directory names. Task-number: QT3DS-16 Change-Id: I5b23349232c760afa9a8efcc5e8945ee19d2e31a Reviewed-by: Pasi Keränen <pasi.keranen@qt.io>
Diffstat (limited to 'src/Runtime/Source/stateapplication/debugger')
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h111
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h375
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h190
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp345
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp81
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp534
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h102
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp302
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp312
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h386
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp322
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h925
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h540
-rw-r--r--src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h88
14 files changed, 4613 insertions, 0 deletions
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h
new file mode 100644
index 00000000..38a0b74f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebugger.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_SCENE_GRAPH_DEBUGGER_H
+#define QT3DS_SCENE_GRAPH_DEBUGGER_H
+#pragma once
+#include "Qt3DSStateDebugger.h"
+#include "Qt3DSUIADatamodel.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ struct SSGValue;
+
+ struct SSGPropertyChange;
+
+ struct SGElemIdMap
+ {
+ void *m_Elem;
+ const char *m_Id;
+ };
+
+ // Persistant item that sticks around and appends some information to the binary file.
+ // The runtime debugger must exist for the entire time the runtime is running, not just
+ // during connection because the mapping from element->id only exists at file loading time.
+ class ISceneGraphRuntimeDebugger : public NVRefCounted, public IDebugStreamListener
+ {
+ public:
+ static const char *GetProtocolName() { return "Scene Graph Debugger"; }
+ // Nothing is returned if the object isn't connected. The returned value may not be
+ // valid
+ // after next GetOrCreateCall, so don't hold on to it.
+ virtual void MapPresentationId(void *presentation, const char *id) = 0;
+ virtual void MapElementIds(void *presentation, NVConstDataRef<SGElemIdMap> inIds) = 0;
+ virtual void OnPropertyChanged(void *elem,
+ NVConstDataRef<SSGPropertyChange> changes) = 0;
+ virtual void OnConnection(IDebugOutStream &outStream) = 0;
+ virtual void BinarySave(IOutStream &stream) = 0;
+ // Load the main datastructures, although we know the ids are wrong
+ virtual void BinaryLoad(IInStream &stream, NVDataRef<QT3DSU8> inStringTableData) = 0;
+ // Remap the presentation element points using id to map old presentation ptr to new
+ // presentation ptr.
+ virtual void BinaryLoadPresentation(void *presentation, const char *id,
+ size_t inElemOffset) = 0;
+ virtual void EndFrame() = 0;
+ virtual bool IsConnected() = 0;
+
+ static ISceneGraphRuntimeDebugger &Create(NVFoundationBase &fnd,
+ IStringTable &strTable);
+ };
+
+ class ISceneGraphArchitectDebuggerListener
+ {
+ public:
+ virtual void OnItemsDirty(NVConstDataRef<app::SAppElement *> inDirtySet) = 0;
+ };
+
+ // The architect debugger only exists when debugging.
+ class ISceneGraphArchitectDebugger : public NVRefCounted, public IDebugStreamListener
+ {
+ public:
+ virtual void SetListener(ISceneGraphArchitectDebuggerListener *listener) = 0;
+ // Note that we wrap the att or arg list and the initial values to provide extra
+ // information.
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(app::SAppElement &elem) = 0;
+
+ // These may be empty, so don't expect them. Also they are all string, no registered
+ // strings
+ // regardless of the type.
+ virtual eastl::vector<app::SDatamodelValue>
+ GetElementAttributeValues(app::SAppElement &elem) = 0;
+ virtual app::IDatamodel &GetDatamodel() = 0;
+
+ virtual void AttachToStream(IDebugOutStream &inStream) = 0;
+
+ virtual void RefreshData(bool inNeedReloadData) = 0;
+
+ static ISceneGraphArchitectDebugger &Create(qt3ds::app::IDatamodel &inDatamodel);
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h
new file mode 100644
index 00000000..4fea4322
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerProtocol.h
@@ -0,0 +1,375 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_SCENE_GRAPH_DEBUGGER_PROTOCOL_H
+#define QT3DS_SCENE_GRAPH_DEBUGGER_PROTOCOL_H
+#include "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSSceneGraphDebuggerValue.h"
+#include "foundation/Qt3DSMemoryBuffer.h"
+#include "foundation/SerializationTypes.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ // These are the datatypes we will communicate information with
+ static QT3DSU32 GetSceneGraphProtocolVersion() { return 1; }
+
+ struct SValueUpdate
+ {
+ QT3DSI32 m_Hash;
+ SSGValue m_Value;
+ SValueUpdate(QT3DSI32 h, const SSGValue &v)
+ : m_Hash(h)
+ , m_Value(v)
+ {
+ }
+ SValueUpdate()
+ : m_Hash(0)
+ {
+ }
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Hash);
+ inListener.Handle(m_Value);
+ };
+ };
+
+ struct SElemUpdate
+ {
+ QT3DSU64 m_Elem;
+ NVDataRef<SValueUpdate> m_Updates;
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Elem);
+ inListener.HandleRef(m_Updates);
+ };
+ };
+
+ struct SElemMap
+ {
+ QT3DSU64 m_Elem;
+ CRegisteredString m_Id;
+ SElemMap()
+ : m_Elem(0)
+ {
+ }
+ SElemMap(QT3DSU64 ptr, CRegisteredString name)
+ : m_Elem(ptr)
+ , m_Id(name)
+ {
+ }
+
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Elem);
+ inListener.Handle(m_Id);
+ }
+ };
+
+ struct SIdUpdate
+ {
+ QT3DSU64 m_Presentation;
+ CRegisteredString m_PresentationId;
+ NVDataRef<SElemMap> m_IdUpdates;
+
+ template <typename Listener>
+ void IterateProperties(Listener &inListener)
+ {
+ inListener.Handle(m_Presentation);
+ inListener.Handle(m_PresentationId);
+ inListener.HandleRef(m_IdUpdates);
+ }
+ };
+
+ struct SSGProtocolMessageTypes
+ {
+ enum Enum {
+ UnknownMessage = 0,
+ Initialization,
+ IdUpdate,
+ ElemUpdate,
+ Frame,
+ };
+ };
+
+ // Implemented on runtime side.
+ struct SSGProtocolWriter
+ {
+ IOutStream &m_Stream;
+ MemoryBuffer<> m_WriteBuffer;
+ QT3DSU32 m_HighWaterMark;
+
+ SSGProtocolWriter(IOutStream &s, NVAllocatorCallback &alloc, QT3DSU32 highWaterMark = 4096)
+ : m_Stream(s)
+ , m_WriteBuffer(ForwardingAllocator(alloc, "WriteBuffer"))
+ , m_HighWaterMark(highWaterMark)
+ {
+ }
+
+ void Handle(QT3DSU64 data) { m_WriteBuffer.write(data); }
+
+ void Handle(QT3DSI32 data) { m_WriteBuffer.write(data); }
+
+ void Handle(CRegisteredString str)
+ {
+ QT3DSU32 len = static_cast<QT3DSU32>(strlen(str.c_str()) + 1);
+ m_WriteBuffer.write(len);
+ m_WriteBuffer.write(str.c_str(), len);
+ }
+ void Handle(SSGValue &value)
+ {
+ QT3DSU32 valType = static_cast<QT3DSU32>(value.getType());
+ m_WriteBuffer.write(valType);
+ switch (value.getType()) {
+ case SGPropertyValueTypes::Float:
+ m_WriteBuffer.write(value.getData<float>());
+ break;
+ case SGPropertyValueTypes::I32:
+ m_WriteBuffer.write(value.getData<QT3DSI32>());
+ break;
+ case SGPropertyValueTypes::String:
+ Handle(value.getData<CRegisteredString>());
+ break;
+ case SGPropertyValueTypes::Elem:
+ Handle(value.getData<QT3DSU64>());
+ break;
+ case SGPropertyValueTypes::NoSGValue:
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ template <typename TDataType>
+ void HandleRef(NVDataRef<TDataType> &ref)
+ {
+ m_WriteBuffer.write(ref.size());
+ for (QT3DSU32 idx = 0, end = ref.size(); idx < end; ++idx)
+ Handle(ref[idx]);
+ }
+
+ template <typename TDataType>
+ void Handle(TDataType &dtype)
+ {
+ dtype.IterateProperties(*this);
+ }
+
+ void Flush()
+ {
+ if (m_WriteBuffer.size()) {
+ NVConstDataRef<QT3DSU8> writeData(m_WriteBuffer);
+ m_Stream.Write(writeData);
+ m_WriteBuffer.clear();
+ }
+ }
+
+ void CheckBuffer()
+ {
+ if (m_WriteBuffer.size() > m_HighWaterMark)
+ Flush();
+ }
+
+ void Write(SIdUpdate &inIdUpdate)
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::IdUpdate);
+ inIdUpdate.IterateProperties(*this);
+ Flush();
+ }
+
+ void Write(SElemUpdate &inIdUpdate)
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::ElemUpdate);
+ inIdUpdate.IterateProperties(*this);
+ CheckBuffer();
+ }
+
+ void WriteInitialization()
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::Initialization);
+ m_WriteBuffer.write(GetSceneGraphProtocolVersion());
+ Flush();
+ }
+ void WriteFrame()
+ {
+ m_WriteBuffer.write((QT3DSU32)SSGProtocolMessageTypes::Frame);
+ Flush();
+ }
+ };
+
+ struct SSGProtocolReader
+ {
+ NVConstDataRef<QT3DSU8> m_Message;
+ SDataReader m_Reader;
+ IStringTable &m_StringTable;
+ eastl::vector<QT3DSU8> m_DataBuffer;
+ eastl::string m_TempString;
+ QT3DSU32 m_Allocated;
+ bool m_RestartRead;
+ SSGProtocolReader(NVConstDataRef<QT3DSU8> msg, IStringTable &strTable)
+ : m_Message(msg)
+ , m_Reader(const_cast<QT3DSU8 *>(msg.begin()), const_cast<QT3DSU8 *>(msg.end()))
+ , m_StringTable(strTable)
+ , m_Allocated(0)
+ , m_RestartRead(false)
+ {
+ }
+
+ SSGProtocolMessageTypes::Enum MessageType()
+ {
+ QT3DSU32 data = m_Reader.LoadRef<QT3DSU32>();
+ return static_cast<SSGProtocolMessageTypes::Enum>(data);
+ }
+
+ template <typename TDataType>
+ Option<NVDataRef<TDataType>> AllocateData(size_t size)
+ {
+ if (m_RestartRead)
+ return Empty();
+ if (size == 0)
+ return NVDataRef<TDataType>();
+
+ QT3DSU32 current = m_Allocated;
+ QT3DSU32 newAlloc = (QT3DSU32)(size * sizeof(TDataType));
+ // 8 byte align
+ if (newAlloc % 8)
+ newAlloc += 8 - (newAlloc % 8);
+
+ QT3DSU32 required = current + newAlloc;
+
+ if (required > m_DataBuffer.size()) {
+ m_RestartRead = true;
+ m_DataBuffer.resize(required * 2);
+ return Empty();
+ }
+ TDataType *offset = reinterpret_cast<TDataType *>(&m_DataBuffer[current]);
+ m_Allocated += newAlloc;
+ return toDataRef(offset, (QT3DSU32)size);
+ }
+
+ void Handle(QT3DSU64 &data) { data = m_Reader.LoadRef<QT3DSU64>(); }
+
+ void Handle(QT3DSI32 &data) { data = m_Reader.LoadRef<QT3DSI32>(); }
+
+ void Handle(CRegisteredString &str)
+ {
+ QT3DSU32 len = m_Reader.LoadRef<QT3DSU32>();
+ m_TempString.clear();
+ if (len)
+ m_TempString.assign((const char *)m_Reader.m_CurrentPtr, (size_t)(len - 1));
+ m_Reader.m_CurrentPtr += len;
+ if (m_Reader.m_CurrentPtr > m_Reader.m_EndPtr)
+ m_Reader.m_CurrentPtr = m_Reader.m_EndPtr;
+
+ str = m_StringTable.RegisterStr(m_TempString.c_str());
+ }
+
+ void Handle(SSGValue &value)
+ {
+ QT3DSU32 valType = m_Reader.LoadRef<QT3DSU32>();
+ switch (valType) {
+ case SGPropertyValueTypes::Float:
+ value = SSGValue(m_Reader.LoadRef<float>());
+ break;
+ case SGPropertyValueTypes::I32:
+ value = SSGValue(m_Reader.LoadRef<QT3DSI32>());
+ break;
+ case SGPropertyValueTypes::String: {
+ CRegisteredString temp;
+ Handle(temp);
+ value = SSGValue(temp);
+ } break;
+ case SGPropertyValueTypes::Elem:
+ value = m_Reader.LoadRef<QT3DSU64>();
+ break;
+ case SGPropertyValueTypes::NoSGValue:
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ template <typename TDataType>
+ void HandleRef(NVDataRef<TDataType> &ref)
+ {
+ QT3DSU32 numItems = m_Reader.LoadRef<QT3DSU32>();
+ Option<NVDataRef<TDataType>> refOpt = AllocateData<TDataType>(numItems);
+ if (refOpt.hasValue()) {
+ ref = *refOpt;
+ for (QT3DSU32 idx = 0, end = ref.size(); idx < end && m_RestartRead == false;
+ ++idx)
+ Handle(ref[idx]);
+ }
+ }
+
+ template <typename TDataType>
+ void Handle(TDataType &dtype)
+ {
+ dtype.IterateProperties(*this);
+ }
+
+ template <typename TDataType>
+ void DoRead(TDataType &ioValue)
+ {
+ QT3DSU8 *startPtr = m_Reader.m_CurrentPtr;
+ QT3DSU32 restartCount = 0;
+ do {
+ m_RestartRead = false;
+ m_Allocated = 0;
+ m_Reader.m_CurrentPtr = startPtr;
+ ioValue.IterateProperties(*this);
+ ++restartCount;
+ } while (m_RestartRead);
+ }
+
+ SIdUpdate ReadIdUpdate()
+ {
+ SIdUpdate retval;
+ DoRead(retval);
+ return retval;
+ };
+
+ SElemUpdate ReadElemUpdate()
+ {
+ SElemUpdate retval;
+ DoRead(retval);
+ return retval;
+ };
+
+ QT3DSU32 ReadInitialization() { return m_Reader.LoadRef<QT3DSU32>(); }
+
+ bool Finished() { return m_Reader.m_CurrentPtr >= m_Reader.m_EndPtr; }
+ };
+ }
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h
new file mode 100644
index 00000000..3b6a4172
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphDebuggerValue.h
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_SCENE_GRAPH_DEBUGGER_VALUE_H
+#define QT3DS_SCENE_GRAPH_DEBUGGER_VALUE_H
+#include "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSUIADatamodel.h"
+#include "Qt3DSUIADatamodelValue.h"
+#include "Qt3DSStateEditorValue.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+ struct SGPropertyValueTypes
+ {
+ enum Enum {
+ NoSGValue = 0,
+ Float,
+ I32,
+ String,
+ Elem,
+ };
+ };
+
+ template <typename dtype>
+ struct SGValueTypeMap
+ {
+ };
+
+ template <>
+ struct SGValueTypeMap<float>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::Float; }
+ };
+
+ template <>
+ struct SGValueTypeMap<QT3DSI32>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::I32; }
+ };
+
+ template <>
+ struct SGValueTypeMap<CRegisteredString>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::String; }
+ };
+
+ template <>
+ struct SGValueTypeMap<QT3DSU64>
+ {
+ static SGPropertyValueTypes::Enum GetType() { return SGPropertyValueTypes::Elem; }
+ };
+
+ struct SSGValueUnionTraits
+ {
+ typedef SGPropertyValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(QT3DSU64),
+ };
+
+ static TIdType getNoDataId() { return SGPropertyValueTypes::NoSGValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SGValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case SGPropertyValueTypes::Float:
+ return inVisitor(*NVUnionCast<float *>(inData));
+ case SGPropertyValueTypes::I32:
+ return inVisitor(*NVUnionCast<QT3DSI32 *>(inData));
+ case SGPropertyValueTypes::String:
+ return inVisitor(*NVUnionCast<CRegisteredString *>(inData));
+ case SGPropertyValueTypes::Elem:
+ return inVisitor(*NVUnionCast<QT3DSU64 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case SGPropertyValueTypes::NoSGValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case SGPropertyValueTypes::Float:
+ return inVisitor(*NVUnionCast<const float *>(inData));
+ case SGPropertyValueTypes::I32:
+ return inVisitor(*NVUnionCast<const QT3DSI32 *>(inData));
+ case SGPropertyValueTypes::String:
+ return inVisitor(*NVUnionCast<const CRegisteredString *>(inData));
+ case SGPropertyValueTypes::Elem:
+ return inVisitor(*NVUnionCast<const QT3DSU64 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case SGPropertyValueTypes::NoSGValue:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SSGValueUnionTraits,
+ SSGValueUnionTraits::TBufferSize>,
+ SSGValueUnionTraits::TBufferSize>
+ TSGValueUnionType;
+
+ struct SSGValue : public TSGValueUnionType
+ {
+ SSGValue() {}
+ SSGValue(const SSGValue &other)
+ : TSGValueUnionType(static_cast<const TSGValueUnionType &>(other))
+ {
+ }
+ SSGValue(float val)
+ : TSGValueUnionType(val)
+ {
+ }
+ SSGValue(QT3DSI32 val)
+ : TSGValueUnionType(val)
+ {
+ }
+ SSGValue(CRegisteredString val)
+ : TSGValueUnionType(val)
+ {
+ }
+ SSGValue(QT3DSU64 val)
+ : TSGValueUnionType(val)
+ {
+ }
+
+ SSGValue &operator=(const SSGValue &other)
+ {
+ TSGValueUnionType::operator=(static_cast<const TSGValueUnionType &>(other));
+ return *this;
+ }
+ };
+
+ struct SSGPropertyChange
+ {
+ QT3DSI32 m_Hash;
+ SSGValue m_Value;
+ SSGPropertyChange(QT3DSI32 h, const SSGValue &v)
+ : m_Hash(h)
+ , m_Value(v)
+ {
+ }
+ SSGPropertyChange()
+ : m_Hash(0)
+ {
+ }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp
new file mode 100644
index 00000000..403fb580
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSSceneGraphRuntimeDebugger.cpp
@@ -0,0 +1,345 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSSceneGraphDebuggerValue.h"
+#include "Qt3DSSceneGraphDebuggerProtocol.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/StringTable.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "EASTL/sort.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+typedef eastl::pair<QT3DSU64, CStringHandle> TElemStringHandlePair;
+
+struct SRegisteredIDComparator
+{
+ bool operator()(const TElemStringHandlePair &lhs, const TElemStringHandlePair &rhs) const
+ {
+ return lhs.first < rhs.first;
+ }
+};
+
+struct SPresentationIdMap
+{
+ void *m_Presentation;
+ CRegisteredString m_PresentationId;
+ nvvector<TElemStringHandlePair> m_RegisteredIdBackingStore;
+ NVDataRef<TElemStringHandlePair> m_RegisteredIds;
+ SPresentationIdMap(NVAllocatorCallback &alloc)
+ : m_RegisteredIdBackingStore(alloc, "registered ids")
+ {
+ }
+};
+
+struct RuntimeDebuggerImpl : public ISceneGraphRuntimeDebugger
+{
+ NVFoundationBase &m_Foundation;
+ IStringTable &m_StringTable;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ nvvector<SElemMap> m_ElemMapBuffer;
+ nvvector<SPresentationIdMap *> m_PresentationIdMap;
+ nvvector<SValueUpdate> m_ValueUpdates;
+ // Filter mechanism so we don't send the same thing twice.
+ nvhash_map<void *, eastl::vector<SValueUpdate>> m_LastUpdates;
+ SSGProtocolWriter *m_Writer;
+ SSAutoDeallocatorAllocator m_DataAllocator;
+ nvhash_map<void *, CStringHandle> m_ElemToNameMap;
+ QT3DSI32 mRefCount;
+
+ RuntimeDebuggerImpl(NVFoundationBase &fnd, IStringTable &strt)
+ : m_Foundation(fnd)
+ , m_StringTable(strt)
+ , m_ElemMapBuffer(fnd.getAllocator(), "elem map buffer")
+ , m_PresentationIdMap(fnd.getAllocator(), "Presentations")
+ , m_ValueUpdates(fnd.getAllocator(), "Value updates")
+ , m_LastUpdates(fnd.getAllocator(), "Last updates")
+ , m_Writer(NULL)
+ , m_DataAllocator(fnd)
+ , m_ElemToNameMap(fnd.getAllocator(), "ElemToNameMap")
+ , mRefCount(0)
+ {
+ }
+
+ ~RuntimeDebuggerImpl()
+ {
+ Disconnect();
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx)
+ delete m_PresentationIdMap[idx];
+ // the auto-deallocator takes care of the load data.
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ void Disconnect()
+ {
+ if (m_Writer)
+ delete m_Writer;
+ m_Writer = NULL;
+ }
+
+ bool CheckConnection()
+ {
+ if (m_OutStream) {
+ bool connected = m_OutStream->Connected();
+ if (!connected)
+ Disconnect();
+
+ return connected;
+ }
+ return false;
+ }
+
+ void MapPresentationId(void *presentation, const char *id) override
+ {
+ CRegisteredString newId = m_StringTable.RegisterStr(nonNull(id));
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx) {
+ if (m_PresentationIdMap[idx]->m_Presentation == presentation) {
+ m_PresentationIdMap[idx]->m_PresentationId = newId;
+ return;
+ }
+ }
+
+ SPresentationIdMap *map = new SPresentationIdMap(m_Foundation.getAllocator());
+ map->m_Presentation = presentation;
+ map->m_PresentationId = newId;
+ m_PresentationIdMap.push_back(map);
+ }
+
+ void SendElemIdMap(SPresentationIdMap &map)
+ {
+ if (m_ElemMapBuffer.size() && CheckConnection()) {
+ SIdUpdate theUpdate;
+ theUpdate.m_Presentation = (QT3DSU64)map.m_Presentation;
+ theUpdate.m_PresentationId = map.m_PresentationId;
+ theUpdate.m_IdUpdates = m_ElemMapBuffer;
+ m_Writer->Write(theUpdate);
+ }
+ m_ElemMapBuffer.clear();
+ }
+
+ void MapElementIds(void *presentation, NVConstDataRef<SGElemIdMap> inIds) override
+ {
+ SPresentationIdMap *map = NULL;
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end && map == NULL; ++idx)
+ if (m_PresentationIdMap[idx]->m_Presentation == presentation)
+ map = m_PresentationIdMap[idx];
+ if (map == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ m_ElemMapBuffer.clear();
+ m_ElemToNameMap.clear();
+ for (QT3DSU32 idx = 0, end = inIds.size(); idx < end; ++idx) {
+ const SGElemIdMap &item(inIds[idx]);
+ SElemMap newMap;
+ newMap.m_Elem = (QT3DSU64)item.m_Elem;
+ CStringHandle idHandle = m_StringTable.GetHandle(nonNull(item.m_Id));
+ newMap.m_Id = m_StringTable.HandleToStr(idHandle);
+ m_ElemMapBuffer.push_back(newMap);
+ m_ElemToNameMap[item.m_Elem] = idHandle;
+ }
+ SendElemIdMap(*map);
+
+ // store them for later.
+ map->m_RegisteredIdBackingStore.reserve(m_ElemToNameMap.size());
+ for (nvhash_map<void *, CStringHandle>::iterator iter = m_ElemToNameMap.begin(),
+ end = m_ElemToNameMap.end();
+ iter != end; ++iter) {
+ map->m_RegisteredIdBackingStore.push_back(
+ eastl::make_pair((QT3DSU64)iter->first, iter->second));
+ }
+
+ eastl::sort(map->m_RegisteredIdBackingStore.begin(), map->m_RegisteredIdBackingStore.end(),
+ SRegisteredIDComparator());
+
+ map->m_RegisteredIds = NVDataRef<TElemStringHandlePair>(
+ map->m_RegisteredIdBackingStore.data(), (QT3DSU32)map->m_RegisteredIdBackingStore.size());
+ }
+
+ static bool Equals(const SValueUpdate &lhs, const SValueUpdate &rhs)
+ {
+ return lhs.m_Hash == rhs.m_Hash && lhs.m_Value == rhs.m_Value;
+ }
+
+ void OnPropertyChanged(void *elem, NVConstDataRef<SSGPropertyChange> changes) override
+ {
+ if (CheckConnection() == false)
+ return;
+ eastl::vector<SValueUpdate> &updates = m_LastUpdates[elem];
+ updates.resize(changes.size());
+ for (QT3DSU32 changeIdx = 0, changeEnd = changes.size(); changeIdx < changeEnd; ++changeIdx) {
+ SValueUpdate theUpdate;
+ theUpdate.m_Hash = changes[changeIdx].m_Hash;
+ theUpdate.m_Value = changes[changeIdx].m_Value;
+ if (Equals(theUpdate, updates[changeIdx]) == false) {
+ updates[changeIdx] = theUpdate;
+ m_ValueUpdates.push_back(theUpdate);
+ }
+ }
+ if (m_ValueUpdates.size()) {
+ SElemUpdate theUpdate;
+ theUpdate.m_Elem = (QT3DSU64)elem;
+ theUpdate.m_Updates = m_ValueUpdates;
+ m_Writer->Write(theUpdate);
+ m_ValueUpdates.clear();
+ }
+ }
+
+ void SendPresentation(SPresentationIdMap &pres)
+ {
+ if (m_OutStream) {
+ m_ElemMapBuffer.clear();
+ for (TElemStringHandlePair *iter = pres.m_RegisteredIds.begin(),
+ *end = pres.m_RegisteredIds.end();
+ iter != end; ++iter) {
+ SElemMap newMap;
+ newMap.m_Elem = (QT3DSU64)iter->first;
+ newMap.m_Id = m_StringTable.HandleToStr(iter->second);
+ m_ElemMapBuffer.push_back(newMap);
+ }
+ SendElemIdMap(pres);
+ }
+ }
+
+ void OnConnection(IDebugOutStream &outStream) override
+ {
+ Disconnect();
+ m_OutStream = outStream;
+ m_Writer = new SSGProtocolWriter(outStream, m_Foundation.getAllocator());
+ m_Writer->WriteInitialization();
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx) {
+ SPresentationIdMap *map = m_PresentationIdMap[idx];
+ SendPresentation(*map);
+ }
+ }
+
+ bool IsConnected() override { return CheckConnection(); }
+
+ void BinarySave(IOutStream &ioStream) override
+ {
+ qt3ds::foundation::SWriteBuffer theWriteBuffer(m_Foundation.getAllocator(),
+ "BinarySave::writebuffer");
+
+ theWriteBuffer.writeZeros(4); // Overall binary size
+ theWriteBuffer.write((QT3DSU32)m_PresentationIdMap.size());
+ for (QT3DSU32 idx = 0, end = m_PresentationIdMap.size(); idx < end; ++idx) {
+ // There is no use to writing out the presentation pointer address.
+ /*
+ void*
+ m_Presentation;
+ CRegisteredString m_PresentationId;
+ nvhash_map<void*, CRegisteredString> m_RegisteredIds;
+ */
+
+ SPresentationIdMap *map = m_PresentationIdMap[idx];
+ CRegisteredString presId(map->m_PresentationId);
+ presId.Remap(m_StringTable.GetRemapMap());
+ theWriteBuffer.write(presId);
+ theWriteBuffer.write((QT3DSU32)map->m_RegisteredIds.size());
+ theWriteBuffer.write(map->m_RegisteredIds.begin(), map->m_RegisteredIds.size());
+ }
+
+ QT3DSU32 totalSize = theWriteBuffer.size();
+ QT3DSU32 *data = (QT3DSU32 *)theWriteBuffer.begin();
+ data[0] = totalSize - 4;
+ ioStream.Write((QT3DSU8 *)data, totalSize);
+ }
+
+ void BinaryLoad(IInStream &ioStream, NVDataRef<QT3DSU8> strTableData) override
+ {
+ QT3DSU32 length;
+ ioStream.Read(length);
+ QT3DSU8 *data = (QT3DSU8 *)m_DataAllocator.allocate(length, "Binaryload", __FILE__, __LINE__);
+ ioStream.Read(data, length);
+ SDataReader theReader(data, data + length);
+ QT3DSU32 numPresentations = theReader.LoadRef<QT3DSU32>();
+ QT3DS_ASSERT(m_PresentationIdMap.size() == 0);
+ m_PresentationIdMap.resize(numPresentations);
+ for (QT3DSU32 idx = 0, end = numPresentations; idx < end; ++idx) {
+ m_PresentationIdMap[idx] = new SPresentationIdMap(m_Foundation.getAllocator());
+ SPresentationIdMap *map = m_PresentationIdMap[idx];
+ map->m_PresentationId = theReader.LoadRef<CRegisteredString>();
+ map->m_PresentationId.Remap(strTableData);
+ QT3DSU32 numElems = theReader.LoadRef<QT3DSU32>();
+ map->m_RegisteredIds =
+ toDataRef((TElemStringHandlePair *)theReader.m_CurrentPtr, numElems);
+ theReader.m_CurrentPtr += numElems * sizeof(TElemStringHandlePair);
+ }
+ }
+
+ void BinaryLoadPresentation(void *presPtr, const char *id, size_t elemOffset) override
+ {
+ CRegisteredString presId(m_StringTable.RegisterStr(id));
+ SPresentationIdMap *foundPres = NULL;
+ for (QT3DSU32 idx = 0, end = (QT3DSU32)m_PresentationIdMap.size(); idx < end && foundPres == NULL;
+ ++idx) {
+ if (m_PresentationIdMap[idx]->m_PresentationId == presId)
+ foundPres = m_PresentationIdMap[idx];
+ }
+ if (foundPres == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ foundPres->m_Presentation = presPtr;
+ nvvector<eastl::pair<void *, CRegisteredString>> newElemIds(m_Foundation.getAllocator(),
+ "Temp load map");
+ newElemIds.reserve(foundPres->m_RegisteredIds.size());
+ for (TElemStringHandlePair *iter = foundPres->m_RegisteredIds.begin(),
+ *end = foundPres->m_RegisteredIds.end();
+ iter != end; ++iter) {
+ size_t oldId = (size_t)iter->first;
+ size_t newId = elemOffset + oldId;
+ iter->first = (QT3DSU64)newId;
+ }
+ SendPresentation(*foundPres);
+ }
+
+ void EndFrame() override
+ {
+ if (CheckConnection())
+ m_Writer->WriteFrame();
+ }
+
+ void OnMessageReceived(const SDebugStreamMessage &) override { QT3DS_ASSERT(false); }
+};
+}
+
+ISceneGraphRuntimeDebugger &ISceneGraphRuntimeDebugger::Create(NVFoundationBase &fnd,
+ IStringTable &strTable)
+{
+ return *QT3DS_NEW(fnd.getAllocator(), RuntimeDebuggerImpl)(fnd, strTable);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp
new file mode 100644
index 00000000..ced1397b
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDataTest.cpp
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "Qt3DSStateTest.h"
+#include "foundation/IOStreams.h"
+#include "EASTL/string.h"
+#include "foundation/Utils.h"
+#include "foundation/FileTools.h"
+#include "foundation/XML.h"
+#include "foundation/Qt3DSAllocator.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/TrackingAllocator.h"
+#include "foundation/StringTable.h"
+#include "Qt3DSStateContext.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "Qt3DSStateExecutionContext.h"
+#include "Qt3DSStateInterpreter.h"
+
+using namespace qt3ds::state::test;
+using namespace qt3ds::state;
+
+namespace {
+
+struct XMLHandler : public qt3ds::foundation::CXmlErrorHandler
+{
+ IDataLogger &m_Logger;
+ const char8_t *m_File;
+ eastl::string m_ErrorString;
+ XMLHandler(IDataLogger &logger, const char8_t *fname)
+ : m_Logger(logger)
+ , m_File(fname)
+ {
+ }
+
+ void OnXmlError(qt3ds::foundation::TXMLCharPtr errorName, int line, int /*column*/) override
+ {
+ m_ErrorString.assign("Failed to parse test file: ");
+ m_ErrorString.append(m_File);
+ m_Logger.Log(LogType::Error, m_File, line, errorName);
+ }
+};
+
+Option<STestResults> RunTest(const char8_t *inFullPath, const char8_t *inRoot,
+ IDataLogger &inLogger)
+{
+ return STestResults(1, 1);
+}
+}
+
+Option<STestResults> IDataTest::RunFile(const char8_t *fname, const char8_t *inRootDir,
+ IDataLogger &inLogger)
+{
+ return RunTest(fname, inRootDir, inLogger);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp
new file mode 100644
index 00000000..707ea73e
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.cpp
@@ -0,0 +1,534 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "Qt3DSStateDebugStreams.h"
+#include "foundation/StringTable.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSFlags.h"
+#include "foundation/Qt3DSMutex.h"
+#include "foundation/Qt3DSSync.h"
+#include "foundation/Qt3DSMemoryBuffer.h"
+#include "EASTL/string.h"
+
+using namespace qt3ds;
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+struct MultiProtocolMessageTypes
+{
+ enum Enum {
+ UnknownMessageType = 0,
+ NewProtocol = 1,
+ ProtocolMessage = 1 << 2,
+ };
+};
+
+struct SMultiProtocolMessageFlags : public NVFlags<MultiProtocolMessageTypes::Enum, QT3DSU32>
+{
+ bool IsNewProtocol() { return this->operator&(MultiProtocolMessageTypes::NewProtocol); }
+ void SetNewProtocol(bool inValue)
+ {
+ this->clearOrSet(inValue, MultiProtocolMessageTypes::NewProtocol);
+ }
+
+ bool IsProtocolMessage() { return this->operator&(MultiProtocolMessageTypes::ProtocolMessage); }
+ void SetProtocolMessage(bool inValue)
+ {
+ this->clearOrSet(inValue, MultiProtocolMessageTypes::ProtocolMessage);
+ }
+};
+
+struct SMultiProtocolInitializer
+{
+ static QT3DSU16 GetCurrentMultiProtocolVersion() { return 1; }
+
+ QT3DSU64 m_TimeNumerator;
+ QT3DSU64 m_TimeDenominator;
+ QT3DSU32 m_ProtocolVersion;
+
+ SMultiProtocolInitializer()
+ : m_TimeNumerator(Time::sCounterFreq.mNumerator)
+ , m_TimeDenominator(Time::sCounterFreq.mDenominator)
+ , m_ProtocolVersion(GetCurrentMultiProtocolVersion())
+ {
+ }
+};
+
+struct SMultiProtocolMessageHeader
+{
+
+ SMultiProtocolMessageFlags m_Flags;
+ QT3DSU32 m_Size;
+ QT3DSU32 m_ProtocolId;
+ QT3DSU64 m_Timestamp;
+ SMultiProtocolMessageHeader(MultiProtocolMessageTypes::Enum inMessageType, QT3DSU32 size,
+ QT3DSU32 protocolId, QT3DSU64 timestamp)
+ : m_Size(size)
+ , m_ProtocolId(protocolId)
+ , m_Timestamp(timestamp)
+ {
+ m_Flags.clearOrSet(true, inMessageType);
+ }
+ SMultiProtocolMessageHeader() {}
+};
+
+struct IProtocolMessageHandler
+{
+protected:
+ virtual ~IProtocolMessageHandler() {}
+public:
+ virtual void OnMessageReceived(SDebugStreamMessage msgData) = 0;
+};
+
+struct IProtocolHandler
+{
+protected:
+ virtual ~IProtocolHandler() {}
+public:
+ virtual void OnNewProtocol(CRegisteredString inProtocolName) = 0;
+};
+
+struct SSharedStreamImpl : public NVRefCounted
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<SocketStream> m_Stream;
+ IOutStream &m_WriteStream;
+ SMultiProtocolInitializer m_Initializer;
+ eastl::hash_map<CRegisteredString, QT3DSU32> m_ProtocolIdMap;
+ eastl::hash_map<QT3DSU32, IProtocolMessageHandler *> m_MessageHandlers;
+ MemoryBuffer<> m_ReadBuffer;
+ IStringTable &m_StringTable;
+ IProtocolHandler *m_ProtocolHandler;
+ QT3DSU32 m_NextProtocolId;
+ QT3DSI32 mRefCount;
+
+ SSharedStreamImpl(NVFoundationBase &fnd, SocketStream &stream, IOutStream &writeStream,
+ IStringTable &strTable, IProtocolHandler &pHandler)
+ : m_Foundation(fnd)
+ , m_Stream(stream)
+ , m_WriteStream(writeStream)
+ , m_ReadBuffer(ForwardingAllocator(fnd.getAllocator(), "ReadBuffer"))
+ , m_StringTable(strTable)
+ , m_ProtocolHandler(&pHandler)
+ , m_NextProtocolId(1)
+ , mRefCount(0)
+ {
+ NVConstDataRef<QT3DSU8> msgData = toU8DataRef(m_Initializer);
+ bool streamValid = m_Stream->Write(msgData);
+ if (streamValid == false)
+ m_Stream = NULL;
+ }
+ ~SSharedStreamImpl() {}
+
+ bool Initialize()
+ {
+ if (m_Stream) {
+ NVDataRef<QT3DSU8> msgData = toU8DataRef(m_Initializer);
+ QT3DSU32 numBytes = m_Stream->Read(msgData);
+ if (numBytes != sizeof(SMultiProtocolInitializer)
+ || m_Initializer.m_ProtocolVersion
+ > SMultiProtocolInitializer::GetCurrentMultiProtocolVersion()) {
+ m_Stream = NULL;
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ QT3DSU32 GetIdForProtocol(CRegisteredString protocol)
+ {
+ if (protocol.IsValid() == false)
+ return 0;
+ eastl::pair<eastl::hash_map<CRegisteredString, QT3DSU32>::iterator, bool> inserter =
+ m_ProtocolIdMap.insert(eastl::make_pair(protocol, m_NextProtocolId));
+ if (inserter.second) {
+ QT3DSU32 newId = m_NextProtocolId;
+ ++m_NextProtocolId;
+ if (m_Stream) {
+ QT3DSU32 msgLen = (QT3DSU32)strlen(protocol) + 1;
+ NVConstDataRef<QT3DSU8> writeBuf(reinterpret_cast<const QT3DSU8 *>(protocol.c_str()),
+ msgLen);
+ WriteMessage(MultiProtocolMessageTypes::NewProtocol, writeBuf, newId);
+ }
+ }
+ return inserter.first->second;
+ }
+
+ CRegisteredString GetProtocolForId(QT3DSU32 id)
+ {
+ for (eastl::hash_map<CRegisteredString, QT3DSU32>::iterator iter = m_ProtocolIdMap.begin(),
+ end = m_ProtocolIdMap.end();
+ iter != end; ++iter) {
+ if (iter->second == id)
+ return iter->first;
+ }
+ return CRegisteredString();
+ }
+
+ void AddMessageHandler(CRegisteredString protocol, IProtocolMessageHandler &hdl)
+ {
+ m_MessageHandlers.insert(eastl::make_pair(GetIdForProtocol(protocol), &hdl));
+ }
+
+ void RemoveMessageHandler(CRegisteredString protocol)
+ {
+ m_MessageHandlers.erase(GetIdForProtocol(protocol));
+ }
+
+ IProtocolMessageHandler *GetMessageHandler(CRegisteredString protocol)
+ {
+ return GetMessageHandler(GetIdForProtocol(protocol));
+ }
+
+ IProtocolMessageHandler *GetMessageHandler(QT3DSU32 protocolId)
+ {
+ eastl::hash_map<QT3DSU32, IProtocolMessageHandler *>::iterator iter =
+ m_MessageHandlers.find(protocolId);
+ if (iter != m_MessageHandlers.end())
+ return iter->second;
+ return NULL;
+ }
+
+ void ProtocolHandlerLeaving() { m_ProtocolHandler = NULL; }
+
+ void DispatchMessage(SMultiProtocolMessageHeader inHeader, NVConstDataRef<QT3DSU8> msg)
+ {
+ if (inHeader.m_Flags.IsNewProtocol()) {
+ char *pId = reinterpret_cast<char *>(const_cast<QT3DSU8 *>(msg.begin()));
+ // Ensure null terminated, which should be done anyway but we don't know it will be.
+ pId[inHeader.m_Size] = 0;
+ CRegisteredString protocolName = m_StringTable.RegisterStr(pId);
+ eastl::pair<eastl::hash_map<CRegisteredString, QT3DSU32>::iterator, bool> inserter =
+ m_ProtocolIdMap.insert(eastl::make_pair(protocolName, inHeader.m_ProtocolId));
+ if (inserter.second == false) {
+ // remap id to higher id to reduce the chance of conflicts.
+ QT3DSU32 potentialNewId = NVMax(inserter.first->second, inHeader.m_ProtocolId);
+ if (potentialNewId != inserter.first->second) {
+ m_NextProtocolId = NVMax(m_NextProtocolId, potentialNewId + 1);
+ CRegisteredString existing = GetProtocolForId(potentialNewId);
+ if (existing.IsValid()) {
+ m_ProtocolIdMap.erase(protocolName);
+ GetIdForProtocol(protocolName);
+ return;
+ } else {
+ inserter.first->second = potentialNewId;
+ }
+ }
+ } else {
+ if (m_ProtocolHandler != NULL) {
+ m_ProtocolHandler->OnNewProtocol(protocolName);
+ }
+ }
+ } else {
+ IProtocolMessageHandler *handler = GetMessageHandler(inHeader.m_ProtocolId);
+ if (handler != NULL)
+ handler->OnMessageReceived(SDebugStreamMessage(inHeader.m_Timestamp, msg));
+ }
+ }
+
+ NVDataRef<QT3DSU8> ReadChunk(NVDataRef<QT3DSU8> target)
+ {
+ QT3DSU32 totalRead = 0;
+ do {
+ NVDataRef<QT3DSU8> nextBuf(target.begin() + totalRead, target.size() - totalRead);
+ QT3DSU32 readResult = m_Stream->Read(nextBuf);
+ totalRead += readResult;
+ if (totalRead < target.size()) {
+ totalRead = totalRead;
+ }
+ } while (connected() && totalRead < target.size());
+
+ return toDataRef(m_ReadBuffer.begin(), totalRead);
+ }
+
+ NVDataRef<QT3DSU8> ReadChunk(QT3DSU32 size)
+ {
+ m_ReadBuffer.reserve(size);
+ return ReadChunk(toDataRef(m_ReadBuffer.begin(), size));
+ }
+
+ virtual SDebugStreamMessage WaitForNextMessage(CRegisteredString protocol)
+ {
+ QT3DSU32 msgId = GetIdForProtocol(protocol);
+
+ while (m_Stream) {
+ SMultiProtocolMessageHeader header;
+ NVDataRef<QT3DSU8> buf = toU8DataRef(header);
+ buf = ReadChunk(buf);
+ if (buf.size() < sizeof(header)) {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+ } else {
+ NVDataRef<QT3DSU8> readResult = ReadChunk(header.m_Size);
+ if (readResult.mSize != header.m_Size) {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+ } else {
+ if (header.m_ProtocolId == msgId) {
+ SDebugStreamMessage message;
+ message.m_Timestamp = header.m_Timestamp;
+ message.m_Data = readResult;
+ return message;
+ } else
+ DispatchMessage(header, readResult);
+ }
+ }
+ }
+
+ return SDebugStreamMessage();
+ }
+
+ virtual void MessagePump()
+ {
+ if (m_Stream == NULL)
+ return;
+ bool lastMessage = true;
+ do {
+ SMultiProtocolMessageHeader header;
+ NVDataRef<QT3DSU8> buf = toU8DataRef(header);
+ QT3DSU32 amountRead = m_Stream->nonBlockingRead(buf);
+ if (amountRead == 0) {
+ if (m_Stream->connected() == false)
+ m_Stream = NULL;
+ lastMessage = false;
+ } else {
+ // read the rest of the header.
+ QT3DSU32 leftover = buf.size() - amountRead;
+ if (leftover) {
+ NVDataRef<QT3DSU8> nextPiece(buf.begin() + amountRead, leftover);
+ nextPiece = ReadChunk(nextPiece);
+ amountRead += nextPiece.size();
+ }
+
+ if (amountRead < sizeof(SMultiProtocolMessageHeader)) {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+
+ } else {
+ NVDataRef<QT3DSU8> msgData = ReadChunk(header.m_Size);
+ if (msgData.size() == header.m_Size) {
+ DispatchMessage(header, msgData);
+ } else {
+ m_Stream = NULL;
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ } while (lastMessage && m_Stream);
+ }
+
+ SMultiProtocolMessageHeader CreateHeader(MultiProtocolMessageTypes::Enum type, QT3DSU32 size,
+ QT3DSU32 protocolId)
+ {
+ SMultiProtocolMessageHeader retval;
+ retval.m_Flags.clearOrSet(true, type);
+ retval.m_ProtocolId = protocolId;
+ retval.m_Size = size;
+ retval.m_Timestamp = Time::getCurrentCounterValue();
+ return retval;
+ }
+
+ bool WriteMessage(MultiProtocolMessageTypes::Enum type, NVConstDataRef<QT3DSU8> data,
+ QT3DSU32 protocolId)
+ {
+ if (connected()) {
+ SMultiProtocolMessageHeader header(CreateHeader(type, data.size(), protocolId));
+ NVConstDataRef<QT3DSU8> writeBuf = toU8DataRef(header);
+ bool success = m_WriteStream.Write(writeBuf);
+ if (success) {
+ success = m_WriteStream.Write(data);
+ }
+ if (!success)
+ m_Stream = NULL;
+ return success;
+ }
+ return false;
+ }
+
+ virtual bool Write(CRegisteredString protocol, NVConstDataRef<QT3DSU8> data)
+ {
+ return WriteMessage(MultiProtocolMessageTypes::ProtocolMessage, data,
+ GetIdForProtocol(protocol));
+ }
+
+ bool connected() { return m_Stream != NULL && m_Stream->connected(); }
+};
+
+struct SMultiProtocolSocketStreamImpl : public IMultiProtocolSocketStream,
+ public IProtocolMessageHandler
+{
+ NVFoundationBase &m_Foundation;
+ CRegisteredString m_Protocol;
+ IDebugStreamListener *m_Listener;
+ NVScopedRefCounted<SSharedStreamImpl> m_SharedStream;
+ bool m_StreamValid;
+ QT3DSI32 mRefCount;
+
+ SMultiProtocolSocketStreamImpl(NVFoundationBase &fnd, CRegisteredString protocol,
+ IDebugStreamListener *listener, SSharedStreamImpl &stream)
+ : m_Foundation(fnd)
+ , m_Protocol(protocol)
+ , m_Listener(listener)
+ , m_SharedStream(stream)
+ , m_StreamValid(true)
+ , mRefCount(0)
+ {
+ m_SharedStream->AddMessageHandler(m_Protocol, *this);
+ }
+
+ ~SMultiProtocolSocketStreamImpl() { m_SharedStream->RemoveMessageHandler(m_Protocol); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ IDebugStreamListener *GetListener() override { return m_Listener; }
+ CRegisteredString GetProtocolName() override { return m_Protocol; }
+
+ void SetListener(IDebugStreamListener *listener) override { m_Listener = listener; }
+
+ bool Write(NVConstDataRef<QT3DSU8> data) override
+ {
+ if (m_StreamValid)
+ m_StreamValid = m_SharedStream->Write(m_Protocol, data);
+
+ return m_StreamValid;
+ }
+
+ SDebugStreamMessage WaitForNextMessage() override
+ {
+ if (m_StreamValid)
+ return m_SharedStream->WaitForNextMessage(m_Protocol);
+ return SDebugStreamMessage();
+ }
+
+ void OnMessageReceived(SDebugStreamMessage data) override
+ {
+ if (m_Listener)
+ m_Listener->OnMessageReceived(data);
+ }
+
+ bool Connected() override { return m_SharedStream->connected(); }
+};
+
+struct SMultiProtocolSocketImpl : public IMultiProtocolSocket, public IProtocolHandler
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<SSharedStreamImpl> m_SharedStream;
+ NVScopedRefCounted<IMultiProtocolSocketListener> m_ProtocolListener;
+ QT3DSI32 mRefCount;
+ SMultiProtocolSocketImpl(NVFoundationBase &fnd, SocketStream &inStream, IStringTable &strTable,
+ IMultiProtocolSocketListener *protocolListener)
+ : m_Foundation(fnd)
+ , m_ProtocolListener(protocolListener)
+ , mRefCount(0)
+ {
+ // At some point I may switch the writer to a buffered stream, at least on the client side.
+ m_SharedStream = QT3DS_NEW(m_Foundation.getAllocator(), SSharedStreamImpl)(
+ m_Foundation, inStream, inStream, strTable, *this);
+ }
+
+ ~SMultiProtocolSocketImpl() { m_SharedStream->ProtocolHandlerLeaving(); }
+
+ bool Initialize() override { return m_SharedStream->Initialize(); }
+
+ bool Connected() override { return m_SharedStream->connected(); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Foundation.getAllocator())
+
+ virtual NVScopedRefCounted<IMultiProtocolSocketStream>
+ CreateProtocol(const char *name, IDebugStreamListener *inListener) override
+ {
+ NVScopedRefCounted<IMultiProtocolSocketStream> retval = GetProtocol(name);
+ if (retval) {
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+ CRegisteredString protocolName = m_SharedStream->m_StringTable.RegisterStr(name);
+ if (protocolName.IsValid() == false) {
+ QT3DS_ASSERT(false);
+ return retval;
+ }
+ SMultiProtocolSocketStreamImpl *newStream =
+ QT3DS_NEW(m_Foundation.getAllocator(), SMultiProtocolSocketStreamImpl)(
+ m_Foundation, protocolName, inListener, *m_SharedStream);
+ return newStream;
+ }
+
+ NVScopedRefCounted<IMultiProtocolSocketStream> GetProtocol(const char *name) override
+ {
+ CRegisteredString protocolName = m_SharedStream->m_StringTable.RegisterStr(name);
+ IProtocolMessageHandler *handler = m_SharedStream->GetMessageHandler(protocolName);
+ if (handler) {
+ SMultiProtocolSocketStreamImpl *theImpl =
+ static_cast<SMultiProtocolSocketStreamImpl *>(handler);
+ return theImpl;
+ }
+ return NVScopedRefCounted<IMultiProtocolSocketStream>();
+ }
+
+ void OnNewProtocol(CRegisteredString inProtocolName) override
+ {
+ if (m_ProtocolListener) {
+ // We can expect the user to call create protocol at this point.
+ IDebugStreamListener *handler = m_ProtocolListener->OnNewProtocol(inProtocolName);
+ if (handler) {
+ SMultiProtocolSocketStreamImpl *newStream =
+ QT3DS_NEW(m_Foundation.getAllocator(), SMultiProtocolSocketStreamImpl)(
+ m_Foundation, inProtocolName, handler, *m_SharedStream);
+ m_ProtocolListener->OnNewProtocolStream(inProtocolName, *newStream);
+ }
+ }
+ }
+
+ CounterFrequencyToTensOfNanos SourceConversion() override
+ {
+ return CounterFrequencyToTensOfNanos(m_SharedStream->m_Initializer.m_TimeNumerator,
+ m_SharedStream->m_Initializer.m_TimeDenominator);
+ }
+
+ void MessagePump() override { m_SharedStream->MessagePump(); }
+};
+}
+
+NVScopedRefCounted<IMultiProtocolSocket>
+IMultiProtocolSocket::CreateProtocolSocket(NVFoundationBase &fnd, SocketStream &inStream,
+ IStringTable &strTable,
+ IMultiProtocolSocketListener *protocolListener)
+{
+ return QT3DS_NEW(fnd.getAllocator(), SMultiProtocolSocketImpl)(fnd, inStream, strTable,
+ protocolListener);
+}
+
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h
new file mode 100644
index 00000000..5bbc0b26
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugStreams.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUG_STREAMS_H
+#define QT3DS_STATE_DEBUG_STREAMS_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Socket.h"
+#include "foundation/Qt3DSTime.h"
+#include "Qt3DSStateDebugger.h"
+
+struct script_State;
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ class IMultiProtocolSocketStream : public IDebugOutStream
+ {
+ public:
+ virtual CRegisteredString GetProtocolName() = 0;
+ };
+
+ class IMultiProtocolSocketListener : public NVRefCounted
+ {
+ public:
+ // If a listener is returned from on new protocol, the system creates a stream
+ // with the returned listener
+ virtual IDebugStreamListener *OnNewProtocol(CRegisteredString inProtocolName) = 0;
+ // Created with the listener returned from OnNewProtocol.
+ virtual void OnNewProtocolStream(CRegisteredString inProtocolName,
+ IMultiProtocolSocketStream &inStream) = 0;
+ };
+
+ // Create a system of multiplexing multipler unrelated protocols
+ // through a single network socket.
+ class IMultiProtocolSocket : public NVRefCounted
+ {
+ public:
+ virtual bool Initialize() = 0;
+ virtual NVScopedRefCounted<IMultiProtocolSocketStream>
+ CreateProtocol(const char *name, IDebugStreamListener *inListener) = 0;
+ virtual NVScopedRefCounted<IMultiProtocolSocketStream>
+ GetProtocol(const char *name) = 0;
+ virtual bool Connected() = 0;
+
+ // Upon connection the multi protocol system does a handshake where both sides send
+ // their nanosecond
+ // conversions across. Note that the times sent on the multi protocol packets are in a
+ // system-specific 64 bit quantity that
+ // needs conversion to actual nanoseconds to be useful (identical to
+ // QueryHighPerformanceFrequency, QueryHighPerformanceCounter
+ virtual CounterFrequencyToTensOfNanos SourceConversion() = 0;
+
+ // Manually do a nonblocking check on the network socket for any new information and
+ // call the various stream listeners
+ // with information packets if found.
+ virtual void MessagePump() = 0;
+
+ static NVScopedRefCounted<IMultiProtocolSocket>
+ CreateProtocolSocket(NVFoundationBase &fnd, SocketStream &inStream,
+ IStringTable &strTable,
+ IMultiProtocolSocketListener *protocolListener);
+ };
+
+ class CProtocolNames
+ {
+ public:
+ static const char *getMobdebugProtocolName() { return "mobdebug"; }
+ static const char *getSCXMLProtocolName() { return "scxml"; }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp
new file mode 100644
index 00000000..a64e8589
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggedInterpreter.cpp
@@ -0,0 +1,302 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "EASTL/map.h"
+#include "EASTL/set.h"
+#include "foundation/Qt3DSAtomic.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+// Horrible name, I know.
+struct SDebuggedInterpreter : public IDebuggedInterpreter
+{
+ typedef eastl::set<TDebugStr> TDebugStrSet;
+ typedef eastl::vector<SBreakpoint> TBreakpointList;
+ typedef eastl::map<TDebugStr, SDatamodelValue> TDataModelTable;
+ typedef eastl::hash_map<SDatamodelTable *, TDataModelTable> TTableMap;
+ typedef eastl::vector<STableEntry> TTableEntryList;
+ // Null is the datamodel table.
+
+ NVAllocatorCallback &m_Allocator;
+ NVScopedRefCounted<IDebugOutStream> m_Stream;
+ QT3DSI32 mRefCount;
+ TEditorPtr m_Editor;
+ TDebugStr m_Filename;
+ SMessageSerializer m_Serializer;
+ QT3DSI32 m_StreamId;
+ TBreakpointList m_Breakpoints;
+ TDebugStrList m_StrList;
+ TDebugStrSet m_Configuration;
+ bool m_BreakOnMicrostep;
+ bool m_IsBroken;
+ Option<SBreakpoint> m_BrokenBreakpoint;
+ IDebuggerMasterListener &m_Listener;
+ TTableMap m_DatamodelValues;
+ mutable TTableEntryList m_TempEntries;
+ bool m_MicrostepHasData;
+ SMicrostepData m_MicrostepData;
+ TDebugStr m_MicrostepEvent;
+ TDebugStrList m_ConfigurationList;
+ TDebugStrList m_PreviousConfiguration;
+
+ SDebuggedInterpreter(NVAllocatorCallback &inAlloc, IDebugOutStream &inStream,
+ TEditorPtr inEditor, const TDebugStr &inFname, QT3DSI32 inStreamId,
+ NVConstDataRef<TDebugStr> inConfiguration,
+ IDebuggerMasterListener &inListener)
+ : m_Allocator(inAlloc)
+ , m_Stream(inStream)
+ , mRefCount(0)
+ , m_Editor(inEditor)
+ , m_Filename(inFname)
+ , m_StreamId(inStreamId)
+ , m_BreakOnMicrostep(false)
+ , m_IsBroken(false)
+ , m_Listener(inListener)
+ , m_MicrostepHasData(false)
+ {
+ m_ConfigurationList.assign(inConfiguration.begin(), inConfiguration.end());
+ m_Configuration.insert(inConfiguration.begin(), inConfiguration.end());
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+
+ // Get the editor that represents the state graph for this debugged interpreter.
+ TEditorPtr GetEditor() override { return m_Editor; }
+ TDebugStr GetFilename() const override { return m_Filename; }
+
+ template <typename TMessageType>
+ void SendMessage(const TMessageType &inMessage)
+ {
+ m_Serializer.Serialize(m_StreamId, inMessage, 0);
+ m_Stream->Write(m_Serializer.ToRawMessage());
+ }
+
+ // Used when connecting to a live session.
+ void SetBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ TBreakpointList::iterator iter =
+ eastl::find(m_Breakpoints.begin(), m_Breakpoints.end(), inBreakpoint);
+ if (iter == m_Breakpoints.end()) {
+ SendMessage(SSetBreakpoint(inBreakpoint));
+ m_Breakpoints.push_back(inBreakpoint);
+ }
+ }
+
+ void ClearBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ TBreakpointList::iterator iter =
+ eastl::find(m_Breakpoints.begin(), m_Breakpoints.end(), inBreakpoint);
+ if (iter != m_Breakpoints.end()) {
+ SendMessage(SClearBreakpoint(inBreakpoint));
+ m_Breakpoints.erase(iter);
+ }
+ }
+
+ NVConstDataRef<SBreakpoint> GetBreakpoints() override
+ {
+ return toDataRef(m_Breakpoints.data(), m_Breakpoints.size());
+ }
+
+ void ClearAllBreakpoints() override
+ {
+ SendMessage(SClearAllBreakpoints());
+ m_Breakpoints.clear();
+ }
+
+ // break at the *end* of the microstep so you can see the data of the microstep.
+ void SetBreakOnMicrostep(bool inBreak) override
+ {
+ if (m_BreakOnMicrostep != inBreak) {
+ m_BreakOnMicrostep = inBreak;
+ SendMessage(SBreakOnMicrostep(inBreak));
+ }
+ }
+ bool IsBreakOnMicrostep() const override { return m_BreakOnMicrostep; }
+
+ NVConstDataRef<STableEntry> TableToList(const TDataModelTable &inTable) const
+ {
+ m_TempEntries.resize(inTable.size());
+ QT3DSU32 idx = 0;
+ for (TDataModelTable::const_iterator iter = inTable.begin(), end = inTable.end();
+ iter != end; ++iter, ++idx)
+ m_TempEntries[idx] = STableEntry(iter->first, iter->second);
+ return toConstDataRef(m_TempEntries.data(), m_TempEntries.size());
+ }
+
+ NVConstDataRef<STableEntry> GetTableValues(SDatamodelTable *inTable) const override
+ {
+ TTableMap::const_iterator iter = m_DatamodelValues.find(inTable);
+ if (iter != m_DatamodelValues.end())
+ return TableToList(iter->second);
+ return NVConstDataRef<STableEntry>();
+ }
+
+ NVConstDataRef<TDebugStr> GetPreviousConfiguration() const override
+ {
+ return toConstDataRef(m_PreviousConfiguration.data(), m_PreviousConfiguration.size());
+ }
+ NVConstDataRef<TDebugStr> GetConfiguration() const override
+ {
+ return toConstDataRef(m_ConfigurationList.data(), m_ConfigurationList.size());
+ }
+ const TDebugStr &GetMicrostepEvent() const override { return m_MicrostepEvent; }
+ NVConstDataRef<STransitionId> GetMicrostepTransitions() const override
+ {
+ return toConstDataRef(m_MicrostepData.m_Transitions.data(),
+ m_MicrostepData.m_Transitions.size());
+ }
+ NVConstDataRef<TDebugStr> GetMicrostepEnterStates() const override
+ {
+ return toConstDataRef(m_MicrostepData.m_EnterStates.data(),
+ m_MicrostepData.m_EnterStates.size());
+ }
+ NVConstDataRef<TDebugStr> GetMicrostepExitStates() const override
+ {
+ return toConstDataRef(m_MicrostepData.m_ExitStates.data(),
+ m_MicrostepData.m_ExitStates.size());
+ }
+ void Continue() override
+ {
+ SendMessage(SContinue());
+ m_IsBroken = false;
+ m_BrokenBreakpoint = Option<SBreakpoint>();
+ }
+
+ void Disconnect() override
+ {
+ ClearAllBreakpoints();
+ SetBreakOnMicrostep(false);
+ if (m_IsBroken == true)
+ Continue();
+
+ SendMessage(SDisconnect());
+ }
+
+ void BreakpointHit(const SBreakpoint &inBreakpoint) override
+ {
+ m_BrokenBreakpoint = inBreakpoint;
+ m_IsBroken = true;
+ m_Listener.OnInterpreterBroken(*this, inBreakpoint);
+ }
+
+ void OnEventQueued(const SEventQueued &inMsg) override
+ {
+ m_Listener.OnEventQueued(inMsg.m_Event, inMsg.m_Internal);
+ }
+
+ void OnBeginStep(const SBeginStep &) override { m_Listener.OnBeginStep(*this); }
+
+ void OnBeginMicrostep(const SBeginMicrostep &) override
+ {
+ m_MicrostepEvent.clear();
+ m_MicrostepData.m_Transitions.clear();
+ m_MicrostepData.m_EnterStates.clear();
+ m_MicrostepData.m_ExitStates.clear();
+ m_MicrostepHasData = false;
+ m_Listener.OnBeginMicrostep(*this);
+ }
+
+ void OnMicrostepEvent(const SMicrostepEvent &inMsg) override
+ {
+ m_MicrostepEvent = inMsg.m_Event;
+ }
+
+ void OnMicrostepData(const SMicrostepData &inMsg) override
+ {
+ m_MicrostepData = inMsg;
+ m_MicrostepHasData = true;
+ }
+ void OnEndMicrostep(const SEndMicrostep & /*inMsg*/) override
+ {
+ if (m_MicrostepHasData == false && m_MicrostepEvent.empty() == false) {
+ m_Listener.OnEventProcessed(m_MicrostepEvent, false);
+ } else {
+ m_PreviousConfiguration = m_ConfigurationList;
+
+ for (TDebugStrList::const_iterator iter = m_MicrostepData.m_ExitStates.begin(),
+ end = m_MicrostepData.m_ExitStates.end();
+ iter != end; ++iter)
+ m_Configuration.erase(*iter);
+
+ m_Configuration.insert(m_MicrostepData.m_EnterStates.begin(),
+ m_MicrostepData.m_EnterStates.end());
+
+ m_ConfigurationList.clear();
+ m_ConfigurationList.insert(m_ConfigurationList.end(), m_Configuration.begin(),
+ m_Configuration.end());
+ if (m_BreakOnMicrostep) {
+ m_IsBroken = true;
+ m_Listener.OnInterpreterBroken(*this, Option<SBreakpoint>());
+ } else
+ m_Listener.OnMicrostep(*this);
+ }
+ }
+
+ void OnModifyTable(const SModifyTableValues &inValues) override
+ {
+ TTableMap::iterator iter =
+ m_DatamodelValues.insert(eastl::make_pair(inValues.m_TablePtr, TDataModelTable()))
+ .first;
+ for (QT3DSU32 idx = 0, end = inValues.m_Modifications.size(); idx < end; ++idx) {
+ const STableModification &theMod(inValues.m_Modifications[idx]);
+ switch (theMod.m_Type) {
+ case TableModificationType::RemoveKey:
+ iter->second.erase(theMod.m_Entry.m_Key);
+ break;
+ case TableModificationType::SetKey:
+ (iter->second)[theMod.m_Entry.m_Key] = theMod.m_Entry.m_Value;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ m_Listener.OnDatamodelChange(
+ *this, inValues.m_TablePtr,
+ toConstDataRef(inValues.m_Modifications.data(), inValues.m_Modifications.size()));
+ }
+
+ bool IsBroken() const override { return m_IsBroken; }
+};
+}
+
+IDebuggedInterpreter &IDebuggedInterpreter::Create(NVAllocatorCallback &inAlloc,
+ IDebugOutStream &inStream, TEditorPtr inEditor,
+ const TDebugStr &inFilename, QT3DSI32 inStreamId,
+ NVConstDataRef<TDebugStr> inConfiguration,
+ IDebuggerMasterListener &inListener)
+{
+ return *QT3DS_NEW(inAlloc, SDebuggedInterpreter)(inAlloc, inStream, inEditor, inFilename,
+ inStreamId, inConfiguration, inListener);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp
new file mode 100644
index 00000000..5917db60
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.cpp
@@ -0,0 +1,312 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerValues.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/IOStreams.h"
+
+using namespace qt3ds::state::debugger;
+using namespace qt3ds::state;
+
+namespace {
+
+static MallocAllocator g_MallocAlloc;
+
+struct SDebugger : public IDebugger
+{
+ typedef eastl::vector<NVScopedRefCounted<IStateMachineDebugInterface>> TDebugList;
+ typedef eastl::hash_map<IStateMachineDebugInterface *, QT3DSI32> TMachineIdMap;
+ typedef eastl::hash_map<QT3DSI32, eastl::pair<NVScopedRefCounted<IStateMachineDebugInterface>,
+ NVScopedRefCounted<IStateMachineListener>>>
+ TIdMachineMap;
+ QT3DSI32 mRefCount;
+ QT3DSI32 m_NextStateMachineId;
+ TDebugList m_StateMachineList;
+ TMachineIdMap m_StateMachines;
+ TIdMachineMap m_IdToStateMachines;
+ QT3DSU64 m_StartTime;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ SMessageParser<SDebugger> m_Parser;
+ SMessageSerializer m_MessageSerializer;
+ TDebugStr m_MessageStr;
+
+ SDebugger()
+ : mRefCount(0)
+ , m_NextStateMachineId(1)
+ , m_StartTime(Time::getCurrentCounterValue())
+ {
+ }
+
+ virtual ~SDebugger() { DisconnectFromServer(); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(g_MallocAlloc)
+
+ void ConnectStateMachine(IStateMachineDebugInterface &inMachine)
+ {
+ TMachineIdMap::iterator theIter = m_StateMachines.find(&inMachine);
+ if (theIter != m_StateMachines.end())
+ return;
+ QT3DSI32 theId(m_NextStateMachineId);
+ ++m_NextStateMachineId;
+ IStateMachineListener *theListener =
+ &IStateMachineListener::Create(g_MallocAlloc, theId, *m_OutStream.mPtr, m_StartTime,
+ *this, inMachine.GetScriptContext());
+ inMachine.SetStateMachineListener(theListener);
+ m_StateMachines.insert(eastl::make_pair(&inMachine, theId));
+ m_IdToStateMachines.insert(
+ eastl::make_pair(theId, eastl::make_pair(&inMachine, theListener)));
+ }
+
+ void OnServerConnected(IDebugOutStream &inStream) override
+ {
+ m_OutStream = inStream;
+ inStream.SetListener(this);
+ m_MessageSerializer.Serialize(0, SInitialization(), m_StartTime);
+ m_OutStream->Write(m_MessageSerializer.ToRawMessage());
+ for (QT3DSU32 idx = 0, end = m_StateMachineList.size(); idx < end; ++idx)
+ ConnectStateMachine(*m_StateMachineList[idx]);
+ }
+
+ void Connect(IStateMachineDebugInterface &inMachine) override
+ {
+ TDebugList::iterator theFind =
+ eastl::find(m_StateMachineList.begin(), m_StateMachineList.end(), &inMachine);
+ if (theFind == m_StateMachineList.end()) {
+ m_StateMachineList.push_back(&inMachine);
+ if (m_OutStream)
+ ConnectStateMachine(inMachine);
+ }
+ }
+
+ void DisconnectStateMachine(IStateMachineDebugInterface &inMachine)
+ {
+ TMachineIdMap::iterator theIter = m_StateMachines.find(&inMachine);
+ if (theIter == m_StateMachines.end())
+ return;
+
+ QT3DSI32 theId = theIter->second;
+ m_MessageSerializer.Serialize(theId, SDisconnect(),
+ Time::getCurrentCounterValue() - m_StartTime);
+ m_OutStream->Write(m_MessageSerializer.ToRawMessage());
+ inMachine.SetStateMachineListener(NULL);
+ m_StateMachines.erase(&inMachine);
+ m_IdToStateMachines.erase(theId);
+ }
+ void Disconnect(IStateMachineDebugInterface &inMachine) override
+ {
+ TDebugList::iterator theFind =
+ eastl::find(m_StateMachineList.begin(), m_StateMachineList.end(), &inMachine);
+ if (theFind != m_StateMachineList.end()) {
+ DisconnectStateMachine(inMachine);
+ m_StateMachineList.erase(theFind);
+ }
+ }
+
+ void DisconnectFromServer() override
+ {
+ if (m_OutStream) {
+ m_MessageSerializer.Serialize(-1, SDisconnect(),
+ Time::getCurrentCounterValue() - m_StartTime);
+ m_OutStream->Write(m_MessageSerializer.ToRawMessage());
+ }
+ m_StateMachines.clear();
+ m_IdToStateMachines.clear();
+ m_StateMachineList.clear();
+ m_OutStream = NULL;
+ }
+
+ void OnMessageReceived(const SDebugStreamMessage &msg) override
+ {
+ if (msg.m_Data.size())
+ m_Parser.Parse(msg.m_Data, *this);
+ }
+
+// Set of ignored messages
+#define IGNORE_DEBUG_MESSAGE_TYPE(tname) \
+ void OnMessage(QT3DSI32, QT3DSU64, const S##tname &) { QT3DS_ASSERT(false); }
+
+ // this are outgoing messages; we shouldn't be receiving them.
+ IGNORE_DEBUG_MESSAGE_TYPE(Initialization)
+ IGNORE_DEBUG_MESSAGE_TYPE(Connect)
+ IGNORE_DEBUG_MESSAGE_TYPE(BreakpointHit)
+ IGNORE_DEBUG_MESSAGE_TYPE(DebugLog)
+ IGNORE_DEBUG_MESSAGE_TYPE(EventQueued)
+ IGNORE_DEBUG_MESSAGE_TYPE(BeginStep)
+ IGNORE_DEBUG_MESSAGE_TYPE(BeginMicrostep)
+ IGNORE_DEBUG_MESSAGE_TYPE(MicrostepEvent)
+ IGNORE_DEBUG_MESSAGE_TYPE(MicrostepData)
+ IGNORE_DEBUG_MESSAGE_TYPE(EndMicrostep)
+ IGNORE_DEBUG_MESSAGE_TYPE(ModifyTableValues)
+
+ IStateMachineDebugInterface *GetStateMachine(QT3DSI32 inStreamId)
+ {
+ TIdMachineMap::iterator theIter = m_IdToStateMachines.find(inStreamId);
+ if (theIter == m_IdToStateMachines.end())
+ return NULL;
+ return theIter->second.first.mPtr;
+ }
+
+ IStateMachineListener *GetStateMachineListener(QT3DSI32 inStreamId)
+ {
+ TIdMachineMap::iterator theIter = m_IdToStateMachines.find(inStreamId);
+ if (theIter == m_IdToStateMachines.end())
+ return NULL;
+ return theIter->second.second.mPtr;
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SSetBreakpoint &inBp)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->SetBreakpoint(inBp.m_Breakpoint);
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SClearBreakpoint &inBp)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->ClearBreakpoint(inBp.m_Breakpoint);
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SClearAllBreakpoints &)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->ClearAllBreakpoints();
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SBreakOnMicrostep &inCmd)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->SetBreakOnMicrostep(inCmd.m_Value);
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SContinue &)
+ {
+ IStateMachineListener *iface = GetStateMachineListener(sid);
+ if (iface)
+ iface->Continue();
+ }
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SDisconnect &)
+ {
+ if (sid != 0 && sid != (QT3DSI32)QT3DS_MAX_U32) {
+ IStateMachineDebugInterface *iface = GetStateMachine(sid);
+ if (iface)
+ Disconnect(*iface);
+ } else {
+ if (sid == (QT3DSI32)QT3DS_MAX_U32)
+ DisconnectFromServer();
+ }
+ }
+
+ void error(const char8_t *, const char8_t *) {}
+};
+
+static inline NVConstDataRef<QT3DSU8> ToRef(const TDebugStr &str)
+{
+ return NVConstDataRef<QT3DSU8>((const QT3DSU8 *)str.begin(), str.size());
+}
+
+bool TestBasicParseRobustness()
+{
+ typedef STestMessageHandler<SInitialization> THandlerType;
+ typedef SMessageParser<THandlerType> TParserType;
+ // Just ensure things don't crash.
+ SInitialization testType;
+ THandlerType theHandler(2, 5, testType);
+ TDebugStr theStr;
+ TParserType theParser;
+ theParser.Parse(ToRef(theStr), theHandler);
+
+ theStr = "Initialization";
+ theParser.Parse(ToRef(theStr), theHandler);
+
+ theStr = "Initialization ";
+ theParser.Parse(ToRef(theStr), theHandler);
+ return true;
+}
+
+template <typename TDataType>
+bool TestSerialization(const TDataType &testType, SMessageSerializer &ioSerializer,
+ QT3DSU64 inTimestamp)
+{
+ typedef STestMessageHandler<TDataType> THandlerType;
+ typedef SMessageParser<THandlerType> TParserType;
+
+ ioSerializer.Serialize(5, testType, inTimestamp);
+ THandlerType theHandler(5, inTimestamp, testType);
+ TParserType theParser;
+ theParser.Parse(ioSerializer.ToRawMessage(), theHandler);
+ return theHandler.m_Result;
+}
+}
+
+IDebugger &IDebugger::CreateDebugger()
+{
+ return *QT3DS_NEW(g_MallocAlloc, SDebugger)();
+}
+
+bool CDebuggerTests::TestStreamProtocol()
+{
+ // for each message type, fill on some values, serialize to string and back and see what happens
+ TDebugStrList theList;
+ theList.push_back("one");
+ theList.push_back("two");
+ TTransitionIdList theTransList;
+ theTransList.push_back(STransitionId("one", 2));
+ theTransList.push_back(STransitionId("two", -1));
+ SBreakpoint bp1(SStateEnterBreakpoint("one"));
+ SBreakpoint bp2(SStateExitBreakpoint("two"));
+ SBreakpoint bp3(STransitionId("three", 4));
+ SMessageSerializer theSerializer;
+ bool retval = TestBasicParseRobustness();
+ // breaking out the large && statement to make testing easier.
+ retval = retval && TestSerialization(SInitialization(), theSerializer, 5);
+ retval = retval && TestSerialization(SConnect("abe.scxml", "<one>hey you</one>", theList),
+ theSerializer, 6);
+ retval = retval
+ && TestSerialization(SDebugLog(TDebugStr("Hey joe, where you goin'")), theSerializer, 7);
+ retval = retval && TestSerialization(SBeginMicrostep(), theSerializer, 8);
+ retval = retval && TestSerialization(SMicrostepEvent("evt1", true), theSerializer, 9);
+ retval = retval
+ && TestSerialization(SMicrostepData(theTransList, theList, theList), theSerializer, 9);
+ retval = retval && TestSerialization(SEndMicrostep(), theSerializer, 10);
+ retval = retval && TestSerialization(SEventQueued("evt1", false), theSerializer, 11);
+ retval = retval && TestSerialization(SEventQueued("evt1", false), theSerializer, 12);
+ retval = retval && TestSerialization(SSetBreakpoint(bp1), theSerializer, 13);
+ retval = retval && TestSerialization(SSetBreakpoint(bp2), theSerializer, 14);
+ retval = retval && TestSerialization(SSetBreakpoint(bp3), theSerializer, 15);
+ retval = retval && TestSerialization(SClearBreakpoint(bp1), theSerializer, 16);
+ retval = retval && TestSerialization(SClearAllBreakpoints(), theSerializer, 17);
+ return retval;
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h
new file mode 100644
index 00000000..00814e42
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebugger.h
@@ -0,0 +1,386 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUGGER_H
+#define QT3DS_STATE_DEBUGGER_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSDataRef.h"
+#include "foundation/StringTable.h"
+#include "foundation/IOStreams.h"
+#include "Qt3DSStateEditor.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+ using namespace editor;
+
+ typedef TEditorStr TDebugStr;
+ struct SDebugValue;
+ struct SBreakpoint;
+ struct SNil;
+ struct SDatamodelTable;
+ struct SDatamodelUserData;
+ struct SDatamodelFunction;
+ struct SDatamodelCFunction;
+ struct SDatamodelThread;
+
+ typedef eastl::vector<SBreakpoint> TBreakpointList;
+
+ struct BreakpointTypes
+ {
+ enum Enum {
+ UnknownBreakpointType = 0,
+ StateEnter,
+ StateExit,
+ Transition,
+ };
+ };
+
+ struct SDebugStreamMessage
+ {
+ // Timestamp needs to be converted using the SourceConversion on the protocol socket.
+ QT3DSU64 m_Timestamp;
+ NVConstDataRef<QT3DSU8> m_Data;
+ SDebugStreamMessage()
+ : m_Timestamp(0)
+ {
+ }
+ SDebugStreamMessage(QT3DSU64 ts, NVConstDataRef<QT3DSU8> msg)
+ : m_Timestamp(ts)
+ , m_Data(msg)
+ {
+ }
+ };
+
+ // Note the stream listeners are not ref counted.
+ // it is your job to ensure it lasts as long as debug stream.
+ class IDebugStreamListener
+ {
+ protected:
+ virtual ~IDebugStreamListener() {}
+ public:
+ // Async message interface. Clients must provide this. Only called on main thread
+ virtual void OnMessageReceived(const SDebugStreamMessage &inMessage) = 0;
+ };
+
+ class IDebugOutStream : public NVRefCounted, public IOutStream
+ {
+ protected:
+ virtual ~IDebugOutStream() {}
+ public:
+ virtual void SetListener(IDebugStreamListener *listener) = 0;
+ virtual IDebugStreamListener *GetListener() = 0;
+ // return true if the stream is still connected to an output, false if
+ // something caused the connection to fail or close.
+ virtual SDebugStreamMessage WaitForNextMessage() = 0;
+ virtual bool Connected() = 0;
+ };
+
+ struct STransitionId
+ {
+ TDebugStr m_StateId;
+ // Index in file order of the transition.
+ //-1 means initial or history::transition, depending on if the state is
+ // a normal state or history state.
+ QT3DSI32 m_TransitionIndex;
+ STransitionId(const TDebugStr &inId = TDebugStr(), QT3DSI32 inIdx = -2)
+ : m_StateId(inId)
+ , m_TransitionIndex(inIdx)
+ {
+ }
+ bool operator==(const STransitionId &inOther) const
+ {
+ return m_StateId == inOther.m_StateId
+ && m_TransitionIndex == inOther.m_TransitionIndex;
+ }
+ };
+
+ typedef eastl::vector<STransitionId> TTransitionIdList;
+
+ ///////////////////////////////////////////////////////////////////////
+ // Client (runtime) side types
+ ///////////////////////////////////////////////////////////////////////
+
+ struct DatamodelValueTypes
+ {
+ enum Enum {
+ UnknownType = 0,
+ Nil,
+ Boolean,
+ Number,
+ String,
+ Table,
+ UserData,
+ Function,
+ CFunction,
+ Thread,
+ };
+ };
+
+ struct SDatamodelValue;
+
+ struct SDatamodelTable;
+ struct SDatamodelUserData;
+ struct SDatamodelFunction;
+ struct SDatamodelCFunction;
+ struct SDatamodelThread;
+ struct STableEntry;
+
+ class IScriptStateListener
+ {
+ protected:
+ virtual ~IScriptStateListener() {}
+ public:
+ virtual void SetKey(void *inTable, const TDebugStr &inStr,
+ const SDatamodelValue &inValue) = 0;
+ virtual void RemoveKey(void *inTable, const TDebugStr &inStr) = 0;
+ };
+
+ class IDebugger;
+ // Information coming from the state machine
+ class IStateMachineListener : public NVRefCounted
+ {
+ protected:
+ virtual ~IStateMachineListener() {}
+ public:
+ // Events coming from the interpreter and sent over the interface.
+ virtual void OnConnect(const TDebugStr &SCXMLFilename, const TDebugStr &SCXMLFileData,
+ NVConstDataRef<TDebugStr> inConfiguration) = 0;
+
+ virtual void Log(const TDebugStr &inStr) = 0;
+ virtual void EventQueued(const TDebugStr &inEventName, bool inInternal) = 0;
+ virtual void BeginStep() = 0;
+ virtual void BeginMicroStep() = 0;
+ virtual void SetEvent(const TDebugStr &inEventName, bool inInternal) = 0;
+ virtual void SetTransitionSet(NVConstDataRef<STransitionId> inTransitions) = 0;
+ virtual void SetExitSet(NVConstDataRef<TDebugStr> inSet) = 0;
+ virtual void SetEnterSet(NVConstDataRef<TDebugStr> inSet) = 0;
+ // Log statements run through the debugger as well.
+ virtual void EndMicroStep() = 0;
+ virtual void EndStep() = 0;
+
+ // So far the breakpoints have all been things that are internal to the
+ // state machine.
+ virtual void SetBreakOnMicrostep(bool inEnableStep) = 0;
+ virtual void SetBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual void ClearBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual void ClearAllBreakpoints() = 0;
+
+ // Called internally.
+ virtual void Continue() = 0;
+ virtual void OnExternalBreak() = 0;
+
+ static IStateMachineListener &Create(NVAllocatorCallback &inAlloc, QT3DSU32 inStreamId,
+ IDebugOutStream &inOutStr, QT3DSU64 inStartTime,
+ IDebugger &inDebugger,
+ IScriptContext &inScriptContext);
+ };
+
+ // Information coming from the network stream will call these iterfaces
+ class IStateMachineDebugInterface : public NVRefCounted
+ {
+ protected:
+ virtual ~IStateMachineDebugInterface() {}
+ public:
+ virtual void SetStateMachineListener(IStateMachineListener *inListener) = 0;
+ virtual void OnExternalBreak() = 0;
+ virtual IScriptContext &GetScriptContext() = 0;
+ };
+
+ // The debugger element is the object that sits in the debug process interpreter and sends
+ // information.
+ // There should be only one of these per network connection.
+ class IDebugger : public IDebugStreamListener, public NVRefCounted
+ {
+ protected:
+ virtual ~IDebugger() {}
+ public:
+ virtual void OnServerConnected(IDebugOutStream &inStream) = 0;
+ virtual void Connect(IStateMachineDebugInterface &inMachine) = 0;
+ virtual void Disconnect(IStateMachineDebugInterface &inMachine) = 0;
+ // Release any references to any state machines and to the output stream
+ virtual void DisconnectFromServer() = 0;
+
+ static IDebugger &CreateDebugger();
+ };
+
+ ///////////////////////////////////////////////////////////////////////
+ // Server (Architect) side types
+ ///////////////////////////////////////////////////////////////////////
+
+ struct DebugValueTypes
+ {
+ enum Enum { NoDebugValue = 0, String, StringList, TransitionIdList };
+ };
+
+ typedef eastl::vector<TDebugStr> TDebugStrList;
+
+ struct STableModification;
+
+ class IDebuggedInterpreter;
+
+ class IDebuggerMasterListener : public NVRefCounted
+ {
+ protected:
+ virtual ~IDebuggerMasterListener() {}
+ public:
+ virtual void OnInterpreterConnected(IDebuggedInterpreter &inInterpreter) = 0;
+ // If no breakpoint then the interpreter was broken on microstep.
+ virtual void OnEventQueued(const TDebugStr &inEvent, bool inInternal) = 0;
+ virtual void OnInterpreterBroken(IDebuggedInterpreter &inInterpreter,
+ const Option<SBreakpoint> &inBreakpoint) = 0;
+ // Event processed but no microstep was taken.
+ virtual void OnEventProcessed(const TDebugStr &inEvent, bool inInternal) = 0;
+ virtual void OnBeginStep(IDebuggedInterpreter &inInterpreter) = 0;
+ virtual void OnBeginMicrostep(IDebuggedInterpreter &inInterpreter) = 0;
+ // Event processed and microstep was taken.
+ virtual void OnMicrostep(IDebuggedInterpreter &inInterpreter) = 0;
+ // if inmodifications is empty the table needs to be replaced.
+ virtual void OnDatamodelChange(IDebuggedInterpreter &inInterpreter,
+ SDatamodelTable *inTable,
+ NVConstDataRef<STableModification> inModifications) = 0;
+ virtual void OnInterpreterDisconnected(IDebuggedInterpreter &inInterpreter) = 0;
+ virtual void OnLog(IDebuggedInterpreter *inInterpreter, const TDebugStr &inMessage) = 0;
+ };
+
+ struct STableEntry;
+
+ typedef eastl::vector<STableEntry> TTableEntryList;
+
+ // defined in UICStateDebuggerProtocol.h"
+ struct SBeginStep;
+ struct SBeginMicrostep;
+ struct SMicrostepEvent;
+ struct SMicrostepData;
+ struct SEndMicrostep;
+ struct SEventQueued;
+ struct SModifyTableValues;
+ struct SDatamodelTable;
+
+ // Represents the data stream coming from a single interpreter
+ class IDebuggedInterpreter : public NVRefCounted
+ {
+ protected:
+ virtual ~IDebuggedInterpreter() {}
+ public:
+ /* Playback interface, leaving until basic functionality is working.
+ virtual QT3DSU32 GetStepCount() = 0;
+ virtual void SetCurrentStep( QT3DSU32 inStep ) = 0;
+ virtual QT3DSU32 GetMicrostepCount() = 0;
+ virtual QT3DSU32 SetCurrentMicroStep( QT3DSU32 inStep ) = 0;
+ */
+
+ // Get the editor that represents the state graph for this debugged interpreter.
+ virtual TEditorPtr GetEditor() = 0;
+ virtual TDebugStr GetFilename() const = 0;
+
+ // Used when connecting to a live session.
+ virtual void SetBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual void ClearBreakpoint(const SBreakpoint &inBreakpoint) = 0;
+ virtual NVConstDataRef<SBreakpoint> GetBreakpoints() = 0;
+ virtual void ClearAllBreakpoints() = 0;
+ // break at the *end* of the microstep so you can see the data of the microstep.
+ virtual void SetBreakOnMicrostep(bool inBreak) = 0;
+ virtual bool IsBreakOnMicrostep() const = 0;
+
+ // Return values not valid after *next* call.
+ // Null means the root.
+ virtual NVConstDataRef<STableEntry>
+ GetTableValues(SDatamodelTable *inTable = NULL) const = 0;
+
+ virtual NVConstDataRef<TDebugStr> GetPreviousConfiguration() const = 0;
+ virtual NVConstDataRef<TDebugStr> GetConfiguration() const = 0;
+ virtual const TDebugStr &GetMicrostepEvent() const = 0;
+ virtual NVConstDataRef<STransitionId> GetMicrostepTransitions() const = 0;
+ virtual NVConstDataRef<TDebugStr> GetMicrostepEnterStates() const = 0;
+ virtual NVConstDataRef<TDebugStr> GetMicrostepExitStates() const = 0;
+
+ virtual bool IsBroken() const = 0;
+ virtual void Continue() = 0;
+ // Get the set of changed properties since the last time you asked. Obviously this is
+ // assuming there
+ // is only one 'you' asking.
+ virtual void Disconnect() = 0;
+
+ // Semi-advanced debugger functionality that we don't need for version 1.
+
+ // virtual void SetPropertyValue( const char8_t* inName, const SDebugValue& inValue ) =
+ // 0;
+ // Set a property value on the running state machine.
+ // virtual void SetPropertyValue( TObjPtr inEditorObj, const char8_t* inName, const
+ // SValueOpt& inValue ) = 0;
+
+ // Eval code in the current state machine context. Works when connected live, wouldn't
+ // work if you
+ // were not connected live.
+ // virtual TDebugStr EvalCode( const TDebugStr& inStr ) = 0;
+
+ // Information coming from the stream, clients should not call these functions
+ virtual void BreakpointHit(const SBreakpoint &inBreakpoint) = 0;
+ virtual void OnEventQueued(const SEventQueued &inMsg) = 0;
+ virtual void OnBeginStep(const SBeginStep &inMsg) = 0;
+ virtual void OnBeginMicrostep(const SBeginMicrostep &inMsg) = 0;
+ virtual void OnMicrostepEvent(const SMicrostepEvent &inMsg) = 0;
+ virtual void OnMicrostepData(const SMicrostepData &inMsg) = 0;
+ virtual void OnEndMicrostep(const SEndMicrostep &inMsg) = 0;
+ virtual void OnModifyTable(const SModifyTableValues &inValues) = 0;
+
+ static IDebuggedInterpreter &Create(NVAllocatorCallback &inAlloc,
+ IDebugOutStream &inStream, TEditorPtr inEditor,
+ const TDebugStr &inFilename, QT3DSI32 inStreamId,
+ NVConstDataRef<TDebugStr> inConfiguration,
+ IDebuggerMasterListener &inListener);
+ };
+
+ // Debugger sites in the master process (Architect process) and controls one or more
+ // debuggers
+ // for one or more state machines running in the debug process.
+ class IDebuggerMaster : public IDebugStreamListener, public NVRefCounted
+ {
+ protected:
+ virtual ~IDebuggerMaster() {}
+ public:
+ virtual void Disconnect() = 0;
+
+ static IDebuggerMaster &CreateMaster(IDebugOutStream &outStream,
+ IDebuggerMasterListener &inListener);
+ };
+
+ class CDebuggerTests
+ {
+ public:
+ static bool TestStreamProtocol();
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp
new file mode 100644
index 00000000..2045f84f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerListener.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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 "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerValues.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "Qt3DSStateScriptContext.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+// Component sits in the state machine and receives messages from the state machine
+// itself.
+struct SListener : public IStateMachineListener, public IScriptStateListener
+{
+ typedef eastl::hash_map<SDatamodelTable *, SModifyTableValues> TTableModificationMap;
+ NVAllocatorCallback &m_Allocator;
+ QT3DSI32 mRefCount;
+ QT3DSU32 m_StreamId;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ SMessageSerializer m_Serializer;
+ TDebugStrList m_DebugStrList;
+ TDebugStrList m_DebugStrAltList;
+ TTransitionIdList m_TransitionIdList;
+ QT3DSU64 m_StartTime;
+ bool m_BreakOnMicrostep;
+ bool m_Blocking;
+ IDebugger &m_Debugger;
+ TDebugStr m_BreakStr;
+ NVScopedRefCounted<IScriptContext> m_ScriptContext;
+ TTableModificationMap m_TableModifications;
+ TBreakpointList m_Breakpoints;
+ SMicrostepData m_MicrostepData;
+ bool m_MicrostepBeginSent;
+ bool m_StepSent;
+
+ SListener(NVAllocatorCallback &alloc, QT3DSU32 inStreamId, IDebugOutStream &inOutStr,
+ QT3DSU64 inStartTime, IDebugger &inDebugger, IScriptContext &inScriptContext)
+ : m_Allocator(alloc)
+ , mRefCount(0)
+ , m_StreamId(inStreamId)
+ , m_OutStream(inOutStr)
+ , m_StartTime(inStartTime)
+ , m_BreakOnMicrostep(false)
+ , m_Blocking(false)
+ , m_Debugger(inDebugger)
+ , m_ScriptContext(inScriptContext)
+ , m_MicrostepBeginSent(false)
+ , m_StepSent(false)
+ {
+ }
+
+ virtual ~SListener() { m_Blocking = false; }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Allocator)
+
+ template <typename TDataType>
+ static const eastl::vector<TDataType> &RefToList(NVConstDataRef<TDataType> inRef,
+ eastl::vector<TDataType> &outList)
+ {
+ outList.assign(inRef.begin(), inRef.end());
+ return outList;
+ }
+
+ template <typename TDataType>
+ void Serialize(const TDataType &dtype, QT3DSU64 inTime)
+ {
+ m_Serializer.Serialize(m_StreamId, dtype, inTime);
+ m_OutStream->Write(m_Serializer.ToRawMessage());
+ }
+
+ template <typename TDataType>
+ void Serialize(const TDataType &dtype)
+ {
+ Serialize(dtype, CurrentTime());
+ }
+
+ void OnConnect(const TDebugStr &SCXMLFilename, const TDebugStr &SCXMLFileData,
+ NVConstDataRef<TDebugStr> inConfiguration) override
+ {
+ Serialize(
+ SConnect(SCXMLFilename, SCXMLFileData, RefToList(inConfiguration, m_DebugStrList)));
+ DumpScriptState();
+ }
+
+ QT3DSU64 CurrentTime() { return Time::getCurrentCounterValue() - m_StartTime; }
+
+ void DumpScriptState()
+ {
+ // Run through the global state and output any keys that have changed.
+ m_TableModifications.clear();
+ m_ScriptContext->DumpState(*this);
+ for (TTableModificationMap::iterator iter = m_TableModifications.begin(),
+ end = m_TableModifications.end();
+ iter != end; ++iter) {
+ Serialize(iter->second);
+ }
+ m_TableModifications.clear();
+ }
+ virtual void OnBreakpointHit(const SBreakpoint &inBreakpointId)
+ {
+ DumpScriptState();
+ Serialize(SBreakpointHit(inBreakpointId));
+ Break();
+ }
+
+ void Log(const TDebugStr &inStr) override { Serialize(SDebugLog(inStr)); }
+
+ void SetBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ m_Breakpoints.push_back(inBreakpoint);
+ }
+
+ void ClearBreakpoint(const SBreakpoint &inBreakpoint) override
+ {
+ TBreakpointList::iterator removeIter =
+ eastl::remove(m_Breakpoints.begin(), m_Breakpoints.end(), inBreakpoint);
+ if (removeIter != m_Breakpoints.end())
+ m_Breakpoints.erase(removeIter, m_Breakpoints.end());
+ }
+ void ClearAllBreakpoints() override { m_Breakpoints.clear(); }
+ void EventQueued(const TDebugStr &inEventName, bool inInternal) override
+ {
+ Serialize(SEventQueued(inEventName, inInternal));
+ }
+ void SendMicrostepBegin()
+ {
+ if (m_MicrostepBeginSent == false) {
+ if (m_StepSent == false) {
+ m_StepSent = true;
+ Serialize(SBeginStep());
+ }
+
+ m_MicrostepBeginSent = true;
+ Serialize(SBeginMicrostep());
+ }
+ }
+
+ void BeginStep() override { m_StepSent = false; }
+
+ void BeginMicroStep() override
+ {
+ m_MicrostepBeginSent = false;
+ m_MicrostepData.m_Transitions.clear();
+ m_MicrostepData.m_EnterStates.clear();
+ m_MicrostepData.m_ExitStates.clear();
+ }
+
+ void SetEvent(const TDebugStr &inEvent, bool inInternal) override
+ {
+ if (!inEvent.empty()) {
+ SendMicrostepBegin();
+ Serialize(SMicrostepEvent(inEvent, inInternal));
+ }
+ }
+ void SetTransitionSet(NVConstDataRef<STransitionId> inTransitions) override
+ {
+ SendMicrostepBegin();
+ RefToList(inTransitions, m_MicrostepData.m_Transitions);
+ }
+ void SetExitSet(NVConstDataRef<TDebugStr> inSet) override
+ {
+ SendMicrostepBegin();
+ RefToList(inSet, m_MicrostepData.m_ExitStates);
+ }
+ void SetEnterSet(NVConstDataRef<TDebugStr> inSet) override
+ {
+ SendMicrostepBegin();
+ RefToList(inSet, m_MicrostepData.m_EnterStates);
+ }
+
+ static inline bool FindInList(const TDebugStr &inStr, const TDebugStrList &inStrList)
+ {
+ if (eastl::find(inStrList.begin(), inStrList.end(), inStr) != inStrList.end())
+ return true;
+ return false;
+ }
+
+ static inline bool FindInList(const STransitionId &inStr, const TTransitionIdList &inStrList)
+ {
+ if (eastl::find(inStrList.begin(), inStrList.end(), inStr) != inStrList.end())
+ return true;
+ return false;
+ }
+
+ const SBreakpoint *FindHitBreakpoint()
+ {
+ // Now check for breakpoints.
+ for (QT3DSU32 breakIdx = 0, breakEnd = m_Breakpoints.size(); breakIdx < breakEnd; ++breakIdx) {
+ const SBreakpoint &theBreakpoint = m_Breakpoints[breakIdx];
+ switch (theBreakpoint.getType()) {
+ case BreakpointTypes::StateEnter:
+ if (FindInList(theBreakpoint.getData<SStateEnterBreakpoint>().m_ObjectId,
+ m_MicrostepData.m_EnterStates))
+ return &theBreakpoint;
+ case BreakpointTypes::StateExit:
+ if (FindInList(theBreakpoint.getData<SStateExitBreakpoint>().m_ObjectId,
+ m_MicrostepData.m_ExitStates))
+ return &theBreakpoint;
+ case BreakpointTypes::Transition:
+ if (FindInList(theBreakpoint.getData<STransitionId>(),
+ m_MicrostepData.m_Transitions))
+ return &theBreakpoint;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ return NULL;
+ }
+
+ // Log statements run through the debugger as well.
+ void EndMicroStep() override
+ {
+ DumpScriptState();
+ if (m_MicrostepBeginSent) {
+ if (m_MicrostepData.m_Transitions.empty() == false)
+ Serialize(m_MicrostepData);
+
+ Serialize(SEndMicrostep());
+ const SBreakpoint *theHitBreakpoint = FindHitBreakpoint();
+ if (theHitBreakpoint)
+ OnBreakpointHit(*theHitBreakpoint);
+ else if (m_BreakOnMicrostep)
+ Break();
+ }
+ }
+
+ void EndStep() override { m_StepSent = false; }
+
+ void SetBreakOnMicrostep(bool inEnableStep) override { m_BreakOnMicrostep = inEnableStep; }
+
+ virtual void Break()
+ {
+ if (!m_Blocking) {
+ // We may get disconnected *while* we are breaking.
+ // We need to ensure we don't get nuked while we are breaking.
+ NVScopedRefCounted<SListener> tempVar(this);
+ m_Blocking = true;
+ while (m_Blocking) {
+ SDebugStreamMessage msg = m_OutStream->WaitForNextMessage();
+ // Some out streams will have a reference to the debugger and some will not.
+ // If they do, then we do not have to hand our string to the debugger, we can assume
+ // the underlying implementation will have done that. If not, then we need to
+ // pass the message to the debugger.
+ if (m_Blocking && msg.m_Data.size())
+ m_Debugger.OnMessageReceived(msg);
+
+ if (!m_OutStream->Connected()) {
+ m_Blocking = false;
+ m_Debugger.DisconnectFromServer();
+ }
+ }
+ }
+ }
+
+ void Continue() override { m_Blocking = false; }
+
+ void OnExternalBreak() override { DumpScriptState(); }
+
+ // IScriptStateListener
+ void SetKey(void *inTable, const TDebugStr &inStr, const SDatamodelValue &inValue) override
+ {
+ SDatamodelTable *theTable((SDatamodelTable *)inTable);
+ SModifyTableValues &theModification =
+ m_TableModifications.insert(eastl::make_pair(theTable, SModifyTableValues(theTable)))
+ .first->second;
+ theModification.m_Modifications.push_back(
+ STableModification(STableEntry(inStr, inValue), TableModificationType::SetKey));
+ }
+
+ void RemoveKey(void *inTable, const TDebugStr &inStr) override
+ {
+ SDatamodelTable *theTable((SDatamodelTable *)inTable);
+ SModifyTableValues &theModification =
+ m_TableModifications.insert(eastl::make_pair(theTable, SModifyTableValues(theTable)))
+ .first->second;
+ theModification.m_Modifications.push_back(
+ STableModification(STableEntry(inStr), TableModificationType::RemoveKey));
+ }
+};
+}
+
+IStateMachineListener &IStateMachineListener::Create(NVAllocatorCallback &inAlloc, QT3DSU32 inStreamId,
+ IDebugOutStream &inOutStr, QT3DSU64 inStartTime,
+ IDebugger &inDebugger,
+ IScriptContext &inScriptContext)
+{
+ return *QT3DS_NEW(inAlloc, SListener)(inAlloc, inStreamId, inOutStr, inStartTime, inDebugger,
+ inScriptContext);
+}
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h
new file mode 100644
index 00000000..03230a61
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerProtocol.h
@@ -0,0 +1,925 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUGGER_PROTOCOL_H
+#define QT3DS_STATE_DEBUGGER_PROTOCOL_H
+#include "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerValues.h"
+#include "foundation/StringConversion.h"
+#include "foundation/StringConversionImpl.h"
+#include "foundation/Qt3DSTime.h"
+
+// Stream protocol, regardless of binary vs. text.
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+
+ struct SInitialization
+ {
+ QT3DSI32 m_Version;
+ QT3DSI32 m_Padding;
+ CounterFrequencyToTensOfNanos m_TimerFrequency;
+ static QT3DSI32 GetCurrentVersion() { return 1; }
+ SInitialization()
+ : m_Version(GetCurrentVersion())
+ , m_Padding(0)
+ , m_TimerFrequency(Time::sCounterFreq)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Version);
+ inVisitor.visit(m_TimerFrequency.mNumerator);
+ inVisitor.visit(m_TimerFrequency.mDenominator);
+ }
+ bool operator==(const SInitialization &inOther) const
+ {
+ return m_Version == inOther.m_Version
+ && m_TimerFrequency.mNumerator == inOther.m_TimerFrequency.mNumerator
+ && m_TimerFrequency.mDenominator == inOther.m_TimerFrequency.mDenominator;
+ }
+ };
+
+ // Listener interface. For on connect, we send the current timestamp for that state
+ // machine.
+ struct SConnect
+ {
+ TDebugStr m_Filename;
+ TDebugStr m_SCXMLData; // scxml file contents
+ TDebugStrList m_Configuration;
+ SConnect() {}
+ SConnect(const TDebugStr &fn, const TDebugStr &xmlData, const TDebugStrList &config)
+ : m_Filename(fn)
+ , m_SCXMLData(xmlData)
+ , m_Configuration(config)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.randomText(m_Filename);
+ inVisitor.randomText(m_SCXMLData);
+ inVisitor.visitIdList(m_Configuration);
+ }
+ bool operator==(const SConnect &inOther) const
+ {
+ return m_Filename == inOther.m_Filename && m_SCXMLData == inOther.m_SCXMLData
+ && m_Configuration == inOther.m_Configuration;
+ }
+ };
+
+ struct SBreakpointHit
+ {
+ SBreakpoint m_Breakpoint;
+ SBreakpointHit() {}
+ SBreakpointHit(const SBreakpoint &bp)
+ : m_Breakpoint(bp)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Breakpoint);
+ }
+ bool operator==(const SBreakpointHit &inOther) const
+ {
+ return m_Breakpoint == inOther.m_Breakpoint;
+ }
+ };
+
+ struct SDebugLog
+ {
+ TDebugStr m_Message;
+ SDebugLog() {}
+ SDebugLog(const TDebugStr &msg)
+ : m_Message(msg)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.randomText(m_Message);
+ }
+ bool operator==(const SDebugLog &inOther) const
+ {
+ return m_Message == inOther.m_Message;
+ }
+ };
+
+ struct SModifyTableValues
+ {
+ SDatamodelTable *m_TablePtr;
+ TTableModificationList m_Modifications;
+
+ SModifyTableValues(SDatamodelTable *inTable = NULL,
+ const TTableModificationList &inList = TTableModificationList())
+ : m_TablePtr(inTable)
+ , m_Modifications(inList)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_TablePtr);
+ inVisitor.visit(m_Modifications);
+ }
+
+ bool operator==(const SModifyTableValues &inOther) const
+ {
+ return m_TablePtr == inOther.m_TablePtr
+ && m_Modifications == inOther.m_Modifications;
+ }
+ };
+
+ struct SBeginStep
+ {
+ SBeginStep() {}
+ template <typename TVisitor>
+ void Visit(TVisitor &)
+ {
+ }
+
+ bool operator==(const SBeginStep &) const { return true; }
+ };
+
+ struct SBeginMicrostep
+ {
+ SBeginMicrostep() {}
+ template <typename TVisitor>
+ void Visit(TVisitor &)
+ {
+ }
+
+ bool operator==(const SBeginMicrostep &) const { return true; }
+ };
+
+ struct SMicrostepEvent
+ {
+ TDebugStr m_Event;
+ bool m_Internal;
+
+ SMicrostepEvent(const TDebugStr &inEvent = TDebugStr(), bool inIsInternal = false)
+ : m_Event(inEvent)
+ , m_Internal(inIsInternal)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visitId(m_Event);
+ inVisitor.visit(m_Internal);
+ }
+
+ bool operator==(const SMicrostepEvent &inOther) const
+ {
+ return m_Event == inOther.m_Event && m_Internal == inOther.m_Internal;
+ }
+ };
+
+ struct SMicrostepData
+ {
+ TTransitionIdList m_Transitions;
+ TDebugStrList m_ExitStates;
+ TDebugStrList m_EnterStates;
+
+ SMicrostepData() {}
+ SMicrostepData(const TTransitionIdList &inTransitions,
+ const TDebugStrList &inExitStates, const TDebugStrList &inEnterStates)
+ : m_Transitions(inTransitions)
+ , m_ExitStates(inExitStates)
+ , m_EnterStates(inEnterStates)
+ {
+ }
+
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ // Eventless transitions cause us to need to always write the string length.
+ inVisitor.visit(m_Transitions);
+ inVisitor.visitIdList(m_ExitStates);
+ inVisitor.visitIdList(m_EnterStates);
+ }
+ bool operator==(const SMicrostepData &inOther) const
+ {
+ return m_Transitions == inOther.m_Transitions
+ && m_ExitStates == inOther.m_ExitStates
+ && m_EnterStates == inOther.m_EnterStates;
+ }
+ };
+
+ struct SEndMicrostep
+ {
+ SEndMicrostep() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+
+ bool operator==(const SEndMicrostep &) const { return true; }
+ };
+
+ struct SEventQueued
+ {
+ TDebugStr m_Event;
+ bool m_Internal;
+ SEventQueued()
+ : m_Internal(false)
+ {
+ }
+ SEventQueued(const TDebugStr &evt, bool intern)
+ : m_Event(evt)
+ , m_Internal(intern)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visitEventName(m_Event);
+ inVisitor.visit(m_Internal);
+ }
+ bool operator==(const SEventQueued &inOther) const
+ {
+ return m_Event == inOther.m_Event && m_Internal == inOther.m_Internal;
+ }
+ };
+
+ // Debug interface
+
+ struct SSetBreakpoint
+ {
+ SBreakpoint m_Breakpoint;
+ SSetBreakpoint() {}
+ SSetBreakpoint(const SBreakpoint &bp)
+ : m_Breakpoint(bp)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Breakpoint);
+ }
+ bool operator==(const SSetBreakpoint &inOther) const
+ {
+ return m_Breakpoint == inOther.m_Breakpoint;
+ }
+ };
+
+ struct SClearBreakpoint
+ {
+ SBreakpoint m_Breakpoint;
+ SClearBreakpoint() {}
+ SClearBreakpoint(const SBreakpoint &bp)
+ : m_Breakpoint(bp)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Breakpoint);
+ }
+ bool operator==(const SClearBreakpoint &inOther) const
+ {
+ return m_Breakpoint == inOther.m_Breakpoint;
+ }
+ };
+
+ struct SClearAllBreakpoints
+ {
+ SClearAllBreakpoints() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+ bool operator==(const SClearAllBreakpoints & /*inOther*/) const { return true; }
+ };
+
+ struct SBreakOnMicrostep
+ {
+ bool m_Value;
+ SBreakOnMicrostep(bool val = false)
+ : m_Value(val)
+ {
+ }
+ template <typename TVisitor>
+ void Visit(TVisitor &inVisitor)
+ {
+ inVisitor.visit(m_Value);
+ }
+ bool operator==(const SBreakOnMicrostep &inOther) const
+ {
+ return m_Value == inOther.m_Value;
+ }
+ };
+
+ struct SContinue
+ {
+ SContinue() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+ bool operator==(const SContinue & /*inOther*/) const { return true; }
+ };
+
+ struct SDisconnect
+ {
+ SDisconnect() {}
+ template <typename TVisitor>
+ void Visit(TVisitor & /*inVisitor*/)
+ {
+ }
+ bool operator==(const SDisconnect & /*inOther*/) const { return true; }
+ };
+
+ // Visitors to handle the breakpoint subtypes.
+ // The empty top visitor will force a compile error if there is a breakpoint type we don't
+ // recognize.
+ template <typename TDataType>
+ struct SBreakpointSerializationVisitor
+ {
+ };
+
+ template <>
+ struct SBreakpointSerializationVisitor<SStateEnterBreakpoint>
+ {
+ enum { Type = BreakpointTypes::StateEnter };
+ template <typename TVisitor>
+ static void Visit(TVisitor &inVisitor, SStateEnterBreakpoint &inBp)
+ {
+ inVisitor.visitId(inBp.m_ObjectId);
+ }
+ };
+
+ template <>
+ struct SBreakpointSerializationVisitor<SStateExitBreakpoint>
+ {
+ enum { Type = BreakpointTypes::StateExit };
+ template <typename TVisitor>
+ static void Visit(TVisitor &inVisitor, SStateExitBreakpoint &inBp)
+ {
+ inVisitor.visitId(inBp.m_ObjectId);
+ }
+ };
+
+ template <>
+ struct SBreakpointSerializationVisitor<STransitionId>
+ {
+ enum { Type = BreakpointTypes::Transition };
+ template <typename TVisitor>
+ static void Visit(TVisitor &inVisitor, STransitionId &inBp)
+ {
+ inVisitor.visitId(inBp.m_StateId);
+ inVisitor.visit(inBp.m_TransitionIndex);
+ }
+ };
+
+#define ITERATE_BREAKPOINT_TYPES \
+ HANDLE_BREAKPOINT_TYPE(StateEnter, SStateEnterBreakpoint) \
+ HANDLE_BREAKPOINT_TYPE(StateExit, SStateExitBreakpoint) \
+ HANDLE_BREAKPOINT_TYPE(Transition, STransitionId)
+
+#define ITERATE_DEBUG_MESSAGE_TYPES \
+ HANDLE_DEBUG_MESSAGE_TYPE(Initialization) \
+ HANDLE_DEBUG_MESSAGE_TYPE(Connect) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BreakpointHit) \
+ HANDLE_DEBUG_MESSAGE_TYPE(DebugLog) \
+ HANDLE_DEBUG_MESSAGE_TYPE(ModifyTableValues) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BeginStep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BeginMicrostep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(MicrostepEvent) \
+ HANDLE_DEBUG_MESSAGE_TYPE(MicrostepData) \
+ HANDLE_DEBUG_MESSAGE_TYPE(EndMicrostep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(EventQueued) \
+ HANDLE_DEBUG_MESSAGE_TYPE(SetBreakpoint) \
+ HANDLE_DEBUG_MESSAGE_TYPE(ClearBreakpoint) \
+ HANDLE_DEBUG_MESSAGE_TYPE(ClearAllBreakpoints) \
+ HANDLE_DEBUG_MESSAGE_TYPE(Disconnect) \
+ HANDLE_DEBUG_MESSAGE_TYPE(BreakOnMicrostep) \
+ HANDLE_DEBUG_MESSAGE_TYPE(Continue)
+
+ template <typename TStructType>
+ struct STypeToName
+ {
+ };
+
+#define HANDLE_BREAKPOINT_TYPE(enumType, structType) \
+ template <> \
+ struct STypeToName<structType> \
+ { \
+ static const char8_t *GetName() { return #enumType; } \
+ };
+#define HANDLE_DEBUG_MESSAGE_TYPE(msgType) \
+ template <> \
+ struct STypeToName<S##msgType> \
+ { \
+ static const char8_t *GetName() { return #msgType; } \
+ };
+
+ ITERATE_BREAKPOINT_TYPES
+ ITERATE_DEBUG_MESSAGE_TYPES
+
+#undef HANDLE_BREAKPOINT_TYPE
+#undef HANDLE_DEBUG_MESSAGE_TYPE
+
+ template <typename TMessageHandler>
+ struct SMessageParser
+ {
+ TDebugStr m_MessageHeaderStr;
+ TDebugStr m_TimestampStr;
+ TDebugStr m_ParseStr;
+ TDebugStr m_BreakpointTypeStr;
+ TDebugStr m_TempStr[4];
+ TMessageHandler *m_CurrentHandler;
+ bool m_Valid;
+ static void Trim(TDebugStr &inStr)
+ {
+ if (inStr.empty())
+ return;
+ eastl::string::size_type nonSpace = inStr.find_first_not_of(' ');
+ eastl::string::size_type lastNonSpace = inStr.find_last_not_of(' ');
+ if (nonSpace == TDebugStr::npos || lastNonSpace == TDebugStr::npos) {
+ inStr.clear();
+ return;
+ }
+
+ eastl::string::size_type msgLen = lastNonSpace - nonSpace;
+ inStr.erase(inStr.begin(), inStr.begin() + nonSpace);
+ inStr.resize(msgLen + 1);
+ }
+
+ void TrimForward(eastl::string::size_type startPos)
+ {
+ eastl::string::size_type nextPos = m_ParseStr.find_first_not_of(' ', startPos);
+ if (nextPos != TDebugStr::npos)
+ m_ParseStr.erase(m_ParseStr.begin(), m_ParseStr.begin() + nextPos);
+ else
+ m_ParseStr.clear();
+ }
+
+ void TrimForward()
+ {
+ eastl::string::size_type nextSpace = m_ParseStr.find_first_of(' ');
+ TrimForward(nextSpace);
+ }
+
+ void visitId(TDebugStr &inStr)
+ {
+ eastl::string::size_type nextSpace = m_ParseStr.find_first_of(' ');
+ if (nextSpace == TDebugStr::npos)
+ nextSpace = m_ParseStr.size();
+ inStr.assign(m_ParseStr.begin(), m_ParseStr.begin() + nextSpace);
+ TrimForward(nextSpace);
+ }
+ void visit(TDebugStr &inStr) { randomText(inStr); }
+ void visit(QT3DSI32 &inData)
+ {
+ StringConversion<QT3DSI32>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+ void visit(QT3DSU32 &inData)
+ {
+ StringConversion<QT3DSU32>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+
+ void visit(QT3DSU64 &inData)
+ {
+ StringConversion<QT3DSU64>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+
+ void visit(float &inData)
+ {
+ StringConversion<QT3DSF32>().StrTo(m_ParseStr.c_str(), inData);
+ TrimForward();
+ }
+
+ void visit(bool &inData)
+ {
+ if (CompareNChars(m_ParseStr.c_str(), "True", 4) == 0)
+ inData = true;
+ else
+ inData = false;
+
+ TrimForward();
+ }
+
+ template <typename TPtrType>
+ void visitOpaquePtr(TPtrType *&inPtr)
+ {
+ QT3DSU64 temp;
+ visit(temp);
+ inPtr = reinterpret_cast<TPtrType *>(static_cast<size_t>(temp));
+ }
+
+ void visit(SDatamodelTable *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelUserData *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelFunction *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelCFunction *&inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelThread *&inData) { visitOpaquePtr(inData); }
+ void visit(SNil &) {}
+
+ void visit(SDatamodelValue &inValue)
+ {
+ visitId(m_TempStr[0]);
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, dtype) \
+ if (AreEqual(m_TempStr[0].c_str(), #name)) { \
+ dtype theData; \
+ visit(theData); \
+ inValue = theData; \
+ } else
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ void visit(TTableModificationList &inModifications)
+ {
+ QT3DSU32 numMods = 0;
+ visit(numMods);
+ inModifications.resize(numMods);
+ for (QT3DSU32 idx = 0, end = inModifications.size(); idx < end; ++idx) {
+ STableModification &theModification(inModifications[idx]);
+ visitId(m_TempStr[0]);
+ if (AreEqual(m_TempStr[0].c_str(), "SetKey"))
+ theModification.m_Type = TableModificationType::SetKey;
+ else if (AreEqual(m_TempStr[0].c_str(), "RemoveKey"))
+ theModification.m_Type = TableModificationType::RemoveKey;
+ else {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+ visit(theModification.m_Entry.m_Key);
+ if (theModification.m_Type == TableModificationType::SetKey)
+ visit(theModification.m_Entry.m_Value);
+ }
+ }
+
+ void visit(TTableEntryList &inEntryList)
+ {
+ QT3DSU32 numMods = 0;
+ visit(numMods);
+ inEntryList.resize(numMods);
+ for (QT3DSU32 idx = 0, end = inEntryList.size(); idx < end; ++idx) {
+ STableEntry &theEntry(inEntryList[idx]);
+ visit(theEntry.m_Key);
+ visit(theEntry.m_Value);
+ }
+ }
+
+ void randomText(TDebugStr &outStr)
+ {
+ QT3DSU32 strLen = 0;
+ visit(strLen);
+ outStr.assign(m_ParseStr.begin(), m_ParseStr.begin() + strLen);
+ TrimForward(strLen);
+ }
+
+ void visitIdList(TDebugStrList &inList)
+ {
+ QT3DSU32 numItems = 0;
+ visit(numItems);
+ inList.resize(numItems);
+ for (QT3DSU32 idx = 0, end = numItems; idx < end; ++idx)
+ visitId(inList[idx]);
+ }
+
+ void visit(TTransitionIdList &inList)
+ {
+ QT3DSU32 numItems = 0;
+ visit(numItems);
+ inList.resize(numItems);
+ for (QT3DSU32 idx = 0, end = numItems; idx < end; ++idx)
+ inList[idx] = BreakpointTransition();
+ }
+
+ void visitEventName(TDebugStr &outStr) { visitId(outStr); }
+
+ SStateEnterBreakpoint BreakpointStateEnter()
+ {
+ SStateEnterBreakpoint retval;
+ SBreakpointSerializationVisitor<SStateEnterBreakpoint>::Visit(*this, retval);
+ return retval;
+ }
+
+ SStateExitBreakpoint BreakpointStateExit()
+ {
+ SStateExitBreakpoint retval;
+ SBreakpointSerializationVisitor<SStateExitBreakpoint>::Visit(*this, retval);
+ return retval;
+ }
+
+ STransitionId BreakpointTransition()
+ {
+ STransitionId retval;
+ SBreakpointSerializationVisitor<STransitionId>::Visit(*this, retval);
+ return retval;
+ }
+
+ void visit(SBreakpoint &breakpoint)
+ {
+ visitId(m_BreakpointTypeStr);
+#define HANDLE_BREAKPOINT_TYPE(enumType, bpType) \
+ if (AreEqual(STypeToName<bpType>::GetName(), m_BreakpointTypeStr.c_str())) { \
+ breakpoint = Breakpoint##enumType(); \
+ } else
+ ITERATE_BREAKPOINT_TYPES
+#undef HANDLE_BREAKPOINT_TYPE
+ // else is defined in the iterate breakpoints.
+ {
+ m_Valid = false;
+ m_CurrentHandler->error("Unrecognized breakpoint type: ",
+ m_BreakpointTypeStr.c_str());
+ }
+ }
+
+ template <typename TDataType>
+ void DoParseT(TDebugStr &inStr, QT3DSU64 inTimestamp, QT3DSI32 streamId,
+ TMessageHandler &inHandler)
+ {
+ TDataType theType;
+ m_ParseStr = inStr;
+ m_Valid = true;
+ m_CurrentHandler = &inHandler;
+ theType.Visit(*this);
+ if (m_Valid)
+ inHandler.OnMessage(streamId, inTimestamp, theType);
+ }
+
+ // destructive parsing.
+ void Parse(NVConstDataRef<QT3DSU8> data, TMessageHandler &inHandler)
+ {
+ m_ParseStr.assign((const char *)data.begin(), (const char *)data.end());
+ Trim(m_ParseStr);
+ size_t spacePos = m_ParseStr.find_first_of(' ');
+ if (spacePos == TDebugStr::npos) {
+ m_ParseStr.assign((const char *)data.begin(), (const char *)data.end());
+ inHandler.error("Failed to parse message: ", m_ParseStr.c_str());
+ return;
+ }
+ qt3ds::QT3DSI32 streamId = 0;
+ visitId(m_MessageHeaderStr);
+ visit(streamId);
+ qt3ds::QT3DSU64 timestamp = 0;
+ visit(timestamp);
+#define HANDLE_DEBUG_MESSAGE_TYPE(name) \
+ if (AreEqual(STypeToName<S##name>::GetName(), m_MessageHeaderStr.c_str())) { \
+ DoParseT<S##name>(m_ParseStr, timestamp, streamId, inHandler); \
+ } else
+ ITERATE_DEBUG_MESSAGE_TYPES
+#undef HANDLE_DEBUG_MESSAGE_TYPE
+ {
+ inHandler.error("Failed to parse message type: ", m_MessageHeaderStr.c_str());
+ }
+ }
+ };
+
+ struct SMessageSerializer
+ {
+ TDebugStr m_Message;
+ void appendSpace() { m_Message.append(1, ' '); }
+
+ void visitId(const TDebugStr &inStr)
+ {
+ m_Message.append(inStr);
+ appendSpace();
+ }
+ template <typename TDataType>
+ void rawTypeToStr(const TDataType &inType)
+ {
+ char8_t buf[256] = { 0 };
+ StringConversion<TDataType>().ToStr(inType, toDataRef(buf, 256));
+ m_Message.append(buf);
+ appendSpace();
+ }
+ void visit(QT3DSI32 inData) { rawTypeToStr(inData); }
+ void visit(QT3DSU32 inData) { rawTypeToStr(inData); }
+
+ void visit(QT3DSU64 inData) { rawTypeToStr(inData); }
+
+ void visit(bool inData) { rawTypeToStr(inData); }
+
+ void visit(const TDebugStr &inStr) { randomText(inStr); }
+
+ void visit(const SNil &) {}
+
+ void visit(const TTransitionIdList &inData)
+ {
+ QT3DSU32 len = (QT3DSU32)inData.size();
+ visit(len);
+ for (QT3DSU32 idx = 0; idx < len; ++idx)
+ Breakpoint(inData[idx]);
+ }
+
+ void randomText(const TDebugStr &outStr)
+ {
+ QT3DSU32 len = (QT3DSU32)outStr.size();
+ visit(len);
+ if (len)
+ m_Message.append(outStr);
+ appendSpace();
+ }
+
+ void visitIdList(const TDebugStrList &inList)
+ {
+ QT3DSU32 numItems = (QT3DSU32)inList.size();
+ visit(numItems);
+ for (QT3DSU32 idx = 0; idx < numItems; ++idx)
+ visitId(inList[idx]);
+ }
+
+ void visitEventName(const TDebugStr &outStr) { visitId(outStr); }
+ void visit(float inValue) { rawTypeToStr(inValue); }
+ template <typename TPtrType>
+ void visitOpaquePtr(const TPtrType *inPtr)
+ {
+ size_t ptrValue = reinterpret_cast<size_t>(inPtr);
+ QT3DSU64 fullValue = static_cast<QT3DSU64>(ptrValue);
+ visit(fullValue);
+ }
+
+ void visit(SDatamodelTable *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelUserData *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelFunction *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelCFunction *inData) { visitOpaquePtr(inData); }
+ void visit(SDatamodelThread *inData) { visitOpaquePtr(inData); }
+
+ void visit(const SDatamodelValue &inValue)
+ {
+ switch (inValue.getType()) {
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, dtype) \
+ case DatamodelValueTypes::name: { \
+ m_Message.append(#name); \
+ appendSpace(); \
+ visit(inValue.getData<dtype>()); \
+ break; \
+ }
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ void visit(const TTableModificationList &inModifications)
+ {
+ QT3DSU32 numItems = (QT3DSU32)inModifications.size();
+ visit(numItems);
+ for (QT3DSU32 idx = 0, end = inModifications.size(); idx < end; ++idx) {
+ const STableModification &theMod(inModifications[idx]);
+ switch (theMod.m_Type) {
+ case TableModificationType::SetKey: {
+ m_Message.append("SetKey");
+ appendSpace();
+ visit(theMod.m_Entry.m_Key);
+ visit(theMod.m_Entry.m_Value);
+ break;
+ }
+ case TableModificationType::RemoveKey: {
+ m_Message.append("RemoveKey");
+ appendSpace();
+ visit(theMod.m_Entry.m_Key);
+ break;
+ }
+ default:
+ QT3DS_ASSERT(false);
+ m_Message.append("badvalue");
+ appendSpace();
+ continue;
+ }
+ }
+ }
+ void visit(TTableEntryList &inEntries)
+ {
+ QT3DSU32 numItems = (QT3DSU32)inEntries.size();
+ visit(numItems);
+ for (QT3DSU32 idx = 0, end = inEntries.size(); idx < end; ++idx) {
+ const STableEntry &theEntry(inEntries[idx]);
+ visit(theEntry.m_Key);
+ visit(theEntry.m_Value);
+ }
+ }
+
+ template <typename TBreakpointType>
+ void Breakpoint(const TBreakpointType &inBp)
+ {
+ SBreakpointSerializationVisitor<TBreakpointType>::Visit(
+ *this, const_cast<TBreakpointType &>(inBp));
+ }
+
+ void visit(const SBreakpoint &inBp)
+ {
+ switch (inBp.getType()) {
+#define HANDLE_BREAKPOINT_TYPE(enumName, typeName) \
+ case BreakpointTypes::enumName: \
+ m_Message.append(STypeToName<typeName>::GetName()); \
+ appendSpace(); \
+ Breakpoint(inBp.getData<typeName>()); \
+ break;
+ ITERATE_BREAKPOINT_TYPES
+#undef HANDLE_BREAKPOINT_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+
+ template <typename TMsgType>
+ void Serialize(QT3DSI32 inStreamId, const TMsgType &inMsg, QT3DSU64 inTimestamp)
+ {
+ m_Message.clear();
+ m_Message.append(STypeToName<TMsgType>::GetName());
+ appendSpace();
+ visit(inStreamId);
+ visit(inTimestamp);
+ const_cast<TMsgType &>(inMsg).Visit(*this);
+ }
+
+ NVConstDataRef<QT3DSU8> ToRawMessage()
+ {
+ if (m_Message.size() == 0)
+ return NVConstDataRef<QT3DSU8>();
+ return NVConstDataRef<QT3DSU8>(reinterpret_cast<const QT3DSU8 *>(m_Message.c_str()),
+ (QT3DSU32)m_Message.size());
+ }
+ };
+
+ template <typename TLhs, typename TRhs>
+ struct SMsgComparer
+ {
+ static bool Compare(const TLhs &, const TRhs &) { return false; }
+ };
+#define HANDLE_DEBUG_MESSAGE_TYPE(typeName) \
+ template <> \
+ struct SMsgComparer<S##typeName, S##typeName> \
+ { \
+ static bool Compare(const S##typeName &lhs, const S##typeName &rhs) { return lhs == rhs; } \
+ };
+ ITERATE_DEBUG_MESSAGE_TYPES;
+#undef HANDLE_DEBUG_MESSAGE_TYPE
+
+ template <typename TMessageType>
+ struct STestMessageHandler
+ {
+ bool m_Result;
+ QT3DSU64 m_Timestamp;
+ QT3DSU32 m_StreamId;
+ const TMessageType &m_ExpectedResult;
+
+ STestMessageHandler(QT3DSU32 sid, QT3DSU64 ts, const TMessageType &res)
+ : m_Result(false)
+ , m_Timestamp(ts)
+ , m_StreamId(sid)
+ , m_ExpectedResult(res)
+ {
+ }
+
+ template <typename TCrapType>
+ void OnMessage(QT3DSU32 sid, QT3DSU64 ts, const TCrapType &msgType)
+ {
+ if (ts == m_Timestamp && m_StreamId == sid)
+ m_Result =
+ SMsgComparer<TCrapType, TMessageType>::Compare(msgType, m_ExpectedResult);
+ }
+
+ void error(const char8_t *, const char8_t *) {}
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h
new file mode 100644
index 00000000..340f46cb
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateDebuggerValues.h
@@ -0,0 +1,540 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_DEBUGGER_VALUES_H
+#define QT3DS_STATE_DEBUGGER_VALUES_H
+#pragma once
+#include "Qt3DSStateDebugger.h"
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+
+namespace qt3ds {
+namespace state {
+ namespace debugger {
+ using namespace qt3ds::state::editor;
+
+ // Force compile error if unsupported datatype requested
+ template <typename TDataType>
+ struct SDebugValueTypeMap
+ {
+ };
+
+ template <>
+ struct SDebugValueTypeMap<TDebugStr>
+ {
+ static DebugValueTypes::Enum GetType() { return DebugValueTypes::String; }
+ };
+ template <>
+ struct SDebugValueTypeMap<TDebugStrList>
+ {
+ static DebugValueTypes::Enum GetType() { return DebugValueTypes::StringList; }
+ };
+ template <>
+ struct SDebugValueTypeMap<TTransitionIdList>
+ {
+ static DebugValueTypes::Enum GetType() { return DebugValueTypes::TransitionIdList; }
+ };
+
+ struct SDebugValueUnionTraits
+ {
+ typedef DebugValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(TDebugStrList),
+ };
+
+ static TIdType getNoDataId() { return DebugValueTypes::NoDebugValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SDebugValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case DebugValueTypes::String:
+ return inVisitor(*NVUnionCast<TDebugStr *>(inData));
+ case DebugValueTypes::StringList:
+ return inVisitor(*NVUnionCast<TDebugStrList *>(inData));
+ case DebugValueTypes::TransitionIdList:
+ return inVisitor(*NVUnionCast<TTransitionIdList *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case DebugValueTypes::NoDebugValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case DebugValueTypes::String:
+ return inVisitor(*NVUnionCast<const TDebugStr *>(inData));
+ case DebugValueTypes::StringList:
+ return inVisitor(*NVUnionCast<const TDebugStrList *>(inData));
+ case DebugValueTypes::TransitionIdList:
+ return inVisitor(*NVUnionCast<const TTransitionIdList *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case DebugValueTypes::NoDebugValue:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SDebugValueUnionTraits,
+ SDebugValueUnionTraits::
+ TBufferSize>,
+ SDebugValueUnionTraits::TBufferSize>
+ TDebugValueUnionType;
+
+ struct SDebugValue : public TDebugValueUnionType
+ {
+ SDebugValue() {}
+
+ SDebugValue(const SDebugValue &inOther)
+ : TDebugValueUnionType(static_cast<const TDebugValueUnionType &>(inOther))
+ {
+ }
+
+ SDebugValue(const char8_t *inOther)
+ : TDebugValueUnionType(TDebugStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SDebugValue(const TDataType &inDt)
+ : TDebugValueUnionType(inDt)
+ {
+ }
+
+ SDebugValue &operator=(const SDebugValue &inOther)
+ {
+ TDebugValueUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SDebugValue &inOther) const
+ {
+ return TDebugValueUnionType::operator==(inOther);
+ }
+ bool operator!=(const SDebugValue &inOther) const
+ {
+ return TDebugValueUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == DebugValueTypes::NoDebugValue; }
+ };
+
+ struct SDebugValueOpt : public Option<SDebugValue>
+ {
+ SDebugValueOpt(const SDebugValueOpt &inOther)
+ : Option<SDebugValue>(inOther)
+ {
+ }
+ SDebugValueOpt(const Empty &)
+ : Option<SDebugValue>()
+ {
+ }
+ SDebugValueOpt() {}
+ template <typename TDataType>
+ SDebugValueOpt(const TDataType &inOther)
+ : Option<SDebugValue>(SDebugValue(inOther))
+ {
+ }
+ SDebugValueOpt &operator=(const SDebugValueOpt &inOther)
+ {
+ Option<SDebugValue>::operator=(inOther);
+ return *this;
+ }
+ };
+
+ // Breakpoint subtypes
+
+ struct SStateEnterBreakpoint
+ {
+ TDebugStr m_ObjectId;
+ SStateEnterBreakpoint(const TDebugStr &inObj = TDebugStr())
+ : m_ObjectId(inObj)
+ {
+ }
+ bool operator==(const SStateEnterBreakpoint &inOther) const
+ {
+ return m_ObjectId == inOther.m_ObjectId;
+ }
+ };
+
+ struct SStateExitBreakpoint
+ {
+ TDebugStr m_ObjectId;
+ SStateExitBreakpoint(const TDebugStr &inObj = TDebugStr())
+ : m_ObjectId(inObj)
+ {
+ }
+ bool operator==(const SStateExitBreakpoint &inOther) const
+ {
+ return m_ObjectId == inOther.m_ObjectId;
+ }
+ };
+
+ template <typename TDataType>
+ struct SBreakpointTypeMap
+ {
+ static BreakpointTypes::Enum GetType()
+ {
+ return BreakpointTypes::UnknownBreakpointType;
+ }
+ };
+
+ template <>
+ struct SBreakpointTypeMap<SStateEnterBreakpoint>
+ {
+ static BreakpointTypes::Enum GetType() { return BreakpointTypes::StateEnter; }
+ };
+ template <>
+ struct SBreakpointTypeMap<SStateExitBreakpoint>
+ {
+ static BreakpointTypes::Enum GetType() { return BreakpointTypes::StateExit; }
+ };
+ template <>
+ struct SBreakpointTypeMap<STransitionId>
+ {
+ static BreakpointTypes::Enum GetType() { return BreakpointTypes::Transition; }
+ };
+
+ struct SBreakpointUnionTraits
+ {
+ typedef BreakpointTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(STransitionId),
+ };
+
+ static TIdType getNoDataId() { return BreakpointTypes::UnknownBreakpointType; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SBreakpointTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case BreakpointTypes::StateEnter:
+ return inVisitor(*NVUnionCast<SStateEnterBreakpoint *>(inData));
+ case BreakpointTypes::StateExit:
+ return inVisitor(*NVUnionCast<SStateExitBreakpoint *>(inData));
+ case BreakpointTypes::Transition:
+ return inVisitor(*NVUnionCast<STransitionId *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case BreakpointTypes::UnknownBreakpointType:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case BreakpointTypes::StateEnter:
+ return inVisitor(*NVUnionCast<const SStateEnterBreakpoint *>(inData));
+ case BreakpointTypes::StateExit:
+ return inVisitor(*NVUnionCast<const SStateExitBreakpoint *>(inData));
+ case BreakpointTypes::Transition:
+ return inVisitor(*NVUnionCast<const STransitionId *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case BreakpointTypes::UnknownBreakpointType:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SBreakpointUnionTraits,
+ SBreakpointUnionTraits::
+ TBufferSize>,
+ SBreakpointUnionTraits::TBufferSize>
+ TBreakpointUnionType;
+
+ struct SBreakpoint : public TBreakpointUnionType
+ {
+ SBreakpoint() {}
+
+ SBreakpoint(const SBreakpoint &inOther)
+ : TBreakpointUnionType(static_cast<const TBreakpointUnionType &>(inOther))
+ {
+ }
+
+ SBreakpoint(const char8_t *inOther)
+ : TBreakpointUnionType(TEditorStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SBreakpoint(const TDataType &inDt)
+ : TBreakpointUnionType(inDt)
+ {
+ }
+
+ SBreakpoint &operator=(const SBreakpoint &inOther)
+ {
+ TBreakpointUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SBreakpoint &inOther) const
+ {
+ return TBreakpointUnionType::operator==(inOther);
+ }
+ bool operator!=(const SBreakpoint &inOther) const
+ {
+ return TBreakpointUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == SBreakpointUnionTraits::getNoDataId(); }
+ };
+
+ struct SNil
+ {
+ bool operator==(const SNil &) const { return true; }
+ };
+
+#define ITERATE_DATAMODEL_VALUE_TYPES \
+ HANDLE_DATAMODEL_VALUE_TYPE(Number, float) \
+ HANDLE_DATAMODEL_VALUE_TYPE(String, TDebugStr) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Nil, SNil) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Boolean, bool) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Table, SDatamodelTable *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(UserData, SDatamodelUserData *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Function, SDatamodelFunction *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(CFunction, SDatamodelCFunction *) \
+ HANDLE_DATAMODEL_VALUE_TYPE(Thread, SDatamodelThread *)
+
+ template <typename TDataType>
+ struct SDatamodelValueTypeMap
+ {
+ };
+
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, type) \
+ template <> \
+ struct SDatamodelValueTypeMap<type> \
+ { \
+ static DatamodelValueTypes::Enum GetType() { return DatamodelValueTypes::name; } \
+ };
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+
+ struct SDatamodelValueUnionTraits
+ {
+ typedef DatamodelValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(TDebugStr),
+ };
+
+ static TIdType getNoDataId() { return DatamodelValueTypes::UnknownType; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SDatamodelValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, type) \
+ case DatamodelValueTypes::name: \
+ return inVisitor(*NVUnionCast<type *>(inData));
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ case DatamodelValueTypes::UnknownType:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+#define HANDLE_DATAMODEL_VALUE_TYPE(name, type) \
+ case DatamodelValueTypes::name: \
+ return inVisitor(*NVUnionCast<const type *>(inData));
+ ITERATE_DATAMODEL_VALUE_TYPES
+#undef HANDLE_DATAMODEL_VALUE_TYPE
+ default:
+ QT3DS_ASSERT(false);
+ case DatamodelValueTypes::UnknownType:
+ return inVisitor();
+ }
+ }
+ };
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SDatamodelValueUnionTraits,
+ SDatamodelValueUnionTraits::
+ TBufferSize>,
+ SDatamodelValueUnionTraits::TBufferSize>
+ TDatamodelValueUnionType;
+
+ struct SDatamodelValue : public TDatamodelValueUnionType
+ {
+ SDatamodelValue() {}
+
+ SDatamodelValue(const SDatamodelValue &inOther)
+ : TDatamodelValueUnionType(static_cast<const TDatamodelValueUnionType &>(inOther))
+ {
+ }
+
+ SDatamodelValue(const char8_t *inOther)
+ : TDatamodelValueUnionType(TDebugStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SDatamodelValue(const TDataType &inDt)
+ : TDatamodelValueUnionType(inDt)
+ {
+ }
+
+ SDatamodelValue &operator=(const SDatamodelValue &inOther)
+ {
+ TDatamodelValueUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SDatamodelValue &inOther) const
+ {
+ return TDatamodelValueUnionType::operator==(inOther);
+ }
+ bool operator!=(const SDatamodelValue &inOther) const
+ {
+ return TDatamodelValueUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == SDatamodelValueUnionTraits::getNoDataId(); }
+ };
+
+ struct STableEntry
+ {
+ TDebugStr m_Key;
+ SDatamodelValue m_Value;
+ STableEntry(const TDebugStr &inKey = TDebugStr(),
+ const SDatamodelValue &inValue = SDatamodelValue())
+ : m_Key(inKey)
+ , m_Value(inValue)
+ {
+ }
+
+ bool operator==(const STableEntry &inOther) const
+ {
+ return m_Key == inOther.m_Key && m_Value == inOther.m_Value;
+ }
+ };
+
+ struct TableModificationType
+ {
+ enum Enum {
+ UnknownModification = 0,
+ SetKey = 1,
+ RemoveKey = 2,
+ };
+ };
+
+ struct STableModification
+ {
+ STableEntry m_Entry;
+ TableModificationType::Enum m_Type;
+ STableModification(
+ const STableEntry &inEntry = STableEntry(),
+ TableModificationType::Enum inEnum = TableModificationType::UnknownModification)
+ : m_Entry(inEntry)
+ , m_Type(inEnum)
+ {
+ }
+ bool operator==(const STableModification &inMod) const
+ {
+ return inMod.m_Entry == m_Entry && inMod.m_Type == m_Type;
+ }
+ };
+
+ typedef eastl::vector<STableModification> TTableModificationList;
+ }
+}
+}
+
+#ifndef _INTEGRITYPLATFORM
+namespace qt3ds {
+namespace foundation {
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SNil>
+ {
+ void destruct(qt3ds::state::debugger::SNil &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelTable *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelTable *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelUserData *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelUserData *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelFunction *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelFunction *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelCFunction *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelCFunction *) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::state::debugger::SDatamodelThread *>
+ {
+ void destruct(qt3ds::state::debugger::SDatamodelThread *) {}
+ };
+}
+}
+#endif
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h
new file mode 100644
index 00000000..6edbe366
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/debugger/Qt3DSStateTest.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 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$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_TEST_H
+#define QT3DS_STATE_TEST_H
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/Qt3DSOption.h"
+
+namespace qt3ds {
+namespace state {
+ namespace test {
+
+ struct LogType
+ {
+ enum Enum {
+ Error = 0,
+ Info = 1,
+ };
+ };
+
+ struct STestResults
+ {
+ QT3DSU32 m_TotalTests;
+ QT3DSU32 m_PassingTests;
+ STestResults(QT3DSU32 totalTests = 0, QT3DSU32 testPassed = 0)
+ : m_TotalTests(totalTests)
+ , m_PassingTests(testPassed)
+ {
+ }
+ STestResults &operator+=(const STestResults &inOther)
+ {
+ m_TotalTests += inOther.m_TotalTests;
+ m_PassingTests += inOther.m_PassingTests;
+ return *this;
+ }
+
+ bool Failed() const { return m_TotalTests != m_PassingTests; }
+ bool Passed() const { return !Failed(); }
+ };
+
+ class IDataLogger
+ {
+ protected:
+ virtual ~IDataLogger() {}
+ public:
+ virtual void Log(LogType::Enum inLogType, const char8_t *file, int line,
+ const char8_t *message) = 0;
+ };
+
+ // Run a data test returning a true/false result for pass/fail.
+ class IDataTest
+ {
+ public:
+ static Option<STestResults> RunFile(const char8_t *fname, const char8_t *inRootDir,
+ IDataLogger &inLogger);
+ };
+ }
+}
+}
+
+#endif