| // Copyright 2023 The Dawn & Tint Authors |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // 1. Redistributions of source code must retain the above copyright notice, this |
| // list of conditions and the following disclaimer. |
| // |
| // 2. Redistributions in binary form must reproduce the above copyright notice, |
| // this list of conditions and the following disclaimer in the documentation |
| // and/or other materials provided with the distribution. |
| // |
| // 3. Neither the name of the copyright holder nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
| // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
| // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "dawn/native/d3d11/CommandBufferD3D11.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "dawn/common/WindowsUtils.h" |
| #include "dawn/native/ChainUtils.h" |
| #include "dawn/native/CommandEncoder.h" |
| #include "dawn/native/CommandValidation.h" |
| #include "dawn/native/Commands.h" |
| #include "dawn/native/ExternalTexture.h" |
| #include "dawn/native/RenderBundle.h" |
| #include "dawn/native/d3d/D3DError.h" |
| #include "dawn/native/d3d11/BindGroupTrackerD3D11.h" |
| #include "dawn/native/d3d11/BufferD3D11.h" |
| #include "dawn/native/d3d11/CommandRecordingContextD3D11.h" |
| #include "dawn/native/d3d11/ComputePipelineD3D11.h" |
| #include "dawn/native/d3d11/DeviceD3D11.h" |
| #include "dawn/native/d3d11/Forward.h" |
| #include "dawn/native/d3d11/PipelineLayoutD3D11.h" |
| #include "dawn/native/d3d11/QuerySetD3D11.h" |
| #include "dawn/native/d3d11/RenderPipelineD3D11.h" |
| #include "dawn/native/d3d11/TextureD3D11.h" |
| #include "dawn/native/d3d11/UtilsD3D11.h" |
| #include "partition_alloc/pointers/raw_ptr.h" |
| |
| namespace dawn::native::d3d11 { |
| 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: |
| DAWN_UNREACHABLE(); |
| } |
| } |
| |
| class VertexBufferTracker { |
| public: |
| explicit VertexBufferTracker(const ScopedSwapStateCommandRecordingContext* commandContext) |
| : mCommandContext(commandContext) {} |
| |
| ~VertexBufferTracker() { |
| mD3D11Buffers = {}; |
| mStrides = {}; |
| mOffsets = {}; |
| mCommandContext->GetD3D11DeviceContext4()->IASetVertexBuffers( |
| 0, kMaxVertexBuffers, mD3D11Buffers.data(), mStrides.data(), mOffsets.data()); |
| } |
| |
| void OnSetVertexBuffer(VertexBufferSlot slot, ID3D11Buffer* buffer, uint64_t offset) { |
| mD3D11Buffers[slot] = buffer; |
| mOffsets[slot] = offset; |
| } |
| |
| void Apply(const RenderPipeline* renderPipeline) { |
| DAWN_ASSERT(renderPipeline != nullptr); |
| |
| // If the vertex state has changed, we need to update the strides. |
| if (mLastAppliedRenderPipeline != renderPipeline) { |
| mLastAppliedRenderPipeline = renderPipeline; |
| for (VertexBufferSlot slot : IterateBitSet(renderPipeline->GetVertexBuffersUsed())) { |
| mStrides[slot] = renderPipeline->GetVertexBuffer(slot).arrayStride; |
| } |
| } |
| |
| mCommandContext->GetD3D11DeviceContext4()->IASetVertexBuffers( |
| 0, kMaxVertexBuffers, mD3D11Buffers.data(), mStrides.data(), mOffsets.data()); |
| } |
| |
| private: |
| raw_ptr<const ScopedSwapStateCommandRecordingContext> mCommandContext; |
| raw_ptr<const RenderPipeline> mLastAppliedRenderPipeline = nullptr; |
| PerVertexBuffer<ID3D11Buffer*> mD3D11Buffers = {}; |
| PerVertexBuffer<UINT> mStrides = {}; |
| PerVertexBuffer<UINT> mOffsets = {}; |
| }; |
| |
| // Handle pixel local storage attachments and return a vector of all pixel local storage UAVs. |
| // - For implicit attachments, create the texture and clear it to 0. |
| // - For explicit attachments, clear them to the specified clear color if their load operation is |
| // `clear` |
| ResultOrError<std::vector<ComPtr<ID3D11UnorderedAccessView>>> |
| HandlePixelLocalStorageAndGetPixelLocalStorageUAVs( |
| DeviceBase* device, |
| const BeginRenderPassCmd* renderPass, |
| const ScopedSwapStateCommandRecordingContext* commandContext) { |
| std::vector<ComPtr<ID3D11UnorderedAccessView>> pixelLocalStorageUAVs; |
| auto d3d11DeviceContext = commandContext->GetD3D11DeviceContext4(); |
| |
| const std::vector<wgpu::TextureFormat>& storageAttachmentSlots = |
| renderPass->attachmentState->GetStorageAttachmentSlots(); |
| uint32_t nextImplicitAttachmentIndex = 0; |
| for (size_t attachment = 0; attachment < storageAttachmentSlots.size(); attachment++) { |
| ComPtr<ID3D11UnorderedAccessView> pixelLocalStorageUAV; |
| if (storageAttachmentSlots[attachment] == wgpu::TextureFormat::Undefined) { |
| // Get implicit pixel local storage attachment |
| TextureViewBase* implicitPixelLocalStorageTextureView = nullptr; |
| DAWN_TRY_ASSIGN( |
| implicitPixelLocalStorageTextureView, |
| ToBackend(device)->GetOrCreateCachedImplicitPixelLocalStorageAttachment( |
| renderPass->width, renderPass->height, nextImplicitAttachmentIndex)); |
| ++nextImplicitAttachmentIndex; |
| |
| // Get and clear the UAV of the implicit pixel local storage attachment |
| DAWN_TRY_ASSIGN(pixelLocalStorageUAV, ToBackend(implicitPixelLocalStorageTextureView) |
| ->GetOrCreateD3D11UnorderedAccessView()); |
| |
| // TODO(dawn:1704): investigate if we can only clear it when necessary. |
| uint32_t clearValue[4] = {0, 0, 0, 0}; |
| d3d11DeviceContext->ClearUnorderedAccessViewUint(pixelLocalStorageUAV.Get(), |
| clearValue); |
| } else { |
| // Get the UAV of the explicit pixel local storage attachment |
| auto& attachmentInfo = renderPass->storageAttachments[attachment]; |
| DAWN_TRY_ASSIGN( |
| pixelLocalStorageUAV, |
| ToBackend(attachmentInfo.storage.Get())->GetOrCreateD3D11UnorderedAccessView()); |
| |
| // Execute the load operation of the pixel local storage attachment |
| switch (attachmentInfo.loadOp) { |
| case wgpu::LoadOp::Clear: { |
| switch (attachmentInfo.storage->GetFormat().format) { |
| case wgpu::TextureFormat::R32Float: { |
| float clearValue[4] = {static_cast<float>(attachmentInfo.clearColor.r), |
| 0, 0, 0}; |
| d3d11DeviceContext->ClearUnorderedAccessViewFloat( |
| pixelLocalStorageUAV.Get(), clearValue); |
| break; |
| } |
| case wgpu::TextureFormat::R32Sint: { |
| uint32_t clearValue[4] = {static_cast<uint32_t>(static_cast<int32_t>( |
| attachmentInfo.clearColor.r)), |
| 0, 0, 0}; |
| d3d11DeviceContext->ClearUnorderedAccessViewUint( |
| pixelLocalStorageUAV.Get(), clearValue); |
| break; |
| } |
| case wgpu::TextureFormat::R32Uint: { |
| uint32_t clearValue[4] = { |
| static_cast<uint32_t>(attachmentInfo.clearColor.r), 0, 0, 0}; |
| d3d11DeviceContext->ClearUnorderedAccessViewUint( |
| pixelLocalStorageUAV.Get(), clearValue); |
| break; |
| } |
| default: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| break; |
| } |
| case wgpu::LoadOp::Load: |
| case wgpu::LoadOp::ExpandResolveTexture: |
| break; |
| case wgpu::LoadOp::Undefined: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| } |
| pixelLocalStorageUAVs.push_back(pixelLocalStorageUAV); |
| } |
| |
| return pixelLocalStorageUAVs; |
| } |
| |
| } // namespace |
| |
| // Create CommandBuffer |
| Ref<CommandBuffer> CommandBuffer::Create(CommandEncoder* encoder, |
| const CommandBufferDescriptor* descriptor) { |
| return AcquireRef(new CommandBuffer(encoder, descriptor)); |
| } |
| |
| MaybeError CommandBuffer::Execute(const ScopedSwapStateCommandRecordingContext* commandContext) { |
| auto LazyClearSyncScope = [&commandContext](const SyncScopeResourceUsage& scope) -> MaybeError { |
| for (size_t i = 0; i < scope.textures.size(); i++) { |
| Texture* texture = ToBackend(scope.textures[i]); |
| |
| // Clear subresources that are not render attachments or storage attachment. Render |
| // attachments will be cleared in RecordBeginRenderPass by setting the loadop to clear |
| // when the texture subresource has not been initialized before the render pass. Storage |
| // attachments will also be cleared in RecordBeginRenderPass by |
| // ClearUnorderedAccessView*() when the texture subresource has not been initialized |
| // before the render pass. |
| DAWN_TRY(scope.textureSyncInfos[i].Iterate([&](const SubresourceRange& range, |
| TextureSyncInfo syncInfo) -> MaybeError { |
| if (syncInfo.usage & ~(wgpu::TextureUsage::RenderAttachment | |
| wgpu::TextureUsage::StorageAttachment)) { |
| DAWN_TRY(texture->EnsureSubresourceContentInitialized(commandContext, range)); |
| } |
| return {}; |
| })); |
| } |
| |
| for (BufferBase* buffer : scope.buffers) { |
| DAWN_TRY(ToBackend(buffer)->EnsureDataInitialized(commandContext)); |
| } |
| |
| return {}; |
| }; |
| |
| size_t nextComputePassNumber = 0; |
| size_t nextRenderPassNumber = 0; |
| |
| Command type; |
| while (mCommands.NextCommandId(&type)) { |
| switch (type) { |
| case Command::BeginComputePass: { |
| mCommands.NextCommand<BeginComputePassCmd>(); |
| for (TextureBase* texture : |
| GetResourceUsages().computePasses[nextComputePassNumber].referencedTextures) { |
| DAWN_TRY(ToBackend(texture)->SynchronizeTextureBeforeUse(commandContext)); |
| } |
| for (const SyncScopeResourceUsage& scope : |
| GetResourceUsages().computePasses[nextComputePassNumber].dispatchUsages) { |
| for (TextureBase* texture : scope.textures) { |
| DAWN_TRY(ToBackend(texture)->SynchronizeTextureBeforeUse(commandContext)); |
| } |
| DAWN_TRY(LazyClearSyncScope(scope)); |
| } |
| DAWN_TRY(ExecuteComputePass(commandContext)); |
| |
| nextComputePassNumber++; |
| break; |
| } |
| |
| case Command::BeginRenderPass: { |
| auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>(); |
| for (TextureBase* texture : |
| GetResourceUsages().renderPasses[nextRenderPassNumber].textures) { |
| DAWN_TRY(ToBackend(texture)->SynchronizeTextureBeforeUse(commandContext)); |
| } |
| for (ExternalTextureBase* externalTexture : |
| GetResourceUsages().renderPasses[nextRenderPassNumber].externalTextures) { |
| for (auto& view : externalTexture->GetTextureViews()) { |
| if (view.Get()) { |
| DAWN_TRY(ToBackend(view->GetTexture()) |
| ->SynchronizeTextureBeforeUse(commandContext)); |
| } |
| } |
| } |
| DAWN_TRY( |
| LazyClearSyncScope(GetResourceUsages().renderPasses[nextRenderPassNumber])); |
| LazyClearRenderPassAttachments(cmd); |
| DAWN_TRY(ExecuteRenderPass(cmd, commandContext)); |
| |
| nextRenderPassNumber++; |
| break; |
| } |
| |
| case Command::CopyBufferToBuffer: { |
| CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>(); |
| if (copy->size == 0) { |
| // Skip no-op copies. |
| break; |
| } |
| |
| Buffer* source = ToBackend(copy->source.Get()); |
| Buffer* destination = ToBackend(copy->destination.Get()); |
| |
| // Buffer::Copy() will ensure the source and destination buffers are initialized. |
| DAWN_TRY(Buffer::Copy(commandContext, source, copy->sourceOffset, copy->size, |
| destination, copy->destinationOffset)); |
| source->MarkUsedInPendingCommands(); |
| destination->MarkUsedInPendingCommands(); |
| break; |
| } |
| |
| case Command::CopyBufferToTexture: { |
| CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>(); |
| if (copy->copySize.width == 0 || copy->copySize.height == 0 || |
| copy->copySize.depthOrArrayLayers == 0) { |
| // Skip no-op copies. |
| continue; |
| } |
| |
| auto& src = copy->source; |
| auto& dst = copy->destination; |
| |
| Buffer* buffer = ToBackend(src.buffer.Get()); |
| uint64_t bufferOffset = src.offset; |
| Ref<BufferBase> stagingBuffer; |
| // If the buffer is not mappable, we need to create a staging buffer and copy the |
| // data from the buffer to the staging buffer. |
| if (!(buffer->GetUsage() & kMappableBufferUsages)) { |
| const TexelBlockInfo& blockInfo = |
| ToBackend(dst.texture)->GetFormat().GetAspectInfo(dst.aspect).block; |
| // TODO(dawn:1768): use compute shader to copy data from buffer to texture. |
| BufferDescriptor desc; |
| DAWN_TRY_ASSIGN(desc.size, |
| ComputeRequiredBytesInCopy(blockInfo, copy->copySize, |
| src.bytesPerRow, src.rowsPerImage)); |
| desc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead; |
| DAWN_TRY_ASSIGN(stagingBuffer, Buffer::Create(ToBackend(GetDevice()), |
| Unpack(&desc), commandContext)); |
| |
| DAWN_TRY(Buffer::Copy(commandContext, buffer, src.offset, |
| stagingBuffer->GetSize(), ToBackend(stagingBuffer.Get()), |
| 0)); |
| buffer = ToBackend(stagingBuffer.Get()); |
| bufferOffset = 0; |
| } |
| |
| Buffer::ScopedMap scopedMap; |
| DAWN_TRY_ASSIGN(scopedMap, Buffer::ScopedMap::Create(commandContext, buffer)); |
| DAWN_TRY(buffer->EnsureDataInitialized(commandContext)); |
| |
| Texture* texture = ToBackend(dst.texture.Get()); |
| DAWN_TRY(texture->SynchronizeTextureBeforeUse(commandContext)); |
| SubresourceRange subresources = GetSubresourcesAffectedByCopy(dst, copy->copySize); |
| |
| DAWN_ASSERT(scopedMap.GetMappedData()); |
| const uint8_t* data = scopedMap.GetMappedData() + bufferOffset; |
| DAWN_TRY(texture->Write(commandContext, subresources, dst.origin, copy->copySize, |
| data, src.bytesPerRow, src.rowsPerImage)); |
| |
| buffer->MarkUsedInPendingCommands(); |
| break; |
| } |
| |
| case Command::CopyTextureToBuffer: { |
| CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>(); |
| if (copy->copySize.width == 0 || copy->copySize.height == 0 || |
| copy->copySize.depthOrArrayLayers == 0) { |
| // Skip no-op copies. |
| continue; |
| } |
| |
| auto& src = copy->source; |
| auto& dst = copy->destination; |
| |
| SubresourceRange subresources = GetSubresourcesAffectedByCopy(src, copy->copySize); |
| Texture* texture = ToBackend(src.texture.Get()); |
| DAWN_TRY(texture->SynchronizeTextureBeforeUse(commandContext)); |
| DAWN_TRY( |
| texture->EnsureSubresourceContentInitialized(commandContext, subresources)); |
| |
| Buffer* buffer = ToBackend(dst.buffer.Get()); |
| Buffer::ScopedMap scopedDstMap; |
| DAWN_TRY_ASSIGN(scopedDstMap, Buffer::ScopedMap::Create(commandContext, buffer)); |
| |
| DAWN_TRY(buffer->EnsureDataInitializedAsDestination(commandContext, copy)); |
| |
| Texture::ReadCallback callback = [&](const uint8_t* data, uint64_t offset, |
| uint64_t size) -> MaybeError { |
| DAWN_TRY(ToBackend(dst.buffer) |
| ->Write(commandContext, dst.offset + offset, data, size)); |
| return {}; |
| }; |
| |
| DAWN_TRY(ToBackend(src.texture) |
| ->Read(commandContext, subresources, src.origin, copy->copySize, |
| dst.bytesPerRow, dst.rowsPerImage, callback)); |
| |
| dst.buffer->MarkUsedInPendingCommands(); |
| break; |
| } |
| |
| case Command::CopyTextureToTexture: { |
| CopyTextureToTextureCmd* copy = mCommands.NextCommand<CopyTextureToTextureCmd>(); |
| if (copy->copySize.width == 0 || copy->copySize.height == 0 || |
| copy->copySize.depthOrArrayLayers == 0) { |
| // Skip no-op copies. |
| continue; |
| } |
| |
| DAWN_TRY(ToBackend(copy->source.texture.Get()) |
| ->SynchronizeTextureBeforeUse(commandContext)); |
| DAWN_TRY(ToBackend(copy->destination.texture.Get()) |
| ->SynchronizeTextureBeforeUse(commandContext)); |
| DAWN_TRY(Texture::Copy(commandContext, copy)); |
| break; |
| } |
| |
| case Command::ClearBuffer: { |
| ClearBufferCmd* cmd = mCommands.NextCommand<ClearBufferCmd>(); |
| if (cmd->size == 0) { |
| // Skip no-op fills. |
| break; |
| } |
| Buffer* buffer = ToBackend(cmd->buffer.Get()); |
| DAWN_TRY(buffer->Clear(commandContext, 0, cmd->offset, cmd->size)); |
| buffer->MarkUsedInPendingCommands(); |
| break; |
| } |
| |
| case Command::ResolveQuerySet: { |
| ResolveQuerySetCmd* cmd = mCommands.NextCommand<ResolveQuerySetCmd>(); |
| QuerySet* querySet = ToBackend(cmd->querySet.Get()); |
| uint32_t firstQuery = cmd->firstQuery; |
| uint32_t queryCount = cmd->queryCount; |
| Buffer* destination = ToBackend(cmd->destination.Get()); |
| uint64_t destinationOffset = cmd->destinationOffset; |
| |
| DAWN_TRY(querySet->Resolve(commandContext, firstQuery, queryCount, destination, |
| destinationOffset)); |
| destination->MarkUsedInPendingCommands(); |
| break; |
| } |
| |
| case Command::WriteTimestamp: { |
| return DAWN_UNIMPLEMENTED_ERROR("WriteTimestamp unimplemented"); |
| } |
| |
| case Command::WriteBuffer: { |
| WriteBufferCmd* cmd = mCommands.NextCommand<WriteBufferCmd>(); |
| if (cmd->size == 0) { |
| // Skip no-op writes. |
| continue; |
| } |
| |
| Buffer* dstBuffer = ToBackend(cmd->buffer.Get()); |
| uint8_t* data = mCommands.NextData<uint8_t>(cmd->size); |
| DAWN_TRY(dstBuffer->Write(commandContext, cmd->offset, data, cmd->size)); |
| dstBuffer->MarkUsedInPendingCommands(); |
| |
| break; |
| } |
| |
| case Command::InsertDebugMarker: |
| case Command::PopDebugGroup: |
| case Command::PushDebugGroup: { |
| HandleDebugCommands(commandContext, &mCommands, type); |
| break; |
| } |
| |
| default: |
| return DAWN_FORMAT_INTERNAL_ERROR("Unknown command type: %d", type); |
| } |
| } |
| |
| return {}; |
| } |
| |
| MaybeError CommandBuffer::ExecuteComputePass( |
| const ScopedSwapStateCommandRecordingContext* commandContext) { |
| ComputePipeline* lastPipeline = nullptr; |
| BindGroupTracker bindGroupTracker(commandContext, /*isRenderPass=*/false); |
| |
| Command type; |
| while (mCommands.NextCommandId(&type)) { |
| switch (type) { |
| case Command::EndComputePass: { |
| mCommands.NextCommand<EndComputePassCmd>(); |
| return {}; |
| } |
| |
| case Command::Dispatch: { |
| DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>(); |
| |
| DAWN_TRY(bindGroupTracker.Apply()); |
| |
| DAWN_TRY(RecordNumWorkgroupsForDispatch(lastPipeline, commandContext, dispatch)); |
| commandContext->GetD3D11DeviceContext4()->Dispatch(dispatch->x, dispatch->y, |
| dispatch->z); |
| |
| break; |
| } |
| |
| case Command::DispatchIndirect: { |
| DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>(); |
| |
| DAWN_TRY(bindGroupTracker.Apply()); |
| |
| Buffer* indirectBuffer = ToBackend(dispatch->indirectBuffer.Get()); |
| |
| if (lastPipeline->UsesNumWorkgroups()) { |
| // Copy indirect args into the uniform buffer for built-in workgroup variables. |
| DAWN_TRY(Buffer::Copy(commandContext, indirectBuffer, dispatch->indirectOffset, |
| sizeof(uint32_t) * 3, commandContext->GetUniformBuffer(), |
| 0)); |
| } |
| |
| commandContext->GetD3D11DeviceContext4()->DispatchIndirect( |
| indirectBuffer->GetD3D11NonConstantBuffer(), dispatch->indirectOffset); |
| |
| break; |
| } |
| |
| case Command::SetComputePipeline: { |
| SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>(); |
| lastPipeline = ToBackend(cmd->pipeline).Get(); |
| lastPipeline->ApplyNow(commandContext); |
| bindGroupTracker.OnSetPipeline(lastPipeline); |
| break; |
| } |
| |
| case Command::SetBindGroup: { |
| SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>(); |
| |
| uint32_t* dynamicOffsets = nullptr; |
| if (cmd->dynamicOffsetCount > 0) { |
| dynamicOffsets = mCommands.NextData<uint32_t>(cmd->dynamicOffsetCount); |
| } |
| |
| bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), |
| cmd->dynamicOffsetCount, dynamicOffsets); |
| |
| break; |
| } |
| |
| case Command::WriteTimestamp: { |
| return DAWN_UNIMPLEMENTED_ERROR("WriteTimestamp unimplemented"); |
| } |
| |
| case Command::InsertDebugMarker: |
| case Command::PopDebugGroup: |
| case Command::PushDebugGroup: { |
| HandleDebugCommands(commandContext, &mCommands, type); |
| break; |
| } |
| |
| default: |
| DAWN_UNREACHABLE(); |
| } |
| } |
| |
| // EndComputePass should have been called |
| DAWN_UNREACHABLE(); |
| } |
| |
| MaybeError CommandBuffer::ExecuteRenderPass( |
| BeginRenderPassCmd* renderPass, |
| const ScopedSwapStateCommandRecordingContext* commandContext) { |
| auto* d3d11DeviceContext = commandContext->GetD3D11DeviceContext4(); |
| |
| // Hold ID3D11RenderTargetView ComPtr to make attachments alive. |
| PerColorAttachment<ID3D11RenderTargetView*> d3d11RenderTargetViews = {}; |
| ColorAttachmentIndex attachmentCount{}; |
| bool clearWithDraw = GetDevice()->IsToggleEnabled(Toggle::ClearColorWithDraw); |
| // TODO(dawn:1815): Shrink the sparse attachments to accommodate more UAVs. |
| for (auto i : IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { |
| TextureView* colorTextureView = ToBackend(renderPass->colorAttachments[i].view.Get()); |
| DAWN_TRY_ASSIGN(d3d11RenderTargetViews[i], |
| colorTextureView->GetOrCreateD3D11RenderTargetView( |
| renderPass->colorAttachments[i].depthSlice)); |
| if (!clearWithDraw && renderPass->colorAttachments[i].loadOp == wgpu::LoadOp::Clear) { |
| std::array<float, 4> clearColor = |
| ConvertToFloatColor(renderPass->colorAttachments[i].clearColor); |
| d3d11DeviceContext->ClearRenderTargetView(d3d11RenderTargetViews[i], clearColor.data()); |
| } |
| attachmentCount = ityp::PlusOne(i); |
| } |
| |
| ID3D11DepthStencilView* d3d11DepthStencilView = nullptr; |
| if (renderPass->attachmentState->HasDepthStencilAttachment()) { |
| auto* attachmentInfo = &renderPass->depthStencilAttachment; |
| const Format& attachmentFormat = attachmentInfo->view->GetTexture()->GetFormat(); |
| |
| TextureView* depthStencilTextureView = |
| ToBackend(renderPass->depthStencilAttachment.view.Get()); |
| DAWN_TRY_ASSIGN(d3d11DepthStencilView, |
| depthStencilTextureView->GetOrCreateD3D11DepthStencilView( |
| attachmentInfo->depthReadOnly, attachmentInfo->stencilReadOnly)); |
| UINT clearFlags = 0; |
| if (attachmentFormat.HasDepth() && |
| renderPass->depthStencilAttachment.depthLoadOp == wgpu::LoadOp::Clear) { |
| clearFlags |= D3D11_CLEAR_DEPTH; |
| } |
| |
| if (attachmentFormat.HasStencil() && |
| renderPass->depthStencilAttachment.stencilLoadOp == wgpu::LoadOp::Clear) { |
| clearFlags |= D3D11_CLEAR_STENCIL; |
| } |
| |
| d3d11DeviceContext->ClearDepthStencilView(d3d11DepthStencilView, clearFlags, |
| attachmentInfo->clearDepth, |
| attachmentInfo->clearStencil); |
| } |
| |
| d3d11DeviceContext->OMSetRenderTargets(static_cast<uint8_t>(attachmentCount), |
| d3d11RenderTargetViews.data(), d3d11DepthStencilView); |
| |
| std::vector<ComPtr<ID3D11UnorderedAccessView>> pixelLocalStorageUAVs; |
| if (renderPass->attachmentState->HasPixelLocalStorage()) { |
| DAWN_TRY_ASSIGN(pixelLocalStorageUAVs, HandlePixelLocalStorageAndGetPixelLocalStorageUAVs( |
| GetDevice(), renderPass, commandContext)); |
| } |
| |
| // Set viewport |
| D3D11_VIEWPORT defautViewport; |
| defautViewport.TopLeftX = 0; |
| defautViewport.TopLeftY = 0; |
| defautViewport.Width = renderPass->width; |
| defautViewport.Height = renderPass->height; |
| defautViewport.MinDepth = 0.0f; |
| defautViewport.MaxDepth = 1.0f; |
| d3d11DeviceContext->RSSetViewports(1, &defautViewport); |
| |
| // Set scissor |
| D3D11_RECT scissor; |
| scissor.left = 0; |
| scissor.top = 0; |
| scissor.right = renderPass->width; |
| scissor.bottom = renderPass->height; |
| d3d11DeviceContext->RSSetScissorRects(1, &scissor); |
| |
| RenderPipeline* lastPipeline = nullptr; |
| BindGroupTracker bindGroupTracker(commandContext, /*isRenderPass=*/true, |
| std::move(pixelLocalStorageUAVs)); |
| VertexBufferTracker vertexBufferTracker(commandContext); |
| std::array<float, 4> blendColor = {0.0f, 0.0f, 0.0f, 0.0f}; |
| uint32_t stencilReference = 0; |
| |
| auto DoRenderBundleCommand = [&](CommandIterator* iter, Command type) -> MaybeError { |
| switch (type) { |
| case Command::Draw: { |
| DrawCmd* draw = iter->NextCommand<DrawCmd>(); |
| |
| DAWN_TRY(bindGroupTracker.Apply()); |
| vertexBufferTracker.Apply(lastPipeline); |
| DAWN_TRY(RecordFirstIndexOffset(lastPipeline, commandContext, draw->firstVertex, |
| draw->firstInstance)); |
| commandContext->GetD3D11DeviceContext4()->DrawInstanced( |
| draw->vertexCount, draw->instanceCount, draw->firstVertex, draw->firstInstance); |
| |
| break; |
| } |
| |
| case Command::DrawIndexed: { |
| DrawIndexedCmd* draw = iter->NextCommand<DrawIndexedCmd>(); |
| |
| DAWN_TRY(bindGroupTracker.Apply()); |
| vertexBufferTracker.Apply(lastPipeline); |
| DAWN_TRY(RecordFirstIndexOffset(lastPipeline, commandContext, draw->baseVertex, |
| draw->firstInstance)); |
| commandContext->GetD3D11DeviceContext4()->DrawIndexedInstanced( |
| draw->indexCount, draw->instanceCount, draw->firstIndex, draw->baseVertex, |
| draw->firstInstance); |
| |
| break; |
| } |
| |
| case Command::DrawIndirect: { |
| DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>(); |
| |
| Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get()); |
| DAWN_ASSERT(indirectBuffer != nullptr); |
| |
| DAWN_TRY(bindGroupTracker.Apply()); |
| vertexBufferTracker.Apply(lastPipeline); |
| |
| if (lastPipeline->UsesVertexIndex() || lastPipeline->UsesInstanceIndex()) { |
| // Copy StartVertexLocation and StartInstanceLocation into the uniform buffer |
| // for built-in variables. |
| uint64_t offset = |
| draw->indirectOffset + |
| offsetof(D3D11_DRAW_INSTANCED_INDIRECT_ARGS, StartVertexLocation); |
| DAWN_TRY(Buffer::Copy(commandContext, indirectBuffer, offset, |
| sizeof(uint32_t) * 2, commandContext->GetUniformBuffer(), |
| 0)); |
| } |
| |
| commandContext->GetD3D11DeviceContext4()->DrawInstancedIndirect( |
| indirectBuffer->GetD3D11NonConstantBuffer(), draw->indirectOffset); |
| |
| break; |
| } |
| |
| case Command::DrawIndexedIndirect: { |
| DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>(); |
| |
| Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get()); |
| DAWN_ASSERT(indirectBuffer != nullptr); |
| |
| DAWN_TRY(bindGroupTracker.Apply()); |
| vertexBufferTracker.Apply(lastPipeline); |
| |
| if (lastPipeline->UsesVertexIndex() || lastPipeline->UsesInstanceIndex()) { |
| // Copy StartVertexLocation and StartInstanceLocation into the uniform buffer |
| // for built-in variables. |
| uint64_t offset = |
| draw->indirectOffset + |
| offsetof(D3D11_DRAW_INDEXED_INSTANCED_INDIRECT_ARGS, BaseVertexLocation); |
| DAWN_TRY(Buffer::Copy(commandContext, indirectBuffer, offset, |
| sizeof(uint32_t) * 2, commandContext->GetUniformBuffer(), |
| 0)); |
| } |
| |
| commandContext->GetD3D11DeviceContext4()->DrawIndexedInstancedIndirect( |
| indirectBuffer->GetD3D11NonConstantBuffer(), draw->indirectOffset); |
| |
| break; |
| } |
| |
| case Command::SetRenderPipeline: { |
| SetRenderPipelineCmd* cmd = iter->NextCommand<SetRenderPipelineCmd>(); |
| |
| lastPipeline = ToBackend(cmd->pipeline.Get()); |
| lastPipeline->ApplyNow(commandContext, blendColor, stencilReference); |
| bindGroupTracker.OnSetPipeline(lastPipeline); |
| |
| break; |
| } |
| |
| case Command::SetBindGroup: { |
| SetBindGroupCmd* cmd = iter->NextCommand<SetBindGroupCmd>(); |
| |
| uint32_t* dynamicOffsets = nullptr; |
| if (cmd->dynamicOffsetCount > 0) { |
| dynamicOffsets = iter->NextData<uint32_t>(cmd->dynamicOffsetCount); |
| } |
| bindGroupTracker.OnSetBindGroup(cmd->index, cmd->group.Get(), |
| cmd->dynamicOffsetCount, dynamicOffsets); |
| |
| break; |
| } |
| |
| case Command::SetIndexBuffer: { |
| SetIndexBufferCmd* cmd = iter->NextCommand<SetIndexBufferCmd>(); |
| |
| UINT indexBufferBaseOffset = cmd->offset; |
| DXGI_FORMAT indexBufferFormat = DXGIIndexFormat(cmd->format); |
| |
| commandContext->GetD3D11DeviceContext4()->IASetIndexBuffer( |
| ToBackend(cmd->buffer)->GetD3D11NonConstantBuffer(), indexBufferFormat, |
| indexBufferBaseOffset); |
| |
| break; |
| } |
| |
| case Command::SetVertexBuffer: { |
| SetVertexBufferCmd* cmd = iter->NextCommand<SetVertexBufferCmd>(); |
| ID3D11Buffer* buffer = ToBackend(cmd->buffer)->GetD3D11NonConstantBuffer(); |
| vertexBufferTracker.OnSetVertexBuffer(cmd->slot, buffer, cmd->offset); |
| break; |
| } |
| |
| case Command::InsertDebugMarker: |
| case Command::PopDebugGroup: |
| case Command::PushDebugGroup: { |
| HandleDebugCommands(commandContext, iter, type); |
| break; |
| } |
| |
| default: |
| DAWN_UNREACHABLE(); |
| break; |
| } |
| |
| return {}; |
| }; |
| |
| Command type; |
| while (mCommands.NextCommandId(&type)) { |
| switch (type) { |
| case Command::EndRenderPass: { |
| mCommands.NextCommand<EndRenderPassCmd>(); |
| d3d11DeviceContext->OMSetRenderTargets(0, nullptr, nullptr); |
| |
| if (renderPass->attachmentState->GetSampleCount() <= 1) { |
| return {}; |
| } |
| |
| // Resolve multisampled textures. |
| for (auto i : |
| IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { |
| const auto& attachment = renderPass->colorAttachments[i]; |
| if (!attachment.resolveTarget.Get()) { |
| continue; |
| } |
| |
| DAWN_ASSERT(attachment.view->GetAspects() == Aspect::Color); |
| DAWN_ASSERT(attachment.resolveTarget->GetAspects() == Aspect::Color); |
| |
| Texture* resolveTexture = ToBackend(attachment.resolveTarget->GetTexture()); |
| Texture* colorTexture = ToBackend(attachment.view->GetTexture()); |
| uint32_t dstSubresource = resolveTexture->GetSubresourceIndex( |
| attachment.resolveTarget->GetBaseMipLevel(), |
| attachment.resolveTarget->GetBaseArrayLayer(), Aspect::Color); |
| uint32_t srcSubresource = colorTexture->GetSubresourceIndex( |
| attachment.view->GetBaseMipLevel(), attachment.view->GetBaseArrayLayer(), |
| Aspect::Color); |
| d3d11DeviceContext->ResolveSubresource( |
| resolveTexture->GetD3D11Resource(), dstSubresource, |
| colorTexture->GetD3D11Resource(), srcSubresource, |
| d3d::DXGITextureFormat(attachment.resolveTarget->GetFormat().format)); |
| } |
| |
| return {}; |
| } |
| |
| case Command::SetStencilReference: { |
| SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>(); |
| stencilReference = cmd->reference; |
| if (lastPipeline) { |
| lastPipeline->ApplyDepthStencilState(commandContext, stencilReference); |
| } |
| break; |
| } |
| |
| case Command::SetViewport: { |
| SetViewportCmd* cmd = mCommands.NextCommand<SetViewportCmd>(); |
| |
| D3D11_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; |
| commandContext->GetD3D11DeviceContext4()->RSSetViewports(1, &viewport); |
| break; |
| } |
| |
| case Command::SetScissorRect: { |
| SetScissorRectCmd* cmd = mCommands.NextCommand<SetScissorRectCmd>(); |
| |
| D3D11_RECT scissorRect = {static_cast<LONG>(cmd->x), static_cast<LONG>(cmd->y), |
| static_cast<LONG>(cmd->x + cmd->width), |
| static_cast<LONG>(cmd->y + cmd->height)}; |
| commandContext->GetD3D11DeviceContext4()->RSSetScissorRects(1, &scissorRect); |
| break; |
| } |
| |
| case Command::SetBlendConstant: { |
| SetBlendConstantCmd* cmd = mCommands.NextCommand<SetBlendConstantCmd>(); |
| blendColor = ConvertToFloatColor(cmd->color); |
| if (lastPipeline) { |
| lastPipeline->ApplyBlendState(commandContext, blendColor); |
| } |
| break; |
| } |
| |
| case Command::ExecuteBundles: { |
| ExecuteBundlesCmd* cmd = mCommands.NextCommand<ExecuteBundlesCmd>(); |
| auto bundles = mCommands.NextData<Ref<RenderBundleBase>>(cmd->count); |
| for (uint32_t i = 0; i < cmd->count; ++i) { |
| CommandIterator* iter = bundles[i]->GetCommands(); |
| iter->Reset(); |
| while (iter->NextCommandId(&type)) { |
| DAWN_TRY(DoRenderBundleCommand(iter, type)); |
| } |
| } |
| break; |
| } |
| |
| case Command::BeginOcclusionQuery: { |
| BeginOcclusionQueryCmd* cmd = mCommands.NextCommand<BeginOcclusionQueryCmd>(); |
| QuerySet* querySet = ToBackend(cmd->querySet.Get()); |
| querySet->BeginQuery(commandContext->GetD3D11DeviceContext4(), cmd->queryIndex); |
| break; |
| } |
| |
| case Command::EndOcclusionQuery: { |
| EndOcclusionQueryCmd* cmd = mCommands.NextCommand<EndOcclusionQueryCmd>(); |
| QuerySet* querySet = ToBackend(cmd->querySet.Get()); |
| querySet->EndQuery(commandContext->GetD3D11DeviceContext4(), cmd->queryIndex); |
| break; |
| } |
| |
| case Command::WriteTimestamp: |
| return DAWN_UNIMPLEMENTED_ERROR("WriteTimestamp unimplemented"); |
| |
| default: { |
| DAWN_TRY(DoRenderBundleCommand(&mCommands, type)); |
| } |
| } |
| } |
| |
| // EndRenderPass should have been called |
| DAWN_UNREACHABLE(); |
| } |
| |
| void CommandBuffer::HandleDebugCommands( |
| const ScopedSwapStateCommandRecordingContext* commandContext, |
| CommandIterator* iter, |
| Command command) { |
| switch (command) { |
| case Command::InsertDebugMarker: { |
| InsertDebugMarkerCmd* cmd = iter->NextCommand<InsertDebugMarkerCmd>(); |
| std::wstring label = UTF8ToWStr(iter->NextData<char>(cmd->length + 1)); |
| commandContext->GetD3DUserDefinedAnnotation()->SetMarker(label.c_str()); |
| break; |
| } |
| |
| case Command::PopDebugGroup: { |
| [[maybe_unused]] auto cmd = iter->NextCommand<PopDebugGroupCmd>(); |
| commandContext->GetD3DUserDefinedAnnotation()->EndEvent(); |
| break; |
| } |
| |
| case Command::PushDebugGroup: { |
| PushDebugGroupCmd* cmd = iter->NextCommand<PushDebugGroupCmd>(); |
| std::wstring label = UTF8ToWStr(iter->NextData<char>(cmd->length + 1)); |
| commandContext->GetD3DUserDefinedAnnotation()->BeginEvent(label.c_str()); |
| break; |
| } |
| default: |
| DAWN_UNREACHABLE(); |
| } |
| } |
| |
| MaybeError CommandBuffer::RecordFirstIndexOffset( |
| RenderPipeline* renderPipeline, |
| const ScopedSwapStateCommandRecordingContext* commandContext, |
| uint32_t firstVertex, |
| uint32_t firstInstance) { |
| constexpr uint32_t kFirstVertexOffset = 0; |
| constexpr uint32_t kFirstInstanceOffset = 1; |
| |
| if (renderPipeline->UsesVertexIndex()) { |
| commandContext->WriteUniformBuffer(kFirstVertexOffset, firstVertex); |
| } |
| if (renderPipeline->UsesInstanceIndex()) { |
| commandContext->WriteUniformBuffer(kFirstInstanceOffset, firstInstance); |
| } |
| |
| return commandContext->FlushUniformBuffer(); |
| } |
| |
| MaybeError CommandBuffer::RecordNumWorkgroupsForDispatch( |
| ComputePipeline* computePipeline, |
| const ScopedSwapStateCommandRecordingContext* commandContext, |
| DispatchCmd* dispatchCmd) { |
| if (!computePipeline->UsesNumWorkgroups()) { |
| // Workgroup size is not used in shader, so we don't need to update the uniform buffer. The |
| // original value in the uniform buffer will not be used, so we don't need to clear it. |
| return {}; |
| } |
| |
| commandContext->WriteUniformBuffer(/*offset=*/0, dispatchCmd->x); |
| commandContext->WriteUniformBuffer(/*offset=*/1, dispatchCmd->y); |
| commandContext->WriteUniformBuffer(/*offset=*/2, dispatchCmd->z); |
| return commandContext->FlushUniformBuffer(); |
| } |
| |
| } // namespace dawn::native::d3d11 |