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