d3d11: CommandBuffer: implement render pass

Bug: dawn:1705
Change-Id: I21c6a8550f098cb1e97d5f0c9a93da53600fa8a3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127700
Reviewed-by: Austin Eng <enga@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Peng Huang <penghuang@chromium.org>
Auto-Submit: Peng Huang <penghuang@chromium.org>
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.cpp b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
index 885638a..9fdd0a7 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.cpp
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.cpp
@@ -41,6 +41,17 @@
 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 BindGroupTracker : public BindGroupTrackerBase<false, uint64_t> {
   public:
     MaybeError Apply(CommandRecordingContext* commandContext) {
@@ -245,6 +256,7 @@
     };
 
     size_t nextComputePassNumber = 0;
+    size_t nextRenderPassNumber = 0;
 
     Command type;
     while (mCommands.NextCommandId(&type)) {
@@ -262,8 +274,14 @@
             }
 
             case Command::BeginRenderPass: {
-                [[maybe_unused]] auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
-                return DAWN_UNIMPLEMENTED_ERROR("Render pass not implemented");
+                auto* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
+                DAWN_TRY(
+                    LazyClearSyncScope(GetResourceUsages().renderPasses[nextRenderPassNumber]));
+                LazyClearRenderPassAttachments(cmd);
+                DAWN_TRY(ExecuteRenderPass(cmd, commandContext));
+
+                nextRenderPassNumber++;
+                break;
             }
 
             case Command::CopyBufferToBuffer: {
@@ -553,6 +571,285 @@
     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));
