mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-07-13 16:45:56 +00:00
Fix crash when device is removed before CreateReady*Pipeline callback
This patch fixes a crash issue when the device is destroyed before the callback of CreateReady{Render, Compute}Pipeline is called. Now when the callback is called in DeviceBase::ShutDown(), the cached pipeline object will also be destroyed before the callback returns. BUG=dawn:529 TEST=dawn_end2end_tests Change-Id: I91ec2608b53591d265c0648f5c02daf7fadac85e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/30744 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Austin Eng <enga@chromium.org> Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
This commit is contained in:
parent
ebdbc03b77
commit
42103bc2e9
@ -516,7 +516,8 @@
|
|||||||
{"value": 0, "name": "success"},
|
{"value": 0, "name": "success"},
|
||||||
{"value": 1, "name": "error"},
|
{"value": 1, "name": "error"},
|
||||||
{"value": 2, "name": "device lost"},
|
{"value": 2, "name": "device lost"},
|
||||||
{"value": 3, "name": "unknown"}
|
{"value": 3, "name": "device destroyed"},
|
||||||
|
{"value": 4, "name": "unknown"}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"create ready render pipeline callback": {
|
"create ready render pipeline callback": {
|
||||||
|
@ -14,7 +14,9 @@
|
|||||||
|
|
||||||
#include "dawn_native/CreateReadyPipelineTracker.h"
|
#include "dawn_native/CreateReadyPipelineTracker.h"
|
||||||
|
|
||||||
|
#include "dawn_native/ComputePipeline.h"
|
||||||
#include "dawn_native/Device.h"
|
#include "dawn_native/Device.h"
|
||||||
|
#include "dawn_native/RenderPipeline.h"
|
||||||
|
|
||||||
namespace dawn_native {
|
namespace dawn_native {
|
||||||
|
|
||||||
@ -33,13 +35,21 @@ namespace dawn_native {
|
|||||||
mCreateReadyComputePipelineCallback(callback) {
|
mCreateReadyComputePipelineCallback(callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateReadyComputePipelineTask::Finish() {
|
void CreateReadyComputePipelineTask::Finish(WGPUCreateReadyPipelineStatus status) {
|
||||||
ASSERT(mPipeline != nullptr);
|
ASSERT(mPipeline != nullptr);
|
||||||
ASSERT(mCreateReadyComputePipelineCallback != nullptr);
|
ASSERT(mCreateReadyComputePipelineCallback != nullptr);
|
||||||
|
|
||||||
mCreateReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus_Success,
|
if (status != WGPUCreateReadyPipelineStatus_Success) {
|
||||||
reinterpret_cast<WGPUComputePipeline>(mPipeline), "",
|
// TODO(jiawei.shao@intel.com): support handling device lost
|
||||||
mUserData);
|
ASSERT(status == WGPUCreateReadyPipelineStatus_DeviceDestroyed);
|
||||||
|
mCreateReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus_DeviceDestroyed,
|
||||||
|
nullptr, "Device destroyed before callback",
|
||||||
|
mUserData);
|
||||||
|
mPipeline->Release();
|
||||||
|
} else {
|
||||||
|
mCreateReadyComputePipelineCallback(
|
||||||
|
status, reinterpret_cast<WGPUComputePipeline>(mPipeline), "", mUserData);
|
||||||
|
}
|
||||||
|
|
||||||
// Set mCreateReadyComputePipelineCallback to nullptr in case it is called more than once.
|
// Set mCreateReadyComputePipelineCallback to nullptr in case it is called more than once.
|
||||||
mCreateReadyComputePipelineCallback = nullptr;
|
mCreateReadyComputePipelineCallback = nullptr;
|
||||||
@ -54,13 +64,21 @@ namespace dawn_native {
|
|||||||
mCreateReadyRenderPipelineCallback(callback) {
|
mCreateReadyRenderPipelineCallback(callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CreateReadyRenderPipelineTask::Finish() {
|
void CreateReadyRenderPipelineTask::Finish(WGPUCreateReadyPipelineStatus status) {
|
||||||
ASSERT(mPipeline != nullptr);
|
ASSERT(mPipeline != nullptr);
|
||||||
ASSERT(mCreateReadyRenderPipelineCallback != nullptr);
|
ASSERT(mCreateReadyRenderPipelineCallback != nullptr);
|
||||||
|
|
||||||
mCreateReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus_Success,
|
if (status != WGPUCreateReadyPipelineStatus_Success) {
|
||||||
reinterpret_cast<WGPURenderPipeline>(mPipeline), "",
|
// TODO(jiawei.shao@intel.com): support handling device lost
|
||||||
mUserData);
|
ASSERT(status == WGPUCreateReadyPipelineStatus_DeviceDestroyed);
|
||||||
|
mCreateReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus_DeviceDestroyed,
|
||||||
|
nullptr, "Device destroyed before callback",
|
||||||
|
mUserData);
|
||||||
|
mPipeline->Release();
|
||||||
|
} else {
|
||||||
|
mCreateReadyRenderPipelineCallback(
|
||||||
|
status, reinterpret_cast<WGPURenderPipeline>(mPipeline), "", mUserData);
|
||||||
|
}
|
||||||
|
|
||||||
// Set mCreateReadyPipelineCallback to nullptr in case it is called more than once.
|
// Set mCreateReadyPipelineCallback to nullptr in case it is called more than once.
|
||||||
mCreateReadyRenderPipelineCallback = nullptr;
|
mCreateReadyRenderPipelineCallback = nullptr;
|
||||||
@ -81,9 +99,16 @@ namespace dawn_native {
|
|||||||
|
|
||||||
void CreateReadyPipelineTracker::Tick(ExecutionSerial finishedSerial) {
|
void CreateReadyPipelineTracker::Tick(ExecutionSerial finishedSerial) {
|
||||||
for (auto& task : mCreateReadyPipelineTasksInFlight.IterateUpTo(finishedSerial)) {
|
for (auto& task : mCreateReadyPipelineTasksInFlight.IterateUpTo(finishedSerial)) {
|
||||||
task->Finish();
|
task->Finish(WGPUCreateReadyPipelineStatus_Success);
|
||||||
}
|
}
|
||||||
mCreateReadyPipelineTasksInFlight.ClearUpTo(finishedSerial);
|
mCreateReadyPipelineTasksInFlight.ClearUpTo(finishedSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CreateReadyPipelineTracker::ClearForShutDown() {
|
||||||
|
for (auto& task : mCreateReadyPipelineTasksInFlight.IterateAll()) {
|
||||||
|
task->Finish(WGPUCreateReadyPipelineStatus_DeviceDestroyed);
|
||||||
|
}
|
||||||
|
mCreateReadyPipelineTasksInFlight.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
@ -32,7 +32,7 @@ namespace dawn_native {
|
|||||||
CreateReadyPipelineTaskBase(void* userData);
|
CreateReadyPipelineTaskBase(void* userData);
|
||||||
virtual ~CreateReadyPipelineTaskBase();
|
virtual ~CreateReadyPipelineTaskBase();
|
||||||
|
|
||||||
virtual void Finish() = 0;
|
virtual void Finish(WGPUCreateReadyPipelineStatus status) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void* mUserData;
|
void* mUserData;
|
||||||
@ -43,7 +43,7 @@ namespace dawn_native {
|
|||||||
WGPUCreateReadyComputePipelineCallback callback,
|
WGPUCreateReadyComputePipelineCallback callback,
|
||||||
void* userdata);
|
void* userdata);
|
||||||
|
|
||||||
void Finish() final;
|
void Finish(WGPUCreateReadyPipelineStatus status) final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ComputePipelineBase* mPipeline;
|
ComputePipelineBase* mPipeline;
|
||||||
@ -55,7 +55,7 @@ namespace dawn_native {
|
|||||||
WGPUCreateReadyRenderPipelineCallback callback,
|
WGPUCreateReadyRenderPipelineCallback callback,
|
||||||
void* userdata);
|
void* userdata);
|
||||||
|
|
||||||
void Finish() final;
|
void Finish(WGPUCreateReadyPipelineStatus status) final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
RenderPipelineBase* mPipeline;
|
RenderPipelineBase* mPipeline;
|
||||||
@ -69,6 +69,7 @@ namespace dawn_native {
|
|||||||
|
|
||||||
void TrackTask(std::unique_ptr<CreateReadyPipelineTaskBase> task, ExecutionSerial serial);
|
void TrackTask(std::unique_ptr<CreateReadyPipelineTaskBase> task, ExecutionSerial serial);
|
||||||
void Tick(ExecutionSerial finishedSerial);
|
void Tick(ExecutionSerial finishedSerial);
|
||||||
|
void ClearForShutDown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DeviceBase* mDevice;
|
DeviceBase* mDevice;
|
||||||
|
@ -151,7 +151,8 @@ namespace dawn_native {
|
|||||||
// pending callbacks.
|
// pending callbacks.
|
||||||
mErrorScopeTracker->Tick(GetCompletedCommandSerial());
|
mErrorScopeTracker->Tick(GetCompletedCommandSerial());
|
||||||
GetDefaultQueue()->Tick(GetCompletedCommandSerial());
|
GetDefaultQueue()->Tick(GetCompletedCommandSerial());
|
||||||
mCreateReadyPipelineTracker->Tick(GetCompletedCommandSerial());
|
|
||||||
|
mCreateReadyPipelineTracker->ClearForShutDown();
|
||||||
|
|
||||||
// call TickImpl once last time to clean up resources
|
// call TickImpl once last time to clean up resources
|
||||||
// Ignore errors so that we can continue with destruction
|
// Ignore errors so that we can continue with destruction
|
||||||
@ -787,6 +788,7 @@ namespace dawn_native {
|
|||||||
mDynamicUploader->Deallocate(mCompletedSerial);
|
mDynamicUploader->Deallocate(mCompletedSerial);
|
||||||
mErrorScopeTracker->Tick(mCompletedSerial);
|
mErrorScopeTracker->Tick(mCompletedSerial);
|
||||||
GetDefaultQueue()->Tick(mCompletedSerial);
|
GetDefaultQueue()->Tick(mCompletedSerial);
|
||||||
|
|
||||||
mCreateReadyPipelineTracker->Tick(mCompletedSerial);
|
mCreateReadyPipelineTracker->Tick(mCompletedSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,20 +40,21 @@ namespace dawn_wire { namespace client {
|
|||||||
// Fire pending error scopes
|
// Fire pending error scopes
|
||||||
auto errorScopes = std::move(mErrorScopes);
|
auto errorScopes = std::move(mErrorScopes);
|
||||||
for (const auto& it : errorScopes) {
|
for (const auto& it : errorScopes) {
|
||||||
it.second.callback(WGPUErrorType_Unknown, "Device destroyed", it.second.userdata);
|
it.second.callback(WGPUErrorType_Unknown, "Device destroyed before callback",
|
||||||
|
it.second.userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto createReadyPipelineRequests = std::move(mCreateReadyPipelineRequests);
|
auto createReadyPipelineRequests = std::move(mCreateReadyPipelineRequests);
|
||||||
for (const auto& it : createReadyPipelineRequests) {
|
for (const auto& it : createReadyPipelineRequests) {
|
||||||
if (it.second.createReadyComputePipelineCallback != nullptr) {
|
if (it.second.createReadyComputePipelineCallback != nullptr) {
|
||||||
it.second.createReadyComputePipelineCallback(WGPUCreateReadyPipelineStatus_Unknown,
|
it.second.createReadyComputePipelineCallback(
|
||||||
nullptr, "Device destroyed",
|
WGPUCreateReadyPipelineStatus_DeviceDestroyed, nullptr,
|
||||||
it.second.userdata);
|
"Device destroyed before callback", it.second.userdata);
|
||||||
} else {
|
} else {
|
||||||
ASSERT(it.second.createReadyRenderPipelineCallback != nullptr);
|
ASSERT(it.second.createReadyRenderPipelineCallback != nullptr);
|
||||||
it.second.createReadyRenderPipelineCallback(WGPUCreateReadyPipelineStatus_Unknown,
|
it.second.createReadyRenderPipelineCallback(
|
||||||
nullptr, "Device destroyed",
|
WGPUCreateReadyPipelineStatus_DeviceDestroyed, nullptr,
|
||||||
it.second.userdata);
|
"Device destroyed before callback", it.second.userdata);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,10 @@ namespace dawn_wire { namespace server {
|
|||||||
const DawnProcTable& procs,
|
const DawnProcTable& procs,
|
||||||
CommandSerializer* serializer,
|
CommandSerializer* serializer,
|
||||||
MemoryTransferService* memoryTransferService)
|
MemoryTransferService* memoryTransferService)
|
||||||
: mSerializer(serializer), mProcs(procs), mMemoryTransferService(memoryTransferService) {
|
: mSerializer(serializer),
|
||||||
|
mProcs(procs),
|
||||||
|
mMemoryTransferService(memoryTransferService),
|
||||||
|
mIsAlive(std::make_shared<bool>(true)) {
|
||||||
if (mMemoryTransferService == nullptr) {
|
if (mMemoryTransferService == nullptr) {
|
||||||
// If a MemoryTransferService is not provided, fallback to inline memory.
|
// If a MemoryTransferService is not provided, fallback to inline memory.
|
||||||
mOwnedMemoryTransferService = CreateInlineMemoryTransferService();
|
mOwnedMemoryTransferService = CreateInlineMemoryTransferService();
|
||||||
|
@ -56,6 +56,7 @@ namespace dawn_wire { namespace server {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct CreateReadyPipelineUserData {
|
struct CreateReadyPipelineUserData {
|
||||||
|
std::weak_ptr<bool> isServerAlive;
|
||||||
Server* server;
|
Server* server;
|
||||||
uint64_t requestSerial;
|
uint64_t requestSerial;
|
||||||
ObjectId pipelineObjectID;
|
ObjectId pipelineObjectID;
|
||||||
@ -131,6 +132,8 @@ namespace dawn_wire { namespace server {
|
|||||||
DawnProcTable mProcs;
|
DawnProcTable mProcs;
|
||||||
std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr;
|
std::unique_ptr<MemoryTransferService> mOwnedMemoryTransferService = nullptr;
|
||||||
MemoryTransferService* mMemoryTransferService = nullptr;
|
MemoryTransferService* mMemoryTransferService = nullptr;
|
||||||
|
|
||||||
|
std::shared_ptr<bool> mIsAlive;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<MemoryTransferService> CreateInlineMemoryTransferService();
|
std::unique_ptr<MemoryTransferService> CreateInlineMemoryTransferService();
|
||||||
|
@ -30,20 +30,34 @@ namespace dawn_wire { namespace server {
|
|||||||
WGPUComputePipeline pipeline,
|
WGPUComputePipeline pipeline,
|
||||||
const char* message,
|
const char* message,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
CreateReadyPipelineUserData* createReadyPipelineUserData =
|
std::unique_ptr<CreateReadyPipelineUserData> createReadyPipelineUserData(
|
||||||
static_cast<CreateReadyPipelineUserData*>(userdata);
|
static_cast<CreateReadyPipelineUserData*>(userdata));
|
||||||
|
|
||||||
|
// We need to ensure createReadyPipelineUserData->server is still pointing to a valid
|
||||||
|
// object before doing any operations on it.
|
||||||
|
if (createReadyPipelineUserData->isServerAlive.expired()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
createReadyPipelineUserData->server->OnCreateReadyComputePipelineCallback(
|
createReadyPipelineUserData->server->OnCreateReadyComputePipelineCallback(
|
||||||
status, pipeline, message, createReadyPipelineUserData);
|
status, pipeline, message, createReadyPipelineUserData.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::ForwardCreateReadyRenderPipeline(WGPUCreateReadyPipelineStatus status,
|
void Server::ForwardCreateReadyRenderPipeline(WGPUCreateReadyPipelineStatus status,
|
||||||
WGPURenderPipeline pipeline,
|
WGPURenderPipeline pipeline,
|
||||||
const char* message,
|
const char* message,
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
CreateReadyPipelineUserData* createReadyPipelineUserData =
|
std::unique_ptr<CreateReadyPipelineUserData> createReadyPipelineUserData(
|
||||||
static_cast<CreateReadyPipelineUserData*>(userdata);
|
static_cast<CreateReadyPipelineUserData*>(userdata));
|
||||||
|
|
||||||
|
// We need to ensure createReadyPipelineUserData->server is still pointing to a valid
|
||||||
|
// object before doing any operations on it.
|
||||||
|
if (createReadyPipelineUserData->isServerAlive.expired()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
createReadyPipelineUserData->server->OnCreateReadyRenderPipelineCallback(
|
createReadyPipelineUserData->server->OnCreateReadyRenderPipelineCallback(
|
||||||
status, pipeline, message, createReadyPipelineUserData);
|
status, pipeline, message, createReadyPipelineUserData.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::OnUncapturedError(WGPUErrorType type, const char* message) {
|
void Server::OnUncapturedError(WGPUErrorType type, const char* message) {
|
||||||
@ -87,6 +101,7 @@ namespace dawn_wire { namespace server {
|
|||||||
|
|
||||||
std::unique_ptr<CreateReadyPipelineUserData> userdata =
|
std::unique_ptr<CreateReadyPipelineUserData> userdata =
|
||||||
std::make_unique<CreateReadyPipelineUserData>();
|
std::make_unique<CreateReadyPipelineUserData>();
|
||||||
|
userdata->isServerAlive = mIsAlive;
|
||||||
userdata->server = this;
|
userdata->server = this;
|
||||||
userdata->requestSerial = requestSerial;
|
userdata->requestSerial = requestSerial;
|
||||||
userdata->pipelineObjectID = pipelineObjectHandle.id;
|
userdata->pipelineObjectID = pipelineObjectHandle.id;
|
||||||
@ -101,10 +116,27 @@ namespace dawn_wire { namespace server {
|
|||||||
const char* message,
|
const char* message,
|
||||||
CreateReadyPipelineUserData* userdata) {
|
CreateReadyPipelineUserData* userdata) {
|
||||||
std::unique_ptr<CreateReadyPipelineUserData> data(userdata);
|
std::unique_ptr<CreateReadyPipelineUserData> data(userdata);
|
||||||
if (status != WGPUCreateReadyPipelineStatus_Success) {
|
|
||||||
ComputePipelineObjects().Free(data->pipelineObjectID);
|
auto* computePipelineObject = ComputePipelineObjects().Get(data->pipelineObjectID);
|
||||||
} else {
|
ASSERT(computePipelineObject != nullptr);
|
||||||
ComputePipelineObjects().Get(data->pipelineObjectID)->handle = pipeline;
|
|
||||||
|
switch (status) {
|
||||||
|
case WGPUCreateReadyPipelineStatus_Success:
|
||||||
|
computePipelineObject->handle = pipeline;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WGPUCreateReadyPipelineStatus_Error:
|
||||||
|
ComputePipelineObjects().Free(data->pipelineObjectID);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Currently this code is unreachable because WireServer is always deleted before the
|
||||||
|
// removal of the device. In the future this logic may be changed when we decide to
|
||||||
|
// support sharing one pair of WireServer/WireClient to multiple devices.
|
||||||
|
case WGPUCreateReadyPipelineStatus_DeviceLost:
|
||||||
|
case WGPUCreateReadyPipelineStatus_DeviceDestroyed:
|
||||||
|
case WGPUCreateReadyPipelineStatus_Unknown:
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnDeviceCreateReadyComputePipelineCallbackCmd cmd;
|
ReturnDeviceCreateReadyComputePipelineCallbackCmd cmd;
|
||||||
@ -128,6 +160,7 @@ namespace dawn_wire { namespace server {
|
|||||||
|
|
||||||
std::unique_ptr<CreateReadyPipelineUserData> userdata =
|
std::unique_ptr<CreateReadyPipelineUserData> userdata =
|
||||||
std::make_unique<CreateReadyPipelineUserData>();
|
std::make_unique<CreateReadyPipelineUserData>();
|
||||||
|
userdata->isServerAlive = mIsAlive;
|
||||||
userdata->server = this;
|
userdata->server = this;
|
||||||
userdata->requestSerial = requestSerial;
|
userdata->requestSerial = requestSerial;
|
||||||
userdata->pipelineObjectID = pipelineObjectHandle.id;
|
userdata->pipelineObjectID = pipelineObjectHandle.id;
|
||||||
@ -142,10 +175,27 @@ namespace dawn_wire { namespace server {
|
|||||||
const char* message,
|
const char* message,
|
||||||
CreateReadyPipelineUserData* userdata) {
|
CreateReadyPipelineUserData* userdata) {
|
||||||
std::unique_ptr<CreateReadyPipelineUserData> data(userdata);
|
std::unique_ptr<CreateReadyPipelineUserData> data(userdata);
|
||||||
if (status != WGPUCreateReadyPipelineStatus_Success) {
|
|
||||||
RenderPipelineObjects().Free(data->pipelineObjectID);
|
auto* renderPipelineObject = RenderPipelineObjects().Get(data->pipelineObjectID);
|
||||||
} else {
|
ASSERT(renderPipelineObject != nullptr);
|
||||||
RenderPipelineObjects().Get(data->pipelineObjectID)->handle = pipeline;
|
|
||||||
|
switch (status) {
|
||||||
|
case WGPUCreateReadyPipelineStatus_Success:
|
||||||
|
renderPipelineObject->handle = pipeline;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WGPUCreateReadyPipelineStatus_Error:
|
||||||
|
RenderPipelineObjects().Free(data->pipelineObjectID);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Currently this code is unreachable because WireServer is always deleted before the
|
||||||
|
// removal of the device. In the future this logic may be changed when we decide to
|
||||||
|
// support sharing one pair of WireServer/WireClient to multiple devices.
|
||||||
|
case WGPUCreateReadyPipelineStatus_DeviceLost:
|
||||||
|
case WGPUCreateReadyPipelineStatus_DeviceDestroyed:
|
||||||
|
case WGPUCreateReadyPipelineStatus_Unknown:
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
ReturnDeviceCreateReadyRenderPipelineCallbackCmd cmd;
|
ReturnDeviceCreateReadyRenderPipelineCallbackCmd cmd;
|
||||||
|
@ -26,7 +26,10 @@ namespace {
|
|||||||
};
|
};
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
class CreateReadyPipelineTest : public DawnTest {};
|
class CreateReadyPipelineTest : public DawnTest {
|
||||||
|
protected:
|
||||||
|
CreateReadyPipelineTask task;
|
||||||
|
};
|
||||||
|
|
||||||
// Verify the basic use of CreateReadyComputePipeline works on all backends.
|
// Verify the basic use of CreateReadyComputePipeline works on all backends.
|
||||||
TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyComputePipeline) {
|
TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyComputePipeline) {
|
||||||
@ -42,7 +45,6 @@ TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyComputePipeline) {
|
|||||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
|
||||||
csDesc.computeStage.entryPoint = "main";
|
csDesc.computeStage.entryPoint = "main";
|
||||||
|
|
||||||
CreateReadyPipelineTask task;
|
|
||||||
device.CreateReadyComputePipeline(
|
device.CreateReadyComputePipeline(
|
||||||
&csDesc,
|
&csDesc,
|
||||||
[](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
|
[](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
|
||||||
@ -110,7 +112,6 @@ TEST_P(CreateReadyPipelineTest, CreateComputePipelineFailed) {
|
|||||||
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
|
||||||
csDesc.computeStage.entryPoint = "main0";
|
csDesc.computeStage.entryPoint = "main0";
|
||||||
|
|
||||||
CreateReadyPipelineTask task;
|
|
||||||
device.CreateReadyComputePipeline(
|
device.CreateReadyComputePipeline(
|
||||||
&csDesc,
|
&csDesc,
|
||||||
[](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
|
[](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
|
||||||
@ -159,7 +160,6 @@ TEST_P(CreateReadyPipelineTest, BasicUseOfCreateReadyRenderPipeline) {
|
|||||||
renderPipelineDescriptor.cColorStates[0].format = kOutputAttachmentFormat;
|
renderPipelineDescriptor.cColorStates[0].format = kOutputAttachmentFormat;
|
||||||
renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
|
renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
|
||||||
CreateReadyPipelineTask task;
|
|
||||||
device.CreateReadyRenderPipeline(
|
device.CreateReadyRenderPipeline(
|
||||||
&renderPipelineDescriptor,
|
&renderPipelineDescriptor,
|
||||||
[](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
|
[](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
|
||||||
@ -237,7 +237,6 @@ TEST_P(CreateReadyPipelineTest, CreateRenderPipelineFailed) {
|
|||||||
renderPipelineDescriptor.cColorStates[0].format = kOutputAttachmentFormat;
|
renderPipelineDescriptor.cColorStates[0].format = kOutputAttachmentFormat;
|
||||||
renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
|
renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
|
||||||
CreateReadyPipelineTask task;
|
|
||||||
device.CreateReadyRenderPipeline(
|
device.CreateReadyRenderPipeline(
|
||||||
&renderPipelineDescriptor,
|
&renderPipelineDescriptor,
|
||||||
[](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
|
[](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
|
||||||
@ -259,6 +258,75 @@ TEST_P(CreateReadyPipelineTest, CreateRenderPipelineFailed) {
|
|||||||
ASSERT_EQ(nullptr, task.computePipeline.Get());
|
ASSERT_EQ(nullptr, task.computePipeline.Get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify there is no error when the device is released before the callback of
|
||||||
|
// CreateReadyComputePipeline() is called.
|
||||||
|
TEST_P(CreateReadyPipelineTest, ReleaseDeviceBeforeCallbackOfCreateReadyComputePipeline) {
|
||||||
|
const char* computeShader = R"(
|
||||||
|
#version 450
|
||||||
|
void main() {
|
||||||
|
})";
|
||||||
|
|
||||||
|
wgpu::ComputePipelineDescriptor csDesc;
|
||||||
|
csDesc.computeStage.module =
|
||||||
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Compute, computeShader);
|
||||||
|
csDesc.computeStage.entryPoint = "main";
|
||||||
|
|
||||||
|
device.CreateReadyComputePipeline(
|
||||||
|
&csDesc,
|
||||||
|
[](WGPUCreateReadyPipelineStatus status, WGPUComputePipeline returnPipeline,
|
||||||
|
const char* message, void* userdata) {
|
||||||
|
ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_DeviceDestroyed,
|
||||||
|
status);
|
||||||
|
|
||||||
|
CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
|
||||||
|
task->computePipeline = wgpu::ComputePipeline::Acquire(returnPipeline);
|
||||||
|
task->isCompleted = true;
|
||||||
|
task->message = message;
|
||||||
|
},
|
||||||
|
&task);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify there is no error when the device is released before the callback of
|
||||||
|
// CreateReadyRenderPipeline() is called.
|
||||||
|
TEST_P(CreateReadyPipelineTest, ReleaseDeviceBeforeCallbackOfCreateReadyRenderPipeline) {
|
||||||
|
const char* vertexShader = R"(
|
||||||
|
#version 450
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(0.f, 0.f, 0.f, 1.f);
|
||||||
|
gl_PointSize = 1.0f;
|
||||||
|
})";
|
||||||
|
const char* fragmentShader = R"(
|
||||||
|
#version 450
|
||||||
|
layout(location = 0) out vec4 o_color;
|
||||||
|
void main() {
|
||||||
|
o_color = vec4(0.f, 1.f, 0.f, 1.f);
|
||||||
|
})";
|
||||||
|
|
||||||
|
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor(device);
|
||||||
|
wgpu::ShaderModule vsModule =
|
||||||
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Vertex, vertexShader);
|
||||||
|
wgpu::ShaderModule fsModule =
|
||||||
|
utils::CreateShaderModule(device, utils::SingleShaderStage::Fragment, fragmentShader);
|
||||||
|
renderPipelineDescriptor.vertexStage.module = vsModule;
|
||||||
|
renderPipelineDescriptor.cFragmentStage.module = fsModule;
|
||||||
|
renderPipelineDescriptor.cColorStates[0].format = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
renderPipelineDescriptor.primitiveTopology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
|
||||||
|
device.CreateReadyRenderPipeline(
|
||||||
|
&renderPipelineDescriptor,
|
||||||
|
[](WGPUCreateReadyPipelineStatus status, WGPURenderPipeline returnPipeline,
|
||||||
|
const char* message, void* userdata) {
|
||||||
|
ASSERT_EQ(WGPUCreateReadyPipelineStatus::WGPUCreateReadyPipelineStatus_DeviceDestroyed,
|
||||||
|
status);
|
||||||
|
|
||||||
|
CreateReadyPipelineTask* task = static_cast<CreateReadyPipelineTask*>(userdata);
|
||||||
|
task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline);
|
||||||
|
task->isCompleted = true;
|
||||||
|
task->message = message;
|
||||||
|
},
|
||||||
|
&task);
|
||||||
|
}
|
||||||
|
|
||||||
DAWN_INSTANTIATE_TEST(CreateReadyPipelineTest,
|
DAWN_INSTANTIATE_TEST(CreateReadyPipelineTest,
|
||||||
D3D12Backend(),
|
D3D12Backend(),
|
||||||
MetalBackend(),
|
MetalBackend(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user