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>
diff --git a/src/dawn_native/CreatePipelineAsyncTask.cpp b/src/dawn_native/CreatePipelineAsyncTask.cpp
index 6a35b94..fae8ad2 100644
--- a/src/dawn_native/CreatePipelineAsyncTask.cpp
+++ b/src/dawn_native/CreatePipelineAsyncTask.cpp
@@ -106,7 +106,7 @@
size_t blueprintHash,
WGPUCreateComputePipelineAsyncCallback callback,
void* userdata)
- : mComputePipeline(nonInitializedComputePipeline),
+ : mComputePipeline(std::move(nonInitializedComputePipeline)),
mBlueprintHash(blueprintHash),
mCallback(callback),
mUserdata(userdata) {
@@ -139,4 +139,41 @@
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
diff --git a/src/dawn_native/CreatePipelineAsyncTask.h b/src/dawn_native/CreatePipelineAsyncTask.h
index 34456aa..33e3e95 100644
--- a/src/dawn_native/CreatePipelineAsyncTask.h
+++ b/src/dawn_native/CreatePipelineAsyncTask.h
@@ -52,17 +52,17 @@
WGPUCreateComputePipelineAsyncCallback mCreateComputePipelineAsyncCallback;
};
- struct CreateRenderPipelineAsyncCallbackTask final : CreatePipelineAsyncCallbackTaskBase {
+ struct CreateRenderPipelineAsyncCallbackTask : CreatePipelineAsyncCallbackTaskBase {
CreateRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
std::string errorMessage,
WGPUCreateRenderPipelineAsyncCallback callback,
void* userdata);
- void Finish() final;
+ void Finish() override;
void HandleShutDown() final;
void HandleDeviceLoss() final;
- private:
+ protected:
Ref<RenderPipelineBase> mPipeline;
WGPUCreateRenderPipelineAsyncCallback mCreateRenderPipelineAsyncCallback;
};
@@ -76,18 +76,37 @@
WGPUCreateComputePipelineAsyncCallback callback,
void* userdata);
- virtual ~CreateComputePipelineAsyncTask() = default;
void Run();
static void RunAsync(std::unique_ptr<CreateComputePipelineAsyncTask> task);
- protected:
+ private:
Ref<ComputePipelineBase> mComputePipeline;
size_t mBlueprintHash;
WGPUCreateComputePipelineAsyncCallback mCallback;
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
#endif // DAWNNATIVE_CREATEPIPELINEASYNCTASK_H_
diff --git a/src/dawn_native/Device.cpp b/src/dawn_native/Device.cpp
index 3696adb..7ade6ab 100644
--- a/src/dawn_native/Device.cpp
+++ b/src/dawn_native/Device.cpp
@@ -1338,9 +1338,8 @@
// Otherwise we will create the pipeline object in CreateRenderPipelineAsyncImpl(),
// where the pipeline object may be created asynchronously and the result will be saved
// to mCreatePipelineAsyncTracker.
- FlatRenderPipelineDescriptor appliedFlatDescriptor(&descriptorWithPipelineLayout);
const size_t blueprintHash = pipelineAndBlueprintFromCache.second;
- CreateRenderPipelineAsyncImpl(&appliedFlatDescriptor, blueprintHash, callback,
+ CreateRenderPipelineAsyncImpl(&descriptorWithPipelineLayout, blueprintHash, callback,
userdata);
}
@@ -1530,8 +1529,8 @@
}
void Finish() final {
- // TODO(jiawei.shao@intel.com): call AddOrGetCachedComputePipeline() asynchronously
- // in CreateComputePipelineAsyncTaskImpl::Run() when the front-end pipeline cache is
+ // TODO(dawn:529): call AddOrGetCachedComputePipeline() asynchronously in
+ // CreateComputePipelineAsyncTaskImpl::Run() when the front-end pipeline cache is
// thread-safe.
if (mPipeline.Get() != nullptr) {
mPipeline = mPipeline->GetDevice()->AddOrGetCachedComputePipeline(
@@ -1550,6 +1549,50 @@
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() {
return PipelineCompatibilityToken(mNextPipelineCompatibilityToken++);
}
diff --git a/src/dawn_native/Device.h b/src/dawn_native/Device.h
index dd59f4f..3688ee1 100644
--- a/src/dawn_native/Device.h
+++ b/src/dawn_native/Device.h
@@ -299,6 +299,11 @@
WGPUCreateComputePipelineAsyncCallback callback,
void* userdata,
size_t blueprintHash);
+ void AddRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
+ std::string errorMessage,
+ WGPUCreateRenderPipelineAsyncCallback callback,
+ void* userdata,
+ size_t blueprintHash);
PipelineCompatibilityToken GetNextPipelineCompatibilityToken();
diff --git a/src/dawn_native/RenderPipeline.cpp b/src/dawn_native/RenderPipeline.cpp
index b417deb..7bdfd0a 100644
--- a/src/dawn_native/RenderPipeline.cpp
+++ b/src/dawn_native/RenderPipeline.cpp
@@ -364,72 +364,6 @@
}
} // 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
size_t IndexFormatSize(wgpu::IndexFormat format) {
switch (format) {
@@ -954,4 +888,8 @@
return true;
}
+
+ MaybeError RenderPipelineBase::Initialize() {
+ return {};
+ }
} // namespace dawn_native
diff --git a/src/dawn_native/RenderPipeline.h b/src/dawn_native/RenderPipeline.h
index 35840b3..ea618da 100644
--- a/src/dawn_native/RenderPipeline.h
+++ b/src/dawn_native/RenderPipeline.h
@@ -29,31 +29,6 @@
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,
const RenderPipelineDescriptor* descriptor);
@@ -130,6 +105,11 @@
private:
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
// Vertex state
uint32_t mVertexBufferCount;
diff --git a/src/dawn_native/d3d12/DeviceD3D12.cpp b/src/dawn_native/d3d12/DeviceD3D12.cpp
index c8c9762..97d5f10 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.cpp
+++ b/src/dawn_native/d3d12/DeviceD3D12.cpp
@@ -382,6 +382,12 @@
void* 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) {
std::unique_ptr<StagingBufferBase> stagingBuffer =
diff --git a/src/dawn_native/d3d12/DeviceD3D12.h b/src/dawn_native/d3d12/DeviceD3D12.h
index 8430969..cefa780 100644
--- a/src/dawn_native/d3d12/DeviceD3D12.h
+++ b/src/dawn_native/d3d12/DeviceD3D12.h
@@ -177,6 +177,10 @@
size_t blueprintHash,
WGPUCreateComputePipelineAsyncCallback callback,
void* userdata) override;
+ void CreateRenderPipelineAsyncImpl(const RenderPipelineDescriptor* descriptor,
+ size_t blueprintHash,
+ WGPUCreateRenderPipelineAsyncCallback callback,
+ void* userdata) override;
void ShutDownImpl() override;
MaybeError WaitForIdleForDestruction() override;
diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
index e30fdb0..75de2b3 100644
--- a/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
+++ b/src/dawn_native/d3d12/RenderPipelineD3D12.cpp
@@ -16,6 +16,8 @@
#include "common/Assert.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/DeviceD3D12.h"
#include "dawn_native/d3d12/PipelineLayoutD3D12.h"
@@ -471,4 +473,16 @@
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
diff --git a/src/dawn_native/d3d12/RenderPipelineD3D12.h b/src/dawn_native/d3d12/RenderPipelineD3D12.h
index ed71da9..199107f 100644
--- a/src/dawn_native/d3d12/RenderPipelineD3D12.h
+++ b/src/dawn_native/d3d12/RenderPipelineD3D12.h
@@ -29,6 +29,11 @@
static ResultOrError<Ref<RenderPipeline>> Create(
Device* device,
const RenderPipelineDescriptor* descriptor);
+ static void CreateAsync(Device* device,
+ const RenderPipelineDescriptor* descriptor,
+ size_t blueprintHash,
+ WGPUCreateRenderPipelineAsyncCallback callback,
+ void* userdata);
RenderPipeline() = delete;
D3D12_PRIMITIVE_TOPOLOGY GetD3D12PrimitiveTopology() const;
@@ -42,7 +47,7 @@
private:
~RenderPipeline() override;
using RenderPipelineBase::RenderPipelineBase;
- MaybeError Initialize();
+ MaybeError Initialize() override;
D3D12_INPUT_LAYOUT_DESC ComputeInputLayout(
std::array<D3D12_INPUT_ELEMENT_DESC, kMaxVertexAttributes>* inputElementDescriptors);
diff --git a/src/dawn_native/metal/RenderPipelineMTL.h b/src/dawn_native/metal/RenderPipelineMTL.h
index 07a2d77..1d81072 100644
--- a/src/dawn_native/metal/RenderPipelineMTL.h
+++ b/src/dawn_native/metal/RenderPipelineMTL.h
@@ -47,7 +47,7 @@
private:
using RenderPipelineBase::RenderPipelineBase;
- MaybeError Initialize();
+ MaybeError Initialize() override;
MTLVertexDescriptor* MakeVertexDesc();
diff --git a/src/dawn_native/opengl/RenderPipelineGL.h b/src/dawn_native/opengl/RenderPipelineGL.h
index 3c7a4d3..1881fcd 100644
--- a/src/dawn_native/opengl/RenderPipelineGL.h
+++ b/src/dawn_native/opengl/RenderPipelineGL.h
@@ -42,7 +42,7 @@
private:
RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
~RenderPipeline() override;
- MaybeError Initialize();
+ MaybeError Initialize() override;
void CreateVAOForVertexState();
diff --git a/src/dawn_native/vulkan/RenderPipelineVk.h b/src/dawn_native/vulkan/RenderPipelineVk.h
index fe653c8..6a9207f 100644
--- a/src/dawn_native/vulkan/RenderPipelineVk.h
+++ b/src/dawn_native/vulkan/RenderPipelineVk.h
@@ -38,7 +38,7 @@
private:
~RenderPipeline() override;
using RenderPipelineBase::RenderPipelineBase;
- MaybeError Initialize();
+ MaybeError Initialize() override;
struct PipelineVertexInputStateCreateInfoTemporaryAllocations {
std::array<VkVertexInputBindingDescription, kMaxVertexBuffers> bindings;
diff --git a/src/tests/end2end/CreatePipelineAsyncTests.cpp b/src/tests/end2end/CreatePipelineAsyncTests.cpp
index eab8bc2..d8b8af9 100644
--- a/src/tests/end2end/CreatePipelineAsyncTests.cpp
+++ b/src/tests/end2end/CreatePipelineAsyncTests.cpp
@@ -68,6 +68,47 @@
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(
const utils::ComboRenderPipelineDescriptor& renderPipelineDescriptor) {
device.CreateRenderPipelineAsync(
@@ -212,36 +253,7 @@
DoCreateRenderPipelineAsync(renderPipelineDescriptor);
- 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 (!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);
+ ValidateCreateRenderPipelineAsync();
}
// Verify the render pipeline created with CreateRenderPipelineAsync() still works when the entry
@@ -448,7 +460,7 @@
// Verify creating compute pipeline with same descriptor and CreateComputePipelineAsync() at the
// same time works correctly.
-TEST_P(CreatePipelineAsyncTest, CreateSamePipelineTwiceAtSameTime) {
+TEST_P(CreatePipelineAsyncTest, CreateSameComputePipelineTwiceAtSameTime) {
wgpu::BindGroupLayoutEntry binding = {};
binding.binding = 0;
binding.buffer.type = wgpu::BufferBindingType::Storage;
@@ -505,6 +517,50 @@
}
}
+// 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.
TEST_P(CreatePipelineAsyncTest, CreateRenderPipelineAsyncWithVertexBufferLayouts) {
wgpu::TextureDescriptor textureDescriptor;