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_StagingBuffer_h
7 : #define mozilla_gfx_layers_mlgpu_StagingBuffer_h
8 :
9 : #include "mozilla/MathAlgorithms.h"
10 : #include "mozilla/UniquePtr.h"
11 : #include "UtilityMLGPU.h"
12 : #include <algorithm>
13 : #include <utility>
14 : #include <limits.h>
15 :
16 : namespace mozilla {
17 : namespace layers {
18 :
19 : class MLGDevice;
20 :
21 : // A StagingBuffer is a writable memory buffer for arbitrary contents.
22 : template <size_t Alignment = 0>
23 0 : class StagingBuffer
24 : {
25 : public:
26 0 : StagingBuffer()
27 0 : : StagingBuffer(0)
28 0 : {}
29 :
30 : // By default, staging buffers operate in "forward" mode: items are added to
31 : // the end of the buffer. In "reverse" mode the cursor is at the end of the
32 : // buffer, and items are added to the beginning.
33 : //
34 : // This must be called before the buffer is written.
35 0 : void SetReversed() {
36 0 : MOZ_ASSERT(IsEmpty());
37 0 : mReversed = true;
38 0 : }
39 :
40 : // Write a series of components as a single item. When this is first used, the
41 : // buffer records the initial item size and requires that all future items be
42 : // the exact same size.
43 : //
44 : // This directs to either AppendItem or PrependItem depending on the buffer
45 : // state.
46 : template <typename T>
47 : bool AddItem(const T& aItem) {
48 : if (mReversed) {
49 : return PrependItem(aItem);
50 : }
51 : return AppendItem(aItem);
52 : }
53 :
54 : // Helper for adding a single item as two components.
55 : template <typename T1, typename T2>
56 0 : bool AddItem(const T1& aItem1, const T2& aItem2) {
57 0 : if (mReversed) {
58 0 : return PrependItem(aItem1, aItem2);
59 : }
60 0 : return AppendItem(aItem1, aItem2);
61 : }
62 :
63 : // This may only be called on forward buffers.
64 : template <typename T>
65 0 : bool AppendItem(const T& aItem) {
66 0 : MOZ_ASSERT(!mReversed);
67 :
68 0 : size_t alignedBytes = AlignUp<Alignment>::calc(sizeof(aItem));
69 0 : if (!mUniformSize) {
70 0 : mUniformSize = alignedBytes;
71 : }
72 :
73 0 : if (!EnsureForwardRoomFor(alignedBytes)) {
74 0 : return false;
75 : }
76 0 : if (mUniformSize != alignedBytes) {
77 0 : MOZ_ASSERT_UNREACHABLE("item of incorrect size added!");
78 : return false;
79 : }
80 :
81 0 : *reinterpret_cast<T*>(mPos) = aItem;
82 0 : mPos += alignedBytes;
83 0 : MOZ_ASSERT(mPos <= mEnd);
84 :
85 0 : mNumItems++;
86 0 : return true;
87 : }
88 :
89 : // Append an item in two stages.
90 : template <typename T1, typename T2>
91 0 : bool AppendItem(const T1& aFirst, const T2& aSecond) {
92 : struct Combined {
93 : T1 first;
94 : T2 second;
95 0 : } value = { aFirst, aSecond };
96 :
97 : // The combined value must be packed.
98 : static_assert(sizeof(value) == sizeof(aFirst) + sizeof(aSecond),
99 : "Items must be packed within struct");
100 0 : return AppendItem(value);
101 : }
102 :
103 : // This may only be called on reversed buffers.
104 : template <typename T>
105 0 : bool PrependItem(const T& aItem) {
106 0 : MOZ_ASSERT(mReversed);
107 :
108 0 : size_t alignedBytes = AlignUp<Alignment>::calc(sizeof(aItem));
109 0 : if (!mUniformSize) {
110 0 : mUniformSize = alignedBytes;
111 : }
112 :
113 0 : if (!EnsureBackwardRoomFor(alignedBytes)) {
114 0 : return false;
115 : }
116 0 : if (mUniformSize != alignedBytes) {
117 0 : MOZ_ASSERT_UNREACHABLE("item of incorrect size added!");
118 : return false;
119 : }
120 :
121 0 : mPos -= alignedBytes;
122 0 : *reinterpret_cast<T*>(mPos) = aItem;
123 0 : MOZ_ASSERT(mPos >= mBuffer.get());
124 :
125 0 : mNumItems++;
126 0 : return true;
127 : }
128 :
129 : // Prepend an item in two stages.
130 : template <typename T1, typename T2>
131 0 : bool PrependItem(const T1& aFirst, const T2& aSecond) {
132 : struct Combined {
133 : T1 first;
134 : T2 second;
135 0 : } value = { aFirst, aSecond };
136 :
137 : // The combined value must be packed.
138 : static_assert(sizeof(value) == sizeof(aFirst) + sizeof(aSecond),
139 : "Items must be packed within struct");
140 0 : return PrependItem(value);
141 : }
142 :
143 0 : size_t NumBytes() const {
144 0 : return mReversed
145 0 : ? mEnd - mPos
146 0 : : mPos - mBuffer.get();
147 : }
148 0 : uint8_t* GetBufferStart() const {
149 0 : return mReversed
150 : ? mPos
151 0 : : mBuffer.get();
152 : }
153 0 : size_t SizeOfItem() const {
154 0 : return mUniformSize;
155 : }
156 0 : size_t NumItems() const {
157 0 : return mNumItems;
158 : }
159 :
160 : void Reset() {
161 : mPos = mReversed ? mEnd : mBuffer.get();
162 : mUniformSize = 0;
163 : mNumItems = 0;
164 : }
165 :
166 : // RestorePosition must only be called with a previous call to
167 : // GetPosition.
168 : typedef std::pair<size_t,size_t> Position;
169 0 : Position GetPosition() const {
170 0 : return std::make_pair(NumBytes(), mNumItems);
171 : }
172 0 : void RestorePosition(const Position& aPosition) {
173 0 : mPos = mBuffer.get() + aPosition.first;
174 0 : mNumItems = aPosition.second;
175 0 : if (mNumItems == 0) {
176 0 : mUniformSize = 0;
177 : }
178 :
179 : // Make sure the buffer is still coherent.
180 0 : MOZ_ASSERT(mPos >= mBuffer.get() && mPos <= mEnd);
181 0 : MOZ_ASSERT(mNumItems * mUniformSize == NumBytes());
182 0 : }
183 :
184 0 : bool IsEmpty() const {
185 0 : return mNumItems == 0;
186 : }
187 :
188 : protected:
189 0 : explicit StagingBuffer(size_t aMaxSize)
190 : : mPos(nullptr),
191 : mEnd(nullptr),
192 : mUniformSize(0),
193 : mNumItems(0),
194 : mMaxSize(aMaxSize),
195 0 : mReversed(false)
196 0 : {}
197 :
198 : static const size_t kDefaultSize = 8;
199 :
200 0 : bool EnsureForwardRoomFor(size_t aAlignedBytes) {
201 0 : if (size_t(mEnd - mPos) < aAlignedBytes) {
202 0 : return GrowBuffer(aAlignedBytes);
203 : }
204 0 : return true;
205 : }
206 :
207 0 : bool EnsureBackwardRoomFor(size_t aAlignedBytes) {
208 0 : if (size_t(mPos - mBuffer.get()) < aAlignedBytes) {
209 0 : return GrowBuffer(aAlignedBytes);
210 : }
211 0 : return true;
212 : }
213 :
214 0 : bool GrowBuffer(size_t aAlignedBytes) {
215 : // We should not be writing items that are potentially bigger than the
216 : // maximum constant buffer size, that's crazy. An assert should be good
217 : // enough since the size of writes is static - and shader compilers
218 : // would explode anyway.
219 0 : MOZ_ASSERT_IF(mMaxSize, aAlignedBytes < mMaxSize);
220 0 : MOZ_ASSERT_IF(mMaxSize, kDefaultSize * Alignment < mMaxSize);
221 :
222 0 : if (!mBuffer) {
223 0 : size_t newSize = std::max(kDefaultSize * Alignment, aAlignedBytes);
224 0 : MOZ_ASSERT_IF(mMaxSize, newSize < mMaxSize);
225 :
226 0 : mBuffer = MakeUnique<uint8_t[]>(newSize);
227 0 : mEnd = mBuffer.get() + newSize;
228 0 : mPos = mReversed ? mEnd : mBuffer.get();
229 0 : return true;
230 : }
231 :
232 : // Take the bigger of exact-fit or 1.5x the previous size, and make sure
233 : // the new size doesn't overflow size_t. If needed, clamp to the max
234 : // size.
235 0 : size_t oldSize = mEnd - mBuffer.get();
236 0 : size_t trySize = std::max(oldSize + aAlignedBytes, oldSize + oldSize / 2);
237 0 : size_t newSize = mMaxSize ? std::min(trySize, mMaxSize) : trySize;
238 0 : if (newSize < oldSize || newSize - oldSize < aAlignedBytes) {
239 0 : return false;
240 : }
241 :
242 0 : UniquePtr<uint8_t[]> newBuffer = MakeUnique<uint8_t[]>(newSize);
243 0 : if (!newBuffer) {
244 0 : return false;
245 : }
246 :
247 : // When the buffer is in reverse mode, we have to copy from the end of the
248 : // buffer, not the beginning.
249 0 : if (mReversed) {
250 0 : size_t usedBytes = mEnd - mPos;
251 0 : size_t newPos = newSize - usedBytes;
252 0 : MOZ_RELEASE_ASSERT(newPos + usedBytes <= newSize);
253 :
254 0 : memcpy(newBuffer.get() + newPos, mPos, usedBytes);
255 0 : mPos = newBuffer.get() + newPos;
256 : } else {
257 0 : size_t usedBytes = mPos - mBuffer.get();
258 0 : MOZ_RELEASE_ASSERT(usedBytes <= newSize);
259 :
260 0 : memcpy(newBuffer.get(), mBuffer.get(), usedBytes);
261 0 : mPos = newBuffer.get() + usedBytes;
262 : }
263 0 : mEnd = newBuffer.get() + newSize;
264 0 : mBuffer = Move(newBuffer);
265 :
266 0 : MOZ_RELEASE_ASSERT(mPos >= mBuffer.get() && mPos <= mEnd);
267 0 : return true;
268 : }
269 :
270 : protected:
271 : UniquePtr<uint8_t[]> mBuffer;
272 : uint8_t* mPos;
273 : uint8_t* mEnd;
274 : size_t mUniformSize;
275 : size_t mNumItems;
276 : size_t mMaxSize;
277 : bool mReversed;
278 : };
279 :
280 : class ConstantStagingBuffer : public StagingBuffer<16>
281 : {
282 : public:
283 : explicit ConstantStagingBuffer(MLGDevice* aDevice);
284 : };
285 :
286 : } // namespace layers
287 : } // namespace mozilla
288 :
289 : #endif // mozilla_gfx_layers_mlgpu_StagingBuffer_h
|