1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "CoreThread/BsCoreObject.h"
4#include "CoreThread/BsCoreObjectCore.h"
5#include "CoreThread/BsCoreThread.h"
6#include "CoreThread/BsCoreObjectManager.h"
7
8using namespace std::placeholders;
9
10namespace bs
11{
12 CoreObject::CoreObject(bool initializeOnCoreThread)
13 : mFlags(initializeOnCoreThread ? CGO_INIT_ON_CORE_THREAD : 0)
14 , mCoreDirtyFlags(0)
15 , mInternalID(CoreObjectManager::instance().generateId())
16 {
17 }
18
19 CoreObject::~CoreObject()
20 {
21 if(!isDestroyed())
22 {
23 // Object must be released with destroy() otherwise engine can still try to use it, even if it was destructed
24 // (e.g. if an object has one of its methods queued in a command queue, and is destructed, you will be accessing invalid memory)
25 BS_EXCEPT(InternalErrorException, "Destructor called but object is not destroyed. This will result in nasty issues.");
26 }
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 CoreObjects manually.");
33 }
34#endif
35 }
36
37 void CoreObject::destroy()
38 {
39 CoreObjectManager::instance().unregisterObject(this);
40 setIsDestroyed(true);
41
42 if(requiresInitOnCoreThread())
43 {
44 assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot destroy sim thead object from core thread.");
45
46 // This will only destroy the ct::CoreObject if this was the last reference
47 queueDestroyGpuCommand(mCoreSpecific);
48 }
49
50 mCoreSpecific = nullptr;
51 }
52
53 void CoreObject::initialize()
54 {
55 CoreObjectManager::instance().registerObject(this);
56 mCoreSpecific = createCore();
57
58 if (mCoreSpecific != nullptr)
59 {
60 if (requiresInitOnCoreThread())
61 {
62 mCoreSpecific->setScheduledToBeInitialized(true);
63
64 assert(BS_THREAD_CURRENT_ID != CoreThread::instance().getCoreThreadId() && "Cannot initialize sim thread object from core thread.");
65
66 queueInitializeGpuCommand(mCoreSpecific);
67 }
68 else
69 {
70 mCoreSpecific->initialize();
71
72 // Even though this object might not require initialization on the core thread, it will be used on it, therefore
73 // do a memory barrier to ensure any stores are finished before continuing (When it requires init on core thread
74 // we use the core queue which uses a mutex, and therefore executes all stores as well, so we dont need to
75 // do this explicitly)
76 std::atomic_thread_fence(std::memory_order_release); // TODO - Need atomic variable, currently this does nothing
77 }
78 }
79
80 mFlags |= CGO_INITIALIZED;
81 markDependenciesDirty();
82 }
83
84 void CoreObject::blockUntilCoreInitialized() const
85 {
86 if (mCoreSpecific != nullptr)
87 mCoreSpecific->synchronize();
88 }
89
90 void CoreObject::syncToCore()
91 {
92 CoreObjectManager::instance().syncToCore(this);
93 }
94
95 void CoreObject::markCoreDirty(UINT32 flags)
96 {
97 bool wasDirty = isCoreDirty();
98
99 mCoreDirtyFlags |= flags;
100
101 if (!wasDirty && isCoreDirty())
102 CoreObjectManager::instance().notifyCoreDirty(this);
103 }
104
105 void CoreObject::markDependenciesDirty()
106 {
107 CoreObjectManager::instance().notifyDependenciesDirty(this);
108 }
109
110 void CoreObject::_setThisPtr(SPtr<CoreObject> ptrThis)
111 {
112 mThis = ptrThis;
113 }
114
115 void CoreObject::queueGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void()> func)
116 {
117 // We call another internal method and go through an additional layer of abstraction in order to keep an active
118 // reference to the obj (saved in the bound function).
119 // We could have called the function directly using "this" pointer but then we couldn't have used a shared_ptr for the object,
120 // in which case there is a possibility that the object would be released and deleted while still being in the command queue.
121 gCoreThread().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
122 }
123
124 AsyncOp CoreObject::queueReturnGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void(AsyncOp&)> func)
125 {
126 // See queueGpuCommand
127 return gCoreThread().queueReturnCommand(std::bind(&CoreObject::executeReturnGpuCommand, obj, func, _1));
128 }
129
130 void CoreObject::queueInitializeGpuCommand(const SPtr<ct::CoreObject>& obj)
131 {
132 std::function<void()> func = std::bind(&ct::CoreObject::initialize, obj.get());
133
134 CoreThread::instance().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
135 }
136
137 void CoreObject::queueDestroyGpuCommand(const SPtr<ct::CoreObject>& obj)
138 {
139 std::function<void()> func = [&](){}; // Do nothing function. We just need the shared pointer to stay alive until it reaches the core thread
140
141 gCoreThread().queueCommand(std::bind(&CoreObject::executeGpuCommand, obj, func));
142 }
143
144 void CoreObject::executeGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void()> func)
145 {
146 volatile SPtr<ct::CoreObject> objParam = obj; // Makes sure obj isn't optimized out?
147
148 func();
149 }
150
151 void CoreObject::executeReturnGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void(AsyncOp&)> func,
152 AsyncOp& op)
153 {
154 volatile SPtr<ct::CoreObject> objParam = obj; // Makes sure obj isn't optimized out?
155
156 func(op);
157 }
158}