diff options
Diffstat (limited to 'NvBlast/sdk/toolkit/source')
23 files changed, 5838 insertions, 0 deletions
diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.cpp new file mode 100644 index 0000000..028e0f1 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.cpp @@ -0,0 +1,434 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "NvBlastPreprocessor.h" + +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastTkActorImpl.h" +#include "NvBlastTkGroupImpl.h" +#include "NvBlastTkAssetImpl.h" +#include "NvBlastTkFamilyImpl.h" +#include "NvBlastTkJointImpl.h" + +#include "NvBlast.h" +#include "NvBlastAssert.h" +#include "NvBlastMemory.h" + +#include "Px.h" +#include "PxFileBuf.h" +#include "PxAllocatorCallback.h" +#include "PxTransform.h" + +using namespace physx::general_PxIOStream2; + + +namespace Nv +{ +namespace Blast +{ + +TkActorImpl* TkActorImpl::create(const TkActorDesc& desc) +{ + const TkAssetImpl* asset = static_cast<const TkAssetImpl*>(desc.asset); + + TkFamilyImpl* family = TkFamilyImpl::create(asset); + + NvBlastFamily* familyLL = family->getFamilyLLInternal(); + TkArray<char>::type scratch((uint32_t)NvBlastFamilyGetRequiredScratchForCreateFirstActor(familyLL, TkFrameworkImpl::get()->log)); + NvBlastActor* actorLL = NvBlastFamilyCreateFirstActor(familyLL, &desc, scratch.begin(), TkFrameworkImpl::get()->log); + if (actorLL == nullptr) + { + NVBLASTTK_LOG_ERROR("TkActorImpl::create: low-level actor could not be created."); + return nullptr; + } + + TkActorImpl* actor = family->addActor(actorLL); + + // Add internal joints + if (actor != nullptr) + { + const uint32_t internalJointCount = asset->getJointDescCountInternal(); + const TkAssetJointDesc* jointDescs = asset->getJointDescsInternal(); + const NvBlastSupportGraph graph = asset->getGraph(); + TkJointImpl* joints = family->getInternalJoints(); + for (uint32_t jointNum = 0; jointNum < internalJointCount; ++jointNum) + { + const TkAssetJointDesc& assetJointDesc = jointDescs[jointNum]; + NVBLAST_ASSERT(assetJointDesc.nodeIndices[0] < graph.nodeCount && assetJointDesc.nodeIndices[1] < graph.nodeCount); + TkJointDesc jointDesc; + jointDesc.families[0] = jointDesc.families[1] = family; + jointDesc.chunkIndices[0] = graph.chunkIndices[assetJointDesc.nodeIndices[0]]; + jointDesc.chunkIndices[1] = graph.chunkIndices[assetJointDesc.nodeIndices[1]]; + jointDesc.attachPositions[0] = assetJointDesc.attachPositions[0]; + jointDesc.attachPositions[1] = assetJointDesc.attachPositions[1]; + TkJointImpl* joint = new (joints + jointNum) TkJointImpl(jointDesc, family); + actor->addJoint(joint->m_links[0]); + } + } + + return actor; +} + + +//////// Member functions //////// + +TkActorImpl::TkActorImpl() + : m_actorLL(nullptr) + , m_family(nullptr) + , m_group(nullptr) + , m_groupJobIndex(invalidIndex<uint32_t>()) + , m_flags(0) + , m_jointCount(0) +{ +#if NV_PROFILE + NvBlastTimersReset(&m_timers); +#endif +} + + +TkActorImpl::~TkActorImpl() +{ +} + + +void TkActorImpl::release() +{ + // Disassoaciate all joints + + // Copy joint array for safety against implementation of joint->setActor + TkJointImpl** joints = reinterpret_cast<TkJointImpl**>(NvBlastAlloca(sizeof(TkJointImpl*)*getJointCountInternal())); + TkJointImpl** stop = joints + getJointCountInternal(); + TkJointImpl** jointHandle = joints; + for (JointIt j(*this); (bool)j; ++j) + { + *jointHandle++ = *j; + } + jointHandle = joints; + while (jointHandle < stop) + { + NVBLAST_ASSERT(*jointHandle != nullptr); + NVBLAST_ASSERT((*jointHandle)->getDataInternal().actors[0] == this || (*jointHandle)->getDataInternal().actors[1] == this); + (*jointHandle++)->setActors(nullptr, nullptr); + } + NVBLAST_ASSERT(getJointCountInternal() == 0); + + if (m_group != nullptr) + { + m_group->removeActor(*this); + } + + if (m_actorLL != nullptr) + { + NvBlastActorDeactivate(m_actorLL, TkFrameworkImpl::get()->log); + } + + if (m_family != nullptr) + { + m_family->removeActor(this); + + // Make sure we dispatch any remaining events when this family is emptied, since it will no longer be done by any group + if (m_family->getActorCountInternal() == 0) + { + m_family->getQueue().dispatch(); + } + } +} + + +const NvBlastActor* TkActorImpl::getActorLL() const +{ + return m_actorLL; +} + + +TkFamily& TkActorImpl::getFamily() const +{ + return getFamilyImpl(); +} + + +uint32_t TkActorImpl::getIndex() const +{ + return getIndexInternal(); +} + + +TkGroup* TkActorImpl::getGroup() const +{ + return getGroupImpl(); +} + + +TkGroup* TkActorImpl::removeFromGroup() +{ + if (m_group == nullptr) + { + NVBLASTTK_LOG_WARNING("TkActorImpl::removeFromGroup: actor not in a group."); + return nullptr; + } + + if (m_group->isProcessing()) + { + NVBLASTTK_LOG_ERROR("TkActorImpl::removeFromGroup: cannot alter Group while processing."); + return nullptr; + } + + TkGroup* group = m_group; + + return m_group->removeActor(*this) ? group : nullptr; +} + + +NvBlastFamily* TkActorImpl::getFamilyLL() const +{ + return m_family->getFamilyLLInternal(); +} + + +const TkAsset* TkActorImpl::getAsset() const +{ + return m_family->getAssetImpl(); +} + + +uint32_t TkActorImpl::getVisibleChunkCount() const +{ + return NvBlastActorGetVisibleChunkCount(m_actorLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkActorImpl::getVisibleChunkIndices(uint32_t* visibleChunkIndices, uint32_t visibleChunkIndicesSize) const +{ + return NvBlastActorGetVisibleChunkIndices(visibleChunkIndices, visibleChunkIndicesSize, m_actorLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkActorImpl::getGraphNodeCount() const +{ + return NvBlastActorGetGraphNodeCount(m_actorLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkActorImpl::getGraphNodeIndices(uint32_t* graphNodeIndices, uint32_t graphNodeIndicesSize) const +{ + return NvBlastActorGetGraphNodeIndices(graphNodeIndices, graphNodeIndicesSize, m_actorLL, TkFrameworkImpl::get()->log); +} + + +const float* TkActorImpl::getBondHealths() const +{ + return NvBlastActorGetBondHealths(m_actorLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkActorImpl::getSplitMaxActorCount() const +{ + return NvBlastActorGetMaxActorCountForSplit(m_actorLL, TkFrameworkImpl::get()->log); +} + + +bool TkActorImpl::isDamaged() const +{ + NVBLAST_ASSERT(!m_flags.isSet(TkActorFlag::DAMAGED) || (m_flags.isSet(TkActorFlag::DAMAGED) && m_flags.isSet(TkActorFlag::PENDING))); + return m_flags.isSet(TkActorFlag::DAMAGED); +} + + +void TkActorImpl::markAsDamaged() +{ + m_flags |= TkActorFlag::DAMAGED; + makePending(); +} + + +void TkActorImpl::makePending() +{ + if (m_group != nullptr && !isPending()) + { + m_group->enqueue(this); + } + + m_flags |= TkActorFlag::PENDING; +} + + +TkActorImpl::operator Nv::Blast::TkActorData() const +{ + TkActorData data = { m_family, userData, getIndex() }; + return data; +} + + +void TkActorImpl::damage(const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) +{ + PERF_SCOPE_L("TkActor::damage"); + + if (m_group == nullptr) + { + NVBLASTTK_LOG_WARNING("TkActor::damage: actor is not in a group, cannot fracture."); + return; + } + + if (m_group->isProcessing()) + { + NVBLASTTK_LOG_WARNING("TkActor::damage: group is being processed, cannot fracture this actor."); + return; + } + + if (NvBlastActorCanFracture(m_actorLL, TkFrameworkImpl::get()->log)) + { + m_damageBuffer.pushBack(DamageData(program, programParams)); + makePending(); + } +} + + +void TkActorImpl::damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize) +{ + damage(program, damageDesc, descSize, m_family->getMaterial()); +} + + +void TkActorImpl::damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize, const void* material) +{ + PERF_SCOPE_L("TkActor::damage"); + + if (m_group == nullptr) + { + NVBLASTTK_LOG_WARNING("TkActor::damage: actor is not in a group, cannot fracture."); + return; + } + + if (m_group->isProcessing()) + { + NVBLASTTK_LOG_WARNING("TkActor::damage: group is being processed, cannot fracture this actor."); + return; + } + + if (NvBlastActorCanFracture(m_actorLL, TkFrameworkImpl::get()->log)) + { + bool appended = false; + for (auto& damageData : m_damageBuffer) + { + if (damageData.tryAppend(program, material, damageDesc, descSize)) + { + appended = true; + break; + } + } + + if (!appended) + { + m_damageBuffer.pushBack(DamageData(program, material, damageDesc, descSize)); + } + + makePending(); + } +} + + +void TkActorImpl::generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) const +{ + PERF_SCOPE_L("TkActor::generateFracture"); + + if (m_group && m_group->isProcessing()) + { + NVBLASTTK_LOG_WARNING("TkActor::generateFracture: group is being processed, cannot fracture this actor."); + return; + } + + // const context, must make m_timers mutable otherwise + NvBlastActorGenerateFracture(commands, m_actorLL, program, programParams, TkFrameworkImpl::get()->log, const_cast<NvBlastTimers*>(&m_timers)); +} + + +void TkActorImpl::applyFracture(NvBlastFractureBuffers* eventBuffers, const NvBlastFractureBuffers* commands) +{ + PERF_SCOPE_L("TkActor::applyFracture"); + + if (m_group && m_group->isProcessing()) + { + NVBLASTTK_LOG_WARNING("TkActor::applyFracture: group is being processed, cannot fracture this actor."); + return; + } + + NvBlastActorApplyFracture(eventBuffers, m_actorLL, commands, TkFrameworkImpl::get()->log, &m_timers); + + if (commands->chunkFractureCount > 0 || commands->bondFractureCount > 0) + { + markAsDamaged(); + + TkFractureCommands* fevt = getFamilyImpl().getQueue().allocData<TkFractureCommands>(); + fevt->tkActorData = *this; + fevt->buffers = *commands; + getFamilyImpl().getQueue().addEvent(fevt); + getFamilyImpl().getQueue().dispatch(); + } +} + + +uint32_t TkActorImpl::getJointCount() const +{ + return getJointCountInternal(); +} + + +uint32_t TkActorImpl::getJoints(TkJoint** joints, uint32_t jointsSize) const +{ + uint32_t jointsWritten = 0; + + for (JointIt j(*this); (bool)j && jointsWritten < jointsSize; ++j) + { + joints[jointsWritten++] = *j; + } + + return jointsWritten; +} + + +//////// TkActorImpl::DamageData methods //////// + +static bool operator==(const NvBlastDamageProgram& lhs, const NvBlastDamageProgram& rhs) +{ + return lhs.graphShaderFunction == rhs.graphShaderFunction && lhs.subgraphShaderFunction == rhs.subgraphShaderFunction; +} + + +TkActorImpl::DamageData::DamageData(const NvBlastDamageProgram& program, const NvBlastProgramParams* params) + : m_program(program), m_programParams(params), m_damageDescCount(0) +{ +} + + +TkActorImpl::DamageData::DamageData(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize) + : m_program(program), m_material(material), m_damageDescs((char*)desc, (char*)desc + descSize), m_damageDescCount(1) +{ +} + + +bool TkActorImpl::DamageData::tryAppend(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize) +{ + if (getType() == Buffered && m_program == program && m_material == material) + { + const uint32_t currentDescSize = m_damageDescs.size() / m_damageDescCount; + if (descSize == currentDescSize) + { + const uint32_t s = m_damageDescs.size(); + m_damageDescs.resizeUninitialized(s + static_cast<uint32_t>(descSize)); + memcpy(m_damageDescs.begin() + s, desc, descSize); + m_damageDescCount++; + return true; + } + } + return false; +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.h new file mode 100644 index 0000000..4d65660 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkActorImpl.h @@ -0,0 +1,375 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKACTORIMPL_H +#define NVBLASTTKACTORIMPL_H + + +#include "NvBlastTkCommon.h" + +#include "NvBlastAssert.h" +#include "NvBlastDLink.h" +#include "NvBlastIteratorBase.h" + +#include "NvBlastTkJointImpl.h" + +#include "NvBlast.h" + +#include "NvBlastTkActor.h" + +#include "PxFlags.h" + +namespace Nv +{ +namespace Blast +{ + +// Forward declarations: +class TkGroupImpl; +class TkFamilyImpl; +class TkAssetImpl; +class TkJointImpl; + + +/** +Struct-enum for actor status flags, used in TkGroup processing. +*/ +struct TkActorFlag +{ + enum Enum + { + DAMAGED = (1 << 0), //!< The actor had fractures applied successfully and will take the split step. + PENDING = (1 << 1), //!< The actor will be processed when its group executes, used to update job queues when moving group. + }; +}; + + +/** +Implementation of TkActor. +*/ +class TkActorImpl : public TkActor +{ +public: + TkActorImpl(); + ~TkActorImpl(); + + // Begin TkActor + virtual const NvBlastActor* getActorLL() const override; + + virtual TkFamily& getFamily() const override; + + virtual uint32_t getIndex() const override; + + virtual TkGroup* getGroup() const override; + + virtual TkGroup* removeFromGroup() override; + + virtual const TkAsset* getAsset() const override; + + virtual uint32_t getVisibleChunkCount() const override; + + virtual uint32_t getVisibleChunkIndices(uint32_t* visibleChunkIndices, uint32_t visibleChunkIndicesSize) const override; + + virtual uint32_t getGraphNodeCount() const override; + + virtual uint32_t getGraphNodeIndices(uint32_t* graphNodeIndices, uint32_t graphNodeIndicesSize) const override; + + virtual const float* getBondHealths() const override; + + virtual uint32_t getSplitMaxActorCount() const override; + + virtual void damage(const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) override; + virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize) override; + virtual void damage(const NvBlastDamageProgram& program, const void* damageDesc, uint32_t descSize, const void* material) override; + + virtual bool isPending() const override; + + virtual void generateFracture(NvBlastFractureBuffers* commands, const NvBlastDamageProgram& program, const NvBlastProgramParams* programParams) const override; + + virtual void applyFracture(NvBlastFractureBuffers* eventBuffers, const NvBlastFractureBuffers* commands) override; + + virtual uint32_t getJointCount() const override; + + virtual uint32_t getJoints(TkJoint** joints, uint32_t jointsSize) const override; + // End TkActor + + // Begin TkObject + virtual void release() override; + // End TkObject + + + // Public methods + + /** + Factory create method. + + \param[in] desc Actor descriptor set by the user. + + \return a pointer to a new TkActorImpl object if successful, NULL otherwise. + */ + static TkActorImpl* create(const TkActorDesc& desc); + + /** + TkActorImpl objects are created in an array within a TkFamilyImpl. Actors may become + 'inactive' without their memory being freed. If inactive, the actor should be treated as if + it has been released. + + \return the active status of this TkActorImpl. + */ + bool isActive() const; + + /** + Utility to return the low-level family to which the low-level actor belongs. + + \return a pointer to the NvBlastFamily to which the low-level actor belongs. + */ + NvBlastFamily* getFamilyLL() const; + + /** + Utility to access the TkFamily to which this actor belongs. + + \return a reference to the TkFamilyImpl to which this TkActorImpl belongs. + */ + TkFamilyImpl& getFamilyImpl() const; + + /** + \return the index of this actor with its TkFamilyImpl. + */ + uint32_t getIndexInternal() const; + + /** + Access to the group to which this actor belongs, if any. + + \return a pointer to the TkGroupImpl to which this TkActorImpl belongs, if any. If this actor is not in a group, this function returns NULL. + */ + TkGroupImpl* getGroupImpl() const; + + /** + Access to the low-level actor associated with this TkActorImpl. + + \return a pointer to the NvBlastActor associated with this TkActorImpl. If this actor is inactive (see isActive), this function returns NULL. + */ + NvBlastActor* getActorLLInternal() const; + + /** + \return the number of TkJointImpl objects that reference this actor. + */ + uint32_t getJointCountInternal() const; + + /** + Joint iterator. Usage: + + Given a TkActorImpl a, + + for (TkActorImpl::JointIt i(a); (bool)i; ++i) + { + TkJointImpl* joint = (TkJointImpl*)i; + // ... + } + */ + class JointIt : public DList::It + { + public: + /** Constructed from an actor. */ + JointIt(const TkActorImpl& actor, Direction dir = Forward); + + /** Current joint. */ + TkJointImpl* operator * () const; + }; + + /** + Implicit converter to TkActorData for events. + */ + operator Nv::Blast::TkActorData() const; + +private: + /** + Used to buffer damage for deferred fracture generation. Unifies 2 different ways to pass and store damage data. + */ + struct DamageData + { + DamageData(const NvBlastDamageProgram& program, const NvBlastProgramParams* params); + DamageData(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize); + + bool tryAppend(const NvBlastDamageProgram& program, const void* material, const void* desc, uint32_t descSize); + void generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastActor* actorLL, NvBlastTimers* timers) const; + + enum Type + { + Plain, + Buffered + }; + + Type getType() const; + + NvBlastDamageProgram m_program; + union + { + const void* m_material; //!< for Buffered type + const NvBlastProgramParams* m_programParams; //!< for Plain type + }; + TkArray<char>::type m_damageDescs; + uint32_t m_damageDescCount; + }; + + + /** + Functions to raise or check 'damaged' state: this actor will take the split step. + 'damaged' actors automatically become 'pending' also. + */ + void markAsDamaged(); + bool isDamaged() const; + + /** + Raise actor to 'pending' state: this actor will be processed when its group executes next. + Enqueues the actor in its group's job list if a group is set. Otherwise the group will enqueue the actor when it is added. + */ + void makePending(); + + /** + Functions to add or remove an internal reference to a joint. (Joints and actors mutually reference each other.) + */ + void addJoint(TkJointLink& jointLink); + void removeJoint(TkJointLink& jointLink); + + + // Data + + NvBlastActor* m_actorLL; //!< The low-level actor associated with this actor + TkFamilyImpl* m_family; //!< The TkFamilyImpl to which this actor belongs + TkGroupImpl* m_group; //!< The TkGroupImpl (if any) to which this actor belongs + uint32_t m_groupJobIndex; //!< The index of this actor's job within its group's job list + physx::PxFlags<TkActorFlag::Enum, char> m_flags; //!< Status flags for this actor + TkArray<DamageData>::type m_damageBuffer; //!< Buffered damage input + uint32_t m_jointCount; //!< The number of joints referenced in m_jointList + DList m_jointList; //!< A doubly-linked list of joint references + +//#if NV_PROFILE + NvBlastTimers m_timers; //!< If profiling, each actor stores timing data +//#endif + + friend class TkWorker; // m_damageBuffer and m_flags + friend class TkGroupImpl; + friend class TkFamilyImpl; + friend class TkJointImpl; + friend class TkFrameworkImpl; +}; + + +//////// TkActorImpl inline methods //////// + +NV_INLINE TkFamilyImpl& TkActorImpl::getFamilyImpl() const +{ + NVBLAST_ASSERT(m_family != nullptr); + + return *m_family; +} + + +NV_INLINE uint32_t TkActorImpl::getIndexInternal() const +{ + NVBLAST_ASSERT(isActive()); + return NvBlastActorGetIndex(m_actorLL, TkFrameworkImpl::get()->log); +} + + +NV_INLINE NvBlastActor* TkActorImpl::getActorLLInternal() const +{ + return m_actorLL; +} + + +NV_INLINE uint32_t TkActorImpl::getJointCountInternal() const +{ + return m_jointCount; +} + + +NV_INLINE TkGroupImpl* TkActorImpl::getGroupImpl() const +{ + return m_group; +} + + +NV_INLINE bool TkActorImpl::isActive() const +{ + return m_actorLL != nullptr; +} + + +NV_INLINE bool TkActorImpl::isPending() const +{ + return m_flags.isSet(TkActorFlag::PENDING); +} + + +NV_INLINE void TkActorImpl::addJoint(TkJointLink& jointLink) +{ + NVBLAST_ASSERT(m_jointList.isSolitary(jointLink)); + + m_jointList.insertHead(jointLink); + ++m_jointCount; +} + + +NV_INLINE void TkActorImpl::removeJoint(TkJointLink& jointLink) +{ + NVBLAST_ASSERT(!m_jointList.isSolitary(jointLink)); + NVBLAST_ASSERT(m_jointCount > 0); + if (m_jointCount > 0) + { + --m_jointCount; + m_jointList.remove(jointLink); + } +} + + +//////// TkActorImpl::DamageData inline methods //////// + +NV_INLINE TkActorImpl::DamageData::Type TkActorImpl::DamageData::getType() const +{ + return m_damageDescCount > 0 ? Buffered : Plain; +} + + +NV_INLINE void TkActorImpl::DamageData::generateFracture(NvBlastFractureBuffers* commandBuffers, const NvBlastActor* actorLL, NvBlastTimers* timers) const +{ + if (getType() == Plain) + { + NvBlastActorGenerateFracture(commandBuffers, actorLL, m_program, m_programParams, TkFrameworkImpl::get()->log, timers); + } + else + { + const NvBlastProgramParams programParams = { + m_damageDescs.begin(), + m_damageDescCount, + m_material, + }; + NvBlastActorGenerateFracture(commandBuffers, actorLL, m_program, &programParams, TkFrameworkImpl::get()->log, timers); + } +} + + +//////// TkActorImpl::JointIt methods //////// + +NV_INLINE TkActorImpl::JointIt::JointIt(const TkActorImpl& actor, Direction dir) : DList::It(actor.m_jointList, dir) {} + + +NV_INLINE TkJointImpl* TkActorImpl::JointIt::operator * () const +{ + const DLink* link = (const DLink*)(*this); + return reinterpret_cast<const TkJointLink*>(link)->m_joint; +} + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKACTORIMPL_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.cpp new file mode 100644 index 0000000..b1c2c65 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.cpp @@ -0,0 +1,22 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "NvBlastTkAllocator.h" + + +namespace Nv +{ +namespace Blast +{ + +physx::PxAllocatorCallback* TkAllocator::s_allocatorCallback = nullptr; + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.h b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.h new file mode 100644 index 0000000..abc7b16 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkAllocator.h @@ -0,0 +1,49 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKALLOCATOR_H +#define NVBLASTTKALLOCATOR_H + +#include "PxAllocatorCallback.h" + + +namespace Nv +{ +namespace Blast +{ + +/** +An allocator which can be used in PxShared containers. +*/ +class TkAllocator +{ +public: + TkAllocator(const char* = 0) + { + } + + void* allocate(size_t size, const char* file, int line) + { + return s_allocatorCallback->allocate(size, nullptr, file, line); + } + + void deallocate(void* ptr) + { + return s_allocatorCallback->deallocate(ptr); + } + + static physx::PxAllocatorCallback* s_allocatorCallback; +}; + +} // namespace Blast +} // namespace Nv + + +#endif // #ifndef NVBLASTTKALLOCATOR_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkArray.h b/NvBlast/sdk/toolkit/source/NvBlastTkArray.h new file mode 100644 index 0000000..c07dc11 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkArray.h @@ -0,0 +1,41 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKARRAY_H +#define NVBLASTTKARRAY_H + + +#include "NvBlastTkAllocator.h" +#include "PsInlineArray.h" + + +namespace Nv +{ +namespace Blast +{ + +template <class T> +struct TkArray +{ + typedef physx::shdfnd::Array<T, TkAllocator> type; +}; + + +template <class T, uint32_t N> +struct TkInlineArray +{ + typedef physx::shdfnd::InlineArray<T, N, TkAllocator> type; +}; + +} // namespace Blast +} // namespace Nv + + +#endif // #ifndef NVBLASTTKARRAY_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.cpp new file mode 100644 index 0000000..577d46b --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.cpp @@ -0,0 +1,337 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + + +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastTkAssetImpl.h" +#include "NvBlastTkFamilyImpl.h" + +#include "NvBlast.h" +#include "NvBlastMemory.h" + +#include "Px.h" +#include "PxFileBuf.h" +#include "PxAllocatorCallback.h" + + +using namespace physx::general_PxIOStream2; + + +namespace Nv +{ +namespace Blast +{ + +//////// Static data //////// + +NVBLASTTK_DEFINE_TYPE_SERIALIZABLE(Asset); + + +//////// Member functions //////// + +TkAssetImpl::TkAssetImpl() + : m_assetLL(nullptr), m_ownsAsset(false) +{ +} + + +TkAssetImpl::TkAssetImpl(const NvBlastID& id) + : TkAssetType(id), m_assetLL(nullptr), m_ownsAsset(false) +{ +} + + +TkAssetImpl::~TkAssetImpl() +{ + if (m_assetLL != nullptr && m_ownsAsset) + { + TkFrameworkImpl::get()->free(m_assetLL); + } +} + + +const NvBlastAsset* TkAssetImpl::getAssetLL() const +{ + return getAssetLLInternal(); +} + + +uint32_t TkAssetImpl::getChunkCount() const +{ + return NvBlastAssetGetChunkCount(m_assetLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkAssetImpl::getLeafChunkCount() const +{ + return NvBlastAssetGetLeafChunkCount(m_assetLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkAssetImpl::getBondCount() const +{ + return NvBlastAssetGetBondCount(m_assetLL, TkFrameworkImpl::get()->log); +} + + +const NvBlastChunk* TkAssetImpl::getChunks() const +{ + return NvBlastAssetGetChunks(m_assetLL, TkFrameworkImpl::get()->log); +} + + +const NvBlastBond* TkAssetImpl::getBonds() const +{ + return NvBlastAssetGetBonds(m_assetLL, TkFrameworkImpl::get()->log); +} + + +const NvBlastSupportGraph TkAssetImpl::getGraph() const +{ + return NvBlastAssetGetSupportGraph(m_assetLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkAssetImpl::getDataSize() const +{ + return NvBlastAssetGetSize(m_assetLL, TkFrameworkImpl::get()->log); +} + + +uint32_t TkAssetImpl::getJointDescCount() const +{ + return getJointDescCountInternal(); +} + + +const TkAssetJointDesc* TkAssetImpl::getJointDescs() const +{ + return getJointDescsInternal(); +} + + +void TkAssetImpl::release() +{ + const TkType& tkType = TkFamilyImpl::s_type; + const uint32_t num = TkFrameworkImpl::get()->getObjectCount(tkType); + + if (num) + { + TkArray<TkIdentifiable*>::type dependents(num); + TkFrameworkImpl::get()->getObjects(dependents.begin(), dependents.size(), tkType); + + for (TkObject* o : dependents) + { + TkFamilyImpl* f = static_cast<TkFamilyImpl*>(o); + if (f->getAssetImpl() == this) + { + f->release(); + } + } + } + + NVBLASTTK_DELETE(this, TkAssetImpl); +} + + +bool TkAssetImpl::serialize(PxFileBuf& stream) const +{ + TkFrameworkImpl::get()->serializeHeader(*this, stream); + + // Asset data + const uint32_t assetSize = NvBlastAssetGetSize(m_assetLL, TkFrameworkImpl::get()->log); + stream.storeDword(assetSize); + stream.write(m_assetLL, assetSize); + + // Joint descs + stream.storeDword((uint32_t)m_jointDescs.size()); + for (uint32_t i = 0; i < m_jointDescs.size(); ++i) + { + const TkAssetJointDesc& jointDesc = m_jointDescs[i]; + stream.storeDword(jointDesc.nodeIndices[0]); + stream.storeDword(jointDesc.nodeIndices[1]); + stream.storeFloat(jointDesc.attachPositions[0].x); + stream.storeFloat(jointDesc.attachPositions[0].y); + stream.storeFloat(jointDesc.attachPositions[0].z); + stream.storeFloat(jointDesc.attachPositions[1].x); + stream.storeFloat(jointDesc.attachPositions[1].y); + stream.storeFloat(jointDesc.attachPositions[1].z); + } + + return true; +} + + +//////// Static functions //////// + +TkSerializable* TkAssetImpl::deserialize(PxFileBuf& stream, const NvBlastID& id) +{ + // Allocate + TkAssetImpl* asset = NVBLASTTK_NEW(TkAssetImpl)(id); + if (asset == nullptr) + { + NVBLASTTK_LOG_ERROR("TkAssetImpl::deserialize: asset allocation failed."); + return nullptr; + } + + // Asset data + const uint32_t assetSize = stream.readDword(); + asset->m_assetLL = static_cast<NvBlastAsset*>(TkFrameworkImpl::get()->alloc(assetSize)); + asset->m_ownsAsset = true; + stream.read(asset->m_assetLL, assetSize); + + // Joint descs + const uint32_t jointDescCount = stream.readDword(); + asset->m_jointDescs.resize(jointDescCount); + for (uint32_t i = 0; i < asset->m_jointDescs.size(); ++i) + { + TkAssetJointDesc& jointDesc = asset->m_jointDescs[i]; + jointDesc.nodeIndices[0] = stream.readDword(); + jointDesc.nodeIndices[1] = stream.readDword(); + jointDesc.attachPositions[0].x = stream.readFloat(); + jointDesc.attachPositions[0].y = stream.readFloat(); + jointDesc.attachPositions[0].z = stream.readFloat(); + jointDesc.attachPositions[1].x = stream.readFloat(); + jointDesc.attachPositions[1].y = stream.readFloat(); + jointDesc.attachPositions[1].z = stream.readFloat(); + } + + + if (asset->m_assetLL == nullptr) + { + asset->release(); + asset = nullptr; + } + + return asset; +} + + +TkAssetImpl* TkAssetImpl::create(const TkAssetDesc& desc) +{ + TkAssetImpl* asset = NVBLASTTK_NEW(TkAssetImpl); + + TkArray<char>::type scratch((uint32_t)NvBlastGetRequiredScratchForCreateAsset(&desc, TkFrameworkImpl::get()->log)); + void* mem = TkFrameworkImpl::get()->alloc(NvBlastGetAssetMemorySize(&desc, TkFrameworkImpl::get()->log)); + asset->m_assetLL = NvBlastCreateAsset(mem, &desc, scratch.begin(), TkFrameworkImpl::get()->log); + if (asset->m_assetLL == nullptr) + { + NVBLASTTK_LOG_ERROR("TkAssetImpl::create: low-level asset could not be created."); + asset->release(); + return nullptr; + } + + if (desc.bondFlags != nullptr) + { + for (uint32_t bondN = 0; bondN < desc.bondCount; ++bondN) + { + if (0 != (desc.bondFlags[bondN] & TkAssetDesc::BondJointed)) + { + const NvBlastBondDesc& bondDesc = desc.bondDescs[bondN]; + const uint32_t c0 = bondDesc.chunkIndices[0]; + const uint32_t c1 = bondDesc.chunkIndices[1]; + if (c0 >= desc.chunkCount || c1 >= desc.chunkCount) + { + NVBLASTTK_LOG_WARNING("TkAssetImpl::create: joint flag set for badly described bond. No joint descriptor created."); + continue; + } + + if (!asset->addJointDesc(c0, c1)) + { + NVBLASTTK_LOG_WARNING("TkAssetImpl::create: no bond corresponds to the user-described bond indices. No joint descriptor created."); + } + } + } + } + + asset->m_ownsAsset = true; +// asset->setID(NvBlastAssetGetID(asset->m_assetLL, TkFrameworkImpl::get()->log)); // Keeping LL and Tk IDs distinct + + return asset; +} + + +TkAssetImpl* TkAssetImpl::create(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs, uint32_t jointDescCount, bool ownsAsset) +{ + TkAssetImpl* asset = NVBLASTTK_NEW(TkAssetImpl); + + //NOTE: Why are we passing in a const NvBlastAsset* and then discarding the const? + asset->m_assetLL = const_cast<NvBlastAsset*>(assetLL); + if (asset->m_assetLL == nullptr) + { + NVBLASTTK_LOG_ERROR("TkAssetImpl::create: low-level asset could not be created."); + asset->release(); + return nullptr; + } + + asset->m_ownsAsset = ownsAsset; + asset->setID(NvBlastAssetGetID(asset->m_assetLL, TkFrameworkImpl::get()->log)); + + asset->m_jointDescs.resize(jointDescCount); + for (uint32_t i = 0; i < asset->m_jointDescs.size(); ++i) + { + asset->m_jointDescs[i] = jointDescs[i]; + } + + return asset; +} + +bool TkAssetImpl::addJointDesc(uint32_t chunkIndex0, uint32_t chunkIndex1) +{ + if (m_assetLL == nullptr) + { + return false; + } + + const uint32_t upperSupportChunkCount = NvBlastAssetGetFirstSubsupportChunkIndex(m_assetLL, TkFrameworkImpl::get()->log); + if (chunkIndex0 >= upperSupportChunkCount || chunkIndex1 >= upperSupportChunkCount) + { + return false; + } + + const uint32_t* chunkToGraphNodeMap = NvBlastAssetGetChunkToGraphNodeMap(m_assetLL, TkFrameworkImpl::get()->log); + const uint32_t node0 = chunkToGraphNodeMap[chunkIndex0]; + const uint32_t node1 = chunkToGraphNodeMap[chunkIndex1]; + const NvBlastSupportGraph graph = NvBlastAssetGetSupportGraph(m_assetLL, TkFrameworkImpl::get()->log); + if (node0 >= graph.nodeCount && node1 >= graph.nodeCount) + { + return false; + } + + // Find bond index + // Iterate through all neighbors of node0 chunk + uint32_t bondIndex = 0xFFFFFFFF; + for (uint32_t i = graph.adjacencyPartition[node0]; i < graph.adjacencyPartition[node0 + 1]; i++) + { + if (graph.adjacentNodeIndices[i] == node1) + { + bondIndex = graph.adjacentBondIndices[i]; + break; + } + } + + if (bondIndex >= NvBlastAssetGetBondCount(m_assetLL, TkFrameworkImpl::get()->log)) + { + return false; + } + + const NvBlastBond& bond = NvBlastAssetGetBonds(m_assetLL, TkFrameworkImpl::get()->log)[bondIndex]; + + TkAssetJointDesc jointDesc; + jointDesc.attachPositions[0] = jointDesc.attachPositions[1] = physx::PxVec3(bond.centroid[0], bond.centroid[1], bond.centroid[2]); + jointDesc.nodeIndices[0] = node0; + jointDesc.nodeIndices[1] = node1; + m_jointDescs.pushBack(jointDesc); + + return true; +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.h new file mode 100644 index 0000000..ae68af8 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkAssetImpl.h @@ -0,0 +1,162 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKASSETIMPL_H +#define NVBLASTTKASSETIMPL_H + + +#include "NvBlastTkCommon.h" +#include "NvBlastTkJoint.h" +#include "NvBlastTkAsset.h" +#include "NvBlastTkTypeImpl.h" +#include "NvBlastTkArray.h" + + +// Forward declarations +struct NvBlastAsset; + + +namespace Nv +{ +namespace Blast +{ + +/** +Implementation of TkAsset +*/ +NVBLASTTK_IMPL_DECLARE(Asset) +{ +public: + /** + Enum which keeps track of the serialized data format. + */ + enum Version + { + /** Initial version */ + Initial, + + // New formats must come before Count. They should be given descriptive names with more information in comments. + + /** The number of serialized formats. */ + Count, + + /** The current version. This should always be Count-1 */ + Current = Count - 1 + }; + + TkAssetImpl(); + TkAssetImpl(const NvBlastID& id); + ~TkAssetImpl(); + + NVBLASTTK_IMPL_DEFINE_SERIALIZABLE('A', 'S', 'S', 'T'); + + // Public methods + + /** + Factory create method. This method creates a low-level asset and stores a reference to it. + + \param[in] desc Asset descriptor set by the user. + + \return a pointer to a new TkAssetImpl object if successful, NULL otherwise. + */ + static TkAssetImpl* create(const TkAssetDesc& desc); + + /** + Static method to create an asset from an existing low-level asset. + + \param[in] assetLL A valid low-level asset passed in by the user. + \param[in] jointDescs Optional joint descriptors to add to the new asset. + \param[in] jointDescCount The number of joint descriptors in the jointDescs array. If non-zero, jointDescs cannot be NULL. + \param[in] ownsAsset Whether or not to let this TkAssetImpl object release the low-level NvBlastAsset memory upon its own release. + + \return a pointer to a new TkAssetImpl object if successful, NULL otherwise. + */ + static TkAssetImpl* create(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs = nullptr, uint32_t jointDescCount = 0, bool ownsAsset = false); + + /** + \return a pointer to the underlying low-level NvBlastAsset associated with this asset. + */ + const NvBlastAsset* getAssetLLInternal() const; + + /** + \return the number of internal joint descriptors stored with this asset. + */ + uint32_t getJointDescCountInternal() const; + + /** + \return the array of internal joint descriptors stored with this asset, with size given by getJointDescCountInternal(). + */ + const TkAssetJointDesc* getJointDescsInternal() const; + + // Begin TkAsset + virtual const NvBlastAsset* getAssetLL() const override; + + virtual uint32_t getChunkCount() const override; + + virtual uint32_t getLeafChunkCount() const override; + + virtual uint32_t getBondCount() const override; + + virtual const NvBlastChunk* getChunks() const override; + + virtual const NvBlastBond* getBonds() const override; + + virtual const NvBlastSupportGraph getGraph() const override; + + virtual uint32_t getDataSize() const override; + + virtual uint32_t getJointDescCount() const override; + + virtual const TkAssetJointDesc* getJointDescs() const override; + // End TkAsset + +private: + /** + Utility to add a joint descriptor between the indexed chunks. The two chunks + must be support chunks, and there must exist a bond between them. The joint's + attachment positions will be the bond centroid. + + \param[in] chunkIndex0 The first chunk index. + \param[in] chunkIndex1 The second chunk index. + + \return true iff successful. + */ + bool addJointDesc(uint32_t chunkIndex0, uint32_t chunkIndex1); + + NvBlastAsset* m_assetLL; //!< The underlying low-level asset. + TkArray<TkAssetJointDesc>::type m_jointDescs; //!< The array of internal joint descriptors. + bool m_ownsAsset; //!< Whether or not this asset should release its low-level asset upon its own release. +}; + + +//////// TkAssetImpl inline methods //////// + +NV_INLINE const NvBlastAsset* TkAssetImpl::getAssetLLInternal() const +{ + return m_assetLL; +} + + +NV_INLINE uint32_t TkAssetImpl::getJointDescCountInternal() const +{ + return m_jointDescs.size(); +} + + +NV_INLINE const TkAssetJointDesc* TkAssetImpl::getJointDescsInternal() const +{ + return m_jointDescs.begin(); +} + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKASSETIMPL_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkCommon.h b/NvBlast/sdk/toolkit/source/NvBlastTkCommon.h new file mode 100644 index 0000000..edc1a91 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkCommon.h @@ -0,0 +1,110 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKCOMMON_H +#define NVBLASTTKCOMMON_H + + +#include "NvPreprocessor.h" +#include "NvBlastTkGUID.h" + + +// Macro to load a uint32_t (or larger) with four characters +#define NVBLASTTK_FOURCC(_a, _b, _c, _d) ( (uint32_t)(_a) | (uint32_t)(_b)<<8 | (uint32_t)(_c)<<16 | (uint32_t)(_d)<<24 ) + + +// Macro to define standard object classes. An intermediate class is defined which holds common implementations. +#define NVBLASTTK_IMPL_DECLARE(_name) \ +class Tk##_name##Type : public Tk##_name \ +{ \ +public: \ + /* Blank constructor generates a new NvBlastID and informs framework */ \ + Tk##_name##Type() \ + { \ + memset(&m_ID, 0, sizeof(NvBlastID)); \ + setID(TkGenerateGUID(this)); \ + TkFrameworkImpl::get()->onCreate(*this); \ + } \ + \ + /* This constructor takes an existing NvBlastID and informs framework */ \ + Tk##_name##Type(const NvBlastID& id) \ + { \ + memset(&m_ID, 0, sizeof(NvBlastID)); \ + setID(id); \ + TkFrameworkImpl::get()->onCreate(*this); \ + } \ + \ + /* Destructor informs framework */ \ + ~Tk##_name##Type() { TkFrameworkImpl::get()->onDestroy(*this); } \ + \ + /* Begin TkIdentifiable */ \ + virtual void setID(const NvBlastID& id) override \ + { \ + /* Inform framework of ID change */ \ + TkFrameworkImpl::get()->onIDChange(*this, m_ID, id); \ + m_ID = id; \ + } \ + virtual const NvBlastID& getID() const override { return getIDInternal(); } \ + virtual const TkType& getType() const override { return s_type; } \ + /* End TkIdentifiable */ \ + \ + /* Begin public API */ \ + \ + /* Inline method for internal access to NvBlastID */ \ + const NvBlastID& getIDInternal() const { return m_ID; } \ + \ + /* End public API */ \ + \ + /* Static type information */ \ + static TkTypeImpl s_type; \ + \ +private: \ + NvBlastID m_ID; /* NvBlastID for a TkIdentifiable object */ \ +}; \ + \ +/* Derive object implementation from common implementation class above */ \ +class Tk##_name##Impl final : public Tk##_name##Type + + +// Macro to declare standard object interfaces, enums, etc. +#define NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE(_id0, _id1, _id2, _id3) \ + /* Begin TkObject */ \ + virtual void release() override; \ + /* End TkObject */ \ + \ + /* Enums */ \ + \ + /* Generate a ClassID enum used to identify this TkIdentifiable. */ \ + enum { ClassID = NVBLASTTK_FOURCC(_id0, _id1, _id2, _id3) } + + +// Macro to declare standard object interfaces, enums, etc (serializable version) +#define NVBLASTTK_IMPL_DEFINE_SERIALIZABLE(_id0, _id1, _id2, _id3) \ + NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE(_id0, _id1, _id2, _id3); \ + \ + /* Begin TkSerializable */ \ + virtual bool serialize(physx::general_PxIOStream2::PxFileBuf& stream) const override; \ + /* End TkSerializable */ \ + \ + /* Static deserialization function, called by TkFrameworkImpl::deserialize after header data */ \ + static TkSerializable* deserialize(physx::general_PxIOStream2::PxFileBuf& stream, const NvBlastID& id) + + +// Macro to define class type data +#define NVBLASTTK_DEFINE_TYPE_IDENTIFIABLE(_name) \ + TkTypeImpl Tk##_name##Type::s_type("Tk" #_name, Tk##_name##Impl::ClassID, 0, nullptr) + + +// Macro to define class type data (serializable version) +#define NVBLASTTK_DEFINE_TYPE_SERIALIZABLE(_name) \ + TkTypeImpl Tk##_name##Type::s_type("Tk" #_name, Tk##_name##Impl::ClassID, Tk##_name##Impl::Version::Current, Tk##_name##Impl::deserialize) + + +#endif // ifndef NVBLASTTKCOMMON_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h b/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h new file mode 100644 index 0000000..00a1a61 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkEventQueue.h @@ -0,0 +1,231 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKEVENTQUEUE_H +#define NVBLASTTKEVENTQUEUE_H + +#include <algorithm> +#include <vector> + +#include <mutex> +#include <atomic> + +#include "PxAllocatorCallback.h" +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastAssert.h" + + +namespace Nv { +namespace Blast { + +/** +A dispatcher queue providing preallocation and thread-safe insertions therein. + +Typical usage: +- preallocate space for events and payload: + - reserveEvents, reserveData +- enable asserts to detect undersized storage (allocations are not thread safe): + - protect(true) +- get pointers to payload data and events to fill in, thread safe for preallocated memory: + - allocData, addEvent +- back on main thread, ensure consistency: + - protect(false) + +- continue adding events and payload on main thread if necessary like above (allocations are safe here) +eventually dispatch, or reset if dispatched by proxy +*/ +class TkEventQueue +{ +public: + TkEventQueue() : m_currentEvent(0), m_poolCapacity(0), m_pool(nullptr), m_allowAllocs(true) {} + + /** + Peek events queue for dispatch. + Do not use in protected state. + */ + operator const TkArray<TkEvent>::type&() + { + NVBLAST_ASSERT(m_allowAllocs); + NVBLAST_ASSERT(m_currentEvent == m_events.size()); + return m_events; + } + + /** + Debug help to catch (unwanted) allocations during task work. + Note that this will not actually avoid allocations, but assert in debug builds. + + Set true before using in distributed environment. + Set false to return to single-thread mode. + */ + void protect(bool enable) + { + // During parallel use, m_events.size() and m_currentEvent are allowed to diverge. + // This is fine because resizeUninitialized does not alter the stored data. + NVBLAST_ASSERT(m_currentEvent <= m_events.capacity()); + m_events.resizeUninitialized(m_currentEvent); + m_allowAllocs = !enable; + } + + /** + Restores initial state. + Data memory is currently not being reused. To be improved. + */ + void reset() + { + m_events.clear(); + m_currentEvent = 0; + for (void* mem : m_memory) + { + NVBLASTTK_FREE(mem); + } + m_memory.clear(); + m_currentData = 0; + m_allowAllocs = true; + m_poolCapacity = 0; + m_pool = nullptr; + } + + /** + Queue an event with a payload. + */ + template<class T> + void addEvent(T* payload) + { + uint32_t index = m_currentEvent.fetch_add(1); + + // Should not allocate in protected state. + NVBLAST_ASSERT(m_allowAllocs || m_currentEvent <= m_events.capacity()); + + m_events.resizeUninitialized(m_currentEvent); + + // During parallel use, m_events.size() and m_currentEvent are allowed to diverge. + // Consistency is restored in protect(). + NVBLAST_ASSERT(!m_allowAllocs || m_currentEvent == m_events.size()); + + TkEvent& evt = m_events[index]; + evt.type = TkEvent::Type(T::EVENT_TYPE); + evt.payload = payload; + } + + /** + Request storage for payload. + */ + template<typename T> + T* allocData() + { + uint32_t index = m_currentData.fetch_add(sizeof(T)); + if (m_currentData <= m_poolCapacity) + { + return reinterpret_cast<T*>(&m_pool[index]); + } + else + { + // Could do larger block allocation here. + reserveData(sizeof(T)); + // Account for the requested size. + m_currentData = sizeof(T); + return reinterpret_cast<T*>(&m_pool[0]); + } + } + + /** + Preallocate a memory block of size Bytes for payload data. + Note that this will inevitably allocate a new memory block. + Subsequent calls to allocData will use this memory piecewise. + */ + void reserveData(size_t size) + { + NVBLAST_ASSERT(m_allowAllocs); + m_pool = reinterpret_cast<uint8_t*>(allocDataBySize(size)); + m_poolCapacity = size; + m_currentData = 0; + } + + /** + Preallocate space for events. + */ + void reserveEvents(uint32_t n) + { + NVBLAST_ASSERT(m_allowAllocs); + m_events.reserve(m_events.size() + n); + } + + /** + Add a listener to dispatch to. + */ + void addListener(TkEventListener& l) + { + m_listeners.pushBack(&l); + } + + /** + Remove a listener from dispatch list. + */ + void removeListener(TkEventListener& l) + { + m_listeners.findAndReplaceWithLast(&l); + } + + /** + Dispatch the stored events to the registered listeners. + After dispatch, all data is invalidated. + */ + void dispatch() + { + dispatch(*this); + reset(); + } + + /** + Proxy function to dispatch events to this queue's listeners. + */ + void dispatch(const TkArray<TkEvent>::type& events) const + { + if (events.size()) + { + for (TkEventListener* l : m_listeners) + { + PERF_SCOPE_M("TkEventQueue::dispatch"); + l->receive(events.begin(), events.size()); + } + } + } + +private: + /** + Allocates and stores a block of size Bytes of payload data. + */ + void* allocDataBySize(size_t size) + { + void* memory = nullptr; + if (size > 0) + { + memory = NVBLASTTK_ALLOC(size, "TkEventQueue Data"); + m_memory.pushBack(memory); + } + return memory; + } + + + TkArray<TkEvent>::type m_events; //!< holds events + TkArray<void*>::type m_memory; //!< holds allocated data memory blocks + std::atomic<uint32_t> m_currentEvent; //!< reference index for event insertion + std::atomic<uint32_t> m_currentData; //!< reference index for data insertion + size_t m_poolCapacity; //!< size of the currently active memory block (m_pool) + uint8_t* m_pool; //!< the current memory block allocData() uses + bool m_allowAllocs; //!< assert guard + TkInlineArray<TkEventListener*,4>::type m_listeners; //!< objects to dispatch to +}; + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKEVENTQUEUE_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp new file mode 100644 index 0000000..33baafe --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.cpp @@ -0,0 +1,815 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastTkFamilyImpl.h" +#include "NvBlastTkGroupImpl.h" +#include "NvBlastTkAssetImpl.h" +#include "NvBlastTkActorImpl.h" +#include "NvBlastTkJointImpl.h" + +#include "Px.h" +#include "PxFileBuf.h" +#include "PxAllocatorCallback.h" + +#include "NvBlastIndexFns.h" +#include "NvBlastMemory.h" + +using namespace physx::general_PxIOStream2; + + +namespace Nv +{ +namespace Blast +{ + +//////// Static data //////// + +NVBLASTTK_DEFINE_TYPE_SERIALIZABLE(Family); + + +//////// Member functions //////// + +TkFamilyImpl::TkFamilyImpl() : m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr), m_material(nullptr) +{ +} + + +TkFamilyImpl::TkFamilyImpl(const NvBlastID& id) : TkFamilyType(id), m_familyLL(nullptr), m_internalJointCount(0), m_asset(nullptr), m_material(nullptr) +{ +} + + +TkFamilyImpl::~TkFamilyImpl() +{ + if (m_familyLL != nullptr) + { + uint32_t familyActorCount = NvBlastFamilyGetActorCount(m_familyLL, TkFrameworkImpl::get()->log); + if (familyActorCount != 0) + { + NVBLASTTK_LOG_WARNING("TkFamilyImpl::~TkFamilyImpl(): family actor count is not 0."); + } + TkFrameworkImpl::get()->free(m_familyLL); + } +} + + +void TkFamilyImpl::release() +{ + for (TkActorImpl& actor : m_actors) + { + if (actor.isActive()) + { + actor.release(); + } + } + + m_actors.clear(); + + NVBLASTTK_DELETE(this, TkFamilyImpl); +} + + +const NvBlastFamily* TkFamilyImpl::getFamilyLL() const +{ + return m_familyLL; +} + + +TkActorImpl* TkFamilyImpl::addActor(NvBlastActor* actorLL) +{ + TkActorImpl* actor = getActorByActorLL(actorLL); + NVBLAST_ASSERT(actor); + actor->m_actorLL = actorLL; + actor->m_family = this; + return actor; +} + + +void TkFamilyImpl::removeActor(TkActorImpl* actor) +{ + NVBLAST_ASSERT(actor != nullptr && actor->m_family == this); + //actor->m_family = nullptr; + actor->m_actorLL = nullptr; +} + + +uint32_t TkFamilyImpl::getActorCount() const +{ + return getActorCountInternal(); +} + + +uint32_t TkFamilyImpl::getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart /*= 0*/) const +{ + uint32_t actorCount = getActorCount(); + if (actorCount <= indexStart) + { + NVBLASTTK_LOG_WARNING("TkFamilyImpl::getActors: indexStart beyond end of actor list."); + return 0; + } + + actorCount -= indexStart; + if (actorCount > bufferSize) + { + actorCount = static_cast<uint32_t>(bufferSize); + } + + uint32_t index = 0; + for (const TkActorImpl& actor : m_actors) + { + if (actor.isActive()) + { + if (index >= indexStart) + { + if ((index - indexStart) >= actorCount) + { + break; + } + else + { + *buffer++ = const_cast<TkActorImpl*>(&actor); + } + } + index++; + } + } + + return actorCount; +} + + +NV_INLINE bool areLLActorsEqual(const NvBlastActor* actor0, const NvBlastActor* actor1, TkArray<uint32_t>::type& scratch) +{ + if (NvBlastActorGetGraphNodeCount(actor0, TkFrameworkImpl::get()->log) != NvBlastActorGetGraphNodeCount(actor1, TkFrameworkImpl::get()->log)) + { + return false; + } + + const uint32_t chunkCount = NvBlastActorGetVisibleChunkCount(actor0, TkFrameworkImpl::get()->log); + if (chunkCount != NvBlastActorGetVisibleChunkCount(actor1, TkFrameworkImpl::get()->log)) + { + return false; + } + + scratch.resize(chunkCount * 2); + NvBlastActorGetVisibleChunkIndices(scratch.begin(), chunkCount, actor0, TkFrameworkImpl::get()->log); + NvBlastActorGetVisibleChunkIndices(scratch.begin() + chunkCount, chunkCount, actor1, TkFrameworkImpl::get()->log); + return memcmp(scratch.begin(), scratch.begin() + chunkCount, chunkCount * sizeof(uint32_t)) == 0; +} + + +void TkFamilyImpl::reinitialize(const NvBlastFamily* newFamily, TkGroup* group) +{ + NVBLAST_ASSERT(newFamily); +#if NV_ENABLE_ASSERTS + NvBlastID id0 = NvBlastFamilyGetAssetID(m_familyLL, TkFrameworkImpl::get()->log); + NvBlastID id1 = NvBlastFamilyGetAssetID(newFamily, TkFrameworkImpl::get()->log); + NVBLAST_ASSERT(TkGUIDsEqual(&id0, &id1)); +#endif + NVBLAST_ASSERT(NvBlastFamilyGetSize(m_familyLL, TkFrameworkImpl::get()->log) == NvBlastFamilyGetSize(newFamily, TkFrameworkImpl::get()->log)); + + // alloc and init new family + const uint32_t blockSize = NvBlastFamilyGetSize(newFamily, TkFrameworkImpl::get()->log); + NvBlastFamily* newFamilyCopy = (NvBlastFamily*)TkFrameworkImpl::get()->alloc(blockSize); + memcpy(newFamilyCopy, newFamily, blockSize); + NvBlastFamilySetAsset(newFamilyCopy, m_asset->getAssetLL(), TkFrameworkImpl::get()->log); + + // get actors from new family + TkArray<NvBlastActor*>::type newLLActors(NvBlastFamilyGetActorCount(newFamilyCopy, TkFrameworkImpl::get()->log)); + uint32_t actorCount = NvBlastFamilyGetActors(newLLActors.begin(), newLLActors.size(), newFamilyCopy, TkFrameworkImpl::get()->log); + + // reset actor families to nullptr (we use it as a flag later) + for (TkActorImpl& actor : m_actors) + { + if (actor.isActive()) + { + actor.m_family = nullptr; + } + } + + // prepare split event with new actors + auto newActorsSplitEvent = getQueue().allocData<TkSplitEvent>(); + TkArray<TkActor*>::type children(actorCount); + children.resizeUninitialized(0); + newActorsSplitEvent->children = children.begin(); + + // scratch + TkArray<uint32_t>::type scratch(m_asset->getChunkCount()); + + for (uint32_t i = 0; i < actorCount; ++i) + { + NvBlastActor* newLLActor = newLLActors[i]; + uint32_t actorIndex = NvBlastActorGetIndex(newLLActor, TkFrameworkImpl::get()->log); + TkActorImpl& tkActor = *getActorByIndex(actorIndex); + + tkActor.m_family = this; + + if (!tkActor.isActive() || !areLLActorsEqual(newLLActor, tkActor.m_actorLL, scratch)) + { + if (tkActor.isActive()) + { + auto removeSplitEvent = getQueue().allocData<TkSplitEvent>(); + removeSplitEvent->parentData.family = this; + removeSplitEvent->numChildren = 0; + removeSplitEvent->parentData.userData = tkActor.userData; + removeSplitEvent->parentData.index = tkActor.getIndex(); + getQueue().addEvent(removeSplitEvent); + } + + tkActor.m_actorLL = newLLActor; + + // switch groups + TkGroupImpl* prevGroup = tkActor.m_group; + if (prevGroup != group) + { + if (prevGroup) + { + prevGroup->removeActor(tkActor); + } + if (group) + { + group->addActor(tkActor); + } + } + + children.pushBack(&tkActor); + } + else + { + tkActor.m_actorLL = newLLActor; + } + } + + // if m_family is still nullptr for an active actor -> remove it. It doesn't exist in new family. + for (TkActorImpl& tkActor : m_actors) + { + if (tkActor.isActive() && tkActor.m_family == nullptr) + { + tkActor.m_family = this; + if (tkActor.m_group) + { + tkActor.m_group->removeActor(tkActor); + } + + auto removeSplitEvent = getQueue().allocData<TkSplitEvent>(); + removeSplitEvent->parentData.family = this; + removeSplitEvent->numChildren = 0; + removeSplitEvent->parentData.userData = tkActor.userData; + removeSplitEvent->parentData.index = tkActor.getIndex(); + getQueue().addEvent(removeSplitEvent); + + tkActor.m_actorLL = nullptr; + } + } + + // add split event with all new actors + newActorsSplitEvent->parentData.family = this; + newActorsSplitEvent->parentData.userData = 0; + newActorsSplitEvent->parentData.index = invalidIndex<uint32_t>(); + newActorsSplitEvent->numChildren = children.size(); + if (newActorsSplitEvent->numChildren > 0) + { + getQueue().addEvent(newActorsSplitEvent); + } + + // replace family + TkFrameworkImpl::get()->free(m_familyLL); + m_familyLL = newFamilyCopy; + + // update joints + for (TkActorImpl& tkActor : m_actors) + { + if (!tkActor.m_jointList.isEmpty()) + { + updateJoints(&tkActor); + } + } + + getQueue().dispatch(); +} + + +TkActorImpl* TkFamilyImpl::getActorByChunk(uint32_t chunk) +{ + if (chunk >= NvBlastAssetGetChunkCount(m_asset->getAssetLLInternal(), TkFrameworkImpl::get()->log)) + { + NVBLASTTK_LOG_WARNING("TkFamilyImpl::getActorByChunk: invalid chunk index. Returning NULL."); + return nullptr; + } + + NvBlastActor* actorLL = NvBlastFamilyGetChunkActor(m_familyLL, chunk, TkFrameworkImpl::get()->log); + return actorLL ? getActorByActorLL(actorLL) : nullptr; +} + + +void TkFamilyImpl::applyFractureInternal(const NvBlastFractureBuffers* commands) +{ + NvBlastSupportGraph graph = getAsset()->getGraph(); + + // apply bond fracture commands on relevant actors + { + TkActorImpl* currActor = nullptr; + NvBlastBondFractureData* bondFractures = commands->bondFractures; + uint32_t bondFracturesCount = 0; + + auto applyFracture = [&]() + { + if (bondFracturesCount > 0) + { + if (currActor != nullptr && currActor->isActive()) + { + NvBlastFractureBuffers newCommands; + newCommands.bondFractures = bondFractures; + newCommands.bondFractureCount = bondFracturesCount; + newCommands.chunkFractures = nullptr; + newCommands.chunkFractureCount = 0; + currActor->applyFracture(nullptr, &newCommands); + } + + bondFractures += bondFracturesCount; + bondFracturesCount = 0; + } + }; + + for (uint32_t i = 0; i < commands->bondFractureCount; ++i, ++bondFracturesCount) + { + const NvBlastBondFractureData& command = commands->bondFractures[i]; + uint32_t chunk0 = graph.chunkIndices[command.nodeIndex0]; + uint32_t chunk1 = graph.chunkIndices[command.nodeIndex1]; + TkActorImpl* actor0 = getActorByChunk(chunk0); + TkActorImpl* actor1 = getActorByChunk(chunk1); + if (actor0 != actor1) + { + // skipping this event, bond already broken + actor0 = nullptr; + } + if (actor0 != currActor) + { + applyFracture(); + currActor = actor0; + } + } + + if (bondFracturesCount > 0) + { + applyFracture(); + } + } + + // apply chunk fracture commands on relevant actors + { + TkActorImpl* currActor = nullptr; + NvBlastChunkFractureData* chunkFractures = commands->chunkFractures; + uint32_t chunkFracturesCount = 0; + + auto applyFracture = [&]() + { + if (chunkFracturesCount > 0) + { + if (currActor != nullptr && currActor->isActive()) + { + NvBlastFractureBuffers newCommands; + newCommands.bondFractures = nullptr; + newCommands.bondFractureCount = 0; + newCommands.chunkFractures = chunkFractures; + newCommands.chunkFractureCount = chunkFracturesCount; + currActor->applyFracture(nullptr, &newCommands); + } + + chunkFractures += chunkFracturesCount; + chunkFracturesCount = 0; + } + }; + + for (uint32_t i = 0; i < commands->chunkFractureCount; ++i, ++chunkFracturesCount) + { + const NvBlastChunkFractureData& command = commands->chunkFractures[i]; + TkActorImpl* actor = getActorByChunk(command.chunkIndex); + if (actor != currActor) + { + applyFracture(); + currActor = actor; + } + } + if (chunkFracturesCount > 0) + { + applyFracture(); + } + } +} + + +void TkFamilyImpl::updateJoints(TkActorImpl* actor, TkEventQueue* alternateQueue) +{ + // Copy joint array for safety against implementation of joint->setActor + TkJointImpl** joints = reinterpret_cast<TkJointImpl**>(NvBlastAlloca(sizeof(TkJointImpl*)*actor->getJointCountInternal())); + TkJointImpl** stop = joints + actor->getJointCountInternal(); + TkJointImpl** jointHandle = joints; + for (TkActorImpl::JointIt j(*actor); (bool)j; ++j) + { + *jointHandle++ = *j; + } + jointHandle = joints; + while (jointHandle < stop) + { + TkJointImpl* joint = *jointHandle++; + + const TkJointData& data = joint->getDataInternal(); + + TkActorImpl* actor0 = data.actors[0] != nullptr ? + static_cast<TkActorImpl&>(*data.actors[0]).getFamilyImpl().getActorByChunk(data.chunkIndices[0]) : nullptr; + + TkActorImpl* actor1 = data.actors[1] != nullptr ? + static_cast<TkActorImpl&>(*data.actors[1]).getFamilyImpl().getActorByChunk(data.chunkIndices[1]) : nullptr; + + joint->setActors(actor0, actor1, alternateQueue); + } +} + + +const TkAsset* TkFamilyImpl::getAsset() const +{ + return m_asset; +} + + +bool TkFamilyImpl::serialize(PxFileBuf& stream) const +{ + TkFrameworkImpl::get()->serializeHeader(*this, stream); + + if (m_material != nullptr) + { + NVBLASTTK_LOG_WARNING("TkFamilyImpl::serialize(): Material pointer is not nullptr, it will be lost during serialization."); + } + + NVBLASTTK_CHECK_ERROR(m_asset != nullptr, "TkFamilyImpl::serialize(): TkFamily asset is nullptr, can't be serialized.", return false); + NVBLASTTK_CHECK_ERROR(m_familyLL != nullptr, "TkFamilyImpl::serialize(): TkFamily family is nullptr, can't be serialized.", return false); + + // Asset ID + const NvBlastID& assetID = m_asset->getID(); + NVBLASTTK_CHECK_ERROR(!TkGUIDIsZero(&assetID), "TkFamilyImpl::serialize(): Associated asset doesn't have an ID set.", return false); + stream.write(&assetID, sizeof(NvBlastID)); + + // Family + const uint32_t familySize = NvBlastFamilyGetSize(m_familyLL, TkFrameworkImpl::get()->log); + stream.storeDword(familySize); + stream.write(m_familyLL, familySize); + + //// Joints //// + + // Internal joint data + stream.storeDword(m_internalJointCount); + + // External joint family ID list + stream.storeDword(m_jointSets.size()); + for (uint32_t i = 0; i < m_jointSets.size(); ++i) + { + const JointSet* jointSet = m_jointSets[i]; + stream.write(&jointSet->m_familyID, sizeof(NvBlastID)); + } + + // Actor joint lists + TkJointImpl* internalJoints = getInternalJoints(); + for (uint32_t actorNum = 0; actorNum < m_actors.size(); ++actorNum) + { + const TkActorImpl& actor = m_actors[actorNum]; + if (!actor.isActive()) + { + continue; // We may need a better way of iterating through active actors + } + + stream.storeDword(actor.getJointCount()); + + for (TkActorImpl::JointIt j(actor); (bool)j; ++j) + { + TkJointImpl* joint = *j; + + const TkJointData& jointData = joint->getDataInternal(); + NVBLAST_ASSERT(jointData.actors[0] == &actor || jointData.actors[1] == &actor); + + const uint32_t attachmentFlags = (uint32_t)(jointData.actors[0] == &actor) | (uint32_t)(jointData.actors[1] == &actor) << 1; + stream.storeDword(attachmentFlags); + + const TkActorImpl* otherActor = static_cast<const TkActorImpl*>(jointData.actors[(attachmentFlags >> 1) ^ 1]); + + if (joint->m_owner == this) + { + // Internal joint - write internal joint index + const uint32_t jointIndex = static_cast<uint32_t>(joint - internalJoints); + stream.storeDword(jointIndex); + if (otherActor != nullptr && otherActor->getIndexInternal() < actorNum) // No need to write the joint data, it has already been written + { + continue; + } + } + else + { + // External joint - write external family index and joint information + stream.storeDword(invalidIndex<uint32_t>()); // Denotes external joint + + const FamilyIDMap::Entry* e = m_familyIDMap.find(getFamilyID(otherActor)); + NVBLASTTK_CHECK_ERROR(e != nullptr, "TkFamilyImpl::deserialize(): Bad data - attached family's ID not recorded.", return false); + + stream.storeDword(e->second); // Write family ID index + } + + // Write joint data + for (int side = 0; side < 2; ++side) + { + stream.storeDword(jointData.chunkIndices[side]); + const physx::PxVec3& attachPosition = jointData.attachPositions[side]; + stream.storeFloat(attachPosition.x); stream.storeFloat(attachPosition.y); stream.storeFloat(attachPosition.z); + } + } + } + + return true; +} + + +//////// Static functions //////// + +TkSerializable* TkFamilyImpl::deserialize(PxFileBuf& stream, const NvBlastID& id) +{ + // Asset resolve + NvBlastID assetID; + stream.read(&assetID, sizeof(NvBlastID)); + TkIdentifiable* object = TkFrameworkImpl::get()->findObjectByIDInternal(assetID); + NVBLASTTK_CHECK_ERROR(object && object->getType() == TkAssetImpl::s_type, "TkFamilyImpl::deserialize: can't find asset with corresponding ID.", return nullptr); + TkAssetImpl* asset = static_cast<TkAssetImpl*>(object); + + // Allocate + TkFamilyImpl* family = NVBLASTTK_NEW(TkFamilyImpl)(id); + NVBLASTTK_CHECK_ERROR(family != nullptr, "TkFamilyImpl::deserialize: family allocation failed.", return nullptr); + + // associate with found asset + family->m_asset = asset; + + // Family + const uint32_t familySize = stream.readDword(); + family->m_familyLL = static_cast<NvBlastFamily*>(TkFrameworkImpl::get()->alloc(familySize)); + stream.read(family->m_familyLL, familySize); + + if (family->m_familyLL == nullptr) + { + NVBLASTTK_LOG_ERROR("TkFamilyImpl::deserialize: low-level family could not be created."); + family->release(); + return nullptr; + } + +#if NV_ENABLE_ASSERTS && 0 + NvBlastID id = NvBlastFamilyGetAssetID(family->m_familyLL, TkFrameworkImpl::get()->log); + NVBLAST_ASSERT(TkGUIDsEqual(&id, &assetID)); +#endif + + // preallocate actors + uint32_t maxActorCount = NvBlastFamilyGetMaxActorCount(family->m_familyLL, TkFrameworkImpl::get()->log); + family->m_actors.resize(maxActorCount); + + // get actors from family + TkArray<NvBlastActor*>::type newLLActors(NvBlastFamilyGetActorCount(family->m_familyLL, TkFrameworkImpl::get()->log)); + uint32_t actorCount = NvBlastFamilyGetActors(newLLActors.begin(), newLLActors.size(), family->m_familyLL, TkFrameworkImpl::get()->log); + + // fill actors + for (uint32_t i = 0; i < actorCount; ++i) + { + NvBlastActor* newLLActor = newLLActors[i]; + uint32_t actorIndex = NvBlastActorGetIndex(newLLActor, TkFrameworkImpl::get()->log); + TkActorImpl& tkActor = *family->getActorByIndex(actorIndex); + + tkActor.m_family = family; + tkActor.m_actorLL = newLLActor; + } + + //// Create joints //// + + // internal + family->m_internalJointCount = stream.readDword(); + family->m_internalJointBuffer.resize(family->m_internalJointCount * sizeof(TkJointImpl), '\0'); + TkJointImpl* internalJoints = family->getInternalJoints(); + + // external joint family ID list + const uint32_t jointSetCount = stream.readDword(); + family->m_jointSets.resize(jointSetCount); + for (uint32_t i = 0; i < jointSetCount; ++i) + { + family->m_jointSets[i] = NVBLASTTK_NEW(JointSet); + stream.read(&family->m_jointSets[i]->m_familyID, sizeof(NvBlastID)); + family->m_familyIDMap[family->m_jointSets[i]->m_familyID] = i; + } + + // fill actor joint lists + for (uint32_t actorNum = 0; actorNum < family->m_actors.size(); ++actorNum) + { + TkActorImpl& actor = family->m_actors[actorNum]; + if (!actor.isActive()) + { + continue; // We may need a better way of iterating through active actors + } + + // Read joint information + uint32_t jointCount = stream.readDword(); + while (jointCount--) + { + const uint32_t attachmentFlags = stream.readDword(); + const uint32_t jointIndex = stream.readDword(); + if (!isInvalidIndex(jointIndex)) + { + // Internal joint + TkJointImpl& joint = internalJoints[jointIndex]; + TkJointData& jointData = joint.getDataWritable(); + + // Initialize joint if it has not been encountered yet + NVBLAST_ASSERT((joint.m_links[0].m_joint == nullptr) == (joint.m_links[1].m_joint == nullptr)); + if (joint.m_links[0].m_joint == nullptr) + { + new (&joint) TkJointImpl; + joint.m_owner = family; + for (int side = 0; side < 2; ++side) + { + jointData.chunkIndices[side] = stream.readDword(); + physx::PxVec3& attachPosition = jointData.attachPositions[side]; + attachPosition.x = stream.readFloat(); attachPosition.y = stream.readFloat(); attachPosition.z = stream.readFloat(); + } + } + + if (attachmentFlags & 1) + { + jointData.actors[0] = &actor; + actor.addJoint(joint.m_links[0]); + } + + if (attachmentFlags & 2) + { + jointData.actors[1] = &actor; + if (jointData.actors[0] != jointData.actors[1]) + { + actor.addJoint(joint.m_links[1]); + } + } + } + else + { + // External joint + const uint32_t otherFamilyIndex = stream.readDword(); + NVBLASTTK_CHECK_ERROR(otherFamilyIndex < family->m_jointSets.size(), "TkFamilyImpl::deserialize: family allocation failed.", return nullptr); + const NvBlastID& otherFamilyID = family->m_jointSets[otherFamilyIndex]->m_familyID; + TkFamilyImpl* otherFamily = static_cast<TkFamilyImpl*>(TkFrameworkImpl::get()->findObjectByIDInternal(otherFamilyID)); + + TkJointDesc jointDesc; + for (int side = 0; side < 2; ++side) + { + jointDesc.chunkIndices[side] = stream.readDword(); + physx::PxVec3& attachPosition = jointDesc.attachPositions[side]; + attachPosition.x = stream.readFloat(); attachPosition.y = stream.readFloat(); attachPosition.z = stream.readFloat(); + } + + NVBLASTTK_CHECK_ERROR(attachmentFlags != 3, "TkFamilyImpl::deserialize: both attached actors are the same in an external joint.", return nullptr); + + const uint32_t attachmentIndex = attachmentFlags >> 1; + + TkJointImpl** jointHandle = family->createExternalJointHandle(otherFamilyID, jointDesc.chunkIndices[attachmentIndex], jointDesc.chunkIndices[attachmentIndex ^ 1]); + NVBLASTTK_CHECK_ERROR(jointHandle != nullptr, "TkFamilyImpl::deserialize: joint handle could not be created.", return nullptr); + + if (otherFamily == nullptr) + { + // Other family does not exist yet, we'll create the joint + jointDesc.families[attachmentIndex] = family; + jointDesc.families[attachmentIndex ^ 1] = nullptr; + + TkJointImpl* joint = NVBLASTTK_NEW(TkJointImpl)(jointDesc, nullptr); + NVBLASTTK_CHECK_ERROR(joint != nullptr, "TkFamilyImpl::deserialize: joint createion failed.", return nullptr); + + *jointHandle = joint; + + actor.addJoint(joint->m_links[attachmentIndex]); + } + else + { + // Other family exists, and should have created the joint + TkJointImpl* joint = otherFamily->findExternalJoint(family, ExternalJointKey(jointDesc.chunkIndices[attachmentIndex ^ 1], jointDesc.chunkIndices[attachmentIndex])); + NVBLASTTK_CHECK_ERROR(joint != nullptr, "TkFamilyImpl::deserialize: other family should have created joint, but did not.", return nullptr); + + *jointHandle = joint; + + // Add the joint to its actor(s) + joint->getDataWritable().actors[attachmentIndex] = &actor; + actor.addJoint(joint->m_links[attachmentIndex]); + } + } + } + } + + return family; +} + + +TkFamilyImpl* TkFamilyImpl::create(const TkAssetImpl* asset) +{ + TkFamilyImpl* family = NVBLASTTK_NEW(TkFamilyImpl); + family->m_asset = asset; + void* mem = TkFrameworkImpl::get()->alloc(NvBlastAssetGetFamilyMemorySize(asset->getAssetLL(), TkFrameworkImpl::get()->log)); + family->m_familyLL = NvBlastAssetCreateFamily(mem, asset->getAssetLL(), TkFrameworkImpl::get()->log); + //family->addListener(*TkFrameworkImpl::get()); + + if (family->m_familyLL == nullptr) + { + NVBLASTTK_LOG_ERROR("TkFamilyImpl::create: low-level family could not be created."); + family->release(); + return nullptr; + } + + uint32_t maxActorCount = NvBlastFamilyGetMaxActorCount(family->m_familyLL, TkFrameworkImpl::get()->log); + family->m_actors.resize(maxActorCount); + + family->m_internalJointBuffer.resize(asset->getJointDescCountInternal() * sizeof(TkJointImpl), 0); + family->m_internalJointCount = asset->getJointDescCountInternal(); + + return family; +} + + +TkJointImpl** TkFamilyImpl::createExternalJointHandle(const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1) +{ + JointSet* jointSet; + const FamilyIDMap::Entry* jointSetIndexEntry = m_familyIDMap.find(otherFamilyID); + uint32_t otherFamilyIndex; + if (jointSetIndexEntry != nullptr) + { + otherFamilyIndex = jointSetIndexEntry->second; + jointSet = m_jointSets[otherFamilyIndex]; + } + else + { + jointSet = NVBLASTTK_NEW(JointSet); + NVBLASTTK_CHECK_ERROR(jointSet != nullptr, "TkFamilyImpl::addExternalJoint: failed to create joint set for other family ID.", return nullptr); + jointSet->m_familyID = otherFamilyID; + otherFamilyIndex = m_jointSets.size(); + m_familyIDMap[otherFamilyID] = otherFamilyIndex; + m_jointSets.pushBack(jointSet); + } + + const ExternalJointKey key(chunkIndex0, chunkIndex1); + const bool jointExists = jointSet->m_joints.find(key) != nullptr; + NVBLASTTK_CHECK_WARNING(!jointExists, "TkFamilyImpl::addExternalJoint: joint already added.", return nullptr); + + return &jointSet->m_joints[key]; +} + + +bool TkFamilyImpl::deleteExternalJointHandle(TkJointImpl*& joint, const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1) +{ + const FamilyIDMap::Entry* jointSetIndexEntry = m_familyIDMap.find(otherFamilyID); + if (jointSetIndexEntry != nullptr) + { + const uint32_t jointSetIndex = jointSetIndexEntry->second; + TkHashMap<ExternalJointKey, TkJointImpl*>::type::Entry e; + if (m_jointSets[jointSetIndex]->m_joints.erase(ExternalJointKey(chunkIndex0, chunkIndex1), e)) + { + // Delete the joint set if it is empty + if (m_jointSets[jointSetIndex]->m_joints.size() == 0) + { + NVBLASTTK_DELETE(m_jointSets[jointSetIndex], JointSet); + m_jointSets.replaceWithLast(jointSetIndex); + m_familyIDMap.erase(otherFamilyID); + if (jointSetIndex < m_jointSets.size()) + { + m_familyIDMap[m_jointSets[jointSetIndex]->m_familyID] = jointSetIndex; + } + } + + // Return value that was stored + joint = e.second; + return true; + } + } + + return false; +} + + +TkJointImpl* TkFamilyImpl::findExternalJoint(const TkFamilyImpl* otherFamily, ExternalJointKey key) const +{ + const FamilyIDMap::Entry* jointSetIndexEntry = m_familyIDMap.find(getFamilyID(otherFamily)); + if (jointSetIndexEntry != nullptr) + { + const TkHashMap<ExternalJointKey, TkJointImpl*>::type::Entry* e = m_jointSets[jointSetIndexEntry->second]->m_joints.find(key); + if (e != nullptr) + { + return e->second; + } + } + + return nullptr; +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.h new file mode 100644 index 0000000..571ee75 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkFamilyImpl.h @@ -0,0 +1,245 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKFAMILYIMPL_H +#define NVBLASTTKFAMILYIMPL_H + +#include "NvBlastTkCommon.h" + +#include "NvBlastTkFamily.h" +#include "NvBlastTkTypeImpl.h" +#include "NvBlastTkActorImpl.h" + +#include "NvBlastTkEventQueue.h" +#include "NvBlastTkHashSet.h" +#include "NvBlastTkHashMap.h" + +#include "NvBlast.h" +#include "NvBlastAssert.h" +#include "NvBlastDLink.h" + + +// Forward declarations +struct NvBlastFamily; + +namespace Nv +{ +namespace Blast +{ + +// Forward declarations +class TkGroupImpl; +class TkAssetImpl; + + +NVBLASTTK_IMPL_DECLARE(Family) +{ +public: + /** + Enum which keeps track of the serialized data format. + */ + enum Version + { + /** Initial version */ + Initial, + + // New formats must come before Count. They should be given descriptive names with more information in comments. + + /** The number of serialized formats. */ + Count, + + /** The current version. This should always be Count-1 */ + Current = Count - 1 + }; + + TkFamilyImpl(); + TkFamilyImpl(const NvBlastID& id); + ~TkFamilyImpl(); + + NVBLASTTK_IMPL_DEFINE_SERIALIZABLE('A', 'C', 'T', 'F'); + + // Begin TkFamily + virtual const NvBlastFamily* getFamilyLL() const override; + + virtual uint32_t getActorCount() const override; + + virtual uint32_t getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart = 0) const override; + + virtual void addListener(TkEventListener& l) override { m_queue.addListener(l); } + + virtual void removeListener(TkEventListener& l) override { m_queue.removeListener(l); } + + virtual void applyFracture(const NvBlastFractureBuffers* commands) override { applyFractureInternal(commands); } + + virtual const TkAsset* getAsset() const override; + + virtual void reinitialize(const NvBlastFamily* newFamily, TkGroup* group) override; + + virtual const void* getMaterial() const override; + + virtual void setMaterial(const void* material) override; + // End TkFamily + + // Public methods + static TkFamilyImpl* create(const TkAssetImpl* asset); + + const TkAssetImpl* getAssetImpl() const; + + NvBlastFamily* getFamilyLLInternal() const; + + uint32_t getActorCountInternal() const; + + TkActorImpl* addActor(NvBlastActor* actorLL); + + void applyFractureInternal(const NvBlastFractureBuffers* commands); + + void removeActor(TkActorImpl* actorLL); + + TkEventQueue& getQueue() { return m_queue; } + + TkActorImpl* getActorByActorLL(const NvBlastActor* actorLL); + + void updateJoints(TkActorImpl* actor, TkEventQueue* alternateQueue = nullptr); + + TkArray<TkActorImpl>::type& getActorsInternal(); + + uint32_t getInternalJointCount() const; + + TkJointImpl* getInternalJoints() const; + + TkJointImpl** createExternalJointHandle(const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1); + + bool deleteExternalJointHandle(TkJointImpl*& joint, const NvBlastID& otherFamilyID, uint32_t chunkIndex0, uint32_t chunkIndex1); + + void releaseJoint(TkJointImpl& joint); + + TkActorImpl* getActorByChunk(uint32_t chunkIndex); + + typedef physx::shdfnd::Pair<uint32_t, uint32_t> ExternalJointKey; //!< The chunk indices within the TkFamily joined by the joint. This chunks will be a supports chunks. + + TkJointImpl* findExternalJoint(const TkFamilyImpl* otherFamily, ExternalJointKey key) const; + +private: + TkActorImpl* getActorByIndex(uint32_t index); + + struct JointSet + { + NvBlastID m_familyID; + TkHashMap<ExternalJointKey, TkJointImpl*>::type m_joints; + }; + + typedef TkHashMap<NvBlastID, uint32_t>::type FamilyIDMap; + + NvBlastFamily* m_familyLL; + TkArray<TkActorImpl>::type m_actors; + uint32_t m_internalJointCount; + TkArray<uint8_t>::type m_internalJointBuffer; + TkArray<JointSet*>::type m_jointSets; + FamilyIDMap m_familyIDMap; + const TkAssetImpl* m_asset; + const void* m_material; + + TkEventQueue m_queue; +}; + + +//////// TkFamilyImpl inline methods //////// + +NV_INLINE const TkAssetImpl* TkFamilyImpl::getAssetImpl() const +{ + return m_asset; +} + + +NV_INLINE NvBlastFamily* TkFamilyImpl::getFamilyLLInternal() const +{ + return m_familyLL; +} + + +NV_INLINE uint32_t TkFamilyImpl::getActorCountInternal() const +{ + NVBLAST_ASSERT(m_familyLL != nullptr); + + return NvBlastFamilyGetActorCount(m_familyLL, TkFrameworkImpl::get()->log); +} + + +NV_INLINE TkActorImpl* TkFamilyImpl::getActorByIndex(uint32_t index) +{ + NVBLAST_ASSERT(index < m_actors.size()); + return &m_actors[index]; +} + + +NV_INLINE TkActorImpl* TkFamilyImpl::getActorByActorLL(const NvBlastActor* actorLL) +{ + uint32_t index = NvBlastActorGetIndex(actorLL, TkFrameworkImpl::get()->log); + return getActorByIndex(index); +} + + +NV_INLINE const void* TkFamilyImpl::getMaterial() const +{ + return m_material; +} + + +NV_INLINE void TkFamilyImpl::setMaterial(const void* material) +{ + m_material = material; +} + + +NV_INLINE TkArray<TkActorImpl>::type& TkFamilyImpl::getActorsInternal() +{ + return m_actors; +} + + +NV_INLINE uint32_t TkFamilyImpl::getInternalJointCount() const +{ + return m_internalJointCount; +} + + +NV_INLINE TkJointImpl* TkFamilyImpl::getInternalJoints() const +{ + return const_cast<TkJointImpl*>(reinterpret_cast<const TkJointImpl*>(m_internalJointBuffer.begin())); +} + + +NV_INLINE void TkFamilyImpl::releaseJoint(TkJointImpl& joint) +{ + NVBLAST_ASSERT(joint.m_owner == this); + NVBLAST_ASSERT(&joint >= getInternalJoints() && &joint < getInternalJoints() + getInternalJointCount() * sizeof(TkJointImpl)); + + joint.~TkJointImpl(); + joint.m_owner = nullptr; +} + + +//////// Inline global functions //////// + +NV_INLINE const NvBlastID& getFamilyID(const TkActor* actor) +{ + return actor != nullptr ? static_cast<const TkActorImpl*>(actor)->getFamilyImpl().getIDInternal() : *reinterpret_cast<const NvBlastID*>("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + +NV_INLINE const NvBlastID& getFamilyID(const TkFamilyImpl* family) +{ + return family != nullptr ? family->getIDInternal() : *reinterpret_cast<const NvBlastID*>("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"); +} + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKFAMILYIMPL_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.cpp new file mode 100644 index 0000000..6201101 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.cpp @@ -0,0 +1,613 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "NvBlastAssert.h" + +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastTkAssetImpl.h" +#include "NvBlastTkFamilyImpl.h" +#include "NvBlastTkGroupImpl.h" +#include "NvBlastTkActorImpl.h" +#include "NvBlastTkJointImpl.h" +#include "NvBlastTkTypeImpl.h" + +#include "PxAllocatorCallback.h" +#include "PxErrorCallback.h" +#include "PxFileBuf.h" + +#include <algorithm> + + +using namespace physx; +using namespace physx::shdfnd; +using namespace physx::general_PxIOStream2; + + +NV_INLINE bool operator < (const NvBlastID& id1, const NvBlastID& id2) +{ + return memcmp(&id1, &id2, sizeof(NvBlastID)) < 0; +} + + +namespace Nv +{ +namespace Blast +{ + +//////// Local definitions //////// + +// Map type ID to static type data +#define NVBLASTTK_REGISTER_TYPE(_name) \ + if (!Tk##_name##Impl::s_type.indexIsValid()) \ + { \ + Tk##_name##Impl::s_type.setIndex(TkTypeIndex::_name); \ + } \ + m_types[TkTypeIndex::_name] = &Tk##_name##Impl::s_type; \ + m_typeIDToIndex[Tk##_name##Impl::s_type.getID()] = TkTypeIndex::_name + + +#define NVBLASTTK_RELEASE_TYPE(_name) \ + { \ + TkTypeImpl& type = Tk##_name##Impl::s_type; \ + auto& toRelease = m_objects[type.getIndex()]; \ + for (TkObject* obj : toRelease) \ + { \ + obj->release(); \ + } \ + } + + +//////// TkFrameworkImpl static variables //////// + +TkFrameworkImpl* TkFrameworkImpl::s_framework = nullptr; + + +//////// TkFrameworkImpl static function //////// + +TkFrameworkImpl* TkFrameworkImpl::get() +{ + return s_framework; +} + + +bool TkFrameworkImpl::set(TkFrameworkImpl* framework) +{ + if (s_framework != nullptr) + { + if (framework != nullptr) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::set: framework already set. Pass NULL to this function to destroy framework."); + return false; + } + + PxAllocatorCallback& allocator = s_framework->getAllocatorCallbackInternal(); + s_framework->~TkFrameworkImpl(); + allocator.deallocate(s_framework); + } + + s_framework = framework; + + return true; +} + + +void TkFrameworkImpl::log(int type, const char* msg, const char* file, int line) +{ + if (s_framework == nullptr) + { + return; + } + + PxErrorCode::Enum pxErrorCode = PxErrorCode::eNO_ERROR; + switch (type) + { + case NvBlastMessage::Error: pxErrorCode = PxErrorCode::eINVALID_OPERATION; break; + case NvBlastMessage::Warning: pxErrorCode = PxErrorCode::eDEBUG_WARNING; break; + case NvBlastMessage::Info: pxErrorCode = PxErrorCode::eDEBUG_INFO; break; + case NvBlastMessage::Debug: pxErrorCode = PxErrorCode::eNO_ERROR; break; + } + + s_framework->getErrorCallback().reportError(pxErrorCode, msg, file, line); +} + + +void* TkFrameworkImpl::alloc(size_t size) +{ + if (s_framework == nullptr) + { + return nullptr; + } + + NV_COMPILE_TIME_ASSERT(Alignment > 0 && Alignment <= 256); + + unsigned char* mem = reinterpret_cast<unsigned char*>(s_framework->m_allocatorCallback->allocate(size + (size_t)Alignment, "NvBlast", __FILE__, __LINE__)); + + const unsigned char offset = (unsigned char)((uintptr_t)Alignment - (uintptr_t)mem % (size_t)Alignment - 1); + mem += offset; + *mem++ = offset; + + return mem; +} + + +void TkFrameworkImpl::free(void* mem) +{ + if (s_framework == nullptr) + { + return; + } + + unsigned char* ptr = reinterpret_cast<unsigned char*>(mem); + const unsigned char offset = *--ptr; + + return s_framework->m_allocatorCallback->deallocate(ptr - offset); +} + + +//////// TkFrameworkImpl methods //////// + +TkFrameworkImpl::TkFrameworkImpl(const TkFrameworkDesc& desc) + : TkFramework() + , m_errorCallback(desc.errorCallback) + , m_allocatorCallback(desc.allocatorCallback) +{ + // Static create() function should ensure these pointers are not NULL + NVBLAST_ASSERT(m_errorCallback != nullptr); + NVBLAST_ASSERT(m_allocatorCallback != nullptr); + + // Register types + m_types.resize(TkTypeIndex::TypeCount); + m_objects.resize(TkTypeIndex::TypeCount); + NVBLASTTK_REGISTER_TYPE(Asset); + NVBLASTTK_REGISTER_TYPE(Family); + NVBLASTTK_REGISTER_TYPE(Group); +} + + +TkFrameworkImpl::~TkFrameworkImpl() +{ +} + + +void TkFrameworkImpl::release() +{ + // Special release of joints, which are not TkIdentifiable: + TkArray<TkJointImpl*>::type joints; // Since the EraseIterator is not exposed + joints.reserve(m_joints.size()); + for (auto j = m_joints.getIterator(); !j.done(); ++j) + { + joints.pushBack(*j); + } + for (uint32_t i = 0; i < joints.size(); ++i) + { + joints[i]->release(); + } + NVBLAST_ASSERT(m_joints.size() == 0); + joints.reset(); // Since we will be deleting the allocator + + NVBLASTTK_RELEASE_TYPE(Group); + NVBLASTTK_RELEASE_TYPE(Asset); + set(nullptr); + Nv::Blast::TkAllocator::s_allocatorCallback = nullptr; +} + + +physx::PxErrorCallback& TkFrameworkImpl::getErrorCallback() const +{ + return getErrorCallbackInternal(); +} + + +physx::PxAllocatorCallback& TkFrameworkImpl::getAllocatorCallback() const +{ + return getAllocatorCallbackInternal(); +} + + +NvBlastLog TkFrameworkImpl::getLogFn() const +{ + return TkFrameworkImpl::log; +} + + +TkSerializable* TkFrameworkImpl::deserialize(PxFileBuf& stream) +{ + // Read framework ID + if (stream.readDword() != ClassID) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::deserialize: stream does not contain a BlastTk object."); + return nullptr; + } + + // Read object class ID and get class type data + const auto it = m_typeIDToIndex.find(stream.readDword()); + if (it == nullptr) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::deserialize: BlastTk object type unrecognized."); + return nullptr; + } + + const uint32_t index = (*it).second; + NVBLAST_ASSERT(index < m_types.size()); + + const TkTypeImpl* type = m_types[index]; + + // Read object class version and ensure it's current + if (stream.readDword() != type->getVersionInternal()) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::deserialize: BlastTk object version does not equal the current version for the loaded type."); + return nullptr; + } + + // Object ID + NvBlastID id; + stream.read(&id, sizeof(NvBlastID)); + + // Serializable user data + const uint32_t lsq = stream.readDword(); + const uint32_t msq = stream.readDword(); + + // All checks out, deserialize + TkSerializable* object = type->getDeserializeFn()(stream, id); + + // Set serializable user data if deserialization was successful + if (object != nullptr) + { + object->userIntData = static_cast<uint64_t>(msq) << 32 | static_cast<uint64_t>(lsq); + } + + return object; +} + + +const TkType* TkFrameworkImpl::getType(TkTypeIndex::Enum typeIndex) const +{ + if (typeIndex < 0 || typeIndex >= TkTypeIndex::TypeCount) + { + NVBLASTTK_LOG_WARNING("TkFrameworkImpl::getType: invalid typeIndex."); + return nullptr; + } + + return m_types[typeIndex]; +} + + +TkIdentifiable* TkFrameworkImpl::findObjectByID(const NvBlastID& id) const +{ + TkIdentifiable* object = findObjectByIDInternal(id); + + if (object == nullptr) + { + NVBLASTTK_LOG_WARNING("TkFrameworkImpl::findObjectByID: object not found."); + } + + return object; +} + + +uint32_t TkFrameworkImpl::getObjectCount(const TkType& type) const +{ + const uint32_t index = static_cast<const TkTypeImpl&>(type).getIndex(); + + if (index >= m_objects.size()) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::getObjectCount: BlastTk object type unrecognized."); + return 0; + + } + + return m_objects[index].size(); +} + + +uint32_t TkFrameworkImpl::getObjects(TkIdentifiable** buffer, uint32_t bufferSize, const TkType& type, uint32_t indexStart /* = 0 */) const +{ + const uint32_t index = static_cast<const TkTypeImpl&>(type).getIndex(); + + if (index >= m_objects.size()) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::getObjectCount: BlastTk object type unrecognized."); + return 0; + } + + const auto& objectArray = m_objects[index]; + + uint32_t objectCount = objectArray.size(); + if (objectCount <= indexStart) + { + NVBLASTTK_LOG_WARNING("TkFrameworkImpl::getObjects: indexStart beyond end of object list."); + return 0; + } + + objectCount -= indexStart; + if (objectCount > bufferSize) + { + objectCount = bufferSize; + } + + memcpy(buffer, objectArray.begin() + indexStart, objectCount * sizeof(TkObject*)); + + return objectCount; +} + + +bool TkFrameworkImpl::reorderAssetDescChunks(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount, NvBlastBondDesc* bondDescs, uint32_t bondCount, uint32_t* chunkReorderMap /*= nullptr*/) const +{ + uint32_t* map = chunkReorderMap != nullptr ? chunkReorderMap : static_cast<uint32_t*>(NVBLASTTK_ALLOC(chunkCount * sizeof(uint32_t), "reorderAssetDescChunks:chunkReorderMap")); + void* scratch = NVBLASTTK_ALLOC(chunkCount * sizeof(NvBlastChunkDesc), "reorderAssetDescChunks:scratch"); + const bool result = NvBlastReorderAssetDescChunks(chunkDescs, chunkCount, bondDescs, bondCount, map, scratch, log); + NVBLASTTK_FREE(scratch); + if (chunkReorderMap == nullptr) + { + NVBLASTTK_FREE(map); + } + return result; +} + + +bool TkFrameworkImpl::ensureAssetExactSupportCoverage(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount) const +{ + void* scratch = NVBLASTTK_ALLOC(chunkCount, "ensureAssetExactSupportCoverage:scratch"); + const bool result = NvBlastEnsureAssetExactSupportCoverage(chunkDescs, chunkCount, scratch, log); + NVBLASTTK_FREE(scratch); + return result; +} + + +TkAsset* TkFrameworkImpl::createAsset(const TkAssetDesc& desc) +{ + TkAssetImpl* asset = TkAssetImpl::create(desc); + if (asset == nullptr) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createAsset: failed to create asset."); + } + + return asset; +} + + +TkAsset* TkFrameworkImpl::createAsset(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs, uint32_t jointDescCount, bool ownsAsset) +{ + TkAssetImpl* asset = TkAssetImpl::create(assetLL, jointDescs, jointDescCount, ownsAsset); + if (asset == nullptr) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createAsset: failed to create asset."); + } + + return asset; +} + + +TkGroup* TkFrameworkImpl::createGroup(const TkGroupDesc& desc) +{ + TkGroupImpl* group = TkGroupImpl::create(desc); + if (group == nullptr) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createGroup: failed to create group."); + } + + return group; +} + + +TkActor* TkFrameworkImpl::createActor(const TkActorDesc& desc) +{ + TkActor* actor = TkActorImpl::create(desc); + if (actor == nullptr) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::createActor: failed to create actor."); + } + + return actor; +} + + +TkJoint* TkFrameworkImpl::createJoint(const TkJointDesc& desc) +{ + TkJointImpl** handle0 = nullptr; + TkJointImpl** handle1 = nullptr; + + TkFamilyImpl* family0 = static_cast<TkFamilyImpl*>(desc.families[0]); + TkFamilyImpl* family1 = static_cast<TkFamilyImpl*>(desc.families[1]); + + NVBLASTTK_CHECK_ERROR(family0 != nullptr || family1 != nullptr, "TkFrameworkImpl::createJoint: at least one family in the TkJointDesc must be valid.", return nullptr); + + NVBLASTTK_CHECK_ERROR(family0 == nullptr || desc.chunkIndices[0] < family0->getAssetImpl()->getChunkCount(), "TkFrameworkImpl::createJoint: desc.chunkIndices[0] is invalid.", return nullptr); + NVBLASTTK_CHECK_ERROR(family1 == nullptr || desc.chunkIndices[1] < family1->getAssetImpl()->getChunkCount(), "TkFrameworkImpl::createJoint: desc.chunkIndices[1] is invalid.", return nullptr); + + const bool actorsAreTheSame = family0 == family1 && family0->getActorByChunk(desc.chunkIndices[0]) == family1->getActorByChunk(desc.chunkIndices[1]); + NVBLASTTK_CHECK_ERROR(!actorsAreTheSame, "TkFrameworkImpl::createJoint: the chunks listed in the TkJointDesc must be in different actors.", return nullptr); + + if (family0 != nullptr) + { + const bool isSupportChunk = !isInvalidIndex(NvBlastAssetGetChunkToGraphNodeMap(family0->getAssetImpl()->getAssetLLInternal(), log)[desc.chunkIndices[0]]); + NVBLASTTK_CHECK_ERROR(isSupportChunk, "TkFrameworkImpl::createJoint: desc.chunkIndices[0] is not a support chunk in the asset for desc.families[0]. Joint not created.", return nullptr); + handle0 = family0->createExternalJointHandle(getFamilyID(family1), desc.chunkIndices[0], desc.chunkIndices[1]); + NVBLASTTK_CHECK_ERROR(handle0 != nullptr, "TkFrameworkImpl::createJoint: could not create joint handle in family[0]. Joint not created.", return nullptr); + } + + if (family1 != nullptr) + { + const bool isSupportChunk = !isInvalidIndex(NvBlastAssetGetChunkToGraphNodeMap(family1->getAssetImpl()->getAssetLLInternal(), log)[desc.chunkIndices[1]]); + NVBLASTTK_CHECK_ERROR(isSupportChunk, "TkFrameworkImpl::createJoint: desc.chunkIndices[1] is not a support chunk in the asset for desc.families[1]. Joint not created.", return nullptr); + if (family1 != family0) + { + handle1 = family1->createExternalJointHandle(getFamilyID(family0), desc.chunkIndices[1], desc.chunkIndices[0]); + NVBLASTTK_CHECK_ERROR(handle1 != nullptr, "TkFrameworkImpl::createJoint: could not create joint handle in family[1]. Joint not created.", return nullptr); + } + } + + TkJointImpl* joint = NVBLASTTK_NEW(TkJointImpl)(desc, nullptr); + NVBLASTTK_CHECK_ERROR(joint != nullptr, "TkFrameworkImpl::createJoint: failed to create joint.", return nullptr); + + const TkJointData& jointData = joint->getDataInternal(); + + if (handle0 != nullptr) + { + *handle0 = joint; + static_cast<TkActorImpl*>(jointData.actors[0])->addJoint(joint->m_links[0]); + } + + if (handle1 != nullptr) + { + *handle1 = joint; + if (jointData.actors[0] != jointData.actors[1]) + { + static_cast<TkActorImpl*>(jointData.actors[1])->addJoint(joint->m_links[1]); + } + } + + return joint; +} + + +bool TkFrameworkImpl::serializeHeader(const TkSerializable& object, PxFileBuf& stream) +{ + const TkTypeImpl& type = static_cast<const TkTypeImpl&>(object.getType()); + + // Tk framework identifier + stream.storeDword(ClassID); + + // Object header + stream.storeDword(type.getID()); + stream.storeDword(type.getVersionInternal()); + + // Object ID + stream.write(&object.getID(), sizeof(NvBlastID)); + + // Serializable user data + stream.storeDword(static_cast<uint32_t>(object.userIntData & 0xFFFFFFFF)); + stream.storeDword(static_cast<uint32_t>(object.userIntData >> 32)); + + return true; +} + + +void TkFrameworkImpl::onCreate(TkIdentifiable& object) +{ + const TkTypeImpl& type = static_cast<const TkTypeImpl&>(object.getType()); + + const uint32_t index = type.getIndex(); + + if (index >= m_objects.size()) + { + if (!isInvalidIndex(index)) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::addObject: object type unrecognized."); + } + return; + } + + auto& objectArray = m_objects[index]; + NVBLAST_ASSERT(objectArray.find(&object) == objectArray.end()); + objectArray.pushBack(&object); +} + + +void TkFrameworkImpl::onDestroy(TkIdentifiable& object) +{ + // remove from id map if present + const auto id = object.getID(); + if (!TkGUIDIsZero(&id)) + { + m_IDToObject.erase(id); + } + + // remove from object list + const TkTypeImpl& type = static_cast<const TkTypeImpl&>(object.getType()); + + const uint32_t index = type.getIndex(); + + if (index >= m_objects.size()) + { + if (!isInvalidIndex(index)) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::removeObject: object type unrecognized."); + } + return; + } + + auto& objectArray = m_objects[index]; + objectArray.findAndReplaceWithLast(&object); +} + + +void TkFrameworkImpl::onCreate(TkJointImpl& joint) +{ + NVBLASTTK_CHECK_ERROR(m_joints.insert(&joint), "TkFrameworkImpl::onCreate: Joint already tracked.", return); +} + + +void TkFrameworkImpl::onDestroy(TkJointImpl& joint) +{ + NVBLASTTK_CHECK_ERROR(m_joints.erase(&joint), "TkFrameworkImpl::onDestroy: Joint not tracked.", return); +} + + +void TkFrameworkImpl::onIDChange(TkIdentifiable& object, const NvBlastID& IDPrev, const NvBlastID& IDCurr) +{ + if (!TkGUIDIsZero(&IDPrev)) + { + if (!m_IDToObject.erase(IDPrev)) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::reportIDChanged: object with previous ID doesn't exist."); + } + } + + if (!TkGUIDIsZero(&IDCurr)) + { + auto& value = m_IDToObject[IDCurr]; + if (value != nullptr) + { + NVBLASTTK_LOG_ERROR("TkFrameworkImpl::reportIDChanged: object with new ID already exists."); + return; + } + value = &object; + } +} + +} // namespace Blast +} // namespace Nv + + +//////// Global API implementation //////// + +Nv::Blast::TkFramework* NvBlastTkFrameworkCreate(const Nv::Blast::TkFrameworkDesc& desc) +{ + if (desc.errorCallback == nullptr) + { + return nullptr; + } + + if (desc.allocatorCallback == nullptr) + { + desc.errorCallback->reportError(PxErrorCode::eINVALID_OPERATION, "TkFramework::create: NULL allocator given in descriptor.", __FILE__, __LINE__); + return nullptr; + } + + if (Nv::Blast::TkFrameworkImpl::get() != nullptr) + { + desc.errorCallback->reportError(PxErrorCode::eINVALID_OPERATION, "TkFramework::create: framework already created. Use TkFramework::get() to access.", __FILE__, __LINE__); + return nullptr; + } + + Nv::Blast::TkAllocator::s_allocatorCallback = desc.allocatorCallback; + + Nv::Blast::TkFrameworkImpl* framework = new (desc.allocatorCallback->allocate(sizeof(Nv::Blast::TkFrameworkImpl), "TkFrameworkImpl", __FILE__, __LINE__)) Nv::Blast::TkFrameworkImpl(desc); + Nv::Blast::TkFrameworkImpl::set(framework); + + return Nv::Blast::TkFrameworkImpl::get(); +} + + +Nv::Blast::TkFramework* NvBlastTkFrameworkGet() +{ + return Nv::Blast::TkFrameworkImpl::get(); +} diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.h new file mode 100644 index 0000000..ceeff06 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkFrameworkImpl.h @@ -0,0 +1,225 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKFRAMEWORKIMPL_H +#define NVBLASTTKFRAMEWORKIMPL_H + +#include "NvBlastTkFramework.h" +#include "NvBlastProfilerInternal.h" + +#include "NvBlastTkCommon.h" + +#include "NvBlastTkArray.h" +#include "NvBlastTkHashMap.h" +#include "NvBlastTkHashSet.h" + + +//////// Log macros that use the TkFrameworkImpl::log function //////// + +#define NVBLASTTK_LOG_ERROR(_msg) NVBLAST_LOG_ERROR(TkFrameworkImpl::log, _msg) +#define NVBLASTTK_LOG_WARNING(_msg) NVBLAST_LOG_WARNING(TkFrameworkImpl::log, _msg) +#define NVBLASTTK_LOG_INFO(_msg) NVBLAST_LOG_INFO(TkFrameworkImpl::log, _msg) +#define NVBLASTTK_LOG_DEBUG(_msg) NVBLAST_LOG_DEBUG(TkFrameworkImpl::log, _msg) + +#define NVBLASTTK_CHECK(_expr, _messageType, _msg, _onFail) \ + { \ + if(!(_expr)) \ + { \ + TkFrameworkImpl::log(_messageType, _msg, __FILE__, __LINE__); \ + { _onFail; }; \ + } \ + } + +#define NVBLASTTK_CHECK_ERROR(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Error, _msg, _onFail) +#define NVBLASTTK_CHECK_WARNING(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Warning, _msg, _onFail) +#define NVBLASTTK_CHECK_INFO(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Info, _msg, _onFail) +#define NVBLASTTK_CHECK_DEBUG(_expr, _msg, _onFail) NVBLASTTK_CHECK(_expr, NvBlastMessage::Debug, _msg, _onFail) + + +//////// Allocator macros //////// + +#define NVBLASTTK_ALLOC(_size, _name) TkFrameworkImpl::get()->getAllocatorCallbackInternal().allocate(_size, _name, __FILE__, __LINE__) +#define NVBLASTTK_FREE(_mem) TkFrameworkImpl::get()->getAllocatorCallbackInternal().deallocate(_mem) + +#define NVBLASTTK_NEW(T) new (NVBLASTTK_ALLOC(sizeof(T), #T)) T +#define NVBLASTTK_DELETE(obj, T) \ + (obj)->~T(); \ + NVBLASTTK_FREE(obj) + + + + + +namespace Nv +{ +namespace Blast +{ + +// Forward declarations +class TkTypeImpl; +class TkJointImpl; + +/** +Implementation of TkFramework +*/ +class TkFrameworkImpl : public TkFramework +{ +public: + TkFrameworkImpl(const TkFrameworkDesc& desc); + ~TkFrameworkImpl(); + + // Begin TkFramework + virtual void release() override; + + virtual physx::PxErrorCallback& getErrorCallback() const override; + + virtual physx::PxAllocatorCallback& getAllocatorCallback() const override; + + virtual NvBlastLog getLogFn() const override; + + virtual TkSerializable* deserialize(physx::general_PxIOStream2::PxFileBuf& stream) override; + + virtual const TkType* getType(TkTypeIndex::Enum typeIndex) const override; + + virtual TkIdentifiable* findObjectByID(const NvBlastID& id) const override; + + virtual uint32_t getObjectCount(const TkType& type) const override; + + virtual uint32_t getObjects(TkIdentifiable** buffer, uint32_t bufferSize, const TkType& type, uint32_t indexStart = 0) const override; + + virtual bool reorderAssetDescChunks(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount, NvBlastBondDesc* bondDescs, uint32_t bondCount, uint32_t* chunkReorderMap = nullptr) const override; + + virtual bool ensureAssetExactSupportCoverage(NvBlastChunkDesc* chunkDescs, uint32_t chunkCount) const override; + + virtual TkAsset* createAsset(const TkAssetDesc& desc) override; + + virtual TkAsset* createAsset(const NvBlastAsset* assetLL, Nv::Blast::TkAssetJointDesc* jointDescs = nullptr, uint32_t jointDescCount = 0, bool ownsAsset = false) override; + + virtual TkGroup* createGroup(const TkGroupDesc& desc) override; + + virtual TkActor* createActor(const TkActorDesc& desc) override; + + virtual TkJoint* createJoint(const TkJointDesc& desc) override; + // End TkFramework + + // Public methods + /** + Access to the error callback set by the user. + */ + physx::PxErrorCallback& getErrorCallbackInternal() const; + + /** + Access to the allocator callback set by the user. + */ + physx::PxAllocatorCallback& getAllocatorCallbackInternal() const; + + /** + To be called by any TkIdentifiable object when it is created, so the framework can track it. + */ + void onCreate(TkIdentifiable& object); + + /** + To be called by any TkIdentifiable object when it is deleted, so the framework can stop tracking it. + */ + void onDestroy(TkIdentifiable& object); + + /** + Special onCreate method for joints, since they are not TkIdentifiable. + */ + void onCreate(TkJointImpl& joint); + + /** + Special onDestroy method for joints, since they are not TkIdentifiable. + */ + void onDestroy(TkJointImpl& joint); + + /** + Must be called whenever a TkIdentifiable object's ID is changed, so that the framework can associate the new ID with it. + */ + void onIDChange(TkIdentifiable& object, const NvBlastID& IDPrev, const NvBlastID& IDCurr); + + /** + Internal (non-virtual) method to find a TkIdentifiable object based upon its NvBlastID. + */ + TkIdentifiable* findObjectByIDInternal(const NvBlastID& id) const; + + /** + Serialize a TkSerializable's standard header data, including its type ID, type version, object ID, and TkObject::userIntData. + */ + bool serializeHeader(const TkSerializable& object, physx::general_PxIOStream2::PxFileBuf& stream); + + // Access to singleton + + /** Retrieve the global singleton. */ + static TkFrameworkImpl* get(); + + /** Set the global singleton, if it's not already set, or set it to NULL. Returns true iff successful. */ + static bool set(TkFrameworkImpl* framework); + + // Blast LL context functions + static void log(int type, const char* msg, const char* file, int line); //!< A function with the NvBlastLog signature which can be used in NvBlast low-level function calls + static void* alloc(size_t size); //!< A function with the std::malloc signature which returns 16-byte aligned memory + static void free(void* mem); //!< A function with the std::free signature which can deallocate memory created by alloc + +private: + // Enums + enum { Alignment = 16 }; //!< Memory alignment used for allocations + enum { ClassID = NVBLASTTK_FOURCC('T', 'K', 'F', 'W') }; //!< TkFramework identifier token, used in serialization + + // Static data + static TkFrameworkImpl* s_framework; //!< Global (singleton) object pointer + + // Callbacks + physx::PxErrorCallback* m_errorCallback; //!< User-supplied error callback + physx::PxAllocatorCallback* m_allocatorCallback; //!< User-supplied allocator callback + + // Types + TkInlineArray<const TkTypeImpl*, TkTypeIndex::TypeCount>::type m_types; //!< TkIdentifiable static type data + TkHashMap<uint32_t, uint32_t>::type m_typeIDToIndex; //!< Map to type data keyed by ClassID + + // Objects and object names + TkHashMap<NvBlastID, TkIdentifiable*>::type m_IDToObject; //!< Map to all TkIdentifiable objects, keyed by NvBlastID + TkInlineArray<TkArray<TkIdentifiable*>::type, TkTypeIndex::TypeCount>::type m_objects; //!< Catalog of all TkIdentifiable objects, grouped by type. (Revisit implementation.) + + // Track external joints (to do: make this a pool) + TkHashSet<TkJointImpl*>::type m_joints; //!< All internal joints +}; + + +//////// TkFrameworkImpl inline methods //////// + +NV_INLINE physx::PxErrorCallback& TkFrameworkImpl::getErrorCallbackInternal() const +{ + return *m_errorCallback; +} + + +NV_INLINE physx::PxAllocatorCallback& TkFrameworkImpl::getAllocatorCallbackInternal() const +{ + return *m_allocatorCallback; +} + + +NV_INLINE TkIdentifiable* TkFrameworkImpl::findObjectByIDInternal(const NvBlastID& id) const +{ + const auto entry = m_IDToObject.find(id); + if (entry == nullptr) + { + return nullptr; + } + + return entry->second; +} + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKFRAMEWORKIMPL_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkGUID.h b/NvBlast/sdk/toolkit/source/NvBlastTkGUID.h new file mode 100644 index 0000000..a770092 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkGUID.h @@ -0,0 +1,135 @@ +// This code contains NVIDIA Confidential Information and is disclosed to you +// under a form of NVIDIA software license agreement provided separately to you. +// +// Notice +// NVIDIA Corporation and its licensors retain all intellectual property and +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKGUID_H +#define NVBLASTTKGUID_H + +#include "NvPreprocessor.h" + +#if NV_WINDOWS_FAMILY +#include <rpc.h> +#else +//#include <uuid/uuid.h> +#include "NvBlastTime.h" +#endif + +#include "PsHash.h" + +namespace Nv +{ +namespace Blast +{ + +#if NV_WINDOWS_FAMILY + +NV_INLINE NvBlastID TkGenerateGUID(void* ptr) +{ + NV_UNUSED(ptr); + + NV_COMPILE_TIME_ASSERT(sizeof(UUID) == sizeof(NvBlastID)); + + NvBlastID guid; + UuidCreate(reinterpret_cast<UUID*>(&guid)); + + return guid; +} + +#else + +NV_INLINE NvBlastID TkGenerateGUID(void* ptr) +{ +// NV_COMPILE_TIME_ASSERT(sizeof(uuid_t) == sizeof(NvBlastID)); + Time time; + + NvBlastID guid; + // uuid_generate_random(reinterpret_cast<uuid_t&>(guid)); + + *reinterpret_cast<uint64_t*>(guid.data) = reinterpret_cast<uintptr_t>(ptr); + *reinterpret_cast<int64_t*>(guid.data + 8) = time.getLastTickCount(); + + return guid; +} + +#endif + + +/** +Compares two NvBlastIDs. + +\param[in] id1 A pointer to the first id to compare. +\param[in] id2 A pointer to the second id to compare. + +\return true iff ids are equal. +*/ +NV_INLINE bool TkGUIDsEqual(const NvBlastID* id1, const NvBlastID* id2) +{ + return !memcmp(id1, id2, sizeof(NvBlastID)); +} + + +/** +Clears an NvBlastID (sets all of its fields to zero). + +\param[out] id A pointer to the ID to clear. +*/ +NV_INLINE void TkGUIDReset(NvBlastID* id) +{ + memset(id, 0, sizeof(NvBlastID)); +} + + +/** +Tests an NvBlastID to determine if it's zeroed. After calling TkGUIDReset +on an ID, passing it to this function will return a value of true. + +\param[in] id A pointer to the ID to test. +*/ +NV_INLINE bool TkGUIDIsZero(const NvBlastID* id) +{ + return *reinterpret_cast<const uint64_t*>(&id->data[0]) == 0 && *reinterpret_cast<const uint64_t*>(&id->data[8]) == 0; +} + +} // namespace Blast +} // namespace Nv + + +namespace physx +{ +namespace shdfnd +{ + +// hash specialization for NvBlastID +template <> +struct Hash<NvBlastID> +{ + uint32_t operator()(const NvBlastID& k) const + { + // "DJB" string hash + uint32_t h = 5381; + for (uint32_t i = 0; i < sizeof(k.data) / sizeof(k.data[0]); ++i) + h = ((h << 5) + h) ^ uint32_t(k.data[i]); + return h; + } + bool equal(const NvBlastID& k0, const NvBlastID& k1) const + { + return Nv::Blast::TkGUIDsEqual(&k0, &k1); + } +}; + +} // namespace shdfnd +} // namespace physx + + +#endif // #ifndef NVBLASTTKGUID_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.cpp new file mode 100644 index 0000000..d9a4b29 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.cpp @@ -0,0 +1,592 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "NvPreprocessor.h" + +#include "NvBlastAssert.h" +#include "NvBlast.h" + +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastTkGroupImpl.h" +#include "NvBlastTkActorImpl.h" +#include "NvBlastTkFamilyImpl.h" +#include "NvBlastTkAssetImpl.h" +#include "NvBlastTkTaskImpl.h" + +#include "Px.h" +#include "PxFileBuf.h" +#include "PxAllocatorCallback.h" +#include "task/PxCpuDispatcher.h" + +#undef max +#undef min +#include <algorithm> + +using namespace physx; +using namespace physx::general_PxIOStream2; + + +namespace Nv +{ +namespace Blast +{ + +//////// Static data //////// + +NVBLASTTK_DEFINE_TYPE_IDENTIFIABLE(Group); + + +//////// Local (static) functions //////// + +static uint32_t getNumThreads(PxTaskManager* tm) +{ + if (tm == nullptr) return 0; + if (tm->getCpuDispatcher() == nullptr) return 0; + return tm->getCpuDispatcher()->getWorkerCount(); +} + + +//////// Member functions //////// + +TkGroupImpl::TkGroupImpl() : m_actorCount(0), m_isProcessing(false), m_sync(0) +{ +#if NV_PROFILE + memset(&m_stats, 0, sizeof(TkGroupStats)); +#endif +} + + +TkGroupImpl::~TkGroupImpl() +{ + NVBLAST_ASSERT(getActorCount() == 0); + NVBLAST_ASSERT(m_sharedMemory.size() == 0); +} + + +void TkGroupImpl::release() +{ + if (isProcessing()) + { + // abort all processing? + NVBLASTTK_LOG_ERROR("TkGroup::release: cannot release Group while processing."); + NVBLAST_ALWAYS_ASSERT_MESSAGE("TkGroup::release: cannot release Group while processing."); + return; + } + + for (auto it = m_sharedMemory.getIterator(); !it.done(); ++it) + { + TkFamilyImpl* family = it->first; + for (TkActorImpl& actor : family->getActorsInternal()) + { + if (actor.m_group == this) + { + removeActorInternal(actor); + } + } + SharedMemory* mem = it->second; + mem->release(); + NVBLASTTK_DELETE(mem, SharedMemory); + } + m_sharedMemory.clear(); + + m_bondTempDataBlock.release(); + m_chunkTempDataBlock.release(); + m_bondEventDataBlock.release(); + m_chunkEventDataBlock.release(); + m_splitScratchBlock.release(); + + NVBLASTTK_DELETE(this, TkGroupImpl); +} + + +void TkGroupImpl::addActorsInternal(TkActorImpl** actors, uint32_t numActors) +{ + for (uint32_t i = 0; i < numActors; i++) + { + addActorInternal(*actors[i]); + } +} + + +void TkGroupImpl::addActorInternal(TkActorImpl& tkActor) +{ + NVBLAST_ASSERT(tkActor.getGroup() == nullptr); + tkActor.m_group = this; + m_actorCount++; +} + + +bool TkGroupImpl::addActor(TkActor& actor) +{ + TkActorImpl& tkActor = static_cast<TkActorImpl&>(actor); + if (tkActor.getGroup() != nullptr) + { + NVBLASTTK_LOG_ERROR("TkGroup::addActor: actor already belongs to a Group. Remove from current group first."); + return false; + } + + if (isProcessing()) + { + NVBLASTTK_LOG_ERROR("TkGroup::addActor: cannot alter Group while processing."); + return false; + } + + // mark the actor that it now belongs to this group + addActorInternal(tkActor); + + // actors that were fractured already or have damage requested + // must be enqueued to be processed + if (tkActor.isPending()) + { + enqueue(&tkActor); + } + + TkFamilyImpl& family = tkActor.getFamilyImpl(); + SharedMemory* mem = m_sharedMemory[&family]; + if (mem == nullptr) + { + // the actor belongs to a family not involved in this group yet + // shared memory must be allocated and temporary buffers adjusted accordingly + + PERF_ZONE_BEGIN("family memory"); + mem = NVBLASTTK_NEW(SharedMemory); + mem->allocate(family); + m_sharedMemory[&family] = mem; + PERF_ZONE_END("family memory"); + + PERF_ZONE_BEGIN("group memory"); + + const uint32_t numThreads = getNumThreads(m_pxTaskManager); + // one worker always exists, even if it is the main thread (when numThreads is 0) + const uint32_t numWorkers = std::max(numThreads, (uint32_t)1); + + // the number of threads could have changed, however this is unexpected and handled in process() + + + NvBlastLog theLog = TkFrameworkImpl::get()->log; + + // this group's tasks will use one temporary buffer each, which is of max size of, for all families involved + const size_t requiredScratch = NvBlastActorGetRequiredScratchForSplit(tkActor.getActorLL(), theLog); + if (static_cast<size_t>(m_splitScratchBlock.numElementsPerBlock()) < requiredScratch) + { + m_splitScratchBlock.release(); + m_splitScratchBlock.allocate(static_cast<uint32_t>(requiredScratch), numWorkers); + } + + // generate and apply fracture may create an entry for each bond + const uint32_t bondCount = NvBlastAssetGetBondCount(tkActor.getAsset()->getAssetLL(), theLog); + if (m_bondTempDataBlock.numElementsPerBlock() < bondCount) + { + m_bondTempDataBlock.release(); + m_bondTempDataBlock.allocate(bondCount, numWorkers); + m_bondEventDataBlock.release(); + m_bondEventDataBlock.allocate(bondCount, numWorkers); + } + + // apply fracture may create an entry for each lower-support chunk + const uint32_t graphNodeCount = NvBlastAssetGetSupportGraph(tkActor.getAsset()->getAssetLL(), theLog).nodeCount; + const uint32_t subsupportChunkCount + = NvBlastAssetGetChunkCount(tkActor.getAsset()->getAssetLL(), theLog) + - NvBlastAssetGetFirstSubsupportChunkIndex(tkActor.getAsset()->getAssetLL(), theLog); + const uint32_t chunkCount = graphNodeCount + subsupportChunkCount; + if (m_chunkTempDataBlock.numElementsPerBlock() < chunkCount) + { + m_chunkTempDataBlock.release(); + m_chunkTempDataBlock.allocate(chunkCount, numWorkers); + m_chunkEventDataBlock.release(); + m_chunkEventDataBlock.allocate(chunkCount, numWorkers); + } + PERF_ZONE_END("group memory"); + } + mem->addReference(); + + return true; +} + + +uint32_t TkGroupImpl::getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart /* = 0 */) const +{ + PERF_SCOPE_L("TkGroup::getActors"); + + uint32_t actorCount = m_actorCount; + if (actorCount <= indexStart) + { + NVBLASTTK_LOG_WARNING("TkGroup::getActors: indexStart beyond end of actor list."); + return 0; + } + + actorCount -= indexStart; + if (actorCount > bufferSize) + { + actorCount = bufferSize; + } + + uint32_t index = 0; + bool done = false; + for (auto it = const_cast<TkGroupImpl*>(this)->m_sharedMemory.getIterator(); !it.done();++it) + { + TkFamilyImpl* fam = it->first; + for (TkActorImpl& actor : fam->getActorsInternal()) + { + if (actor.m_group == this) + { + NVBLAST_ASSERT(actor.isActive()); + + if (index >= indexStart) + { + *buffer++ = &actor; + } + + index++; + done = (index - indexStart) >= actorCount; + } + if (done) break; + } + if (done) break; + } + + return actorCount; +} + + +void TkGroupImpl::removeActorInternal(TkActorImpl& tkActor) +{ + NVBLAST_ASSERT(tkActor.m_group == this); + tkActor.m_group = nullptr; + m_actorCount--; +} + + +void TkGroupImpl::releaseSharedMemory(TkFamilyImpl* fam, SharedMemory* mem) +{ + NVBLAST_ASSERT(mem != nullptr && m_sharedMemory[fam] == mem); + mem->release(); + m_sharedMemory.erase(fam); + NVBLASTTK_DELETE(mem, SharedMemory); +} + + +bool TkGroupImpl::removeActor(TkActor& actor) +{ + TkActorImpl& tkActor = static_cast<TkActorImpl&>(actor); + + if (tkActor.getGroup() != this) + { + NVBLASTTK_LOG_ERROR("TkGroup::removeActor: actor does not belong to this Group."); + return false; + } + + if (isProcessing()) + { + NVBLASTTK_LOG_ERROR("TkGroup::removeActor: cannot alter Group while processing."); + return false; + } + + removeActorInternal(tkActor); + + // pending actors must be removed from the job queue as well + if(tkActor.isPending()) + { + uint32_t index = tkActor.m_groupJobIndex; + tkActor.m_groupJobIndex = invalidIndex<uint32_t>(); + m_jobs.replaceWithLast(index); + if (index < m_jobs.size()) + { + NVBLAST_ASSERT(m_jobs[index].m_tkActor->m_groupJobIndex == m_jobs.size()); + NVBLAST_ASSERT(m_jobs[index].m_tkActor->isPending()); + m_jobs[index].m_tkActor->m_groupJobIndex = index; + } + } + + // if the actor is the last of its family in this group + // the group-family memory can be released + TkFamilyImpl* family = &tkActor.getFamilyImpl(); + SharedMemory* mem = getSharedMemory(family); + if (mem->removeReference()) + { + releaseSharedMemory(family, mem); + } + + return true; +} + + +TkGroupImpl* TkGroupImpl::create(const TkGroupDesc& desc) +{ + if (desc.pxTaskManager == nullptr) + { + NVBLASTTK_LOG_WARNING("TkGroup::create: attempting to create a Group with a NULL pxTaskManager."); + } + + TkGroupImpl* group = NVBLASTTK_NEW(TkGroupImpl); + + group->m_pxTaskManager = desc.pxTaskManager; + group->m_initialNumThreads = getNumThreads(group->m_pxTaskManager); + + return group; +} + + +bool TkGroupImpl::process() +{ + PERF_SCOPE_L("TkGroup::process"); + + if (!setProcessing(true)) + { + NVBLASTTK_LOG_WARNING("TkGroup::process: Group is still processing, call TkGroup::sync first."); + return false; + } + + if (m_jobs.size() > 0) + { + PERF_ZONE_BEGIN("task setup"); + + PERF_ZONE_BEGIN("task memory"); + const uint32_t numThreads = getNumThreads(m_pxTaskManager); + // one worker always exists, even if it is the main thread (when numThreads is 0) + const uint32_t numWorkers = std::max(numThreads, (uint32_t)1); + + if (numThreads != m_initialNumThreads) + { + NVBLASTTK_LOG_WARNING("TkGroup::process: number of threads has changed, memory is being reallocated."); + m_initialNumThreads = numThreads; + + const uint32_t bondCount = m_bondTempDataBlock.numElementsPerBlock(); + if (bondCount > 0) + { + m_bondTempDataBlock.release(); + m_bondTempDataBlock.allocate(bondCount, numWorkers); + m_bondEventDataBlock.release(); + m_bondEventDataBlock.allocate(bondCount, numWorkers); + } + const uint32_t chunkCount = m_chunkTempDataBlock.numElementsPerBlock(); + m_chunkTempDataBlock.release(); + m_chunkTempDataBlock.allocate(chunkCount, numWorkers); + m_chunkEventDataBlock.release(); + m_chunkEventDataBlock.allocate(chunkCount, numWorkers); + const uint32_t scratchSize = m_splitScratchBlock.numElementsPerBlock(); + m_splitScratchBlock.release(); + m_splitScratchBlock.allocate(scratchSize, numWorkers); + } + PERF_ZONE_END("task memory"); + + + PERF_ZONE_BEGIN("setup job queue"); + for (const auto& job : m_jobs) + { + const TkActorImpl* a = job.m_tkActor; + SharedMemory* mem = getSharedMemory(&a->getFamilyImpl()); + + const uint32_t damageCount = a->m_damageBuffer.size(); + + // applyFracture'd actor do not necessarily have damage queued + NVBLAST_ASSERT(damageCount > 0 || a->m_flags.isSet(TkActorFlag::DAMAGED)); + + // no reason to be here without these + NVBLAST_ASSERT(a->m_flags.isSet(TkActorFlag::PENDING)); + NVBLAST_ASSERT(a->m_group == this); + + // collect the amount of event payload memory to preallocate for TkWorkers + mem->m_eventsMemory += damageCount * (sizeof(TkFractureCommands) + sizeof(TkFractureEvents)) + sizeof(TkSplitEvent); + + // collect the amount of event entries to preallocate for TkWorkers + // (two TkFracture* events per damage plus one TkSplitEvent) + mem->m_eventsCount += 2 * damageCount + 1; + } + PERF_ZONE_END("setup job queue"); + + PERF_ZONE_BEGIN("memory protect"); + for (auto it = m_sharedMemory.getIterator(); !it.done(); ++it) + { + // preallocate the event memory for TkWorkers + SharedMemory* mem = it->second; + mem->m_events.reserveData(mem->m_eventsMemory); + mem->m_events.reserveEvents(mem->m_eventsCount); + + // these counters are not used anymore + // reset them immediately for next time + mem->m_eventsCount = 0; + mem->m_eventsMemory = 0; + + // switch to parallel mode + mem->m_events.protect(true); + } + PERF_ZONE_END("memory protect"); + + PERF_ZONE_END("task setup"); + + // ready queue for the workers + const uint32_t numJobs = m_jobs.size(); + m_jobQueue.init(m_jobs.begin(), numJobs); + + // do not start more workers than there are jobs + const uint32_t workersToRun = std::min(numWorkers, numJobs); + m_workers.resize(workersToRun); + m_sync.setCount(workersToRun); + + uint32_t workerId = 0; + if (numThreads > 0) + { + for (auto& task : m_workers) + { + PERF_SCOPE_M("task release"); + task.m_id = workerId++; + task.m_group = this; + task.setContinuation(*m_pxTaskManager, nullptr); + // mind m_sync.setCount above, immediately removing reference would not work with a continuation task + task.removeReference(); + } + } + else + { + // let this thread do the work + NVBLAST_ASSERT(m_workers.size() == 1); + for (auto& task : m_workers) + { + task.m_id = workerId++; + task.m_group = this; + task.run(); + task.release(); + } + } + } + + + return true; +} + + +bool TkGroupImpl::sync(bool block /*= true*/) +{ + if (!m_sync.isDone() && block) + { + PERF_SCOPE_L("TkGroupImpl::sync wait"); + m_sync.wait(); + } + + if (isProcessing() && m_sync.isDone()) + { + PERF_SCOPE_L("TkGroupImpl::sync finalize"); + + if (m_jobs.size() > 0) + { +#if NV_PROFILE + PERF_ZONE_BEGIN("accumulate timers"); + NvBlastTimers accumulated; + NvBlastTimersReset(&accumulated); + uint32_t jobCount = 0; + int64_t workerTime = 0; + for (TkWorker& worker : m_workers) + { + accumulated += worker.m_stats.timers; + jobCount += worker.m_stats.processedActorsCount; + workerTime += worker.m_stats.workerTime; + } + m_stats.timers = accumulated; + m_stats.processedActorsCount = jobCount; + m_stats.workerTime = workerTime; + PERF_ZONE_END("accumulate timers"); +#endif + + PERF_ZONE_BEGIN("job update"); + for (auto& j : m_jobs) + { + if (j.m_newActorsCount) + { + TkFamilyImpl* fam = &j.m_tkActor->getFamilyImpl(); + SharedMemory* mem = getSharedMemory(fam); + + // as LL is implemented, where newActorsCount the parent is always deleted + removeActorInternal(*j.m_tkActor); + mem->removeReference(); + addActorsInternal(j.m_newActors, j.m_newActorsCount); + mem->addReference(j.m_newActorsCount); + + // Update joints + mem->m_events.protect(false); // allow allocations again + fam->updateJoints(j.m_tkActor, &mem->m_events); + } + + // virtually dequeue the actor + // the queue itself is cleared right after this loop + j.m_tkActor->m_flags.clear(TkActorFlag::PENDING); + j.m_tkActor->m_groupJobIndex = invalidIndex<uint32_t>(); + j.m_tkActor->m_damageBuffer.clear(); + } + m_jobs.clear(); + PERF_ZONE_END("job update"); + + PERF_ZONE_BEGIN("event dispatch"); + for (auto it = m_sharedMemory.getIterator(); !it.done(); ++it) + { + TkFamilyImpl* family = it->first; + SharedMemory* mem = it->second; + + NVBLAST_ASSERT(family != nullptr); + NVBLAST_ASSERT(mem != nullptr && mem->isUsed()); + + // where no actor of a family has split, + // its group/family event queue has not been + // unprotected in the jobs loop above + mem->m_events.protect(false); + + family->getQueue().dispatch(mem->m_events); + + mem->m_events.reset(); + mem->reset(); + } + PERF_ZONE_END("event dispatch"); + + PERF_ZONE_BEGIN("event memory release"); + for (auto& worker : m_workers) + { + worker.m_bondBuffer.clear(); + worker.m_chunkBuffer.clear(); + } + PERF_ZONE_END("event memory release"); + } + + bool success = setProcessing(false); + NVBLAST_ASSERT(success); + return success; + } + + return false; +} + + +bool TkGroupImpl::setProcessing(bool value) +{ + bool expected = !value; + return m_isProcessing.compare_exchange_strong(expected, value); +} + + +void TkGroupImpl::enqueue(TkActorImpl* tkActor) +{ + NVBLAST_ASSERT(tkActor->getGroupImpl() != nullptr); + NVBLAST_ASSERT(tkActor->getGroupImpl() == this); + NVBLAST_ASSERT(isInvalidIndex(tkActor->m_groupJobIndex)); + NVBLAST_ASSERT(isProcessing() == false); +#if NV_DEBUG + for (TkWorkerJob& j : m_jobs) + { + NVBLAST_ASSERT(j.m_tkActor != tkActor); + } +#endif + + tkActor->m_groupJobIndex = m_jobs.size(); + TkWorkerJob& j = m_jobs.insert(); + j.m_tkActor = tkActor; +} + + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.h new file mode 100644 index 0000000..db7e7c1 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkGroupImpl.h @@ -0,0 +1,174 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKGROUPIMPL_H +#define NVBLASTTKGROUPIMPL_H + + +#include "NvBlastTkTaskImpl.h" +#include "NvBlastTkGroup.h" +#include "NvBlastTkTypeImpl.h" + + +namespace Nv +{ +namespace Blast +{ + +class TkActorImpl; +class TkFamilyImpl; + +NVBLASTTK_IMPL_DECLARE(Group) +{ + ~TkGroupImpl(); + +public: + TkGroupImpl(); + + NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE('G', 'R', 'P', '\0'); + + static TkGroupImpl* create(const TkGroupDesc& desc); + + // Begin TkGroup + virtual bool addActor(TkActor& actor) override; + + virtual uint32_t getActorCount() const override; + + virtual uint32_t getActors(TkActor** buffer, uint32_t bufferSize, uint32_t indexStart = 0) const override; + + virtual bool process() override; + + virtual bool sync(bool block = true) override; + + virtual void getStats(TkGroupStats& stats) const override; + // End TkGroup + + // TkGroupImpl API + + /** + Remove the actor from this group if the actor actually belongs to it and the group is not processing. + + \param[in] actor The TkActor to remove. + + \return true if removing succeeded, false otherwise + */ + bool removeActor(TkActor& actor); + + /** + Add the actor to this group's job queue. + It is the caller's responsibility to add an actor only once. This condition is checked in debug builds. + */ + void enqueue(TkActorImpl* tkActor); + + /** + Atomically check if this group is processing actors. @see setProcessing() + + \return true between process() and sync() calls, false otherwise + */ + bool isProcessing() const; + +private: + /** + Atomically set the processing state. This function checks for the current state + before changing it. @see isProcessing() + + \param[in] value the value of the new state + + \return true if the new state could be set, false otherwise + */ + bool setProcessing(bool value); + + /** + Get the group-family shared memory for the specified family. To be used when the memory is expected to already exist. + */ + SharedMemory* getSharedMemory(TkFamilyImpl* family); + void releaseSharedMemory(TkFamilyImpl* fam, SharedMemory* mem); + + // functions to add/remove actors _without_ group-family memory management + void addActorInternal(TkActorImpl& tkActor); + void addActorsInternal(TkActorImpl** actors, uint32_t numActors); + void removeActorInternal(TkActorImpl& tkActor); + + + physx::PxTaskManager* m_pxTaskManager; //!< the task manager used to dispatch workers + uint32_t m_initialNumThreads; //!< tracks the number of worker threads + + uint32_t m_actorCount; //!< number of actors in this group + + TkHashMap<TkFamilyImpl*, SharedMemory*>::type m_sharedMemory; //!< memory sharable by actors in the same family in this group + + // it is assumed no more than the asset's number of bond and chunks fracture commands are produced + SharedBlock<NvBlastChunkFractureData> m_chunkTempDataBlock; //!< chunk data for damage/fracture + SharedBlock<NvBlastBondFractureData> m_bondTempDataBlock; //!< bond data for damage/fracture + SharedBlock<NvBlastChunkFractureData> m_chunkEventDataBlock; //!< initial memory block for event data + SharedBlock<NvBlastBondFractureData> m_bondEventDataBlock; //!< initial memory block for event data + SharedBlock<char> m_splitScratchBlock; //!< split scratch memory + + std::atomic<bool> m_isProcessing; //!< true while workers are processing + TaskSync m_sync; //!< keeps track of finished workers + + TkArray<TkWorker>::type m_workers; //!< this group's workers + TkAtomicJobQueue m_jobQueue; //!< shared job queue for workers + + TkArray<TkWorkerJob>::type m_jobs; //!< this group's process jobs + +//#if NV_PROFILE + TkGroupStats m_stats; //!< accumulated group's worker stats +//#endif + + friend class TkWorker; +}; + + +NV_INLINE bool TkGroupImpl::isProcessing() const +{ + return m_isProcessing.load(); +} + + +NV_INLINE void TkGroupImpl::getStats(TkGroupStats& stats) const +{ +#if NV_PROFILE + memcpy(&stats, &m_stats, sizeof(TkGroupStats)); +#else + NV_UNUSED(stats); +#endif +} + + +NV_INLINE uint32_t TkGroupImpl::getActorCount() const +{ + return m_actorCount; +} + + +NV_INLINE SharedMemory* TkGroupImpl::getSharedMemory(TkFamilyImpl* family) +{ + SharedMemory* mem = m_sharedMemory[family]; + NVBLAST_ASSERT(mem != nullptr); + return mem; +} + + +NV_FORCE_INLINE void operator +=(NvBlastTimers& lhs, const NvBlastTimers& rhs) +{ + lhs.material += rhs.material; + lhs.fracture += rhs.fracture; + lhs.island += rhs.fracture; + lhs.partition += rhs.partition; + lhs.visibility += rhs.visibility; +} + + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKGROUPIMPL_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkHashMap.h b/NvBlast/sdk/toolkit/source/NvBlastTkHashMap.h new file mode 100644 index 0000000..14730c6 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkHashMap.h @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKHASHMAP_H +#define NVBLASTTKHASHMAP_H + + +#include "NvBlastTkAllocator.h" +#include "PsHashMap.h" + + +namespace Nv +{ +namespace Blast +{ + +template <class Key, class Value, class HashFn = physx::shdfnd::Hash<Key>> +struct TkHashMap +{ + typedef physx::shdfnd::HashMap<Key, Value, HashFn, TkAllocator> type; +}; + +} // namespace Blast +} // namespace Nv + + +#endif // #ifndef NVBLASTTKHASHMAP_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkHashSet.h b/NvBlast/sdk/toolkit/source/NvBlastTkHashSet.h new file mode 100644 index 0000000..4a3a3a0 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkHashSet.h @@ -0,0 +1,34 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKHASHSET_H +#define NVBLASTTKHASHSET_H + + +#include "NvBlastTkAllocator.h" +#include "PsHashSet.h" + + +namespace Nv +{ +namespace Blast +{ + +template <class Key, class HashFn = physx::shdfnd::Hash<Key>> +struct TkHashSet +{ + typedef physx::shdfnd::HashSet<Key, HashFn, TkAllocator> type; +}; + +} // namespace Blast +} // namespace Nv + + +#endif // #ifndef NVBLASTTKHASHSET_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.cpp new file mode 100644 index 0000000..46d6378 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.cpp @@ -0,0 +1,183 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastTkJointImpl.h" +#include "NvBlastTkActorImpl.h" +#include "NvBlastTkAssetImpl.h" +#include "NvBlastTkFamilyImpl.h" + +#include "Px.h" +#include "PxFileBuf.h" +#include "PxAllocatorCallback.h" + +using namespace physx::general_PxIOStream2; + + +namespace Nv +{ +namespace Blast +{ + +//////// Member functions //////// + +TkJointImpl::TkJointImpl(const TkJointDesc& desc, TkFamilyImpl* owner) : m_owner(owner) +{ + userData = nullptr; + + // Do not fire off a creation event. Creation events will only be fired when a family-internal joint is created. + NVBLAST_ASSERT(desc.families[0] != nullptr || desc.families[1] != nullptr); + NVBLAST_ASSERT(desc.families[0] == nullptr || desc.chunkIndices[0] < static_cast<TkFamilyImpl*>(desc.families[0])->getAssetImpl()->getChunkCount()); + NVBLAST_ASSERT(desc.attachPositions[0].isFinite()); + NVBLAST_ASSERT(desc.families[1] == nullptr || desc.chunkIndices[1] < static_cast<TkFamilyImpl*>(desc.families[1])->getAssetImpl()->getChunkCount()); + NVBLAST_ASSERT(desc.attachPositions[1].isFinite()); + + for (int i = 0; i < 2; ++i) + { + m_data.actors[i] = desc.families[i] != nullptr ? static_cast<TkFamilyImpl*>(desc.families[i])->getActorByChunk(desc.chunkIndices[i]) : nullptr; + m_data.chunkIndices[i] = desc.chunkIndices[i]; + m_data.attachPositions[i] = desc.attachPositions[i]; + m_links[i].m_joint = this; + } + + if (owner == nullptr) + { + TkFrameworkImpl::get()->onCreate(*this); + } +} + + +void TkJointImpl::release() +{ + removeReferencesInActors(); + + if (m_owner != nullptr) + { + // Internal joint + m_owner->releaseJoint(*this); + } + else + { + // External joint + removeReferencesInFamilies(); + TkFrameworkImpl::get()->onDestroy(*this); + NVBLASTTK_DELETE(this, TkJointImpl); + } +} + + +void TkJointImpl::setActors(TkActorImpl* actor0, TkActorImpl* actor1, TkEventQueue* alternateQueue) +{ + NVBLAST_ASSERT(m_data.actors[0] != nullptr || m_data.actors[1] != nullptr); + + const bool unreferenced = (actor0 == nullptr && m_data.actors[0] != nullptr) || (actor1 == nullptr && m_data.actors[1] != nullptr); + + removeReferencesInActors(); + + if (!unreferenced) + { + if (actor0 != nullptr) + { + actor0->addJoint(m_links[0]); + } + + if (actor1 != nullptr && actor1 != actor0) // If the actors are the same, we only need one joint reference + { + actor1->addJoint(m_links[1]); + } + } + + // We do _not_ return if m_data.m_actors[0] == actor0 && m_data.m_actors[1] == actor1 since + // this leads to a bug. This function will only be called when an actor is split. It is + // possible that the two TkActors in a joint are the same as before, but in this case one + // of the actors will be the split actor. Since will be represented by a different + // physical actor, this case still needs to be reported in an event. Returning when neither + // TkActor has changed will prevent that, and lead to unwanted joint disconnection. + + const uint32_t familyToUse = m_data.actors[0] != actor0 ? 0 : 1; + + TkEventQueue* q = alternateQueue == nullptr ? + &static_cast<TkActorImpl*>(m_data.actors[familyToUse])->getFamilyImpl().getQueue() + : alternateQueue; + + const bool jointWasInternal = m_data.actors[0] == m_data.actors[1]; + + if (unreferenced) + { + removeReferencesInFamilies(); + actor0 = actor1 = nullptr; // Make both new actors NULL + } + + if (!jointWasInternal || actor0 != actor1) + { + // The original actors were different, or they are now, signal a joint update + TkJointUpdateEvent* e = q->allocData<TkJointUpdateEvent>(); + e->joint = this; + e->subtype = unreferenced ? TkJointUpdateEvent::Unreferenced : (jointWasInternal ? TkJointUpdateEvent::External : TkJointUpdateEvent::Changed); + m_data.actors[0] = actor0; + m_data.actors[1] = actor1; + q->addEvent(e); + } + else + if (jointWasInternal) + { + // The joint was originally created within the same actor and now it remains within the same actor. + m_data.actors[0] = m_data.actors[1] = actor0; + } +} + + +const TkJointData TkJointImpl::getData() const +{ + return getDataInternal(); +} + + +void TkJointImpl::removeReferencesInActors() +{ + TkActorImpl* actor0 = static_cast<TkActorImpl*>(m_data.actors[0]); + TkActorImpl* actor1 = static_cast<TkActorImpl*>(m_data.actors[1]); + + if (actor0 != nullptr) + { + actor0->removeJoint(m_links[0]); + } + + if (actor1 != nullptr && actor1 != actor0) // If the actors are the same, we only had one joint reference + { + actor1->removeJoint(m_links[1]); + } +} + + +void TkJointImpl::removeReferencesInFamilies() +{ + if (m_owner != nullptr) + { + return; // Only concerned with external joints + } + + NVBLAST_ASSERT(m_data.actors[0] != m_data.actors[1] || m_data.actors[0] == nullptr); // This is enforced by the initial assumption in TkFrameworkImpl::createJoint. + + for (int i = 0; i < 2; ++i) + { + if (m_data.actors[i] != nullptr) + { + TkFamilyImpl& family = static_cast<TkActorImpl*>(m_data.actors[i])->getFamilyImpl(); + TkJointImpl* joint = nullptr; + const bool found = family.deleteExternalJointHandle(joint, getFamilyID(m_data.actors[i ^ 1]), m_data.chunkIndices[i], m_data.chunkIndices[i ^ 1]); + NVBLAST_ASSERT((!found && m_data.actors[i ^ 1] == nullptr) || joint == this); // Might not be found if the actors in a family are in the process of being deleted + NV_UNUSED(found); + } + } +} + +} // namespace Blast +} // namespace Nv diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.h new file mode 100644 index 0000000..ec57309 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkJointImpl.h @@ -0,0 +1,146 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKJOINTIMPL_H +#define NVBLASTTKJOINTIMPL_H + + +#include "NvBlastTkJoint.h" +#include "NvBlastTkCommon.h" +#include "NvBlastIndexFns.h" + +#include "NvBlastAssert.h" +#include "NvBlastDLink.h" + +#include <atomic> + + +namespace Nv +{ +namespace Blast +{ + +// Forward declarations +class TkActorImpl; +class TkJointImpl; +class TkFamilyImpl; +class TkEventQueue; + + +/** +Double-sided link (DLink) which holds a reference back to a joint which contains it. +*/ +struct TkJointLink : public DLink +{ + TkJointImpl* m_joint; //!< The joint containing this link. +}; + + +/** +Implementation of TkJoint. +*/ +class TkJointImpl : public TkJoint +{ +public: + /** Blank constructor only creates valid TkJointLinks (point back to this object) */ + TkJointImpl(); + + /** + This constructor sets all internal data. If the joint is defined in an asset, the family + instanced from that asset will own this joint, and the 'owner' parameter is that family. + Otherwise, in the case where a joint is created from TkFramwork::createJoint, the joint + is not owned by a family and 'owner' will be NULL. + */ + TkJointImpl(const TkJointDesc& desc, TkFamilyImpl* owner); + + // Begin TkObject + virtual void release() override; + // End TkObject + + // Begin TkJoint + virtual const TkJointData getData() const override; + // End TkJoint + + // Public API + + /** + Internal method to access a const reference to the joint data. + + \return a const reference to the joint data. + */ + const TkJointData& getDataInternal() const; + + /** + Internal method to access a non-const reference to the joint data. + + \return a non-const reference to the joint data. + */ + TkJointData& getDataWritable(); + + /** + Set the actors that this joint attaches to. When the actors are different from the joint's current actors, + an event will be generated on one of the actors' families event queues to signal the change. Alternatively, + if alternateQueue is not NULL then it will be used to hold the event. + + If a non-NULL attached actor becomes NULL, then this joint will detach its references to both actors (if + they exist) and send an event of subtype Unreferenced. This signals the user that the joint may be deleted. + + \param[in] actor0 The new TkActor to replace the first attached actor. + \param[in] actor1 The new TkActor to replace the second attached actor. + \param[in] alternateQueue If not NULL, this queue will be used to hold events generated by this function. + */ + void setActors(TkActorImpl* actor0, TkActorImpl* actor1, TkEventQueue* alternateQueue = nullptr); + + /** + Ensures that any attached actors no longer refer to this joint. + */ + void removeReferencesInActors(); + + /** + Ensures that any attached actors' families no longer refer to this joint. External joints (created using + TkFramework::createJoint) are referenced by the attached actors' families. + */ + void removeReferencesInFamilies(); + +private: + TkJointData m_data; //!< The data given to the user: attached actors, chunk indices, and actor-local attachment positions. + TkJointLink m_links[2]; //!< One link for each actor in m_data.m_actors. If m_data.m_actors[0] == m_data.m_actors[1], then only m_links[0] is used. + TkFamilyImpl* m_owner; //!< The owning family if this is an internal joint created during TkFramework::createActor() from a TkAssetDesc with joint flags. + + friend class TkFrameworkImpl; + friend class TkFamilyImpl; + friend class TkActorImpl; +}; + + +//////// TkJointImpl inline methods //////// + +NV_INLINE TkJointImpl::TkJointImpl() +{ + m_links[0].m_joint = m_links[1].m_joint = this; +} + + +NV_INLINE const TkJointData& TkJointImpl::getDataInternal() const +{ + return m_data; +} + + +NV_INLINE TkJointData& TkJointImpl::getDataWritable() +{ + return m_data; +} + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKJOINTIMPL_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.cpp b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.cpp new file mode 100644 index 0000000..3249928 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.cpp @@ -0,0 +1,263 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#include "NvBlastTime.h" + +#include "NvBlastTkTaskImpl.h" +#include "NvBlastTkFamilyImpl.h" +#include "NvBlastTkAssetImpl.h" +#include "NvBlastTkGroupImpl.h" + + +using namespace Nv::Blast; + + +void SharedMemory::allocate(TkFamilyImpl& tkFamily) +{ + NVBLAST_ASSERT(m_refCount == 0); + const NvBlastAsset* assetLL = tkFamily.getAsset()->getAssetLL(); + + // at most leafChunkCount actors can be created within a family + // tasks will grab their portion out of these memory blocks + uint32_t leafChunkCount = NvBlastAssetGetLeafChunkCount(assetLL, TkFrameworkImpl::get()->log); + m_newActorBuffers.allocate(2 * leafChunkCount); // GWD-167 workaround (2*) + m_newTkActorBuffers.allocate(leafChunkCount); +} + + +/** +Creates a TkEvent::FractureCommand according to the input buffer for tkActor +into events queue using the LocalBuffers to store the actual event data. +*/ +NV_FORCE_INLINE void reportFractureCommands( + const NvBlastFractureBuffers& buffer, + LocalBuffer<NvBlastBondFractureData>& bondBuffer, LocalBuffer<NvBlastChunkFractureData>& chunkBuffer, + TkEventQueue& events, const TkActorImpl* tkActor) +{ + + NvBlastBondFractureData* bdata = nullptr; + if (buffer.bondFractureCount > 0) + { + bdata = bondBuffer.allocate(buffer.bondFractureCount); + memcpy(bdata, buffer.bondFractures, sizeof(NvBlastBondFractureData)*buffer.bondFractureCount); + } + + NvBlastChunkFractureData* cdata = nullptr; + if (buffer.chunkFractureCount > 0) + { + cdata = chunkBuffer.allocate(buffer.chunkFractureCount); + memcpy(cdata, buffer.chunkFractures, sizeof(NvBlastChunkFractureData)*buffer.chunkFractureCount); + } + + TkFractureCommands* fevt = events.allocData<TkFractureCommands>(); + fevt->tkActorData = *tkActor; + fevt->buffers = { buffer.bondFractureCount, buffer.chunkFractureCount, bdata, cdata }; + events.addEvent(fevt); +} + + +/** +Creates a TkEvent::FractureEvent according to the input buffer for tkActor +into events queue using the LocalBuffers to store the actual event data. +*/ +NV_FORCE_INLINE void reportFractureEvents( + const NvBlastFractureBuffers& buffer, + LocalBuffer<NvBlastBondFractureData>& bondBuffer, LocalBuffer<NvBlastChunkFractureData>& chunkBuffer, + TkEventQueue& events, const TkActorImpl* tkActor) +{ + uint32_t result[4] = { 0,0,0,0 }; + + NvBlastBondFractureData* bdata = nullptr; + if (buffer.bondFractureCount > 0) + { + bdata = bondBuffer.allocate(buffer.bondFractureCount); + for (uint32_t b = 0; b < buffer.bondFractureCount; ++b) + { + bdata[b] = buffer.bondFractures[b]; + result[buffer.bondFractures[b].health > 0 ? 0 : 1]++; + } + } + + NvBlastChunkFractureData* cdata = nullptr; + if (buffer.chunkFractureCount > 0) + { + cdata = chunkBuffer.allocate(buffer.chunkFractureCount); + for (uint32_t c = 0; c < buffer.chunkFractureCount; ++c) + { + cdata[c] = buffer.chunkFractures[c]; + result[buffer.chunkFractures[c].health > 0 ? 2 : 3]++; + } + } + + TkFractureEvents* fevt = events.allocData<TkFractureEvents>(); + fevt->tkActorData = *tkActor; + fevt->buffers = { buffer.bondFractureCount, buffer.chunkFractureCount, bdata, cdata }; + fevt->bondsDamaged = result[0]; + fevt->bondsBroken = result[1]; + fevt->chunksDamaged = result[2]; + fevt->chunksBroken = result[3]; + events.addEvent(fevt); +} + + +void TkWorker::run() +{ + PERF_SCOPE_L("TkWorker Task"); + + NvBlastTimers* timers = nullptr; + +#if NV_PROFILE + NvBlastTimers myTimers; + timers = &myTimers; + NvBlastTimersReset(timers); + uint32_t jobCount = 0; + Time workTime; +#endif + + // temporary memory used to generate and apply fractures + // it must fit for the largest family involved in the group that owns this worker + NvBlastBondFractureData* bondFractureData = m_group->m_bondTempDataBlock.getBlock(m_id); + uint32_t bondFractureCount = m_group->m_bondTempDataBlock.numElementsPerBlock(); + NvBlastChunkFractureData* chunkFractureData = m_group->m_chunkTempDataBlock.getBlock(m_id); + uint32_t chunkFractureCount = m_group->m_chunkTempDataBlock.numElementsPerBlock(); + const NvBlastFractureBuffers tempBuffer = { bondFractureCount, chunkFractureCount, bondFractureData, chunkFractureData }; + + // temporary memory used to split the actor + // large enough for the largest family involved + void* splitScratch = m_group->m_splitScratchBlock.getBlock(m_id); + + // to avoid unnecessary allocations, preallocated memory exists to fit all chunks and bonds taking damage once + // where multiple damage occurs, more memory will be allocated on demand (this may thwart other threads doing the same) + m_bondBuffer.initialize(m_group->m_bondEventDataBlock.getBlock(m_id), m_group->m_bondEventDataBlock.numElementsPerBlock()); + m_chunkBuffer.initialize(m_group->m_chunkEventDataBlock.getBlock(m_id), m_group->m_chunkEventDataBlock.numElementsPerBlock()); + + TkAtomicJobQueue& q = m_group->m_jobQueue; + TkWorkerJob* j; + + while ((j = q.next()) != nullptr) + { + PERF_SCOPE_M("TkActor"); + + TkActorImpl* tkActor = j->m_tkActor; + const uint32_t tkActorIndex = tkActor->getIndex(); + NvBlastActor* actorLL = tkActor->getActorLLInternal(); + TkFamilyImpl& family = tkActor->getFamilyImpl(); + SharedMemory* mem = m_group->getSharedMemory(&family); + TkEventQueue& events = mem->m_events; + + NVBLAST_ASSERT(tkActor->getGroupImpl() == m_group); + +#if NV_PROFILE + *timers += tkActor->m_timers; + NvBlastTimersReset(&tkActor->m_timers); + jobCount++; +#endif + + // generate and apply fracture for all damage requested on this actor + // and queue events accordingly + for (const auto& damage : tkActor->m_damageBuffer) + { + NvBlastFractureBuffers commandBuffer = tempBuffer; + + PERF_ZONE_BEGIN("Material"); + damage.generateFracture(&commandBuffer, actorLL, timers); + PERF_ZONE_END("Material"); + + if (commandBuffer.chunkFractureCount > 0 || commandBuffer.bondFractureCount > 0) + { + PERF_SCOPE_M("Fill Command Events"); + reportFractureCommands(commandBuffer, m_bondBuffer, m_chunkBuffer, events, tkActor); + } + + NvBlastFractureBuffers eventBuffer = tempBuffer; + + PERF_ZONE_BEGIN("Fracture"); + NvBlastActorApplyFracture(&eventBuffer, actorLL, &commandBuffer, TkFrameworkImpl::get()->log, timers); + PERF_ZONE_END("Fracture"); + + if (eventBuffer.chunkFractureCount > 0 || eventBuffer.bondFractureCount > 0) + { + PERF_SCOPE_M("Fill Fracture Events"); + tkActor->m_flags |= (TkActorFlag::DAMAGED); + reportFractureEvents(eventBuffer, m_bondBuffer, m_chunkBuffer, events, tkActor); + } + } + + + // split the actor, which could have been damaged directly though the TkActor's fracture functions + // i.e. it did not have damage queued for the above loop + + NvBlastActorSplitEvent splitEvent = { nullptr, nullptr }; + if (tkActor->isDamaged()) + { + PERF_ZONE_BEGIN("Split Memory"); + uint32_t maxActorCount = NvBlastActorGetMaxActorCountForSplit(actorLL, TkFrameworkImpl::get()->log); + splitEvent.newActors = mem->reserveNewActors(maxActorCount); + PERF_ZONE_END("Split Memory"); + PERF_ZONE_BEGIN("Split"); + j->m_newActorsCount = NvBlastActorSplit(&splitEvent, actorLL, maxActorCount, splitScratch, TkFrameworkImpl::get()->log, timers); + PERF_ZONE_END("Split"); + + tkActor->m_flags.clear(TkActorFlag::DAMAGED); + } + else + { + j->m_newActorsCount = 0; + } + + + // update the TkActor according to the LL split results and queue events accordingly + if (j->m_newActorsCount > 0) + { + NVBLAST_ASSERT(splitEvent.deletedActor == tkActor->getActorLL()); + + PERF_ZONE_BEGIN("memory new actors"); + + auto tkSplitEvent = events.allocData<TkSplitEvent>(); + + tkSplitEvent->children = mem->reserveNewTkActors(j->m_newActorsCount); + tkSplitEvent->numChildren = j->m_newActorsCount; + + tkSplitEvent->parentData.family = &family; + tkSplitEvent->parentData.userData = tkActor->userData; + tkSplitEvent->parentData.index = tkActorIndex; + family.removeActor(tkActor); + + PERF_ZONE_END("memory new actors"); + + + PERF_ZONE_BEGIN("create new actors"); + for (uint32_t i = 0; i < j->m_newActorsCount; ++i) + { + TkActorImpl* newActor = family.addActor(splitEvent.newActors[i]); + tkSplitEvent->children[i] = newActor; + } + j->m_newActors = reinterpret_cast<TkActorImpl**>(tkSplitEvent->children); + PERF_ZONE_END("create new actors"); + + PERF_ZONE_BEGIN("split event"); + events.addEvent(tkSplitEvent); + PERF_ZONE_END("split event"); + } + } + +#if NV_PROFILE + PERF_ZONE_BEGIN("write timers"); + m_stats.timers = *timers; + m_stats.processedActorsCount = jobCount; + m_stats.workerTime = workTime.getElapsedTicks(); + PERF_ZONE_END("write timers"); +#endif +} + +void TkWorker::release() +{ + m_group->m_sync.notify(); +} diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.h new file mode 100644 index 0000000..75f92f8 --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkTaskImpl.h @@ -0,0 +1,444 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKTASKIMPL_H +#define NVBLASTTKTASKIMPL_H + +#include "NvBlast.h" + +#include "NvBlastTkFrameworkImpl.h" +#include "NvBlastTkEventQueue.h" +#include "NvBlastTkArray.h" + +#include <atomic> +#include <mutex> +#include <condition_variable> + +#include "task/PxTask.h" +#include "NvBlastAssert.h" + +#include "NvBlastTkGroup.h" // TkGroupStats + + +namespace Nv +{ +namespace Blast +{ + +class TkGroupImpl; +class TkActorImpl; +class TkFamilyImpl; + + +/** +Transient structure describing a job and its results. +*/ +struct TkWorkerJob +{ + TkActorImpl* m_tkActor; //!< the actor to process + TkActorImpl** m_newActors; //!< list of child actors created by splitting + uint32_t m_newActorsCount; //!< the number of child actors created +}; + + +/** +Counting synchronization object for waiting on TkWorkers to finish. +*/ +class TaskSync +{ +public: + /** + Initializes with an expected number of notifications. + */ + TaskSync(uint32_t count) : m_count(count) {} + + /** + Blocks until the expected number of notifications happened. + */ + void wait() + { + std::unique_lock<std::mutex> lk(m_mutex); + m_cv.wait(lk, [&]{ return m_count == 0; }); + } + + /** + Decrement the wait() count by one. + */ + void notify() + { + PERF_SCOPE_H("TaskSync::notify"); + std::unique_lock<std::mutex> lk(m_mutex); + m_count--; + if (m_count == 0) + { + lk.unlock(); + m_cv.notify_one(); + } + } + + /** + Peek if notifications are pending. + */ + bool isDone() + { + std::unique_lock<std::mutex> lk(m_mutex); + return m_count == 0; + } + + /** + Sets the expected number of notifications for wait() to unblock. + */ + void setCount(uint32_t count) + { + m_count = count; + } + +private: + std::mutex m_mutex; + std::condition_variable m_cv; + uint32_t m_count; +}; + + +/** +A list of equally sized memory blocks sharable between tasks. +*/ +template<typename T> +class SharedBlock +{ +public: + + SharedBlock() : m_numElementsPerBlock(0), m_numBlocks(0), m_buffer(nullptr) {} + + /** + Allocates one large memory block of elementsPerBlock*numBlocks elements. + */ + void allocate(uint32_t elementsPerBlock, uint32_t numBlocks) + { + NVBLAST_ASSERT(elementsPerBlock > 0 && numBlocks > 0); + + m_buffer = reinterpret_cast<T*>(NVBLASTTK_ALLOC(elementsPerBlock*numBlocks*sizeof(T), "SharedBlock")); + m_numElementsPerBlock = elementsPerBlock; + m_numBlocks = numBlocks; + } + + /** + Returns the pointer to the first element of a block of numElementsPerBlock() elements. + */ + T* getBlock(uint32_t id) + { + NVBLAST_ASSERT(id < m_numBlocks || 0 == m_numElementsPerBlock); + return &m_buffer[id*m_numElementsPerBlock]; + } + + /** + The number of elements available per block. + */ + uint32_t numElementsPerBlock() const + { + return m_numElementsPerBlock; + } + + /** + Frees the whole memory block. + */ + void release() + { + m_numBlocks = 0; + m_numElementsPerBlock = 0; + NVBLASTTK_FREE(m_buffer); + m_buffer = nullptr; + } + +private: + uint32_t m_numElementsPerBlock; //!< elements available in one block + uint32_t m_numBlocks; //!< number of virtual blocks available + T* m_buffer; //!< contiguous memory for all blocks +}; + + +/** +A preallocated, shared array from which can be allocated from in tasks. +Intended to be used when the maximum amount of data (e.g. for a family) +is known in advance. No further allocations take place on exhaustion. +Exhaustion asserts in debug builds and overflows otherwise. +*/ +template<typename T> +class SharedBuffer +{ +public: + SharedBuffer() : m_capacity(0), m_used(0), m_buffer(nullptr) {} + + /** + Atomically gets a pointer to the first element of an array of n elements. + */ + T* reserve(size_t n) + { + NVBLAST_ASSERT(m_used + n <= m_capacity); + size_t start = m_used.fetch_add(n); + return &m_buffer[start]; + } + + /** + Preallocates memory for capacity elements. + */ + void allocate(size_t capacity) + { + NVBLAST_ASSERT(m_buffer == nullptr); + m_buffer = reinterpret_cast<T*>(NVBLASTTK_ALLOC(capacity*sizeof(T), "SplitMemory")); + m_capacity = capacity; + } + + /** + Preserves the memory allocated but resets to reserve from the beginning of the array. + */ + void reset() + { + m_used = 0; + } + + /** + Frees the preallocated array. + */ + void release() + { + NVBLAST_ASSERT(m_buffer != nullptr); + NVBLASTTK_FREE(m_buffer); + m_buffer = nullptr; + m_capacity = m_used = 0; + } + +private: + size_t m_capacity; //!< available elements in the buffer + std::atomic<size_t> m_used; //!< used elements in the buffer + T* m_buffer; //!< the memory containing T's +}; + + +/** +Allocates from a preallocated, externally owned memory block initialized with. +When blocks run out of space, new ones are allocated and owned by this class. +*/ +template<typename T> +class LocalBuffer +{ +public: + /** + Returns the pointer to the first element of an array of n elements. + Allocates a new block of memory when exhausted, its size being the larger of n and capacity set with initialize(). + */ + T* allocate(size_t n) + { + if (m_used + n > m_capacity) + { + allocateNewBlock(n > m_capacity ? n : m_capacity); + } + + size_t index = m_used; + m_used += n; + return &m_currentBlock[index]; + } + + /** + Release the additionally allocated memory blocks. + The externally owned memory block remains untouched. + */ + void clear() + { + for (void* block : m_memoryBlocks) + { + NVBLASTTK_FREE(block); + } + m_memoryBlocks.clear(); + } + + /** + Set the externally owned memory block to start allocating from, + with a size of capacity elements. + */ + void initialize(T* block, size_t capacity) + { + m_currentBlock = block; + m_capacity = capacity; + m_used = 0; + } + +private: + /** + Allocates space for capacity elements. + */ + void allocateNewBlock(size_t capacity) + { + PERF_SCOPE_L("Local Buffer allocation"); + m_capacity = capacity; + m_currentBlock = static_cast<T*>(NVBLASTTK_ALLOC(capacity*sizeof(T), "Blast LocalBuffer")); + m_memoryBlocks.pushBack(m_currentBlock); + m_used = 0; + } + + TkInlineArray<void*, 4>::type m_memoryBlocks; //!< storage for memory blocks + T* m_currentBlock; //!< memory block used to allocate from + size_t m_used; //!< elements used in current block + size_t m_capacity; //!< elements available in current block +}; + + +/** +Holds the memory used by TkWorker for each family in each group. +*/ +class SharedMemory +{ +public: + SharedMemory() : m_eventsMemory(0), m_eventsCount(0), m_refCount(0) {} + + /** + Reserves n entries from preallocated memory. + */ + NvBlastActor** reserveNewActors(size_t n) + { + return m_newActorBuffers.reserve(n); + } + + /** + Reserves n entries from preallocated memory. + */ + TkActor** reserveNewTkActors(size_t n) + { + return m_newTkActorBuffers.reserve(n); + } + + /** + Allocates buffers to hold + */ + void allocate(TkFamilyImpl&); + + /** + Resets the internal buffers to reserve from their beginning. + Preserves the allocated memory. + */ + void reset() + { + m_newActorBuffers.reset(); + m_newTkActorBuffers.reset(); + } + + /** + Increments the reference count. + */ + void addReference() { m_refCount++; } + + /** + Increments the reference count by n. + */ + void addReference(size_t n) { m_refCount += n; } + + /** + Decrements the reference count. + Returns true if the count reached zero. + */ + bool removeReference() + { + m_refCount--; + return !isUsed(); + } + + /** + Checks if the reference count is not zero. + */ + bool isUsed() + { + return m_refCount > 0; + } + + /** + Release the internal buffers' memory. + */ + void release() + { + m_newActorBuffers.release(); + m_newTkActorBuffers.release(); + } + + TkEventQueue m_events; //!< event queue shared across a group's actors of the same family + uint32_t m_eventsMemory; //!< expected memory size for event data + uint32_t m_eventsCount; //!< expected number of events + +private: + size_t m_refCount; //!< helper for usage and releasing memory + + SharedBuffer<NvBlastActor*> m_newActorBuffers; //!< memory for splitting + SharedBuffer<TkActor*> m_newTkActorBuffers; //!< memory for split events +}; + + +/** +Shared job queue from which TkWorkers atomically fetch the next job. +*/ +template <typename T> +class TkAtomicQueue +{ +public: + /** + Initialize for a new batch of jobs. + */ + void init(TkWorkerJob* jobs, uint32_t numJobs) + { + m_jobs = jobs; + m_maxCount = numJobs; + m_current = 0; + } + + /** + Fetch a pointer to the next job. Returns nullptr when exhausted. + */ + T* next() + { + size_t index = m_current.fetch_add(1, std::memory_order_relaxed); + if (index < m_maxCount) + { + return &m_jobs[index]; + } + return nullptr; + } + +private: + T* m_jobs; //!< the list of jobs + size_t m_maxCount; //!< number of jobs available in the list + std::atomic<size_t> m_current; //!< current job counter +}; + +typedef TkAtomicQueue<TkWorkerJob> TkAtomicJobQueue; + + +/** +Thread worker fracturing and splitting actors sequentially. +The list of actual jobs is provided by the group owning this worker. +*/ +class TkWorker : public physx::PxLightCpuTask +{ +public: + void run(); + void release(); + const char* getName() const { return "TkWorker"; } + + uint32_t m_id; //!< this worker's id + TkGroupImpl* m_group; //!< the group owning this worker + + LocalBuffer<NvBlastChunkFractureData> m_chunkBuffer; //!< memory manager for chunk event data + LocalBuffer<NvBlastBondFractureData> m_bondBuffer; //!< memory manager for bonds event data + +#if NV_PROFILE + TkGroupStats m_stats; +#endif +}; +} +} + +#endif // NVBLASTTKTASKIMPL_H diff --git a/NvBlast/sdk/toolkit/source/NvBlastTkTypeImpl.h b/NvBlast/sdk/toolkit/source/NvBlastTkTypeImpl.h new file mode 100644 index 0000000..a29c32f --- /dev/null +++ b/NvBlast/sdk/toolkit/source/NvBlastTkTypeImpl.h @@ -0,0 +1,174 @@ +/* +* Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved. +* +* NVIDIA CORPORATION and its licensors retain all intellectual property +* and proprietary rights in and to this software, related documentation +* and any modifications thereto. Any use, reproduction, disclosure or +* distribution of this software and related documentation without an express +* license agreement from NVIDIA CORPORATION is strictly prohibited. +*/ + +#ifndef NVBLASTTKTYPEIMPL_H +#define NVBLASTTKTYPEIMPL_H + + +#include "NvPreprocessor.h" + +#include "NvBlastTkType.h" + + +// Forward declarations +namespace physx +{ +namespace general_PxIOStream2 +{ +class PxFileBuf; +} +} + + +namespace Nv +{ +namespace Blast +{ + +// Forward declarations +class TkSerializable; + + +// Serialization function signature +typedef TkSerializable* (*TkDeserializeFn)(physx::general_PxIOStream2::PxFileBuf&, const NvBlastID& id); + + +/** +Implementation of TkType, storing class information for TkIdentifiable-derived classes. +*/ +class TkTypeImpl : public TkType +{ +public: + TkTypeImpl(const char* typeName, uint32_t typeID, uint32_t version, TkDeserializeFn deserializeFn); + + // Begin TkType + virtual const char* getName() const override { return getNameInternal(); } + + virtual uint32_t getVersion() const override { return getVersionInternal(); } + // End TkType + + // Public methods + + /** + Access to the class name. + + \return a C string pointer to the class name. + */ + const char* getNameInternal() const; + + /** + Access to the data format version for the class (used if it TkSerializable-derived). + + \return the data format version. + */ + uint32_t getVersionInternal() const; + + /** + Access to a unique identifier for the class (set using the NVBLASTTK_IMPL_DEFINE_IDENTIFIABLE or NVBLASTTK_IMPL_DEFINE_SERIALIZABLE macro). + + \return the class's unique identifier. + */ + uint32_t getID() const; + + /** + \return the class's deserialization function. + */ + TkDeserializeFn getDeserializeFn() const; + + /** + Access to a runtime-unique small index for the class. + + \return the index for the class. + */ + uint32_t getIndex() const; + + /** + \return whether or not the index has been set (see setIndex) to a valid value. + */ + bool indexIsValid() const; + +private: + enum { InvalidIndex = 0xFFFFFFFF }; + + /** + Sets the type index. + + \param[in] index The index to set. + */ + void setIndex(uint32_t index); + + const char* m_name; //!< The name of the class, set by the constructor. + uint32_t m_ID; //!< The unique identifier for the class, set by the constructor. + uint32_t m_version; //!< The data format version for the class, set by the constructor. + TkDeserializeFn m_deserializeFn; //!< The class deserialization function, set by the constructor. + uint32_t m_index; //!< The index set for this class, set using setIndex(). + + friend class TkFrameworkImpl; +}; + + +//////// TkTypeImpl inline methods //////// + +NV_INLINE TkTypeImpl::TkTypeImpl(const char* typeName, uint32_t typeID, uint32_t version, TkDeserializeFn deserializeFn) + : m_name(typeName) + , m_ID(typeID) + , m_version(version) + , m_deserializeFn(deserializeFn) + , m_index((uint32_t)InvalidIndex) +{ +} + + +NV_INLINE const char* TkTypeImpl::getNameInternal() const +{ + return m_name; +} + + +NV_INLINE uint32_t TkTypeImpl::getVersionInternal() const +{ + return m_version; +} + + +NV_INLINE uint32_t TkTypeImpl::getID() const +{ + return m_ID; +} + + +NV_INLINE TkDeserializeFn TkTypeImpl::getDeserializeFn() const +{ + return m_deserializeFn; +} + + +NV_INLINE uint32_t TkTypeImpl::getIndex() const +{ + return m_index; +} + + +NV_INLINE bool TkTypeImpl::indexIsValid() const +{ + return m_index != (uint32_t)InvalidIndex; +} + + +NV_INLINE void TkTypeImpl::setIndex(uint32_t index) +{ + m_index = index; +} + +} // namespace Blast +} // namespace Nv + + +#endif // ifndef NVBLASTTKTYPEIMPL_H |