dawn-cmake/src/dawn_native/CreatePipelineAsyncTracker.cpp
Jiawei Shao c243f67d58 Refactor APICreateComputePipelineAsync to support both sync and async path
This patch refactors the implementation of APICreateComputePipelineAsync
as a preparation of the async path of the creation of compute pipeline.

Now the code path of APICreateComputePipelineAsync() includes the following
3 parts:
- When an error occurs in the front-end validations, the callback will be
  called at once in the main thread.
- When we can find a proper compute pipeline object in the cache, the
  callback will be called at once in the main thread.
- When we cannot find the proper comptue pipeline object in the cache, the
  newly-created pipeline object, the callback and userdata will be saved
  into the CreatePipelineAsyncTracker, and the callback will be called in
  device.Tick(). All the logic mentioned in this section has been put into
  one function CreateComputePipelineAsyncImpl(), which will be overrided
  by its asynchronous version on all the backends that support creating
  pipeline objects asynchronously.

Note that APICreateRenderPipelineAsync is not changed in this patch because
it is now under refactoring to match the current updates in WebGPU SPEC.

BUG=dawn:529
TEST=dawn_end2end_tests

Change-Id: Ie1cf2f9fc8e18c3e6ad723c6a0cefce29a0eb69c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/45842
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
2021-04-08 01:22:22 +00:00

147 lines
5.9 KiB
C++

// Copyright 2020 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/CreatePipelineAsyncTracker.h"
#include "dawn_native/ComputePipeline.h"
#include "dawn_native/Device.h"
#include "dawn_native/RenderPipeline.h"
namespace dawn_native {
CreatePipelineAsyncTaskBase::CreatePipelineAsyncTaskBase(std::string errorMessage,
void* userdata)
: mErrorMessage(errorMessage), mUserData(userdata) {
}
CreatePipelineAsyncTaskBase::~CreatePipelineAsyncTaskBase() {
}
CreateComputePipelineAsyncTask::CreateComputePipelineAsyncTask(
Ref<ComputePipelineBase> pipeline,
std::string errorMessage,
WGPUCreateComputePipelineAsyncCallback callback,
void* userdata)
: CreatePipelineAsyncTaskBase(errorMessage, userdata),
mPipeline(std::move(pipeline)),
mCreateComputePipelineAsyncCallback(callback) {
}
void CreateComputePipelineAsyncTask::Finish() {
ASSERT(mCreateComputePipelineAsyncCallback != nullptr);
if (mPipeline.Get() != nullptr) {
mCreateComputePipelineAsyncCallback(
WGPUCreatePipelineAsyncStatus_Success,
reinterpret_cast<WGPUComputePipeline>(mPipeline.Detach()), "", mUserData);
} else {
mCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_Error, nullptr,
mErrorMessage.c_str(), mUserData);
}
}
void CreateComputePipelineAsyncTask::HandleShutDown() {
ASSERT(mCreateComputePipelineAsyncCallback != nullptr);
mCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
"Device destroyed before callback", mUserData);
}
void CreateComputePipelineAsyncTask::HandleDeviceLoss() {
ASSERT(mCreateComputePipelineAsyncCallback != nullptr);
mCreateComputePipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceLost, nullptr,
"Device lost before callback", mUserData);
}
CreateRenderPipelineAsyncTask::CreateRenderPipelineAsyncTask(
Ref<RenderPipelineBase> pipeline,
std::string errorMessage,
WGPUCreateRenderPipelineAsyncCallback callback,
void* userdata)
: CreatePipelineAsyncTaskBase(errorMessage, userdata),
mPipeline(std::move(pipeline)),
mCreateRenderPipelineAsyncCallback(callback) {
}
void CreateRenderPipelineAsyncTask::Finish() {
ASSERT(mCreateRenderPipelineAsyncCallback != nullptr);
if (mPipeline.Get() != nullptr) {
mCreateRenderPipelineAsyncCallback(
WGPUCreatePipelineAsyncStatus_Success,
reinterpret_cast<WGPURenderPipeline>(mPipeline.Detach()), "", mUserData);
} else {
mCreateRenderPipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_Error, nullptr,
mErrorMessage.c_str(), mUserData);
}
}
void CreateRenderPipelineAsyncTask::HandleShutDown() {
ASSERT(mCreateRenderPipelineAsyncCallback != nullptr);
mCreateRenderPipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceDestroyed, nullptr,
"Device destroyed before callback", mUserData);
}
void CreateRenderPipelineAsyncTask::HandleDeviceLoss() {
ASSERT(mCreateRenderPipelineAsyncCallback != nullptr);
mCreateRenderPipelineAsyncCallback(WGPUCreatePipelineAsyncStatus_DeviceLost, nullptr,
"Device lost before callback", mUserData);
}
CreatePipelineAsyncTracker::CreatePipelineAsyncTracker(DeviceBase* device) : mDevice(device) {
}
CreatePipelineAsyncTracker::~CreatePipelineAsyncTracker() {
ASSERT(mCreatePipelineAsyncTasksInFlight.Empty());
}
void CreatePipelineAsyncTracker::TrackTask(std::unique_ptr<CreatePipelineAsyncTaskBase> task,
ExecutionSerial serial) {
mCreatePipelineAsyncTasksInFlight.Enqueue(std::move(task), serial);
mDevice->AddFutureSerial(serial);
}
void CreatePipelineAsyncTracker::Tick(ExecutionSerial finishedSerial) {
// If a user calls Queue::Submit inside Create*PipelineAsync, then the device will be
// ticked, which in turns ticks the tracker, causing reentrance here. To prevent the
// reentrant call from invalidating mCreatePipelineAsyncTasksInFlight while in use by the
// first call, we remove the tasks to finish from the queue, update
// mCreatePipelineAsyncTasksInFlight, then run the callbacks.
std::vector<std::unique_ptr<CreatePipelineAsyncTaskBase>> tasks;
for (auto& task : mCreatePipelineAsyncTasksInFlight.IterateUpTo(finishedSerial)) {
tasks.push_back(std::move(task));
}
mCreatePipelineAsyncTasksInFlight.ClearUpTo(finishedSerial);
for (auto& task : tasks) {
if (mDevice->IsLost()) {
task->HandleDeviceLoss();
} else {
task->Finish();
}
}
}
void CreatePipelineAsyncTracker::ClearForShutDown() {
for (auto& task : mCreatePipelineAsyncTasksInFlight.IterateAll()) {
task->HandleShutDown();
}
mCreatePipelineAsyncTasksInFlight.Clear();
}
} // namespace dawn_native