// Copyright 2017 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "dawn_native/Buffer.h" #include "common/Assert.h" #include "dawn_native/Device.h" #include #include namespace dawn_native { // Buffer BufferBase::BufferBase(BufferBuilder* builder) : mDevice(builder->mDevice), mSize(builder->mSize), mUsage(builder->mAllowedUsage) { } BufferBase::~BufferBase() { if (mIsMapped) { CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); } } BufferViewBuilder* BufferBase::CreateBufferViewBuilder() { return new BufferViewBuilder(mDevice, this); } DeviceBase* BufferBase::GetDevice() const { return mDevice; } uint32_t BufferBase::GetSize() const { return mSize; } dawn::BufferUsageBit BufferBase::GetUsage() const { return mUsage; } void BufferBase::CallMapReadCallback(uint32_t serial, dawnBufferMapAsyncStatus status, const void* pointer) { if (mMapReadCallback != nullptr && serial == mMapSerial) { ASSERT(mMapWriteCallback == nullptr); // Tag the callback as fired before firing it, otherwise it could fire a second time if // for example buffer.Unmap() is called inside the application-provided callback. dawnBufferMapReadCallback callback = mMapReadCallback; mMapReadCallback = nullptr; callback(status, pointer, mMapUserdata); } } void BufferBase::CallMapWriteCallback(uint32_t serial, dawnBufferMapAsyncStatus status, void* pointer) { if (mMapWriteCallback != nullptr && serial == mMapSerial) { ASSERT(mMapReadCallback == nullptr); // Tag the callback as fired before firing it, otherwise it could fire a second time if // for example buffer.Unmap() is called inside the application-provided callback. dawnBufferMapWriteCallback callback = mMapWriteCallback; mMapWriteCallback = nullptr; callback(status, pointer, mMapUserdata); } } void BufferBase::SetSubData(uint32_t start, uint32_t count, const uint8_t* data) { if (mDevice->ConsumedError(ValidateSetSubData(start, count))) { return; } SetSubDataImpl(start, count, data); } void BufferBase::MapReadAsync(uint32_t start, uint32_t size, dawnBufferMapReadCallback callback, dawnCallbackUserdata userdata) { if (mDevice->ConsumedError(ValidateMap(start, size, dawn::BufferUsageBit::MapRead))) { callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata); return; } ASSERT(mMapWriteCallback == nullptr); // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes. mMapSerial++; mMapReadCallback = callback; mMapUserdata = userdata; mIsMapped = true; MapReadAsyncImpl(mMapSerial, start, size); } void BufferBase::MapWriteAsync(uint32_t start, uint32_t size, dawnBufferMapWriteCallback callback, dawnCallbackUserdata userdata) { if (mDevice->ConsumedError(ValidateMap(start, size, dawn::BufferUsageBit::MapWrite))) { callback(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata); return; } ASSERT(mMapReadCallback == nullptr); // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes. mMapSerial++; mMapWriteCallback = callback; mMapUserdata = userdata; mIsMapped = true; MapWriteAsyncImpl(mMapSerial, start, size); } void BufferBase::Unmap() { if (mDevice->ConsumedError(ValidateUnmap())) { return; } // A map request can only be called once, so this will fire only if the request wasn't // completed before the Unmap CallMapReadCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); CallMapWriteCallback(mMapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); UnmapImpl(); mIsMapped = false; mMapReadCallback = nullptr; mMapWriteCallback = nullptr; mMapUserdata = 0; } MaybeError BufferBase::ValidateSetSubData(uint32_t start, uint32_t count) const { // TODO(cwallez@chromium.org): check for overflows. if (start + count > GetSize()) { DAWN_RETURN_ERROR("Buffer subdata out of range"); } if (!(mUsage & dawn::BufferUsageBit::TransferDst)) { DAWN_RETURN_ERROR("Buffer needs the transfer dst usage bit"); } return {}; } MaybeError BufferBase::ValidateMap(uint32_t start, uint32_t size, dawn::BufferUsageBit requiredUsage) const { // TODO(cwallez@chromium.org): check for overflows. if (start + size > GetSize()) { DAWN_RETURN_ERROR("Buffer map read out of range"); } if (mIsMapped) { DAWN_RETURN_ERROR("Buffer already mapped"); } if (!(mUsage & requiredUsage)) { DAWN_RETURN_ERROR("Buffer needs the correct map usage bit"); } return {}; } MaybeError BufferBase::ValidateUnmap() const { if (!mIsMapped) { DAWN_RETURN_ERROR("Buffer wasn't mapped"); } return {}; } // BufferBuilder enum BufferSetProperties { BUFFER_PROPERTY_ALLOWED_USAGE = 0x1, BUFFER_PROPERTY_SIZE = 0x2, }; BufferBuilder::BufferBuilder(DeviceBase* device) : Builder(device) { } BufferBase* BufferBuilder::GetResultImpl() { constexpr int allProperties = BUFFER_PROPERTY_ALLOWED_USAGE | BUFFER_PROPERTY_SIZE; if ((mPropertiesSet & allProperties) != allProperties) { HandleError("Buffer missing properties"); return nullptr; } const dawn::BufferUsageBit kMapWriteAllowedUsages = dawn::BufferUsageBit::MapWrite | dawn::BufferUsageBit::TransferSrc; if (mAllowedUsage & dawn::BufferUsageBit::MapWrite && (mAllowedUsage & kMapWriteAllowedUsages) != mAllowedUsage) { HandleError("Only TransferSrc is allowed with MapWrite"); return nullptr; } const dawn::BufferUsageBit kMapReadAllowedUsages = dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::TransferDst; if (mAllowedUsage & dawn::BufferUsageBit::MapRead && (mAllowedUsage & kMapReadAllowedUsages) != mAllowedUsage) { HandleError("Only TransferDst is allowed with MapRead"); return nullptr; } return mDevice->CreateBuffer(this); } void BufferBuilder::SetAllowedUsage(dawn::BufferUsageBit usage) { if ((mPropertiesSet & BUFFER_PROPERTY_ALLOWED_USAGE) != 0) { HandleError("Buffer allowedUsage property set multiple times"); return; } mAllowedUsage = usage; mPropertiesSet |= BUFFER_PROPERTY_ALLOWED_USAGE; } void BufferBuilder::SetSize(uint32_t size) { if ((mPropertiesSet & BUFFER_PROPERTY_SIZE) != 0) { HandleError("Buffer size property set multiple times"); return; } mSize = size; mPropertiesSet |= BUFFER_PROPERTY_SIZE; } // BufferViewBase BufferViewBase::BufferViewBase(BufferViewBuilder* builder) : mBuffer(std::move(builder->mBuffer)), mSize(builder->mSize), mOffset(builder->mOffset) { } BufferBase* BufferViewBase::GetBuffer() { return mBuffer.Get(); } uint32_t BufferViewBase::GetSize() const { return mSize; } uint32_t BufferViewBase::GetOffset() const { return mOffset; } // BufferViewBuilder enum BufferViewSetProperties { BUFFER_VIEW_PROPERTY_EXTENT = 0x1, }; BufferViewBuilder::BufferViewBuilder(DeviceBase* device, BufferBase* buffer) : Builder(device), mBuffer(buffer) { } BufferViewBase* BufferViewBuilder::GetResultImpl() { constexpr int allProperties = BUFFER_VIEW_PROPERTY_EXTENT; if ((mPropertiesSet & allProperties) != allProperties) { HandleError("Buffer view missing properties"); return nullptr; } return mDevice->CreateBufferView(this); } void BufferViewBuilder::SetExtent(uint32_t offset, uint32_t size) { if ((mPropertiesSet & BUFFER_VIEW_PROPERTY_EXTENT) != 0) { HandleError("Buffer view extent property set multiple times"); return; } uint64_t viewEnd = static_cast(offset) + static_cast(size); if (viewEnd > static_cast(mBuffer->GetSize())) { HandleError("Buffer view end is OOB"); return; } mOffset = offset; mSize = size; mPropertiesSet |= BUFFER_VIEW_PROPERTY_EXTENT; } } // namespace dawn_native