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