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,
|
||||
WGPUCreateComputePipelineAsyncCallback callback,
|
||||
void* userdata)
|
||||
: mComputePipeline(nonInitializedComputePipeline),
|
||||
: mComputePipeline(std::move(nonInitializedComputePipeline)),
|
||||
mBlueprintHash(blueprintHash),
|
||||
mCallback(callback),
|
||||
mUserdata(userdata) {
|
||||
|
@ -139,4 +139,41 @@ namespace dawn_native {
|
|||
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
|
||||
|
|
|
@ -52,17 +52,17 @@ namespace dawn_native {
|
|||
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 @@ namespace dawn_native {
|
|||
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_
|
||||
|
|
|
@ -1338,9 +1338,8 @@ namespace dawn_native {
|
|||
// 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 @@ namespace dawn_native {
|
|||
}
|
||||
|
||||
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 @@ namespace dawn_native {
|
|||
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++);
|
||||
}
|
||||
|
|
|
@ -299,6 +299,11 @@ namespace dawn_native {
|
|||
WGPUCreateComputePipelineAsyncCallback callback,
|
||||
void* userdata,
|
||||
size_t blueprintHash);
|
||||
void AddRenderPipelineAsyncCallbackTask(Ref<RenderPipelineBase> pipeline,
|
||||
std::string errorMessage,
|
||||
WGPUCreateRenderPipelineAsyncCallback callback,
|
||||
void* userdata,
|
||||
size_t blueprintHash);
|
||||
|
||||
PipelineCompatibilityToken GetNextPipelineCompatibilityToken();
|
||||
|
||||
|
|
|
@ -364,72 +364,6 @@ namespace dawn_native {
|
|||
}
|
||||
} // 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 @@ namespace dawn_native {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
MaybeError RenderPipelineBase::Initialize() {
|
||||
return {};
|
||||
}
|
||||
} // namespace dawn_native
|
||||
|
|
|
@ -29,31 +29,6 @@ namespace dawn_native {
|
|||
|
||||
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 @@ namespace dawn_native {
|
|||
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;
|
||||
|
|
|
@ -382,6 +382,12 @@ namespace dawn_native { namespace d3d12 {
|
|||
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 =
|
||||
|
|
|
@ -177,6 +177,10 @@ namespace dawn_native { namespace d3d12 {
|
|||
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;
|
||||
|
|
|
@ -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 @@ namespace dawn_native { namespace d3d12 {
|
|||
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
|
||||
|
|
|
@ -29,6 +29,11 @@ namespace dawn_native { namespace d3d12 {
|
|||
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 @@ namespace dawn_native { namespace d3d12 {
|
|||
private:
|
||||
~RenderPipeline() override;
|
||||
using RenderPipelineBase::RenderPipelineBase;
|
||||
MaybeError Initialize();
|
||||
MaybeError Initialize() override;
|
||||
D3D12_INPUT_LAYOUT_DESC ComputeInputLayout(
|
||||
std::array<D3D12_INPUT_ELEMENT_DESC, kMaxVertexAttributes>* inputElementDescriptors);
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ namespace dawn_native { namespace metal {
|
|||
|
||||
private:
|
||||
using RenderPipelineBase::RenderPipelineBase;
|
||||
MaybeError Initialize();
|
||||
MaybeError Initialize() override;
|
||||
|
||||
MTLVertexDescriptor* MakeVertexDesc();
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ namespace dawn_native { namespace opengl {
|
|||
private:
|
||||
RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
|
||||
~RenderPipeline() override;
|
||||
MaybeError Initialize();
|
||||
MaybeError Initialize() override;
|
||||
|
||||
void CreateVAOForVertexState();
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace dawn_native { namespace vulkan {
|
|||
private:
|
||||
~RenderPipeline() override;
|
||||
using RenderPipelineBase::RenderPipelineBase;
|
||||
MaybeError Initialize();
|
||||
MaybeError Initialize() override;
|
||||
|
||||
struct PipelineVertexInputStateCreateInfoTemporaryAllocations {
|
||||
std::array<VkVertexInputBindingDescription, kMaxVertexBuffers> bindings;
|
||||
|
|
|
@ -68,6 +68,47 @@ class CreatePipelineAsyncTest : public DawnTest {
|
|||
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 @@ TEST_P(CreatePipelineAsyncTest, BasicUseOfCreateRenderPipelineAsync) {
|
|||
|
||||
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 @@ TEST_P(CreatePipelineAsyncTest, CreateSameComputePipelineTwice) {
|
|||
|
||||
// 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 @@ 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.
|
||||
TEST_P(CreatePipelineAsyncTest, CreateRenderPipelineAsyncWithVertexBufferLayouts) {
|
||||
wgpu::TextureDescriptor textureDescriptor;
|
||||
|
|
Loading…
Reference in New Issue