Line data Source code
1 : /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #ifndef mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h
7 : #define mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h
8 :
9 : #include "ShaderDefinitionsMLGPU.h"
10 : #include "MLGDeviceTypes.h"
11 : #include "StagingBuffer.h"
12 : #include "mozilla/gfx/Logging.h"
13 :
14 : namespace mozilla {
15 : namespace layers {
16 :
17 : class MLGBuffer;
18 :
19 : class SharedBufferMLGPU
20 : {
21 : public:
22 : virtual ~SharedBufferMLGPU();
23 :
24 : bool Init();
25 :
26 : // Call before starting a new frame.
27 : void Reset();
28 :
29 : // Call to finish any pending uploads.
30 : void PrepareForUsage();
31 :
32 : protected:
33 : SharedBufferMLGPU(MLGDevice* aDevice, MLGBufferType aType, size_t aDefaultSize);
34 :
35 : bool EnsureMappedBuffer(size_t aBytes);
36 : bool GrowBuffer(size_t aBytes);
37 : void ForgetBuffer();
38 : bool Map();
39 : void Unmap();
40 :
41 0 : uint8_t* GetBufferPointer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer) {
42 0 : if (!EnsureMappedBuffer(aBytes)) {
43 0 : return nullptr;
44 : }
45 :
46 0 : ptrdiff_t newPos = mCurrentPosition + aBytes;
47 0 : MOZ_ASSERT(size_t(newPos) <= mMaxSize);
48 :
49 0 : *aOutOffset = mCurrentPosition;
50 0 : *aOutBuffer = mBuffer;
51 :
52 0 : uint8_t* ptr = reinterpret_cast<uint8_t*>(mMap.mData) + mCurrentPosition;
53 0 : mCurrentPosition = newPos;
54 0 : return ptr;
55 : }
56 :
57 : protected:
58 : // Note: RefPtr here would cause a cycle. Only MLGDevice should own
59 : // SharedBufferMLGPU objects for now.
60 : MLGDevice* mDevice;
61 : MLGBufferType mType;
62 : size_t mDefaultSize;
63 : bool mCanUseOffsetAllocation;
64 :
65 : // When |mBuffer| is non-null, mMaxSize is the buffer size. If mapped, the
66 : // position is between 0 and mMaxSize, otherwise it is always 0.
67 : RefPtr<MLGBuffer> mBuffer;
68 : ptrdiff_t mCurrentPosition;
69 : size_t mMaxSize;
70 :
71 : MLGMappedResource mMap;
72 : bool mMapped;
73 :
74 : // These are used to track how many frames come in under the default
75 : // buffer size in a row.
76 : size_t mBytesUsedThisFrame;
77 : size_t mNumSmallFrames;
78 : };
79 :
80 0 : class VertexBufferSection final
81 : {
82 : friend class SharedVertexBuffer;
83 : public:
84 0 : VertexBufferSection()
85 0 : : mOffset(-1),
86 : mNumVertices(0),
87 0 : mStride(0)
88 0 : {}
89 :
90 0 : uint32_t Stride() const {
91 0 : return mStride;
92 : }
93 0 : MLGBuffer* GetBuffer() const {
94 0 : return mBuffer;
95 : }
96 0 : ptrdiff_t Offset() const {
97 0 : MOZ_ASSERT(IsValid());
98 0 : return mOffset;
99 : }
100 0 : size_t NumVertices() const {
101 0 : return mNumVertices;
102 : }
103 0 : bool IsValid() const {
104 0 : return !!mBuffer;
105 : }
106 :
107 : protected:
108 0 : void Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aNumVertices, size_t aStride) {
109 0 : mBuffer = aBuffer;
110 0 : mOffset = aOffset;
111 0 : mNumVertices = aNumVertices;
112 0 : mStride = aStride;
113 0 : }
114 :
115 : protected:
116 : RefPtr<MLGBuffer> mBuffer;
117 : ptrdiff_t mOffset;
118 : size_t mNumVertices;
119 : size_t mStride;
120 : };
121 :
122 0 : class ConstantBufferSection final
123 : {
124 : friend class SharedConstantBuffer;
125 :
126 : public:
127 0 : ConstantBufferSection()
128 0 : : mOffset(-1)
129 0 : {}
130 :
131 0 : uint32_t NumConstants() const {
132 0 : return NumConstantsForBytes(mNumBytes);
133 : }
134 : size_t NumItems() const {
135 : return mNumItems;
136 : }
137 0 : uint32_t Offset() const {
138 0 : MOZ_ASSERT(IsValid());
139 0 : return mOffset / 16;
140 : }
141 0 : MLGBuffer* GetBuffer() const {
142 0 : return mBuffer;
143 : }
144 0 : bool IsValid() const {
145 0 : return !!mBuffer;
146 : }
147 0 : bool HasOffset() const {
148 0 : return mOffset != -1;
149 : }
150 :
151 : protected:
152 0 : static constexpr size_t NumConstantsForBytes(size_t aBytes) {
153 0 : return (aBytes + ((256 - (aBytes % 256)) % 256)) / 16;
154 : }
155 :
156 0 : void Init(MLGBuffer* aBuffer, ptrdiff_t aOffset, size_t aBytes, size_t aNumItems) {
157 0 : mBuffer = aBuffer;
158 0 : mOffset = aOffset;
159 0 : mNumBytes = aBytes;
160 0 : mNumItems = aNumItems;
161 0 : }
162 :
163 : protected:
164 : RefPtr<MLGBuffer> mBuffer;
165 : ptrdiff_t mOffset;
166 : size_t mNumBytes;
167 : size_t mNumItems;
168 : };
169 :
170 : // Vertex buffers don't need special alignment.
171 : typedef StagingBuffer<0> VertexStagingBuffer;
172 :
173 0 : class SharedVertexBuffer final : public SharedBufferMLGPU
174 : {
175 : public:
176 : SharedVertexBuffer(MLGDevice* aDevice, size_t aDefaultSize);
177 :
178 : // Allocate a buffer that can be uploaded immediately.
179 0 : bool Allocate(VertexBufferSection* aHolder, const VertexStagingBuffer& aStaging) {
180 0 : return Allocate(aHolder,
181 : aStaging.NumItems(),
182 : aStaging.SizeOfItem(),
183 0 : aStaging.GetBufferStart());
184 : }
185 :
186 : // Allocate a buffer that can be uploaded immediately. This is the
187 : // direct access version, for cases where a StagingBuffer is not
188 : // needed.
189 0 : bool Allocate(VertexBufferSection* aHolder,
190 : size_t aNumItems,
191 : size_t aSizeOfItem,
192 : const void* aData)
193 : {
194 0 : RefPtr<MLGBuffer> buffer;
195 : ptrdiff_t offset;
196 0 : size_t bytes = aSizeOfItem * aNumItems;
197 0 : uint8_t* ptr = GetBufferPointer(bytes, &offset, &buffer);
198 0 : if (!ptr) {
199 0 : return false;
200 : }
201 :
202 0 : memcpy(ptr, aData, bytes);
203 0 : aHolder->Init(buffer, offset, aNumItems, aSizeOfItem);
204 0 : return true;
205 : }
206 :
207 : template <typename T>
208 : bool Allocate(VertexBufferSection* aHolder, const T& aItem) {
209 : return Allocate(aHolder, 1, sizeof(T), &aItem);
210 : }
211 : };
212 :
213 : // To support older Direct3D versions, we need to support one-off MLGBuffers,
214 : // where data is uploaded immediately rather than at the end of all batch
215 : // preparation. We achieve this through a small helper class.
216 : //
217 : // Note: the unmap is not inline sincce we don't include MLGDevice.h.
218 : class MOZ_STACK_CLASS AutoBufferUploadBase
219 : {
220 : public:
221 0 : AutoBufferUploadBase() : mPtr(nullptr) {}
222 0 : ~AutoBufferUploadBase() {
223 0 : if (mBuffer) {
224 0 : UnmapBuffer();
225 : }
226 0 : }
227 :
228 0 : void Init(void* aPtr) {
229 0 : MOZ_ASSERT(!mPtr && aPtr);
230 0 : mPtr = aPtr;
231 0 : }
232 0 : void Init(void* aPtr, MLGDevice* aDevice, MLGBuffer* aBuffer) {
233 0 : MOZ_ASSERT(!mPtr && aPtr);
234 0 : mPtr = aPtr;
235 0 : mDevice = aDevice;
236 0 : mBuffer = aBuffer;
237 0 : }
238 0 : void* get() {
239 0 : return const_cast<void*>(mPtr);
240 : }
241 :
242 : private:
243 : void UnmapBuffer();
244 :
245 : protected:
246 : RefPtr<MLGDevice> mDevice;
247 : RefPtr<MLGBuffer> mBuffer;
248 : void* mPtr;
249 : };
250 :
251 : // This is a typed helper for AutoBufferUploadBase.
252 : template <typename T>
253 0 : class AutoBufferUpload : public AutoBufferUploadBase
254 : {
255 : public:
256 0 : AutoBufferUpload()
257 0 : {}
258 :
259 0 : T* operator ->() const {
260 0 : return reinterpret_cast<T*>(mPtr);
261 : }
262 : };
263 :
264 0 : class SharedConstantBuffer final : public SharedBufferMLGPU
265 : {
266 : public:
267 : SharedConstantBuffer(MLGDevice* aDevice, size_t aDefaultSize);
268 :
269 : // Allocate a buffer that can be immediately uploaded.
270 : bool Allocate(ConstantBufferSection* aHolder, const ConstantStagingBuffer& aStaging) {
271 : MOZ_ASSERT(aStaging.NumItems() * aStaging.SizeOfItem() == aStaging.NumBytes());
272 : return Allocate(aHolder, aStaging.NumItems(), aStaging.SizeOfItem(), aStaging.GetBufferStart());
273 : }
274 :
275 : // Allocate a buffer of one item that can be immediately uploaded.
276 : template <typename T>
277 0 : bool Allocate(ConstantBufferSection* aHolder, const T& aItem) {
278 0 : return Allocate(aHolder, 1, sizeof(aItem), &aItem);
279 : }
280 :
281 : // Allocate a buffer of N items that can be immediately uploaded.
282 : template <typename T>
283 0 : bool Allocate(ConstantBufferSection* aHolder, const T* aItems, size_t aNumItems) {
284 0 : return Allocate(aHolder, aNumItems, sizeof(T), aItems);
285 : }
286 :
287 : // Allocate a buffer that is uploaded after the caller has finished writing
288 : // to it. This should method should generally not be used unless copying T
289 : // is expensive, since the default immediate-upload version has an implicit
290 : // extra copy to the GPU. This version exposes the mapped memory directly.
291 : template <typename T>
292 0 : bool Allocate(ConstantBufferSection* aHolder, AutoBufferUpload<T>* aPtr) {
293 : MOZ_ASSERT(sizeof(T) % 16 == 0, "Items must be padded to 16 bytes");
294 :
295 0 : return Allocate(aHolder, aPtr, 1, sizeof(T));
296 : }
297 :
298 : private:
299 0 : bool Allocate(ConstantBufferSection* aHolder,
300 : size_t aNumItems,
301 : size_t aSizeOfItem,
302 : const void* aData)
303 : {
304 0 : AutoBufferUploadBase ptr;
305 0 : if (!Allocate(aHolder, &ptr, aNumItems, aSizeOfItem)) {
306 0 : return false;
307 : }
308 0 : memcpy(ptr.get(), aData, aNumItems * aSizeOfItem);
309 0 : return true;
310 : }
311 :
312 0 : bool Allocate(ConstantBufferSection* aHolder,
313 : AutoBufferUploadBase* aPtr,
314 : size_t aNumItems,
315 : size_t aSizeOfItem)
316 : {
317 0 : MOZ_ASSERT(aSizeOfItem % 16 == 0, "Items must be padded to 16 bytes");
318 :
319 0 : size_t bytes = aNumItems * aSizeOfItem;
320 0 : if (bytes > mMaxConstantBufferBindSize) {
321 0 : gfxWarning() << "Attempted to allocate too many bytes into a constant buffer";
322 0 : return false;
323 : }
324 :
325 :
326 0 : RefPtr<MLGBuffer> buffer;
327 : ptrdiff_t offset;
328 0 : if (!GetBufferPointer(aPtr, bytes, &offset, &buffer)) {
329 0 : return false;
330 : }
331 :
332 0 : aHolder->Init(buffer, offset, bytes, aNumItems);
333 0 : return true;
334 : }
335 :
336 0 : bool GetBufferPointer(AutoBufferUploadBase* aPtr,
337 : size_t aBytes,
338 : ptrdiff_t* aOutOffset,
339 : RefPtr<MLGBuffer>* aOutBuffer)
340 : {
341 0 : if (!mCanUseOffsetAllocation) {
342 0 : uint8_t* ptr = AllocateNewBuffer(aBytes, aOutOffset, aOutBuffer);
343 0 : if (!ptr) {
344 0 : return false;
345 : }
346 0 : aPtr->Init(ptr, mDevice, *aOutBuffer);
347 0 : return true;
348 : }
349 :
350 : // Align up the allocation to 256 bytes, since D3D11 requires that
351 : // constant buffers start at multiples of 16 elements.
352 0 : size_t alignedBytes = AlignUp<256>::calc(aBytes);
353 :
354 0 : uint8_t* ptr = SharedBufferMLGPU::GetBufferPointer(alignedBytes, aOutOffset, aOutBuffer);
355 0 : if (!ptr) {
356 0 : return false;
357 : }
358 :
359 0 : aPtr->Init(ptr);
360 0 : return true;
361 : }
362 :
363 : uint8_t* AllocateNewBuffer(size_t aBytes, ptrdiff_t* aOutOffset, RefPtr<MLGBuffer>* aOutBuffer);
364 :
365 : private:
366 : size_t mMaxConstantBufferBindSize;
367 : };
368 :
369 : } // namespace layers
370 : } // namespace mozilla
371 :
372 : #endif // mozilla_gfx_layers_mlgpu_SharedBufferMLGPU_h
|