// 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/Commands.h" #include "dawn_native/d3d12/BindGroupD3D12.h" #include "dawn_native/d3d12/BindGroupLayoutD3D12.h" #include "dawn_native/d3d12/BufferD3D12.h" #include "dawn_native/d3d12/ComputePipelineD3D12.h" #include "dawn_native/d3d12/DescriptorHeapAllocator.h" #include "dawn_native/d3d12/DeviceD3D12.h" #include "dawn_native/d3d12/InputStateD3D12.h" #include "dawn_native/d3d12/PipelineLayoutD3D12.h" #include "dawn_native/d3d12/RenderPassDescriptorD3D12.h" #include "dawn_native/d3d12/RenderPipelineD3D12.h" #include "dawn_native/d3d12/ResourceAllocator.h" #include "dawn_native/d3d12/SamplerD3D12.h" #include "dawn_native/d3d12/TextureCopySplitter.h" #include "dawn_native/d3d12/TextureD3D12.h" namespace dawn_native { namespace d3d12 { namespace { DXGI_FORMAT DXGIIndexFormat(dawn::IndexFormat format) { switch (format) { case dawn::IndexFormat::Uint16: return DXGI_FORMAT_R16_UINT; case dawn::IndexFormat::Uint32: return DXGI_FORMAT_R32_UINT; default: UNREACHABLE(); } } } // anonymous namespace struct BindGroupStateTracker { uint32_t cbvSrvUavDescriptorIndex = 0; uint32_t samplerDescriptorIndex = 0; DescriptorHeapHandle cbvSrvUavCPUDescriptorHeap = {}; DescriptorHeapHandle samplerCPUDescriptorHeap = {}; DescriptorHeapHandle cbvSrvUavGPUDescriptorHeap = {}; DescriptorHeapHandle samplerGPUDescriptorHeap = {}; std::array bindGroups = {}; bool inCompute = false; Device* device; BindGroupStateTracker(Device* device) : device(device) { } void SetInComputePass(bool inCompute_) { inCompute = inCompute_; } void TrackSetBindGroup(BindGroup* group, uint32_t index, uint32_t indexInSubmit) { if (bindGroups[index] != group) { bindGroups[index] = group; // Descriptors don't need to be recorded if they have already been recorded in // the heap. Indices are only updated when descriptors are recorded const uint64_t serial = device->GetPendingCommandSerial(); if (group->GetHeapSerial() != serial || group->GetIndexInSubmit() != indexInSubmit) { group->RecordDescriptors(cbvSrvUavCPUDescriptorHeap, &cbvSrvUavDescriptorIndex, samplerCPUDescriptorHeap, &samplerDescriptorIndex, serial, indexInSubmit); } } } void TrackInheritedGroups(PipelineLayout* oldLayout, PipelineLayout* newLayout, uint32_t indexInSubmit) { if (oldLayout == nullptr) { return; } uint32_t inheritUntil = oldLayout->GroupsInheritUpTo(newLayout); for (uint32_t i = 0; i < inheritUntil; ++i) { TrackSetBindGroup(bindGroups[i], i, indexInSubmit); } } void SetBindGroup(ComPtr commandList, PipelineLayout* pipelineLayout, BindGroup* group, uint32_t index, bool force = false) { if (bindGroups[index] != group || force) { bindGroups[index] = group; uint32_t cbvUavSrvCount = ToBackend(group->GetLayout())->GetCbvUavSrvDescriptorCount(); uint32_t samplerCount = ToBackend(group->GetLayout())->GetSamplerDescriptorCount(); if (cbvUavSrvCount > 0) { uint32_t parameterIndex = pipelineLayout->GetCbvUavSrvRootParameterIndex(index); if (inCompute) { commandList->SetComputeRootDescriptorTable( parameterIndex, cbvSrvUavGPUDescriptorHeap.GetGPUHandle( group->GetCbvUavSrvHeapOffset())); } else { commandList->SetGraphicsRootDescriptorTable( parameterIndex, cbvSrvUavGPUDescriptorHeap.GetGPUHandle( group->GetCbvUavSrvHeapOffset())); } } if (samplerCount > 0) { uint32_t parameterIndex = pipelineLayout->GetSamplerRootParameterIndex(index); if (inCompute) { commandList->SetComputeRootDescriptorTable( parameterIndex, samplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset())); } else { commandList->SetGraphicsRootDescriptorTable( parameterIndex, samplerGPUDescriptorHeap.GetGPUHandle(group->GetSamplerHeapOffset())); } } } } void SetInheritedBindGroups(ComPtr commandList, PipelineLayout* oldLayout, PipelineLayout* newLayout) { if (oldLayout == nullptr) { return; } uint32_t inheritUntil = oldLayout->GroupsInheritUpTo(newLayout); for (uint32_t i = 0; i < inheritUntil; ++i) { SetBindGroup(commandList, newLayout, bindGroups[i], i, true); } } void Reset() { for (uint32_t i = 0; i < kMaxBindGroups; ++i) { bindGroups[i] = nullptr; } } }; namespace { void AllocateAndSetDescriptorHeaps(Device* device, BindGroupStateTracker* bindingTracker, CommandIterator* commands, int indexInSubmit) { auto* descriptorHeapAllocator = device->GetDescriptorHeapAllocator(); // TODO(enga@google.com): This currently allocates CPU heaps of arbitrarily chosen sizes // This will not work if there are too many descriptors bindingTracker->cbvSrvUavCPUDescriptorHeap = descriptorHeapAllocator->AllocateCPUHeap( D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 8192); bindingTracker->samplerCPUDescriptorHeap = descriptorHeapAllocator->AllocateCPUHeap(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 2048); { Command type; PipelineLayout* lastLayout = nullptr; while (commands->NextCommandId(&type)) { switch (type) { case Command::SetComputePipeline: { SetComputePipelineCmd* cmd = commands->NextCommand(); PipelineLayout* layout = ToBackend(cmd->pipeline->GetLayout()); bindingTracker->TrackInheritedGroups(lastLayout, layout, indexInSubmit); lastLayout = layout; } break; case Command::SetRenderPipeline: { SetRenderPipelineCmd* cmd = commands->NextCommand(); PipelineLayout* layout = ToBackend(cmd->pipeline->GetLayout()); bindingTracker->TrackInheritedGroups(lastLayout, layout, indexInSubmit); lastLayout = layout; } break; case Command::SetBindGroup: { SetBindGroupCmd* cmd = commands->NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); bindingTracker->TrackSetBindGroup(group, cmd->index, indexInSubmit); } break; default: SkipCommand(commands, type); } } commands->Reset(); } if (bindingTracker->cbvSrvUavDescriptorIndex > 0) { // Allocate a GPU-visible heap and copy from the CPU-only heap to the GPU-visible // heap bindingTracker->cbvSrvUavGPUDescriptorHeap = descriptorHeapAllocator->AllocateGPUHeap( D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, bindingTracker->cbvSrvUavDescriptorIndex); device->GetD3D12Device()->CopyDescriptorsSimple( bindingTracker->cbvSrvUavDescriptorIndex, bindingTracker->cbvSrvUavGPUDescriptorHeap.GetCPUHandle(0), bindingTracker->cbvSrvUavCPUDescriptorHeap.GetCPUHandle(0), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); } if (bindingTracker->samplerDescriptorIndex > 0) { bindingTracker->samplerGPUDescriptorHeap = descriptorHeapAllocator->AllocateGPUHeap( D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, bindingTracker->samplerDescriptorIndex); device->GetD3D12Device()->CopyDescriptorsSimple( bindingTracker->samplerDescriptorIndex, bindingTracker->samplerGPUDescriptorHeap.GetCPUHandle(0), bindingTracker->samplerCPUDescriptorHeap.GetCPUHandle(0), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); } } } // anonymous namespace CommandBuffer::CommandBuffer(CommandBufferBuilder* builder) : CommandBufferBase(builder), mCommands(builder->AcquireCommands()) { } CommandBuffer::~CommandBuffer() { FreeCommands(&mCommands); } void CommandBuffer::RecordCommands(ComPtr commandList, uint32_t indexInSubmit) { Device* device = ToBackend(GetDevice()); BindGroupStateTracker bindingTracker(device); // Precompute the allocation of bindgroups in descriptor heaps // TODO(cwallez@chromium.org): Iterating over all the commands here is inefficient. We // should have a system where commands and descriptors are recorded in parallel then the // heaps set using a small CommandList inserted just before the main CommandList. { AllocateAndSetDescriptorHeaps(device, &bindingTracker, &mCommands, indexInSubmit); bindingTracker.Reset(); ID3D12DescriptorHeap* descriptorHeaps[2] = { bindingTracker.cbvSrvUavGPUDescriptorHeap.Get(), bindingTracker.samplerGPUDescriptorHeap.Get()}; if (descriptorHeaps[0] && descriptorHeaps[1]) { commandList->SetDescriptorHeaps(2, descriptorHeaps); } else if (descriptorHeaps[0]) { commandList->SetDescriptorHeaps(1, descriptorHeaps); } else if (descriptorHeaps[1]) { commandList->SetDescriptorHeaps(1, &descriptorHeaps[1]); } } // Records the necessary barriers for the resource usage pre-computed by the frontend auto TransitionForPass = [](ComPtr commandList, const PassResourceUsage& usages) { for (size_t i = 0; i < usages.buffers.size(); ++i) { Buffer* buffer = ToBackend(usages.buffers[i]); buffer->TransitionUsageNow(commandList, usages.bufferUsages[i]); } for (size_t i = 0; i < usages.textures.size(); ++i) { Texture* texture = ToBackend(usages.textures[i]); texture->TransitionUsageNow(commandList, usages.textureUsages[i]); } }; const std::vector& passResourceUsages = GetResourceUsages().perPass; uint32_t nextPassNumber = 0; Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::BeginComputePass: { mCommands.NextCommand(); TransitionForPass(commandList, passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(true); RecordComputePass(commandList, &bindingTracker); nextPassNumber++; } break; case Command::BeginRenderPass: { BeginRenderPassCmd* beginRenderPassCmd = mCommands.NextCommand(); TransitionForPass(commandList, passResourceUsages[nextPassNumber]); bindingTracker.SetInComputePass(false); RecordRenderPass(commandList, &bindingTracker, ToBackend(beginRenderPassCmd->info.Get())); nextPassNumber++; } break; case Command::CopyBufferToBuffer: { CopyBufferToBufferCmd* copy = mCommands.NextCommand(); Buffer* srcBuffer = ToBackend(copy->source.buffer.Get()); Buffer* dstBuffer = ToBackend(copy->destination.buffer.Get()); srcBuffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferSrc); dstBuffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferDst); commandList->CopyBufferRegion( dstBuffer->GetD3D12Resource().Get(), copy->destination.offset, srcBuffer->GetD3D12Resource().Get(), copy->source.offset, copy->size); } break; case Command::CopyBufferToTexture: { CopyBufferToTextureCmd* copy = mCommands.NextCommand(); Buffer* buffer = ToBackend(copy->source.buffer.Get()); Texture* texture = ToBackend(copy->destination.texture.Get()); buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferSrc); texture->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferDst); auto copySplit = ComputeTextureCopySplit( copy->destination.origin, copy->copySize, static_cast(TextureFormatPixelSize(texture->GetFormat())), copy->source.offset, copy->source.rowPitch, copy->source.imageHeight); D3D12_TEXTURE_COPY_LOCATION textureLocation; textureLocation.pResource = texture->GetD3D12Resource(); textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; textureLocation.SubresourceIndex = texture->GetNumMipLevels() * copy->destination.slice + copy->destination.level; for (uint32_t i = 0; i < copySplit.count; ++i) { auto& info = copySplit.copies[i]; D3D12_TEXTURE_COPY_LOCATION bufferLocation; bufferLocation.pResource = buffer->GetD3D12Resource().Get(); bufferLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; bufferLocation.PlacedFootprint.Offset = copySplit.offset; bufferLocation.PlacedFootprint.Footprint.Format = texture->GetD3D12Format(); bufferLocation.PlacedFootprint.Footprint.Width = info.bufferSize.width; bufferLocation.PlacedFootprint.Footprint.Height = info.bufferSize.height; bufferLocation.PlacedFootprint.Footprint.Depth = info.bufferSize.depth; bufferLocation.PlacedFootprint.Footprint.RowPitch = copy->source.rowPitch; D3D12_BOX sourceRegion; sourceRegion.left = info.bufferOffset.x; sourceRegion.top = info.bufferOffset.y; sourceRegion.front = info.bufferOffset.z; sourceRegion.right = info.bufferOffset.x + info.copySize.width; sourceRegion.bottom = info.bufferOffset.y + info.copySize.height; sourceRegion.back = info.bufferOffset.z + info.copySize.depth; 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->TransitionUsageNow(commandList, dawn::TextureUsageBit::TransferSrc); buffer->TransitionUsageNow(commandList, dawn::BufferUsageBit::TransferDst); auto copySplit = ComputeTextureCopySplit( copy->source.origin, copy->copySize, static_cast(TextureFormatPixelSize(texture->GetFormat())), copy->destination.offset, copy->destination.rowPitch, copy->destination.imageHeight); D3D12_TEXTURE_COPY_LOCATION textureLocation; textureLocation.pResource = texture->GetD3D12Resource(); textureLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; textureLocation.SubresourceIndex = texture->GetNumMipLevels() * copy->source.slice + copy->source.level; for (uint32_t i = 0; i < copySplit.count; ++i) { auto& info = copySplit.copies[i]; D3D12_TEXTURE_COPY_LOCATION bufferLocation; bufferLocation.pResource = buffer->GetD3D12Resource().Get(); bufferLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; bufferLocation.PlacedFootprint.Offset = copySplit.offset; bufferLocation.PlacedFootprint.Footprint.Format = texture->GetD3D12Format(); bufferLocation.PlacedFootprint.Footprint.Width = info.bufferSize.width; bufferLocation.PlacedFootprint.Footprint.Height = info.bufferSize.height; bufferLocation.PlacedFootprint.Footprint.Depth = info.bufferSize.depth; bufferLocation.PlacedFootprint.Footprint.RowPitch = copy->destination.rowPitch; D3D12_BOX sourceRegion; sourceRegion.left = info.textureOffset.x; sourceRegion.top = info.textureOffset.y; sourceRegion.front = info.textureOffset.z; sourceRegion.right = info.textureOffset.x + info.copySize.width; sourceRegion.bottom = info.textureOffset.y + info.copySize.height; sourceRegion.back = info.textureOffset.z + info.copySize.depth; commandList->CopyTextureRegion(&bufferLocation, info.bufferOffset.x, info.bufferOffset.y, info.bufferOffset.z, &textureLocation, &sourceRegion); } } break; default: { UNREACHABLE(); } break; } } } void CommandBuffer::FlushSetVertexBuffers(ComPtr commandList, VertexBuffersInfo* vertexBuffersInfo, const InputState* inputState) { DAWN_ASSERT(vertexBuffersInfo != nullptr); DAWN_ASSERT(inputState != nullptr); auto inputsMask = inputState->GetInputsSetMask(); uint32_t startSlot = vertexBuffersInfo->startSlot; uint32_t endSlot = vertexBuffersInfo->endSlot; // If the input 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 (vertexBuffersInfo->lastInputState != inputState) { vertexBuffersInfo->lastInputState = inputState; for (uint32_t slot : IterateBitSet(inputsMask)) { startSlot = std::min(startSlot, slot); endSlot = std::max(endSlot, slot + 1); vertexBuffersInfo->d3d12BufferViews[slot].StrideInBytes = inputState->GetInput(slot).stride; } } if (endSlot <= startSlot) { return; } // d3d12BufferViews is kept up to date with the most recent data passed // to SetVertexBuffers. This makes it correct to only track the start // and end of the dirty range. When FlushSetVertexBuffers is called, // we will at worst set non-dirty vertex buffers in duplicate. uint32_t count = endSlot - startSlot; commandList->IASetVertexBuffers(startSlot, count, &vertexBuffersInfo->d3d12BufferViews[startSlot]); vertexBuffersInfo->startSlot = kMaxVertexInputs; vertexBuffersInfo->endSlot = 0; } void CommandBuffer::RecordComputePass(ComPtr commandList, BindGroupStateTracker* bindingTracker) { PipelineLayout* lastLayout = nullptr; Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::Dispatch: { DispatchCmd* dispatch = mCommands.NextCommand(); commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z); } break; case Command::EndComputePass: { mCommands.NextCommand(); return; } break; 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->SetInheritedBindGroups(commandList, lastLayout, layout); lastLayout = layout; } break; case Command::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index); } break; default: { UNREACHABLE(); } break; } } } void CommandBuffer::RecordRenderPass(ComPtr commandList, BindGroupStateTracker* bindingTracker, RenderPassDescriptor* renderPass) { // Clear framebuffer attachments as needed and transition to render target { for (uint32_t i : IterateBitSet(renderPass->GetColorAttachmentMask())) { auto& attachmentInfo = renderPass->GetColorAttachment(i); // Load op - color if (attachmentInfo.loadOp == dawn::LoadOp::Clear) { D3D12_CPU_DESCRIPTOR_HANDLE handle = renderPass->GetRTVDescriptor(i); commandList->ClearRenderTargetView(handle, attachmentInfo.clearColor.data(), 0, nullptr); } } if (renderPass->HasDepthStencilAttachment()) { auto& attachmentInfo = renderPass->GetDepthStencilAttachment(); Texture* texture = ToBackend(attachmentInfo.view->GetTexture()); // Load op - depth/stencil bool doDepthClear = TextureFormatHasDepth(texture->GetFormat()) && (attachmentInfo.depthLoadOp == dawn::LoadOp::Clear); bool doStencilClear = TextureFormatHasStencil(texture->GetFormat()) && (attachmentInfo.stencilLoadOp == dawn::LoadOp::Clear); D3D12_CLEAR_FLAGS clearFlags = {}; if (doDepthClear) { clearFlags |= D3D12_CLEAR_FLAG_DEPTH; } if (doStencilClear) { clearFlags |= D3D12_CLEAR_FLAG_STENCIL; } if (clearFlags) { auto handle = renderPass->GetDSVDescriptor(); // TODO(kainino@chromium.org): investigate: should the Dawn clear // stencil type be uint8_t? uint8_t clearStencil = static_cast(attachmentInfo.clearStencil); commandList->ClearDepthStencilView( handle, clearFlags, attachmentInfo.clearDepth, clearStencil, 0, nullptr); } } } // Set up render targets { RenderPassDescriptor::OMSetRenderTargetArgs args = renderPass->GetSubpassOMSetRenderTargetArgs(); if (args.dsv.ptr) { commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, &args.dsv); } else { commandList->OMSetRenderTargets(args.numRTVs, args.RTVs.data(), FALSE, nullptr); } } // Set up default dynamic state { uint32_t width = renderPass->GetWidth(); uint32_t height = renderPass->GetHeight(); 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; InputState* lastInputState = nullptr; VertexBuffersInfo vertexBuffersInfo = {}; Command type; while (mCommands.NextCommandId(&type)) { switch (type) { case Command::EndRenderPass: { mCommands.NextCommand(); return; } break; case Command::Draw: { DrawCmd* draw = mCommands.NextCommand(); FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastInputState); commandList->DrawInstanced(draw->vertexCount, draw->instanceCount, draw->firstVertex, draw->firstInstance); } break; case Command::DrawIndexed: { DrawIndexedCmd* draw = mCommands.NextCommand(); FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastInputState); commandList->DrawIndexedInstanced(draw->indexCount, draw->instanceCount, draw->firstIndex, draw->baseVertex, draw->firstInstance); } break; case Command::SetRenderPipeline: { SetRenderPipelineCmd* cmd = mCommands.NextCommand(); RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get(); PipelineLayout* layout = ToBackend(pipeline->GetLayout()); InputState* inputState = ToBackend(pipeline->GetInputState()); commandList->SetGraphicsRootSignature(layout->GetRootSignature().Get()); commandList->SetPipelineState(pipeline->GetPipelineState().Get()); commandList->IASetPrimitiveTopology(pipeline->GetD3D12PrimitiveTopology()); bindingTracker->SetInheritedBindGroups(commandList, lastLayout, layout); lastPipeline = pipeline; lastLayout = layout; lastInputState = inputState; } break; case Command::SetStencilReference: { SetStencilReferenceCmd* cmd = mCommands.NextCommand(); commandList->OMSetStencilRef(cmd->reference); } 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::SetBindGroup: { SetBindGroupCmd* cmd = mCommands.NextCommand(); BindGroup* group = ToBackend(cmd->group.Get()); bindingTracker->SetBindGroup(commandList, lastLayout, group, cmd->index); } break; case Command::SetIndexBuffer: { SetIndexBufferCmd* cmd = mCommands.NextCommand(); Buffer* buffer = ToBackend(cmd->buffer.Get()); D3D12_INDEX_BUFFER_VIEW bufferView; bufferView.BufferLocation = buffer->GetVA() + cmd->offset; bufferView.SizeInBytes = buffer->GetSize() - cmd->offset; // TODO(cwallez@chromium.org): Make index buffers lazily applied, right now // this will break if the pipeline is changed for one with a different index // format after SetIndexBuffer bufferView.Format = DXGIIndexFormat(lastPipeline->GetIndexFormat()); commandList->IASetIndexBuffer(&bufferView); } break; case Command::SetVertexBuffers: { SetVertexBuffersCmd* cmd = mCommands.NextCommand(); auto buffers = mCommands.NextData>(cmd->count); auto offsets = mCommands.NextData(cmd->count); vertexBuffersInfo.startSlot = std::min(vertexBuffersInfo.startSlot, cmd->startSlot); vertexBuffersInfo.endSlot = std::max(vertexBuffersInfo.endSlot, cmd->startSlot + cmd->count); for (uint32_t i = 0; i < cmd->count; ++i) { Buffer* buffer = ToBackend(buffers[i].Get()); auto* d3d12BufferView = &vertexBuffersInfo.d3d12BufferViews[cmd->startSlot + i]; d3d12BufferView->BufferLocation = buffer->GetVA() + offsets[i]; d3d12BufferView->SizeInBytes = buffer->GetSize() - offsets[i]; // The bufferView stride is set based on the input state before a draw. } } break; default: { UNREACHABLE(); } break; } } } }} // namespace dawn_native::d3d12