| // Copyright 2023 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/d3d11/CommandBufferD3D11.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <string> |
| #include <vector> |
| |
| #include "dawn/common/WindowsUtils.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/VertexFormat.h" |
| #include "dawn/native/d3d/D3DError.h" |
| #include "dawn/native/d3d11/BindGroupTrackerD3D11.h" |
| #include "dawn/native/d3d11/BufferD3D11.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" |
| |
| 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: |
| UNREACHABLE(); |
| } |
| } |
| |
| class VertexBufferTracker { |
| public: |
| explicit VertexBufferTracker(CommandRecordingContext* commandContext) |
| : mCommandContext(commandContext) {} |
| |
| ~VertexBufferTracker() { |
| mD3D11Buffers = {}; |
| mStrides = {}; |
| mOffsets = {}; |
| mCommandContext->GetD3D11DeviceContext()->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) { |
| 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->GetVertexBufferSlotsUsed())) { |
| mStrides[slot] = renderPipeline->GetVertexBuffer(slot).arrayStride; |
| } |
| } |
| |
| mCommandContext->GetD3D11DeviceContext()->IASetVertexBuffers( |
| 0, kMaxVertexBuffers, mD3D11Buffers.data(), mStrides.data(), mOffsets.data()); |
| } |
| |
| private: |
| CommandRecordingContext* const mCommandContext; |
| const RenderPipeline* mLastAppliedRenderPipeline = nullptr; |
| ityp::array<VertexBufferSlot, ID3D11Buffer*, kMaxVertexBuffers> mD3D11Buffers = {}; |
| ityp::array<VertexBufferSlot, UINT, kMaxVertexBuffers> mStrides = {}; |
| ityp::array<VertexBufferSlot, UINT, kMaxVertexBuffers> mOffsets = {}; |
| }; |
| |
| } // namespace |
| |
| // Create CommandBuffer |
| Ref<CommandBuffer> CommandBuffer::Create(CommandEncoder* encoder, |
| const CommandBufferDescriptor* descriptor) { |
| return AcquireRef(new CommandBuffer(encoder, descriptor)); |
| } |
| |
| MaybeError CommandBuffer::Execute() { |
| CommandRecordingContext* commandContext = ToBackend(GetDevice())->GetPendingCommandContext(); |
| |
| 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. 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. |
| DAWN_TRY(scope.textureUsages[i].Iterate([&](const SubresourceRange& range, |
| wgpu::TextureUsage usage) -> MaybeError { |
| if (usage & ~wgpu::TextureUsage::RenderAttachment) { |
| 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 (const SyncScopeResourceUsage& scope : |
| GetResourceUsages().computePasses[nextComputePassNumber].dispatchUsages) { |
| DAWN_TRY(LazyClearSyncScope(scope)); |
| } |
| DAWN_TRY(ExecuteComputePass(commandContext)); |
| |
| nextComputePassNumber++; |
| break; |
| } |
| |
| case Command::BeginRenderPass: { |
| auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>(); |
| 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, GetDevice()->CreateBuffer(&desc)); |
| |
| 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(buffer)); |
| DAWN_TRY(buffer->EnsureDataInitialized(commandContext)); |
| |
| Texture* texture = ToBackend(dst.texture.Get()); |
| 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->EnsureSubresourceContentInitialized(commandContext, subresources)); |
| |
| Buffer* buffer = ToBackend(dst.buffer.Get()); |
| Buffer::ScopedMap scopedDstMap; |
| DAWN_TRY_ASSIGN(scopedDstMap, Buffer::ScopedMap::Create(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(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(CommandRecordingContext* 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->GetD3D11DeviceContext()->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->GetD3D11DeviceContext()->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: |
| UNREACHABLE(); |
| } |
| } |
| |
| // EndComputePass should have been called |
| UNREACHABLE(); |
| } |
| |
| MaybeError CommandBuffer::ExecuteRenderPass(BeginRenderPassCmd* renderPass, |
| CommandRecordingContext* commandContext) { |
| ID3D11DeviceContext1* d3d11DeviceContext1 = commandContext->GetD3D11DeviceContext1(); |
| |
| // Hold ID3D11RenderTargetView ComPtr to make attachments alive. |
| ityp::array<ColorAttachmentIndex, ComPtr<ID3D11RenderTargetView>, kMaxColorAttachments> |
| d3d11RenderTargetViews = {}; |
| ityp::array<ColorAttachmentIndex, ID3D11RenderTargetView*, kMaxColorAttachments> |
| d3d11RenderTargetViewPtrs = {}; |
| ColorAttachmentIndex attachmentCount(uint8_t(0)); |
| // TODO(dawn:1815): Shrink the sparse attachments to accommodate more UAVs. |
| for (ColorAttachmentIndex i : |
| IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { |
| TextureView* colorTextureView = ToBackend(renderPass->colorAttachments[i].view.Get()); |
| DAWN_TRY_ASSIGN(d3d11RenderTargetViews[i], colorTextureView->CreateD3D11RenderTargetView( |
| colorTextureView->GetBaseMipLevel())); |
| d3d11RenderTargetViewPtrs[i] = d3d11RenderTargetViews[i].Get(); |
| if (renderPass->colorAttachments[i].loadOp == wgpu::LoadOp::Clear) { |
| std::array<float, 4> clearColor = |
| ConvertToFloatColor(renderPass->colorAttachments[i].clearColor); |
| d3d11DeviceContext1->ClearRenderTargetView(d3d11RenderTargetViews[i].Get(), |
| clearColor.data()); |
| } |
| attachmentCount = i; |
| attachmentCount++; |
| } |
| |
| ComPtr<ID3D11DepthStencilView> d3d11DepthStencilView; |
| 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->CreateD3D11DepthStencilView( |
| attachmentInfo->depthReadOnly, attachmentInfo->stencilReadOnly, |
| depthStencilTextureView->GetBaseMipLevel())); |
| 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; |
| } |
| |
| d3d11DeviceContext1->ClearDepthStencilView(d3d11DepthStencilView.Get(), clearFlags, |
| attachmentInfo->clearDepth, |
| attachmentInfo->clearStencil); |
| } |
| |
| d3d11DeviceContext1->OMSetRenderTargets(static_cast<uint8_t>(attachmentCount), |
| d3d11RenderTargetViewPtrs.data(), |
| d3d11DepthStencilView.Get()); |
| |
| // 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; |
| d3d11DeviceContext1->RSSetViewports(1, &defautViewport); |
| |
| // Set scissor |
| D3D11_RECT scissor; |
| scissor.left = 0; |
| scissor.top = 0; |
| scissor.right = renderPass->width; |
| scissor.bottom = renderPass->height; |
| d3d11DeviceContext1->RSSetScissorRects(1, &scissor); |
| |
| RenderPipeline* lastPipeline = nullptr; |
| BindGroupTracker bindGroupTracker(commandContext, /*isRenderPass=*/true); |
| 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->GetD3D11DeviceContext()->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->GetD3D11DeviceContext()->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()); |
| 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->GetD3D11DeviceContext()->DrawInstancedIndirect( |
| indirectBuffer->GetD3D11NonConstantBuffer(), draw->indirectOffset); |
| |
| break; |
| } |
| |
| case Command::DrawIndexedIndirect: { |
| DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>(); |
| |
| Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get()); |
| 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->GetD3D11DeviceContext()->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->GetD3D11DeviceContext()->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: |
| UNREACHABLE(); |
| break; |
| } |
| |
| return {}; |
| }; |
| |
| Command type; |
| while (mCommands.NextCommandId(&type)) { |
| switch (type) { |
| case Command::EndRenderPass: { |
| mCommands.NextCommand<EndRenderPassCmd>(); |
| ID3D11DeviceContext* d3d11DeviceContext = commandContext->GetD3D11DeviceContext(); |
| d3d11DeviceContext->OMSetRenderTargets(0, nullptr, nullptr); |
| |
| if (renderPass->attachmentState->GetSampleCount() <= 1) { |
| return {}; |
| } |
| |
| // Resolve multisampled textures. |
| for (ColorAttachmentIndex i : |
| IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) { |
| const auto& attachment = renderPass->colorAttachments[i]; |
| if (!attachment.resolveTarget.Get()) { |
| continue; |
| } |
| |
| ASSERT(attachment.view->GetAspects() == Aspect::Color); |
| 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->GetD3D11DeviceContext()->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->GetD3D11DeviceContext()->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->GetD3D11DeviceContext(), cmd->queryIndex); |
| break; |
| } |
| |
| case Command::EndOcclusionQuery: { |
| EndOcclusionQueryCmd* cmd = mCommands.NextCommand<EndOcclusionQueryCmd>(); |
| QuerySet* querySet = ToBackend(cmd->querySet.Get()); |
| querySet->EndQuery(commandContext->GetD3D11DeviceContext(), cmd->queryIndex); |
| break; |
| } |
| |
| case Command::WriteTimestamp: |
| return DAWN_UNIMPLEMENTED_ERROR("WriteTimestamp unimplemented"); |
| |
| default: { |
| DAWN_TRY(DoRenderBundleCommand(&mCommands, type)); |
| } |
| } |
| } |
| |
| // EndRenderPass should have been called |
| UNREACHABLE(); |
| } |
| |
| void CommandBuffer::HandleDebugCommands(CommandRecordingContext* 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: { |
| std::ignore = 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: |
| UNREACHABLE(); |
| } |
| } |
| |
| MaybeError CommandBuffer::RecordFirstIndexOffset(RenderPipeline* renderPipeline, |
| CommandRecordingContext* 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, |
| CommandRecordingContext* 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 |