blob: ec4f0d42af8968e19f82ce8070dfdd8e8e3d5f01 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/lib/escher/vk/image_layout_updater.h"
#include <utility>
#include "src/ui/lib/escher/vk/command_buffer.h"
namespace escher {
namespace {
// For given new layout, we need to specify the pipeline stage and the access
// flags for that image in vkCmdPipelineBarrier. We use the following logic to
// get destination stage and access mask flags for each given image layout.
std::pair<vk::PipelineStageFlags, vk::AccessFlags> GetDstMask(vk::ImageLayout new_layout) {
vk::PipelineStageFlags dst_stage_mask;
vk::AccessFlags dst_access_mask;
switch (new_layout) {
case vk::ImageLayout::eColorAttachmentOptimal:
dst_access_mask =
vk::AccessFlagBits::eColorAttachmentRead | vk::AccessFlagBits::eColorAttachmentWrite;
dst_stage_mask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
break;
case vk::ImageLayout::eDepthStencilAttachmentOptimal:
dst_access_mask = vk::AccessFlagBits::eDepthStencilAttachmentRead |
vk::AccessFlagBits::eDepthStencilAttachmentWrite;
dst_stage_mask = vk::PipelineStageFlagBits::eEarlyFragmentTests;
break;
case vk::ImageLayout::eGeneral:
dst_access_mask = vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eShaderWrite;
dst_stage_mask = vk::PipelineStageFlagBits::eComputeShader;
break;
case vk::ImageLayout::ePresentSrcKHR:
dst_access_mask = vk::AccessFlagBits::eMemoryRead;
dst_stage_mask = vk::PipelineStageFlagBits::eAllGraphics;
break;
case vk::ImageLayout::eShaderReadOnlyOptimal:
dst_access_mask = vk::AccessFlagBits::eShaderRead;
dst_stage_mask = vk::PipelineStageFlagBits::eAllCommands;
break;
case vk::ImageLayout::eTransferDstOptimal:
dst_access_mask = vk::AccessFlagBits::eTransferWrite;
dst_stage_mask = vk::PipelineStageFlagBits::eTransfer;
break;
case vk::ImageLayout::eTransferSrcOptimal:
dst_access_mask = vk::AccessFlagBits::eTransferRead;
dst_stage_mask = vk::PipelineStageFlagBits::eTransfer;
break;
case vk::ImageLayout::eUndefined:
default:
FXL_LOG(ERROR) << "CommandBuffer does not know how to transition to layout: "
<< vk::to_string(new_layout);
FXL_DCHECK(false);
}
return std::make_pair(dst_stage_mask, dst_access_mask);
}
} // namespace
ImageLayoutUpdater::ImageLayoutUpdater(EscherWeakPtr const escher) : escher_(escher) {
if (!escher_) {
FXL_LOG(ERROR) << "Fatal: No valid escher, ImageLayoutUpdater will fail.";
}
}
ImageLayoutUpdater::~ImageLayoutUpdater() {
// Check that there is no pending tasks / pending semaphores.
FXL_DCHECK(images_to_set_.empty() && pending_image_layout_to_set_.empty() &&
wait_semaphores_.empty() && signal_semaphores_.empty());
}
void ImageLayoutUpdater::ScheduleSetImageInitialLayout(const escher::ImagePtr& image,
vk::ImageLayout new_layout) {
FXL_DCHECK(image->layout() == vk::ImageLayout::eUndefined ||
image->layout() == vk::ImageLayout::ePreinitialized);
FXL_CHECK(images_to_set_.find(image) == images_to_set_.end())
<< "Initial layout can be set only once for each image.";
images_to_set_.insert(image);
pending_image_layout_to_set_.emplace_back(image, new_layout);
}
void ImageLayoutUpdater::GenerateCommands(CommandBuffer* cmds) {
if (!NeedsCommandBuffer()) {
return;
}
// Check existence of the command buffer |cmds|.
FXL_DCHECK(cmds);
for (const auto& [image, new_layout] : pending_image_layout_to_set_) {
FXL_DCHECK(!image->is_layout_initialized())
<< "Error: layout of VkImage " << image->vk() << " is already initialized.";
auto src_stage_mask = vk::PipelineStageFlagBits::eTopOfPipe;
auto src_access_mask = vk::AccessFlags();
std::pair<vk::PipelineStageFlags, vk::AccessFlags> dst_masks;
if (cmds->type() == CommandBuffer::Type::kTransfer) {
// For transfer command buffers, since there can be only transfer
// commands, we will only synchronize image layout update with transfer
// pipeline stage.
dst_masks = {vk::PipelineStageFlagBits::eTransfer,
vk::AccessFlagBits::eTransferWrite | vk::AccessFlagBits::eTransferRead};
} else {
// For command buffers of other type (compute, graphics), we choose
// the destination masks based on its target layout.
dst_masks = GetDstMask(new_layout);
}
auto dst_stage_mask = dst_masks.first;
auto dst_access_mask = dst_masks.second;
cmds->ImageBarrier(image, image->layout(), new_layout, src_stage_mask, src_access_mask,
dst_stage_mask, dst_access_mask);
cmds->KeepAlive(image);
}
// Add semaphores for the submitted command buffer to wait on.
for (auto& pair : wait_semaphores_) {
cmds->AddWaitSemaphore(std::move(pair.first), pair.second);
}
wait_semaphores_.clear();
// Add semaphores for the submitted command buffer to signal.
for (auto& sem : signal_semaphores_) {
cmds->AddSignalSemaphore(std::move(sem));
}
signal_semaphores_.clear();
images_to_set_.clear();
pending_image_layout_to_set_.clear();
}
void ImageLayoutUpdater::Submit(fit::function<void()> callback, CommandBuffer::Type type) {
if (NeedsCommandBuffer()) {
auto cmds = CommandBuffer::NewForType(escher_.get(), type, /* use_protected_memory */ false);
GenerateCommands(cmds.get());
cmds->Submit(std::move(callback));
}
// After this function is called, the |pending_image_layout_to_set_|,
// |images_to_set_| and all semaphores will be clear so that the image layout
// updater will be reused again.
FXL_DCHECK(images_to_set_.empty() && pending_image_layout_to_set_.empty() &&
wait_semaphores_.empty() && signal_semaphores_.empty());
}
} // namespace escher