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