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#pragma once
4
5#include "BsCorePrerequisites.h"
6#include "CoreThread/BsCoreObjectCore.h"
7#include "Threading/BsAsyncOp.h"
8
9namespace bs
10{
11 /** @addtogroup CoreThread
12 * @{
13 */
14
15 /**
16 * Core objects provides functionality for dealing with objects that need to exist on both simulation and core thread.
17 * It handles cross-thread initialization, destruction as well as syncing data between the two threads.
18 *
19 * It also provides a standardized way to initialize/destroy objects, and a way to specify dependant CoreObject%s. For
20 * those purposes it might also be used for objects that only exist on the core thread.
21 *
22 * @note ct::CoreObject is a counterpart to CoreObject that is used exclusively on the core thread. CoreObject on the
23 * other hand should be used exclusively on the simulation thread. Types that exist on both threads need to
24 * implement both of these.
25 */
26 class BS_CORE_EXPORT CoreObject
27 {
28 protected:
29 /** Values that represent current state of the core object */
30 enum Flags
31 {
32 CGO_DESTROYED = 0x01, /**< Object has been destroyed and shouldn't be used. */
33 CGO_INIT_ON_CORE_THREAD = 0x02, /**< Object requires initialization on core thread. */
34 CGO_INITIALIZED = 0x04 /**< Object's initialize() method has been called. */
35 };
36
37 public:
38 /**
39 * Frees all the data held by this object.
40 *
41 * @note
42 * If this object require initialization on core thread destruction is not done immediately, and is
43 * instead just scheduled on the core thread. Otherwise the object is destroyed immediately.
44 */
45 virtual void destroy();
46
47 /**
48 * Initializes all the internal resources of this object. Must be called right after construction. Generally you
49 * should call this from a factory method to avoid the issue where user forgets to call it.
50 *
51 * @note
52 * If this object require initialization on core thread initialization is not done immediately, and is instead just
53 * scheduled on the core thread. Otherwise the object is initialized immediately.
54 */
55 virtual void initialize();
56
57 /** Returns true if the object has been initialized. Non-initialized object should not be used. */
58 bool isInitialized() const { return (mFlags & CGO_INITIALIZED) != 0; }
59
60 /** Returns true if the object has been destroyed. Destroyed object should not be used. */
61 bool isDestroyed() const { return (mFlags & CGO_DESTROYED) != 0; }
62
63 /**
64 * Blocks the current thread until the resource is fully initialized.
65 *
66 * @note
67 * If you call this without calling initialize first a deadlock will occur. You should not call this from core thread.
68 */
69 void blockUntilCoreInitialized() const;
70
71 /** Returns an unique identifier for this object. */
72 UINT64 getInternalID() const { return mInternalID; }
73
74 /** Returns a shared_ptr version of "this" pointer. */
75 SPtr<CoreObject> getThisPtr() const { return mThis.lock(); }
76
77 /**
78 * Returns an object that contains a core thread specific implementation of this CoreObject. Null is a valid return
79 * value in case object requires no core thread implementation.
80 *
81 * @note Thread safe to retrieve, but its data is only valid on the core thread.
82 */
83 SPtr<ct::CoreObject> getCore() const { return mCoreSpecific; }
84
85 /**
86 * Ensures all dirty syncable data is send to the core thread counterpart of this object (if any).
87 *
88 * @note Call this if you have modified the object and need to make sure core thread has an up to date version.
89 * Normally this is done automatically at the end of a frame.
90 * @note This is an @ref asyncMethod "asynchronous method".
91 */
92 void syncToCore();
93
94 public: // ***** INTERNAL ******
95 /** @name Internal
96 * @{
97 */
98
99 /**
100 * Sets a shared this pointer to this object. This must be called immediately after construction, but before
101 * initialize().
102 *
103 * @note This should be called by the factory creation methods so user doesn't have to call it manually.
104 */
105 void _setThisPtr(SPtr<CoreObject> ptrThis);
106
107 /** Schedules the object to be destroyed, and then deleted. */
108 template<class T, class MemAlloc>
109 static void _delete(CoreObject* obj)
110 {
111 if (!obj->isDestroyed())
112 obj->destroy();
113
114 bs_delete<T, MemAlloc>((T*)obj);
115 }
116
117 /** @} */
118 protected:
119 /**
120 * Constructs a new core object.
121 *
122 * @param[in] requiresCoreInit (optional) Determines if the ct::CoreObject counterpart of this object
123 * (if it has any, see createCore()) requires initialization and destruction on the
124 * core thread.
125 */
126 CoreObject(bool requiresCoreInit = true);
127 virtual ~CoreObject();
128
129 /**
130 * Queues a command to be executed on the core thread, without a return value.
131 *
132 * @note
133 * Requires a shared pointer to the object this function will be executed on, in order to make sure the object is
134 * not deleted before the command executes. Can be null if the function is static or global.
135 */
136 static void queueGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void()> func);
137
138 /**
139 * Queues a command to be executed on the core thread, with a return value in the form of AsyncOp.
140 *
141 * @see AsyncOp
142 *
143 * @note
144 * Requires a shared pointer to the object this function will be executed on, in order to make sure the object is
145 * not deleted before the command executes. Can be null if the function is static or global.
146 */
147 static AsyncOp queueReturnGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void(AsyncOp&)> func);
148
149 bool requiresInitOnCoreThread() const { return (mFlags & CGO_INIT_ON_CORE_THREAD) != 0; }
150 void setIsDestroyed(bool destroyed) { mFlags = destroyed ? mFlags | CGO_DESTROYED : mFlags & ~CGO_DESTROYED; }
151 private:
152 friend class CoreObjectManager;
153
154 volatile UINT8 mFlags;
155 UINT32 mCoreDirtyFlags;
156 UINT64 mInternalID; // ID == 0 is not a valid ID
157 std::weak_ptr<CoreObject> mThis;
158
159 /**
160 * Queues object initialization command on the core thread. The command is added to the primary core thread queue
161 * and will be executed as soon as the core thread is ready.
162 */
163 static void queueInitializeGpuCommand(const SPtr<ct::CoreObject>& obj);
164
165 /**
166 * Queues object destruction command on the core thread. The command is added to the core thread queue of this
167 * thread and will be executed after qzeze commands are submitted and any previously queued commands are executed.
168 *
169 * @note It is up to the caller to ensure no other threads attempt to use this object.
170 */
171 static void queueDestroyGpuCommand(const SPtr<ct::CoreObject>& obj);
172
173 /** Helper wrapper method used for queuing commands with no return value on the core thread. */
174 static void executeGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void()> func);
175
176 /** Helper wrapper method used for queuing commands with a return value on the core thread. */
177 static void executeReturnGpuCommand(const SPtr<ct::CoreObject>& obj, std::function<void(AsyncOp&)> func,
178 AsyncOp& op);
179
180 protected:
181 /************************************************************************/
182 /* CORE OBJECT SYNC */
183 /************************************************************************/
184
185 /**
186 * Creates an object that contains core thread specific data and methods for this CoreObject. Can be null if such
187 * object is not required.
188 */
189 virtual SPtr<ct::CoreObject> createCore() const { return nullptr; }
190
191 /**
192 * Marks the core data as dirty. This causes the syncToCore() method to trigger the next time objects are synced
193 * between core and sim threads.
194 *
195 * @param[in] flags (optional) Flags in case you want to signal that only part of the internal data is dirty.
196 * syncToCore() will be called regardless and it's up to the implementation to read
197 * the flags value if needed.
198 */
199 void markCoreDirty(UINT32 flags = 0xFFFFFFFF);
200
201 /** Marks the core data as clean. Normally called right after syncToCore() has been called. */
202 void markCoreClean() { mCoreDirtyFlags = 0; }
203
204 /**
205 * Notifies the core object manager that this object is dependant on some other CoreObject(s), and the dependencies
206 * changed since the last call to this method. This will trigger a call to getCoreDependencies() to collect the
207 * new dependencies.
208 */
209 void markDependenciesDirty();
210
211 /**
212 * Checks is the core dirty flag set. This is used by external systems to know when internal data has changed and
213 * core thread potentially needs to be notified.
214 */
215 bool isCoreDirty() const { return mCoreDirtyFlags != 0; }
216
217 /**
218 * Returns the exact value of the internal flag that signals whether an object needs to be synced with the core thread.
219 */
220 UINT32 getCoreDirtyFlags() const { return mCoreDirtyFlags; }
221
222 /**
223 * Copy internal dirty data to a memory buffer that will be used for updating core thread version of that data.
224 *
225 * @note
226 * This generally happens at the end of every sim thread frame. Synced data becomes available to the core thread
227 * the start of the next core thread frame.
228 */
229 virtual CoreSyncData syncToCore(FrameAlloc* allocator) { return CoreSyncData(); }
230
231 /**
232 * Populates the provided array with all core objects that this core object depends upon. Dependencies are required
233 * for syncing to the core thread, so the system can be aware to update the dependant objects if a dependency is
234 * marked as dirty (for example updating a camera's viewport should also trigger an update on camera so it has
235 * a chance to potentially update its data).
236 */
237 virtual void getCoreDependencies(Vector<CoreObject*>& dependencies) { }
238
239 /**
240 * Gets called on an object when one of the dependencies (as returned from getCoreDependencies()) is marked as
241 * dirty. It gives the dependant object a chance to determine should it mark itself as dirty due to the dependency
242 * change. Dirty flags of the dependency object can be examined for more information on what part of the dependency
243 * was modified.
244 */
245 virtual void onDependencyDirty(CoreObject* dependency, UINT32 dirtyFlags)
246 {
247 // By default any changes on a dependency mark the parent dirty as well
248 mCoreDirtyFlags |= DIRTY_DEPENDENCY_MASK;
249 }
250
251 protected:
252 SPtr<ct::CoreObject> mCoreSpecific;
253 };
254
255 /**
256 * Creates a new core object using the specified allocators and returns a shared pointer to it.
257 *
258 * @note
259 * All core thread object shared pointers must be created using this method or its overloads and you should not create
260 * them manually.
261 */
262 template<class Type, class MainAlloc, class PtrDataAlloc, class... Args>
263 SPtr<Type> bs_core_ptr_new(Args &&...args)
264 {
265 return SPtr<Type>(bs_new<Type, MainAlloc>(std::forward<Args>(args)...),
266 &CoreObject::_delete<Type, MainAlloc>, StdAlloc<Type, PtrDataAlloc>());
267 }
268
269 /**
270 * Creates a new core object using the specified allocator and returns a shared pointer to it.
271 *
272 * @note
273 * All core thread object shared pointers must be created using this method or its overloads and you should not create
274 * them manually.
275 */
276 template<class Type, class MainAlloc, class... Args>
277 SPtr<Type> bs_core_ptr_new(Args &&...args)
278 {
279 return SPtr<Type>(bs_new<Type, MainAlloc>(std::forward<Args>(args)...),
280 &CoreObject::_delete<Type, MainAlloc>, StdAlloc<Type, GenAlloc>());
281 }
282
283 /**
284 * Creates a new core object and returns a shared pointer to it.
285 *
286 * @note
287 * All core thread object shared pointers must be created using this method or its overloads and you should not create
288 * them manually.
289 */
290 template<class Type, class... Args>
291 SPtr<Type> bs_core_ptr_new(Args &&...args)
292 {
293 return SPtr<Type>(bs_new<Type, GenAlloc>(std::forward<Args>(args)...),
294 &CoreObject::_delete<Type, GenAlloc>, StdAlloc<Type, GenAlloc>());
295 }
296
297 /**
298 * Creates a core object shared pointer using a previously constructed object.
299 *
300 * @note
301 * All core thread object shared pointers must be created using this method or its overloads and you should not create
302 * them manually.
303 */
304 template<class Type, class MainAlloc = GenAlloc, class PtrDataAlloc = GenAlloc>
305 SPtr<Type> bs_core_ptr(Type* data)
306 {
307 return SPtr<Type>(data, &CoreObject::_delete<Type, MainAlloc>, StdAlloc<Type, PtrDataAlloc>());
308 }
309
310 /** @} */
311}
312
313