BsCoreObject.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. //__________________________ Banshee Project - A modern game development toolkit _________________________________//
  2. //_____________________________________ www.banshee-project.com __________________________________________________//
  3. //________________________ Copyright (c) 2014 Marko Pintera. All rights reserved. ________________________________//
  4. #include "BsCoreObject.h"
  5. #include "BsCoreThread.h"
  6. #include "BsCoreObjectManager.h"
  7. #include "BsCoreThreadAccessor.h"
  8. #include "BsDebug.h"
  9. using namespace std::placeholders;
  10. namespace BansheeEngine
  11. {
  12. BS_STATIC_THREAD_SYNCHRONISER_CLASS_INSTANCE(mCoreGpuObjectLoadedCondition, CoreObject)
  13. BS_STATIC_MUTEX_CLASS_INSTANCE(mCoreGpuObjectLoadedMutex, CoreObject)
  14. CoreObject::CoreObject(bool initializeOnRenderThread)
  15. : mFlags(0), mInternalID(0)
  16. {
  17. mInternalID = CoreObjectManager::instance().registerObject(this);
  18. mFlags = initializeOnRenderThread ? mFlags | CGO_INIT_ON_CORE_THREAD : mFlags;
  19. }
  20. CoreObject::~CoreObject()
  21. {
  22. if(isInitialized())
  23. {
  24. // Object must be released with destroy() otherwise engine can still try to use it, even if it was destructed
  25. // (e.g. if an object has one of its methods queued in a command queue, and is destructed, you will be accessing invalid memory)
  26. BS_EXCEPT(InternalErrorException, "Destructor called but object is not destroyed. This will result in nasty issues.");
  27. }
  28. #if BS_DEBUG_MODE
  29. if(!mThis.expired())
  30. {
  31. BS_EXCEPT(InternalErrorException, "Shared pointer to this object still has active references but " \
  32. "the object is being deleted? You shouldn't delete CoreGpuObjects manually.");
  33. }
  34. #endif
  35. CoreObjectManager::instance().unregisterObject(this);
  36. }
  37. void CoreObject::destroy()
  38. {
  39. if(requiresInitOnCoreThread())
  40. {
  41. setScheduledToBeDeleted(true);
  42. if(BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
  43. mThis.lock()->destroy_internal();
  44. else
  45. queueDestroyGpuCommand(mThis.lock());
  46. }
  47. else
  48. {
  49. destroy_internal();
  50. }
  51. }
  52. void CoreObject::destroy_internal()
  53. {
  54. #if BS_DEBUG_MODE
  55. if(!isInitialized())
  56. BS_EXCEPT(InternalErrorException, "Trying to destroy an object that is already destroyed (or it never was initialized).");
  57. #endif
  58. setIsInitialized(false);
  59. }
  60. void CoreObject::initialize()
  61. {
  62. #if BS_DEBUG_MODE
  63. if(isInitialized() || isScheduledToBeInitialized())
  64. BS_EXCEPT(InternalErrorException, "Trying to initialize an object that is already initialized.");
  65. #endif
  66. if(requiresInitOnCoreThread())
  67. {
  68. setScheduledToBeInitialized(true);
  69. if(BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
  70. mThis.lock()->initialize_internal();
  71. else
  72. queueInitializeGpuCommand(mThis.lock());
  73. }
  74. else
  75. {
  76. initialize_internal();
  77. }
  78. }
  79. void CoreObject::initialize_internal()
  80. {
  81. if(requiresInitOnCoreThread())
  82. {
  83. {
  84. BS_LOCK_MUTEX(mCoreGpuObjectLoadedMutex);
  85. setIsInitialized(true);
  86. }
  87. setScheduledToBeInitialized(false);
  88. BS_THREAD_NOTIFY_ALL(mCoreGpuObjectLoadedCondition);
  89. }
  90. else
  91. {
  92. setIsInitialized(true);
  93. }
  94. }
  95. void CoreObject::synchronize()
  96. {
  97. if(!isInitialized())
  98. {
  99. if(requiresInitOnCoreThread())
  100. {
  101. #if BS_DEBUG_MODE
  102. if(BS_THREAD_CURRENT_ID == CoreThread::instance().getCoreThreadId())
  103. BS_EXCEPT(InternalErrorException, "You cannot call this method on the core thread. It will cause a deadlock!");
  104. #endif
  105. BS_LOCK_MUTEX_NAMED(mCoreGpuObjectLoadedMutex, lock);
  106. while(!isInitialized())
  107. {
  108. if(!isScheduledToBeInitialized())
  109. BS_EXCEPT(InternalErrorException, "Attempting to wait until initialization finishes but object is not scheduled to be initialized.");
  110. BS_THREAD_WAIT(mCoreGpuObjectLoadedCondition, mCoreGpuObjectLoadedMutex, lock);
  111. }
  112. }
  113. else
  114. {
  115. BS_EXCEPT(InternalErrorException, "Attempting to wait until initialization finishes but object is not scheduled to be initialized.");
  116. }
  117. }
  118. }
  119. void CoreObject::_setThisPtr(std::shared_ptr<CoreObject> ptrThis)
  120. {
  121. mThis = ptrThis;
  122. }
  123. void CoreObject::_deleteDelayedInternal(CoreObject* obj)
  124. {
  125. assert(obj != nullptr);
  126. // This method usually gets called automatically by the shared pointer when all references are released. The process:
  127. // - If the object wasn't initialized delete it right away
  128. // - Otherwise:
  129. // - We re-create the reference to the object by setting mThis pointer
  130. // - We queue the object to be destroyed so all of its GPU resources may be released on the core thread
  131. // - destroy() makes sure it keeps a reference of mThis so object isn't deleted
  132. // - Once the destroy() finishes the reference is removed and the default shared_ptr deleter is called
  133. #if BS_DEBUG_MODE
  134. if(obj->isScheduledToBeInitialized())
  135. {
  136. BS_EXCEPT(InternalErrorException, "Object scheduled to be initialized, yet it's being deleted. " \
  137. "By design objects queued in the command queue should always have a reference count >= 1, therefore never be deleted " \
  138. "while still in the queue.");
  139. }
  140. #endif
  141. }
  142. void CoreObject::queueGpuCommand(std::shared_ptr<CoreObject>& obj, std::function<void()> func)
  143. {
  144. // We call another internal method and go through an additional layer of abstraction in order to keep an active
  145. // reference to the obj (saved in the bound function).
  146. // We could have called the function directly using "this" pointer but then we couldn't have used a shared_ptr for the object,
  147. // in which case there is a possibility that the object would be released and deleted while still being in the command queue.
  148. gCoreAccessor().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
  149. }
  150. AsyncOp CoreObject::queueReturnGpuCommand(std::shared_ptr<CoreObject>& obj, std::function<void(AsyncOp&)> func)
  151. {
  152. // See queueGpuCommand
  153. return gCoreAccessor().queueReturnCommand(std::bind(&CoreObject::executeReturnGpuCommand, obj, func, _1));
  154. }
  155. void CoreObject::queueInitializeGpuCommand(std::shared_ptr<CoreObject>& obj)
  156. {
  157. std::function<void()> func = std::bind(&CoreObject::initialize_internal, obj.get());
  158. CoreThread::instance().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
  159. }
  160. void CoreObject::queueDestroyGpuCommand(std::shared_ptr<CoreObject>& obj)
  161. {
  162. std::function<void()> func = std::bind(&CoreObject::destroy_internal, obj.get());
  163. gCoreAccessor().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
  164. }
  165. void CoreObject::executeGpuCommand(std::shared_ptr<CoreObject> obj, std::function<void()> func)
  166. {
  167. volatile std::shared_ptr<CoreObject> objParam = obj; // Makes sure obj isn't optimized out?
  168. func();
  169. }
  170. void CoreObject::executeReturnGpuCommand(std::shared_ptr<CoreObject> obj, std::function<void(AsyncOp&)> func, AsyncOp& op)
  171. {
  172. volatile std::shared_ptr<CoreObject> objParam = obj; // Makes sure obj isn't optimized out?
  173. func(op);
  174. }
  175. }