#include "BsCoreObject.h" #include "BsCoreObjectCore.h" #include "BsCoreThread.h" #include "BsCoreObjectManager.h" #include "BsCoreThreadAccessor.h" #include "BsFrameAlloc.h" #include "BsDebug.h" using namespace std::placeholders; namespace BansheeEngine { CoreObject::CoreObject(bool initializeOnRenderThread) : mFlags(0), mInternalID(0), mCoreDirtyFlags(0xFFFFFFFF) { mInternalID = CoreObjectManager::instance().registerObject(this); mFlags = initializeOnRenderThread ? mFlags | CGO_INIT_ON_CORE_THREAD : mFlags; } CoreObject::~CoreObject() { if(!isDestroyed()) { // Object must be released with destroy() otherwise engine can still try to use it, even if it was destructed // (e.g. if an object has one of its methods queued in a command queue, and is destructed, you will be accessing invalid memory) BS_EXCEPT(InternalErrorException, "Destructor called but object is not destroyed. This will result in nasty issues."); } #if BS_DEBUG_MODE if(!mThis.expired()) { BS_EXCEPT(InternalErrorException, "Shared pointer to this object still has active references but " \ "the object is being deleted? You shouldn't delete CoreObjects manually."); } #endif } void CoreObject::destroy() { CoreObjectManager::instance().unregisterObject(this); setIsDestroyed(true); if(requiresInitOnCoreThread()) { assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot destroy sim thead object from core thread."); // This will only destroy the CoreObjectCore if this was the last reference queueDestroyGpuCommand(mCoreSpecific); } mCoreSpecific = nullptr; } void CoreObject::initialize() { mCoreSpecific = createCore(); if (mCoreSpecific != nullptr) { if (requiresInitOnCoreThread()) { mCoreSpecific->setScheduledToBeInitialized(true); assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot initialize sim thread object from core thread."); queueInitializeGpuCommand(mCoreSpecific); } else { mCoreSpecific->initialize(); // Even though this object might not require initialization on the core thread, it will be used on it, therefore // do a memory barrier to ensure any stores are finished before continuing (When it requires init on core thread // we use the core accessor which uses a mutex, and therefore executes all stores as well, so we dont need to // do this explicitly) std::atomic_thread_fence(std::memory_order_release); } } markDependenciesDirty(); } void CoreObject::blockUntilCoreInitialized() { if (mCoreSpecific != nullptr) mCoreSpecific->synchronize(); } void CoreObject::syncToCore(CoreAccessor& accessor) { CoreObjectManager::instance().syncToCore(this, accessor); } void CoreObject::markCoreDirty(UINT32 flags) { bool wasDirty = isCoreDirty(); mCoreDirtyFlags |= flags; if (!wasDirty && isCoreDirty()) CoreObjectManager::instance().notifyCoreDirty(this); } void CoreObject::markDependenciesDirty() { CoreObjectManager::instance().notifyDependenciesDirty(this); } void CoreObject::_setThisPtr(std::shared_ptr ptrThis) { mThis = ptrThis; } void CoreObject::queueGpuCommand(const SPtr& obj, std::function func) { // We call another internal method and go through an additional layer of abstraction in order to keep an active // reference to the obj (saved in the bound function). // We could have called the function directly using "this" pointer but then we couldn't have used a shared_ptr for the object, // in which case there is a possibility that the object would be released and deleted while still being in the command queue. gCoreAccessor().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func)); } AsyncOp CoreObject::queueReturnGpuCommand(const SPtr& obj, std::function func) { // See queueGpuCommand return gCoreAccessor().queueReturnCommand(std::bind(&CoreObject::executeReturnGpuCommand, obj, func, _1)); } void CoreObject::queueInitializeGpuCommand(const SPtr& obj) { std::function func = std::bind(&CoreObjectCore::initialize, obj.get()); CoreThread::instance().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func)); } void CoreObject::queueDestroyGpuCommand(const SPtr& obj) { std::function func = [&](){}; // Do nothing function. We just need the shared pointer to stay alive until it reaches the core thread gCoreAccessor().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func)); } void CoreObject::executeGpuCommand(const SPtr& obj, std::function func) { volatile std::shared_ptr objParam = obj; // Makes sure obj isn't optimized out? func(); } void CoreObject::executeReturnGpuCommand(const SPtr& obj, std::function func, AsyncOp& op) { volatile std::shared_ptr objParam = obj; // Makes sure obj isn't optimized out? func(op); } }