// Copyright 2017 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/CommandBufferD3D12.h" #include "common/Assert.h" #include "dawn_native/BindGroupAndStorageBarrierTracker.h" #include "dawn_native/CommandEncoder.h" #include "dawn_native/Commands.h" #include "dawn_native/RenderBundle.h" #include "dawn_native/d3d12/BindGroupD3D12.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" #include "dawn_native/d3d12/BufferD3D12.h" #include "dawn_native/d3d12/CommandRecordingContext.h" #include "dawn_native/d3d12/ComputePipelineD3D12.h" #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/ShaderVisibleDescriptorAllocatorD3D12.h" #include "dawn_native/d3d12/StagingDescriptorAllocatorD3D12.h" #include "dawn_native/d3d12/TextureCopySplitter.h" #include "dawn_native/d3d12/TextureD3D12.h" #include "dawn_native/d3d12/UtilsD3D12.h" #include namespace dawn_native { namespace d3d12 { namespace { DXGI_FORMAT DXGIIndexFormat(wgpu::IndexFormat format) { switch (format) { case wgpu::IndexFormat::Uint16: return DXGI_FORMAT_R16_UINT; case wgpu::IndexFormat::Uint32: return DXGI_FORMAT_R32_UINT; default: UNREACHABLE(); } } bool CanUseCopyResource(const Texture* src, const Texture* dst, const Extent3D& copySize) { // Checked by validation ASSERT(src->GetSampleCount() == dst->GetSampleCount()); ASSERT(src->GetFormat().format == dst->GetFormat().format); const Extent3D& srcSize = src->GetSize(); const Extent3D& dstSize = dst->GetSize(); auto GetCopyDepth = [](const Texture* texture) { switch (texture->GetDimension()) { case wgpu::TextureDimension::e1D: return 1u; case wgpu::TextureDimension::e2D: return texture->GetArrayLayers(); case wgpu::TextureDimension::e3D: return texture->GetSize().depth; } }; // https://docs.microsoft.com/en-us/windows/win32/api/d3d12/nf-d3d12-id3d12graphicscommandlist-copyresource // In order to use D3D12's copy resource, the textures must be the same dimensions, and // the copy must be of the entire resource. // TODO(dawn:129): Support 1D textures. return src->GetDimension() == dst->GetDimension() && // dst->GetNumMipLevels() == 1 && // src->GetNumMipLevels() == 1 && // A copy command is of a single mip, so if a // resource has more than one, we definitely // cannot use CopyResource. copySize.width == dstSize.width && // copySize.width == srcSize.width && // copySize.height == dstSize.height && // copySize.height == srcSize.height && // copySize.depth == GetCopyDepth(src) && // copySize.depth == GetCopyDepth(dst); } } // anonymous namespace class BindGroupStateTracker : public BindGroupAndStorageBarrierTrackerBase { public: BindGroupStateTracker(Device* device) : BindGroupAndStorageBarrierTrackerBase(), mAllocator(device->GetShaderVisibleDescriptorAllocator()) { } void SetInComputePass(bool inCompute_) { mInCompute = inCompute_; } MaybeError Apply(CommandRecordingContext* commandContext) { // Bindgroups are allocated in shader-visible descriptor heaps which are managed by a // ringbuffer. There can be a single shader-visible descriptor heap of each type bound // at any given time. This means that when we switch heaps, all other currently bound // bindgroups must be re-populated. Bindgroups can fail allocation gracefully which is // the signal to change the bounded heaps. // Re-populating all bindgroups after the last one fails causes duplicated allocations // to occur on overflow. // TODO(bryan.bernhart@intel.com): Consider further optimization. bool didCreateBindGroups = true; for (uint32_t index : IterateBitSet(mDirtyBindGroups)) { DAWN_TRY_ASSIGN(didCreateBindGroups, ToBackend(mBindGroups[index])->Populate(mAllocator)); if (!didCreateBindGroups) { break; } } // This will re-create bindgroups for both heaps even if only one overflowed. // TODO(bryan.bernhart@intel.com): Consider re-allocating heaps independently // such that overflowing one doesn't re-allocate the another. ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); if (!didCreateBindGroups) { DAWN_TRY(mAllocator->AllocateAndSwitchShaderVisibleHeaps()); mDirtyBindGroupsObjectChangedOrIsDynamic |= mBindGroupLayoutsMask; mDirtyBindGroups |= mBindGroupLayoutsMask; // Must be called before applying the bindgroups. SetID3D12DescriptorHeaps(commandList); for (uint32_t index : IterateBitSet(mBindGroupLayoutsMask)) { DAWN_TRY_ASSIGN(didCreateBindGroups, ToBackend(mBindGroups[index])->Populate(mAllocator)); ASSERT(didCreateBindGroups); } } for (uint32_t index : IterateBitSet(mDirtyBindGroupsObjectChangedOrIsDynamic)) { BindGroup* group = ToBackend(mBindGroups[index]); ApplyBindGroup(commandList, ToBackend(mPipelineLayout), index, group, mDynamicOffsetCounts[index], mDynamicOffsets[index].data()); } if (mInCompute) { for (uint32_t index : IterateBitSet(mBindGroupLayoutsMask)) { for (uint32_t binding : IterateBitSet(mBuffersNeedingBarrier[index])) { wgpu::BindingType bindingType = mBindingTypes[index][binding]; switch (bindingType) { case wgpu::BindingType::StorageBuffer: ToBackend(mBuffers[index][binding]) ->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::Storage); break; case wgpu::BindingType::StorageTexture: case wgpu::BindingType::ReadonlyStorageTexture: case wgpu::BindingType::WriteonlyStorageTexture: // Not implemented. case wgpu::BindingType::UniformBuffer: case wgpu::BindingType::ReadonlyStorageBuffer: case wgpu::BindingType::Sampler: case wgpu::BindingType::ComparisonSampler: case wgpu::BindingType::SampledTexture: // Don't require barriers. default: UNREACHABLE(); break; } } } } DidApply(); return {}; } void SetID3D12DescriptorHeaps(ID3D12GraphicsCommandList* commandList) { ASSERT(commandList != nullptr); std::array descriptorHeaps = mAllocator->GetShaderVisibleHeaps(); ASSERT(descriptorHeaps[0] != nullptr); ASSERT(descriptorHeaps[1] != nullptr); commandList->SetDescriptorHeaps(2, descriptorHeaps.data()); } private: void ApplyBindGroup(ID3D12GraphicsCommandList* commandList, const PipelineLayout* pipelineLayout, uint32_t index, BindGroup* group, uint32_t dynamicOffsetCount, const uint64_t* dynamicOffsets) { ASSERT(dynamicOffsetCount == group->GetLayout()->GetDynamicBufferCount()); // Usually, the application won't set the same offsets many times, // so always try to apply dynamic offsets even if the offsets stay the same if (dynamicOffsetCount != 0) { // Update dynamic offsets. // Dynamic buffer bindings are packed at the beginning of the layout. for (BindingIndex bindingIndex = 0; bindingIndex < dynamicOffsetCount; ++bindingIndex) { uint32_t parameterIndex = pipelineLayout->GetDynamicRootParameterIndex(index, bindingIndex); BufferBinding binding = group->GetBindingAsBufferBinding(bindingIndex); // Calculate buffer locations that root descriptors links to. The location // is (base buffer location + initial offset + dynamic offset) uint64_t dynamicOffset = dynamicOffsets[bindingIndex]; uint64_t offset = binding.offset + dynamicOffset; D3D12_GPU_VIRTUAL_ADDRESS bufferLocation = ToBackend(binding.buffer)->GetVA() + offset; switch (group->GetLayout()->GetBindingInfo(bindingIndex).type) { case wgpu::BindingType::UniformBuffer: if (mInCompute) { commandList->SetComputeRootConstantBufferView(parameterIndex, bufferLocation); } else { commandList->SetGraphicsRootConstantBufferView(parameterIndex, bufferLocation); } break; case wgpu::BindingType::StorageBuffer: if (mInCompute) { commandList->SetComputeRootUnorderedAccessView(parameterIndex, bufferLocation); } else { commandList->SetGraphicsRootUnorderedAccessView(parameterIndex, bufferLocation); } break; case wgpu::BindingType::ReadonlyStorageBuffer: if (mInCompute) { commandList->SetComputeRootShaderResourceView(parameterIndex, bufferLocation); } else { commandList->SetGraphicsRootShaderResourceView(parameterIndex, bufferLocation); } break; case wgpu::BindingType::SampledTexture: case wgpu::BindingType::Sampler: case wgpu::BindingType::ComparisonSampler: case wgpu::BindingType::StorageTexture: case wgpu::BindingType::ReadonlyStorageTexture: case wgpu::BindingType::WriteonlyStorageTexture: UNREACHABLE(); break; } } } // It's not necessary to update descriptor tables if only the dynamic offset changed. if (!mDirtyBindGroups[index]) { return; } const uint32_t cbvUavSrvCount = ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount(); const uint32_t samplerCount = ToBackend(group->GetLayout())->GetSamplerDescriptorCount(); if (cbvUavSrvCount > 0) { uint32_t parameterIndex = pipelineLayout->GetCbvUavSrvRootParameterIndex(index); const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor = group->GetBaseCbvUavSrvDescriptor(); if (mInCompute) { commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor); } else { commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor); } } if (samplerCount > 0) { uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index); const D3D12_GPU_DESCRIPTOR_HANDLE baseDescriptor = group->GetBaseSamplerDescriptor(); if (mInCompute) { commandList->SetComputeRootDescriptorTable(parameterIndex, baseDescriptor); } else { commandList->SetGraphicsRootDescriptorTable(parameterIndex, baseDescriptor); } } } bool mInCompute = false; ShaderVisibleDescriptorAllocator* mAllocator; }; namespace { class VertexBufferTracker { public: void OnSetVertexBuffer(uint32_t slot, Buffer* buffer, uint64_t offset) { mStartSlot = std::min(mStartSlot, slot); mEndSlot = std::max(mEndSlot, slot + 1); auto* d3d12BufferView = &mD3D12BufferViews[slot]; d3d12BufferView->BufferLocation = buffer->GetVA() + offset; d3d12BufferView->SizeInBytes = buffer->GetSize() - offset; // The bufferView stride is set based on the vertex state before a draw. } void Apply(ID3D12GraphicsCommandList* commandList, const RenderPipeline* renderPipeline) { ASSERT(renderPipeline != nullptr); std::bitset vertexBufferSlotsUsed = renderPipeline->GetVertexBufferSlotsUsed(); uint32_t startSlot = mStartSlot; uint32_t endSlot = mEndSlot; // If the vertex state has changed, we need to update the StrideInBytes // for the D3D12 buffer views. We also need to extend the dirty range to // touch all these slots because the stride may have changed. if (mLastAppliedRenderPipeline != renderPipeline) { mLastAppliedRenderPipeline = renderPipeline; for (uint32_t slot : IterateBitSet(vertexBufferSlotsUsed)) { startSlot = std::min(startSlot, slot); endSlot = std::max(endSlot, slot + 1); mD3D12BufferViews[slot].StrideInBytes = renderPipeline->GetVertexBuffer(slot).arrayStride; } } if (endSlot <= startSlot) { return; } // mD3D12BufferViews is kept up to date with the most recent data passed // to SetVertexBuffer. This makes it correct to only track the start // and end of the dirty range. When Apply is called, // we will at worst set non-dirty vertex buffers in duplicate. uint32_t count = endSlot - startSlot; commandList->IASetVertexBuffers(startSlot, count, &mD3D12BufferViews[startSlot]); mStartSlot = kMaxVertexBuffers; mEndSlot = 0; } private: // startSlot and endSlot indicate the range of dirty vertex buffers. // If there are multiple calls to SetVertexBuffer, the start and end // represent the union of the dirty ranges (the union may have non-dirty // data in the middle of the range). const RenderPipeline* mLastAppliedRenderPipeline = nullptr; uint32_t mStartSlot = kMaxVertexBuffers; uint32_t mEndSlot = 0; std::array mD3D12BufferViews = {}; }; class IndexBufferTracker { public: void OnSetIndexBuffer(Buffer* buffer, uint64_t offset) { mD3D12BufferView.BufferLocation = buffer->GetVA() + offset; mD3D12BufferView.SizeInBytes = buffer->GetSize() - offset; // We don't need to dirty the state unless BufferLocation or SizeInBytes // change, but most of the time this will always be the case. mLastAppliedIndexFormat = DXGI_FORMAT_UNKNOWN; } void OnSetPipeline(const RenderPipelineBase* pipeline) { mD3D12BufferView.Format = DXGIIndexFormat(pipeline->GetVertexStateDescriptor()->indexFormat); } void Apply(ID3D12GraphicsCommandList* commandList) { if (mD3D12BufferView.Format == mLastAppliedIndexFormat) { return; } commandList->IASetIndexBuffer(&mD3D12BufferView); mLastAppliedIndexFormat = mD3D12BufferView.Format; } private: DXGI_FORMAT mLastAppliedIndexFormat = DXGI_FORMAT_UNKNOWN; D3D12_INDEX_BUFFER_VIEW mD3D12BufferView = {}; }; void ResolveMultisampledRenderPass(CommandRecordingContext* commandContext, BeginRenderPassCmd* renderPass) { ASSERT(renderPass != nullptr); for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { TextureViewBase* resolveTarget = renderPass->colorAttachments[i].resolveTarget.Get(); if (resolveTarget == nullptr) { continue; } Texture* colorTexture = ToBackend(renderPass->colorAttachments[i].view->GetTexture()); Texture* resolveTexture = ToBackend(resolveTarget->GetTexture()); // Transition the usages of the color attachment and resolve target. colorTexture->TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RESOLVE_SOURCE); resolveTexture->TrackUsageAndTransitionNow(commandContext, D3D12_RESOURCE_STATE_RESOLVE_DEST); // Do MSAA resolve with ResolveSubResource(). ID3D12Resource* colorTextureHandle = colorTexture->GetD3D12Resource(); ID3D12Resource* resolveTextureHandle = resolveTexture->GetD3D12Resource(); const uint32_t resolveTextureSubresourceIndex = resolveTexture->GetSubresourceIndex( resolveTarget->GetBaseMipLevel(), resolveTarget->GetBaseArrayLayer()); constexpr uint32_t kColorTextureSubresourceIndex = 0; commandContext->GetCommandList()->ResolveSubresource( resolveTextureHandle, resolveTextureSubresourceIndex, colorTextureHandle, kColorTextureSubresourceIndex, colorTexture->GetD3D12Format()); } } } // anonymous namespace CommandBuffer::CommandBuffer(CommandEncoder* encoder, const CommandBufferDescriptor* descriptor) : CommandBufferBase(encoder, descriptor), mCommands(encoder->AcquireCommands()) { } CommandBuffer::~CommandBuffer() { FreeCommands(&mCommands); } MaybeError CommandBuffer::RecordCommands(CommandRecordingContext* commandContext) { Device* device = ToBackend(GetDevice()); BindGroupStateTracker bindingTracker(device); ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); // Make sure we use the correct descriptors for this command list. Could be done once per // actual command list but here is ok because there should be few command buffers. bindingTracker.SetID3D12DescriptorHeaps(commandList); // Records the necessary barriers for the resource usage pre-computed by the frontend auto PrepareResourcesForSubmission = [](CommandRecordingContext* commandContext, const PassResourceUsage& usages) -> bool { std::vector 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]) ->TrackUsageAndGetResourceBarrier(commandContext, &barrier, usages.bufferUsages[i])) { barriers.push_back(barrier); } bufferUsages |= usages.bufferUsages[i]; } for (size_t i = 0; i < usages.textures.size(); ++i) { Texture* texture = ToBackend(usages.textures[i]); // Clear textures that are not output attachments. Output attachments will be // cleared during record render pass if the texture subresource has not been // initialized before the render pass. if (!(usages.textureUsages[i] & wgpu::TextureUsage::OutputAttachment)) { texture->EnsureSubresourceContentInitialized(commandContext, 0, texture->GetNumMipLevels(), 0, texture->GetArrayLayers()); } } 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]) ->TrackUsageAndGetResourceBarrier(commandContext, &barrier, 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& passResourceUsages = GetResourceUsages().perPass; uint32_t nextPassNumber = 0; Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::BeginComputePass: { mCommands.NextCommand(); PrepareResourcesForSubmission(commandContext, passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(true); DAWN_TRY(RecordComputePass(commandContext, &bindingTracker)); nextPassNumber++; break; } case Command::BeginRenderPass: { BeginRenderPassCmd* beginRenderPassCmd = mCommands.NextCommand(); const bool passHasUAV = PrepareResourcesForSubmission( commandContext, passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(false); LazyClearRenderPassAttachments(beginRenderPassCmd); DAWN_TRY(RecordRenderPass(commandContext, &bindingTracker, beginRenderPassCmd, passHasUAV)); nextPassNumber++; break; } case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = mCommands.NextCommand(); Buffer* srcBuffer = ToBackend(copy->source.Get()); Buffer* dstBuffer = ToBackend(copy->destination.Get()); srcBuffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc); dstBuffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst); commandList->CopyBufferRegion( dstBuffer->GetD3D12Resource().Get(), copy->destinationOffset, srcBuffer->GetD3D12Resource().Get(), copy->sourceOffset, copy->size); break; } case Command::CopyBufferToTexture: { CopyBufferToTextureCmd* copy = mCommands.NextCommand(); Buffer* buffer = ToBackend(copy->source.buffer.Get()); Texture* texture = ToBackend(copy->destination.texture.Get()); if (IsCompleteSubresourceCopiedTo(texture, copy->copySize, copy->destination.mipLevel)) { texture->SetIsSubresourceContentInitialized( true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1); } else { texture->EnsureSubresourceContentInitialized( commandContext, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1); } buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopySrc); texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst); auto copySplit = ComputeTextureCopySplit( copy->destination.origin, copy->copySize, texture->GetFormat(), copy->source.offset, copy->source.rowPitch, copy->source.imageHeight); D3D12_TEXTURE_COPY_LOCATION textureLocation = ComputeTextureCopyLocationForTexture(texture, copy->destination.mipLevel, copy->destination.arrayLayer); for (uint32_t i = 0; i < copySplit.count; ++i) { TextureCopySplit::CopyInfo& info = copySplit.copies[i]; D3D12_TEXTURE_COPY_LOCATION bufferLocation = ComputeBufferLocationForCopyTextureRegion( texture, buffer->GetD3D12Resource().Get(), info.bufferSize, copySplit.offset, copy->source.rowPitch); D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize(info.bufferOffset, info.copySize); commandList->CopyTextureRegion(&textureLocation, info.textureOffset.x, info.textureOffset.y, info.textureOffset.z, &bufferLocation, &sourceRegion); } break; } case Command::CopyTextureToBuffer: { CopyTextureToBufferCmd* copy = mCommands.NextCommand(); Texture* texture = ToBackend(copy->source.texture.Get()); Buffer* buffer = ToBackend(copy->destination.buffer.Get()); texture->EnsureSubresourceContentInitialized( commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1); texture->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc); buffer->TrackUsageAndTransitionNow(commandContext, wgpu::BufferUsage::CopyDst); TextureCopySplit copySplit = ComputeTextureCopySplit( copy->source.origin, copy->copySize, texture->GetFormat(), copy->destination.offset, copy->destination.rowPitch, copy->destination.imageHeight); D3D12_TEXTURE_COPY_LOCATION textureLocation = ComputeTextureCopyLocationForTexture(texture, copy->source.mipLevel, copy->source.arrayLayer); for (uint32_t i = 0; i < copySplit.count; ++i) { TextureCopySplit::CopyInfo& info = copySplit.copies[i]; D3D12_TEXTURE_COPY_LOCATION bufferLocation = ComputeBufferLocationForCopyTextureRegion( texture, buffer->GetD3D12Resource().Get(), info.bufferSize, copySplit.offset, copy->destination.rowPitch); D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize(info.textureOffset, info.copySize); commandList->CopyTextureRegion(&bufferLocation, info.bufferOffset.x, info.bufferOffset.y, info.bufferOffset.z, &textureLocation, &sourceRegion); } break; } case Command::CopyTextureToTexture: { CopyTextureToTextureCmd* copy = mCommands.NextCommand(); Texture* source = ToBackend(copy->source.texture.Get()); Texture* destination = ToBackend(copy->destination.texture.Get()); source->EnsureSubresourceContentInitialized( commandContext, copy->source.mipLevel, 1, copy->source.arrayLayer, 1); if (IsCompleteSubresourceCopiedTo(destination, copy->copySize, copy->destination.mipLevel)) { destination->SetIsSubresourceContentInitialized( true, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1); } else { destination->EnsureSubresourceContentInitialized( commandContext, copy->destination.mipLevel, 1, copy->destination.arrayLayer, 1); } source->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopySrc); destination->TrackUsageAndTransitionNow(commandContext, wgpu::TextureUsage::CopyDst); if (CanUseCopyResource(source, destination, copy->copySize)) { commandList->CopyResource(destination->GetD3D12Resource(), source->GetD3D12Resource()); } else { D3D12_TEXTURE_COPY_LOCATION srcLocation = ComputeTextureCopyLocationForTexture(source, copy->source.mipLevel, copy->source.arrayLayer); D3D12_TEXTURE_COPY_LOCATION dstLocation = ComputeTextureCopyLocationForTexture(destination, copy->destination.mipLevel, copy->destination.arrayLayer); D3D12_BOX sourceRegion = ComputeD3D12BoxFromOffsetAndSize(copy->source.origin, copy->copySize); commandList->CopyTextureRegion( &dstLocation, copy->destination.origin.x, copy->destination.origin.y, copy->destination.origin.z, &srcLocation, &sourceRegion); } break; } default: { UNREACHABLE(); break; } } } return {}; } MaybeError CommandBuffer::RecordComputePass(CommandRecordingContext* commandContext, BindGroupStateTracker* bindingTracker) { PipelineLayout* lastLayout = nullptr; ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::Dispatch: { DispatchCmd* dispatch = mCommands.NextCommand(); DAWN_TRY(bindingTracker->Apply(commandContext)); commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z); break; } case Command::DispatchIndirect: { DispatchIndirectCmd* dispatch = mCommands.NextCommand(); DAWN_TRY(bindingTracker->Apply(commandContext)); Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get()); ComPtr signature = ToBackend(GetDevice())->GetDispatchIndirectSignature(); commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource().Get(), dispatch->indirectOffset, nullptr, 0); break; } case Command::EndComputePass: { mCommands.NextCommand(); return {}; } case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = mCommands.NextCommand(); ComputePipeline* pipeline = ToBackend(cmd->pipeline).Get(); PipelineLayout* layout = ToBackend(pipeline->GetLayout()); commandList->SetComputeRootSignature(layout->GetRootSignature().Get()); commandList->SetPipelineState(pipeline->GetPipelineState().Get()); bindingTracker->OnSetPipeline(pipeline); lastLayout = layout; break; } case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); uint32_t* dynamicOffsets = nullptr; if (cmd->dynamicOffsetCount > 0) { dynamicOffsets = mCommands.NextData(cmd->dynamicOffsetCount); } bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount, dynamicOffsets); break; } case Command::InsertDebugMarker: { InsertDebugMarkerCmd* cmd = mCommands.NextCommand(); const char* label = mCommands.NextData(cmd->length + 1); if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { // PIX color is 1 byte per channel in ARGB format constexpr uint64_t kPIXBlackColor = 0xff000000; ToBackend(GetDevice()) ->GetFunctions() ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label); } break; } case Command::PopDebugGroup: { mCommands.NextCommand(); if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { ToBackend(GetDevice()) ->GetFunctions() ->pixEndEventOnCommandList(commandList); } break; } case Command::PushDebugGroup: { PushDebugGroupCmd* cmd = mCommands.NextCommand(); const char* label = mCommands.NextData(cmd->length + 1); if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { // PIX color is 1 byte per channel in ARGB format constexpr uint64_t kPIXBlackColor = 0xff000000; ToBackend(GetDevice()) ->GetFunctions() ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label); } break; } default: { UNREACHABLE(); break; } } } return {}; } MaybeError CommandBuffer::SetupRenderPass(CommandRecordingContext* commandContext, BeginRenderPassCmd* renderPass, RenderPassBuilder* renderPassBuilder) { Device* device = ToBackend(GetDevice()); for (uint32_t i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { RenderPassColorAttachmentInfo& attachmentInfo = renderPass->colorAttachments[i]; TextureView* view = ToBackend(attachmentInfo.view.Get()); // Set view attachment. CPUDescriptorHeapAllocation rtvAllocation; DAWN_TRY_ASSIGN( rtvAllocation, device->GetRenderTargetViewAllocator()->AllocateTransientCPUDescriptors()); const D3D12_RENDER_TARGET_VIEW_DESC viewDesc = view->GetRTVDescriptor(); const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = rtvAllocation.GetBaseDescriptor(); device->GetD3D12Device()->CreateRenderTargetView( ToBackend(view->GetTexture())->GetD3D12Resource(), &viewDesc, baseDescriptor); renderPassBuilder->SetRenderTargetView(i, baseDescriptor); // 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->TrackUsageAndTransitionNow( commandContext, D3D12_RESOURCE_STATE_RESOLVE_DEST); renderPassBuilder->SetRenderTargetEndingAccessResolve(i, attachmentInfo.storeOp, view, resolveDestinationView); } else { renderPassBuilder->SetRenderTargetEndingAccess(i, attachmentInfo.storeOp); } } if (renderPass->attachmentState->HasDepthStencilAttachment()) { RenderPassDepthStencilAttachmentInfo& attachmentInfo = renderPass->depthStencilAttachment; TextureView* view = ToBackend(renderPass->depthStencilAttachment.view.Get()); // Set depth attachment. CPUDescriptorHeapAllocation dsvAllocation; DAWN_TRY_ASSIGN( dsvAllocation, device->GetDepthStencilViewAllocator()->AllocateTransientCPUDescriptors()); const D3D12_DEPTH_STENCIL_VIEW_DESC viewDesc = view->GetDSVDescriptor(); const D3D12_CPU_DESCRIPTOR_HANDLE baseDescriptor = dsvAllocation.GetBaseDescriptor(); device->GetD3D12Device()->CreateDepthStencilView( ToBackend(view->GetTexture())->GetD3D12Resource(), &viewDesc, baseDescriptor); renderPassBuilder->SetDepthStencilView(baseDescriptor); const bool hasDepth = view->GetTexture()->GetFormat().HasDepth(); const bool hasStencil = view->GetTexture()->GetFormat().HasStencil(); // 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(); } } else { renderPassBuilder->SetDepthStencilNoAccess(); } return {}; } void CommandBuffer::EmulateBeginRenderPass(CommandRecordingContext* commandContext, const RenderPassBuilder* renderPassBuilder) const { ID3D12GraphicsCommandList* commandList = commandContext->GetCommandList(); // Clear framebuffer attachments as needed. { for (uint32_t i = 0; i < renderPassBuilder->GetColorAttachmentCount(); i++) { // Load op - color 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 (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? if (clearFlags) { commandList->ClearDepthStencilView( renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor, clearFlags, depthClear, stencilClear, 0, nullptr); } } } commandList->OMSetRenderTargets( renderPassBuilder->GetColorAttachmentCount(), renderPassBuilder->GetRenderTargetViews(), FALSE, renderPassBuilder->HasDepth() ? &renderPassBuilder->GetRenderPassDepthStencilDescriptor()->cpuDescriptor : nullptr); } MaybeError CommandBuffer::RecordRenderPass(CommandRecordingContext* commandContext, BindGroupStateTracker* bindingTracker, BeginRenderPassCmd* renderPass, const bool passHasUAV) { Device* device = ToBackend(GetDevice()); const bool useRenderPass = device->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(passHasUAV); DAWN_TRY(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; uint32_t height = renderPass->height; D3D12_VIEWPORT viewport = { 0.f, 0.f, static_cast(width), static_cast(height), 0.f, 1.f}; D3D12_RECT scissorRect = {0, 0, static_cast(width), static_cast(height)}; commandList->RSSetViewports(1, &viewport); commandList->RSSetScissorRects(1, &scissorRect); static constexpr std::array defaultBlendFactor = {0, 0, 0, 0}; commandList->OMSetBlendFactor(&defaultBlendFactor[0]); } RenderPipeline* lastPipeline = nullptr; PipelineLayout* lastLayout = nullptr; VertexBufferTracker vertexBufferTracker = {}; IndexBufferTracker indexBufferTracker = {}; auto EncodeRenderBundleCommand = [&](CommandIterator* iter, Command type) -> MaybeError { switch (type) { case Command::Draw: { DrawCmd* draw = iter->NextCommand(); DAWN_TRY(bindingTracker->Apply(commandContext)); vertexBufferTracker.Apply(commandList, lastPipeline); commandList->DrawInstanced(draw->vertexCount, draw->instanceCount, draw->firstVertex, draw->firstInstance); break; } case Command::DrawIndexed: { DrawIndexedCmd* draw = iter->NextCommand(); DAWN_TRY(bindingTracker->Apply(commandContext)); indexBufferTracker.Apply(commandList); vertexBufferTracker.Apply(commandList, lastPipeline); commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount, draw->firstIndex, draw->baseVertex, draw->firstInstance); break; } case Command::DrawIndirect: { DrawIndirectCmd* draw = iter->NextCommand(); DAWN_TRY(bindingTracker->Apply(commandContext)); vertexBufferTracker.Apply(commandList, lastPipeline); Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); ComPtr signature = ToBackend(GetDevice())->GetDrawIndirectSignature(); commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource().Get(), draw->indirectOffset, nullptr, 0); break; } case Command::DrawIndexedIndirect: { DrawIndexedIndirectCmd* draw = iter->NextCommand(); DAWN_TRY(bindingTracker->Apply(commandContext)); indexBufferTracker.Apply(commandList); vertexBufferTracker.Apply(commandList, lastPipeline); Buffer* buffer = ToBackend(draw->indirectBuffer.Get()); ComPtr signature = ToBackend(GetDevice())->GetDrawIndexedIndirectSignature(); commandList->ExecuteIndirect(signature.Get(), 1, buffer->GetD3D12Resource().Get(), draw->indirectOffset, nullptr, 0); break; } case Command::InsertDebugMarker: { InsertDebugMarkerCmd* cmd = iter->NextCommand(); const char* label = iter->NextData(cmd->length + 1); if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { // PIX color is 1 byte per channel in ARGB format constexpr uint64_t kPIXBlackColor = 0xff000000; ToBackend(GetDevice()) ->GetFunctions() ->pixSetMarkerOnCommandList(commandList, kPIXBlackColor, label); } break; } case Command::PopDebugGroup: { iter->NextCommand(); if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { ToBackend(GetDevice()) ->GetFunctions() ->pixEndEventOnCommandList(commandList); } break; } case Command::PushDebugGroup: { PushDebugGroupCmd* cmd = iter->NextCommand(); const char* label = iter->NextData(cmd->length + 1); if (ToBackend(GetDevice())->GetFunctions()->IsPIXEventRuntimeLoaded()) { // PIX color is 1 byte per channel in ARGB format constexpr uint64_t kPIXBlackColor = 0xff000000; ToBackend(GetDevice()) ->GetFunctions() ->pixBeginEventOnCommandList(commandList, kPIXBlackColor, label); } break; } case Command::SetRenderPipeline: { SetRenderPipelineCmd* cmd = iter->NextCommand(); RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); PipelineLayout* layout = ToBackend(pipeline->GetLayout()); commandList->SetGraphicsRootSignature(layout->GetRootSignature().Get()); commandList->SetPipelineState(pipeline->GetPipelineState().Get()); commandList->IASetPrimitiveTopology(pipeline->GetD3D12PrimitiveTopology()); bindingTracker->OnSetPipeline(pipeline); indexBufferTracker.OnSetPipeline(pipeline); lastPipeline = pipeline; lastLayout = layout; break; } case Command::SetBindGroup: { SetBindGroupCmd* cmd = iter->NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); uint32_t* dynamicOffsets = nullptr; if (cmd->dynamicOffsetCount > 0) { dynamicOffsets = iter->NextData(cmd->dynamicOffsetCount); } bindingTracker->OnSetBindGroup(cmd->index, group, cmd->dynamicOffsetCount, dynamicOffsets); break; } case Command::SetIndexBuffer: { SetIndexBufferCmd* cmd = iter->NextCommand(); indexBufferTracker.OnSetIndexBuffer(ToBackend(cmd->buffer.Get()), cmd->offset); break; } case Command::SetVertexBuffer: { SetVertexBufferCmd* cmd = iter->NextCommand(); vertexBufferTracker.OnSetVertexBuffer(cmd->slot, ToBackend(cmd->buffer.Get()), cmd->offset); break; } default: UNREACHABLE(); break; } return {}; }; Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::EndRenderPass: { mCommands.NextCommand(); if (useRenderPass) { commandContext->GetCommandList4()->EndRenderPass(); } else if (renderPass->attachmentState->GetSampleCount() > 1) { ResolveMultisampledRenderPass(commandContext, renderPass); } return {}; } case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); commandList->OMSetStencilRef(cmd->reference); break; } case Command::SetViewport: { SetViewportCmd* cmd = mCommands.NextCommand(); D3D12_VIEWPORT viewport; viewport.TopLeftX = cmd->x; viewport.TopLeftY = cmd->y; viewport.Width = cmd->width; viewport.Height = cmd->height; viewport.MinDepth = cmd->minDepth; viewport.MaxDepth = cmd->maxDepth; commandList->RSSetViewports(1, &viewport); break; } case Command::SetScissorRect: { SetScissorRectCmd* cmd = mCommands.NextCommand(); D3D12_RECT rect; rect.left = cmd->x; rect.top = cmd->y; rect.right = cmd->x + cmd->width; rect.bottom = cmd->y + cmd->height; commandList->RSSetScissorRects(1, &rect); break; } case Command::SetBlendColor: { SetBlendColorCmd* cmd = mCommands.NextCommand(); commandList->OMSetBlendFactor(static_cast(&cmd->color.r)); break; } case Command::ExecuteBundles: { ExecuteBundlesCmd* cmd = mCommands.NextCommand(); auto bundles = mCommands.NextData>(cmd->count); for (uint32_t i = 0; i < cmd->count; ++i) { CommandIterator* iter = bundles[i]->GetCommands(); iter->Reset(); while (iter->NextCommandId(&type)) { DAWN_TRY(EncodeRenderBundleCommand(iter, type)); } } break; } default: { DAWN_TRY(EncodeRenderBundleCommand(&mCommands, type)); break; } } } return {}; } }} // namespace dawn_native::d3d12