Implement MapWrite except in the wire.

Also this MapWrite doesn't zero out memory yet.
This commit is contained in:
Corentin Wallez 2018-03-20 20:56:39 -04:00 committed by Corentin Wallez
parent 8565e0056a
commit cc0a54dbdb
25 changed files with 521 additions and 102 deletions

View File

@ -38,6 +38,7 @@ typedef uint64_t nxtCallbackUserdata;
typedef void (*nxtDeviceErrorCallback)(const char* message, nxtCallbackUserdata userdata); typedef void (*nxtDeviceErrorCallback)(const char* message, nxtCallbackUserdata userdata);
typedef void (*nxtBuilderErrorCallback)(nxtBuilderErrorStatus status, const char* message, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2); typedef void (*nxtBuilderErrorCallback)(nxtBuilderErrorStatus status, const char* message, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2);
typedef void (*nxtBufferMapReadCallback)(nxtBufferMapAsyncStatus status, const void* data, nxtCallbackUserdata userdata); typedef void (*nxtBufferMapReadCallback)(nxtBufferMapAsyncStatus status, const void* data, nxtCallbackUserdata userdata);
typedef void (*nxtBufferMapWriteCallback)(nxtBufferMapAsyncStatus status, void* data, nxtCallbackUserdata userdata);
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {

View File

@ -64,6 +64,14 @@ void ProcTableAsClass::BufferMapReadAsync(nxtBuffer self, uint32_t start, uint32
OnBufferMapReadAsyncCallback(self, start, size, callback, userdata); OnBufferMapReadAsyncCallback(self, start, size, callback, userdata);
} }
void ProcTableAsClass::BufferMapWriteAsync(nxtBuffer self, uint32_t start, uint32_t size, nxtBufferMapWriteCallback callback, nxtCallbackUserdata userdata) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(self);
object->mapWriteCallback = callback;
object->userdata1 = userdata;
OnBufferMapWriteAsyncCallback(self, start, size, callback, userdata);
}
void ProcTableAsClass::CallDeviceErrorCallback(nxtDevice device, const char* message) { void ProcTableAsClass::CallDeviceErrorCallback(nxtDevice device, const char* message) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(device); auto object = reinterpret_cast<ProcTableAsClass::Object*>(device);
object->deviceErrorCallback(message, object->userdata1); object->deviceErrorCallback(message, object->userdata1);
@ -77,6 +85,11 @@ void ProcTableAsClass::CallMapReadCallback(nxtBuffer buffer, nxtBufferMapAsyncSt
object->mapReadCallback(status, data, object->userdata1); object->mapReadCallback(status, data, object->userdata1);
} }
void ProcTableAsClass::CallMapWriteCallback(nxtBuffer buffer, nxtBufferMapAsyncStatus status, void* data) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(buffer);
object->mapWriteCallback(status, data, object->userdata1);
}
{% for type in by_category["object"] if type.is_builder %} {% for type in by_category["object"] if type.is_builder %}
void ProcTableAsClass::{{as_MethodSuffix(type.name, Name("set error callback"))}}({{as_cType(type.name)}} self, nxtBuilderErrorCallback callback, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2) { void ProcTableAsClass::{{as_MethodSuffix(type.name, Name("set error callback"))}}({{as_cType(type.name)}} self, nxtBuilderErrorCallback callback, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2) {
auto object = reinterpret_cast<ProcTableAsClass::Object*>(self); auto object = reinterpret_cast<ProcTableAsClass::Object*>(self);

View File

@ -58,22 +58,27 @@ class ProcTableAsClass {
// Stores callback and userdata and calls the On* methods // Stores callback and userdata and calls the On* methods
void DeviceSetErrorCallback(nxtDevice self, nxtDeviceErrorCallback callback, nxtCallbackUserdata userdata); void DeviceSetErrorCallback(nxtDevice self, nxtDeviceErrorCallback callback, nxtCallbackUserdata userdata);
void BufferMapReadAsync(nxtBuffer self, uint32_t start, uint32_t size, nxtBufferMapReadCallback callback, nxtCallbackUserdata userdata); void BufferMapReadAsync(nxtBuffer self, uint32_t start, uint32_t size, nxtBufferMapReadCallback callback, nxtCallbackUserdata userdata);
void BufferMapWriteAsync(nxtBuffer self, uint32_t start, uint32_t size, nxtBufferMapWriteCallback callback, nxtCallbackUserdata userdata);
// Special cased mockable methods // Special cased mockable methods
virtual void OnDeviceSetErrorCallback(nxtDevice device, nxtDeviceErrorCallback callback, nxtCallbackUserdata userdata) = 0; virtual void OnDeviceSetErrorCallback(nxtDevice device, nxtDeviceErrorCallback callback, nxtCallbackUserdata userdata) = 0;
virtual void OnBuilderSetErrorCallback(nxtBufferBuilder builder, nxtBuilderErrorCallback callback, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2) = 0; virtual void OnBuilderSetErrorCallback(nxtBufferBuilder builder, nxtBuilderErrorCallback callback, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2) = 0;
virtual void OnBufferMapReadAsyncCallback(nxtBuffer buffer, uint32_t start, uint32_t size, nxtBufferMapReadCallback callback, nxtCallbackUserdata userdata) = 0; virtual void OnBufferMapReadAsyncCallback(nxtBuffer buffer, uint32_t start, uint32_t size, nxtBufferMapReadCallback callback, nxtCallbackUserdata userdata) = 0;
virtual void OnBufferMapWriteAsyncCallback(nxtBuffer buffer, uint32_t start, uint32_t size, nxtBufferMapWriteCallback callback, nxtCallbackUserdata userdata) = 0;
// Calls the stored callbacks // Calls the stored callbacks
void CallDeviceErrorCallback(nxtDevice device, const char* message); void CallDeviceErrorCallback(nxtDevice device, const char* message);
void CallBuilderErrorCallback(void* builder , nxtBuilderErrorStatus status, const char* message); void CallBuilderErrorCallback(void* builder , nxtBuilderErrorStatus status, const char* message);
void CallMapReadCallback(nxtBuffer buffer, nxtBufferMapAsyncStatus status, const void* data); void CallMapReadCallback(nxtBuffer buffer, nxtBufferMapAsyncStatus status, const void* data);
void CallMapWriteCallback(nxtBuffer buffer, nxtBufferMapAsyncStatus status, void* data);
struct Object { struct Object {
ProcTableAsClass* procs = nullptr; ProcTableAsClass* procs = nullptr;
nxtDeviceErrorCallback deviceErrorCallback = nullptr; nxtDeviceErrorCallback deviceErrorCallback = nullptr;
nxtBuilderErrorCallback builderErrorCallback = nullptr; nxtBuilderErrorCallback builderErrorCallback = nullptr;
nxtBufferMapReadCallback mapReadCallback = nullptr; nxtBufferMapReadCallback mapReadCallback = nullptr;
nxtBufferMapWriteCallback mapWriteCallback = nullptr;
nxtCallbackUserdata userdata1 = 0; nxtCallbackUserdata userdata1 = 0;
nxtCallbackUserdata userdata2 = 0; nxtCallbackUserdata userdata2 = 0;
}; };
@ -104,6 +109,7 @@ class MockProcTable : public ProcTableAsClass {
MOCK_METHOD3(OnDeviceSetErrorCallback, void(nxtDevice device, nxtDeviceErrorCallback callback, nxtCallbackUserdata userdata)); MOCK_METHOD3(OnDeviceSetErrorCallback, void(nxtDevice device, nxtDeviceErrorCallback callback, nxtCallbackUserdata userdata));
MOCK_METHOD4(OnBuilderSetErrorCallback, void(nxtBufferBuilder builder, nxtBuilderErrorCallback callback, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2)); MOCK_METHOD4(OnBuilderSetErrorCallback, void(nxtBufferBuilder builder, nxtBuilderErrorCallback callback, nxtCallbackUserdata userdata1, nxtCallbackUserdata userdata2));
MOCK_METHOD5(OnBufferMapReadAsyncCallback, void(nxtBuffer buffer, uint32_t start, uint32_t size, nxtBufferMapReadCallback callback, nxtCallbackUserdata userdata)); MOCK_METHOD5(OnBufferMapReadAsyncCallback, void(nxtBuffer buffer, uint32_t start, uint32_t size, nxtBufferMapReadCallback callback, nxtCallbackUserdata userdata));
MOCK_METHOD5(OnBufferMapWriteAsyncCallback, void(nxtBuffer buffer, uint32_t start, uint32_t size, nxtBufferMapWriteCallback callback, nxtCallbackUserdata userdata));
}; };
#endif // MOCK_NXT_H #endif // MOCK_NXT_H

View File

@ -352,6 +352,11 @@ namespace wire {
*allocCmd = cmd; *allocCmd = cmd;
} }
void ClientBufferMapWriteAsync(Buffer*, uint32_t, uint32_t, nxtBufferMapWriteCallback, nxtCallbackUserdata) {
// TODO(cwallez@chromium.org): Implement the wire for BufferMapWriteAsync
ASSERT(false);
}
void ProxyClientBufferUnmap(Buffer* buffer) { void ProxyClientBufferUnmap(Buffer* buffer) {
//* Invalidate the local pointer, and cancel all other in-flight requests that would turn into //* Invalidate the local pointer, and cancel all other in-flight requests that would turn into
//* errors anyway (you can't double map). This prevents race when the following happens, where //* errors anyway (you can't double map). This prevents race when the following happens, where

View File

@ -210,6 +210,16 @@
{"name": "userdata", "type": "callback userdata"} {"name": "userdata", "type": "callback userdata"}
] ]
}, },
{
"_comment": "Contrary to set sub data, this is in char size",
"name": "map write async",
"args": [
{"name": "start", "type": "uint32_t"},
{"name": "size", "type": "uint32_t"},
{"name": "callback", "type": "buffer map write callback"},
{"name": "userdata", "type": "callback userdata"}
]
},
{ {
"name": "unmap" "name": "unmap"
}, },
@ -257,6 +267,9 @@
"buffer map read callback": { "buffer map read callback": {
"category": "natively defined" "category": "natively defined"
}, },
"buffer map write callback": {
"category": "natively defined"
},
"buffer map async status": { "buffer map async status": {
"category": "enum", "category": "enum",
"values": [ "values": [

View File

@ -33,7 +33,8 @@ namespace backend {
BufferBase::~BufferBase() { BufferBase::~BufferBase() {
if (mIsMapped) { if (mIsMapped) {
CallMapReadCallback(mMapReadSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); CallMapReadCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
CallMapWriteCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
} }
} }
@ -60,12 +61,26 @@ namespace backend {
void BufferBase::CallMapReadCallback(uint32_t serial, void BufferBase::CallMapReadCallback(uint32_t serial,
nxtBufferMapAsyncStatus status, nxtBufferMapAsyncStatus status,
const void* pointer) { const void* pointer) {
if (mMapReadCallback && serial == mMapReadSerial) { if (mMapReadCallback != nullptr && serial == mMapSerial) {
ASSERT(mMapWriteCallback == nullptr);
// Tag the callback as fired before firing it, otherwise it could fire a second time if // 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. // for example buffer.Unmap() is called inside the application-provided callback.
nxtBufferMapReadCallback callback = mMapReadCallback; nxtBufferMapReadCallback callback = mMapReadCallback;
mMapReadCallback = nullptr; mMapReadCallback = nullptr;
callback(status, pointer, mMapReadUserdata); 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);
} }
} }
@ -87,30 +102,40 @@ namespace backend {
uint32_t size, uint32_t size,
nxtBufferMapReadCallback callback, nxtBufferMapReadCallback callback,
nxtCallbackUserdata userdata) { nxtCallbackUserdata userdata) {
if (start + size > GetSize()) { if (!ValidateMapBase(start, size, nxt::BufferUsageBit::MapRead)) {
mDevice->HandleError("Buffer map read out of range");
callback(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata); callback(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata);
return; return;
} }
if (!(mCurrentUsage & nxt::BufferUsageBit::MapRead)) { ASSERT(mMapWriteCallback == nullptr);
mDevice->HandleError("Buffer needs the map read usage bit");
callback(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata);
return;
}
if (mIsMapped) {
mDevice->HandleError("Buffer already mapped");
callback(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata);
return;
}
// TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes. // TODO(cwallez@chromium.org): what to do on wraparound? Could cause crashes.
mMapReadSerial++; mMapSerial++;
mMapReadCallback = callback; mMapReadCallback = callback;
mMapReadUserdata = userdata; mMapUserdata = userdata;
MapReadAsyncImpl(mMapReadSerial, start, size);
mIsMapped = true; 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)) {
callback(NXT_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() { void BufferBase::Unmap() {
@ -121,9 +146,13 @@ namespace backend {
// A map request can only be called once, so this will fire only if the request wasn't // A map request can only be called once, so this will fire only if the request wasn't
// completed before the Unmap // completed before the Unmap
CallMapReadCallback(mMapReadSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr); CallMapReadCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
CallMapWriteCallback(mMapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr);
UnmapImpl(); UnmapImpl();
mIsMapped = false; mIsMapped = false;
mMapReadCallback = nullptr;
mMapWriteCallback = nullptr;
mMapUserdata = 0;
} }
bool BufferBase::IsFrozen() const { bool BufferBase::IsFrozen() const {
@ -248,6 +277,27 @@ namespace backend {
mPropertiesSet |= BUFFER_PROPERTY_SIZE; mPropertiesSet |= BUFFER_PROPERTY_SIZE;
} }
bool BufferBase::ValidateMapBase(uint32_t start,
uint32_t size,
nxt::BufferUsageBit requiredUsage) {
if (start + size > GetSize()) {
mDevice->HandleError("Buffer map read out of range");
return false;
}
if (mIsMapped) {
mDevice->HandleError("Buffer already mapped");
return false;
}
if (!(mCurrentUsage & requiredUsage)) {
mDevice->HandleError("Buffer needs the correct map usage bit");
return false;
}
return true;
}
// BufferViewBase // BufferViewBase
BufferViewBase::BufferViewBase(BufferViewBuilder* builder) BufferViewBase::BufferViewBase(BufferViewBuilder* builder)

View File

@ -46,6 +46,10 @@ namespace backend {
uint32_t size, uint32_t size,
nxtBufferMapReadCallback callback, nxtBufferMapReadCallback callback,
nxtCallbackUserdata userdata); nxtCallbackUserdata userdata);
void MapWriteAsync(uint32_t start,
uint32_t size,
nxtBufferMapWriteCallback callback,
nxtCallbackUserdata userdata);
void Unmap(); void Unmap();
void TransitionUsage(nxt::BufferUsageBit usage); void TransitionUsage(nxt::BufferUsageBit usage);
void FreezeUsage(nxt::BufferUsageBit usage); void FreezeUsage(nxt::BufferUsageBit usage);
@ -54,22 +58,27 @@ namespace backend {
void CallMapReadCallback(uint32_t serial, void CallMapReadCallback(uint32_t serial,
nxtBufferMapAsyncStatus status, nxtBufferMapAsyncStatus status,
const void* pointer); const void* pointer);
void CallMapWriteCallback(uint32_t serial, nxtBufferMapAsyncStatus status, void* pointer);
private: private:
virtual void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) = 0; virtual void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) = 0;
virtual void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t size) = 0; virtual void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t size) = 0;
virtual void MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t size) = 0;
virtual void UnmapImpl() = 0; virtual void UnmapImpl() = 0;
virtual void TransitionUsageImpl(nxt::BufferUsageBit currentUsage, virtual void TransitionUsageImpl(nxt::BufferUsageBit currentUsage,
nxt::BufferUsageBit targetUsage) = 0; nxt::BufferUsageBit targetUsage) = 0;
bool ValidateMapBase(uint32_t start, uint32_t size, nxt::BufferUsageBit requiredUsage);
DeviceBase* mDevice; DeviceBase* mDevice;
uint32_t mSize; uint32_t mSize;
nxt::BufferUsageBit mAllowedUsage = nxt::BufferUsageBit::None; nxt::BufferUsageBit mAllowedUsage = nxt::BufferUsageBit::None;
nxt::BufferUsageBit mCurrentUsage = nxt::BufferUsageBit::None; nxt::BufferUsageBit mCurrentUsage = nxt::BufferUsageBit::None;
nxtBufferMapReadCallback mMapReadCallback = nullptr; nxtBufferMapReadCallback mMapReadCallback = nullptr;
nxtCallbackUserdata mMapReadUserdata = 0; nxtBufferMapWriteCallback mMapWriteCallback = nullptr;
uint32_t mMapReadSerial = 0; nxtCallbackUserdata mMapUserdata = 0;
uint32_t mMapSerial = 0;
bool mIsFrozen = false; bool mIsFrozen = false;
bool mIsMapped = false; bool mIsMapped = false;

View File

@ -144,8 +144,12 @@ namespace backend { namespace d3d12 {
return mResource->GetGPUVirtualAddress(); return mResource->GetGPUVirtualAddress();
} }
void Buffer::OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data) { void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite) {
CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data); if (isWrite) {
CallMapWriteCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
} else {
CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
}
} }
void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) { void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) {
@ -158,8 +162,17 @@ namespace backend { namespace d3d12 {
char* data = nullptr; char* data = nullptr;
ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast<void**>(&data))); ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadRequestTracker(); MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
tracker->Track(this, serial, data + start); tracker->Track(this, serial, data + start, false);
}
void Buffer::MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
D3D12_RANGE readRange = {start, start + count};
char* data = nullptr;
ASSERT_SUCCESS(mResource->Map(0, &readRange, reinterpret_cast<void**>(&data)));
MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
tracker->Track(this, serial, data + start, true);
} }
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {
@ -204,25 +217,27 @@ namespace backend { namespace d3d12 {
return mUavDesc; return mUavDesc;
} }
MapReadRequestTracker::MapReadRequestTracker(Device* device) : mDevice(device) { MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
} }
MapReadRequestTracker::~MapReadRequestTracker() { MapRequestTracker::~MapRequestTracker() {
ASSERT(mInflightRequests.Empty()); ASSERT(mInflightRequests.Empty());
} }
void MapReadRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, const void* data) { void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) {
Request request; Request request;
request.buffer = buffer; request.buffer = buffer;
request.mapSerial = mapSerial; request.mapSerial = mapSerial;
request.data = data; request.data = data;
request.isWrite = isWrite;
mInflightRequests.Enqueue(std::move(request), mDevice->GetSerial()); mInflightRequests.Enqueue(std::move(request), mDevice->GetSerial());
} }
void MapReadRequestTracker::Tick(Serial finishedSerial) { void MapRequestTracker::Tick(Serial finishedSerial) {
for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data); request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.data,
request.isWrite);
} }
mInflightRequests.ClearUpTo(finishedSerial); mInflightRequests.ClearUpTo(finishedSerial);
} }

