• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_native/Buffer.h"
16 
17 #include "common/Assert.h"
18 #include "dawn_native/Device.h"
19 #include "dawn_native/DynamicUploader.h"
20 #include "dawn_native/ValidationUtils_autogen.h"
21 
22 #include <string.h>
23 #include <cstdio>
24 #include <utility>
25 
26 namespace dawn_native {
27 
28     namespace {
29 
30         class ErrorBuffer : public BufferBase {
31           public:
ErrorBuffer(DeviceBase * device)32             ErrorBuffer(DeviceBase* device) : BufferBase(device, ObjectBase::kError) {
33             }
34 
MakeMapped(DeviceBase * device,uint64_t size,uint8_t ** mappedPointer)35             static ErrorBuffer* MakeMapped(DeviceBase* device,
36                                            uint64_t size,
37                                            uint8_t** mappedPointer) {
38                 ASSERT(mappedPointer != nullptr);
39 
40                 ErrorBuffer* buffer = new ErrorBuffer(device);
41                 buffer->mFakeMappedData =
42                     std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[size]);
43                 *mappedPointer = buffer->mFakeMappedData.get();
44 
45                 return buffer;
46             }
47 
ClearMappedData()48             void ClearMappedData() {
49                 mFakeMappedData.reset();
50             }
51 
52           private:
IsMapWritable() const53             bool IsMapWritable() const override {
54                 UNREACHABLE();
55                 return false;
56             }
57 
MapAtCreationImpl(uint8_t ** mappedPointer)58             MaybeError MapAtCreationImpl(uint8_t** mappedPointer) override {
59                 UNREACHABLE();
60                 return {};
61             }
62 
SetSubDataImpl(uint32_t start,uint32_t count,const void * data)63             MaybeError SetSubDataImpl(uint32_t start, uint32_t count, const void* data) override {
64                 UNREACHABLE();
65                 return {};
66             }
MapReadAsyncImpl(uint32_t serial)67             void MapReadAsyncImpl(uint32_t serial) override {
68                 UNREACHABLE();
69             }
MapWriteAsyncImpl(uint32_t serial)70             void MapWriteAsyncImpl(uint32_t serial) override {
71                 UNREACHABLE();
72             }
UnmapImpl()73             void UnmapImpl() override {
74                 UNREACHABLE();
75             }
DestroyImpl()76             void DestroyImpl() override {
77                 UNREACHABLE();
78             }
79 
80             std::unique_ptr<uint8_t[]> mFakeMappedData;
81         };
82 
83     }  // anonymous namespace
84 
ValidateBufferDescriptor(DeviceBase *,const BufferDescriptor * descriptor)85     MaybeError ValidateBufferDescriptor(DeviceBase*, const BufferDescriptor* descriptor) {
86         if (descriptor->nextInChain != nullptr) {
87             return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
88         }
89 
90         DAWN_TRY(ValidateBufferUsageBit(descriptor->usage));
91 
92         dawn::BufferUsageBit usage = descriptor->usage;
93 
94         const dawn::BufferUsageBit kMapWriteAllowedUsages =
95             dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::CopySrc;
96         if (usage & dawn::BufferUsageBit::MapWrite && (usage & kMapWriteAllowedUsages) != usage) {
97             return DAWN_VALIDATION_ERROR("Only CopySrc is allowed with MapWrite");
98         }
99 
100         const dawn::BufferUsageBit kMapReadAllowedUsages =
101             dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::CopyDst;
102         if (usage & dawn::BufferUsageBit::MapRead && (usage & kMapReadAllowedUsages) != usage) {
103             return DAWN_VALIDATION_ERROR("Only CopyDst is allowed with MapRead");
104         }
105 
106         return {};
107     }
108 
109     // Buffer
110 
BufferBase(DeviceBase * device,const BufferDescriptor * descriptor)111     BufferBase::BufferBase(DeviceBase* device, const BufferDescriptor* descriptor)
112         : ObjectBase(device),
113           mSize(descriptor->size),
114           mUsage(descriptor->usage),
115           mState(BufferState::Unmapped) {
116     }
117 
BufferBase(DeviceBase * device,ObjectBase::ErrorTag tag)118     BufferBase::BufferBase(DeviceBase* device, ObjectBase::ErrorTag tag)
119         : ObjectBase(device, tag), mState(BufferState::Unmapped) {
120     }
121 
~BufferBase()122     BufferBase::~BufferBase() {
123         if (mState == BufferState::Mapped) {
124             ASSERT(!IsError());
125             CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u);
126             CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u);
127         }
128     }
129 
130     // static
MakeError(DeviceBase * device)131     BufferBase* BufferBase::MakeError(DeviceBase* device) {
132         return new ErrorBuffer(device);
133     }
134 
135     // static
MakeErrorMapped(DeviceBase * device,uint64_t size,uint8_t ** mappedPointer)136     BufferBase* BufferBase::MakeErrorMapped(DeviceBase* device,
137                                             uint64_t size,
138                                             uint8_t** mappedPointer) {
139         return ErrorBuffer::MakeMapped(device, size, mappedPointer);
140     }
141 
GetSize() const142     uint64_t BufferBase::GetSize() const {
143         ASSERT(!IsError());
144         return mSize;
145     }
146 
GetUsage() const147     dawn::BufferUsageBit BufferBase::GetUsage() const {
148         ASSERT(!IsError());
149         return mUsage;
150     }
151 
MapAtCreation(uint8_t ** mappedPointer)152     MaybeError BufferBase::MapAtCreation(uint8_t** mappedPointer) {
153         ASSERT(!IsError());
154         ASSERT(mappedPointer != nullptr);
155 
156         mState = BufferState::Mapped;
157 
158         if (IsMapWritable()) {
159             DAWN_TRY(MapAtCreationImpl(mappedPointer));
160             ASSERT(*mappedPointer != nullptr);
161             return {};
162         }
163 
164         // If any of these fail, the buffer will be deleted and replaced with an
165         // error buffer.
166         // TODO(enga): Suballocate and reuse memory from a larger staging buffer so we don't create
167         // many small buffers.
168         DynamicUploader* uploader = nullptr;
169         DAWN_TRY_ASSIGN(uploader, GetDevice()->GetDynamicUploader());
170         DAWN_TRY_ASSIGN(mStagingBuffer, uploader->CreateStagingBuffer(GetSize()));
171 
172         ASSERT(mStagingBuffer->GetMappedPointer() != nullptr);
173         *mappedPointer = reinterpret_cast<uint8_t*>(mStagingBuffer->GetMappedPointer());
174 
175         return {};
176     }
177 
ValidateCanUseInSubmitNow() const178     MaybeError BufferBase::ValidateCanUseInSubmitNow() const {
179         ASSERT(!IsError());
180 
181         switch (mState) {
182             case BufferState::Destroyed:
183                 return DAWN_VALIDATION_ERROR("Destroyed buffer used in a submit");
184             case BufferState::Mapped:
185                 return DAWN_VALIDATION_ERROR("Buffer used in a submit while mapped");
186             case BufferState::Unmapped:
187                 return {};
188         }
189     }
190 
CallMapReadCallback(uint32_t serial,DawnBufferMapAsyncStatus status,const void * pointer,uint32_t dataLength)191     void BufferBase::CallMapReadCallback(uint32_t serial,
192                                          DawnBufferMapAsyncStatus status,
193                                          const void* pointer,
194                                          uint32_t dataLength) {
195         ASSERT(!IsError());
196         if (mMapReadCallback != nullptr && serial == mMapSerial) {
197             ASSERT(mMapWriteCallback == nullptr);
198             // Tag the callback as fired before firing it, otherwise it could fire a second time if
199             // for example buffer.Unmap() is called inside the application-provided callback.
200             DawnBufferMapReadCallback callback = mMapReadCallback;
201             mMapReadCallback = nullptr;
202             callback(status, pointer, dataLength, mMapUserdata);
203         }
204     }
205 
CallMapWriteCallback(uint32_t serial,DawnBufferMapAsyncStatus status,void * pointer,uint32_t dataLength)206     void BufferBase::CallMapWriteCallback(uint32_t serial,
207                                           DawnBufferMapAsyncStatus status,
208                                           void* pointer,
209                                           uint32_t dataLength) {
210         ASSERT(!IsError());
211         if (mMapWriteCallback != nullptr && serial == mMapSerial) {
212             ASSERT(mMapReadCallback == nullptr);
213             // Tag the callback as fired before firing it, otherwise it could fire a second time if
214             // for example buffer.Unmap() is called inside the application-provided callback.
215             DawnBufferMapWriteCallback callback = mMapWriteCallback;
216             mMapWriteCallback = nullptr;
217             callback(status, pointer, dataLength, mMapUserdata);
218         }
219     }
220 
SetSubData(uint32_t start,uint32_t count,const void * data)221     void BufferBase::SetSubData(uint32_t start, uint32_t count, const void* data) {
222         if (GetDevice()->ConsumedError(ValidateSetSubData(start, count))) {
223             return;
224         }
225         ASSERT(!IsError());
226 
227         if (GetDevice()->ConsumedError(SetSubDataImpl(start, count, data))) {
228             return;
229         }
230     }
231 
MapReadAsync(DawnBufferMapReadCallback callback,void * userdata)232     void BufferBase::MapReadAsync(DawnBufferMapReadCallback callback, void* userdata) {
233         if (GetDevice()->ConsumedError(ValidateMap(dawn::BufferUsageBit::MapRead))) {
234             callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, userdata);
235             return;
236         }
237         ASSERT(!IsError());
238 
239         ASSERT(mMapWriteCallback == nullptr);
240 
241         // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes.
242         mMapSerial++;
243         mMapReadCallback = callback;
244         mMapUserdata = userdata;
245         mState = BufferState::Mapped;
246 
247         MapReadAsyncImpl(mMapSerial);
248     }
249 
SetSubDataImpl(uint32_t start,uint32_t count,const void * data)250     MaybeError BufferBase::SetSubDataImpl(uint32_t start, uint32_t count, const void* data) {
251         DynamicUploader* uploader = nullptr;
252         DAWN_TRY_ASSIGN(uploader, GetDevice()->GetDynamicUploader());
253 
254         // TODO(bryan.bernhart@intel.com): Remove once alignment constraint is added to validation
255         // (dawn:73). D3D12 does not specify so we assume 4-byte alignment to be safe.
256         static constexpr size_t kDefaultAlignment = 4;
257 
258         UploadHandle uploadHandle;
259         DAWN_TRY_ASSIGN(uploadHandle, uploader->Allocate(count, kDefaultAlignment));
260         ASSERT(uploadHandle.mappedBuffer != nullptr);
261 
262         memcpy(uploadHandle.mappedBuffer, data, count);
263 
264         DAWN_TRY(GetDevice()->CopyFromStagingToBuffer(
265             uploadHandle.stagingBuffer, uploadHandle.startOffset, this, start, count));
266 
267         return {};
268     }
269 
MapWriteAsync(DawnBufferMapWriteCallback callback,void * userdata)270     void BufferBase::MapWriteAsync(DawnBufferMapWriteCallback callback, void* userdata) {
271         if (GetDevice()->ConsumedError(ValidateMap(dawn::BufferUsageBit::MapWrite))) {
272             callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, 0, userdata);
273             return;
274         }
275         ASSERT(!IsError());
276 
277         ASSERT(mMapReadCallback == nullptr);
278 
279         // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes.
280         mMapSerial++;
281         mMapWriteCallback = callback;
282         mMapUserdata = userdata;
283         mState = BufferState::Mapped;
284 
285         MapWriteAsyncImpl(mMapSerial);
286     }
287 
Destroy()288     void BufferBase::Destroy() {
289         if (IsError()) {
290             // It is an error to call Destroy() on an ErrorBuffer, but we still need to reclaim the
291             // fake mapped staging data.
292             reinterpret_cast<ErrorBuffer*>(this)->ClearMappedData();
293         }
294         if (GetDevice()->ConsumedError(ValidateDestroy())) {
295             return;
296         }
297         ASSERT(!IsError());
298 
299         if (mState == BufferState::Mapped) {
300             if (mStagingBuffer == nullptr) {
301                 Unmap();
302             }
303             mStagingBuffer.reset();
304         }
305         DestroyInternal();
306     }
307 
CopyFromStagingBuffer()308     MaybeError BufferBase::CopyFromStagingBuffer() {
309         ASSERT(mStagingBuffer);
310         DAWN_TRY(GetDevice()->CopyFromStagingToBuffer(mStagingBuffer.get(), 0, this, 0, GetSize()));
311 
312         DynamicUploader* uploader = nullptr;
313         DAWN_TRY_ASSIGN(uploader, GetDevice()->GetDynamicUploader());
314         uploader->ReleaseStagingBuffer(std::move(mStagingBuffer));
315 
316         return {};
317     }
318 
Unmap()319     void BufferBase::Unmap() {
320         if (IsError()) {
321             // It is an error to call Unmap() on an ErrorBuffer, but we still need to reclaim the
322             // fake mapped staging data.
323             reinterpret_cast<ErrorBuffer*>(this)->ClearMappedData();
324         }
325         if (GetDevice()->ConsumedError(ValidateUnmap())) {
326             return;
327         }
328         ASSERT(!IsError());
329 
330         if (mStagingBuffer != nullptr) {
331             GetDevice()->ConsumedError(CopyFromStagingBuffer());
332         } else {
333             // A map request can only be called once, so this will fire only if the request wasn't
334             // completed before the Unmap.
335             // Callbacks are not fired if there is no callback registered, so this is correct for
336             // CreateBufferMapped.
337             CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u);
338             CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, 0u);
339             UnmapImpl();
340         }
341         mState = BufferState::Unmapped;
342         mMapReadCallback = nullptr;
343         mMapWriteCallback = nullptr;
344         mMapUserdata = 0;
345     }
346 
ValidateSetSubData(uint32_t start,uint32_t count) const347     MaybeError BufferBase::ValidateSetSubData(uint32_t start, uint32_t count) const {
348         DAWN_TRY(GetDevice()->ValidateObject(this));
349 
350         switch (mState) {
351             case BufferState::Mapped:
352                 return DAWN_VALIDATION_ERROR("Buffer is mapped");
353             case BufferState::Destroyed:
354                 return DAWN_VALIDATION_ERROR("Buffer is destroyed");
355             case BufferState::Unmapped:
356                 break;
357         }
358 
359         if (count > GetSize()) {
360             return DAWN_VALIDATION_ERROR("Buffer subdata with too much data");
361         }
362 
363         // Metal requests buffer to buffer copy size must be a multiple of 4 bytes on macOS
364         if (count % 4 != 0) {
365             return DAWN_VALIDATION_ERROR("Buffer subdata size must be a multiple of 4 bytes");
366         }
367 
368         // Metal requests offset of buffer to buffer copy must be a multiple of 4 bytes on macOS
369         if (start % 4 != 0) {
370             return DAWN_VALIDATION_ERROR("Start position must be a multiple of 4 bytes");
371         }
372 
373         // Note that no overflow can happen because we already checked for GetSize() >= count
374         if (start > GetSize() - count) {
375             return DAWN_VALIDATION_ERROR("Buffer subdata out of range");
376         }
377 
378         if (!(mUsage & dawn::BufferUsageBit::CopyDst)) {
379             return DAWN_VALIDATION_ERROR("Buffer needs the CopyDst usage bit");
380         }
381 
382         return {};
383     }
384 
ValidateMap(dawn::BufferUsageBit requiredUsage) const385     MaybeError BufferBase::ValidateMap(dawn::BufferUsageBit requiredUsage) const {
386         DAWN_TRY(GetDevice()->ValidateObject(this));
387 
388         switch (mState) {
389             case BufferState::Mapped:
390                 return DAWN_VALIDATION_ERROR("Buffer already mapped");
391             case BufferState::Destroyed:
392                 return DAWN_VALIDATION_ERROR("Buffer is destroyed");
393             case BufferState::Unmapped:
394                 break;
395         }
396 
397         if (!(mUsage & requiredUsage)) {
398             return DAWN_VALIDATION_ERROR("Buffer needs the correct map usage bit");
399         }
400 
401         return {};
402     }
403 
ValidateUnmap() const404     MaybeError BufferBase::ValidateUnmap() const {
405         DAWN_TRY(GetDevice()->ValidateObject(this));
406 
407         switch (mState) {
408             case BufferState::Mapped:
409                 // A buffer may be in the Mapped state if it was created with CreateBufferMapped
410                 // even if it did not have a mappable usage.
411                 return {};
412             case BufferState::Unmapped:
413                 if ((mUsage & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) ==
414                     0) {
415                     return DAWN_VALIDATION_ERROR("Buffer does not have map usage");
416                 }
417                 return {};
418             case BufferState::Destroyed:
419                 return DAWN_VALIDATION_ERROR("Buffer is destroyed");
420         }
421     }
422 
ValidateDestroy() const423     MaybeError BufferBase::ValidateDestroy() const {
424         DAWN_TRY(GetDevice()->ValidateObject(this));
425         return {};
426     }
427 
DestroyInternal()428     void BufferBase::DestroyInternal() {
429         if (mState != BufferState::Destroyed) {
430             DestroyImpl();
431         }
432         mState = BufferState::Destroyed;
433     }
434 
435 }  // namespace dawn_native
436