dawn-cmake/src/backend/Buffer.cpp

307 lines
10 KiB
C++
Raw Normal View History

// Copyright 2017 The NXT 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 "backend/Buffer.h"
#include "backend/Device.h"
#include "common/Assert.h"
#include <cstdio>
2017-11-24 18:59:42 +00:00
#include <utility>
namespace backend {
// Buffer
BufferBase::BufferBase(BufferBuilder* builder)
2017-11-23 18:32:51 +00:00
: mDevice(builder->mDevice),
mSize(builder->mSize),
mAllowedUsage(builder->mAllowedUsage),
mCurrentUsage(builder->mCurrentUsage) {
}
BufferBase::~BufferBase() {
2017-11-23 18:32:51 +00:00
if (mIsMapped) {
CallMapReadCallback(mMapReadSerial, NXT_BUFFER_MAP_READ_STATUS_UNKNOWN, nullptr);
}
}
BufferViewBuilder* BufferBase::CreateBufferViewBuilder() {
2017-11-23 18:32:51 +00:00
return new BufferViewBuilder(mDevice, this);
}
2017-12-04 23:06:02 +00:00
DeviceBase* BufferBase::GetDevice() const {
2017-11-23 18:32:51 +00:00
return mDevice;
}
uint32_t BufferBase::GetSize() const {
2017-11-23 18:32:51 +00:00
return mSize;
}
nxt::BufferUsageBit BufferBase::GetAllowedUsage() const {
2017-11-23 18:32:51 +00:00
return mAllowedUsage;
}
nxt::BufferUsageBit BufferBase::GetUsage() const {
2017-11-23 18:32:51 +00:00
return mCurrentUsage;
}
2017-11-24 18:59:42 +00:00
void BufferBase::CallMapReadCallback(uint32_t serial,
nxtBufferMapReadStatus status,
const void* pointer) {
2017-11-23 18:32:51 +00:00
if (mMapReadCallback && serial == mMapReadSerial) {
// 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.
nxtBufferMapReadCallback callback = mMapReadCallback;
2017-11-23 18:32:51 +00:00
mMapReadCallback = nullptr;
callback(status, pointer, mMapReadUserdata);
}
}
void BufferBase::SetSubData(uint32_t start, uint32_t count, const uint32_t* data) {
if ((start + count) * sizeof(uint32_t) > GetSize()) {
2017-11-23 18:32:51 +00:00
mDevice->HandleError("Buffer subdata out of range");
return;
}
2017-11-23 18:32:51 +00:00
if (!(mCurrentUsage & nxt::BufferUsageBit::TransferDst)) {
mDevice->HandleError("Buffer needs the transfer dst usage bit");
return;
}
SetSubDataImpl(start, count, data);
}
2017-11-24 18:59:42 +00:00
void BufferBase::MapReadAsync(uint32_t start,
uint32_t size,
nxtBufferMapReadCallback callback,
nxtCallbackUserdata userdata) {
if (start + size > GetSize()) {
2017-11-23 18:32:51 +00:00
mDevice->HandleError("Buffer map read out of range");
callback(NXT_BUFFER_MAP_READ_STATUS_ERROR, nullptr, userdata);
return;
}
2017-11-23 18:32:51 +00:00
if (!(mCurrentUsage & nxt::BufferUsageBit::MapRead)) {
mDevice->HandleError("Buffer needs the map read usage bit");
callback(NXT_BUFFER_MAP_READ_STATUS_ERROR, nullptr, userdata);
return;
}
2017-11-23 18:32:51 +00:00
if (mIsMapped) {
mDevice->HandleError("Buffer already mapped");
callback(NXT_BUFFER_MAP_READ_STATUS_ERROR, nullptr, userdata);
return;
}
// TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes.
2017-11-24 18:59:42 +00:00
mMapReadSerial++;
2017-11-23 18:32:51 +00:00
mMapReadCallback = callback;
mMapReadUserdata = userdata;
MapReadAsyncImpl(mMapReadSerial, start, size);
mIsMapped = true;
}
void BufferBase::Unmap() {
2017-11-23 18:32:51 +00:00
if (!mIsMapped) {
mDevice->HandleError("Buffer wasn't mapped");
return;
}
// A map request can only be called once, so this will fire only if the request wasn't
// completed before the Unmap
2017-11-23 18:32:51 +00:00
CallMapReadCallback(mMapReadSerial, NXT_BUFFER_MAP_READ_STATUS_UNKNOWN, nullptr);
UnmapImpl();
2017-11-23 18:32:51 +00:00
mIsMapped = false;
}
bool BufferBase::IsFrozen() const {
2017-11-23 18:32:51 +00:00
return mIsFrozen;
}
bool BufferBase::HasFrozenUsage(nxt::BufferUsageBit usage) const {
2017-11-23 18:32:51 +00:00
return mIsFrozen && (usage & mAllowedUsage);
}
bool BufferBase::IsUsagePossible(nxt::BufferUsageBit allowedUsage, nxt::BufferUsageBit usage) {
const nxt::BufferUsageBit allReadBits =
2017-11-24 18:59:42 +00:00
nxt::BufferUsageBit::MapRead | nxt::BufferUsageBit::TransferSrc |
nxt::BufferUsageBit::Index | nxt::BufferUsageBit::Vertex | nxt::BufferUsageBit::Uniform;
bool allowed = (usage & allowedUsage) == usage;
bool readOnly = (usage & allReadBits) == usage;
bool singleUse = nxt::HasZeroOrOneBits(usage);
return allowed && (readOnly || singleUse);
}
bool BufferBase::IsTransitionPossible(nxt::BufferUsageBit usage) const {
2017-11-23 18:32:51 +00:00
if (mIsFrozen || mIsMapped) {
return false;
}
2017-11-23 18:32:51 +00:00
return IsUsagePossible(mAllowedUsage, usage);
}
void BufferBase::UpdateUsageInternal(nxt::BufferUsageBit usage) {
ASSERT(IsTransitionPossible(usage));
2017-11-23 18:32:51 +00:00
mCurrentUsage = usage;
}
void BufferBase::TransitionUsage(nxt::BufferUsageBit usage) {
if (!IsTransitionPossible(usage)) {
2017-11-23 18:32:51 +00:00
mDevice->HandleError("Buffer frozen or usage not allowed");
return;
}
2017-11-23 18:32:51 +00:00
TransitionUsageImpl(mCurrentUsage, usage);
mCurrentUsage = usage;
}
void BufferBase::FreezeUsage(nxt::BufferUsageBit usage) {
if (!IsTransitionPossible(usage)) {
2017-11-23 18:32:51 +00:00
mDevice->HandleError("Buffer frozen or usage not allowed");
return;
}
2017-11-23 18:32:51 +00:00
mAllowedUsage = usage;
TransitionUsageImpl(mCurrentUsage, usage);
mCurrentUsage = usage;
mIsFrozen = true;
}
// BufferBuilder
enum BufferSetProperties {
BUFFER_PROPERTY_ALLOWED_USAGE = 0x1,
BUFFER_PROPERTY_INITIAL_USAGE = 0x2,
BUFFER_PROPERTY_SIZE = 0x4,
};
BufferBuilder::BufferBuilder(DeviceBase* device) : Builder(device) {
}
BufferBase* BufferBuilder::GetResultImpl() {
constexpr int allProperties = BUFFER_PROPERTY_ALLOWED_USAGE | BUFFER_PROPERTY_SIZE;
2017-11-23 18:32:51 +00:00
if ((mPropertiesSet & allProperties) != allProperties) {
HandleError("Buffer missing properties");
return nullptr;
}
2017-11-24 18:59:42 +00:00
const nxt::BufferUsageBit kMapWriteAllowedUsages =
nxt::BufferUsageBit::MapWrite | nxt::BufferUsageBit::TransferSrc;
2017-11-23 18:32:51 +00:00
if (mAllowedUsage & nxt::BufferUsageBit::MapWrite &&
(mAllowedUsage & kMapWriteAllowedUsages) != mAllowedUsage) {
HandleError("Only TransferSrc is allowed with MapWrite");
return nullptr;
}
2017-11-24 18:59:42 +00:00
const nxt::BufferUsageBit kMapReadAllowedUsages =
nxt::BufferUsageBit::MapRead | nxt::BufferUsageBit::TransferDst;
2017-11-23 18:32:51 +00:00
if (mAllowedUsage & nxt::BufferUsageBit::MapRead &&
(mAllowedUsage & kMapReadAllowedUsages) != mAllowedUsage) {
HandleError("Only TransferDst is allowed with MapRead");
return nullptr;
}
2017-11-23 18:32:51 +00:00
if (!BufferBase::IsUsagePossible(mAllowedUsage, mCurrentUsage)) {
HandleError("Initial buffer usage is not allowed");
return nullptr;
}
2017-11-23 18:32:51 +00:00
return mDevice->CreateBuffer(this);
}
void BufferBuilder::SetAllowedUsage(nxt::BufferUsageBit usage) {
2017-11-23 18:32:51 +00:00
if ((mPropertiesSet & BUFFER_PROPERTY_ALLOWED_USAGE) != 0) {
HandleError("Buffer allowedUsage property set multiple times");
return;
}
2017-11-23 18:32:51 +00:00
mAllowedUsage = usage;
mPropertiesSet |= BUFFER_PROPERTY_ALLOWED_USAGE;
}
void BufferBuilder::SetInitialUsage(nxt::BufferUsageBit usage) {
2017-11-23 18:32:51 +00:00
if ((mPropertiesSet & BUFFER_PROPERTY_INITIAL_USAGE) != 0) {
HandleError("Buffer initialUsage property set multiple times");
return;
}
2017-11-23 18:32:51 +00:00
mCurrentUsage = usage;
mPropertiesSet |= BUFFER_PROPERTY_INITIAL_USAGE;
}
void BufferBuilder::SetSize(uint32_t size) {
2017-11-23 18:32:51 +00:00
if ((mPropertiesSet & BUFFER_PROPERTY_SIZE) != 0) {
HandleError("Buffer size property set multiple times");
return;
}
2017-11-23 18:32:51 +00:00
mSize = size;
mPropertiesSet |= BUFFER_PROPERTY_SIZE;
}
// BufferViewBase
BufferViewBase::BufferViewBase(BufferViewBuilder* builder)
2017-11-23 18:32:51 +00:00
: mBuffer(std::move(builder->mBuffer)), mSize(builder->mSize), mOffset(builder->mOffset) {
}
BufferBase* BufferViewBase::GetBuffer() {
2017-11-23 18:32:51 +00:00
return mBuffer.Get();
}
uint32_t BufferViewBase::GetSize() const {
2017-11-23 18:32:51 +00:00
return mSize;
}
uint32_t BufferViewBase::GetOffset() const {
2017-11-23 18:32:51 +00:00
return mOffset;
}
// BufferViewBuilder
enum BufferViewSetProperties {
BUFFER_VIEW_PROPERTY_EXTENT = 0x1,
};
BufferViewBuilder::BufferViewBuilder(DeviceBase* device, BufferBase* buffer)
2017-11-23 18:32:51 +00:00
: Builder(device), mBuffer(buffer) {
}
BufferViewBase* BufferViewBuilder::GetResultImpl() {
constexpr int allProperties = BUFFER_VIEW_PROPERTY_EXTENT;
2017-11-23 18:32:51 +00:00
if ((mPropertiesSet & allProperties) != allProperties) {
HandleError("Buffer view missing properties");
return nullptr;
}
2017-11-23 18:32:51 +00:00
return mDevice->CreateBufferView(this);
}
void BufferViewBuilder::SetExtent(uint32_t offset, uint32_t size) {
2017-11-23 18:32:51 +00:00
if ((mPropertiesSet & BUFFER_VIEW_PROPERTY_EXTENT) != 0) {
HandleError("Buffer view extent property set multiple times");
return;
}
uint64_t viewEnd = static_cast<uint64_t>(offset) + static_cast<uint64_t>(size);
2017-11-23 18:32:51 +00:00
if (viewEnd > static_cast<uint64_t>(mBuffer->GetSize())) {
HandleError("Buffer view end is OOB");
return;
}
2017-11-23 18:32:51 +00:00
mOffset = offset;
mSize = size;
mPropertiesSet |= BUFFER_VIEW_PROPERTY_EXTENT;
}
2017-11-24 18:59:42 +00:00
} // namespace backend