D3D12 replace ASSERT_SUCCESS with better error handling

Added D3D12Error to check HRESULTS and return error messages
with the correct error names.

Remove ASSERT_SUCCESS from D3D12 backend and use
MaybeError and ResultError instead to handle errors.


Bug: dawn:19
Change-Id: Idf2f1987725e7e658bd29a9b13653125ab43c564
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12000
Reviewed-by: Rafael Cintron <rafael.cintron@microsoft.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
This commit is contained in:
Natasha Lee 2019-10-16 09:26:54 +00:00 committed by Commit Bot service account
parent c7d535bd72
commit f3d50baf85
15 changed files with 175 additions and 64 deletions

View File

@ -260,6 +260,8 @@ source_set("libdawn_native_sources") {
"src/dawn_native/d3d12/CommittedResourceAllocatorD3D12.h",
"src/dawn_native/d3d12/ComputePipelineD3D12.cpp",
"src/dawn_native/d3d12/ComputePipelineD3D12.h",
"src/dawn_native/d3d12/D3D12Error.cpp",
"src/dawn_native/d3d12/D3D12Error.h",
"src/dawn_native/d3d12/D3D12Info.cpp",
"src/dawn_native/d3d12/D3D12Info.h",
"src/dawn_native/d3d12/DescriptorHeapAllocator.cpp",

View File

@ -18,6 +18,7 @@
#include "common/Constants.h"
#include "common/Math.h"
#include "dawn_native/d3d12/CommandRecordingContext.h"
#include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
namespace dawn_native { namespace d3d12 {
@ -217,8 +218,9 @@ namespace dawn_native { namespace d3d12 {
MaybeError Buffer::MapAtCreationImpl(uint8_t** mappedPointer) {
mWrittenMappedRange = {0, GetSize()};
ASSERT_SUCCESS(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
reinterpret_cast<void**>(mappedPointer)));
DAWN_TRY(CheckHRESULT(GetD3D12Resource()->Map(0, &mWrittenMappedRange,
reinterpret_cast<void**>(mappedPointer)),
"D3D12 map at creation"));
return {};
}
@ -226,7 +228,9 @@ namespace dawn_native { namespace d3d12 {
mWrittenMappedRange = {};
D3D12_RANGE readRange = {0, GetSize()};
char* data = nullptr;
ASSERT_SUCCESS(GetD3D12Resource()->Map(0, &readRange, reinterpret_cast<void**>(&data)));
DAWN_TRY(
CheckHRESULT(GetD3D12Resource()->Map(0, &readRange, reinterpret_cast<void**>(&data)),
"D3D12 map read async"));
// There is no need to transition the resource to a new state: D3D12 seems to make the GPU
// writes available when the fence is passed.
MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();
@ -237,8 +241,9 @@ namespace dawn_native { namespace d3d12 {
MaybeError Buffer::MapWriteAsyncImpl(uint32_t serial) {
mWrittenMappedRange = {0, GetSize()};
char* data = nullptr;
ASSERT_SUCCESS(
GetD3D12Resource()->Map(0, &mWrittenMappedRange, reinterpret_cast<void**>(&data)));
DAWN_TRY(CheckHRESULT(
GetD3D12Resource()->Map(0, &mWrittenMappedRange, reinterpret_cast<void**>(&data)),
"D3D12 map write async"));
// There is no need to transition the resource to a new state: D3D12 seems to make the CPU
// writes available on queue submission.
MapRequestTracker* tracker = ToBackend(GetDevice())->GetMapRequestTracker();

View File

@ -14,6 +14,7 @@
#include "dawn_native/d3d12/CommandAllocatorManager.h"
#include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
#include "common/Assert.h"
@ -26,12 +27,12 @@ namespace dawn_native { namespace d3d12 {
mFreeAllocators.set();
}
ComPtr<ID3D12CommandAllocator> CommandAllocatorManager::ReserveCommandAllocator() {
ResultOrError<ID3D12CommandAllocator*> CommandAllocatorManager::ReserveCommandAllocator() {
// If there are no free allocators, get the oldest serial in flight and wait on it
if (mFreeAllocators.none()) {
const uint64_t firstSerial = mInFlightCommandAllocators.FirstSerial();
device->WaitForSerial(firstSerial);
Tick(firstSerial);
DAWN_TRY(device->WaitForSerial(firstSerial));
DAWN_TRY(Tick(firstSerial));
}
ASSERT(mFreeAllocators.any());
@ -42,8 +43,10 @@ namespace dawn_native { namespace d3d12 {
if (firstFreeIndex >= mAllocatorCount) {
ASSERT(firstFreeIndex == mAllocatorCount);
mAllocatorCount++;
ASSERT_SUCCESS(device->GetD3D12Device()->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&mCommandAllocators[firstFreeIndex])));
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(&mCommandAllocators[firstFreeIndex])),
"D3D12 create command allocator"));
}
// Mark the command allocator as used
@ -53,17 +56,17 @@ namespace dawn_native { namespace d3d12 {
// ExecuteCommandLists
mInFlightCommandAllocators.Enqueue({mCommandAllocators[firstFreeIndex], firstFreeIndex},
device->GetPendingCommandSerial());
return mCommandAllocators[firstFreeIndex];
return mCommandAllocators[firstFreeIndex].Get();
}
void CommandAllocatorManager::Tick(uint64_t lastCompletedSerial) {
MaybeError CommandAllocatorManager::Tick(uint64_t lastCompletedSerial) {
// Reset all command allocators that are no longer in flight
for (auto it : mInFlightCommandAllocators.IterateUpTo(lastCompletedSerial)) {
ASSERT_SUCCESS(it.commandAllocator->Reset());
DAWN_TRY(CheckHRESULT(it.commandAllocator->Reset(), "D3D12 reset command allocator"));
mFreeAllocators.set(it.index);
}
mInFlightCommandAllocators.ClearUpTo(lastCompletedSerial);
return {};
}
}} // namespace dawn_native::d3d12

View File

@ -18,6 +18,7 @@
#include "dawn_native/d3d12/d3d12_platform.h"
#include "common/SerialQueue.h"
#include "dawn_native/Error.h"
#include <bitset>
@ -31,8 +32,8 @@ namespace dawn_native { namespace d3d12 {
// A CommandAllocator that is reserved must be used on the next ExecuteCommandLists
// otherwise its commands may be reset before execution has completed on the GPU
ComPtr<ID3D12CommandAllocator> ReserveCommandAllocator();
void Tick(uint64_t lastCompletedSerial);
ResultOrError<ID3D12CommandAllocator*> ReserveCommandAllocator();
MaybeError Tick(uint64_t lastCompletedSerial);
private:
Device* device;

View File

@ -13,28 +13,28 @@
// limitations under the License.
#include "dawn_native/d3d12/CommandRecordingContext.h"
#include "dawn_native/d3d12/CommandAllocatorManager.h"
#include "dawn_native/d3d12/D3D12Error.h"
namespace dawn_native { namespace d3d12 {
MaybeError CommandRecordingContext::Open(ID3D12Device* d3d12Device,
CommandAllocatorManager* commandAllocationManager) {
ASSERT(!IsOpen());
ID3D12CommandAllocator* commandAllocator;
DAWN_TRY_ASSIGN(commandAllocator, commandAllocationManager->ReserveCommandAllocator());
if (mD3d12CommandList != nullptr) {
const HRESULT hr = mD3d12CommandList->Reset(
commandAllocationManager->ReserveCommandAllocator().Get(), nullptr);
if (FAILED(hr)) {
MaybeError error = CheckHRESULT(mD3d12CommandList->Reset(commandAllocator, nullptr),
"D3D12 resetting command list");
if (error.IsError()) {
mD3d12CommandList.Reset();
return DAWN_DEVICE_LOST_ERROR("Error resetting command list.");
DAWN_TRY(std::move(error));
}
} else {
ComPtr<ID3D12GraphicsCommandList> d3d12GraphicsCommandList;
const HRESULT hr = d3d12Device->CreateCommandList(
0, D3D12_COMMAND_LIST_TYPE_DIRECT,
commandAllocationManager->ReserveCommandAllocator().Get(), nullptr,
IID_PPV_ARGS(&d3d12GraphicsCommandList));
if (FAILED(hr)) {
return DAWN_DEVICE_LOST_ERROR("Error creating a direct command list.");
}
DAWN_TRY(CheckHRESULT(
d3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, commandAllocator,
nullptr, IID_PPV_ARGS(&d3d12GraphicsCommandList)),
"D3D12 creating direct command list"));
mD3d12CommandList = std::move(d3d12GraphicsCommandList);
}
@ -46,10 +46,11 @@ namespace dawn_native { namespace d3d12 {
ResultOrError<ID3D12GraphicsCommandList*> CommandRecordingContext::Close() {
ASSERT(IsOpen());
mIsOpen = false;
const HRESULT hr = mD3d12CommandList->Close();
if (FAILED(hr)) {
MaybeError error =
CheckHRESULT(mD3d12CommandList->Close(), "D3D12 closing pending command list");
if (error.IsError()) {
mD3d12CommandList.Reset();
return DAWN_DEVICE_LOST_ERROR("Error closing pending command list.");
DAWN_TRY(std::move(error));
}
return mD3d12CommandList.Get();
}

View File

@ -0,0 +1,29 @@
// Copyright 2019 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/d3d12/D3D12Error.h"
#include <string>
namespace dawn_native { namespace d3d12 {
MaybeError CheckHRESULT(HRESULT result, const char* context) {
if (DAWN_LIKELY(SUCCEEDED(result))) {
return {};
}
std::string message = std::string(context) + " failed with " + std::to_string(result);
return DAWN_DEVICE_LOST_ERROR(message);
}
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,28 @@
// Copyright 2019 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.
#ifndef DAWNNATIVE_D3D12_D3D12ERROR_H_
#define DAWNNATIVE_D3D12_D3D12ERROR_H_
#include <d3d12.h>
#include "dawn_native/Error.h"
namespace dawn_native { namespace d3d12 {
// Returns a success only if result of HResult is success
MaybeError CheckHRESULT(HRESULT result, const char* context);
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_D3D12ERROR_H_

View File

@ -25,6 +25,7 @@
#include "dawn_native/d3d12/CommandAllocatorManager.h"
#include "dawn_native/d3d12/CommandBufferD3D12.h"
#include "dawn_native/d3d12/ComputePipelineD3D12.h"
#include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DescriptorHeapAllocator.h"
#include "dawn_native/d3d12/PipelineLayoutD3D12.h"
#include "dawn_native/d3d12/PlatformFunctions.h"
@ -56,10 +57,14 @@ namespace dawn_native { namespace d3d12 {
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
ASSERT_SUCCESS(mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)));
DAWN_TRY(
CheckHRESULT(mD3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&mCommandQueue)),
"D3D12 create command queue"));
DAWN_TRY(CheckHRESULT(mD3d12Device->CreateFence(mLastSubmittedSerial, D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&mFence)),
"D3D12 create fence"));
ASSERT_SUCCESS(mD3d12Device->CreateFence(mLastSubmittedSerial, D3D12_FENCE_FLAG_NONE,
IID_PPV_ARGS(&mFence)));
mFenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
ASSERT(mFenceEvent != nullptr);
@ -70,7 +75,7 @@ namespace dawn_native { namespace d3d12 {
mResourceAllocator = std::make_unique<ResourceAllocator>(this);
mResourceAllocatorManager = std::make_unique<ResourceAllocatorManager>(this);
NextSerial();
DAWN_TRY(NextSerial());
// Initialize indirect commands
D3D12_INDIRECT_ARGUMENT_DESC argumentDesc = {};
@ -103,8 +108,9 @@ namespace dawn_native { namespace d3d12 {
// Immediately forget about all pending commands
mPendingCommands.Release();
NextSerial();
WaitForSerial(mLastSubmittedSerial); // Wait for all in-flight commands to finish executing
ConsumedError(NextSerial());
// Wait for all in-flight commands to finish executing
ConsumedError(WaitForSerial(mLastSubmittedSerial));
// Call tick one last time so resources are cleaned up. Ignore the return value so we can
// continue shutting down in an orderly fashion.
@ -207,27 +213,29 @@ namespace dawn_native { namespace d3d12 {
mDynamicUploader->Deallocate(mCompletedSerial);
mResourceAllocator->Tick(mCompletedSerial);
mCommandAllocatorManager->Tick(mCompletedSerial);
DAWN_TRY(mCommandAllocatorManager->Tick(mCompletedSerial));
mDescriptorHeapAllocator->Deallocate(mCompletedSerial);
mMapRequestTracker->Tick(mCompletedSerial);
mUsedComObjectRefs.ClearUpTo(mCompletedSerial);
DAWN_TRY(ExecuteCommandContext(nullptr));
NextSerial();
DAWN_TRY(NextSerial());
return {};
}
void Device::NextSerial() {
MaybeError Device::NextSerial() {
mLastSubmittedSerial++;
ASSERT_SUCCESS(mCommandQueue->Signal(mFence.Get(), mLastSubmittedSerial));
return CheckHRESULT(mCommandQueue->Signal(mFence.Get(), mLastSubmittedSerial),
"D3D12 command queue signal fence");
}
void Device::WaitForSerial(uint64_t serial) {
MaybeError Device::WaitForSerial(uint64_t serial) {
mCompletedSerial = mFence->GetCompletedValue();
if (mCompletedSerial < serial) {
ASSERT_SUCCESS(mFence->SetEventOnCompletion(serial, mFenceEvent));
DAWN_TRY(CheckHRESULT(mFence->SetEventOnCompletion(serial, mFenceEvent),
"D3D12 set event on completion"));
WaitForSingleObject(mFenceEvent, INFINITE);
}
return {};
}
void Device::ReferenceUntilUnused(ComPtr<IUnknown> object) {
@ -279,14 +287,14 @@ namespace dawn_native { namespace d3d12 {
}
ResultOrError<PipelineLayoutBase*> Device::CreatePipelineLayoutImpl(
const PipelineLayoutDescriptor* descriptor) {
return new PipelineLayout(this, descriptor);
return PipelineLayout::Create(this, descriptor);
}
ResultOrError<QueueBase*> Device::CreateQueueImpl() {
return new Queue(this);
}
ResultOrError<RenderPipelineBase*> Device::CreateRenderPipelineImpl(
const RenderPipelineDescriptor* descriptor) {
return new RenderPipeline(this, descriptor);
return RenderPipeline::Create(this, descriptor);
}
ResultOrError<SamplerBase*> Device::CreateSamplerImpl(const SamplerDescriptor* descriptor) {
return new Sampler(this, descriptor);

View File

@ -73,8 +73,8 @@ namespace dawn_native { namespace d3d12 {
ResultOrError<CommandRecordingContext*> GetPendingCommandContext();
Serial GetPendingCommandSerial() const override;
void NextSerial();
void WaitForSerial(Serial serial);
MaybeError NextSerial();
MaybeError WaitForSerial(Serial serial);
void ReferenceUntilUnused(ComPtr<IUnknown> object);

View File

@ -95,7 +95,7 @@ namespace dawn_native { namespace d3d12 {
// TODO(cwallez@chromium.org) Currently we force the CPU to wait for the GPU to be finished
// with the buffer. Ideally the synchronization should be all done on the GPU.
mDevice->WaitForSerial(mBufferSerials[mCurrentBuffer]);
ASSERT(mDevice->WaitForSerial(mBufferSerials[mCurrentBuffer]).IsSuccess());
return DAWN_SWAP_CHAIN_NO_ERROR;
}
@ -105,7 +105,7 @@ namespace dawn_native { namespace d3d12 {
ASSERT_SUCCESS(mSwapChain->Present(1, 0));
// TODO(cwallez@chromium.org): Make the serial ticking implicit.
mDevice->NextSerial();
ASSERT(mDevice->NextSerial().IsSuccess());
mBufferSerials[mCurrentBuffer] = mDevice->GetPendingCommandSerial();
return DAWN_SWAP_CHAIN_NO_ERROR;

View File

@ -17,6 +17,7 @@
#include "common/Assert.h"
#include "common/BitSetIterator.h"
#include "dawn_native/d3d12/BindGroupLayoutD3D12.h"
#include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/PlatformFunctions.h"
@ -54,8 +55,17 @@ namespace dawn_native { namespace d3d12 {
}
} // anonymous namespace
PipelineLayout::PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor)
: PipelineLayoutBase(device, descriptor) {
ResultOrError<PipelineLayout*> PipelineLayout::Create(
Device* device,
const PipelineLayoutDescriptor* descriptor) {
std::unique_ptr<PipelineLayout> layout =
std::make_unique<PipelineLayout>(device, descriptor);
DAWN_TRY(layout->Initialize());
return layout.release();
}
MaybeError PipelineLayout::Initialize() {
Device* device = ToBackend(GetDevice());
D3D12_ROOT_PARAMETER rootParameters[kMaxBindGroups * 2 + kMaxDynamicBufferCount];
// A root parameter is one of these types
@ -148,11 +158,15 @@ namespace dawn_native { namespace d3d12 {
ComPtr<ID3DBlob> signature;
ComPtr<ID3DBlob> error;
ASSERT_SUCCESS(device->GetFunctions()->d3d12SerializeRootSignature(
&rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error));
ASSERT_SUCCESS(device->GetD3D12Device()->CreateRootSignature(
DAWN_TRY(CheckHRESULT(
device->GetFunctions()->d3d12SerializeRootSignature(
&rootSignatureDescriptor, D3D_ROOT_SIGNATURE_VERSION_1, &signature, &error),
"D3D12 serialize root signature"));
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateRootSignature(
0, signature->GetBufferPointer(), signature->GetBufferSize(),
IID_PPV_ARGS(&mRootSignature)));
IID_PPV_ARGS(&mRootSignature)),
"D3D12 create root signature"));
return {};
}
uint32_t PipelineLayout::GetCbvUavSrvRootParameterIndex(uint32_t group) const {

View File

@ -25,7 +25,8 @@ namespace dawn_native { namespace d3d12 {
class PipelineLayout : public PipelineLayoutBase {
public:
PipelineLayout(Device* device, const PipelineLayoutDescriptor* descriptor);
static ResultOrError<PipelineLayout*> Create(Device* device,
const PipelineLayoutDescriptor* descriptor);
uint32_t GetCbvUavSrvRootParameterIndex(uint32_t group) const;
uint32_t GetSamplerRootParameterIndex(uint32_t group) const;
@ -36,6 +37,8 @@ namespace dawn_native { namespace d3d12 {
ComPtr<ID3D12RootSignature> GetRootSignature() const;
private:
using PipelineLayoutBase::PipelineLayoutBase;
MaybeError Initialize();
std::array<uint32_t, kMaxBindGroups> mCbvUavSrvRootParameterInfo;
std::array<uint32_t, kMaxBindGroups> mSamplerRootParameterInfo;
std::array<std::array<uint32_t, kMaxBindingsPerGroup>, kMaxBindGroups>

View File

@ -15,6 +15,7 @@
#include "dawn_native/d3d12/QueueD3D12.h"
#include "dawn_native/d3d12/CommandBufferD3D12.h"
#include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
namespace dawn_native { namespace d3d12 {
@ -35,7 +36,7 @@ namespace dawn_native { namespace d3d12 {
DAWN_TRY(device->ExecuteCommandContext(&mCommandContext));
device->NextSerial();
DAWN_TRY(device->NextSerial());
return {};
}

View File

@ -15,6 +15,7 @@
#include "dawn_native/d3d12/RenderPipelineD3D12.h"
#include "common/Assert.h"
#include "dawn_native/d3d12/D3D12Error.h"
#include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/PipelineLayoutD3D12.h"
#include "dawn_native/d3d12/PlatformFunctions.h"
@ -288,9 +289,17 @@ namespace dawn_native { namespace d3d12 {
} // anonymous namespace
RenderPipeline::RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor)
: RenderPipelineBase(device, descriptor),
mD3d12PrimitiveTopology(D3D12PrimitiveTopology(GetPrimitiveTopology())) {
ResultOrError<RenderPipeline*> RenderPipeline::Create(
Device* device,
const RenderPipelineDescriptor* descriptor) {
std::unique_ptr<RenderPipeline> pipeline =
std::make_unique<RenderPipeline>(device, descriptor);
DAWN_TRY(pipeline->Initialize(descriptor));
return pipeline.release();
}
MaybeError RenderPipeline::Initialize(const RenderPipelineDescriptor* descriptor) {
Device* device = ToBackend(GetDevice());
uint32_t compileFlags = 0;
#if defined(_DEBUG)
// Enable better shader debugging with the graphics debugging tools.
@ -391,8 +400,12 @@ namespace dawn_native { namespace d3d12 {
descriptorD3D12.SampleDesc.Count = GetSampleCount();
descriptorD3D12.SampleDesc.Quality = 0;
ASSERT_SUCCESS(device->GetD3D12Device()->CreateGraphicsPipelineState(
&descriptorD3D12, IID_PPV_ARGS(&mPipelineState)));
mD3d12PrimitiveTopology = D3D12PrimitiveTopology(GetPrimitiveTopology());
DAWN_TRY(CheckHRESULT(device->GetD3D12Device()->CreateGraphicsPipelineState(
&descriptorD3D12, IID_PPV_ARGS(&mPipelineState)),
"D3D12 create graphics pipeline state"));
return {};
}
RenderPipeline::~RenderPipeline() {

View File

@ -25,13 +25,16 @@ namespace dawn_native { namespace d3d12 {
class RenderPipeline : public RenderPipelineBase {
public:
RenderPipeline(Device* device, const RenderPipelineDescriptor* descriptor);
static ResultOrError<RenderPipeline*> Create(Device* device,
const RenderPipelineDescriptor* descriptor);
~RenderPipeline();
D3D12_PRIMITIVE_TOPOLOGY GetD3D12PrimitiveTopology() const;
ComPtr<ID3D12PipelineState> GetPipelineState();
private:
using RenderPipelineBase::RenderPipelineBase;
MaybeError Initialize(const RenderPipelineDescriptor* descriptor);
D3D12_INPUT_LAYOUT_DESC ComputeInputLayout(
std::array<D3D12_INPUT_ELEMENT_DESC, kMaxVertexAttributes>* inputElementDescriptors);