2017-04-20 18:38:20 +00:00
|
|
|
// 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.
|
|
|
|
|
2017-07-06 18:41:13 +00:00
|
|
|
#include "backend/Buffer.h"
|
2017-04-20 18:38:20 +00:00
|
|
|
|
2017-07-06 18:41:13 +00:00
|
|
|
#include "backend/Device.h"
|
2017-07-10 17:46:05 +00:00
|
|
|
#include "common/Assert.h"
|
2017-04-20 18:38:20 +00:00
|
|
|
|
|
|
|
#include <cstdio>
|
2017-11-24 18:59:42 +00:00
|
|
|
#include <utility>
|
2017-04-20 18:38:20 +00:00
|
|
|
|
|
|
|
namespace backend {
|
|
|
|
|
|
|
|
// Buffer
|
|
|
|
|
|
|
|
BufferBase::BufferBase(BufferBuilder* builder)
|
2018-07-09 13:15:07 +00:00
|
|
|
: mDevice(builder->mDevice), mSize(builder->mSize), mAllowedUsage(builder->mAllowedUsage) {
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2017-06-09 14:51:29 +00:00
|
|
|
BufferBase::~BufferBase() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (mIsMapped) {
|
2018-03-21 00:56:39 +00:00
|
|
|
CallMapReadCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
|
|
|
|
CallMapWriteCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
|
2017-06-09 14:51:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 18:38:20 +00:00
|
|
|
BufferViewBuilder* BufferBase::CreateBufferViewBuilder() {
|
2017-11-23 18:32:51 +00:00
|
|
|
return new BufferViewBuilder(mDevice, this);
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2017-12-04 23:06:02 +00:00
|
|
|
DeviceBase* BufferBase::GetDevice() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mDevice;
|
2017-05-10 13:30:05 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 18:38:20 +00:00
|
|
|
uint32_t BufferBase::GetSize() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mSize;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nxt::BufferUsageBit BufferBase::GetAllowedUsage() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mAllowedUsage;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
void BufferBase::CallMapReadCallback(uint32_t serial,
|
2018-03-20 23:48:54 +00:00
|
|
|
nxtBufferMapAsyncStatus status,
|
2017-11-24 18:59:42 +00:00
|
|
|
const void* pointer) {
|
2018-03-21 00:56:39 +00:00
|
|
|
if (mMapReadCallback != nullptr && serial == mMapSerial) {
|
|
|
|
ASSERT(mMapWriteCallback == nullptr);
|
2018-03-20 23:11:41 +00:00
|
|
|
// 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;
|
2018-03-21 00:56:39 +00:00
|
|
|
callback(status, pointer, mMapUserdata);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BufferBase::CallMapWriteCallback(uint32_t serial,
|
|
|
|
nxtBufferMapAsyncStatus 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.
|
|
|
|
nxtBufferMapWriteCallback callback = mMapWriteCallback;
|
|
|
|
mMapWriteCallback = nullptr;
|
|
|
|
callback(status, pointer, mMapUserdata);
|
2017-06-09 14:51:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-04 16:07:02 +00:00
|
|
|
void BufferBase::SetSubData(uint32_t start, uint32_t count, const uint8_t* data) {
|
|
|
|
if (start + count > GetSize()) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mDevice->HandleError("Buffer subdata out of range");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-09 13:15:07 +00:00
|
|
|
if (!(mAllowedUsage & nxt::BufferUsageBit::TransferDst)) {
|
2017-11-23 18:32:51 +00:00
|
|
|
mDevice->HandleError("Buffer needs the transfer dst usage bit");
|
2017-04-20 18:38:20 +00:00
|
|
|
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) {
|
2018-03-21 00:56:39 +00:00
|
|
|
if (!ValidateMapBase(start, size, nxt::BufferUsageBit::MapRead)) {
|
2018-03-20 23:48:54 +00:00
|
|
|
callback(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata);
|
2017-06-09 14:51:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-21 00:56:39 +00:00
|
|
|
ASSERT(mMapWriteCallback == nullptr);
|
2017-06-09 14:51:29 +00:00
|
|
|
|
2018-03-21 00:56:39 +00:00
|
|
|
// 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,
|
|
|
|
nxtBufferMapWriteCallback callback,
|
|
|
|
nxtCallbackUserdata userdata) {
|
|
|
|
if (!ValidateMapBase(start, size, nxt::BufferUsageBit::MapWrite)) {
|
2018-03-20 23:48:54 +00:00
|
|
|
callback(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata);
|
2017-06-09 14:51:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-03-21 00:56:39 +00:00
|
|
|
ASSERT(mMapReadCallback == nullptr);
|
|
|
|
|
2017-06-09 14:51:29 +00:00
|
|
|
// TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes.
|
2018-03-21 00:56:39 +00:00
|
|
|
mMapSerial++;
|
|
|
|
mMapWriteCallback = callback;
|
|
|
|
mMapUserdata = userdata;
|
2017-11-23 18:32:51 +00:00
|
|
|
mIsMapped = true;
|
2018-03-21 00:56:39 +00:00
|
|
|
|
|
|
|
MapWriteAsyncImpl(mMapSerial, start, size);
|
2017-06-09 14:51:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BufferBase::Unmap() {
|
2017-11-23 18:32:51 +00:00
|
|
|
if (!mIsMapped) {
|
|
|
|
mDevice->HandleError("Buffer wasn't mapped");
|
2017-06-09 14:51:29 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A map request can only be called once, so this will fire only if the request wasn't
|
|
|
|
// completed before the Unmap
|
2018-03-21 00:56:39 +00:00
|
|
|
CallMapReadCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
|
|
|
|
CallMapWriteCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
|
2017-06-09 14:51:29 +00:00
|
|
|
UnmapImpl();
|
2017-11-23 18:32:51 +00:00
|
|
|
mIsMapped = false;
|
2018-03-21 00:56:39 +00:00
|
|
|
mMapReadCallback = nullptr;
|
|
|
|
mMapWriteCallback = nullptr;
|
|
|
|
mMapUserdata = 0;
|
2017-06-09 14:51:29 +00:00
|
|
|
}
|
|
|
|
|
2018-07-09 13:15:07 +00:00
|
|
|
bool BufferBase::ValidateMapBase(uint32_t start,
|
|
|
|
uint32_t size,
|
|
|
|
nxt::BufferUsageBit requiredUsage) {
|
|
|
|
// TODO(cwallez@chromium.org): check for overflows.
|
|
|
|
if (start + size > GetSize()) {
|
|
|
|
mDevice->HandleError("Buffer map read out of range");
|
2017-04-20 18:38:20 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-07-09 13:15:07 +00:00
|
|
|
if (mIsMapped) {
|
|
|
|
mDevice->HandleError("Buffer already mapped");
|
|
|
|
return false;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2018-07-09 13:15:07 +00:00
|
|
|
if (!(mAllowedUsage & requiredUsage)) {
|
|
|
|
mDevice->HandleError("Buffer needs the correct map usage bit");
|
|
|
|
return false;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
2018-07-09 13:15:07 +00:00
|
|
|
|
|
|
|
return true;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BufferBuilder
|
|
|
|
|
|
|
|
enum BufferSetProperties {
|
|
|
|
BUFFER_PROPERTY_ALLOWED_USAGE = 0x1,
|
2018-07-09 13:15:07 +00:00
|
|
|
BUFFER_PROPERTY_SIZE = 0x2,
|
2017-04-20 18:38:20 +00:00
|
|
|
};
|
|
|
|
|
2017-05-08 08:52:11 +00:00
|
|
|
BufferBuilder::BufferBuilder(DeviceBase* device) : Builder(device) {
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 13:17:44 +00:00
|
|
|
BufferBase* BufferBuilder::GetResultImpl() {
|
2017-04-20 18:38:20 +00:00
|
|
|
constexpr int allProperties = BUFFER_PROPERTY_ALLOWED_USAGE | BUFFER_PROPERTY_SIZE;
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((mPropertiesSet & allProperties) != allProperties) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer missing properties");
|
2017-04-20 18:38:20 +00:00
|
|
|
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) {
|
2017-06-16 19:35:33 +00:00
|
|
|
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) {
|
2017-06-16 19:35:33 +00:00
|
|
|
HandleError("Only TransferDst is allowed with MapRead");
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
return mDevice->CreateBuffer(this);
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BufferBuilder::SetAllowedUsage(nxt::BufferUsageBit usage) {
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((mPropertiesSet & BUFFER_PROPERTY_ALLOWED_USAGE) != 0) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer allowedUsage property set multiple times");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mAllowedUsage = usage;
|
|
|
|
mPropertiesSet |= BUFFER_PROPERTY_ALLOWED_USAGE;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BufferBuilder::SetSize(uint32_t size) {
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((mPropertiesSet & BUFFER_PROPERTY_SIZE) != 0) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer size property set multiple times");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mSize = size;
|
|
|
|
mPropertiesSet |= BUFFER_PROPERTY_SIZE;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// BufferViewBase
|
|
|
|
|
|
|
|
BufferViewBase::BufferViewBase(BufferViewBuilder* builder)
|
2017-11-23 18:32:51 +00:00
|
|
|
: mBuffer(std::move(builder->mBuffer)), mSize(builder->mSize), mOffset(builder->mOffset) {
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BufferBase* BufferViewBase::GetBuffer() {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mBuffer.Get();
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t BufferViewBase::GetSize() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mSize;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t BufferViewBase::GetOffset() const {
|
2017-11-23 18:32:51 +00:00
|
|
|
return mOffset;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2017-05-08 13:17:44 +00:00
|
|
|
BufferViewBase* BufferViewBuilder::GetResultImpl() {
|
2017-04-20 18:38:20 +00:00
|
|
|
constexpr int allProperties = BUFFER_VIEW_PROPERTY_EXTENT;
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((mPropertiesSet & allProperties) != allProperties) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer view missing properties");
|
2017-04-20 18:38:20 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
return mDevice->CreateBufferView(this);
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void BufferViewBuilder::SetExtent(uint32_t offset, uint32_t size) {
|
2017-11-23 18:32:51 +00:00
|
|
|
if ((mPropertiesSet & BUFFER_VIEW_PROPERTY_EXTENT) != 0) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer view extent property set multiple times");
|
2017-04-20 18:38:20 +00:00
|
|
|
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())) {
|
2017-05-08 08:52:11 +00:00
|
|
|
HandleError("Buffer view end is OOB");
|
2017-04-20 18:38:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-23 18:32:51 +00:00
|
|
|
mOffset = offset;
|
|
|
|
mSize = size;
|
|
|
|
mPropertiesSet |= BUFFER_VIEW_PROPERTY_EXTENT;
|
2017-04-20 18:38:20 +00:00
|
|
|
}
|
|
|
|
|
2017-11-24 18:59:42 +00:00
|
|
|
} // namespace backend
|