1//************************************ bs::framework - Copyright 2019 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
7namespace bs
8{
9 /** @addtogroup Implementation
10 * @{
11 */
12
13 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
14 ////////////// Various helper methods used for syncing data between the simulation and the core threads. ///////////////
15 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16
17 namespace detail
18 {
19 // Checks is the provided type a shared pointer
20 template<typename T> struct is_shared_ptr : std::false_type {};
21 template<typename T> struct is_shared_ptr<SPtr<T>> : std::true_type {};
22
23 // Checks is the provided type a resource handle
24 template<typename T> struct is_resource_handle : std::false_type {};
25 template<typename T> struct is_resource_handle<ResourceHandle<T>> : std::true_type {};
26
27 // Returns the underlying type if the provided type is a resource handle, or itself otherwise
28 template<typename T> struct decay_handle { using value = T; };
29 template<typename T> struct decay_handle<ResourceHandle<T>> { using value = T; };
30
31 // Returns the underlying type if the provided type is a shared pointer, or itself otherwise
32 template<typename T> struct decay_sptr { using value = T; };
33 template<typename T> struct decay_sptr<SPtr<T>> { using value = typename SPtr<T>::element_type; };
34
35 template<typename T>
36 using decay_all_t = typename decay_sptr<typename decay_handle<std::decay_t<T>>::value>::value;
37
38 // Converts a ResourceHandle to an underlying SPtr, or if the type is not a ResourceHandle it just passes it
39 // through as is.
40
41 /** Pass non-resource-handle types as is. */
42 template <class T>
43 T&& remove_handle(T&& value, std::enable_if_t<
44 !is_resource_handle<std::decay_t<T>>::value>* = 0)
45 {
46 return std::forward<T>(value);
47 }
48
49 /** Convert a resource handle to the underlying resource SPtr. */
50 template <class T>
51 decltype(((std::decay_t<T>*)nullptr)->getInternalPtr()) remove_handle(T&& handle, std::enable_if_t<
52 is_resource_handle<std::decay_t<T>>::value>* = 0)
53 {
54 if(handle.isLoaded(false))
55 return handle.getInternalPtr();
56
57 return nullptr;
58 }
59
60 // Converts a sim thread CoreObject into a core thread CoreObject. If the type is not a core-object, it is just
61 // passed through as is.
62
63 /** Pass non-shared-pointers as is, they aren't core objects. */
64 template <class T>
65 T&& get_core_object(T&& value, std::enable_if_t<
66 !is_shared_ptr<std::decay_t<T>>::value>* = 0)
67 {
68 return std::forward<T>(value);
69 }
70
71 /** Pass shared-pointers to non-classes as is, they aren't core objects. */
72 template <class T>
73 T&& get_core_object(T&& value, std::enable_if_t<
74 is_shared_ptr<std::decay_t<T>>::value &&
75 !std::is_class<std::decay_t<typename std::decay_t<T>::element_type>>::value>* = 0)
76 {
77 return std::forward<T>(value);
78 }
79
80 /** Pass shared-pointers to classes that don't derive from CoreObject as is, they aren't core objects. */
81 template <class T>
82 T&& get_core_object(T&& value, std::enable_if_t<
83 is_shared_ptr<std::decay_t<T>>::value &&
84 (std::is_class<std::decay_t<typename std::decay_t<T>::element_type>>::value &&
85 !std::is_base_of<CoreObject, std::decay_t<typename std::decay_t<T>::element_type>>::value)>* = 0)
86 {
87 return std::forward<T>(value);
88 }
89
90 /** Convert shared-pointers with classes that derive from CoreObject to their core thread variants. */
91 template <class T>
92 decltype(((std::decay_t<typename std::decay_t<T>::element_type>*)nullptr)->getCore())
93 get_core_object(T&& value, std::enable_if_t<
94 is_shared_ptr<std::decay_t<T>>::value &&
95 (std::is_class<std::decay_t<typename std::decay_t<T>::element_type>>::value &&
96 std::is_base_of<CoreObject, std::decay_t<typename std::decay_t<T>::element_type>>::value)>* = 0)
97 {
98 if(value)
99 return value->getCore();
100
101 return nullptr;
102 }
103 }
104
105 /** @} */
106
107 /** @addtogroup CoreThread
108 * @{
109 */
110
111 /**
112 * Writes the provided values into the underlying buffer using rttiWriteElem(). Each write advances the buffer to the
113 * next write location. Caller is responsible for not writing out of range.
114 *
115 * As input accepts any trivially copyable types, types with RTTIPlainType specializations, any shared pointer, as well
116 * as any resource handle or CoreObject shared ptr which will automatically get converted to their core thread variants.
117 *
118 * Types that provide a rttiEnumFields() method will have that method executed with this object provided as the
119 * parameter, * allowing the type to recurse over its fields.
120 */
121 struct RttiCoreSyncWriter
122 {
123 using MyType = RttiCoreSyncWriter;
124
125 RttiCoreSyncWriter(char** data)
126 :mWritePtr(data)
127 {}
128
129 /** If the type offers a rttiEnumFields method, recurse into it. */
130 template<class T>
131 void operator()(T&& value, std::enable_if_t<has_rttiEnumFields<T>::value>* = 0)
132 {
133 value.rttiEnumFields(*this);
134 }
135
136 /** If the type doesn't offer a rttiEnumFields method, perform the write using plain serialization. */
137 template<class T>
138 void operator()(T&& value, std::enable_if_t<!has_rttiEnumFields<T>::value>* = 0)
139 {
140 writeInternal(detail::get_core_object(detail::remove_handle(std::forward<T>(value))));
141 }
142
143 private:
144 template<class T>
145 void writeInternal(T&& value, std::enable_if_t<
146 !detail::is_shared_ptr<std::decay_t<T>>::value>* = 0)
147 {
148 (*mWritePtr) = rttiWriteElem(value, *mWritePtr);
149 }
150
151 template<class T>
152 void writeInternal(T&& value, std::enable_if_t<
153 detail::is_shared_ptr<std::decay_t<T>>::value>* = 0)
154 {
155 using SPtrType = std::decay_t<T>;
156
157 SPtrType* sptrPtr = new (*mWritePtr) SPtrType;
158 *sptrPtr = (value);
159
160 (*mWritePtr) += sizeof(SPtrType);
161 }
162
163 char** mWritePtr;
164 };
165
166 /**
167 * Reads values from the underlying buffer and writes them to the output object using rttiReadElem(). Each read advances
168 * the buffer to the next value. Caller is responsible for not reading out of range.
169 *
170 * As output accepts any trivially copyable types, types with RTTIPlainType specializations and any shared pointers.
171 *
172 * Types that provide a rttiEnumFields() method will have that method executed with this object provided as the
173 * parameter, allowing the type to recurse over its fields.
174 */
175 struct RttiCoreSyncReader
176 {
177 RttiCoreSyncReader(char** data)
178 :mReadPtr(data)
179 {}
180
181 /** If the type offers a rttiEnumFields method, recurse into it. */
182 template<class T>
183 void operator()(T&& value, std::enable_if_t<has_rttiEnumFields<T>::value>* = 0)
184 {
185 value.rttiEnumFields(*this);
186 }
187
188 /** If the type doesn't offer a rttiEnumFields method, perform the read using plain serialization. */
189 template<class T>
190 void operator()(T&& value, std::enable_if_t<!has_rttiEnumFields<T>::value>* = 0)
191 {
192 readInternal(std::forward<T>(value));
193 }
194
195 private:
196 template<class T>
197 void readInternal(T&& value, std::enable_if_t<
198 !detail::is_shared_ptr<std::decay_t<T>>::value>* = 0)
199 {
200 (*mReadPtr) = rttiReadElem(value, *mReadPtr);
201 }
202
203 template<class T>
204 void readInternal(T&& value, std::enable_if_t<
205 detail::is_shared_ptr<std::decay_t<T>>::value>* = 0)
206 {
207 using SPtrType = std::decay_t<T>;
208
209 SPtrType* sptr = (SPtrType*)(*mReadPtr);
210 value = *sptr;
211 sptr->~SPtrType();
212
213 (*mReadPtr) += sizeof(SPtrType);
214 }
215
216 char** mReadPtr;
217 };
218
219 /**
220 * Calculates size of provided values using rttiGetElemSize(). All sizes are accumulated in the location provided upon
221 * construction.
222 *
223 * As input accepts any trivially copyable types, types with RTTIPlainType specializations, any shared pointers,
224 * as well as any resource handle or CoreObject shared ptr which will automatically get converted to
225 * their core thread variants.
226 *
227 * Types that provide a rttiEnumFields() method will have that method executed with this object provided as the
228 * parameter, allowing the type to recurse over its fields.
229 */
230 struct RttiCoreSyncSize
231 {
232 RttiCoreSyncSize(UINT32& size)
233 :mSize(size)
234 { }
235
236 /** If the type offers a rttiEnumFields method, recurse into it. */
237 template<class T>
238 void operator()(T&& value, std::enable_if_t<has_rttiEnumFields<T>::value>* = 0)
239 {
240 value.rttiEnumFields(*this);
241 }
242
243 /** If the type doesn't offer a rttiEnumFields method, perform the read using plain serialization. */
244 template<class T>
245 void operator()(T&& value, std::enable_if_t<!has_rttiEnumFields<T>::value>* = 0)
246 {
247 getSizeInternal(detail::get_core_object(detail::remove_handle(std::forward<T>(value))));
248 }
249
250 private:
251 template<class T>
252 void getSizeInternal(T&& value, std::enable_if_t<
253 !detail::is_shared_ptr<std::decay_t<T>>::value>* = 0)
254 {
255 mSize += rttiGetElemSize(value);
256 }
257
258 template<class T>
259 void getSizeInternal(T&& value, std::enable_if_t<
260 detail::is_shared_ptr<std::decay_t<T>>::value>* = 0)
261 {
262 using SPtrType = std::decay_t<T>;
263 mSize += sizeof(SPtrType);
264 }
265
266 UINT32& mSize;
267 };
268
269 /**
270 * Calculates the size of the provided object, using rules for core object syncing. Object must have rttiEnumFields()
271 * method implementation.
272 */
273 template<class T>
274 UINT32 coreSyncGetElemSize(T& v)
275 {
276 UINT32 size = 0;
277 v.rttiEnumFields(RttiCoreSyncSize(size));
278 return size;
279 }
280
281 /**
282 * Writes the provided object into the provided memory location, using rules for core object syncing. Returns the
283 * memory location after the end of the written object.
284 */
285 template<class T>
286 char* coreSyncWriteElem(T& v, char* memory)
287 {
288 v.rttiEnumFields(RttiCoreSyncWriter(&memory));
289 return memory;
290 }
291
292 /**
293 * Decodes information from the provided memory buffer and writes it into the provided object, using rules for core
294 * object syncing. Returns the memory location after the end of the read object.
295 */
296 template<class T>
297 char* coreSyncReadElem(T& v, char* memory)
298 {
299 v.rttiEnumFields(RttiCoreSyncReader(&memory));
300 return memory;
301 }
302
303 /** @} */
304}
305
306