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/BsCoreObject.h"
7#include "RenderAPI/BsIndexBuffer.h"
8
9namespace bs
10{
11 /** @addtogroup Resources
12 * @{
13 */
14
15 /**
16 * Mesh heap allows you to quickly allocate and deallocate a large amounts of temporary meshes without the large
17 * overhead of normal Mesh creation. Only requirement is that meshes share the same vertex description and index type.
18 *
19 * @note
20 * This class should be considered as a replacement for a normal Mesh if you are constantly updating the mesh (for
21 * example every frame) and you are not able to discard entire mesh contents on each update. Not using discard flag on
22 * normal meshes may introduce GPU-CPU sync points which may severely limit performance. Primary purpose of this class
23 * is to avoid those sync points by not forcing you to discard contents.
24 * Downside is that this class may allocate 2-3x (or more) memory than it is actually needed for your data.
25 * @note
26 * Sim thread only
27 */
28 class BS_CORE_EXPORT MeshHeap : public CoreObject
29 {
30 public:
31 /**
32 * Allocates a new mesh in the heap, expanding the heap if needed. Mesh will be initialized with the provided
33 * @p meshData. You may use the returned transient mesh for drawing.
34 *
35 * @note
36 * Offsets provided by MeshData are ignored. MeshHeap will determine where the data will be written internally.
37 */
38 SPtr<TransientMesh> alloc(const SPtr<MeshData>& meshData, DrawOperationType drawOp = DOT_TRIANGLE_LIST);
39
40 /**
41 * Deallocates the provided mesh and makes that room on the heap re-usable as soon as the GPU is also done with the
42 * mesh.
43 */
44 void dealloc(const SPtr<TransientMesh>& mesh);
45
46 /** Retrieves a core implementation of a mesh heap usable only from the core thread. */
47 SPtr<ct::MeshHeap> getCore() const;
48
49 /**
50 * Creates a new mesh heap.
51 *
52 * @param[in] numVertices Initial number of vertices the heap may store. This will grow automatically if needed.
53 * @param[in] numIndices Initial number of indices the heap may store. This will grow automatically if needed.
54 * @param[in] vertexDesc Description of the stored vertices.
55 * @param[in] indexType Type of the stored indices.
56 */
57 static SPtr<MeshHeap> create(UINT32 numVertices, UINT32 numIndices,
58 const SPtr<VertexDataDesc>& vertexDesc, IndexType indexType = IT_32BIT);
59
60 private:
61 /** @copydoc create */
62 MeshHeap(UINT32 numVertices, UINT32 numIndices,
63 const SPtr<VertexDataDesc>& vertexDesc, IndexType indexType = IT_32BIT);
64
65 /** @copydoc CoreObject::createCore */
66 SPtr<ct::CoreObject> createCore() const override;
67
68 private:
69 UINT32 mNumVertices;
70 UINT32 mNumIndices;
71
72 SPtr<VertexDataDesc> mVertexDesc;
73 IndexType mIndexType;
74
75 Map<UINT32, SPtr<TransientMesh>> mMeshes;
76 UINT32 mNextFreeId;
77 };
78
79 /** @} */
80
81 namespace ct
82 {
83 /** @addtogroup Resources-Internal
84 * @{
85 */
86
87 /**
88 * Core thread version of bs::MeshHeap.
89 *
90 * @note Core thread only.
91 */
92 class BS_CORE_EXPORT MeshHeap : public CoreObject
93 {
94 /** Signifies how is a data chunk used. */
95 enum class UseFlags
96 {
97 Used, /**< Data chunk is used by both CPU and GPU. */
98 CPUFree, /**< Data chunk was released by CPU but not GPU. */
99 GPUFree, /**< Data chunk was released by GPU but not CPU. */
100 Free /**< Data chunk was released by both CPU and GPU. */
101 };
102
103 /** Represents a continuous chunk of memory. */
104 struct ChunkData
105 {
106 UINT32 start, size;
107 };
108
109 /** Represents an allocated piece of data representing a mesh. */
110 struct AllocatedData
111 {
112 UINT32 vertChunkIdx;
113 UINT32 idxChunkIdx;
114
115 UseFlags useFlags;
116 UINT32 eventQueryIdx;
117 SPtr<TransientMesh> mesh;
118 };
119
120 /** Data about a GPU query. */
121 struct QueryData
122 {
123 SPtr<EventQuery> query;
124 UINT32 queryId;
125 };
126
127 public:
128 ~MeshHeap();
129
130 private:
131 friend class bs::MeshHeap;
132 friend class bs::TransientMesh;
133 friend class TransientMesh;
134
135 MeshHeap(UINT32 numVertices, UINT32 numIndices,
136 const SPtr<VertexDataDesc>& vertexDesc, IndexType indexType, GpuDeviceFlags deviceMask);
137
138 /** @copydoc CoreObject::initialize() */
139 void initialize() override;
140
141 /**
142 * Allocates a new mesh in the heap, expanding the heap if needed.
143 *
144 * @param[in] mesh Mesh for which we are allocating the data.
145 * @param[in] meshData Data to initialize the new mesh with.
146 */
147 void alloc(SPtr<TransientMesh> mesh, const SPtr<MeshData>& meshData);
148
149 /** Deallocates the provided mesh. Freed memory will be re-used as soon as the GPU is done with the mesh. */
150 void dealloc(SPtr<TransientMesh> mesh);
151
152 /** Resizes the vertex buffers so they max contain the provided number of vertices. */
153 void growVertexBuffer(UINT32 numVertices);
154
155 /** Resizes the index buffer so they max contain the provided number of indices. */
156 void growIndexBuffer(UINT32 numIndices);
157
158 /**
159 * Creates a new event query or returns an existing one from the pool if available. Returned value is an index
160 * into event query array.
161 */
162 UINT32 createEventQuery();
163
164 /** Frees the event query with the specified index and returns it to the pool so it may be reused later. */
165 void freeEventQuery(UINT32 idx);
166
167 /** Gets internal vertex data for all the meshes. */
168 SPtr<VertexData> getVertexData() const;
169
170 /** Gets internal index data for all the meshes. */
171 SPtr<IndexBuffer> getIndexBuffer() const;
172
173 /** Returns a structure that describes how are the vertices stored in the mesh's vertex buffer. */
174 SPtr<VertexDataDesc> getVertexDesc() const;
175
176 /**
177 * Returns the offset in vertices from the start of the buffer to the first vertex of the mesh with the provided ID.
178 */
179 UINT32 getVertexOffset(UINT32 meshId) const;
180
181 /**
182 * Returns the offset in indices from the start of the buffer to the first index of the mesh with the provided ID.
183 */
184 UINT32 getIndexOffset(UINT32 meshId) const;
185
186 /** Called by the render system when a mesh gets queued to the GPU. */
187 void notifyUsedOnGPU(UINT32 meshId);
188
189 /**
190 * Called by an GPU event query when GPU processes the query. Normally signals the heap that the GPU is done with
191 * the mesh.
192 */
193 static void queryTriggered(SPtr<MeshHeap> thisPtr, UINT32 meshId, UINT32 queryId);
194
195 /**
196 * Attempts to reorganize the vertex and index buffer chunks in order to in order to make free memory contigous.
197 *
198 * @note
199 * This will not actually copy any data from index/vertex buffers, and will only modify the chunk descriptors.
200 */
201 void mergeWithNearbyChunks(UINT32 chunkVertIdx, UINT32 chunkIdxIdx);
202
203 private:
204 UINT32 mNumVertices;
205 UINT32 mNumIndices;
206
207 Vector<UINT8*> mCPUVertexData;
208 UINT8* mCPUIndexData;
209
210 SPtr<VertexData> mVertexData;
211 SPtr<IndexBuffer> mIndexBuffer;
212
213 Map<UINT32, AllocatedData> mMeshAllocData;
214
215 SPtr<VertexDataDesc> mVertexDesc;
216 IndexType mIndexType;
217 GpuDeviceFlags mDeviceMask;
218
219 Vector<ChunkData> mVertChunks;
220 Vector<ChunkData> mIdxChunks;
221
222 Stack<UINT32> mEmptyVertChunks;
223 Stack<UINT32> mEmptyIdxChunks;
224
225 List<UINT32> mFreeVertChunks;
226 List<UINT32> mFreeIdxChunks;
227
228 Vector<QueryData> mEventQueries;
229 Stack<UINT32> mFreeEventQueries;
230
231 UINT32 mNextQueryId;
232
233 static const float GrowPercent;
234 };
235
236 /** @} */
237 }
238}