mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-06-11 09:03:28 +00:00
This patch removes RenderPassDescriptorVk completely and move the function RecordBeginRenderPass to CommandBufferVk.cpp, which is a part of preparation of removing RenderPassDescriptorBuilder from Dawn. BUG=dawn:6 Change-Id: Id666ef2f998fa65de93deb16afedb1d53d6b8bc0 Reviewed-on: https://dawn-review.googlesource.com/c/4540 Reviewed-by: Corentin Wallez <cwallez@chromium.org> Reviewed-by: Kai Ninomiya <kainino@chromium.org> Commit-Queue: Kai Ninomiya <kainino@chromium.org>
520 lines
23 KiB
C++
520 lines
23 KiB
C++
// 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/vulkan/CommandBufferVk.h"
|
|
|
|
#include "dawn_native/Commands.h"
|
|
#include "dawn_native/vulkan/BindGroupVk.h"
|
|
#include "dawn_native/vulkan/BufferVk.h"
|
|
#include "dawn_native/vulkan/ComputePipelineVk.h"
|
|
#include "dawn_native/vulkan/DeviceVk.h"
|
|
#include "dawn_native/vulkan/FencedDeleter.h"
|
|
#include "dawn_native/vulkan/PipelineLayoutVk.h"
|
|
#include "dawn_native/vulkan/RenderPassCache.h"
|
|
#include "dawn_native/vulkan/RenderPipelineVk.h"
|
|
#include "dawn_native/vulkan/TextureVk.h"
|
|
|
|
namespace dawn_native { namespace vulkan {
|
|
|
|
namespace {
|
|
|
|
VkIndexType VulkanIndexType(dawn::IndexFormat format) {
|
|
switch (format) {
|
|
case dawn::IndexFormat::Uint16:
|
|
return VK_INDEX_TYPE_UINT16;
|
|
case dawn::IndexFormat::Uint32:
|
|
return VK_INDEX_TYPE_UINT32;
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
VkBufferImageCopy ComputeBufferImageCopyRegion(const BufferCopy& bufferCopy,
|
|
const TextureCopy& textureCopy,
|
|
const Extent3D& copySize) {
|
|
const Texture* texture = ToBackend(textureCopy.texture.Get());
|
|
|
|
VkBufferImageCopy region;
|
|
|
|
region.bufferOffset = bufferCopy.offset;
|
|
// In Vulkan the row length is in texels while it is in bytes for Dawn
|
|
region.bufferRowLength =
|
|
bufferCopy.rowPitch / TextureFormatPixelSize(texture->GetFormat());
|
|
region.bufferImageHeight = bufferCopy.imageHeight;
|
|
|
|
region.imageSubresource.aspectMask = texture->GetVkAspectMask();
|
|
region.imageSubresource.mipLevel = textureCopy.level;
|
|
region.imageSubresource.baseArrayLayer = textureCopy.slice;
|
|
region.imageSubresource.layerCount = 1;
|
|
|
|
region.imageOffset.x = textureCopy.origin.x;
|
|
region.imageOffset.y = textureCopy.origin.y;
|
|
region.imageOffset.z = textureCopy.origin.z;
|
|
|
|
region.imageExtent.width = copySize.width;
|
|
region.imageExtent.height = copySize.height;
|
|
region.imageExtent.depth = copySize.depth;
|
|
|
|
return region;
|
|
}
|
|
|
|
class DescriptorSetTracker {
|
|
public:
|
|
void OnSetBindGroup(uint32_t index, VkDescriptorSet set) {
|
|
mDirtySets.set(index);
|
|
mSets[index] = set;
|
|
}
|
|
|
|
void OnPipelineLayoutChange(PipelineLayout* layout) {
|
|
if (layout == mCurrentLayout) {
|
|
return;
|
|
}
|
|
|
|
if (mCurrentLayout == nullptr) {
|
|
// We're at the beginning of a pass so all bind groups will be set before any
|
|
// draw / dispatch. Still clear the dirty sets to avoid leftover dirty sets
|
|
// from previous passes.
|
|
mDirtySets.reset();
|
|
} else {
|
|
// Bindgroups that are not inherited will be set again before any draw or
|
|
// dispatch. Resetting the bits also makes sure we don't have leftover dirty
|
|
// bindgroups that don't exist in the pipeline layout.
|
|
mDirtySets &= ~layout->InheritedGroupsMask(mCurrentLayout);
|
|
}
|
|
mCurrentLayout = layout;
|
|
}
|
|
|
|
void Flush(Device* device, VkCommandBuffer commands, VkPipelineBindPoint bindPoint) {
|
|
for (uint32_t dirtyIndex : IterateBitSet(mDirtySets)) {
|
|
device->fn.CmdBindDescriptorSets(commands, bindPoint,
|
|
mCurrentLayout->GetHandle(), dirtyIndex, 1,
|
|
&mSets[dirtyIndex], 0, nullptr);
|
|
}
|
|
mDirtySets.reset();
|
|
}
|
|
|
|
private:
|
|
PipelineLayout* mCurrentLayout = nullptr;
|
|
std::array<VkDescriptorSet, kMaxBindGroups> mSets;
|
|
std::bitset<kMaxBindGroups> mDirtySets;
|
|
};
|
|
|
|
void RecordBeginRenderPass(VkCommandBuffer commands,
|
|
Device* device,
|
|
RenderPassDescriptorBase* renderPass) {
|
|
// Query a VkRenderPass from the cache
|
|
VkRenderPass renderPassVK = VK_NULL_HANDLE;
|
|
{
|
|
RenderPassCacheQuery query;
|
|
|
|
for (uint32_t i : IterateBitSet(renderPass->GetColorAttachmentMask())) {
|
|
const auto& attachmentInfo = renderPass->GetColorAttachment(i);
|
|
query.SetColor(i, attachmentInfo.view->GetTexture()->GetFormat(),
|
|
attachmentInfo.loadOp);
|
|
}
|
|
|
|
if (renderPass->HasDepthStencilAttachment()) {
|
|
const auto& attachmentInfo = renderPass->GetDepthStencilAttachment();
|
|
query.SetDepthStencil(attachmentInfo.view->GetTexture()->GetFormat(),
|
|
attachmentInfo.depthLoadOp, attachmentInfo.stencilLoadOp);
|
|
}
|
|
|
|
renderPassVK = device->GetRenderPassCache()->GetRenderPass(query);
|
|
}
|
|
|
|
// Create a framebuffer that will be used once for the render pass and gather the clear
|
|
// values for the attachments at the same time.
|
|
std::array<VkClearValue, kMaxColorAttachments + 1> clearValues;
|
|
VkFramebuffer framebuffer = VK_NULL_HANDLE;
|
|
uint32_t attachmentCount = 0;
|
|
{
|
|
// Fill in the attachment info that will be chained in the framebuffer create info.
|
|
std::array<VkImageView, kMaxColorAttachments + 1> attachments;
|
|
|
|
for (uint32_t i : IterateBitSet(renderPass->GetColorAttachmentMask())) {
|
|
auto& attachmentInfo = renderPass->GetColorAttachment(i);
|
|
TextureView* view = ToBackend(attachmentInfo.view.Get());
|
|
|
|
attachments[attachmentCount] = view->GetHandle();
|
|
|
|
clearValues[attachmentCount].color.float32[0] = attachmentInfo.clearColor[0];
|
|
clearValues[attachmentCount].color.float32[1] = attachmentInfo.clearColor[1];
|
|
clearValues[attachmentCount].color.float32[2] = attachmentInfo.clearColor[2];
|
|
clearValues[attachmentCount].color.float32[3] = attachmentInfo.clearColor[3];
|
|
|
|
attachmentCount++;
|
|
}
|
|
|
|
if (renderPass->HasDepthStencilAttachment()) {
|
|
auto& attachmentInfo = renderPass->GetDepthStencilAttachment();
|
|
TextureView* view = ToBackend(attachmentInfo.view.Get());
|
|
|
|
attachments[attachmentCount] = view->GetHandle();
|
|
|
|
clearValues[attachmentCount].depthStencil.depth = attachmentInfo.clearDepth;
|
|
clearValues[attachmentCount].depthStencil.stencil = attachmentInfo.clearStencil;
|
|
|
|
attachmentCount++;
|
|
}
|
|
|
|
// Chain attachments and create the framebuffer
|
|
VkFramebufferCreateInfo createInfo;
|
|
createInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
|
createInfo.pNext = nullptr;
|
|
createInfo.flags = 0;
|
|
createInfo.renderPass = renderPassVK;
|
|
createInfo.attachmentCount = attachmentCount;
|
|
createInfo.pAttachments = attachments.data();
|
|
createInfo.width = renderPass->GetWidth();
|
|
createInfo.height = renderPass->GetHeight();
|
|
createInfo.layers = 1;
|
|
|
|
if (device->fn.CreateFramebuffer(device->GetVkDevice(), &createInfo, nullptr,
|
|
&framebuffer) != VK_SUCCESS) {
|
|
ASSERT(false);
|
|
}
|
|
|
|
// We don't reuse VkFramebuffers so mark the framebuffer for deletion as soon as the
|
|
// commands currently being recorded are finished.
|
|
device->GetFencedDeleter()->DeleteWhenUnused(framebuffer);
|
|
}
|
|
|
|
VkRenderPassBeginInfo beginInfo;
|
|
beginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
|
beginInfo.pNext = nullptr;
|
|
beginInfo.renderPass = renderPassVK;
|
|
beginInfo.framebuffer = framebuffer;
|
|
beginInfo.renderArea.offset.x = 0;
|
|
beginInfo.renderArea.offset.y = 0;
|
|
beginInfo.renderArea.extent.width = renderPass->GetWidth();
|
|
beginInfo.renderArea.extent.height = renderPass->GetHeight();
|
|
beginInfo.clearValueCount = attachmentCount;
|
|
beginInfo.pClearValues = clearValues.data();
|
|
|
|
device->fn.CmdBeginRenderPass(commands, &beginInfo, VK_SUBPASS_CONTENTS_INLINE);
|
|
}
|
|
} // anonymous namespace
|
|
|
|
CommandBuffer::CommandBuffer(CommandBufferBuilder* builder)
|
|
: CommandBufferBase(builder), mCommands(builder->AcquireCommands()) {
|
|
}
|
|
|
|
CommandBuffer::~CommandBuffer() {
|
|
FreeCommands(&mCommands);
|
|
}
|
|
|
|
void CommandBuffer::RecordCommands(VkCommandBuffer commands) {
|
|
Device* device = ToBackend(GetDevice());
|
|
|
|
// Records the necessary barriers for the resource usage pre-computed by the frontend
|
|
auto TransitionForPass = [](VkCommandBuffer commands, const PassResourceUsage& usages) {
|
|
for (size_t i = 0; i < usages.buffers.size(); ++i) {
|
|
Buffer* buffer = ToBackend(usages.buffers[i]);
|
|
buffer->TransitionUsageNow(commands, usages.bufferUsages[i]);
|
|
}
|
|
for (size_t i = 0; i < usages.textures.size(); ++i) {
|
|
Texture* texture = ToBackend(usages.textures[i]);
|
|
texture->TransitionUsageNow(commands, usages.textureUsages[i]);
|
|
}
|
|
};
|
|
|
|
const std::vector<PassResourceUsage>& passResourceUsages = GetResourceUsages().perPass;
|
|
size_t nextPassNumber = 0;
|
|
|
|
Command type;
|
|
while (mCommands.NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::CopyBufferToBuffer: {
|
|
CopyBufferToBufferCmd* copy = mCommands.NextCommand<CopyBufferToBufferCmd>();
|
|
auto& src = copy->source;
|
|
auto& dst = copy->destination;
|
|
|
|
ToBackend(src.buffer)
|
|
->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferSrc);
|
|
ToBackend(dst.buffer)
|
|
->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferDst);
|
|
|
|
VkBufferCopy region;
|
|
region.srcOffset = src.offset;
|
|
region.dstOffset = dst.offset;
|
|
region.size = copy->size;
|
|
|
|
VkBuffer srcHandle = ToBackend(src.buffer)->GetHandle();
|
|
VkBuffer dstHandle = ToBackend(dst.buffer)->GetHandle();
|
|
device->fn.CmdCopyBuffer(commands, srcHandle, dstHandle, 1, ®ion);
|
|
} break;
|
|
|
|
case Command::CopyBufferToTexture: {
|
|
CopyBufferToTextureCmd* copy = mCommands.NextCommand<CopyBufferToTextureCmd>();
|
|
auto& src = copy->source;
|
|
auto& dst = copy->destination;
|
|
|
|
ToBackend(src.buffer)
|
|
->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferSrc);
|
|
ToBackend(dst.texture)
|
|
->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferDst);
|
|
|
|
VkBuffer srcBuffer = ToBackend(src.buffer)->GetHandle();
|
|
VkImage dstImage = ToBackend(dst.texture)->GetHandle();
|
|
|
|
VkBufferImageCopy region =
|
|
ComputeBufferImageCopyRegion(src, dst, copy->copySize);
|
|
|
|
// The image is written to so the Dawn guarantees make sure it is in the
|
|
// TRANSFER_DST_OPTIMAL layout
|
|
device->fn.CmdCopyBufferToImage(commands, srcBuffer, dstImage,
|
|
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
|
|
®ion);
|
|
} break;
|
|
|
|
case Command::CopyTextureToBuffer: {
|
|
CopyTextureToBufferCmd* copy = mCommands.NextCommand<CopyTextureToBufferCmd>();
|
|
auto& src = copy->source;
|
|
auto& dst = copy->destination;
|
|
|
|
ToBackend(src.texture)
|
|
->TransitionUsageNow(commands, dawn::TextureUsageBit::TransferSrc);
|
|
ToBackend(dst.buffer)
|
|
->TransitionUsageNow(commands, dawn::BufferUsageBit::TransferDst);
|
|
|
|
VkImage srcImage = ToBackend(src.texture)->GetHandle();
|
|
VkBuffer dstBuffer = ToBackend(dst.buffer)->GetHandle();
|
|
|
|
VkBufferImageCopy region =
|
|
ComputeBufferImageCopyRegion(dst, src, copy->copySize);
|
|
|
|
// The Dawn TransferSrc usage is always mapped to GENERAL
|
|
device->fn.CmdCopyImageToBuffer(commands, srcImage, VK_IMAGE_LAYOUT_GENERAL,
|
|
dstBuffer, 1, ®ion);
|
|
} break;
|
|
|
|
case Command::BeginRenderPass: {
|
|
BeginRenderPassCmd* cmd = mCommands.NextCommand<BeginRenderPassCmd>();
|
|
|
|
TransitionForPass(commands, passResourceUsages[nextPassNumber]);
|
|
RecordRenderPass(commands, ToBackend(cmd->info.Get()));
|
|
|
|
nextPassNumber++;
|
|
} break;
|
|
|
|
case Command::BeginComputePass: {
|
|
mCommands.NextCommand<BeginComputePassCmd>();
|
|
|
|
TransitionForPass(commands, passResourceUsages[nextPassNumber]);
|
|
RecordComputePass(commands);
|
|
|
|
nextPassNumber++;
|
|
} break;
|
|
|
|
default: { UNREACHABLE(); } break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CommandBuffer::RecordComputePass(VkCommandBuffer commands) {
|
|
Device* device = ToBackend(GetDevice());
|
|
|
|
DescriptorSetTracker descriptorSets;
|
|
|
|
Command type;
|
|
while (mCommands.NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::EndComputePass: {
|
|
mCommands.NextCommand<EndComputePassCmd>();
|
|
return;
|
|
} break;
|
|
|
|
case Command::Dispatch: {
|
|
DispatchCmd* dispatch = mCommands.NextCommand<DispatchCmd>();
|
|
descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_COMPUTE);
|
|
device->fn.CmdDispatch(commands, dispatch->x, dispatch->y, dispatch->z);
|
|
} break;
|
|
|
|
case Command::SetBindGroup: {
|
|
SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
|
|
VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();
|
|
|
|
descriptorSets.OnSetBindGroup(cmd->index, set);
|
|
} break;
|
|
|
|
case Command::SetComputePipeline: {
|
|
SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>();
|
|
ComputePipeline* pipeline = ToBackend(cmd->pipeline).Get();
|
|
|
|
device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_COMPUTE,
|
|
pipeline->GetHandle());
|
|
descriptorSets.OnPipelineLayoutChange(ToBackend(pipeline->GetLayout()));
|
|
} break;
|
|
|
|
default: { UNREACHABLE(); } break;
|
|
}
|
|
}
|
|
|
|
// EndComputePass should have been called
|
|
UNREACHABLE();
|
|
}
|
|
void CommandBuffer::RecordRenderPass(VkCommandBuffer commands,
|
|
RenderPassDescriptorBase* renderPass) {
|
|
Device* device = ToBackend(GetDevice());
|
|
|
|
RecordBeginRenderPass(commands, device, renderPass);
|
|
|
|
// Set the default value for the dynamic state
|
|
{
|
|
device->fn.CmdSetLineWidth(commands, 1.0f);
|
|
device->fn.CmdSetDepthBounds(commands, 0.0f, 1.0f);
|
|
|
|
device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK, 0);
|
|
|
|
float blendConstants[4] = {
|
|
0.0f,
|
|
0.0f,
|
|
0.0f,
|
|
0.0f,
|
|
};
|
|
device->fn.CmdSetBlendConstants(commands, blendConstants);
|
|
|
|
// The viewport and scissor default to cover all of the attachments
|
|
VkViewport viewport;
|
|
viewport.x = 0.0f;
|
|
viewport.y = 0.0f;
|
|
viewport.width = static_cast<float>(renderPass->GetWidth());
|
|
viewport.height = static_cast<float>(renderPass->GetHeight());
|
|
viewport.minDepth = 0.0f;
|
|
viewport.maxDepth = 1.0f;
|
|
device->fn.CmdSetViewport(commands, 0, 1, &viewport);
|
|
|
|
VkRect2D scissorRect;
|
|
scissorRect.offset.x = 0;
|
|
scissorRect.offset.y = 0;
|
|
scissorRect.extent.width = renderPass->GetWidth();
|
|
scissorRect.extent.height = renderPass->GetHeight();
|
|
device->fn.CmdSetScissor(commands, 0, 1, &scissorRect);
|
|
}
|
|
|
|
DescriptorSetTracker descriptorSets;
|
|
RenderPipeline* lastPipeline = nullptr;
|
|
|
|
Command type;
|
|
while (mCommands.NextCommandId(&type)) {
|
|
switch (type) {
|
|
case Command::EndRenderPass: {
|
|
mCommands.NextCommand<EndRenderPassCmd>();
|
|
device->fn.CmdEndRenderPass(commands);
|
|
return;
|
|
} break;
|
|
|
|
case Command::Draw: {
|
|
DrawCmd* draw = mCommands.NextCommand<DrawCmd>();
|
|
|
|
descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
|
device->fn.CmdDraw(commands, draw->vertexCount, draw->instanceCount,
|
|
draw->firstVertex, draw->firstInstance);
|
|
} break;
|
|
|
|
case Command::DrawIndexed: {
|
|
DrawIndexedCmd* draw = mCommands.NextCommand<DrawIndexedCmd>();
|
|
|
|
descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
|
device->fn.CmdDrawIndexed(commands, draw->indexCount, draw->instanceCount,
|
|
draw->firstIndex, draw->baseVertex,
|
|
draw->firstInstance);
|
|
} break;
|
|
|
|
case Command::SetBindGroup: {
|
|
SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
|
|
VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();
|
|
|
|
descriptorSets.OnSetBindGroup(cmd->index, set);
|
|
} break;
|
|
|
|
case Command::SetBlendColor: {
|
|
SetBlendColorCmd* cmd = mCommands.NextCommand<SetBlendColorCmd>();
|
|
float blendConstants[4] = {
|
|
cmd->color.r,
|
|
cmd->color.g,
|
|
cmd->color.b,
|
|
cmd->color.a,
|
|
};
|
|
device->fn.CmdSetBlendConstants(commands, blendConstants);
|
|
} break;
|
|
|
|
case Command::SetIndexBuffer: {
|
|
SetIndexBufferCmd* cmd = mCommands.NextCommand<SetIndexBufferCmd>();
|
|
VkBuffer indexBuffer = ToBackend(cmd->buffer)->GetHandle();
|
|
|
|
// TODO(cwallez@chromium.org): get the index type from the last render pipeline
|
|
// and rebind if needed on pipeline change
|
|
ASSERT(lastPipeline != nullptr);
|
|
VkIndexType indexType = VulkanIndexType(lastPipeline->GetIndexFormat());
|
|
device->fn.CmdBindIndexBuffer(
|
|
commands, indexBuffer, static_cast<VkDeviceSize>(cmd->offset), indexType);
|
|
} break;
|
|
|
|
case Command::SetRenderPipeline: {
|
|
SetRenderPipelineCmd* cmd = mCommands.NextCommand<SetRenderPipelineCmd>();
|
|
RenderPipeline* pipeline = ToBackend(cmd->pipeline).Get();
|
|
|
|
device->fn.CmdBindPipeline(commands, VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
pipeline->GetHandle());
|
|
lastPipeline = pipeline;
|
|
|
|
descriptorSets.OnPipelineLayoutChange(ToBackend(pipeline->GetLayout()));
|
|
} break;
|
|
|
|
case Command::SetStencilReference: {
|
|
SetStencilReferenceCmd* cmd = mCommands.NextCommand<SetStencilReferenceCmd>();
|
|
device->fn.CmdSetStencilReference(commands, VK_STENCIL_FRONT_AND_BACK,
|
|
cmd->reference);
|
|
} break;
|
|
|
|
case Command::SetScissorRect: {
|
|
SetScissorRectCmd* cmd = mCommands.NextCommand<SetScissorRectCmd>();
|
|
VkRect2D rect;
|
|
rect.offset.x = cmd->x;
|
|
rect.offset.y = cmd->y;
|
|
rect.extent.width = cmd->width;
|
|
rect.extent.height = cmd->height;
|
|
|
|
device->fn.CmdSetScissor(commands, 0, 1, &rect);
|
|
} break;
|
|
|
|
case Command::SetVertexBuffers: {
|
|
SetVertexBuffersCmd* cmd = mCommands.NextCommand<SetVertexBuffersCmd>();
|
|
auto buffers = mCommands.NextData<Ref<BufferBase>>(cmd->count);
|
|
auto offsets = mCommands.NextData<uint32_t>(cmd->count);
|
|
|
|
std::array<VkBuffer, kMaxVertexInputs> vkBuffers;
|
|
std::array<VkDeviceSize, kMaxVertexInputs> vkOffsets;
|
|
|
|
for (uint32_t i = 0; i < cmd->count; ++i) {
|
|
Buffer* buffer = ToBackend(buffers[i].Get());
|
|
vkBuffers[i] = buffer->GetHandle();
|
|
vkOffsets[i] = static_cast<VkDeviceSize>(offsets[i]);
|
|
}
|
|
|
|
device->fn.CmdBindVertexBuffers(commands, cmd->startSlot, cmd->count,
|
|
vkBuffers.data(), vkOffsets.data());
|
|
} break;
|
|
|
|
default: { UNREACHABLE(); } break;
|
|
}
|
|
}
|
|
|
|
// EndRenderPass should have been called
|
|
UNREACHABLE();
|
|
}
|
|
|
|
}} // namespace dawn_native::vulkan
|