D3D12: Support creating render pipeline asynchronously
This patch implements the asynchronous path of CreateRenderPipelineAsync on D3D12 backend. 1. Call the constructor of dawn_native::d3d12::RenderPipeline in main thread. 2. Execute dawn_native::RenderPipelineBase::Initialize() (a virtual function) asynchronously. 3. Ensure every operation in dawn_native::d3d12::RenderPipeline::Initialize() is thread-safe. 4. Save all the return values (pipeline object or error message, userdata, etc) in a CreateRenderPipelineAsyncWaitableCallbackTask object and insert this callback task into CallbackTaskManager. 5. In Callback.Finish(): - Insert the pipeline object into the pipeline cache if necessary - Call WGPUCreateRenderPipelineAsyncCallback This patch also removes FlatRenderPipelineDescriptor as it is not needed right now. BUG=dawn:529 TEST=dawn_end2end_tests Change-Id: I7fd30339ab7bea599c483dea4bd1100359982409 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/64440 Commit-Queue: Jiawei Shao <jiawei.shao@intel.com> Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
parent
110cac1d09
commit
4ecfc58777
|
@ -106,7 +106,7 @@ namespace dawn_native {
|
||||||
size_t blueprintHash,
|
size_t blueprintHash,
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata)
|
void* userdata)
|
||||||
: mComputePipeline(nonInitializedComputePipeline),
|
: mComputePipeline(std::move(nonInitializedComputePipeline)),
|
||||||
mBlueprintHash(blueprintHash),
|
mBlueprintHash(blueprintHash),
|
||||||
mCallback(callback),
|
mCallback(callback),
|
||||||
mUserdata(userdata) {
|
mUserdata(userdata) {
|
||||||
|
@ -139,4 +139,41 @@ namespace dawn_native {
|
||||||
device->GetAsyncTaskManager()->PostTask(std::move(asyncTask));
|
device->GetAsyncTaskManager()->PostTask(std::move(asyncTask));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CreateRenderPipelineAsyncTask::CreateRenderPipelineAsyncTask(
|
||||||
|
Ref<RenderPipelineBase> nonInitializedRenderPipeline,
|
||||||
|
size_t blueprintHash,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata)
|
||||||
|
: mRenderPipeline(std::move(nonInitializedRenderPipeline)),
|
||||||
|
mBlueprintHash(blueprintHash),
|
||||||
|
mCallback(callback),
|
||||||
|
mUserdata(userdata) {
|
||||||
|
ASSERT(mRenderPipeline != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateRenderPipelineAsyncTask::Run() {
|
||||||
|
MaybeError maybeError = mRenderPipeline->Initialize();
|
||||||
|
std::string errorMessage;
|
||||||
|
if (maybeError.IsError()) {
|
||||||
|
mRenderPipeline = nullptr;
|
||||||
|
errorMessage = maybeError.AcquireError()->GetMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
mRenderPipeline->GetDevice()->AddRenderPipelineAsyncCallbackTask(
|
||||||
|
mRenderPipeline, errorMessage, mCallback, mUserdata, mBlueprintHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateRenderPipelineAsyncTask::RunAsync(
|
||||||
|
std::unique_ptr<CreateRenderPipelineAsyncTask> task) {
|
||||||
|
DeviceBase* device = task->mRenderPipeline->GetDevice();
|
||||||
|
|
||||||
|
// Using "taskPtr = std::move(task)" causes compilation error while it should be supported
|
||||||
|
// since C++14:
|
||||||
|
// https://docs.microsoft.com/en-us/cpp/cpp/lambda-expressions-in-cpp?view=msvc-160
|
||||||
|
auto asyncTask = [taskPtr = task.release()] {
|
||||||
|
std::unique_ptr<CreateRenderPipelineAsyncTask> innerTaskPtr(taskPtr);
|
||||||
|
innerTaskPtr->Run();
|
||||||
|
};
|
||||||
|
device->GetAsyncTaskManager()->PostTask(std::move(asyncTask));
|
||||||
|
}
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -52,17 +52,17 @@ namespace dawn_native {
|
||||||
WGPUCreateComputePipelineAsyncCallback mCreateComputePipelineAsyncCallback;
|
WGPUCreateComputePipelineAsyncCallback mCreateComputePipelineAsyncCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CreateRenderPipelineAsyncCallbackTask final : CreatePipelineAsyncCallbackTaskBase {
|
struct CreateRenderPipelineAsyncCallbackTask : CreatePipelineAsyncCallbackTaskBase {
|
||||||
CreateRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
|
CreateRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
|
||||||
std::string errorMessage,
|
std::string errorMessage,
|
||||||
WGPUCreateRenderPipelineAsyncCallback callback,
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
void* userdata);
|
void* userdata);
|
||||||
|
|
||||||
void Finish() final;
|
void Finish() override;
|
||||||
void HandleShutDown() final;
|
void HandleShutDown() final;
|
||||||
void HandleDeviceLoss() final;
|
void HandleDeviceLoss() final;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
Ref<RenderPipelineBase> mPipeline;
|
Ref<RenderPipelineBase> mPipeline;
|
||||||
WGPUCreateRenderPipelineAsyncCallback mCreateRenderPipelineAsyncCallback;
|
WGPUCreateRenderPipelineAsyncCallback mCreateRenderPipelineAsyncCallback;
|
||||||
};
|
};
|
||||||
|
@ -76,18 +76,37 @@ namespace dawn_native {
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata);
|
void* userdata);
|
||||||
|
|
||||||
virtual ~CreateComputePipelineAsyncTask() = default;
|
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
static void RunAsync(std::unique_ptr<CreateComputePipelineAsyncTask> task);
|
static void RunAsync(std::unique_ptr<CreateComputePipelineAsyncTask> task);
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
Ref<ComputePipelineBase> mComputePipeline;
|
Ref<ComputePipelineBase> mComputePipeline;
|
||||||
size_t mBlueprintHash;
|
size_t mBlueprintHash;
|
||||||
WGPUCreateComputePipelineAsyncCallback mCallback;
|
WGPUCreateComputePipelineAsyncCallback mCallback;
|
||||||
void* mUserdata;
|
void* mUserdata;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// CreateRenderPipelineAsyncTask defines all the inputs and outputs of
|
||||||
|
// CreateRenderPipelineAsync() tasks, which are the same among all the backends.
|
||||||
|
class CreateRenderPipelineAsyncTask {
|
||||||
|
public:
|
||||||
|
CreateRenderPipelineAsyncTask(Ref<RenderPipelineBase> nonInitializedRenderPipeline,
|
||||||
|
size_t blueprintHash,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata);
|
||||||
|
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
static void RunAsync(std::unique_ptr<CreateRenderPipelineAsyncTask> task);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ref<RenderPipelineBase> mRenderPipeline;
|
||||||
|
size_t mBlueprintHash;
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback mCallback;
|
||||||
|
void* mUserdata;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
||||||
#endif // DAWNNATIVE_CREATEPIPELINEASYNCTASK_H_
|
#endif // DAWNNATIVE_CREATEPIPELINEASYNCTASK_H_
|
||||||
|
|
|
@ -1338,9 +1338,8 @@ namespace dawn_native {
|
||||||
// Otherwise we will create the pipeline object in CreateRenderPipelineAsyncImpl(),
|
// Otherwise we will create the pipeline object in CreateRenderPipelineAsyncImpl(),
|
||||||
// where the pipeline object may be created asynchronously and the result will be saved
|
// where the pipeline object may be created asynchronously and the result will be saved
|
||||||
// to mCreatePipelineAsyncTracker.
|
// to mCreatePipelineAsyncTracker.
|
||||||
FlatRenderPipelineDescriptor appliedFlatDescriptor(&descriptorWithPipelineLayout);
|
|
||||||
const size_t blueprintHash = pipelineAndBlueprintFromCache.second;
|
const size_t blueprintHash = pipelineAndBlueprintFromCache.second;
|
||||||
CreateRenderPipelineAsyncImpl(&appliedFlatDescriptor, blueprintHash, callback,
|
CreateRenderPipelineAsyncImpl(&descriptorWithPipelineLayout, blueprintHash, callback,
|
||||||
userdata);
|
userdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1530,8 +1529,8 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Finish() final {
|
void Finish() final {
|
||||||
// TODO(jiawei.shao@intel.com): call AddOrGetCachedComputePipeline() asynchronously
|
// TODO(dawn:529): call AddOrGetCachedComputePipeline() asynchronously in
|
||||||
// in CreateComputePipelineAsyncTaskImpl::Run() when the front-end pipeline cache is
|
// CreateComputePipelineAsyncTaskImpl::Run() when the front-end pipeline cache is
|
||||||
// thread-safe.
|
// thread-safe.
|
||||||
if (mPipeline.Get() != nullptr) {
|
if (mPipeline.Get() != nullptr) {
|
||||||
mPipeline = mPipeline->GetDevice()->AddOrGetCachedComputePipeline(
|
mPipeline = mPipeline->GetDevice()->AddOrGetCachedComputePipeline(
|
||||||
|
@ -1550,6 +1549,50 @@ namespace dawn_native {
|
||||||
std::move(pipeline), errorMessage, callback, userdata, blueprintHash));
|
std::move(pipeline), errorMessage, callback, userdata, blueprintHash));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DeviceBase::AddRenderPipelineAsyncCallbackTask(
|
||||||
|
Ref<RenderPipelineBase> pipeline,
|
||||||
|
std::string errorMessage,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata,
|
||||||
|
size_t blueprintHash) {
|
||||||
|
// CreateRenderPipelineAsyncWaitableCallbackTask is declared as an internal class as it
|
||||||
|
// needs to call the private member function DeviceBase::AddOrGetCachedRenderPipeline().
|
||||||
|
struct CreateRenderPipelineAsyncWaitableCallbackTask final
|
||||||
|
: CreateRenderPipelineAsyncCallbackTask {
|
||||||
|
CreateRenderPipelineAsyncWaitableCallbackTask(
|
||||||
|
Ref<RenderPipelineBase> pipeline,
|
||||||
|
std::string errorMessage,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata,
|
||||||
|
size_t blueprintHash)
|
||||||
|
: CreateRenderPipelineAsyncCallbackTask(std::move(pipeline),
|
||||||
|
errorMessage,
|
||||||
|
callback,
|
||||||
|
userdata),
|
||||||
|
mBlueprintHash(blueprintHash) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finish() final {
|
||||||
|
// TODO(dawn:529): call AddOrGetCachedRenderPipeline() asynchronously in
|
||||||
|
// CreateRenderPipelineAsyncTaskImpl::Run() when the front-end pipeline cache is
|
||||||
|
// thread-safe.
|
||||||
|
if (mPipeline.Get() != nullptr) {
|
||||||
|
mPipeline = mPipeline->GetDevice()->AddOrGetCachedRenderPipeline(
|
||||||
|
mPipeline, mBlueprintHash);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateRenderPipelineAsyncCallbackTask::Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t mBlueprintHash;
|
||||||
|
};
|
||||||
|
|
||||||
|
mCallbackTaskManager->AddCallbackTask(
|
||||||
|
std::make_unique<CreateRenderPipelineAsyncWaitableCallbackTask>(
|
||||||
|
std::move(pipeline), errorMessage, callback, userdata, blueprintHash));
|
||||||
|
}
|
||||||
|
|
||||||
PipelineCompatibilityToken DeviceBase::GetNextPipelineCompatibilityToken() {
|
PipelineCompatibilityToken DeviceBase::GetNextPipelineCompatibilityToken() {
|
||||||
return PipelineCompatibilityToken(mNextPipelineCompatibilityToken++);
|
return PipelineCompatibilityToken(mNextPipelineCompatibilityToken++);
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,6 +299,11 @@ namespace dawn_native {
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata,
|
void* userdata,
|
||||||
size_t blueprintHash);
|
size_t blueprintHash);
|
||||||
|
void AddRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
|
||||||
|
std::string errorMessage,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata,
|
||||||
|
size_t blueprintHash);
|
||||||
|
|
||||||
PipelineCompatibilityToken GetNextPipelineCompatibilityToken();
|
PipelineCompatibilityToken GetNextPipelineCompatibilityToken();
|
||||||
|
|
||||||
|
|
|
@ -364,72 +364,6 @@ namespace dawn_native {
|
||||||
}
|
}
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
// FlatRenderPipelineDescriptor
|
|
||||||
FlatRenderPipelineDescriptor::FlatRenderPipelineDescriptor(
|
|
||||||
const RenderPipelineDescriptor* descriptor)
|
|
||||||
: mLabel(descriptor->label != nullptr ? descriptor->label : ""),
|
|
||||||
mLayout(descriptor->layout),
|
|
||||||
mVertexModule(descriptor->vertex.module),
|
|
||||||
mVertexEntryPoint(descriptor->vertex.entryPoint) {
|
|
||||||
label = mLabel.c_str();
|
|
||||||
|
|
||||||
ASSERT(descriptor->layout != nullptr);
|
|
||||||
layout = mLayout.Get();
|
|
||||||
|
|
||||||
vertex.module = mVertexModule.Get();
|
|
||||||
vertex.entryPoint = mVertexEntryPoint.c_str();
|
|
||||||
vertex.bufferCount = descriptor->vertex.bufferCount;
|
|
||||||
vertex.buffers = mVertexBuffers.data();
|
|
||||||
uint32_t vertexAttributeCount = 0;
|
|
||||||
for (uint32_t vertexBufferIndex = 0; vertexBufferIndex < vertex.bufferCount;
|
|
||||||
++vertexBufferIndex) {
|
|
||||||
const VertexBufferLayout& vertexLayout = descriptor->vertex.buffers[vertexBufferIndex];
|
|
||||||
mVertexBuffers[vertexBufferIndex] = vertexLayout;
|
|
||||||
mVertexBuffers[vertexBufferIndex].attributes = &mVertexAttributes[vertexAttributeCount];
|
|
||||||
for (uint32_t attributeIndex = 0; attributeIndex < vertexLayout.attributeCount;
|
|
||||||
++attributeIndex) {
|
|
||||||
mVertexAttributes[vertexAttributeCount + attributeIndex] =
|
|
||||||
vertexLayout.attributes[attributeIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
vertexAttributeCount += vertexLayout.attributeCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive = descriptor->primitive;
|
|
||||||
|
|
||||||
if (descriptor->depthStencil != nullptr) {
|
|
||||||
mDepthStencilState = *(descriptor->depthStencil);
|
|
||||||
depthStencil = &mDepthStencilState;
|
|
||||||
}
|
|
||||||
|
|
||||||
multisample = descriptor->multisample;
|
|
||||||
|
|
||||||
if (descriptor->fragment != nullptr) {
|
|
||||||
mFragmentModule = descriptor->fragment->module;
|
|
||||||
mFragmentState.module = mFragmentModule.Get();
|
|
||||||
|
|
||||||
mFragmentEntryPoint = descriptor->fragment->entryPoint;
|
|
||||||
mFragmentState.entryPoint = mFragmentEntryPoint.c_str();
|
|
||||||
|
|
||||||
mFragmentState.targetCount = descriptor->fragment->targetCount;
|
|
||||||
|
|
||||||
mFragmentState.targets = mColorTargetStates.data();
|
|
||||||
for (uint32_t colorTargetIndex = 0; colorTargetIndex < mFragmentState.targetCount;
|
|
||||||
++colorTargetIndex) {
|
|
||||||
mColorTargetStates[colorTargetIndex] =
|
|
||||||
descriptor->fragment->targets[colorTargetIndex];
|
|
||||||
|
|
||||||
if (descriptor->fragment->targets[colorTargetIndex].blend != nullptr) {
|
|
||||||
mColorTargetStates[colorTargetIndex].blend = &mBlendStates[colorTargetIndex];
|
|
||||||
mBlendStates[colorTargetIndex] =
|
|
||||||
*(descriptor->fragment->targets[colorTargetIndex].blend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fragment = &mFragmentState;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
size_t IndexFormatSize(wgpu::IndexFormat format) {
|
size_t IndexFormatSize(wgpu::IndexFormat format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
|
@ -954,4 +888,8 @@ namespace dawn_native {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MaybeError RenderPipelineBase::Initialize() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
} // namespace dawn_native
|
} // namespace dawn_native
|
||||||
|
|
|
@ -29,31 +29,6 @@ namespace dawn_native {
|
||||||
|
|
||||||
class DeviceBase;
|
class DeviceBase;
|
||||||
|
|
||||||
// TODO(dawn:529): Use FlatRenderPipelineDescriptor to keep all the members of
|
|
||||||
// RenderPipelineDescriptor (especially the members in pointers) valid when the creation of the
|
|
||||||
// render pipeline is executed asynchronously.
|
|
||||||
struct FlatRenderPipelineDescriptor : public RenderPipelineDescriptor, public NonMovable {
|
|
||||||
public:
|
|
||||||
explicit FlatRenderPipelineDescriptor(const RenderPipelineDescriptor* descriptor);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string mLabel;
|
|
||||||
Ref<PipelineLayoutBase> mLayout;
|
|
||||||
|
|
||||||
Ref<ShaderModuleBase> mVertexModule;
|
|
||||||
std::string mVertexEntryPoint;
|
|
||||||
std::array<VertexBufferLayout, kMaxVertexBuffers> mVertexBuffers;
|
|
||||||
std::array<VertexAttribute, kMaxVertexAttributes> mVertexAttributes;
|
|
||||||
|
|
||||||
FragmentState mFragmentState;
|
|
||||||
Ref<ShaderModuleBase> mFragmentModule;
|
|
||||||
std::string mFragmentEntryPoint;
|
|
||||||
std::array<ColorTargetState, kMaxColorAttachments> mColorTargetStates;
|
|
||||||
std::array<BlendState, kMaxColorAttachments> mBlendStates;
|
|
||||||
|
|
||||||
DepthStencilState mDepthStencilState;
|
|
||||||
};
|
|
||||||
|
|
||||||
MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
|
MaybeError ValidateRenderPipelineDescriptor(DeviceBase* device,
|
||||||
const RenderPipelineDescriptor* descriptor);
|
const RenderPipelineDescriptor* descriptor);
|
||||||
|
|
||||||
|
@ -130,6 +105,11 @@ namespace dawn_native {
|
||||||
private:
|
private:
|
||||||
RenderPipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
RenderPipelineBase(DeviceBase* device, ObjectBase::ErrorTag tag);
|
||||||
|
|
||||||
|
// CreateRenderPipelineAsyncTask is declared as a friend of RenderPipelineBase as it
|
||||||
|
// needs to call the private member function RenderPipelineBase::Initialize().
|
||||||
|
friend class CreateRenderPipelineAsyncTask;
|
||||||
|
virtual MaybeError Initialize();
|
||||||
|
|
||||||
// TODO(dawn:529): store all the following members in a FlatRenderPipelineDescriptor object
|
// TODO(dawn:529): store all the following members in a FlatRenderPipelineDescriptor object
|
||||||
// Vertex state
|
// Vertex state
|
||||||
uint32_t mVertexBufferCount;
|
uint32_t mVertexBufferCount;
|
||||||
|
|
|
@ -382,6 +382,12 @@ namespace dawn_native { namespace d3d12 {
|
||||||
void* userdata) {
|
void* userdata) {
|
||||||
ComputePipeline::CreateAsync(this, descriptor, blueprintHash, callback, userdata);
|
ComputePipeline::CreateAsync(this, descriptor, blueprintHash, callback, userdata);
|
||||||
}
|
}
|
||||||
|
void Device::CreateRenderPipelineAsyncImpl(const RenderPipelineDescriptor* descriptor,
|
||||||
|
size_t blueprintHash,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata) {
|
||||||
|
RenderPipeline::CreateAsync(this, descriptor, blueprintHash, callback, userdata);
|
||||||
|
}
|
||||||
|
|
||||||
ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) {
|
ResultOrError<std::unique_ptr<StagingBufferBase>> Device::CreateStagingBuffer(size_t size) {
|
||||||
std::unique_ptr<StagingBufferBase> stagingBuffer =
|
std::unique_ptr<StagingBufferBase> stagingBuffer =
|
||||||
|
|
|
@ -177,6 +177,10 @@ namespace dawn_native { namespace d3d12 {
|
||||||
size_t blueprintHash,
|
size_t blueprintHash,
|
||||||
WGPUCreateComputePipelineAsyncCallback callback,
|
WGPUCreateComputePipelineAsyncCallback callback,
|
||||||
void* userdata) override;
|
void* userdata) override;
|
||||||
|
void CreateRenderPipelineAsyncImpl(const RenderPipelineDescriptor* descriptor,
|
||||||
|
size_t blueprintHash,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata) override;
|
||||||
|
|
||||||
void ShutDownImpl() override;
|
void ShutDownImpl() override;
|
||||||
MaybeError WaitForIdleForDestruction() override;
|
MaybeError WaitForIdleForDestruction() override;
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#include "common/Assert.h"
|
#include "common/Assert.h"
|
||||||
#include "common/Log.h"
|
#include "common/Log.h"
|
||||||
|
#include "dawn_native/AsyncTask.h"
|
||||||
|
#include "dawn_native/CreatePipelineAsyncTask.h"
|
||||||
#include "dawn_native/d3d12/D3D12Error.h"
|
#include "dawn_native/d3d12/D3D12Error.h"
|
||||||
#include "dawn_native/d3d12/DeviceD3D12.h"
|
#include "dawn_native/d3d12/DeviceD3D12.h"
|
||||||
#include "dawn_native/d3d12/PipelineLayoutD3D12.h"
|
#include "dawn_native/d3d12/PipelineLayoutD3D12.h"
|
||||||
|
@ -471,4 +473,16 @@ namespace dawn_native { namespace d3d12 {
|
||||||
return inputLayoutDescriptor;
|
return inputLayoutDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RenderPipeline::CreateAsync(Device* device,
|
||||||
|
const RenderPipelineDescriptor* descriptor,
|
||||||
|
size_t blueprintHash,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata) {
|
||||||
|
Ref<RenderPipeline> pipeline = AcquireRef(new RenderPipeline(device, descriptor));
|
||||||
|
std::unique_ptr<CreateRenderPipelineAsyncTask> asyncTask =
|
||||||
|
std::make_unique<CreateRenderPipelineAsyncTask>(pipeline, blueprintHash, callback,
|
||||||
|
userdata);
|
||||||
|
CreateRenderPipelineAsyncTask::RunAsync(std::move(asyncTask));
|
||||||
|
}
|
||||||
|
|
||||||
}} // namespace dawn_native::d3d12
|
}} // namespace dawn_native::d3d12
|
||||||
|
|
|
@ -29,6 +29,11 @@ namespace dawn_native { namespace d3d12 {
|
||||||
static ResultOrError<Ref<RenderPipeline>> Create(
|
static ResultOrError<Ref<RenderPipeline>> Create(
|
||||||
Device* device,
|
Device* device,
|
||||||
const RenderPipelineDescriptor* descriptor);
|
const RenderPipelineDescriptor* descriptor);
|
||||||
|
static void CreateAsync(Device* device,
|
||||||
|
const RenderPipelineDescriptor* descriptor,
|
||||||
|
size_t blueprintHash,
|
||||||
|
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||||
|
void* userdata);
|
||||||
RenderPipeline() = delete;
|
RenderPipeline() = delete;
|
||||||
|
|
||||||
D3D12_PRIMITIVE_TOPOLOGY GetD3D12PrimitiveTopology() const;
|
D3D12_PRIMITIVE_TOPOLOGY GetD3D12PrimitiveTopology() const;
|
||||||
|
@ -42,7 +47,7 @@ namespace dawn_native { namespace d3d12 {
|
||||||
private:
|
private:
|
||||||
~RenderPipeline() override;
|
~RenderPipeline() override;
|
||||||
using RenderPipelineBase::RenderPipelineBase;
|
using RenderPipelineBase::RenderPipelineBase;
|
||||||
MaybeError Initialize();
|
MaybeError Initialize() override;
|
||||||
D3D12_INPUT_LAYOUT_DESC ComputeInputLayout(
|
D3D12_INPUT_LAYOUT_DESC ComputeInputLayout(
|
||||||
std::array<D3D12_INPUT_ELEMENT_DESC, kMaxVertexAttributes>* inputElementDescriptors);
|
std::array<D3D12_INPUT_ELEMENT_DESC, kMaxVertexAttributes>* inputElementDescriptors);
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace dawn_native { namespace metal {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using RenderPipelineBase::RenderPipelineBase;
|
using RenderPipelineBase::RenderPipelineBase;
|
||||||
MaybeError Initialize();
|
MaybeError Initialize() override;
|
||||||
|
|
||||||
MTLVertexDescriptor* MakeVertexDesc();
|
MTLVertexDescriptor* MakeVertexDesc();
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ namespace dawn_native { namespace opengl {
|
||||||
private:
|
private:
|
||||||
RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
|
RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
|
||||||
~RenderPipeline() override;
|
~RenderPipeline() override;
|
||||||
MaybeError Initialize();
|
MaybeError Initialize() override;
|
||||||
|
|
||||||
void CreateVAOForVertexState();
|
void CreateVAOForVertexState();
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace dawn_native { namespace vulkan {
|
||||||
private:
|
private:
|
||||||
~RenderPipeline() override;
|
~RenderPipeline() override;
|
||||||
using RenderPipelineBase::RenderPipelineBase;
|
using RenderPipelineBase::RenderPipelineBase;
|
||||||
MaybeError Initialize();
|
MaybeError Initialize() override;
|
||||||
|
|
||||||
struct PipelineVertexInputStateCreateInfoTemporaryAllocations {
|
struct PipelineVertexInputStateCreateInfoTemporaryAllocations {
|
||||||
std::array<VkVertexInputBindingDescription, kMaxVertexBuffers> bindings;
|
std::array<VkVertexInputBindingDescription, kMaxVertexBuffers> bindings;
|
||||||
|
|
|
@ -68,6 +68,47 @@ class CreatePipelineAsyncTest : public DawnTest {
|
||||||
ValidateCreateComputePipelineAsync(&task);
|
ValidateCreateComputePipelineAsync(&task);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ValidateCreateRenderPipelineAsync(CreatePipelineAsyncTask* currentTask) {
|
||||||
|
constexpr wgpu::TextureFormat kRenderAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
|
||||||
|
wgpu::TextureDescriptor textureDescriptor;
|
||||||
|
textureDescriptor.size = {1, 1, 1};
|
||||||
|
textureDescriptor.format = kRenderAttachmentFormat;
|
||||||
|
textureDescriptor.usage =
|
||||||
|
wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
||||||
|
wgpu::Texture outputTexture = device.CreateTexture(&textureDescriptor);
|
||||||
|
|
||||||
|
utils::ComboRenderPassDescriptor renderPassDescriptor({outputTexture.CreateView()});
|
||||||
|
renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
|
||||||
|
renderPassDescriptor.cColorAttachments[0].clearColor = {1.f, 0.f, 0.f, 1.f};
|
||||||
|
|
||||||
|
wgpu::CommandBuffer commands;
|
||||||
|
{
|
||||||
|
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
||||||
|
wgpu::RenderPassEncoder renderPassEncoder =
|
||||||
|
encoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
|
|
||||||
|
while (!currentTask->isCompleted) {
|
||||||
|
WaitABit();
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(currentTask->message.empty());
|
||||||
|
ASSERT_NE(nullptr, currentTask->renderPipeline.Get());
|
||||||
|
|
||||||
|
renderPassEncoder.SetPipeline(currentTask->renderPipeline);
|
||||||
|
renderPassEncoder.Draw(1);
|
||||||
|
renderPassEncoder.EndPass();
|
||||||
|
commands = encoder.Finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.Submit(1, &commands);
|
||||||
|
|
||||||
|
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), outputTexture, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ValidateCreateRenderPipelineAsync() {
|
||||||
|
ValidateCreateRenderPipelineAsync(&task);
|
||||||
|
}
|
||||||
|
|
||||||
void DoCreateRenderPipelineAsync(
|
void DoCreateRenderPipelineAsync(
|
||||||
const utils::ComboRenderPipelineDescriptor& renderPipelineDescriptor) {
|
const utils::ComboRenderPipelineDescriptor& renderPipelineDescriptor) {
|
||||||
device.CreateRenderPipelineAsync(
|
device.CreateRenderPipelineAsync(
|
||||||
|
@ -212,36 +253,7 @@ TEST_P(CreatePipelineAsyncTest, BasicUseOfCreateRenderPipelineAsync) {
|
||||||
|
|
||||||
DoCreateRenderPipelineAsync(renderPipelineDescriptor);
|
DoCreateRenderPipelineAsync(renderPipelineDescriptor);
|
||||||
|
|
||||||
wgpu::TextureDescriptor textureDescriptor;
|
ValidateCreateRenderPipelineAsync();
|
||||||
textureDescriptor.size = {1, 1, 1};
|
|
||||||
textureDescriptor.format = kRenderAttachmentFormat;
|
|
||||||
textureDescriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
|
|
||||||
wgpu::Texture outputTexture = device.CreateTexture(&textureDescriptor);
|
|
||||||
|
|
||||||
utils::ComboRenderPassDescriptor renderPassDescriptor({outputTexture.CreateView()});
|
|
||||||
renderPassDescriptor.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
|
|
||||||
renderPassDescriptor.cColorAttachments[0].clearColor = {1.f, 0.f, 0.f, 1.f};
|
|
||||||
|
|
||||||
wgpu::CommandBuffer commands;
|
|
||||||
{
|
|
||||||
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
|
|
||||||
wgpu::RenderPassEncoder renderPassEncoder = encoder.BeginRenderPass(&renderPassDescriptor);
|
|
||||||
|
|
||||||
while (!task.isCompleted) {
|
|
||||||
WaitABit();
|
|
||||||
}
|
|
||||||
ASSERT_TRUE(task.message.empty());
|
|
||||||
ASSERT_NE(nullptr, task.renderPipeline.Get());
|
|
||||||
|
|
||||||
renderPassEncoder.SetPipeline(task.renderPipeline);
|
|
||||||
renderPassEncoder.Draw(1);
|
|
||||||
renderPassEncoder.EndPass();
|
|
||||||
commands = encoder.Finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
queue.Submit(1, &commands);
|
|
||||||
|
|
||||||
EXPECT_PIXEL_RGBA8_EQ(RGBA8(0, 255, 0, 255), outputTexture, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify the render pipeline created with CreateRenderPipelineAsync() still works when the entry
|
// Verify the render pipeline created with CreateRenderPipelineAsync() still works when the entry
|
||||||
|
@ -448,7 +460,7 @@ TEST_P(CreatePipelineAsyncTest, CreateSameComputePipelineTwice) {
|
||||||
|
|
||||||
// Verify creating compute pipeline with same descriptor and CreateComputePipelineAsync() at the
|
// Verify creating compute pipeline with same descriptor and CreateComputePipelineAsync() at the
|
||||||
// same time works correctly.
|
// same time works correctly.
|
||||||
TEST_P(CreatePipelineAsyncTest, CreateSamePipelineTwiceAtSameTime) {
|
TEST_P(CreatePipelineAsyncTest, CreateSameComputePipelineTwiceAtSameTime) {
|
||||||
wgpu::BindGroupLayoutEntry binding = {};
|
wgpu::BindGroupLayoutEntry binding = {};
|
||||||
binding.binding = 0;
|
binding.binding = 0;
|
||||||
binding.buffer.type = wgpu::BufferBindingType::Storage;
|
binding.buffer.type = wgpu::BufferBindingType::Storage;
|
||||||
|
@ -505,6 +517,50 @@ TEST_P(CreatePipelineAsyncTest, CreateSamePipelineTwiceAtSameTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify the basic use of CreateRenderPipelineAsync() works on all backends.
|
||||||
|
TEST_P(CreatePipelineAsyncTest, CreateSameRenderPipelineTwiceAtSameTime) {
|
||||||
|
constexpr wgpu::TextureFormat kRenderAttachmentFormat = wgpu::TextureFormat::RGBA8Unorm;
|
||||||
|
|
||||||
|
utils::ComboRenderPipelineDescriptor renderPipelineDescriptor;
|
||||||
|
wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
|
||||||
|
[[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 0.0, 0.0, 1.0);
|
||||||
|
})");
|
||||||
|
wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
|
||||||
|
[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
|
||||||
|
return vec4<f32>(0.0, 1.0, 0.0, 1.0);
|
||||||
|
})");
|
||||||
|
renderPipelineDescriptor.vertex.module = vsModule;
|
||||||
|
renderPipelineDescriptor.cFragment.module = fsModule;
|
||||||
|
renderPipelineDescriptor.cTargets[0].format = kRenderAttachmentFormat;
|
||||||
|
renderPipelineDescriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
|
||||||
|
|
||||||
|
auto callback = [](WGPUCreatePipelineAsyncStatus status, WGPURenderPipeline returnPipeline,
|
||||||
|
const char* message, void* userdata) {
|
||||||
|
EXPECT_EQ(WGPUCreatePipelineAsyncStatus::WGPUCreatePipelineAsyncStatus_Success, status);
|
||||||
|
|
||||||
|
CreatePipelineAsyncTask* task = static_cast<CreatePipelineAsyncTask*>(userdata);
|
||||||
|
task->renderPipeline = wgpu::RenderPipeline::Acquire(returnPipeline);
|
||||||
|
task->isCompleted = true;
|
||||||
|
task->message = message;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create two render pipelines with same descriptor.
|
||||||
|
CreatePipelineAsyncTask anotherTask;
|
||||||
|
device.CreateRenderPipelineAsync(&renderPipelineDescriptor, callback, &task);
|
||||||
|
device.CreateRenderPipelineAsync(&renderPipelineDescriptor, callback, &anotherTask);
|
||||||
|
|
||||||
|
// Verify task.renderPipeline and anotherTask.renderPipeline are both created correctly.
|
||||||
|
ValidateCreateRenderPipelineAsync(&task);
|
||||||
|
ValidateCreateRenderPipelineAsync(&anotherTask);
|
||||||
|
|
||||||
|
// Verify task.renderPipeline and anotherTask.renderPipeline are pointing to the same Dawn
|
||||||
|
// object.
|
||||||
|
if (!UsesWire()) {
|
||||||
|
EXPECT_EQ(task.renderPipeline.Get(), anotherTask.renderPipeline.Get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Verify calling CreateRenderPipelineAsync() with valid VertexBufferLayouts works on all backends.
|
// Verify calling CreateRenderPipelineAsync() with valid VertexBufferLayouts works on all backends.
|
||||||
TEST_P(CreatePipelineAsyncTest, CreateRenderPipelineAsyncWithVertexBufferLayouts) {
|
TEST_P(CreatePipelineAsyncTest, CreateRenderPipelineAsyncWithVertexBufferLayouts) {
|
||||||
wgpu::TextureDescriptor textureDescriptor;
|
wgpu::TextureDescriptor textureDescriptor;
|
||||||
|
|
Loading…
Reference in New Issue