+    for (ColorAttachmentIndex i :
+         IterateBitSet(renderPass->attachmentState->GetColorAttachmentsMask())) {
+        TextureView* colorTextureView = ToBackend(renderPass->colorAttachments[i].view.Get());
+        DAWN_TRY_ASSIGN(d3d11RenderTargetViews[i], colorTextureView->CreateD3D11RenderTargetView());
+        d3d11RenderTargetViewPtrs[i] = d3d11RenderTargetViews[i].Get();
+        if (renderPass->colorAttachments[i].loadOp == wgpu::LoadOp::Clear) {
+            d3d11DeviceContext1->ClearRenderTargetView(
+                d3d11RenderTargetViews[i].Get(),
+                ConvertToFloatColor(renderPass->colorAttachments[i].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(false, false));
+        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 viewport;
+    viewport.TopLeftX = 0;
+    viewport.TopLeftY = 0;
+    viewport.Width = renderPass->width;
+    viewport.Height = renderPass->height;
+    viewport.MinDepth = 0.0f;
+    viewport.MaxDepth = 1.0f;
+    d3d11DeviceContext1->RSSetViewports(1, &viewport);
+
+    // 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 = {};
+    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(commandContext));
+                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(commandContext));
+                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: {
+                // TODO(dawn:1716): figure how to setup built-in variables for indirect draw.
+                DrawIndirectCmd* draw = iter->NextCommand<DrawIndirectCmd>();
+
+                DAWN_TRY(bindGroupTracker.Apply(commandContext));
+                uint64_t indirectBufferOffset = draw->indirectOffset;
+                Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
+                ASSERT(indirectBuffer != nullptr);
+
+                commandContext->GetD3D11DeviceContext()->DrawInstancedIndirect(
+                    indirectBuffer->GetD3D11Buffer(), indirectBufferOffset);
+
+                break;
+            }
+
+            case Command::DrawIndexedIndirect: {
+                // TODO(dawn:1716): figure how to setup built-in variables for indirect draw.
+                DrawIndexedIndirectCmd* draw = iter->NextCommand<DrawIndexedIndirectCmd>();
+
+                DAWN_TRY(bindGroupTracker.Apply(commandContext));
+
+                Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
+                ASSERT(indirectBuffer != nullptr);
+
+                commandContext->GetD3D11DeviceContext()->DrawIndexedInstancedIndirect(
+                    indirectBuffer->GetD3D11Buffer(), 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)->GetD3D11Buffer(), indexBufferFormat,
+                    indexBufferBaseOffset);
+
+                break;
+            }
+
+            case Command::SetVertexBuffer: {
+                SetVertexBufferCmd* cmd = iter->NextCommand<SetVertexBufferCmd>();
+                ASSERT(lastPipeline);
+                const VertexBufferInfo& info = lastPipeline->GetVertexBuffer(cmd->slot);
+
+                // TODO(dawn:1705): should we set vertex back to nullptr after the draw call?
+                UINT slot = static_cast<uint8_t>(cmd->slot);
+                ID3D11Buffer* buffer = ToBackend(cmd->buffer)->GetD3D11Buffer();
+                UINT arrayStride = info.arrayStride;
+                UINT offset = cmd->offset;
+                commandContext->GetD3D11DeviceContext()->IASetVertexBuffers(slot, 1, &buffer,
+                                                                            &arrayStride, &offset);
+
+                break;
+            }
+
+            case Command::InsertDebugMarker:
+            case Command::PopDebugGroup:
+            case Command::PushDebugGroup: {
+                HandleDebugCommands(commandContext, type);
+                break;
+            }
+
+            default:
+                UNREACHABLE();
+                break;
+        }
+
+        return {};
+    };
+
+    Command type;
+    while (mCommands.NextCommandId(&type)) {
+        switch (type) {
+            case Command::EndRenderPass: {
+                mCommands.NextCommand<EndRenderPassCmd>();
+                // TODO(dawn:1705): resolve MSAA
+                return {};
+            }
+
+            case Command::SetStencilReference: {
+                SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>();
+                stencilReference = cmd->reference;
+                return {};
+            }
+
+            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);
+                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: {
+                return DAWN_UNIMPLEMENTED_ERROR("BeginOcclusionQuery unimplemented.");
+            }
+
+            case Command::EndOcclusionQuery: {
+                return DAWN_UNIMPLEMENTED_ERROR("EndOcclusionQuery unimplemented.");
+            }
+
+            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, Command command) {
     switch (command) {
         case Command::InsertDebugMarker: {
@@ -579,6 +876,28 @@
     }
 }
 
+MaybeError CommandBuffer::RecordFirstIndexOffset(RenderPipeline* renderPipeline,
+                                                 CommandRecordingContext* commandContext,
+                                                 uint32_t firstVertex,
+                                                 uint32_t firstInstance) {
+    if (!renderPipeline->GetUsesVertexOrInstanceIndex()) {
+        // Vertex and instance index are 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 {};
+    }
+
+    // TODO(dawn:1705): only update the uniform buffer when the value changes.
+    uint32_t offsets[] = {
+        firstVertex,
+        firstInstance,
+    };
+    DAWN_TRY(
+        commandContext->GetUniformBuffer()->Write(commandContext, 0, offsets, sizeof(offsets)));
+
+    return {};
+}
+
 MaybeError CommandBuffer::RecordNumWorkgroupsForDispatch(ComputePipeline* computePipeline,
                                                          CommandRecordingContext* commandContext,
                                                          DispatchCmd* dispatchCmd) {
diff --git a/src/dawn/native/d3d11/CommandBufferD3D11.h b/src/dawn/native/d3d11/CommandBufferD3D11.h
index 4b56130..0fc370e 100644
--- a/src/dawn/native/d3d11/CommandBufferD3D11.h
+++ b/src/dawn/native/d3d11/CommandBufferD3D11.h
@@ -27,6 +27,7 @@
 
 class CommandRecordingContext;
 class ComputePipeline;
+class RenderPipeline;
 
 class CommandBuffer final : public CommandBufferBase {
   public:
@@ -38,8 +39,14 @@
     using CommandBufferBase::CommandBufferBase;
 
     MaybeError ExecuteComputePass(CommandRecordingContext* commandContext);
+    MaybeError ExecuteRenderPass(BeginRenderPassCmd* renderPass,
+                                 CommandRecordingContext* commandContext);
     void HandleDebugCommands(CommandRecordingContext* commandContext, Command command);
 
+    MaybeError RecordFirstIndexOffset(RenderPipeline* renderPipeline,
+                                      CommandRecordingContext* commandContext,
+                                      uint32_t firstVertex,
+                                      uint32_t firstInstance);
     MaybeError RecordNumWorkgroupsForDispatch(ComputePipeline* computePipeline,
                                               CommandRecordingContext* commandContext,
                                               DispatchCmd* dispatchCmd);