diff options
author | Pasi Keränen <pasi.keranen@qt.io> | 2019-04-12 12:47:08 +0300 |
---|---|---|
committer | Pasi Keränen <pasi.keranen@qt.io> | 2019-04-15 07:10:26 +0000 |
commit | cef006904e875b7424b7fa13ea3dc2f9762081d9 (patch) | |
tree | 1e1cb4f9e58c9e692cf5c2f5fc5687db4a313d2a /src/Runtime/Source/stateapplication/debugger | |
parent | 46000b6fb0fca6594827875695c564cf5d7d070c (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')
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 |