Implement D3D12 Native Render Passes

Uses D3D12 native render pass API when possible. On pre-RS5 builds of
Windows, Dawn will fall back to a software emulated render pass. A
toggle was added to provide test coverage to the emulated render pass
implementation and used in tests that test render pass functionality in
particular.

Bug: dawn:36
Change-Id: I297a3ec7655b68d28204db2d3ab78cb82bb4e7a5
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/13082
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Brandon Jones <brandon1.jones@intel.com>
This commit is contained in:
Brandon Jones 2019-11-12 18:14:21 +00:00 committed by Commit Bot service account
parent eac7382eb2
commit 700cfe7664
15 changed files with 581 additions and 138 deletions

View File

@ -291,6 +291,8 @@ source_set("libdawn_native_sources") {
"src/dawn_native/d3d12/PlatformFunctions.h",
"src/dawn_native/d3d12/QueueD3D12.cpp",
"src/dawn_native/d3d12/QueueD3D12.h",
"src/dawn_native/d3d12/RenderPassBuilderD3D12.cpp",
"src/dawn_native/d3d12/RenderPassBuilderD3D12.h",
"src/dawn_native/d3d12/RenderPipelineD3D12.cpp",
"src/dawn_native/d3d12/RenderPipelineD3D12.h",
"src/dawn_native/d3d12/ResourceAllocatorManagerD3D12.cpp",

View File

@ -75,8 +75,12 @@ namespace dawn_native {
{"use_d3d12_resource_heap_tier2",
"Enable support for resource heap tier 2. Resource heap tier 2 allows mixing of "
"texture and buffers in the same heap. This allows better heap re-use and reduces "
"fragmentation."}}}};
"fragmentation."}},
{Toggle::UseD3D12RenderPass,
{"use_d3d12_render_pass",
"Use the D3D12 render pass API introduced in Windows build 1809 by default. On "
"versions of Windows prior to build 1809, or when this toggle is turned off, Dawn "
"will emulate a render pass."}}}};
} // anonymous namespace
void TogglesSet::SetToggle(Toggle toggle, bool enabled) {

View File

@ -31,6 +31,7 @@ namespace dawn_native {
TurnOffVsync,
UseTemporaryBufferInCompressedTextureToTextureCopy,
UseD3D12ResourceHeapTier2,
UseD3D12RenderPass,
EnumCount,
InvalidEnum = EnumCount,

View File

@ -28,6 +28,7 @@
#include "dawn_native/d3d12/DeviceD3D12.h"
#include "dawn_native/d3d12/PipelineLayoutD3D12.h"
#include "dawn_native/d3d12/PlatformFunctions.h"
#include "dawn_native/d3d12/RenderPassBuilderD3D12.h"
#include "dawn_native/d3d12/RenderPipelineD3D12.h"
#include "dawn_native/d3d12/SamplerD3D12.h"
#include "dawn_native/d3d12/TextureCopySplitter.h"
@ -65,12 +66,6 @@ namespace dawn_native { namespace d3d12 {
return false;
}
struct OMSetRenderTargetArgs {
unsigned int numRTVs = 0;
std::array<D3D12_CPU_DESCRIPTOR_HANDLE, kMaxColorAttachments> RTVs = {};
D3D12_CPU_DESCRIPTOR_HANDLE dsv = {};
};
} // anonymous namespace
class BindGroupStateTracker : public BindGroupAndStorageBarrierTrackerBase<false, uint64_t> {
@ -601,11 +596,13 @@ namespace dawn_native { namespace d3d12 {
// Records the necessary barriers for the resource usage pre-computed by the frontend
auto TransitionForPass = [](CommandRecordingContext* commandContext,
const PassResourceUsage& usages) {
const PassResourceUsage& usages) -> bool {
std::vector<D3D12_RESOURCE_BARRIER> barriers;
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
wgpu::BufferUsage bufferUsages = wgpu::BufferUsage::None;
for (size_t i = 0; i < usages.buffers.size(); ++i) {
D3D12_RESOURCE_BARRIER barrier;
if (ToBackend(usages.buffers[i])
@ -613,6 +610,7 @@ namespace dawn_native { namespace d3d12 {
usages.bufferUsages[i])) {
barriers.push_back(barrier);
}
bufferUsages |= usages.bufferUsages[i];
}
for (size_t i = 0; i < usages.textures.size(); ++i) {
@ -627,6 +625,8 @@ namespace dawn_native { namespace d3d12 {
}
}
wgpu::TextureUsage textureUsages = wgpu::TextureUsage::None;
for (size_t i = 0; i < usages.textures.size(); ++i) {
D3D12_RESOURCE_BARRIER barrier;
if (ToBackend(usages.textures[i])
@ -634,11 +634,15 @@ namespace dawn_native { namespace d3d12 {
usages.textureUsages[i])) {
barriers.push_back(barrier);
}
textureUsages |= usages.textureUsages[i];
}
if (barriers.size()) {
commandList->ResourceBarrier(barriers.size(), barriers.data());
}
return (bufferUsages & wgpu::BufferUsage::Storage ||
textureUsages & wgpu::TextureUsage::Storage);
};
const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
@ -661,10 +665,11 @@ namespace dawn_native { namespace d3d12 {
BeginRenderPassCmd* beginRenderPassCmd =
mCommands.NextCommand<BeginRenderPassCmd>();
TransitionForPass(commandContext, passResourceUsages[nextPassNumber]);
const bool passHasUAV =
TransitionForPass(commandContext, passResourceUsages[nextPassNumber]);
bindingTracker.SetInComputePass(false);
RecordRenderPass(commandContext, &bindingTracker, &renderPassTracker,
beginRenderPassCmd);
beginRenderPassCmd, passHasUAV);
nextPassNumber++;
} break;
@ -912,126 +917,194 @@ namespace dawn_native { namespace d3d12 {
}
}
void CommandBuffer::RecordRenderPass(CommandRecordingContext* commandContext,
BindGroupStateTracker* bindingTracker,
RenderPassDescriptorHeapTracker* renderPassTracker,
BeginRenderPassCmd* renderPass) {
OMSetRenderTargetArgs args = renderPassTracker->GetSubpassOMSetRenderTargetArgs(renderPass);
void CommandBuffer::SetupRenderPass(CommandRecordingContext* commandContext,
BeginRenderPassCmd* renderPass,
RenderPassBuilder* renderPassBuilder) {
for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
RenderPassColorAttachmentInfo& attachmentInfo = renderPass->colorAttachments[i];
TextureView* view = ToBackend(attachmentInfo.view.Get());
Texture* texture = ToBackend(view->GetTexture());
// Load operation is changed to clear when the texture is uninitialized.
if (!texture->IsSubresourceContentInitialized(view->GetBaseMipLevel(), 1,
view->GetBaseArrayLayer(), 1) &&
attachmentInfo.loadOp == wgpu::LoadOp::Load) {
attachmentInfo.loadOp = wgpu::LoadOp::Clear;
attachmentInfo.clearColor = {0.0f, 0.0f, 0.0f, 0.0f};
}
// Set color load operation.
renderPassBuilder->SetRenderTargetBeginningAccess(
i, attachmentInfo.loadOp, attachmentInfo.clearColor, view->GetD3D12Format());
// Set color store operation.
if (attachmentInfo.resolveTarget.Get() != nullptr) {
TextureView* resolveDestinationView = ToBackend(attachmentInfo.resolveTarget.Get());
Texture* resolveDestinationTexture =
ToBackend(resolveDestinationView->GetTexture());
resolveDestinationTexture->TransitionUsageNow(commandContext,
D3D12_RESOURCE_STATE_RESOLVE_DEST);
// Mark resolve target as initialized to prevent clearing later.
resolveDestinationTexture->SetIsSubresourceContentInitialized(
true, resolveDestinationView->GetBaseMipLevel(), 1,
resolveDestinationView->GetBaseArrayLayer(), 1);
renderPassBuilder->SetRenderTargetEndingAccessResolve(i, attachmentInfo.storeOp,
view, resolveDestinationView);
} else {
renderPassBuilder->SetRenderTargetEndingAccess(i, attachmentInfo.storeOp);
}
// Set whether or not the texture requires initialization after the pass.
bool isInitialized = attachmentInfo.storeOp == wgpu::StoreOp::Store;
texture->SetIsSubresourceContentInitialized(isInitialized, view->GetBaseMipLevel(), 1,
view->GetBaseArrayLayer(), 1);
}
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
RenderPassDepthStencilAttachmentInfo& attachmentInfo =
renderPass->depthStencilAttachment;
TextureView* view = ToBackend(renderPass->depthStencilAttachment.view.Get());
Texture* texture = ToBackend(view->GetTexture());
const bool hasDepth = view->GetTexture()->GetFormat().HasDepth();
const bool hasStencil = view->GetTexture()->GetFormat().HasStencil();
// Load operations are changed to clear when the texture is uninitialized.
if (!view->GetTexture()->IsSubresourceContentInitialized(
view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
view->GetLayerCount())) {
if (hasDepth && attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) {
attachmentInfo.clearDepth = 0.0f;
attachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
}
if (hasStencil && attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) {
attachmentInfo.clearStencil = 0u;
attachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
}
}
// Set depth/stencil load operations.
if (hasDepth) {
renderPassBuilder->SetDepthAccess(
attachmentInfo.depthLoadOp, attachmentInfo.depthStoreOp,
attachmentInfo.clearDepth, view->GetD3D12Format());
} else {
renderPassBuilder->SetDepthNoAccess();
}
if (hasStencil) {
renderPassBuilder->SetStencilAccess(
attachmentInfo.stencilLoadOp, attachmentInfo.stencilStoreOp,
attachmentInfo.clearStencil, view->GetD3D12Format());
} else {
renderPassBuilder->SetStencilNoAccess();
}
// Set whether or not the texture requires initialization.
ASSERT(!hasDepth || !hasStencil ||
attachmentInfo.depthStoreOp == attachmentInfo.stencilStoreOp);
bool isInitialized = attachmentInfo.depthStoreOp == wgpu::StoreOp::Store;
texture->SetIsSubresourceContentInitialized(isInitialized, view->GetBaseMipLevel(), 1,
view->GetBaseArrayLayer(), 1);
} else {
renderPassBuilder->SetDepthStencilNoAccess();
}
}
void CommandBuffer::EmulateBeginRenderPass(CommandRecordingContext* commandContext,
const RenderPassBuilder* renderPassBuilder) const {
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
// Clear framebuffer attachments as needed and transition to render target
// Clear framebuffer attachments as needed.
{
for (uint32_t i :
IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
auto& attachmentInfo = renderPass->colorAttachments[i];
TextureView* view = ToBackend(attachmentInfo.view.Get());
for (uint32_t i = 0; i < renderPassBuilder->GetColorAttachmentCount(); i++) {
// Load op - color
ASSERT(view->GetLevelCount() == 1);
ASSERT(view->GetLayerCount() == 1);
if (attachmentInfo.loadOp == wgpu::LoadOp::Clear ||
(attachmentInfo.loadOp == wgpu::LoadOp::Load &&
!view->GetTexture()->IsSubresourceContentInitialized(
view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1))) {
D3D12_CPU_DESCRIPTOR_HANDLE handle = args.RTVs[i];
commandList->ClearRenderTargetView(handle, &attachmentInfo.clearColor.r, 0,
nullptr);
}
TextureView* resolveView = ToBackend(attachmentInfo.resolveTarget.Get());
if (resolveView != nullptr) {
// We need to set the resolve target to initialized so that it does not get
// cleared later in the pipeline. The texture will be resolved from the source
// color attachment, which will be correctly initialized.
ToBackend(resolveView->GetTexture())
->SetIsSubresourceContentInitialized(
true, resolveView->GetBaseMipLevel(), resolveView->GetLevelCount(),
resolveView->GetBaseArrayLayer(), resolveView->GetLayerCount());
}
switch (attachmentInfo.storeOp) {
case wgpu::StoreOp::Store: {
view->GetTexture()->SetIsSubresourceContentInitialized(
true, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
} break;
case wgpu::StoreOp::Clear: {
view->GetTexture()->SetIsSubresourceContentInitialized(
false, view->GetBaseMipLevel(), 1, view->GetBaseArrayLayer(), 1);
} break;
default: { UNREACHABLE(); } break;
if (renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i]
.BeginningAccess.Type == D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) {
commandList->ClearRenderTargetView(
renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i].cpuDescriptor,
renderPassBuilder->GetRenderPassRenderTargetDescriptors()[i]
.BeginningAccess.Clear.ClearValue.Color,
0, nullptr);
}
}
if (renderPass->attachmentState->HasDepthStencilAttachment()) {
auto& attachmentInfo = renderPass->depthStencilAttachment;
Texture* texture = ToBackend(renderPass->depthStencilAttachment.view->GetTexture());
TextureView* view = ToBackend(attachmentInfo.view.Get());
float clearDepth = attachmentInfo.clearDepth;
if (renderPassBuilder->HasDepth()) {
D3D12_CLEAR_FLAGS clearFlags = {};
float depthClear = 0.0f;
uint8_t stencilClear = 0u;
if (renderPassBuilder->GetRenderPassDepthStencilDescriptor()
->DepthBeginningAccess.Type ==
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) {
clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
depthClear = renderPassBuilder->GetRenderPassDepthStencilDescriptor()
->DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth;
}
if (renderPassBuilder->GetRenderPassDepthStencilDescriptor()
->StencilBeginningAccess.Type ==
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR) {
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
stencilClear =
renderPassBuilder->GetRenderPassDepthStencilDescriptor()
->StencilBeginningAccess.Clear.ClearValue.DepthStencil.Stencil;
}
// TODO(kainino@chromium.org): investigate: should the Dawn clear
// stencil type be uint8_t?
uint8_t clearStencil = static_cast<uint8_t>(attachmentInfo.clearStencil);
// Load op - depth/stencil
bool doDepthClear = texture->GetFormat().HasDepth() &&
(attachmentInfo.depthLoadOp == wgpu::LoadOp::Clear);
bool doStencilClear = texture->GetFormat().HasStencil() &&
(attachmentInfo.stencilLoadOp == wgpu::LoadOp::Clear);
D3D12_CLEAR_FLAGS clearFlags = {};
if (doDepthClear) {
clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
}
if (doStencilClear) {
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
}
// If the depth stencil texture has not been initialized, we want to use loadop
// clear to init the contents to 0's
if (!texture->IsSubresourceContentInitialized(
view->GetBaseMipLevel(), view->GetLevelCount(), view->GetBaseArrayLayer(),
view->GetLayerCount())) {
if (texture->GetFormat().HasDepth() &&
attachmentInfo.depthLoadOp == wgpu::LoadOp::Load) {
clearDepth = 0.0f;
clearFlags |= D3D12_CLEAR_FLAG_DEPTH;
}
if (texture->GetFormat().HasStencil() &&
attachmentInfo.stencilLoadOp == wgpu::LoadOp::Load) {
clearStencil = 0u;
clearFlags |= D3D12_CLEAR_FLAG_STENCIL;
}
}
if (clearFlags) {
D3D12_CPU_DESCRIPTOR_HANDLE handle = args.dsv;
commandList->ClearDepthStencilView(handle, clearFlags, clearDepth, clearStencil,
0, nullptr);
}
if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Store &&
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Store) {
texture->SetIsSubresourceContentInitialized(
true, view->GetBaseMipLevel(), view->GetLevelCount(),
view->GetBaseArrayLayer(), view->GetLayerCount());
} else if (attachmentInfo.depthStoreOp == wgpu::StoreOp::Clear &&
attachmentInfo.stencilStoreOp == wgpu::StoreOp::Clear) {
texture->SetIsSubresourceContentInitialized(
false, view->GetBaseMipLevel(), view->GetLevelCount(),
view->GetBaseArrayLayer(), view->GetLayerCount());
commandList->ClearDepthStencilView(
renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor,
clearFlags, depthClear, stencilClear, 0, nullptr);
}
}
}
// Set up render targets
{
if (args.dsv.ptr) {
commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, &args.dsv);
} else {
commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, nullptr);
}
commandList->OMSetRenderTargets(
renderPassBuilder->GetColorAttachmentCount(), renderPassBuilder->GetRenderTargetViews(),
FALSE,
renderPassBuilder->HasDepth()
? &renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor
: nullptr);
}
void CommandBuffer::RecordRenderPass(
CommandRecordingContext* commandContext,
BindGroupStateTracker* bindingTracker,
RenderPassDescriptorHeapTracker* renderPassDescriptorHeapTracker,
BeginRenderPassCmd* renderPass,
const bool passHasUAV) {
OMSetRenderTargetArgs args =
renderPassDescriptorHeapTracker->GetSubpassOMSetRenderTargetArgs(renderPass);
const bool useRenderPass = GetDevice()->IsToggleEnabled(Toggle::UseD3D12RenderPass);
// renderPassBuilder must be scoped to RecordRenderPass because any underlying
// D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS structs must remain
// valid until after EndRenderPass() has been called.
RenderPassBuilder renderPassBuilder(args, passHasUAV);
SetupRenderPass(commandContext, renderPass, &renderPassBuilder);
// Use D3D12's native render pass API if it's available, otherwise emulate the
// beginning and ending access operations.
if (useRenderPass) {
commandContext->GetCommandList4()->BeginRenderPass(
renderPassBuilder.GetColorAttachmentCount(),
renderPassBuilder.GetRenderPassRenderTargetDescriptors(),
renderPassBuilder.HasDepth()
? renderPassBuilder.GetRenderPassDepthStencilDescriptor()
: nullptr,
renderPassBuilder.GetRenderPassFlags());
} else {
EmulateBeginRenderPass(commandContext, &renderPassBuilder);
}
ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList();
// Set up default dynamic state
{
uint32_t width = renderPass->width;
@ -1189,10 +1262,9 @@ namespace dawn_native { namespace d3d12 {
switch (type) {
case Command::EndRenderPass: {
mCommands.NextCommand<EndRenderPassCmd>();
// TODO(brandon1.jones@intel.com): avoid calling this function and enable MSAA
// resolve in D3D12 render pass on the platforms that support this feature.
if (renderPass->attachmentState->GetSampleCount() > 1) {
if (useRenderPass) {
commandContext->GetCommandList4()->EndRenderPass();
} else if (renderPass->attachmentState->GetSampleCount() > 1) {
ResolveMultisampledRenderPass(commandContext, renderPass);
}
return;

View File

@ -31,10 +31,17 @@ namespace dawn_native {
namespace dawn_native { namespace d3d12 {
struct OMSetRenderTargetArgs {
unsigned int numRTVs = 0;
std::array<D3D12_CPU_DESCRIPTOR_HANDLE, kMaxColorAttachments> RTVs = {};
D3D12_CPU_DESCRIPTOR_HANDLE dsv = {};
};
class BindGroupStateTracker;
class CommandRecordingContext;
class Device;
class RenderPassDescriptorHeapTracker;
class RenderPassBuilder;
class RenderPipeline;
class CommandBuffer : public CommandBufferBase {
@ -49,8 +56,14 @@ namespace dawn_native { namespace d3d12 {
BindGroupStateTracker* bindingTracker);
void RecordRenderPass(CommandRecordingContext* commandContext,
BindGroupStateTracker* bindingTracker,
RenderPassDescriptorHeapTracker* renderPassTracker,
BeginRenderPassCmd* renderPass);
RenderPassDescriptorHeapTracker* renderPassDescriptorHeapTracker,
BeginRenderPassCmd* renderPass,
bool passHasUAV);
void SetupRenderPass(CommandRecordingContext* commandContext,
BeginRenderPassCmd* renderPass,
RenderPassBuilder* renderPassBuilder);
void EmulateBeginRenderPass(CommandRecordingContext* commandContext,
const RenderPassBuilder* renderPassBuilder) const;
CommandIterator mCommands;
};

View File

@ -41,6 +41,9 @@ namespace dawn_native { namespace d3d12 {
nullptr, IID_PPV_ARGS(&d3d12GraphicsCommandList)),
"D3D12 creating direct command list"));
mD3d12CommandList = std::move(d3d12GraphicsCommandList);
// Store a cast to ID3D12GraphicsCommandList4. This is required to use the D3D12 render
// pass APIs introduced in Windows build 1809.
mD3d12CommandList.As(&mD3d12CommandList4);
}
mIsOpen = true;
@ -80,8 +83,17 @@ namespace dawn_native { namespace d3d12 {
return mD3d12CommandList.Get();
}
// This function will fail on Windows versions prior to 1809. Support must be queried through
// the device before calling.
ID3D12GraphicsCommandList4* CommandRecordingContext::GetCommandList4() const {
ASSERT(IsOpen());
ASSERT(mD3d12CommandList.Get() != nullptr);
return mD3d12CommandList4.Get();
}
void CommandRecordingContext::Release() {
mD3d12CommandList.Reset();
mD3d12CommandList4.Reset();
mIsOpen = false;
mSharedTextures.clear();
}

View File

@ -31,6 +31,7 @@ namespace dawn_native { namespace d3d12 {
CommandAllocatorManager* commandAllocationManager);
ID3D12GraphicsCommandList* GetCommandList() const;
ID3D12GraphicsCommandList4* GetCommandList4() const;
void Release();
bool IsOpen() const;
@ -38,6 +39,7 @@ namespace dawn_native { namespace d3d12 {
private:
ComPtr<ID3D12GraphicsCommandList> mD3d12CommandList;
ComPtr<ID3D12GraphicsCommandList4> mD3d12CommandList4;
bool mIsOpen = false;
std::set<Texture*> mSharedTextures;
};

View File

@ -24,25 +24,33 @@ namespace dawn_native { namespace d3d12 {
ResultOrError<D3D12DeviceInfo> GatherDeviceInfo(const Adapter& adapter) {
D3D12DeviceInfo info = {};
// Gather info about device memory
{
// Newer builds replace D3D_FEATURE_DATA_ARCHITECTURE with
// D3D_FEATURE_DATA_ARCHITECTURE1. However, D3D_FEATURE_DATA_ARCHITECTURE can be used
// for backwards compat.
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_feature
D3D12_FEATURE_DATA_ARCHITECTURE arch = {};
DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(
D3D12_FEATURE_ARCHITECTURE, &arch, sizeof(arch)),
"ID3D12Device::CheckFeatureSupport"));
// Newer builds replace D3D_FEATURE_DATA_ARCHITECTURE with
// D3D_FEATURE_DATA_ARCHITECTURE1. However, D3D_FEATURE_DATA_ARCHITECTURE can be used
// for backwards compat.
// https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/ne-d3d12-d3d12_feature
D3D12_FEATURE_DATA_ARCHITECTURE arch = {};
DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE,
&arch, sizeof(arch)),
"ID3D12Device::CheckFeatureSupport"));
info.isUMA = arch.UMA;
info.isUMA = arch.UMA;
D3D12_FEATURE_DATA_D3D12_OPTIONS options = {};
DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(
D3D12_FEATURE_D3D12_OPTIONS, &options, sizeof(options)),
"ID3D12Device::CheckFeatureSupport"));
D3D12_FEATURE_DATA_D3D12_OPTIONS options = {};
DAWN_TRY(CheckHRESULT(adapter.GetDevice()->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS,
&options, sizeof(options)),
"ID3D12Device::CheckFeatureSupport"));
info.resourceHeapTier = options.ResourceHeapTier;
info.resourceHeapTier = options.ResourceHeapTier;
// Windows builds 1809 and above can use the D3D12 render pass API. If we query
// CheckFeatureSupport for D3D12_FEATURE_D3D12_OPTIONS5 successfully, then we can use
// the render pass API.
D3D12_FEATURE_DATA_D3D12_OPTIONS5 featureOptions5 = {};
if (SUCCEEDED(adapter.GetDevice()->CheckFeatureSupport(
D3D12_FEATURE_D3D12_OPTIONS5, &featureOptions5, sizeof(featureOptions5)))) {
info.supportsRenderPass = true;
} else {
info.supportsRenderPass = false;
}
return info;

View File

@ -25,6 +25,7 @@ namespace dawn_native { namespace d3d12 {
struct D3D12DeviceInfo {
bool isUMA;
uint32_t resourceHeapTier;
bool supportsRenderPass;
};
ResultOrError<D3D12DeviceInfo> GatherDeviceInfo(const Adapter& adapter);

View File

@ -414,6 +414,7 @@ namespace dawn_native { namespace d3d12 {
void Device::InitTogglesFromDriver() {
const bool useResourceHeapTier2 = (GetDeviceInfo().resourceHeapTier >= 2);
SetToggle(Toggle::UseD3D12ResourceHeapTier2, useResourceHeapTier2);
SetToggle(Toggle::UseD3D12RenderPass, GetDeviceInfo().supportsRenderPass);
}
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,229 @@
// 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/RenderPassBuilderD3D12.h"
#include "dawn_native/Format.h"
#include "dawn_native/d3d12/CommandBufferD3D12.h"
#include "dawn_native/d3d12/Forward.h"
#include "dawn_native/d3d12/TextureD3D12.h"
#include "dawn_native/dawn_platform.h"
namespace dawn_native { namespace d3d12 {
namespace {
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE D3D12BeginningAccessType(wgpu::LoadOp loadOp) {
switch (loadOp) {
case wgpu::LoadOp::Clear:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_CLEAR;
case wgpu::LoadOp::Load:
return D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_PRESERVE;
default:
UNREACHABLE();
}
}
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE D3D12EndingAccessType(wgpu::StoreOp storeOp) {
switch (storeOp) {
case wgpu::StoreOp::Clear:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_DISCARD;
case wgpu::StoreOp::Store:
return D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_PRESERVE;
default:
UNREACHABLE();
}
}
D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS D3D12EndingAccessResolveParameters(
wgpu::StoreOp storeOp,
TextureView* resolveSource,
TextureView* resolveDestination) {
D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_PARAMETERS resolveParameters;
resolveParameters.Format = resolveDestination->GetD3D12Format();
resolveParameters.pSrcResource =
ToBackend(resolveSource->GetTexture())->GetD3D12Resource();
resolveParameters.pDstResource =
ToBackend(resolveDestination->GetTexture())->GetD3D12Resource();
// Clear or preserve the resolve source.
if (storeOp == wgpu::StoreOp::Clear) {
resolveParameters.PreserveResolveSource = false;
} else if (storeOp == wgpu::StoreOp::Store) {
resolveParameters.PreserveResolveSource = true;
}
// RESOLVE_MODE_AVERAGE is only valid for non-integer formats.
// TODO: Investigate and determine how integer format resolves should work in WebGPU.
switch (resolveDestination->GetFormat().type) {
case Format::Type::Sint:
case Format::Type::Uint:
resolveParameters.ResolveMode = D3D12_RESOLVE_MODE_MAX;
break;
default:
resolveParameters.ResolveMode = D3D12_RESOLVE_MODE_AVERAGE;
break;
}
resolveParameters.SubresourceCount = 1;
return resolveParameters;
}
D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS
D3D12EndingAccessResolveSubresourceParameters(TextureView* resolveDestination) {
D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS subresourceParameters;
Texture* resolveDestinationTexture = ToBackend(resolveDestination->GetTexture());
subresourceParameters.DstX = 0;
subresourceParameters.DstY = 0;
subresourceParameters.SrcSubresource = 0;
subresourceParameters.DstSubresource = resolveDestinationTexture->GetSubresourceIndex(
resolveDestination->GetBaseMipLevel(), resolveDestination->GetBaseArrayLayer());
subresourceParameters.SrcRect = {0, 0, resolveDestinationTexture->GetSize().width,
resolveDestinationTexture->GetSize().height};
return subresourceParameters;
}
} // anonymous namespace
RenderPassBuilder::RenderPassBuilder(const OMSetRenderTargetArgs& args, bool hasUAV)
: mColorAttachmentCount(args.numRTVs), mRenderTargetViews(args.RTVs.data()) {
for (uint32_t i = 0; i < mColorAttachmentCount; i++) {
mRenderPassRenderTargetDescriptors[i].cpuDescriptor = args.RTVs[i];
}
mRenderPassDepthStencilDesc.cpuDescriptor = args.dsv;
if (hasUAV) {
mRenderPassFlags = D3D12_RENDER_PASS_FLAG_ALLOW_UAV_WRITES;
}
}
uint32_t RenderPassBuilder::GetColorAttachmentCount() const {
return mColorAttachmentCount;
}
bool RenderPassBuilder::HasDepth() const {
return mHasDepth;
}
const D3D12_RENDER_PASS_RENDER_TARGET_DESC*
RenderPassBuilder::GetRenderPassRenderTargetDescriptors() const {
return mRenderPassRenderTargetDescriptors.data();
}
const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC*
RenderPassBuilder::GetRenderPassDepthStencilDescriptor() const {
return &mRenderPassDepthStencilDesc;
}
D3D12_RENDER_PASS_FLAGS RenderPassBuilder::GetRenderPassFlags() const {
return mRenderPassFlags;
}
const D3D12_CPU_DESCRIPTOR_HANDLE* RenderPassBuilder::GetRenderTargetViews() const {
return mRenderTargetViews;
}
void RenderPassBuilder::SetRenderTargetBeginningAccess(uint32_t attachment,
wgpu::LoadOp loadOp,
dawn_native::Color clearColor,
DXGI_FORMAT format) {
mRenderPassRenderTargetDescriptors[attachment].BeginningAccess.Type =
D3D12BeginningAccessType(loadOp);
if (loadOp == wgpu::LoadOp::Clear) {
mRenderPassRenderTargetDescriptors[attachment]
.BeginningAccess.Clear.ClearValue.Color[0] = clearColor.r;
mRenderPassRenderTargetDescriptors[attachment]
.BeginningAccess.Clear.ClearValue.Color[1] = clearColor.g;
mRenderPassRenderTargetDescriptors[attachment]
.BeginningAccess.Clear.ClearValue.Color[2] = clearColor.b;
mRenderPassRenderTargetDescriptors[attachment]
.BeginningAccess.Clear.ClearValue.Color[3] = clearColor.a;
mRenderPassRenderTargetDescriptors[attachment].BeginningAccess.Clear.ClearValue.Format =
format;
}
}
void RenderPassBuilder::SetRenderTargetEndingAccess(uint32_t attachment,
wgpu::StoreOp storeOp) {
mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Type =
D3D12EndingAccessType(storeOp);
}
void RenderPassBuilder::SetRenderTargetEndingAccessResolve(uint32_t attachment,
wgpu::StoreOp storeOp,
TextureView* resolveSource,
TextureView* resolveDestination) {
mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Type =
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_RESOLVE;
mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Resolve =
D3D12EndingAccessResolveParameters(storeOp, resolveSource, resolveDestination);
mSubresourceParams[attachment] =
D3D12EndingAccessResolveSubresourceParameters(resolveDestination);
mRenderPassRenderTargetDescriptors[attachment].EndingAccess.Resolve.pSubresourceParameters =
&mSubresourceParams[attachment];
}
void RenderPassBuilder::SetDepthAccess(wgpu::LoadOp loadOp,
wgpu::StoreOp storeOp,
float clearDepth,
DXGI_FORMAT format) {
mHasDepth = true;
mRenderPassDepthStencilDesc.DepthBeginningAccess.Type = D3D12BeginningAccessType(loadOp);
if (loadOp == wgpu::LoadOp::Clear) {
mRenderPassDepthStencilDesc.DepthBeginningAccess.Clear.ClearValue.DepthStencil.Depth =
clearDepth;
mRenderPassDepthStencilDesc.DepthBeginningAccess.Clear.ClearValue.Format = format;
}
mRenderPassDepthStencilDesc.DepthEndingAccess.Type = D3D12EndingAccessType(storeOp);
}
void RenderPassBuilder::SetStencilAccess(wgpu::LoadOp loadOp,
wgpu::StoreOp storeOp,
uint8_t clearStencil,
DXGI_FORMAT format) {
mRenderPassDepthStencilDesc.StencilBeginningAccess.Type = D3D12BeginningAccessType(loadOp);
if (loadOp == wgpu::LoadOp::Clear) {
mRenderPassDepthStencilDesc.StencilBeginningAccess.Clear.ClearValue.DepthStencil
.Stencil = clearStencil;
mRenderPassDepthStencilDesc.StencilBeginningAccess.Clear.ClearValue.Format = format;
}
mRenderPassDepthStencilDesc.StencilEndingAccess.Type = D3D12EndingAccessType(storeOp);
}
void RenderPassBuilder::SetDepthNoAccess() {
mRenderPassDepthStencilDesc.DepthBeginningAccess.Type =
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS;
mRenderPassDepthStencilDesc.DepthEndingAccess.Type =
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS;
}
void RenderPassBuilder::SetDepthStencilNoAccess() {
SetDepthNoAccess();
SetStencilNoAccess();
}
void RenderPassBuilder::SetStencilNoAccess() {
mRenderPassDepthStencilDesc.StencilBeginningAccess.Type =
D3D12_RENDER_PASS_BEGINNING_ACCESS_TYPE_NO_ACCESS;
mRenderPassDepthStencilDesc.StencilEndingAccess.Type =
D3D12_RENDER_PASS_ENDING_ACCESS_TYPE_NO_ACCESS;
}
}} // namespace dawn_native::d3d12

View File

@ -0,0 +1,89 @@
// 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_RENDERPASSBUILDERD3D12_H_
#define DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_
#include "common/Constants.h"
#include "dawn_native/d3d12/d3d12_platform.h"
#include "dawn_native/dawn_platform.h"
#include <array>
namespace dawn_native { namespace d3d12 {
class TextureView;
struct OMSetRenderTargetArgs;
// RenderPassBuilder stores parameters related to render pass load and store operations.
// When the D3D12 render pass API is available, the needed descriptors can be fetched
// directly from the RenderPassBuilder. When the D3D12 render pass API is not available, the
// descriptors are still fetched and any information necessary to emulate the load and store
// operations is extracted from the descriptors.
class RenderPassBuilder {
public:
RenderPassBuilder(const OMSetRenderTargetArgs& args, bool hasUAV);
uint32_t GetColorAttachmentCount() const;
// Returns descriptors that are fed directly to BeginRenderPass, or are used as parameter
// storage if D3D12 render pass API is unavailable.
const D3D12_RENDER_PASS_RENDER_TARGET_DESC* GetRenderPassRenderTargetDescriptors() const;
const D3D12_RENDER_PASS_DEPTH_STENCIL_DESC* GetRenderPassDepthStencilDescriptor() const;
D3D12_RENDER_PASS_FLAGS GetRenderPassFlags() const;
// Returns attachment RTVs to use with OMSetRenderTargets.
const D3D12_CPU_DESCRIPTOR_HANDLE* GetRenderTargetViews() const;
bool HasDepth() const;
// Functions that set the appropriate values in the render pass descriptors.
void SetDepthAccess(wgpu::LoadOp loadOp,
wgpu::StoreOp storeOp,
float clearDepth,
DXGI_FORMAT format);
void SetDepthNoAccess();
void SetDepthStencilNoAccess();
void SetRenderTargetBeginningAccess(uint32_t attachment,
wgpu::LoadOp loadOp,
dawn_native::Color clearColor,
DXGI_FORMAT format);
void SetRenderTargetEndingAccess(uint32_t attachment, wgpu::StoreOp storeOp);
void SetRenderTargetEndingAccessResolve(uint32_t attachment,
wgpu::StoreOp storeOp,
TextureView* resolveSource,
TextureView* resolveDestination);
void SetStencilAccess(wgpu::LoadOp loadOp,
wgpu::StoreOp storeOp,
uint8_t clearStencil,
DXGI_FORMAT format);
void SetStencilNoAccess();
private:
uint32_t mColorAttachmentCount = 0;
bool mHasDepth = false;
D3D12_RENDER_PASS_FLAGS mRenderPassFlags = D3D12_RENDER_PASS_FLAG_NONE;
D3D12_RENDER_PASS_DEPTH_STENCIL_DESC mRenderPassDepthStencilDesc;
std::array<D3D12_RENDER_PASS_RENDER_TARGET_DESC, kMaxColorAttachments>
mRenderPassRenderTargetDescriptors;
const D3D12_CPU_DESCRIPTOR_HANDLE* mRenderTargetViews;
std::array<D3D12_RENDER_PASS_ENDING_ACCESS_RESOLVE_SUBRESOURCE_PARAMETERS,
kMaxColorAttachments>
mSubresourceParams;
};
}} // namespace dawn_native::d3d12
#endif // DAWNNATIVE_D3D12_RENDERPASSBUILDERD3D12_H_

View File

@ -506,6 +506,7 @@ TEST_P(MultisampledRenderingTest, ResolveInto2DArrayTexture) {
DAWN_INSTANTIATE_TEST(MultisampledRenderingTest,
D3D12Backend,
ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_resource_heap_tier2"}),
ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_render_pass"}),
MetalBackend,
OpenGLBackend,
VulkanBackend,

View File

@ -171,4 +171,9 @@ TEST_P(RenderPassTest, NoCorrespondingFragmentShaderOutputs) {
EXPECT_PIXEL_RGBA8_EQ(kRed, renderTarget, kRTSize - 1, 1);
}
DAWN_INSTANTIATE_TEST(RenderPassTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);
DAWN_INSTANTIATE_TEST(RenderPassTest,
D3D12Backend,
ForceWorkarounds(D3D12Backend, {}, {"use_d3d12_render_pass"}),
MetalBackend,
OpenGLBackend,
VulkanBackend);

View File

@ -779,5 +779,8 @@ TEST_P(TextureZeroInitTest, RenderingLoadingDepthStencilStoreOpClear) {
DAWN_INSTANTIATE_TEST(
TextureZeroInitTest,
ForceWorkarounds(D3D12Backend, {"nonzero_clear_resources_on_creation_for_testing"}),
ForceWorkarounds(D3D12Backend,
{"nonzero_clear_resources_on_creation_for_testing"},
{"use_d3d12_render_pass"}),
ForceWorkarounds(OpenGLBackend, {"nonzero_clear_resources_on_creation_for_testing"}),
ForceWorkarounds(VulkanBackend, {"nonzero_clear_resources_on_creation_for_testing"}));