View File

@ -35,7 +35,7 @@ namespace backend { namespace d3d12 {
bool GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage, bool GetResourceTransitionBarrier(nxt::BufferUsageBit currentUsage,
nxt::BufferUsageBit targetUsage, nxt::BufferUsageBit targetUsage,
D3D12_RESOURCE_BARRIER* barrier); D3D12_RESOURCE_BARRIER* barrier);
void OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data); void OnMapCommandSerialFinished(uint32_t mapSerial, void* data, bool isWrite);
private: private:
Device* mDevice; Device* mDevice;
@ -44,6 +44,7 @@ namespace backend { namespace d3d12 {
// NXT API // NXT API
void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override; void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override;
void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override; void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void UnmapImpl() override; void UnmapImpl() override;
void TransitionUsageImpl(nxt::BufferUsageBit currentUsage, void TransitionUsageImpl(nxt::BufferUsageBit currentUsage,
nxt::BufferUsageBit targetUsage) override; nxt::BufferUsageBit targetUsage) override;
@ -62,12 +63,12 @@ namespace backend { namespace d3d12 {
D3D12_UNORDERED_ACCESS_VIEW_DESC mUavDesc; D3D12_UNORDERED_ACCESS_VIEW_DESC mUavDesc;
}; };
class MapReadRequestTracker { class MapRequestTracker {
public: public:
MapReadRequestTracker(Device* device); MapRequestTracker(Device* device);
~MapReadRequestTracker(); ~MapRequestTracker();
void Track(Buffer* buffer, uint32_t mapSerial, const void* data); void Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite);
void Tick(Serial finishedSerial); void Tick(Serial finishedSerial);
private: private:
@ -76,7 +77,8 @@ namespace backend { namespace d3d12 {
struct Request { struct Request {
Ref<Buffer> buffer; Ref<Buffer> buffer;
uint32_t mapSerial; uint32_t mapSerial;
const void* data; void* data;
bool isWrite;
}; };
SerialQueue<Request> mInflightRequests; SerialQueue<Request> mInflightRequests;
}; };

View File

@ -137,7 +137,7 @@ namespace backend { namespace d3d12 {
// Initialize backend services // Initialize backend services
mCommandAllocatorManager = new CommandAllocatorManager(this); mCommandAllocatorManager = new CommandAllocatorManager(this);
mDescriptorHeapAllocator = new DescriptorHeapAllocator(this); mDescriptorHeapAllocator = new DescriptorHeapAllocator(this);
mMapReadRequestTracker = new MapReadRequestTracker(this); mMapRequestTracker = new MapRequestTracker(this);
mResourceAllocator = new ResourceAllocator(this); mResourceAllocator = new ResourceAllocator(this);
mResourceUploader = new ResourceUploader(this); mResourceUploader = new ResourceUploader(this);
@ -153,7 +153,7 @@ namespace backend { namespace d3d12 {
delete mCommandAllocatorManager; delete mCommandAllocatorManager;
delete mDescriptorHeapAllocator; delete mDescriptorHeapAllocator;
delete mMapReadRequestTracker; delete mMapRequestTracker;
delete mResourceAllocator; delete mResourceAllocator;
delete mResourceUploader; delete mResourceUploader;
} }
@ -174,8 +174,8 @@ namespace backend { namespace d3d12 {
return mDescriptorHeapAllocator; return mDescriptorHeapAllocator;
} }
MapReadRequestTracker* Device::GetMapReadRequestTracker() const { MapRequestTracker* Device::GetMapRequestTracker() const {
return mMapReadRequestTracker; return mMapRequestTracker;
} }
ResourceAllocator* Device::GetResourceAllocator() { ResourceAllocator* Device::GetResourceAllocator() {
@ -215,7 +215,7 @@ namespace backend { namespace d3d12 {
mResourceAllocator->Tick(lastCompletedSerial); mResourceAllocator->Tick(lastCompletedSerial);
mCommandAllocatorManager->Tick(lastCompletedSerial); mCommandAllocatorManager->Tick(lastCompletedSerial);
mDescriptorHeapAllocator->Tick(lastCompletedSerial); mDescriptorHeapAllocator->Tick(lastCompletedSerial);
mMapReadRequestTracker->Tick(lastCompletedSerial); mMapRequestTracker->Tick(lastCompletedSerial);
mUsedComObjectRefs.ClearUpTo(lastCompletedSerial); mUsedComObjectRefs.ClearUpTo(lastCompletedSerial);
ExecuteCommandLists({}); ExecuteCommandLists({});
NextSerial(); NextSerial();

View File

@ -49,7 +49,7 @@ namespace backend { namespace d3d12 {
class CommandAllocatorManager; class CommandAllocatorManager;
class DescriptorHeapAllocator; class DescriptorHeapAllocator;
class MapReadRequestTracker; class MapRequestTracker;
class ResourceAllocator; class ResourceAllocator;
class ResourceUploader; class ResourceUploader;
@ -116,7 +116,7 @@ namespace backend { namespace d3d12 {
ComPtr<ID3D12CommandQueue> GetCommandQueue(); ComPtr<ID3D12CommandQueue> GetCommandQueue();
DescriptorHeapAllocator* GetDescriptorHeapAllocator(); DescriptorHeapAllocator* GetDescriptorHeapAllocator();
MapReadRequestTracker* GetMapReadRequestTracker() const; MapRequestTracker* GetMapRequestTracker() const;
ResourceAllocator* GetResourceAllocator(); ResourceAllocator* GetResourceAllocator();
ResourceUploader* GetResourceUploader(); ResourceUploader* GetResourceUploader();
@ -143,7 +143,7 @@ namespace backend { namespace d3d12 {
CommandAllocatorManager* mCommandAllocatorManager = nullptr; CommandAllocatorManager* mCommandAllocatorManager = nullptr;
DescriptorHeapAllocator* mDescriptorHeapAllocator = nullptr; DescriptorHeapAllocator* mDescriptorHeapAllocator = nullptr;
MapReadRequestTracker* mMapReadRequestTracker = nullptr; MapRequestTracker* mMapRequestTracker = nullptr;
ResourceAllocator* mResourceAllocator = nullptr; ResourceAllocator* mResourceAllocator = nullptr;
ResourceUploader* mResourceUploader = nullptr; ResourceUploader* mResourceUploader = nullptr;

View File

@ -31,11 +31,12 @@ namespace backend { namespace metal {
id<MTLBuffer> GetMTLBuffer(); id<MTLBuffer> GetMTLBuffer();
void OnMapReadCommandSerialFinished(uint32_t mapSerial, uint32_t offset); void OnMapCommandSerialFinished(uint32_t mapSerial, uint32_t offset, bool isWrite);
private: private:
void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override; void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override;
void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override; void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void UnmapImpl() override; void UnmapImpl() override;
void TransitionUsageImpl(nxt::BufferUsageBit currentUsage, void TransitionUsageImpl(nxt::BufferUsageBit currentUsage,
nxt::BufferUsageBit targetUsage) override; nxt::BufferUsageBit targetUsage) override;
@ -48,12 +49,12 @@ namespace backend { namespace metal {
BufferView(BufferViewBuilder* builder); BufferView(BufferViewBuilder* builder);
}; };
class MapReadRequestTracker { class MapRequestTracker {
public: public:
MapReadRequestTracker(Device* device); MapRequestTracker(Device* device);
~MapReadRequestTracker(); ~MapRequestTracker();
void Track(Buffer* buffer, uint32_t mapSerial, uint32_t offset); void Track(Buffer* buffer, uint32_t mapSerial, uint32_t offset, bool isWrite);
void Tick(Serial finishedSerial); void Tick(Serial finishedSerial);
private: private:
@ -63,6 +64,7 @@ namespace backend { namespace metal {
Ref<Buffer> buffer; Ref<Buffer> buffer;
uint32_t mapSerial; uint32_t mapSerial;
uint32_t offset; uint32_t offset;
bool isWrite;
}; };
SerialQueue<Request> mInflightRequests; SerialQueue<Request> mInflightRequests;
}; };

View File

@ -40,9 +40,13 @@ namespace backend { namespace metal {
return mMtlBuffer; return mMtlBuffer;
} }
void Buffer::OnMapReadCommandSerialFinished(uint32_t mapSerial, uint32_t offset) { void Buffer::OnMapCommandSerialFinished(uint32_t mapSerial, uint32_t offset, bool isWrite) {
const char* data = reinterpret_cast<const char*>([mMtlBuffer contents]); char* data = reinterpret_cast<char*>([mMtlBuffer contents]);
CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data + offset); if (isWrite) {
CallMapWriteCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data + offset);
} else {
CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data + offset);
}
} }
void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) { void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) {
@ -52,8 +56,13 @@ namespace backend { namespace metal {
} }
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t) { void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t) {
MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadTracker(); MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapTracker();
tracker->Track(this, serial, start); tracker->Track(this, serial, start, false);
}
void Buffer::MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t) {
MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapTracker();
tracker->Track(this, serial, start, true);
} }
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {
@ -66,25 +75,30 @@ namespace backend { namespace metal {
BufferView::BufferView(BufferViewBuilder* builder) : BufferViewBase(builder) { BufferView::BufferView(BufferViewBuilder* builder) : BufferViewBase(builder) {
} }
MapReadRequestTracker::MapReadRequestTracker(Device* device) : mDevice(device) { MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
} }
MapReadRequestTracker::~MapReadRequestTracker() { MapRequestTracker::~MapRequestTracker() {
ASSERT(mInflightRequests.Empty()); ASSERT(mInflightRequests.Empty());
} }
void MapReadRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, uint32_t offset) { void MapRequestTracker::Track(Buffer* buffer,
uint32_t mapSerial,
uint32_t offset,
bool isWrite) {
Request request; Request request;
request.buffer = buffer; request.buffer = buffer;
request.mapSerial = mapSerial; request.mapSerial = mapSerial;
request.offset = offset; request.offset = offset;
request.isWrite = isWrite;
mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial()); mInflightRequests.Enqueue(std::move(request), mDevice->GetPendingCommandSerial());
} }
void MapReadRequestTracker::Tick(Serial finishedSerial) { void MapRequestTracker::Tick(Serial finishedSerial) {
for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.offset); request.buffer->OnMapCommandSerialFinished(request.mapSerial, request.offset,
request.isWrite);
} }
mInflightRequests.ClearUpTo(finishedSerial); mInflightRequests.ClearUpTo(finishedSerial);
} }

View File

@ -81,7 +81,7 @@ namespace backend { namespace metal {
return ToBackendBase<MetalBackendTraits>(common); return ToBackendBase<MetalBackendTraits>(common);
} }
class MapReadRequestTracker; class MapRequestTracker;
class ResourceUploader; class ResourceUploader;
class Device : public DeviceBase { class Device : public DeviceBase {
@ -117,7 +117,7 @@ namespace backend { namespace metal {
void SubmitPendingCommandBuffer(); void SubmitPendingCommandBuffer();
Serial GetPendingCommandSerial(); Serial GetPendingCommandSerial();
MapReadRequestTracker* GetMapReadTracker() const; MapRequestTracker* GetMapTracker() const;
ResourceUploader* GetResourceUploader() const; ResourceUploader* GetResourceUploader() const;
private: private:
@ -125,7 +125,7 @@ namespace backend { namespace metal {
id<MTLDevice> mMtlDevice = nil; id<MTLDevice> mMtlDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil; id<MTLCommandQueue> mCommandQueue = nil;
MapReadRequestTracker* mMapReadTracker; MapRequestTracker* mMapTracker;
ResourceUploader* mResourceUploader; ResourceUploader* mResourceUploader;
Serial mFinishedCommandSerial = 0; Serial mFinishedCommandSerial = 0;

View File

@ -45,7 +45,7 @@ namespace backend { namespace metal {
Device::Device(id<MTLDevice> mtlDevice) Device::Device(id<MTLDevice> mtlDevice)
: mMtlDevice(mtlDevice), : mMtlDevice(mtlDevice),
mMapReadTracker(new MapReadRequestTracker(this)), mMapTracker(new MapRequestTracker(this)),
mResourceUploader(new ResourceUploader(this)) { mResourceUploader(new ResourceUploader(this)) {
[mMtlDevice retain]; [mMtlDevice retain];
mCommandQueue = [mMtlDevice newCommandQueue]; mCommandQueue = [mMtlDevice newCommandQueue];
@ -65,8 +65,8 @@ namespace backend { namespace metal {
[mPendingCommands release]; [mPendingCommands release];
mPendingCommands = nil; mPendingCommands = nil;
delete mMapReadTracker; delete mMapTracker;
mMapReadTracker = nullptr; mMapTracker = nullptr;
delete mResourceUploader; delete mResourceUploader;
mResourceUploader = nullptr; mResourceUploader = nullptr;
@ -138,7 +138,7 @@ namespace backend { namespace metal {
void Device::TickImpl() { void Device::TickImpl() {
mResourceUploader->Tick(mFinishedCommandSerial); mResourceUploader->Tick(mFinishedCommandSerial);
mMapReadTracker->Tick(mFinishedCommandSerial); mMapTracker->Tick(mFinishedCommandSerial);
// Code above might have added GPU work, submit it. This also makes sure // Code above might have added GPU work, submit it. This also makes sure
// that even when no GPU work is happening, the serial number keeps incrementing. // that even when no GPU work is happening, the serial number keeps incrementing.
@ -186,8 +186,8 @@ namespace backend { namespace metal {
return mPendingCommandSerial; return mPendingCommandSerial;
} }
MapReadRequestTracker* Device::GetMapReadTracker() const { MapRequestTracker* Device::GetMapTracker() const {
return mMapReadTracker; return mMapTracker;
} }
ResourceUploader* Device::GetResourceUploader() const { ResourceUploader* Device::GetResourceUploader() const {

View File

@ -113,12 +113,13 @@ namespace backend { namespace null {
struct BufferMapReadOperation : PendingOperation { struct BufferMapReadOperation : PendingOperation {
virtual void Execute() { virtual void Execute() {
buffer->MapReadOperationCompleted(serial, ptr); buffer->MapReadOperationCompleted(serial, ptr, isWrite);
} }
Ref<Buffer> buffer; Ref<Buffer> buffer;
const void* ptr; void* ptr;
uint32_t serial; uint32_t serial;
bool isWrite;
}; };
Buffer::Buffer(BufferBuilder* builder) : BufferBase(builder) { Buffer::Buffer(BufferBuilder* builder) : BufferBase(builder) {
@ -131,8 +132,12 @@ namespace backend { namespace null {
Buffer::~Buffer() { Buffer::~Buffer() {
} }
void Buffer::MapReadOperationCompleted(uint32_t serial, const void* ptr) { void Buffer::MapReadOperationCompleted(uint32_t serial, void* ptr, bool isWrite) {
CallMapReadCallback(serial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, ptr); if (isWrite) {
CallMapWriteCallback(serial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, ptr);
} else {
CallMapReadCallback(serial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, ptr);
}
} }
void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) { void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) {
@ -142,6 +147,14 @@ namespace backend { namespace null {
} }
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) { void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
MapAsyncImplCommon(serial, start, count, false);
}
void Buffer::MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
MapAsyncImplCommon(serial, start, count, true);
}
void Buffer::MapAsyncImplCommon(uint32_t serial, uint32_t start, uint32_t count, bool isWrite) {
ASSERT(start + count <= GetSize()); ASSERT(start + count <= GetSize());
ASSERT(mBackingData); ASSERT(mBackingData);
@ -149,6 +162,7 @@ namespace backend { namespace null {
operation->buffer = this; operation->buffer = this;
operation->ptr = mBackingData.get() + start; operation->ptr = mBackingData.get() + start;
operation->serial = serial; operation->serial = serial;
operation->isWrite = isWrite;
ToBackend(GetDevice())->AddPendingOperation(std::unique_ptr<PendingOperation>(operation)); ToBackend(GetDevice())->AddPendingOperation(std::unique_ptr<PendingOperation>(operation));
} }

View File

@ -132,15 +132,18 @@ namespace backend { namespace null {
Buffer(BufferBuilder* builder); Buffer(BufferBuilder* builder);
~Buffer(); ~Buffer();
void MapReadOperationCompleted(uint32_t serial, const void* ptr); void MapReadOperationCompleted(uint32_t serial, void* ptr, bool isWrite);
private: private:
void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override; void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override;
void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override; void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void UnmapImpl() override; void UnmapImpl() override;
void TransitionUsageImpl(nxt::BufferUsageBit currentUsage, void TransitionUsageImpl(nxt::BufferUsageBit currentUsage,
nxt::BufferUsageBit targetUsage) override; nxt::BufferUsageBit targetUsage) override;
void MapAsyncImplCommon(uint32_t serial, uint32_t start, uint32_t count, bool isWrite);
std::unique_ptr<char[]> mBackingData; std::unique_ptr<char[]> mBackingData;
}; };

View File

@ -38,13 +38,19 @@ namespace backend { namespace opengl {
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) { void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
// TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high // TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high
// version of OpenGL that would let us map the buffer unsynchronized. // version of OpenGL that would let us map the buffer unsynchronized.
// TODO(cwallez@chromium.org): this crashes on Mac NVIDIA, use GetBufferSubData there
// instead?
glBindBuffer(GL_ARRAY_BUFFER, mBuffer); glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
void* data = glMapBufferRange(GL_ARRAY_BUFFER, start, count, GL_MAP_READ_BIT); void* data = glMapBufferRange(GL_ARRAY_BUFFER, start, count, GL_MAP_READ_BIT);
CallMapReadCallback(serial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data); CallMapReadCallback(serial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
} }
void Buffer::MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) {
// TODO(cwallez@chromium.org): this does GPU->CPU synchronization, we could require a high
// version of OpenGL that would let us map the buffer unsynchronized.
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
void* data = glMapBufferRange(GL_ARRAY_BUFFER, start, count, GL_MAP_WRITE_BIT);
CallMapWriteCallback(serial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
}
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {
glBindBuffer(GL_ARRAY_BUFFER, mBuffer); glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glUnmapBuffer(GL_ARRAY_BUFFER); glUnmapBuffer(GL_ARRAY_BUFFER);

View File

@ -32,6 +32,7 @@ namespace backend { namespace opengl {
private: private:
void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override; void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override;
void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override; void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void UnmapImpl() override; void UnmapImpl() override;
void TransitionUsageImpl(nxt::BufferUsageBit currentUsage, void TransitionUsageImpl(nxt::BufferUsageBit currentUsage,
nxt::BufferUsageBit targetUsage) override; nxt::BufferUsageBit targetUsage) override;

View File

@ -154,6 +154,10 @@ namespace backend { namespace vulkan {
CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data); CallMapReadCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
} }
void Buffer::OnMapWriteCommandSerialFinished(uint32_t mapSerial, void* data) {
CallMapWriteCallback(mapSerial, NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data);
}
VkBuffer Buffer::GetHandle() const { VkBuffer Buffer::GetHandle() const {
return mHandle; return mHandle;
} }
@ -186,11 +190,19 @@ namespace backend { namespace vulkan {
} }
void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t /*count*/) { void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t /*count*/) {
const uint8_t* memory = mMemoryAllocation.GetMappedPointer(); uint8_t* memory = mMemoryAllocation.GetMappedPointer();
ASSERT(memory != nullptr); ASSERT(memory != nullptr);
MapReadRequestTracker* tracker = ToBackend(GetDevice())->GetMapReadRequestTracker(); MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
tracker->Track(this, serial, memory + start); tracker->Track(this, serial, memory + start, false);
}
void Buffer::MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t /*count*/) {
uint8_t* memory = mMemoryAllocation.GetMappedPointer();
ASSERT(memory != nullptr);
MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
tracker->Track(this, serial, memory + start, true);
} }
void Buffer::UnmapImpl() { void Buffer::UnmapImpl() {
@ -203,25 +215,30 @@ namespace backend { namespace vulkan {
RecordBarrier(commands, currentUsage, targetUsage); RecordBarrier(commands, currentUsage, targetUsage);
} }
MapReadRequestTracker::MapReadRequestTracker(Device* device) : mDevice(device) { MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) {
} }
MapReadRequestTracker::~MapReadRequestTracker() { MapRequestTracker::~MapRequestTracker() {
ASSERT(mInflightRequests.Empty()); ASSERT(mInflightRequests.Empty());
} }
void MapReadRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, const void* data) { void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) {
Request request; Request request;
request.buffer = buffer; request.buffer = buffer;
request.mapSerial = mapSerial; request.mapSerial = mapSerial;
request.data = data; request.data = data;
request.isWrite = isWrite;
mInflightRequests.Enqueue(std::move(request), mDevice->GetSerial()); mInflightRequests.Enqueue(std::move(request), mDevice->GetSerial());
} }
void MapReadRequestTracker::Tick(Serial finishedSerial) { void MapRequestTracker::Tick(Serial finishedSerial) {
for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) {
request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data); if (request.isWrite) {
request.buffer->OnMapWriteCommandSerialFinished(request.mapSerial, request.data);
} else {
request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data);
}
} }
mInflightRequests.ClearUpTo(finishedSerial); mInflightRequests.ClearUpTo(finishedSerial);
} }

View File

@ -31,6 +31,7 @@ namespace backend { namespace vulkan {
~Buffer(); ~Buffer();
void OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data); void OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data);
void OnMapWriteCommandSerialFinished(uint32_t mapSerial, void* data);
VkBuffer GetHandle() const; VkBuffer GetHandle() const;
@ -41,6 +42,7 @@ namespace backend { namespace vulkan {
private: private:
void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override; void SetSubDataImpl(uint32_t start, uint32_t count, const uint32_t* data) override;
void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override; void MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t count) override;
void UnmapImpl() override; void UnmapImpl() override;
void TransitionUsageImpl(nxt::BufferUsageBit currentUsage, void TransitionUsageImpl(nxt::BufferUsageBit currentUsage,
nxt::BufferUsageBit targetUsage) override; nxt::BufferUsageBit targetUsage) override;
@ -49,12 +51,12 @@ namespace backend { namespace vulkan {
DeviceMemoryAllocation mMemoryAllocation; DeviceMemoryAllocation mMemoryAllocation;
}; };
class MapReadRequestTracker { class MapRequestTracker {
public: public:
MapReadRequestTracker(Device* device); MapRequestTracker(Device* device);
~MapReadRequestTracker(); ~MapRequestTracker();
void Track(Buffer* buffer, uint32_t mapSerial, const void* data); void Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite);
void Tick(Serial finishedSerial); void Tick(Serial finishedSerial);
private: private:
@ -63,7 +65,8 @@ namespace backend { namespace vulkan {
struct Request { struct Request {
Ref<Buffer> buffer; Ref<Buffer> buffer;
uint32_t mapSerial; uint32_t mapSerial;
const void* data; void* data;
bool isWrite;
}; };
SerialQueue<Request> mInflightRequests; SerialQueue<Request> mInflightRequests;
}; };

View File

@ -142,7 +142,7 @@ namespace backend { namespace vulkan {
mBufferUploader = new BufferUploader(this); mBufferUploader = new BufferUploader(this);
mDeleter = new FencedDeleter(this); mDeleter = new FencedDeleter(this);
mMapReadRequestTracker = new MapReadRequestTracker(this); mMapRequestTracker = new MapRequestTracker(this);
mMemoryAllocator = new MemoryAllocator(this); mMemoryAllocator = new MemoryAllocator(this);
} }
@ -181,8 +181,8 @@ namespace backend { namespace vulkan {
delete mDeleter; delete mDeleter;
mDeleter = nullptr; mDeleter = nullptr;
delete mMapReadRequestTracker; delete mMapRequestTracker;
mMapReadRequestTracker = nullptr; mMapRequestTracker = nullptr;
delete mMemoryAllocator; delete mMemoryAllocator;
mMemoryAllocator = nullptr; mMemoryAllocator = nullptr;
@ -267,7 +267,7 @@ namespace backend { namespace vulkan {
CheckPassedFences(); CheckPassedFences();
RecycleCompletedCommands(); RecycleCompletedCommands();
mMapReadRequestTracker->Tick(mCompletedSerial); mMapRequestTracker->Tick(mCompletedSerial);
mBufferUploader->Tick(mCompletedSerial); mBufferUploader->Tick(mCompletedSerial);
mMemoryAllocator->Tick(mCompletedSerial); mMemoryAllocator->Tick(mCompletedSerial);
@ -307,8 +307,8 @@ namespace backend { namespace vulkan {
return mQueue; return mQueue;
} }
MapReadRequestTracker* Device::GetMapReadRequestTracker() const { MapRequestTracker* Device::GetMapRequestTracker() const {
return mMapReadRequestTracker; return mMapRequestTracker;
} }
MemoryAllocator* Device::GetMemoryAllocator() const { MemoryAllocator* Device::GetMemoryAllocator() const {

View File

@ -55,7 +55,7 @@ namespace backend { namespace vulkan {
class BufferUploader; class BufferUploader;
class FencedDeleter; class FencedDeleter;
class MapReadRequestTracker; class MapRequestTracker;
class MemoryAllocator; class MemoryAllocator;
struct VulkanBackendTraits { struct VulkanBackendTraits {
@ -103,7 +103,7 @@ namespace backend { namespace vulkan {
BufferUploader* GetBufferUploader() const; BufferUploader* GetBufferUploader() const;
FencedDeleter* GetFencedDeleter() const; FencedDeleter* GetFencedDeleter() const;
MapReadRequestTracker* GetMapReadRequestTracker() const; MapRequestTracker* GetMapRequestTracker() const;
MemoryAllocator* GetMemoryAllocator() const; MemoryAllocator* GetMemoryAllocator() const;
Serial GetSerial() const; Serial GetSerial() const;
@ -170,7 +170,7 @@ namespace backend { namespace vulkan {
BufferUploader* mBufferUploader = nullptr; BufferUploader* mBufferUploader = nullptr;
FencedDeleter* mDeleter = nullptr; FencedDeleter* mDeleter = nullptr;
MapReadRequestTracker* mMapReadRequestTracker = nullptr; MapRequestTracker* mMapRequestTracker = nullptr;
MemoryAllocator* mMemoryAllocator = nullptr; MemoryAllocator* mMemoryAllocator = nullptr;
VkFence GetUnusedFence(); VkFence GetUnusedFence();

View File

@ -102,6 +102,86 @@ TEST_P(BufferMapReadTests, LargeRead) {
NXT_INSTANTIATE_TEST(BufferMapReadTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend) NXT_INSTANTIATE_TEST(BufferMapReadTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)
class BufferMapWriteTests : public NXTTest {
protected:
static void MapWriteCallback(nxtBufferMapAsyncStatus status, void* data, nxtCallbackUserdata userdata) {
ASSERT_EQ(NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, status);
ASSERT_NE(nullptr, data);
auto test = reinterpret_cast<BufferMapWriteTests*>(static_cast<uintptr_t>(userdata));
test->mappedData = data;
}
void* MapWriteAsyncAndWait(const nxt::Buffer& buffer, uint32_t start, uint32_t offset) {
buffer.MapWriteAsync(start, offset, MapWriteCallback, static_cast<nxt::CallbackUserdata>(reinterpret_cast<uintptr_t>(this)));
while (mappedData == nullptr) {
WaitABit();
}
return mappedData;
}
private:
void* mappedData = nullptr;
};
// Test that the simplest map write (one u32 at offset 0) works.
TEST_P(BufferMapWriteTests, SmallWriteAtZero) {
nxt::Buffer buffer = device.CreateBufferBuilder()
.SetSize(4)
.SetAllowedUsage(nxt::BufferUsageBit::MapWrite | nxt::BufferUsageBit::TransferSrc)
.SetInitialUsage(nxt::BufferUsageBit::MapWrite)
.GetResult();
uint32_t myData = 2934875;
void* mappedData = MapWriteAsyncAndWait(buffer, 0, 4);
memcpy(mappedData, &myData, sizeof(myData));
buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
}
// Test mapping a buffer at an offset.
TEST_P(BufferMapWriteTests, SmallWriteAtOffset) {
nxt::Buffer buffer = device.CreateBufferBuilder()
.SetSize(4000)
.SetAllowedUsage(nxt::BufferUsageBit::MapWrite | nxt::BufferUsageBit::TransferSrc)
.SetInitialUsage(nxt::BufferUsageBit::MapWrite)
.GetResult();
uint32_t myData = 2934875;
void* mappedData = MapWriteAsyncAndWait(buffer, 2048, 4);
memcpy(mappedData, &myData, sizeof(myData));
buffer.Unmap();
EXPECT_BUFFER_U32_EQ(myData, buffer, 2048);
}
// Test mapping large ranges of a buffer.
TEST_P(BufferMapWriteTests, LargeWrite) {
constexpr uint32_t kDataSize = 1000 * 1000;
std::vector<uint32_t> myData;
for (uint32_t i = 0; i < kDataSize; ++i) {
myData.push_back(i);
}
nxt::Buffer buffer = device.CreateBufferBuilder()
.SetSize(static_cast<uint32_t>(kDataSize * sizeof(uint32_t)))
.SetAllowedUsage(nxt::BufferUsageBit::MapWrite | nxt::BufferUsageBit::TransferSrc)
.SetInitialUsage(nxt::BufferUsageBit::MapWrite)
.GetResult();
void* mappedData = MapWriteAsyncAndWait(buffer, 0, kDataSize * sizeof(uint32_t));
memcpy(mappedData, myData.data(), kDataSize * sizeof(uint32_t));
buffer.Unmap();
EXPECT_BUFFER_U32_RANGE_EQ(myData.data(), buffer, 0, kDataSize);
}
NXT_INSTANTIATE_TEST(BufferMapWriteTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)
class BufferSetSubDataTests : public NXTTest { class BufferSetSubDataTests : public NXTTest {
}; };

View File

@ -29,6 +29,17 @@ static void ToMockBufferMapReadCallback(nxtBufferMapAsyncStatus status, const vo
mockBufferMapReadCallback->Call(status, reinterpret_cast<const uint32_t*>(ptr), userdata); mockBufferMapReadCallback->Call(status, reinterpret_cast<const uint32_t*>(ptr), userdata);
} }
class MockBufferMapWriteCallback {
public:
MOCK_METHOD3(Call, void(nxtBufferMapAsyncStatus status, uint32_t* ptr, nxtCallbackUserdata userdata));
};
static MockBufferMapWriteCallback* mockBufferMapWriteCallback = nullptr;
static void ToMockBufferMapWriteCallback(nxtBufferMapAsyncStatus status, void* ptr, nxtCallbackUserdata userdata) {
// Assume the data is uint32_t to make writing matchers easier
mockBufferMapWriteCallback->Call(status, reinterpret_cast<uint32_t*>(ptr), userdata);
}
class BufferValidationTest : public ValidationTest { class BufferValidationTest : public ValidationTest {
protected: protected:
nxt::Buffer CreateMapReadBuffer(uint32_t size) { nxt::Buffer CreateMapReadBuffer(uint32_t size) {
@ -38,6 +49,13 @@ class BufferValidationTest : public ValidationTest {
.SetInitialUsage(nxt::BufferUsageBit::MapRead) .SetInitialUsage(nxt::BufferUsageBit::MapRead)
.GetResult(); .GetResult();
} }
nxt::Buffer CreateMapWriteBuffer(uint32_t size) {
return device.CreateBufferBuilder()
.SetSize(size)
.SetAllowedUsage(nxt::BufferUsageBit::MapWrite)
.SetInitialUsage(nxt::BufferUsageBit::MapWrite)
.GetResult();
}
nxt::Buffer CreateSetSubDataBuffer(uint32_t size) { nxt::Buffer CreateSetSubDataBuffer(uint32_t size) {
return device.CreateBufferBuilder() return device.CreateBufferBuilder()
.SetSize(size) .SetSize(size)
@ -53,11 +71,13 @@ class BufferValidationTest : public ValidationTest {
ValidationTest::SetUp(); ValidationTest::SetUp();
mockBufferMapReadCallback = new MockBufferMapReadCallback; mockBufferMapReadCallback = new MockBufferMapReadCallback;
mockBufferMapWriteCallback = new MockBufferMapWriteCallback;
queue = device.CreateQueueBuilder().GetResult(); queue = device.CreateQueueBuilder().GetResult();
} }
void TearDown() override { void TearDown() override {
delete mockBufferMapReadCallback; delete mockBufferMapReadCallback;
delete mockBufferMapWriteCallback;
ValidationTest::TearDown(); ValidationTest::TearDown();
} }
@ -177,7 +197,7 @@ TEST_F(BufferValidationTest, CreationMapUsageRestrictions) {
} }
} }
// Test the success cause for mapping buffer for reading // Test the success case for mapping buffer for reading
TEST_F(BufferValidationTest, MapReadSuccess) { TEST_F(BufferValidationTest, MapReadSuccess) {
nxt::Buffer buf = CreateMapReadBuffer(4); nxt::Buffer buf = CreateMapReadBuffer(4);
@ -191,6 +211,20 @@ TEST_F(BufferValidationTest, MapReadSuccess) {
buf.Unmap(); buf.Unmap();
} }
// Test the success case for mapping buffer for writing
TEST_F(BufferValidationTest, MapWriteSuccess) {
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata = 40598;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.Times(1);
queue.Submit(0, nullptr);
buf.Unmap();
}
// Test map reading out of range causes an error // Test map reading out of range causes an error
TEST_F(BufferValidationTest, MapReadOutOfRange) { TEST_F(BufferValidationTest, MapReadOutOfRange) {
nxt::Buffer buf = CreateMapReadBuffer(4); nxt::Buffer buf = CreateMapReadBuffer(4);
@ -202,6 +236,17 @@ TEST_F(BufferValidationTest, MapReadOutOfRange) {
ASSERT_DEVICE_ERROR(buf.MapReadAsync(0, 5, ToMockBufferMapReadCallback, userdata)); ASSERT_DEVICE_ERROR(buf.MapReadAsync(0, 5, ToMockBufferMapReadCallback, userdata));
} }
// Test map writing out of range causes an error
TEST_F(BufferValidationTest, MapWriteOutOfRange) {
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata = 40599;
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(0, 5, ToMockBufferMapWriteCallback, userdata));
}
// Test map reading a buffer with wrong current usage // Test map reading a buffer with wrong current usage
TEST_F(BufferValidationTest, MapReadWrongUsage) { TEST_F(BufferValidationTest, MapReadWrongUsage) {
nxt::Buffer buf = device.CreateBufferBuilder() nxt::Buffer buf = device.CreateBufferBuilder()
@ -217,6 +262,21 @@ TEST_F(BufferValidationTest, MapReadWrongUsage) {
ASSERT_DEVICE_ERROR(buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata)); ASSERT_DEVICE_ERROR(buf.MapReadAsync(0, 4, ToMockBufferMapReadCallback, userdata));
} }
// Test map writing a buffer with wrong current usage
TEST_F(BufferValidationTest, MapWriteWrongUsage) {
nxt::Buffer buf = device.CreateBufferBuilder()
.SetSize(4)
.SetAllowedUsage(nxt::BufferUsageBit::MapWrite | nxt::BufferUsageBit::TransferSrc)
.SetInitialUsage(nxt::BufferUsageBit::TransferSrc)
.GetResult();
nxt::CallbackUserdata userdata = 40600;
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata));
}
// Test map reading a buffer that is already mapped // Test map reading a buffer that is already mapped
TEST_F(BufferValidationTest, MapReadAlreadyMapped) { TEST_F(BufferValidationTest, MapReadAlreadyMapped) {
nxt::Buffer buf = CreateMapReadBuffer(4); nxt::Buffer buf = CreateMapReadBuffer(4);
@ -234,7 +294,25 @@ TEST_F(BufferValidationTest, MapReadAlreadyMapped) {
queue.Submit(0, nullptr); queue.Submit(0, nullptr);
} }
// Test unmapping before having the result gives UNKNOWN // Test map writing a buffer that is already mapped
TEST_F(BufferValidationTest, MapWriteAlreadyMapped) {
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata1 = 40601;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata1);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata1))
.Times(1);
nxt::CallbackUserdata userdata2 = 40602;
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata2))
.Times(1);
ASSERT_DEVICE_ERROR(buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata2));
queue.Submit(0, nullptr);
}
// TODO(cwallez@chromium.org) Test a MapWrite and already MapRead and vice-versa
// Test unmapping before having the result gives UNKNOWN - for reading
TEST_F(BufferValidationTest, MapReadUnmapBeforeResult) { TEST_F(BufferValidationTest, MapReadUnmapBeforeResult) {
nxt::Buffer buf = CreateMapReadBuffer(4); nxt::Buffer buf = CreateMapReadBuffer(4);
@ -250,7 +328,23 @@ TEST_F(BufferValidationTest, MapReadUnmapBeforeResult) {
queue.Submit(0, nullptr); queue.Submit(0, nullptr);
} }
// Test destroying the buffer before having the result gives UNKNOWN // Test unmapping before having the result gives UNKNOWN - for writing
TEST_F(BufferValidationTest, MapWriteUnmapBeforeResult) {
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata = 40603;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
buf.Unmap();
// Submitting the queue makes the null backend process map request, but the callback shouldn't
// be called again
queue.Submit(0, nullptr);
}
// Test destroying the buffer before having the result gives UNKNOWN - for reading
// TODO(cwallez@chromium.org) currently this doesn't work because the buffer doesn't know // TODO(cwallez@chromium.org) currently this doesn't work because the buffer doesn't know
// when its external ref count reaches 0. // when its external ref count reaches 0.
TEST_F(BufferValidationTest, DISABLED_MapReadDestroyBeforeResult) { TEST_F(BufferValidationTest, DISABLED_MapReadDestroyBeforeResult) {
@ -269,7 +363,26 @@ TEST_F(BufferValidationTest, DISABLED_MapReadDestroyBeforeResult) {
queue.Submit(0, nullptr); queue.Submit(0, nullptr);
} }
// When a request is cancelled with Unmap it might still be in flight, test doing a new request // Test destroying the buffer before having the result gives UNKNOWN - for writing
// TODO(cwallez@chromium.org) currently this doesn't work because the buffer doesn't know
// when its external ref count reaches 0.
TEST_F(BufferValidationTest, DISABLED_MapWriteDestroyBeforeResult) {
{
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata = 40604;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
}
// Submitting the queue makes the null backend process map request, but the callback shouldn't
// be called again
queue.Submit(0, nullptr);
}
// When a MapRead is cancelled with Unmap it might still be in flight, test doing a new request
// works as expected and we don't get the cancelled request's data. // works as expected and we don't get the cancelled request's data.
TEST_F(BufferValidationTest, MapReadUnmapBeforeResultThenMapAgain) { TEST_F(BufferValidationTest, MapReadUnmapBeforeResultThenMapAgain) {
nxt::Buffer buf = CreateMapReadBuffer(4); nxt::Buffer buf = CreateMapReadBuffer(4);
@ -289,6 +402,28 @@ TEST_F(BufferValidationTest, MapReadUnmapBeforeResultThenMapAgain) {
.Times(1); .Times(1);
queue.Submit(0, nullptr); queue.Submit(0, nullptr);
} }
// TODO(cwallez@chromium.org) Test a MapWrite and already MapRead and vice-versa
// When a MapWrite is cancelled with Unmap it might still be in flight, test doing a new request
// works as expected and we don't get the cancelled request's data.
TEST_F(BufferValidationTest, MapWriteUnmapBeforeResultThenMapAgain) {
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata = 40605;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
.Times(1);
buf.Unmap();
userdata ++;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.Times(1);
queue.Submit(0, nullptr);
}
// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback // Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
TEST_F(BufferValidationTest, UnmapInsideMapReadCallback) { TEST_F(BufferValidationTest, UnmapInsideMapReadCallback) {
@ -305,6 +440,21 @@ TEST_F(BufferValidationTest, UnmapInsideMapReadCallback) {
queue.Submit(0, nullptr); queue.Submit(0, nullptr);
} }
// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
TEST_F(BufferValidationTest, UnmapInsideMapWriteCallback) {
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata = 40678;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.WillOnce(InvokeWithoutArgs([&]() {
buf.Unmap();
}));
queue.Submit(0, nullptr);
}
// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the callback // Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the callback
TEST_F(BufferValidationTest, DestroyInsideMapReadCallback) { TEST_F(BufferValidationTest, DestroyInsideMapReadCallback) {
nxt::Buffer buf = CreateMapReadBuffer(4); nxt::Buffer buf = CreateMapReadBuffer(4);
@ -320,6 +470,21 @@ TEST_F(BufferValidationTest, DestroyInsideMapReadCallback) {
queue.Submit(0, nullptr); queue.Submit(0, nullptr);
} }
// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the callback
TEST_F(BufferValidationTest, DestroyInsideMapWriteCallback) {
nxt::Buffer buf = CreateMapWriteBuffer(4);
nxt::CallbackUserdata userdata = 40679;
buf.MapWriteAsync(0, 4, ToMockBufferMapWriteCallback, userdata);
EXPECT_CALL(*mockBufferMapWriteCallback, Call(NXT_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Ne(nullptr), userdata))
.WillOnce(InvokeWithoutArgs([&]() {
buf = nxt::Buffer();
}));
queue.Submit(0, nullptr);
}
// Test the success case for Buffer::SetSubData // Test the success case for Buffer::SetSubData
TEST_F(BufferValidationTest, SetSubDataSuccess) { TEST_F(BufferValidationTest, SetSubDataSuccess) {
nxt::Buffer buf = CreateSetSubDataBuffer(4); nxt::Buffer buf = CreateSetSubDataBuffer(4);