Implement timeline fences in Dawn
This change implements timeline fences in Dawn. It includes methods and descriptor members to eventually support multi-queue, but does not implement them. Bug: dawn:26 Change-Id: I81d5fee6acef402fe099227a034d9669a89ab6c3 Reviewed-on: https://dawn-review.googlesource.com/c/2460 Commit-Queue: Austin Eng <enga@chromium.org> Reviewed-by: Kai Ninomiya <kainino@chromium.org>
This commit is contained in:
parent
d758e32610
commit
f0b761f116
6
BUILD.gn
6
BUILD.gn
|
@ -390,6 +390,10 @@ source_set("libdawn_native_sources") {
|
|||
"src/dawn_native/Error.h",
|
||||
"src/dawn_native/ErrorData.cpp",
|
||||
"src/dawn_native/ErrorData.h",
|
||||
"src/dawn_native/Fence.cpp",
|
||||
"src/dawn_native/Fence.h",
|
||||
"src/dawn_native/FenceSignalTracker.cpp",
|
||||
"src/dawn_native/FenceSignalTracker.h",
|
||||
"src/dawn_native/Forward.h",
|
||||
"src/dawn_native/InputState.cpp",
|
||||
"src/dawn_native/InputState.h",
|
||||
|
@ -828,6 +832,7 @@ test("dawn_unittests") {
|
|||
"src/tests/unittests/validation/CopyCommandsValidationTests.cpp",
|
||||
"src/tests/unittests/validation/DepthStencilStateValidationTests.cpp",
|
||||
"src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp",
|
||||
"src/tests/unittests/validation/FenceValidationTests.cpp",
|
||||
"src/tests/unittests/validation/InputStateValidationTests.cpp",
|
||||
"src/tests/unittests/validation/PushConstantsValidationTests.cpp",
|
||||
"src/tests/unittests/validation/QueueSubmitValidationTests.cpp",
|
||||
|
@ -877,6 +882,7 @@ test("dawn_end2end_tests") {
|
|||
"src/tests/end2end/CopyTests.cpp",
|
||||
"src/tests/end2end/DepthStencilStateTests.cpp",
|
||||
"src/tests/end2end/DrawElementsTests.cpp",
|
||||
"src/tests/end2end/FenceTests.cpp",
|
||||
"src/tests/end2end/IndexFormatTests.cpp",
|
||||
"src/tests/end2end/InputStateTests.cpp",
|
||||
"src/tests/end2end/PrimitiveTopologyTests.cpp",
|
||||
|
|
50
dawn.json
50
dawn.json
|
@ -481,6 +481,13 @@
|
|||
"name": "create depth stencil state builder",
|
||||
"returns": "depth stencil state builder"
|
||||
},
|
||||
{
|
||||
"name": "create fence",
|
||||
"returns": "fence",
|
||||
"args": [
|
||||
{"name": "descriptor", "type": "fence descriptor", "annotation": "const*"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "create render pass descriptor builder",
|
||||
"returns": "render pass descriptor builder"
|
||||
|
@ -609,6 +616,42 @@
|
|||
{"value": 3, "name": "both"}
|
||||
]
|
||||
},
|
||||
"fence": {
|
||||
"category": "object",
|
||||
"methods": [
|
||||
{
|
||||
"name": "get completed value",
|
||||
"returns": "uint64_t"
|
||||
},
|
||||
{
|
||||
"name": "on completion",
|
||||
"args": [
|
||||
{"name": "value", "type": "uint64_t"},
|
||||
{"name": "callback", "type": "fence on completion callback"},
|
||||
{"name": "userdata", "type": "callback userdata"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"fence on completion callback": {
|
||||
"category": "natively defined"
|
||||
},
|
||||
"fence completion status": {
|
||||
"category": "enum",
|
||||
"values": [
|
||||
{"value": 0, "name": "success"},
|
||||
{"value": 1, "name": "error"},
|
||||
{"value": 2, "name": "unknown"},
|
||||
{"value": 3, "name": "context lost"}
|
||||
]
|
||||
},
|
||||
"fence descriptor": {
|
||||
"category": "structure",
|
||||
"extensible": true,
|
||||
"members": [
|
||||
{"name": "initial value", "type": "uint64_t"}
|
||||
]
|
||||
},
|
||||
"filter mode": {
|
||||
"category": "enum",
|
||||
"values": [
|
||||
|
@ -707,6 +750,13 @@
|
|||
{"name": "num commands", "type": "uint32_t"},
|
||||
{"name": "commands", "type": "command buffer", "annotation": "const*", "length": "num commands"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "signal",
|
||||
"args": [
|
||||
{"name": "fence", "type": "fence"},
|
||||
{"name": "signal value", "type": "uint64_t"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -53,6 +53,8 @@ typedef void (*dawnDeviceErrorCallback)(const char* message, dawnCallbackUserdat
|
|||
typedef void (*dawnBuilderErrorCallback)(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2);
|
||||
typedef void (*dawnBufferMapReadCallback)(dawnBufferMapAsyncStatus status, const void* data, dawnCallbackUserdata userdata);
|
||||
typedef void (*dawnBufferMapWriteCallback)(dawnBufferMapAsyncStatus status, void* data, dawnCallbackUserdata userdata);
|
||||
typedef void (*dawnFenceOnCompletionCallback)(dawnFenceCompletionStatus status,
|
||||
dawnCallbackUserdata userdata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "dawn_wire/WireCmd.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "common/SerialMap.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
@ -68,6 +69,7 @@ namespace dawn_wire {
|
|||
{% set special_objects = [
|
||||
"device",
|
||||
"buffer",
|
||||
"fence",
|
||||
] %}
|
||||
{% for type in by_category["object"] if not type.name.canonical_case() in special_objects %}
|
||||
struct {{type.name.CamelCase()}} : ObjectBase {
|
||||
|
@ -118,6 +120,35 @@ namespace dawn_wire {
|
|||
bool isWriteMapped = false;
|
||||
};
|
||||
|
||||
struct Fence : ObjectBase {
|
||||
using ObjectBase::ObjectBase;
|
||||
|
||||
~Fence() {
|
||||
//* Callbacks need to be fired in all cases, as they can handle freeing resources
|
||||
//* so we call them with "Unknown" status.
|
||||
for (auto& request : requests.IterateAll()) {
|
||||
request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, request.userdata);
|
||||
}
|
||||
requests.Clear();
|
||||
}
|
||||
|
||||
void CheckPassedFences() {
|
||||
for (auto& request : requests.IterateUpTo(completedValue)) {
|
||||
request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS,
|
||||
request.userdata);
|
||||
}
|
||||
requests.ClearUpTo(completedValue);
|
||||
}
|
||||
|
||||
struct OnCompletionData {
|
||||
dawnFenceOnCompletionCallback completionCallback = nullptr;
|
||||
dawnCallbackUserdata userdata = 0;
|
||||
};
|
||||
uint64_t signaledValue = 0;
|
||||
uint64_t completedValue = 0;
|
||||
SerialMap<OnCompletionData> requests;
|
||||
};
|
||||
|
||||
//* TODO(cwallez@chromium.org): Do something with objects before they are destroyed ?
|
||||
//* - Call still uncalled builder callbacks
|
||||
template<typename T>
|
||||
|
@ -238,6 +269,8 @@ namespace dawn_wire {
|
|||
CommandSerializer* mSerializer = nullptr;
|
||||
};
|
||||
|
||||
{% set client_side_commands = ["FenceGetCompletedValue"] %}
|
||||
|
||||
//* Implementation of the client API functions.
|
||||
{% for type in by_category["object"] %}
|
||||
{% set Type = type.name.CamelCase() %}
|
||||
|
@ -245,50 +278,51 @@ namespace dawn_wire {
|
|||
|
||||
{% for method in type.methods %}
|
||||
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
||||
{% if Suffix not in client_side_commands %}
|
||||
{{as_cType(method.return_type.name)}} Client{{Suffix}}(
|
||||
{{-cType}} cSelf
|
||||
{%- for arg in method.arguments -%}
|
||||
, {{as_annotated_cType(arg)}}
|
||||
{%- endfor -%}
|
||||
) {
|
||||
auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
|
||||
Device* device = self->device;
|
||||
{{Suffix}}Cmd cmd;
|
||||
|
||||
{{as_cType(method.return_type.name)}} Client{{Suffix}}(
|
||||
{{-cType}} cSelf
|
||||
{%- for arg in method.arguments -%}
|
||||
, {{as_annotated_cType(arg)}}
|
||||
{%- endfor -%}
|
||||
) {
|
||||
auto self = reinterpret_cast<{{as_wireType(type)}}>(cSelf);
|
||||
Device* device = self->device;
|
||||
{{Suffix}}Cmd cmd;
|
||||
//* Create the structure going on the wire on the stack and fill it with the value
|
||||
//* arguments so it can compute its size.
|
||||
cmd.self = cSelf;
|
||||
|
||||
//* Create the structure going on the wire on the stack and fill it with the value
|
||||
//* arguments so it can compute its size.
|
||||
cmd.self = cSelf;
|
||||
//* For object creation, store the object ID the client will use for the result.
|
||||
{% if method.return_type.category == "object" %}
|
||||
auto* allocation = self->device->{{method.return_type.name.camelCase()}}.New();
|
||||
|
||||
//* For object creation, store the object ID the client will use for the result.
|
||||
{% if method.return_type.category == "object" %}
|
||||
auto* allocation = self->device->{{method.return_type.name.camelCase()}}.New();
|
||||
{% if type.is_builder %}
|
||||
//* We are in GetResult, so the callback that should be called is the
|
||||
//* currently set one. Copy it over to the created object and prevent the
|
||||
//* builder from calling the callback on destruction.
|
||||
allocation->object->builderCallback = self->builderCallback;
|
||||
self->builderCallback.canCall = false;
|
||||
{% endif %}
|
||||
|
||||
{% if type.is_builder %}
|
||||
//* We are in GetResult, so the callback that should be called is the
|
||||
//* currently set one. Copy it over to the created object and prevent the
|
||||
//* builder from calling the callback on destruction.
|
||||
allocation->object->builderCallback = self->builderCallback;
|
||||
self->builderCallback.canCall = false;
|
||||
cmd.resultId = allocation->object->id;
|
||||
cmd.resultSerial = allocation->serial;
|
||||
{% endif %}
|
||||
|
||||
cmd.resultId = allocation->object->id;
|
||||
cmd.resultSerial = allocation->serial;
|
||||
{% endif %}
|
||||
{% for arg in method.arguments %}
|
||||
cmd.{{as_varName(arg.name)}} = {{as_varName(arg.name)}};
|
||||
{% endfor %}
|
||||
|
||||
{% for arg in method.arguments %}
|
||||
cmd.{{as_varName(arg.name)}} = {{as_varName(arg.name)}};
|
||||
{% endfor %}
|
||||
//* Allocate space to send the command and copy the value args over.
|
||||
size_t requiredSize = cmd.GetRequiredSize();
|
||||
char* allocatedBuffer = static_cast<char*>(device->GetCmdSpace(requiredSize));
|
||||
cmd.Serialize(allocatedBuffer, *device);
|
||||
|
||||
//* Allocate space to send the command and copy the value args over.
|
||||
size_t requiredSize = cmd.GetRequiredSize();
|
||||
char* allocatedBuffer = static_cast<char*>(device->GetCmdSpace(requiredSize));
|
||||
cmd.Serialize(allocatedBuffer, *device);
|
||||
|
||||
{% if method.return_type.category == "object" %}
|
||||
return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get());
|
||||
{% endif %}
|
||||
}
|
||||
{% if method.return_type.category == "object" %}
|
||||
return reinterpret_cast<{{as_cType(method.return_type.name)}}>(allocation->object.get());
|
||||
{% endif %}
|
||||
}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if type.is_builder %}
|
||||
|
@ -379,6 +413,33 @@ namespace dawn_wire {
|
|||
*allocCmd = cmd;
|
||||
}
|
||||
|
||||
uint64_t ClientFenceGetCompletedValue(dawnFence cSelf) {
|
||||
auto fence = reinterpret_cast<Fence*>(cSelf);
|
||||
return fence->completedValue;
|
||||
}
|
||||
|
||||
void ClientFenceOnCompletion(dawnFence cFence,
|
||||
uint64_t value,
|
||||
dawnFenceOnCompletionCallback callback,
|
||||
dawnCallbackUserdata userdata) {
|
||||
Fence* fence = reinterpret_cast<Fence*>(cFence);
|
||||
if (value > fence->signaledValue) {
|
||||
fence->device->HandleError("Value greater than fence signaled value");
|
||||
callback(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value <= fence->completedValue) {
|
||||
callback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
Fence::OnCompletionData request;
|
||||
request.completionCallback = callback;
|
||||
request.userdata = userdata;
|
||||
fence->requests.Enqueue(std::move(request), value);
|
||||
}
|
||||
|
||||
void ProxyClientBufferUnmap(dawnBuffer cBuffer) {
|
||||
Buffer* buffer = reinterpret_cast<Buffer*>(cBuffer);
|
||||
|
||||
|
@ -412,6 +473,25 @@ namespace dawn_wire {
|
|||
ClientBufferUnmap(cBuffer);
|
||||
}
|
||||
|
||||
dawnFence ProxyClientDeviceCreateFence(dawnDevice cSelf,
|
||||
dawnFenceDescriptor const* descriptor) {
|
||||
dawnFence cFence = ClientDeviceCreateFence(cSelf, descriptor);
|
||||
Fence* fence = reinterpret_cast<Fence*>(cFence);
|
||||
fence->signaledValue = descriptor->initialValue;
|
||||
fence->completedValue = descriptor->initialValue;
|
||||
return cFence;
|
||||
}
|
||||
|
||||
void ProxyClientQueueSignal(dawnQueue cQueue, dawnFence cFence, uint64_t signalValue) {
|
||||
Fence* fence = reinterpret_cast<Fence*>(cFence);
|
||||
if (signalValue <= fence->signaledValue) {
|
||||
fence->device->HandleError("Fence value less than or equal to signaled value");
|
||||
return;
|
||||
}
|
||||
fence->signaledValue = signalValue;
|
||||
ClientQueueSignal(cQueue, cFence, signalValue);
|
||||
}
|
||||
|
||||
void ClientDeviceReference(dawnDevice) {
|
||||
}
|
||||
|
||||
|
@ -429,7 +509,7 @@ namespace dawn_wire {
|
|||
// - An autogenerated Client{{suffix}} method that sends the command on the wire
|
||||
// - A manual ProxyClient{{suffix}} method that will be inserted in the proctable instead of
|
||||
// the autogenerated one, and that will have to call Client{{suffix}}
|
||||
{% set proxied_commands = ["BufferUnmap"] %}
|
||||
{% set proxied_commands = ["BufferUnmap", "DeviceCreateFence", "QueueSignal"] %}
|
||||
|
||||
dawnProcTable GetProcs() {
|
||||
dawnProcTable table;
|
||||
|
@ -471,6 +551,9 @@ namespace dawn_wire {
|
|||
case ReturnWireCmd::BufferMapWriteAsyncCallback:
|
||||
success = HandleBufferMapWriteAsyncCallback(&commands, &size);
|
||||
break;
|
||||
case ReturnWireCmd::FenceUpdateCompletedValue:
|
||||
success = HandleFenceUpdateCompletedValue(&commands, &size);
|
||||
break;
|
||||
default:
|
||||
success = false;
|
||||
}
|
||||
|
@ -675,6 +758,25 @@ namespace dawn_wire {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleFenceUpdateCompletedValue(const char** commands, size_t* size) {
|
||||
const auto* cmd = GetCommand<ReturnFenceUpdateCompletedValueCmd>(commands, size);
|
||||
if (cmd == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* fence = mDevice->fence.GetObject(cmd->fenceId);
|
||||
uint32_t fenceSerial = mDevice->fence.GetSerial(cmd->fenceId);
|
||||
|
||||
//* The fence might have been deleted or recreated so this isn't an error.
|
||||
if (fence == nullptr || fenceSerial != cmd->fenceSerial) {
|
||||
return true;
|
||||
}
|
||||
|
||||
fence->completedValue = cmd->value;
|
||||
fence->CheckPassedFences();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -128,6 +128,7 @@ namespace dawn_wire {
|
|||
{% endfor %}
|
||||
BufferMapReadAsyncCallback,
|
||||
BufferMapWriteAsyncCallback,
|
||||
FenceUpdateCompletedValue,
|
||||
};
|
||||
|
||||
//* Command for the server calling a builder status callback.
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
namespace dawn_wire {
|
||||
|
@ -36,6 +37,13 @@ namespace dawn_wire {
|
|||
bool isWrite;
|
||||
};
|
||||
|
||||
struct FenceCompletionUserdata {
|
||||
Server* server;
|
||||
uint32_t fenceId;
|
||||
uint32_t fenceSerial;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
//* Stores what the backend knows about the type.
|
||||
template<typename T>
|
||||
struct ObjectDataBase {
|
||||
|
@ -141,6 +149,36 @@ namespace dawn_wire {
|
|||
std::vector<Data> mKnown;
|
||||
};
|
||||
|
||||
// ObjectIds are lost in deserialization. Store the ids of deserialized
|
||||
// objects here so they can be used in command handlers. This is useful
|
||||
// for creating ReturnWireCmds which contain client ids
|
||||
template <typename T>
|
||||
class ObjectIdLookupTable {
|
||||
public:
|
||||
void Store(T key, ObjectId id) {
|
||||
mTable[key] = id;
|
||||
}
|
||||
|
||||
// Return the cached ObjectId, or 0 (null handle)
|
||||
ObjectId Get(T key) const {
|
||||
const auto it = mTable.find(key);
|
||||
if (it != mTable.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Remove(T key) {
|
||||
auto it = mTable.find(key);
|
||||
if (it != mTable.end()) {
|
||||
mTable.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<T, ObjectId> mTable;
|
||||
};
|
||||
|
||||
void ForwardDeviceErrorToServer(const char* message, dawnCallbackUserdata userdata);
|
||||
|
||||
{% for type in by_category["object"] if type.is_builder%}
|
||||
|
@ -149,6 +187,8 @@ namespace dawn_wire {
|
|||
|
||||
void ForwardBufferMapReadAsync(dawnBufferMapAsyncStatus status, const void* ptr, dawnCallbackUserdata userdata);
|
||||
void ForwardBufferMapWriteAsync(dawnBufferMapAsyncStatus status, void* ptr, dawnCallbackUserdata userdata);
|
||||
void ForwardFenceCompletedValue(dawnFenceCompletionStatus status,
|
||||
dawnCallbackUserdata userdata);
|
||||
|
||||
// A really really simple implementation of the DeserializeAllocator. It's main feature
|
||||
// is that it has some inline storage so as to avoid allocations for the majority of
|
||||
|
@ -301,6 +341,19 @@ namespace dawn_wire {
|
|||
delete data;
|
||||
}
|
||||
|
||||
void OnFenceCompletedValueUpdated(FenceCompletionUserdata* data) {
|
||||
ReturnFenceUpdateCompletedValueCmd cmd;
|
||||
cmd.fenceId = data->fenceId;
|
||||
cmd.fenceSerial = data->fenceSerial;
|
||||
cmd.value = data->value;
|
||||
|
||||
auto allocCmd = static_cast<ReturnFenceUpdateCompletedValueCmd*>(GetCmdSpace(sizeof(cmd)));
|
||||
*allocCmd = cmd;
|
||||
|
||||
delete data;
|
||||
}
|
||||
|
||||
{% set client_side_commands = ["FenceGetCompletedValue"] %}
|
||||
const char* HandleCommands(const char* commands, size_t size) override {
|
||||
mProcs.deviceTick(mKnownDevice.Get(1)->handle);
|
||||
|
||||
|
@ -312,9 +365,11 @@ namespace dawn_wire {
|
|||
{% for type in by_category["object"] %}
|
||||
{% for method in type.methods %}
|
||||
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
||||
case WireCmd::{{Suffix}}:
|
||||
success = Handle{{Suffix}}(&commands, &size);
|
||||
break;
|
||||
{% if Suffix not in client_side_commands %}
|
||||
case WireCmd::{{Suffix}}:
|
||||
success = Handle{{Suffix}}(&commands, &size);
|
||||
break;
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% set Suffix = as_MethodSuffix(type.name, Name("destroy")) %}
|
||||
case WireCmd::{{Suffix}}:
|
||||
|
@ -386,6 +441,11 @@ namespace dawn_wire {
|
|||
KnownObjects<{{as_cType(type.name)}}> mKnown{{type.name.CamelCase()}};
|
||||
{% endfor %}
|
||||
|
||||
{% set reverse_lookup_object_types = ["Fence"] %}
|
||||
{% for type in by_category["object"] if type.name.CamelCase() in reverse_lookup_object_types %}
|
||||
ObjectIdLookupTable<{{as_cType(type.name)}}> m{{type.name.CamelCase()}}IdTable;
|
||||
{% endfor %}
|
||||
|
||||
//* Helper function for the getting of the command data in command handlers.
|
||||
//* Checks there is enough data left, updates the buffer / size and returns
|
||||
//* the command (or nullptr for an error).
|
||||
|
@ -420,86 +480,119 @@ namespace dawn_wire {
|
|||
return true;
|
||||
}
|
||||
|
||||
{% set custom_post_handler_commands = ["QueueSignal"] %}
|
||||
|
||||
bool PostHandleQueueSignal(const QueueSignalCmd& cmd) {
|
||||
ObjectId fenceId = mFenceIdTable.Get(cmd.fence);
|
||||
ASSERT(fenceId != 0);
|
||||
auto* fence = mKnownFence.Get(fenceId);
|
||||
ASSERT(fence != nullptr);
|
||||
|
||||
auto* data = new FenceCompletionUserdata;
|
||||
data->server = this;
|
||||
data->fenceId = fenceId;
|
||||
data->fenceSerial = fence->serial;
|
||||
data->value = cmd.signalValue;
|
||||
|
||||
auto userdata = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(data));
|
||||
mProcs.fenceOnCompletion(cmd.fence, cmd.signalValue, ForwardFenceCompletedValue, userdata);
|
||||
return true;
|
||||
}
|
||||
|
||||
//* Implementation of the command handlers
|
||||
{% for type in by_category["object"] %}
|
||||
{% for method in type.methods %}
|
||||
{% set Suffix = as_MethodSuffix(type.name, method.name) %}
|
||||
{% if Suffix not in client_side_commands %}
|
||||
//* The generic command handlers
|
||||
|
||||
//* The generic command handlers
|
||||
bool Handle{{Suffix}}(const char** commands, size_t* size) {
|
||||
{{Suffix}}Cmd cmd;
|
||||
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator, *this);
|
||||
|
||||
bool Handle{{Suffix}}(const char** commands, size_t* size) {
|
||||
{{Suffix}}Cmd cmd;
|
||||
DeserializeResult deserializeResult = cmd.Deserialize(commands, size, &mAllocator, *this);
|
||||
|
||||
if (deserializeResult == DeserializeResult::FatalError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
{% if Suffix in custom_pre_handler_commands %}
|
||||
if (!PreHandle{{Suffix}}(cmd)) {
|
||||
if (deserializeResult == DeserializeResult::FatalError) {
|
||||
return false;
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
//* Unpack 'self'
|
||||
auto* selfData = mKnown{{type.name.CamelCase()}}.Get(cmd.selfId);
|
||||
ASSERT(selfData != nullptr);
|
||||
|
||||
//* In all cases allocate the object data as it will be refered-to by the client.
|
||||
{% set return_type = method.return_type %}
|
||||
{% set returns = return_type.name.canonical_case() != "void" %}
|
||||
{% if returns %}
|
||||
{% set Type = method.return_type.name.CamelCase() %}
|
||||
auto* resultData = mKnown{{Type}}.Allocate(cmd.resultId);
|
||||
if (resultData == nullptr) {
|
||||
return false;
|
||||
}
|
||||
resultData->serial = cmd.resultSerial;
|
||||
|
||||
{% if type.is_builder %}
|
||||
selfData->builtObjectId = cmd.resultId;
|
||||
selfData->builtObjectSerial = cmd.resultSerial;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
//* After the data is allocated, apply the argument error propagation mechanism
|
||||
if (deserializeResult == DeserializeResult::ErrorObject) {
|
||||
{% if type.is_builder %}
|
||||
selfData->valid = false;
|
||||
//* If we are in GetResult, fake an error callback
|
||||
{% if returns %}
|
||||
On{{type.name.CamelCase()}}Error(DAWN_BUILDER_ERROR_STATUS_ERROR, "Maybe monad", cmd.selfId, selfData->serial);
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
return true;
|
||||
}
|
||||
|
||||
{% if returns %}
|
||||
auto result ={{" "}}
|
||||
{%- endif %}
|
||||
mProcs.{{as_varName(type.name, method.name)}}(cmd.self
|
||||
{%- for arg in method.arguments -%}
|
||||
, cmd.{{as_varName(arg.name)}}
|
||||
{%- endfor -%}
|
||||
);
|
||||
|
||||
{% if returns %}
|
||||
resultData->handle = result;
|
||||
resultData->valid = result != nullptr;
|
||||
|
||||
//* builders remember the ID of the object they built so that they can send it
|
||||
//* in the callback to the client.
|
||||
{% if return_type.is_builder %}
|
||||
if (result != nullptr) {
|
||||
uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
|
||||
uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd.resultId;
|
||||
mProcs.{{as_varName(return_type.name, Name("set error callback"))}}(result, Forward{{return_type.name.CamelCase()}}ToClient, userdata1, userdata2);
|
||||
{% if Suffix in custom_pre_handler_commands %}
|
||||
if (!PreHandle{{Suffix}}(cmd)) {
|
||||
return false;
|
||||
}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
return true;
|
||||
}
|
||||
//* Unpack 'self'
|
||||
auto* selfData = mKnown{{type.name.CamelCase()}}.Get(cmd.selfId);
|
||||
ASSERT(selfData != nullptr);
|
||||
|
||||
//* In all cases allocate the object data as it will be refered-to by the client.
|
||||
{% set return_type = method.return_type %}
|
||||
{% set returns = return_type.name.canonical_case() != "void" %}
|
||||
{% if returns %}
|
||||
{% set Type = method.return_type.name.CamelCase() %}
|
||||
auto* resultData = mKnown{{Type}}.Allocate(cmd.resultId);
|
||||
if (resultData == nullptr) {
|
||||
return false;
|
||||
}
|
||||
resultData->serial = cmd.resultSerial;
|
||||
|
||||
{% if type.is_builder %}
|
||||
selfData->builtObjectId = cmd.resultId;
|
||||
selfData->builtObjectSerial = cmd.resultSerial;
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
//* After the data is allocated, apply the argument error propagation mechanism
|
||||
if (deserializeResult == DeserializeResult::ErrorObject) {
|
||||
{% if type.is_builder %}
|
||||
selfData->valid = false;
|
||||
//* If we are in GetResult, fake an error callback
|
||||
{% if returns %}
|
||||
On{{type.name.CamelCase()}}Error(DAWN_BUILDER_ERROR_STATUS_ERROR, "Maybe monad", cmd.selfId, selfData->serial);
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
return true;
|
||||
}
|
||||
|
||||
{% if returns %}
|
||||
auto result ={{" "}}
|
||||
{%- endif %}
|
||||
mProcs.{{as_varName(type.name, method.name)}}(cmd.self
|
||||
{%- for arg in method.arguments -%}
|
||||
, cmd.{{as_varName(arg.name)}}
|
||||
{%- endfor -%}
|
||||
);
|
||||
|
||||
{% if Suffix in custom_post_handler_commands %}
|
||||
if (!PostHandle{{Suffix}}(cmd)) {
|
||||
return false;
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
{% if returns %}
|
||||
resultData->handle = result;
|
||||
resultData->valid = result != nullptr;
|
||||
|
||||
{% if return_type.name.CamelCase() in reverse_lookup_object_types %}
|
||||
//* For created objects, store a mapping from them back to their client IDs
|
||||
if (result) {
|
||||
m{{return_type.name.CamelCase()}}IdTable.Store(result, cmd.resultId);
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
//* builders remember the ID of the object they built so that they can send it
|
||||
//* in the callback to the client.
|
||||
{% if return_type.is_builder %}
|
||||
if (result != nullptr) {
|
||||
uint64_t userdata1 = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this));
|
||||
uint64_t userdata2 = (uint64_t(resultData->serial) << uint64_t(32)) + cmd.resultId;
|
||||
mProcs.{{as_varName(return_type.name, Name("set error callback"))}}(result, Forward{{return_type.name.CamelCase()}}ToClient, userdata1, userdata2);
|
||||
}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
return true;
|
||||
}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
//* Handlers for the destruction of objects: clients do the tracking of the
|
||||
|
@ -523,6 +616,10 @@ namespace dawn_wire {
|
|||
return false;
|
||||
}
|
||||
|
||||
{% if type.name.CamelCase() in reverse_lookup_object_types %}
|
||||
m{{type.name.CamelCase()}}IdTable.Remove(data->handle);
|
||||
{% endif %}
|
||||
|
||||
if (data->valid) {
|
||||
mProcs.{{as_varName(type.name, Name("release"))}}(data->handle);
|
||||
}
|
||||
|
@ -639,6 +736,13 @@ namespace dawn_wire {
|
|||
auto data = reinterpret_cast<MapUserdata*>(static_cast<uintptr_t>(userdata));
|
||||
data->server->OnMapWriteAsyncCallback(status, ptr, data);
|
||||
}
|
||||
|
||||
void ForwardFenceCompletedValue(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata) {
|
||||
auto data = reinterpret_cast<FenceCompletionUserdata*>(static_cast<uintptr_t>(userdata));
|
||||
if (status == DAWN_FENCE_COMPLETION_STATUS_SUCCESS) {
|
||||
data->server->OnFenceCompletedValueUpdated(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CommandHandler* NewServerCommandHandler(dawnDevice device, const dawnProcTable& procs, CommandSerializer* serializer) {
|
||||
|
|
|
@ -72,6 +72,17 @@ void ProcTableAsClass::BufferMapWriteAsync(dawnBuffer self, uint32_t start, uint
|
|||
OnBufferMapWriteAsyncCallback(self, start, size, callback, userdata);
|
||||
}
|
||||
|
||||
void ProcTableAsClass::FenceOnCompletion(dawnFence self,
|
||||
uint64_t value,
|
||||
dawnFenceOnCompletionCallback callback,
|
||||
dawnCallbackUserdata userdata) {
|
||||
auto object = reinterpret_cast<ProcTableAsClass::Object*>(self);
|
||||
object->fenceOnCompletionCallback = callback;
|
||||
object->userdata1 = userdata;
|
||||
|
||||
OnFenceOnCompletionCallback(self, value, callback, userdata);
|
||||
}
|
||||
|
||||
void ProcTableAsClass::CallDeviceErrorCallback(dawnDevice device, const char* message) {
|
||||
auto object = reinterpret_cast<ProcTableAsClass::Object*>(device);
|
||||
object->deviceErrorCallback(message, object->userdata1);
|
||||
|
@ -90,6 +101,12 @@ void ProcTableAsClass::CallMapWriteCallback(dawnBuffer buffer, dawnBufferMapAsyn
|
|||
object->mapWriteCallback(status, data, object->userdata1);
|
||||
}
|
||||
|
||||
void ProcTableAsClass::CallFenceOnCompletionCallback(dawnFence fence,
|
||||
dawnFenceCompletionStatus status) {
|
||||
auto object = reinterpret_cast<ProcTableAsClass::Object*>(fence);
|
||||
object->fenceOnCompletionCallback(status, object->userdata1);
|
||||
}
|
||||
|
||||
{% 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, dawnBuilderErrorCallback callback, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) {
|
||||
auto object = reinterpret_cast<ProcTableAsClass::Object*>(self);
|
||||
|
|
|
@ -59,19 +59,27 @@ class ProcTableAsClass {
|
|||
void DeviceSetErrorCallback(dawnDevice self, dawnDeviceErrorCallback callback, dawnCallbackUserdata userdata);
|
||||
void BufferMapReadAsync(dawnBuffer self, uint32_t start, uint32_t size, dawnBufferMapReadCallback callback, dawnCallbackUserdata userdata);
|
||||
void BufferMapWriteAsync(dawnBuffer self, uint32_t start, uint32_t size, dawnBufferMapWriteCallback callback, dawnCallbackUserdata userdata);
|
||||
|
||||
void FenceOnCompletion(dawnFence self,
|
||||
uint64_t value,
|
||||
dawnFenceOnCompletionCallback callback,
|
||||
dawnCallbackUserdata userdata);
|
||||
|
||||
// Special cased mockable methods
|
||||
virtual void OnDeviceSetErrorCallback(dawnDevice device, dawnDeviceErrorCallback callback, dawnCallbackUserdata userdata) = 0;
|
||||
virtual void OnBuilderSetErrorCallback(dawnBufferBuilder builder, dawnBuilderErrorCallback callback, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) = 0;
|
||||
virtual void OnBufferMapReadAsyncCallback(dawnBuffer buffer, uint32_t start, uint32_t size, dawnBufferMapReadCallback callback, dawnCallbackUserdata userdata) = 0;
|
||||
virtual void OnBufferMapWriteAsyncCallback(dawnBuffer buffer, uint32_t start, uint32_t size, dawnBufferMapWriteCallback callback, dawnCallbackUserdata userdata) = 0;
|
||||
virtual void OnFenceOnCompletionCallback(dawnFence fence,
|
||||
uint64_t value,
|
||||
dawnFenceOnCompletionCallback callback,
|
||||
dawnCallbackUserdata userdata) = 0;
|
||||
|
||||
// Calls the stored callbacks
|
||||
void CallDeviceErrorCallback(dawnDevice device, const char* message);
|
||||
void CallBuilderErrorCallback(void* builder , dawnBuilderErrorStatus status, const char* message);
|
||||
void CallMapReadCallback(dawnBuffer buffer, dawnBufferMapAsyncStatus status, const void* data);
|
||||
void CallMapWriteCallback(dawnBuffer buffer, dawnBufferMapAsyncStatus status, void* data);
|
||||
void CallFenceOnCompletionCallback(dawnFence fence, dawnFenceCompletionStatus status);
|
||||
|
||||
struct Object {
|
||||
ProcTableAsClass* procs = nullptr;
|
||||
|
@ -79,6 +87,7 @@ class ProcTableAsClass {
|
|||
dawnBuilderErrorCallback builderErrorCallback = nullptr;
|
||||
dawnBufferMapReadCallback mapReadCallback = nullptr;
|
||||
dawnBufferMapWriteCallback mapWriteCallback = nullptr;
|
||||
dawnFenceOnCompletionCallback fenceOnCompletionCallback = nullptr;
|
||||
dawnCallbackUserdata userdata1 = 0;
|
||||
dawnCallbackUserdata userdata2 = 0;
|
||||
};
|
||||
|
@ -112,6 +121,11 @@ class MockProcTable : public ProcTableAsClass {
|
|||
MOCK_METHOD4(OnBuilderSetErrorCallback, void(dawnBufferBuilder builder, dawnBuilderErrorCallback callback, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2));
|
||||
MOCK_METHOD5(OnBufferMapReadAsyncCallback, void(dawnBuffer buffer, uint32_t start, uint32_t size, dawnBufferMapReadCallback callback, dawnCallbackUserdata userdata));
|
||||
MOCK_METHOD5(OnBufferMapWriteAsyncCallback, void(dawnBuffer buffer, uint32_t start, uint32_t size, dawnBufferMapWriteCallback callback, dawnCallbackUserdata userdata));
|
||||
MOCK_METHOD4(OnFenceOnCompletionCallback,
|
||||
void(dawnFence fence,
|
||||
uint64_t value,
|
||||
dawnFenceOnCompletionCallback callback,
|
||||
dawnCallbackUserdata userdata));
|
||||
};
|
||||
|
||||
#endif // MOCK_DAWN_H
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "dawn_native/ComputePipeline.h"
|
||||
#include "dawn_native/DepthStencilState.h"
|
||||
#include "dawn_native/ErrorData.h"
|
||||
#include "dawn_native/Fence.h"
|
||||
#include "dawn_native/FenceSignalTracker.h"
|
||||
#include "dawn_native/InputState.h"
|
||||
#include "dawn_native/PipelineLayout.h"
|
||||
#include "dawn_native/Queue.h"
|
||||
|
@ -51,6 +53,7 @@ namespace dawn_native {
|
|||
|
||||
DeviceBase::DeviceBase() {
|
||||
mCaches = std::make_unique<DeviceBase::Caches>();
|
||||
mFenceSignalTracker = std::make_unique<FenceSignalTracker>(this);
|
||||
}
|
||||
|
||||
DeviceBase::~DeviceBase() {
|
||||
|
@ -72,6 +75,10 @@ namespace dawn_native {
|
|||
return this;
|
||||
}
|
||||
|
||||
FenceSignalTracker* DeviceBase::GetFenceSignalTracker() const {
|
||||
return mFenceSignalTracker.get();
|
||||
}
|
||||
|
||||
ResultOrError<BindGroupLayoutBase*> DeviceBase::GetOrCreateBindGroupLayout(
|
||||
const BindGroupLayoutDescriptor* descriptor) {
|
||||
BindGroupLayoutBase blueprint(this, descriptor, true);
|
||||
|
@ -135,6 +142,15 @@ namespace dawn_native {
|
|||
DepthStencilStateBuilder* DeviceBase::CreateDepthStencilStateBuilder() {
|
||||
return new DepthStencilStateBuilder(this);
|
||||
}
|
||||
FenceBase* DeviceBase::CreateFence(const FenceDescriptor* descriptor) {
|
||||
FenceBase* result = nullptr;
|
||||
|
||||
if (ConsumedError(CreateFenceInternal(&result, descriptor))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
InputStateBuilder* DeviceBase::CreateInputStateBuilder() {
|
||||
return new InputStateBuilder(this);
|
||||
}
|
||||
|
@ -206,6 +222,7 @@ namespace dawn_native {
|
|||
|
||||
void DeviceBase::Tick() {
|
||||
TickImpl();
|
||||
mFenceSignalTracker->Tick(GetCompletedCommandSerial());
|
||||
}
|
||||
|
||||
void DeviceBase::Reference() {
|
||||
|
@ -246,6 +263,13 @@ namespace dawn_native {
|
|||
return {};
|
||||
}
|
||||
|
||||
MaybeError DeviceBase::CreateFenceInternal(FenceBase** result,
|
||||
const FenceDescriptor* descriptor) {
|
||||
DAWN_TRY(ValidateFenceDescriptor(this, descriptor));
|
||||
*result = new FenceBase(this, descriptor);
|
||||
return {};
|
||||
}
|
||||
|
||||
MaybeError DeviceBase::CreatePipelineLayoutInternal(
|
||||
PipelineLayoutBase** result,
|
||||
const PipelineLayoutDescriptor* descriptor) {
|
||||
|
|
|
@ -29,6 +29,8 @@ namespace dawn_native {
|
|||
|
||||
using ErrorCallback = void (*)(const char* errorMessage, void* userData);
|
||||
|
||||
class FenceSignalTracker;
|
||||
|
||||
class DeviceBase {
|
||||
public:
|
||||
DeviceBase();
|
||||
|
@ -47,6 +49,8 @@ namespace dawn_native {
|
|||
// Used by autogenerated code, returns itself
|
||||
DeviceBase* GetDevice();
|
||||
|
||||
FenceSignalTracker* GetFenceSignalTracker() const;
|
||||
|
||||
virtual BindGroupBase* CreateBindGroup(BindGroupBuilder* builder) = 0;
|
||||
virtual BlendStateBase* CreateBlendState(BlendStateBuilder* builder) = 0;
|
||||
virtual BufferViewBase* CreateBufferView(BufferViewBuilder* builder) = 0;
|
||||
|
@ -89,6 +93,7 @@ namespace dawn_native {
|
|||
CommandBufferBuilder* CreateCommandBufferBuilder();
|
||||
ComputePipelineBase* CreateComputePipeline(const ComputePipelineDescriptor* descriptor);
|
||||
DepthStencilStateBuilder* CreateDepthStencilStateBuilder();
|
||||
FenceBase* CreateFence(const FenceDescriptor* descriptor);
|
||||
InputStateBuilder* CreateInputStateBuilder();
|
||||
PipelineLayoutBase* CreatePipelineLayout(const PipelineLayoutDescriptor* descriptor);
|
||||
QueueBase* CreateQueue();
|
||||
|
@ -137,6 +142,7 @@ namespace dawn_native {
|
|||
MaybeError CreateBufferInternal(BufferBase** result, const BufferDescriptor* descriptor);
|
||||
MaybeError CreateComputePipelineInternal(ComputePipelineBase** result,
|
||||
const ComputePipelineDescriptor* descriptor);
|
||||
MaybeError CreateFenceInternal(FenceBase** result, const FenceDescriptor* descriptor);
|
||||
MaybeError CreatePipelineLayoutInternal(PipelineLayoutBase** result,
|
||||
const PipelineLayoutDescriptor* descriptor);
|
||||
MaybeError CreateQueueInternal(QueueBase** result);
|
||||
|
@ -155,6 +161,8 @@ namespace dawn_native {
|
|||
struct Caches;
|
||||
std::unique_ptr<Caches> mCaches;
|
||||
|
||||
std::unique_ptr<FenceSignalTracker> mFenceSignalTracker;
|
||||
|
||||
dawn::DeviceErrorCallback mErrorCallback = nullptr;
|
||||
dawn::CallbackUserdata mErrorUserdata = 0;
|
||||
uint32_t mRefCount = 1;
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2018 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/Fence.h"
|
||||
|
||||
#include "common/Assert.h"
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/ValidationUtils_autogen.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <utility>
|
||||
|
||||
namespace dawn_native {
|
||||
|
||||
MaybeError ValidateFenceDescriptor(DeviceBase*, const FenceDescriptor* descriptor) {
|
||||
if (descriptor->nextInChain != nullptr) {
|
||||
return DAWN_VALIDATION_ERROR("nextInChain must be nullptr");
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// Fence
|
||||
|
||||
FenceBase::FenceBase(DeviceBase* device, const FenceDescriptor* descriptor)
|
||||
: ObjectBase(device),
|
||||
mSignalValue(descriptor->initialValue),
|
||||
mCompletedValue(descriptor->initialValue) {
|
||||
}
|
||||
|
||||
FenceBase::~FenceBase() {
|
||||
for (auto& request : mRequests.IterateAll()) {
|
||||
request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, request.userdata);
|
||||
}
|
||||
mRequests.Clear();
|
||||
}
|
||||
|
||||
uint64_t FenceBase::GetCompletedValue() const {
|
||||
return mCompletedValue;
|
||||
}
|
||||
|
||||
void FenceBase::OnCompletion(uint64_t value,
|
||||
dawn::FenceOnCompletionCallback callback,
|
||||
dawn::CallbackUserdata userdata) {
|
||||
if (GetDevice()->ConsumedError(ValidateOnCompletion(value))) {
|
||||
callback(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value <= mCompletedValue) {
|
||||
callback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata);
|
||||
return;
|
||||
}
|
||||
|
||||
OnCompletionData request;
|
||||
request.completionCallback = callback;
|
||||
request.userdata = userdata;
|
||||
mRequests.Enqueue(std::move(request), value);
|
||||
}
|
||||
|
||||
uint64_t FenceBase::GetSignaledValue() const {
|
||||
return mSignalValue;
|
||||
}
|
||||
|
||||
void FenceBase::SetSignaledValue(uint64_t signalValue) {
|
||||
ASSERT(signalValue > mSignalValue);
|
||||
mSignalValue = signalValue;
|
||||
}
|
||||
|
||||
void FenceBase::SetCompletedValue(uint64_t completedValue) {
|
||||
ASSERT(completedValue <= mSignalValue);
|
||||
ASSERT(completedValue > mCompletedValue);
|
||||
mCompletedValue = completedValue;
|
||||
|
||||
for (auto& request : mRequests.IterateUpTo(mCompletedValue)) {
|
||||
request.completionCallback(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, request.userdata);
|
||||
}
|
||||
mRequests.ClearUpTo(mCompletedValue);
|
||||
}
|
||||
|
||||
MaybeError FenceBase::ValidateOnCompletion(uint64_t value) const {
|
||||
if (value > mSignalValue) {
|
||||
return DAWN_VALIDATION_ERROR("Value greater than fence signaled value");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace dawn_native
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
#ifndef DAWNNATIVE_FENCE_H_
|
||||
#define DAWNNATIVE_FENCE_H_
|
||||
|
||||
#include "common/SerialMap.h"
|
||||
#include "dawn_native/Error.h"
|
||||
#include "dawn_native/Forward.h"
|
||||
#include "dawn_native/ObjectBase.h"
|
||||
|
||||
#include "dawn_native/dawn_platform.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
namespace dawn_native {
|
||||
|
||||
MaybeError ValidateFenceDescriptor(DeviceBase*, const FenceDescriptor* descriptor);
|
||||
|
||||
class FenceBase : public ObjectBase {
|
||||
public:
|
||||
FenceBase(DeviceBase* device, const FenceDescriptor* descriptor);
|
||||
~FenceBase();
|
||||
|
||||
// Dawn API
|
||||
uint64_t GetCompletedValue() const;
|
||||
void OnCompletion(uint64_t value,
|
||||
dawn::FenceOnCompletionCallback callback,
|
||||
dawn::CallbackUserdata userdata);
|
||||
uint64_t GetSignaledValue() const;
|
||||
|
||||
protected:
|
||||
friend class QueueBase;
|
||||
friend class FenceSignalTracker;
|
||||
void SetSignaledValue(uint64_t signalValue);
|
||||
void SetCompletedValue(uint64_t completedValue);
|
||||
|
||||
private:
|
||||
MaybeError ValidateOnCompletion(uint64_t value) const;
|
||||
|
||||
struct OnCompletionData {
|
||||
dawn::FenceOnCompletionCallback completionCallback = nullptr;
|
||||
dawn::CallbackUserdata userdata = 0;
|
||||
};
|
||||
|
||||
uint64_t mSignalValue;
|
||||
uint64_t mCompletedValue;
|
||||
SerialMap<OnCompletionData> mRequests;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
||||
#endif // DAWNNATIVE_FENCE_H_
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2018 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/FenceSignalTracker.h"
|
||||
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/Fence.h"
|
||||
|
||||
namespace dawn_native {
|
||||
|
||||
FenceSignalTracker::FenceSignalTracker(DeviceBase* device) : mDevice(device) {
|
||||
}
|
||||
|
||||
FenceSignalTracker::~FenceSignalTracker() {
|
||||
ASSERT(mFencesInFlight.Empty());
|
||||
}
|
||||
|
||||
void FenceSignalTracker::UpdateFenceOnComplete(FenceBase* fence, uint64_t value) {
|
||||
// Because we currently only have a single queue, we can simply update
|
||||
// the fence completed value once the last submitted serial has passed.
|
||||
mFencesInFlight.Enqueue(FenceInFlight{fence, value},
|
||||
mDevice->GetLastSubmittedCommandSerial());
|
||||
}
|
||||
|
||||
void FenceSignalTracker::Tick(Serial finishedSerial) {
|
||||
for (auto& fenceInFlight : mFencesInFlight.IterateUpTo(finishedSerial)) {
|
||||
fenceInFlight.fence->SetCompletedValue(fenceInFlight.value);
|
||||
}
|
||||
mFencesInFlight.ClearUpTo(finishedSerial);
|
||||
}
|
||||
|
||||
} // namespace dawn_native
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2018 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.
|
||||
|
||||
#ifndef DAWNNATIVE_FENCESIGNALTRACKER_H_
|
||||
#define DAWNNATIVE_FENCESIGNALTRACKER_H_
|
||||
|
||||
#include "common/SerialQueue.h"
|
||||
#include "dawn_native/RefCounted.h"
|
||||
|
||||
namespace dawn_native {
|
||||
|
||||
class DeviceBase;
|
||||
class FenceBase;
|
||||
|
||||
class FenceSignalTracker {
|
||||
struct FenceInFlight {
|
||||
Ref<FenceBase> fence;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
public:
|
||||
FenceSignalTracker(DeviceBase* device);
|
||||
~FenceSignalTracker();
|
||||
|
||||
void UpdateFenceOnComplete(FenceBase* fence, uint64_t value);
|
||||
|
||||
void Tick(Serial finishedSerial);
|
||||
|
||||
private:
|
||||
DeviceBase* mDevice;
|
||||
SerialQueue<FenceInFlight> mFencesInFlight;
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
||||
#endif // DAWNNATIVE_FENCESIGNALTRACKER_H_
|
|
@ -36,6 +36,7 @@ namespace dawn_native {
|
|||
class ComputePassEncoderBase;
|
||||
class DepthStencilStateBase;
|
||||
class DepthStencilStateBuilder;
|
||||
class FenceBase;
|
||||
class InputStateBase;
|
||||
class InputStateBuilder;
|
||||
class PipelineLayoutBase;
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#include "dawn_native/Buffer.h"
|
||||
#include "dawn_native/CommandBuffer.h"
|
||||
#include "dawn_native/Device.h"
|
||||
#include "dawn_native/Fence.h"
|
||||
#include "dawn_native/FenceSignalTracker.h"
|
||||
#include "dawn_native/Texture.h"
|
||||
|
||||
namespace dawn_native {
|
||||
|
@ -34,6 +36,15 @@ namespace dawn_native {
|
|||
SubmitImpl(numCommands, commands);
|
||||
}
|
||||
|
||||
void QueueBase::Signal(FenceBase* fence, uint64_t signalValue) {
|
||||
if (GetDevice()->ConsumedError(ValidateSignal(fence, signalValue))) {
|
||||
return;
|
||||
}
|
||||
|
||||
fence->SetSignaledValue(signalValue);
|
||||
GetDevice()->GetFenceSignalTracker()->UpdateFenceOnComplete(fence, signalValue);
|
||||
}
|
||||
|
||||
MaybeError QueueBase::ValidateSubmit(uint32_t numCommands, CommandBufferBase* const* commands) {
|
||||
for (uint32_t i = 0; i < numCommands; ++i) {
|
||||
const CommandBufferResourceUsage& usages = commands[i]->GetResourceUsages();
|
||||
|
@ -58,4 +69,11 @@ namespace dawn_native {
|
|||
return {};
|
||||
}
|
||||
|
||||
MaybeError QueueBase::ValidateSignal(const FenceBase* fence, uint64_t signalValue) {
|
||||
if (signalValue <= fence->GetSignaledValue()) {
|
||||
return DAWN_VALIDATION_ERROR("Signal value less than or equal to fence signaled value");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -30,11 +30,13 @@ namespace dawn_native {
|
|||
|
||||
// Dawn API
|
||||
void Submit(uint32_t numCommands, CommandBufferBase* const* commands);
|
||||
void Signal(FenceBase* fence, uint64_t signalValue);
|
||||
|
||||
private:
|
||||
virtual void SubmitImpl(uint32_t numCommands, CommandBufferBase* const* commands) = 0;
|
||||
|
||||
MaybeError ValidateSubmit(uint32_t numCommands, CommandBufferBase* const* commands);
|
||||
MaybeError ValidateSignal(const FenceBase* fence, uint64_t signalValue);
|
||||
};
|
||||
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -56,6 +56,14 @@ namespace dawn_wire {
|
|||
uint32_t status;
|
||||
};
|
||||
|
||||
struct ReturnFenceUpdateCompletedValueCmd {
|
||||
ReturnWireCmd commandId = ReturnWireCmd::FenceUpdateCompletedValue;
|
||||
|
||||
ObjectId fenceId;
|
||||
ObjectSerial fenceSerial;
|
||||
uint64_t value;
|
||||
};
|
||||
|
||||
struct BufferUpdateMappedDataCmd {
|
||||
WireCmd commandId = WireCmd::BufferUpdateMappedDataCmd;
|
||||
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
// Copyright 2018 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 <gmock/gmock.h>
|
||||
#include "tests/DawnTest.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
class MockFenceOnCompletionCallback {
|
||||
public:
|
||||
MOCK_METHOD2(Call, void(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata));
|
||||
};
|
||||
|
||||
static std::unique_ptr<MockFenceOnCompletionCallback> mockFenceOnCompletionCallback;
|
||||
static void ToMockFenceOnCompletionCallback(dawnFenceCompletionStatus status,
|
||||
dawnCallbackUserdata userdata) {
|
||||
mockFenceOnCompletionCallback->Call(status, userdata);
|
||||
}
|
||||
class FenceTests : public DawnTest {
|
||||
private:
|
||||
struct CallbackInfo {
|
||||
FenceTests* test;
|
||||
uint64_t value;
|
||||
dawnFenceCompletionStatus status;
|
||||
int32_t callIndex = -1; // If this is -1, the callback was not called
|
||||
|
||||
void Update(dawnFenceCompletionStatus status) {
|
||||
this->callIndex = test->mCallIndex++;
|
||||
this->status = status;
|
||||
}
|
||||
};
|
||||
|
||||
int32_t mCallIndex;
|
||||
|
||||
protected:
|
||||
FenceTests() : mCallIndex(0) {
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
DawnTest::SetUp();
|
||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
mockFenceOnCompletionCallback = nullptr;
|
||||
DawnTest::TearDown();
|
||||
}
|
||||
|
||||
void WaitForCompletedValue(dawn::Fence fence, uint64_t completedValue) {
|
||||
while (fence.GetCompletedValue() < completedValue) {
|
||||
WaitABit();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Test that signaling a fence updates the completed value
|
||||
TEST_P(FenceTests, SimpleSignal) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1u;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
// Completed value starts at initial value
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 1u);
|
||||
|
||||
queue.Signal(fence, 2);
|
||||
WaitForCompletedValue(fence, 2);
|
||||
|
||||
// Completed value updates to signaled value
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
||||
}
|
||||
|
||||
// Test callbacks are called in increasing order of fence completion value
|
||||
TEST_P(FenceTests, OnCompletionOrdering) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 0u;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
queue.Signal(fence, 4);
|
||||
|
||||
dawnCallbackUserdata userdata0 = 1282;
|
||||
dawnCallbackUserdata userdata1 = 4382;
|
||||
dawnCallbackUserdata userdata2 = 1211;
|
||||
dawnCallbackUserdata userdata3 = 1882;
|
||||
|
||||
{
|
||||
testing::InSequence s;
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata0))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata1))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata2))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata3))
|
||||
.Times(1);
|
||||
}
|
||||
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, userdata2);
|
||||
fence.OnCompletion(0u, ToMockFenceOnCompletionCallback, userdata0);
|
||||
fence.OnCompletion(3u, ToMockFenceOnCompletionCallback, userdata3);
|
||||
fence.OnCompletion(1u, ToMockFenceOnCompletionCallback, userdata1);
|
||||
|
||||
WaitForCompletedValue(fence, 4);
|
||||
}
|
||||
|
||||
// Test callbacks still occur if Queue::Signal happens multiple times
|
||||
TEST_P(FenceTests, MultipleSignalOnCompletion) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 0u;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
queue.Signal(fence, 2);
|
||||
queue.Signal(fence, 4);
|
||||
|
||||
dawnCallbackUserdata userdata = 1234;
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata))
|
||||
.Times(1);
|
||||
fence.OnCompletion(3u, ToMockFenceOnCompletionCallback, userdata);
|
||||
|
||||
WaitForCompletedValue(fence, 4);
|
||||
}
|
||||
|
||||
// Test all callbacks are called if they are added for the same fence value
|
||||
TEST_P(FenceTests, OnCompletionMultipleCallbacks) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 0u;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
queue.Signal(fence, 4);
|
||||
|
||||
dawnCallbackUserdata userdata0 = 2341;
|
||||
dawnCallbackUserdata userdata1 = 4598;
|
||||
dawnCallbackUserdata userdata2 = 5690;
|
||||
dawnCallbackUserdata userdata3 = 2783;
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata0))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata1))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata2))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata3))
|
||||
.Times(1);
|
||||
|
||||
fence.OnCompletion(4u, ToMockFenceOnCompletionCallback, userdata0);
|
||||
fence.OnCompletion(4u, ToMockFenceOnCompletionCallback, userdata1);
|
||||
fence.OnCompletion(4u, ToMockFenceOnCompletionCallback, userdata2);
|
||||
fence.OnCompletion(4u, ToMockFenceOnCompletionCallback, userdata3);
|
||||
|
||||
WaitForCompletedValue(fence, 4u);
|
||||
}
|
||||
|
||||
// TODO(enga): Enable when fence is removed from fence signal tracker
|
||||
// Currently it holds a reference and is not destructed
|
||||
TEST_P(FenceTests, DISABLED_DestroyBeforeOnCompletionEnd) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 0u;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
// The fence in this block will be deleted when it goes out of scope
|
||||
{
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 0u;
|
||||
dawn::Fence testFence = device.CreateFence(&descriptor);
|
||||
|
||||
queue.Signal(testFence, 4);
|
||||
|
||||
dawnCallbackUserdata userdata0 = 1341;
|
||||
dawnCallbackUserdata userdata1 = 1598;
|
||||
dawnCallbackUserdata userdata2 = 1690;
|
||||
dawnCallbackUserdata userdata3 = 1783;
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata0))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata1))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata2))
|
||||
.Times(1);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata3))
|
||||
.Times(1);
|
||||
|
||||
testFence.OnCompletion(1u, ToMockFenceOnCompletionCallback, userdata0);
|
||||
testFence.OnCompletion(2u, ToMockFenceOnCompletionCallback, userdata1);
|
||||
testFence.OnCompletion(2u, ToMockFenceOnCompletionCallback, userdata2);
|
||||
testFence.OnCompletion(3u, ToMockFenceOnCompletionCallback, userdata3);
|
||||
}
|
||||
|
||||
// Wait for another fence to be sure all callbacks have cleared
|
||||
queue.Signal(fence, 1);
|
||||
WaitForCompletedValue(fence, 1);
|
||||
}
|
||||
|
||||
DAWN_INSTANTIATE_TEST(FenceTests, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)
|
|
@ -117,6 +117,17 @@ static void ToMockBufferMapWriteCallback(dawnBufferMapAsyncStatus status, void*
|
|||
mockBufferMapWriteCallback->Call(status, lastMapWritePointer, userdata);
|
||||
}
|
||||
|
||||
class MockFenceOnCompletionCallback {
|
||||
public:
|
||||
MOCK_METHOD2(Call, void(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata));
|
||||
};
|
||||
|
||||
static std::unique_ptr<MockFenceOnCompletionCallback> mockFenceOnCompletionCallback;
|
||||
static void ToMockFenceOnCompletionCallback(dawnFenceCompletionStatus status,
|
||||
dawnCallbackUserdata userdata) {
|
||||
mockFenceOnCompletionCallback->Call(status, userdata);
|
||||
}
|
||||
|
||||
class WireTestsBase : public Test {
|
||||
protected:
|
||||
WireTestsBase(bool ignoreSetCallbackCalls)
|
||||
|
@ -128,6 +139,7 @@ class WireTestsBase : public Test {
|
|||
mockBuilderErrorCallback = std::make_unique<MockBuilderErrorCallback>();
|
||||
mockBufferMapReadCallback = std::make_unique<MockBufferMapReadCallback>();
|
||||
mockBufferMapWriteCallback = std::make_unique<MockBufferMapWriteCallback>();
|
||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||
|
||||
dawnProcTable mockProcs;
|
||||
dawnDevice mockDevice;
|
||||
|
@ -162,6 +174,7 @@ class WireTestsBase : public Test {
|
|||
mockBuilderErrorCallback = nullptr;
|
||||
mockBufferMapReadCallback = nullptr;
|
||||
mockBufferMapWriteCallback = nullptr;
|
||||
mockFenceOnCompletionCallback = nullptr;
|
||||
}
|
||||
|
||||
void FlushClient() {
|
||||
|
@ -1163,3 +1176,211 @@ TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) {
|
|||
|
||||
FlushClient();
|
||||
}
|
||||
|
||||
class WireFenceTests : public WireTestsBase {
|
||||
public:
|
||||
WireFenceTests() : WireTestsBase(true) {
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
WireTestsBase::SetUp();
|
||||
|
||||
{
|
||||
dawnFenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
descriptor.nextInChain = nullptr;
|
||||
|
||||
apiFence = api.GetNewFence();
|
||||
fence = dawnDeviceCreateFence(device, &descriptor);
|
||||
|
||||
EXPECT_CALL(api, DeviceCreateFence(apiDevice, _)).WillOnce(Return(apiFence));
|
||||
FlushClient();
|
||||
}
|
||||
{
|
||||
queue = dawnDeviceCreateQueue(device);
|
||||
apiQueue = api.GetNewQueue();
|
||||
EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue));
|
||||
FlushClient();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void DoQueueSignal(uint64_t signalValue) {
|
||||
dawnQueueSignal(queue, fence, signalValue);
|
||||
EXPECT_CALL(api, QueueSignal(apiQueue, apiFence, signalValue)).Times(1);
|
||||
|
||||
// This callback is generated to update the completedValue of the fence
|
||||
// on the client
|
||||
EXPECT_CALL(api, OnFenceOnCompletionCallback(apiFence, signalValue, _, _))
|
||||
.WillOnce(InvokeWithoutArgs([&]() {
|
||||
api.CallFenceOnCompletionCallback(apiFence, DAWN_FENCE_COMPLETION_STATUS_SUCCESS);
|
||||
}));
|
||||
}
|
||||
|
||||
// A successfully created fence
|
||||
dawnFence fence;
|
||||
dawnFence apiFence;
|
||||
|
||||
dawnQueue queue;
|
||||
dawnQueue apiQueue;
|
||||
};
|
||||
|
||||
// Check that signaling a fence succeeds
|
||||
TEST_F(WireFenceTests, QueueSignalSuccess) {
|
||||
DoQueueSignal(2u);
|
||||
DoQueueSignal(3u);
|
||||
FlushClient();
|
||||
FlushServer();
|
||||
}
|
||||
|
||||
// Without any flushes, it is valid to signal a value greater than the current
|
||||
// signaled value
|
||||
TEST_F(WireFenceTests, QueueSignalSynchronousValidationSuccess) {
|
||||
dawnCallbackUserdata userdata = 9157;
|
||||
dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
|
||||
EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0);
|
||||
|
||||
dawnQueueSignal(queue, fence, 2u);
|
||||
dawnQueueSignal(queue, fence, 4u);
|
||||
dawnQueueSignal(queue, fence, 5u);
|
||||
}
|
||||
|
||||
// Without any flushes, errors should be generated when signaling a value less
|
||||
// than or equal to the current signaled value
|
||||
TEST_F(WireFenceTests, QueueSignalSynchronousValidationError) {
|
||||
dawnCallbackUserdata userdata = 3157;
|
||||
dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
|
||||
|
||||
EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
|
||||
dawnQueueSignal(queue, fence, 0u); // Error
|
||||
EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
|
||||
|
||||
EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
|
||||
dawnQueueSignal(queue, fence, 1u); // Error
|
||||
EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
|
||||
|
||||
EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0);
|
||||
dawnQueueSignal(queue, fence, 4u); // Success
|
||||
EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
|
||||
|
||||
EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
|
||||
dawnQueueSignal(queue, fence, 3u); // Error
|
||||
EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
|
||||
}
|
||||
|
||||
// Check that callbacks are immediately called if the fence is already finished
|
||||
TEST_F(WireFenceTests, OnCompletionImmediate) {
|
||||
// Can call on value < (initial) signaled value happens immediately
|
||||
{
|
||||
dawnCallbackUserdata userdata = 9847;
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata))
|
||||
.Times(1);
|
||||
dawnFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, userdata);
|
||||
}
|
||||
|
||||
// Can call on value == (initial) signaled value happens immediately
|
||||
{
|
||||
dawnCallbackUserdata userdata = 4347;
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata))
|
||||
.Times(1);
|
||||
dawnFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, userdata);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all passed client completion callbacks are called
|
||||
TEST_F(WireFenceTests, OnCompletionMultiple) {
|
||||
DoQueueSignal(3u);
|
||||
DoQueueSignal(6u);
|
||||
|
||||
dawnCallbackUserdata userdata0 = 2134;
|
||||
dawnCallbackUserdata userdata1 = 7134;
|
||||
dawnCallbackUserdata userdata2 = 3144;
|
||||
dawnCallbackUserdata userdata3 = 1130;
|
||||
|
||||
// Add callbacks in a non-monotonic order. They should still be called
|
||||
// in order of increasing fence value.
|
||||
// Add multiple callbacks for the same value.
|
||||
dawnFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, userdata0);
|
||||
dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata1);
|
||||
dawnFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, userdata2);
|
||||
dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata3);
|
||||
|
||||
Sequence s1, s2;
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata1))
|
||||
.Times(1)
|
||||
.InSequence(s1);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata3))
|
||||
.Times(1)
|
||||
.InSequence(s2);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata2))
|
||||
.Times(1)
|
||||
.InSequence(s1, s2);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata0))
|
||||
.Times(1)
|
||||
.InSequence(s1, s2);
|
||||
|
||||
FlushClient();
|
||||
FlushServer();
|
||||
}
|
||||
|
||||
// Without any flushes, it is valid to wait on a value less than or equal to
|
||||
// the last signaled value
|
||||
TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) {
|
||||
dawnQueueSignal(queue, fence, 4u);
|
||||
dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0);
|
||||
dawnFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0);
|
||||
dawnFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0);
|
||||
}
|
||||
|
||||
// Without any flushes, errors should be generated when waiting on a value greater
|
||||
// than the last signaled value
|
||||
TEST_F(WireFenceTests, OnCompletionSynchronousValidationError) {
|
||||
dawnCallbackUserdata userdata1 = 3817;
|
||||
dawnCallbackUserdata userdata2 = 3857;
|
||||
dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata2);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata1))
|
||||
.Times(1);
|
||||
EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata2)).Times(1);
|
||||
|
||||
dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata1);
|
||||
}
|
||||
|
||||
// Check that the fence completed value is initialized
|
||||
TEST_F(WireFenceTests, GetCompletedValueInitialization) {
|
||||
EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u);
|
||||
}
|
||||
|
||||
// Check that the fence completed value updates after signaling the fence
|
||||
TEST_F(WireFenceTests, GetCompletedValueUpdate) {
|
||||
DoQueueSignal(3u);
|
||||
FlushClient();
|
||||
FlushServer();
|
||||
|
||||
EXPECT_EQ(dawnFenceGetCompletedValue(fence), 3u);
|
||||
}
|
||||
|
||||
// Check that the fence completed value does not update without a flush
|
||||
TEST_F(WireFenceTests, GetCompletedValueNoUpdate) {
|
||||
dawnQueueSignal(queue, fence, 3u);
|
||||
EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u);
|
||||
}
|
||||
|
||||
// Check that the callback is called with UNKNOWN when the fence is destroyed
|
||||
// before the completed value is updated
|
||||
TEST_F(WireFenceTests, DestroyBeforeOnCompletionEnd) {
|
||||
dawnCallbackUserdata userdata = 8616;
|
||||
dawnQueueSignal(queue, fence, 3u);
|
||||
dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback,
|
||||
Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata))
|
||||
.Times(1);
|
||||
|
||||
dawnFenceRelease(fence);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
// Copyright 2018 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 "tests/unittests/validation/ValidationTest.h"
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
using namespace testing;
|
||||
|
||||
class MockFenceOnCompletionCallback {
|
||||
public:
|
||||
MOCK_METHOD2(Call, void(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata));
|
||||
};
|
||||
|
||||
struct FenceOnCompletionExpectation {
|
||||
dawn::Fence fence;
|
||||
uint64_t value;
|
||||
dawnFenceCompletionStatus status;
|
||||
};
|
||||
|
||||
static std::unique_ptr<MockFenceOnCompletionCallback> mockFenceOnCompletionCallback;
|
||||
static void ToMockFenceOnCompletionCallback(dawnFenceCompletionStatus status,
|
||||
dawnCallbackUserdata userdata) {
|
||||
mockFenceOnCompletionCallback->Call(status, userdata);
|
||||
}
|
||||
|
||||
class FenceValidationTest : public ValidationTest {
|
||||
protected:
|
||||
void TestOnCompletion(dawn::Fence fence, uint64_t value, dawnFenceCompletionStatus status) {
|
||||
FenceOnCompletionExpectation* expectation = new FenceOnCompletionExpectation;
|
||||
expectation->fence = fence;
|
||||
expectation->value = value;
|
||||
expectation->status = status;
|
||||
dawnCallbackUserdata userdata =
|
||||
static_cast<dawnCallbackUserdata>(reinterpret_cast<uintptr_t>(expectation));
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(status, userdata)).Times(1);
|
||||
fence.OnCompletion(value, ToMockFenceOnCompletionCallback, userdata);
|
||||
}
|
||||
|
||||
void Flush() {
|
||||
device.Tick();
|
||||
}
|
||||
|
||||
dawn::Queue queue;
|
||||
|
||||
private:
|
||||
void SetUp() override {
|
||||
ValidationTest::SetUp();
|
||||
|
||||
mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
|
||||
queue = device.CreateQueue();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
// Delete mocks so that expectations are checked
|
||||
mockFenceOnCompletionCallback = nullptr;
|
||||
|
||||
ValidationTest::TearDown();
|
||||
}
|
||||
};
|
||||
|
||||
// Test cases where creation should succeed
|
||||
TEST_F(FenceValidationTest, CreationSuccess) {
|
||||
// Success
|
||||
{
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 0;
|
||||
device.CreateFence(&descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(FenceValidationTest, GetCompletedValue) {
|
||||
// Starts at initial value
|
||||
{
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 1u);
|
||||
}
|
||||
}
|
||||
|
||||
// Test that OnCompletion handlers are called immediately for
|
||||
// already completed fence values
|
||||
TEST_F(FenceValidationTest, OnCompletionImmediate) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, 0))
|
||||
.Times(1);
|
||||
fence.OnCompletion(0u, ToMockFenceOnCompletionCallback, 0);
|
||||
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, 1))
|
||||
.Times(1);
|
||||
fence.OnCompletion(1u, ToMockFenceOnCompletionCallback, 1);
|
||||
}
|
||||
|
||||
// Test setting OnCompletion handlers for values > signaled value
|
||||
TEST_F(FenceValidationTest, OnCompletionLargerThanSignaled) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
// Cannot signal for values > signaled value
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, 0))
|
||||
.Times(1);
|
||||
ASSERT_DEVICE_ERROR(fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, 0));
|
||||
|
||||
// Can set handler after signaling
|
||||
queue.Signal(fence, 2);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, 0))
|
||||
.Times(1);
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, 0);
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
TEST_F(FenceValidationTest, GetCompletedValueInsideCallback) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
queue.Signal(fence, 3);
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, 0);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, 0))
|
||||
.WillOnce(Invoke([&](dawnFenceCompletionStatus status, dawnCallbackUserdata userdata) {
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 3u);
|
||||
}));
|
||||
|
||||
Flush();
|
||||
}
|
||||
|
||||
TEST_F(FenceValidationTest, GetCompletedValueAfterCallback) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
queue.Signal(fence, 2);
|
||||
fence.OnCompletion(2u, ToMockFenceOnCompletionCallback, 0);
|
||||
EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, 0))
|
||||
.Times(1);
|
||||
|
||||
Flush();
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
||||
}
|
||||
|
||||
TEST_F(FenceValidationTest, SignalError) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
// value < fence signaled value
|
||||
ASSERT_DEVICE_ERROR(queue.Signal(fence, 0));
|
||||
|
||||
// value == fence signaled value
|
||||
ASSERT_DEVICE_ERROR(queue.Signal(fence, 1));
|
||||
}
|
||||
|
||||
TEST_F(FenceValidationTest, SignalSuccess) {
|
||||
dawn::FenceDescriptor descriptor;
|
||||
descriptor.initialValue = 1;
|
||||
dawn::Fence fence = device.CreateFence(&descriptor);
|
||||
|
||||
// Success
|
||||
queue.Signal(fence, 2);
|
||||
Flush();
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 2u);
|
||||
|
||||
// Success increasing fence value by more than 1
|
||||
queue.Signal(fence, 6);
|
||||
Flush();
|
||||
EXPECT_EQ(fence.GetCompletedValue(), 6u);
|
||||
}
|
Loading…
Reference in New Issue