// 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/BufferVk.h" #include "dawn_native/vulkan/BufferUploader.h" #include "dawn_native/vulkan/DeviceVk.h" #include "dawn_native/vulkan/FencedDeleter.h" #include namespace dawn_native { namespace vulkan { namespace { VkBufferUsageFlags VulkanBufferUsage(dawn::BufferUsageBit usage) { VkBufferUsageFlags flags = 0; if (usage & dawn::BufferUsageBit::TransferSrc) { flags |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT; } if (usage & dawn::BufferUsageBit::TransferDst) { flags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; } if (usage & dawn::BufferUsageBit::Index) { flags |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; } if (usage & dawn::BufferUsageBit::Vertex) { flags |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; } if (usage & dawn::BufferUsageBit::Uniform) { flags |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; } if (usage & dawn::BufferUsageBit::Storage) { flags |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; } return flags; } VkPipelineStageFlags VulkanPipelineStage(dawn::BufferUsageBit usage) { VkPipelineStageFlags flags = 0; if (usage & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) { flags |= VK_PIPELINE_STAGE_HOST_BIT; } if (usage & (dawn::BufferUsageBit::TransferSrc | dawn::BufferUsageBit::TransferDst)) { flags |= VK_PIPELINE_STAGE_TRANSFER_BIT; } if (usage & (dawn::BufferUsageBit::Index | dawn::BufferUsageBit::Vertex)) { flags |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; } if (usage & (dawn::BufferUsageBit::Uniform | dawn::BufferUsageBit::Storage)) { flags |= VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; } return flags; } VkAccessFlags VulkanAccessFlags(dawn::BufferUsageBit usage) { VkAccessFlags flags = 0; if (usage & dawn::BufferUsageBit::MapRead) { flags |= VK_ACCESS_HOST_READ_BIT; } if (usage & dawn::BufferUsageBit::MapWrite) { flags |= VK_ACCESS_HOST_WRITE_BIT; } if (usage & dawn::BufferUsageBit::TransferSrc) { flags |= VK_ACCESS_TRANSFER_READ_BIT; } if (usage & dawn::BufferUsageBit::TransferDst) { flags |= VK_ACCESS_TRANSFER_WRITE_BIT; } if (usage & dawn::BufferUsageBit::Index) { flags |= VK_ACCESS_INDEX_READ_BIT; } if (usage & dawn::BufferUsageBit::Vertex) { flags |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; } if (usage & dawn::BufferUsageBit::Uniform) { flags |= VK_ACCESS_UNIFORM_READ_BIT; } if (usage & dawn::BufferUsageBit::Storage) { flags |= VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; } return flags; } } // namespace Buffer::Buffer(BufferBuilder* builder) : BufferBase(builder) { Device* device = ToBackend(GetDevice()); VkBufferCreateInfo createInfo; createInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; createInfo.pNext = nullptr; createInfo.flags = 0; createInfo.size = GetSize(); createInfo.usage = VulkanBufferUsage(GetUsage()); createInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; createInfo.queueFamilyIndexCount = 0; createInfo.pQueueFamilyIndices = 0; if (device->fn.CreateBuffer(device->GetVkDevice(), &createInfo, nullptr, &mHandle) != VK_SUCCESS) { ASSERT(false); } VkMemoryRequirements requirements; device->fn.GetBufferMemoryRequirements(device->GetVkDevice(), mHandle, &requirements); bool requestMappable = (GetUsage() & (dawn::BufferUsageBit::MapRead | dawn::BufferUsageBit::MapWrite)) != 0; if (!device->GetMemoryAllocator()->Allocate(requirements, requestMappable, &mMemoryAllocation)) { ASSERT(false); } if (device->fn.BindBufferMemory(device->GetVkDevice(), mHandle, mMemoryAllocation.GetMemory(), mMemoryAllocation.GetMemoryOffset()) != VK_SUCCESS) { ASSERT(false); } } Buffer::~Buffer() { Device* device = ToBackend(GetDevice()); device->GetMemoryAllocator()->Free(&mMemoryAllocation); if (mHandle != VK_NULL_HANDLE) { device->GetFencedDeleter()->DeleteWhenUnused(mHandle); mHandle = VK_NULL_HANDLE; } } void Buffer::OnMapReadCommandSerialFinished(uint32_t mapSerial, const void* data) { CallMapReadCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data); } void Buffer::OnMapWriteCommandSerialFinished(uint32_t mapSerial, void* data) { CallMapWriteCallback(mapSerial, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, data); } VkBuffer Buffer::GetHandle() const { return mHandle; } void Buffer::TransitionUsageNow(VkCommandBuffer commands, dawn::BufferUsageBit usage) { bool lastIncludesTarget = (mLastUsage & usage) == usage; bool lastReadOnly = (mLastUsage & kReadOnlyBufferUsages) == mLastUsage; // We can skip transitions to already current read-only usages. if (lastIncludesTarget && lastReadOnly) { return; } // Special-case for the initial transition: Vulkan doesn't allow access flags to be 0. if (mLastUsage == dawn::BufferUsageBit::None) { mLastUsage = usage; return; } VkPipelineStageFlags srcStages = VulkanPipelineStage(mLastUsage); VkPipelineStageFlags dstStages = VulkanPipelineStage(usage); VkBufferMemoryBarrier barrier; barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER; barrier.pNext = nullptr; barrier.srcAccessMask = VulkanAccessFlags(mLastUsage); barrier.dstAccessMask = VulkanAccessFlags(usage); barrier.srcQueueFamilyIndex = 0; barrier.dstQueueFamilyIndex = 0; barrier.buffer = mHandle; barrier.offset = 0; barrier.size = GetSize(); ToBackend(GetDevice()) ->fn.CmdPipelineBarrier(commands, srcStages, dstStages, 0, 0, nullptr, 1, &barrier, 0, nullptr); mLastUsage = usage; } void Buffer::SetSubDataImpl(uint32_t start, uint32_t count, const uint8_t* data) { Device* device = ToBackend(GetDevice()); VkCommandBuffer commands = device->GetPendingCommandBuffer(); TransitionUsageNow(commands, dawn::BufferUsageBit::TransferDst); BufferUploader* uploader = device->GetBufferUploader(); uploader->BufferSubData(mHandle, start, count, data); } void Buffer::MapReadAsyncImpl(uint32_t serial, uint32_t start, uint32_t /*count*/) { Device* device = ToBackend(GetDevice()); VkCommandBuffer commands = device->GetPendingCommandBuffer(); TransitionUsageNow(commands, dawn::BufferUsageBit::MapRead); uint8_t* memory = mMemoryAllocation.GetMappedPointer(); ASSERT(memory != nullptr); MapRequestTracker* tracker = device->GetMapRequestTracker(); tracker->Track(this, serial, memory + start, false); } void Buffer::MapWriteAsyncImpl(uint32_t serial, uint32_t start, uint32_t /*count*/) { Device* device = ToBackend(GetDevice()); VkCommandBuffer commands = device->GetPendingCommandBuffer(); TransitionUsageNow(commands, dawn::BufferUsageBit::MapWrite); uint8_t* memory = mMemoryAllocation.GetMappedPointer(); ASSERT(memory != nullptr); MapRequestTracker* tracker = device->GetMapRequestTracker(); tracker->Track(this, serial, memory + start, true); } void Buffer::UnmapImpl() { // No need to do anything, we keep CPU-visible memory mapped at all time. } // MapRequestTracker MapRequestTracker::MapRequestTracker(Device* device) : mDevice(device) { } MapRequestTracker::~MapRequestTracker() { ASSERT(mInflightRequests.Empty()); } void MapRequestTracker::Track(Buffer* buffer, uint32_t mapSerial, void* data, bool isWrite) { Request request; request.buffer = buffer; request.mapSerial = mapSerial; request.data = data; request.isWrite = isWrite; mInflightRequests.Enqueue(std::move(request), mDevice->GetSerial()); } void MapRequestTracker::Tick(Serial finishedSerial) { for (auto& request : mInflightRequests.IterateUpTo(finishedSerial)) { if (request.isWrite) { request.buffer->OnMapWriteCommandSerialFinished(request.mapSerial, request.data); } else { request.buffer->OnMapReadCommandSerialFinished(request.mapSerial, request.data); } } mInflightRequests.ClearUpTo(finishedSerial); } }} // namespace dawn_native::vulkan