blob: b5c3d6a147eda072ad181fc2da4dd3613e442287 [file] [log] [blame]
// Copyright 2013 The Flutter 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 "impeller/renderer/backend/vulkan/blit_command_vk.h"
#include <cstdint>
#include "impeller/renderer/backend/vulkan/barrier_vk.h"
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "vulkan/vulkan_core.h"
#include "vulkan/vulkan_enums.hpp"
#include "vulkan/vulkan_structs.hpp"
namespace impeller {
static void InsertImageMemoryBarrier(const vk::CommandBuffer& cmd,
const vk::Image& image,
vk::AccessFlags src_access_mask,
vk::AccessFlags dst_access_mask,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::PipelineStageFlags src_stage,
vk::PipelineStageFlags dst_stage,
uint32_t base_mip_level,
uint32_t mip_level_count = 1u) {
if (old_layout == new_layout) {
return;
}
vk::ImageMemoryBarrier barrier;
barrier.srcAccessMask = src_access_mask;
barrier.dstAccessMask = dst_access_mask;
barrier.oldLayout = old_layout;
barrier.newLayout = new_layout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
barrier.subresourceRange.baseMipLevel = base_mip_level;
barrier.subresourceRange.levelCount = mip_level_count;
barrier.subresourceRange.baseArrayLayer = 0u;
barrier.subresourceRange.layerCount = 1u;
cmd.pipelineBarrier(src_stage, dst_stage, {}, nullptr, nullptr, barrier);
}
BlitEncodeVK::~BlitEncodeVK() = default;
//------------------------------------------------------------------------------
/// BlitCopyTextureToTextureCommandVK
///
BlitCopyTextureToTextureCommandVK::~BlitCopyTextureToTextureCommandVK() =
default;
std::string BlitCopyTextureToTextureCommandVK::GetLabel() const {
return label;
}
bool BlitCopyTextureToTextureCommandVK::Encode(
CommandEncoderVK& encoder) const {
const auto& cmd_buffer = encoder.GetCommandBuffer();
const auto& src = TextureVK::Cast(*source);
const auto& dst = TextureVK::Cast(*destination);
if (!encoder.Track(source) || !encoder.Track(destination)) {
return false;
}
BarrierVK src_barrier;
src_barrier.cmd_buffer = cmd_buffer;
src_barrier.new_layout = vk::ImageLayout::eTransferSrcOptimal;
src_barrier.src_access = vk::AccessFlagBits::eTransferWrite |
vk::AccessFlagBits::eShaderWrite |
vk::AccessFlagBits::eColorAttachmentWrite;
src_barrier.src_stage = vk::PipelineStageFlagBits::eTransfer |
vk::PipelineStageFlagBits::eFragmentShader |
vk::PipelineStageFlagBits::eColorAttachmentOutput;
src_barrier.dst_access = vk::AccessFlagBits::eTransferRead;
src_barrier.dst_stage = vk::PipelineStageFlagBits::eTransfer;
BarrierVK dst_barrier;
dst_barrier.cmd_buffer = cmd_buffer;
dst_barrier.new_layout = vk::ImageLayout::eTransferDstOptimal;
dst_barrier.src_access = {};
dst_barrier.src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
dst_barrier.dst_access =
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferWrite;
dst_barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader |
vk::PipelineStageFlagBits::eTransfer;
if (!src.SetLayout(src_barrier) || !dst.SetLayout(dst_barrier)) {
VALIDATION_LOG << "Could not complete layout transitions.";
return false;
}
vk::ImageCopy image_copy;
image_copy.setSrcSubresource(
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
image_copy.setDstSubresource(
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
image_copy.srcOffset =
vk::Offset3D(source_region.GetX(), source_region.GetY(), 0);
image_copy.dstOffset =
vk::Offset3D(destination_origin.x, destination_origin.y, 0);
image_copy.extent =
vk::Extent3D(source_region.GetWidth(), source_region.GetHeight(), 1);
// Issue the copy command now that the images are already in the right
// layouts.
cmd_buffer.copyImage(src.GetImage(), //
src_barrier.new_layout, //
dst.GetImage(), //
dst_barrier.new_layout, //
image_copy //
);
// If this is an onscreen texture, do not transition the layout
// back to shader read.
if (dst.IsSwapchainImage()) {
return true;
}
BarrierVK barrier;
barrier.cmd_buffer = cmd_buffer;
barrier.new_layout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.src_access = {};
barrier.src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
barrier.dst_access = vk::AccessFlagBits::eShaderRead;
barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader;
return dst.SetLayout(barrier);
}
//------------------------------------------------------------------------------
/// BlitCopyTextureToBufferCommandVK
///
BlitCopyTextureToBufferCommandVK::~BlitCopyTextureToBufferCommandVK() = default;
std::string BlitCopyTextureToBufferCommandVK::GetLabel() const {
return label;
}
bool BlitCopyTextureToBufferCommandVK::Encode(CommandEncoderVK& encoder) const {
const auto& cmd_buffer = encoder.GetCommandBuffer();
// cast source and destination to TextureVK
const auto& src = TextureVK::Cast(*source);
if (!encoder.Track(source) || !encoder.Track(destination)) {
return false;
}
BarrierVK barrier;
barrier.cmd_buffer = cmd_buffer;
barrier.new_layout = vk::ImageLayout::eTransferSrcOptimal;
barrier.src_access = vk::AccessFlagBits::eShaderWrite |
vk::AccessFlagBits::eTransferWrite |
vk::AccessFlagBits::eColorAttachmentWrite;
barrier.src_stage = vk::PipelineStageFlagBits::eFragmentShader |
vk::PipelineStageFlagBits::eTransfer |
vk::PipelineStageFlagBits::eColorAttachmentOutput;
barrier.dst_access = vk::AccessFlagBits::eShaderRead;
barrier.dst_stage = vk::PipelineStageFlagBits::eVertexShader |
vk::PipelineStageFlagBits::eFragmentShader;
const auto& dst = DeviceBufferVK::Cast(*destination);
vk::BufferImageCopy image_copy;
image_copy.setBufferOffset(destination_offset);
image_copy.setBufferRowLength(0);
image_copy.setBufferImageHeight(0);
image_copy.setImageSubresource(
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
image_copy.setImageOffset(
vk::Offset3D(source_region.GetX(), source_region.GetY(), 0));
image_copy.setImageExtent(
vk::Extent3D(source_region.GetWidth(), source_region.GetHeight(), 1));
if (!src.SetLayout(barrier)) {
VALIDATION_LOG << "Could not encode layout transition.";
return false;
}
cmd_buffer.copyImageToBuffer(src.GetImage(), //
barrier.new_layout, //
dst.GetBuffer(), //
image_copy //
);
// If the buffer is used for readback, then apply a transfer -> host memory
// barrier.
if (destination->GetDeviceBufferDescriptor().readback) {
vk::MemoryBarrier barrier;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eHostRead;
cmd_buffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eHost, {}, 1,
&barrier, 0, {}, 0, {});
}
return true;
}
//------------------------------------------------------------------------------
/// BlitCopyBufferToTextureCommandVK
///
BlitCopyBufferToTextureCommandVK::~BlitCopyBufferToTextureCommandVK() = default;
std::string BlitCopyBufferToTextureCommandVK::GetLabel() const {
return label;
}
bool BlitCopyBufferToTextureCommandVK::Encode(CommandEncoderVK& encoder) const {
const auto& cmd_buffer = encoder.GetCommandBuffer();
// cast destination to TextureVK
const auto& dst = TextureVK::Cast(*destination);
const auto& src = DeviceBufferVK::Cast(*source.buffer);
if (!encoder.Track(source.buffer) || !encoder.Track(destination)) {
return false;
}
BarrierVK dst_barrier;
dst_barrier.cmd_buffer = cmd_buffer;
dst_barrier.new_layout = vk::ImageLayout::eTransferDstOptimal;
dst_barrier.src_access = {};
dst_barrier.src_stage = vk::PipelineStageFlagBits::eTopOfPipe;
dst_barrier.dst_access =
vk::AccessFlagBits::eShaderRead | vk::AccessFlagBits::eTransferWrite;
dst_barrier.dst_stage = vk::PipelineStageFlagBits::eFragmentShader |
vk::PipelineStageFlagBits::eTransfer;
vk::BufferImageCopy image_copy;
image_copy.setBufferOffset(source.range.offset);
image_copy.setBufferRowLength(0);
image_copy.setBufferImageHeight(0);
image_copy.setImageSubresource(
vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
image_copy.setImageOffset(
vk::Offset3D(destination_origin.x, destination_origin.y, 0));
image_copy.setImageExtent(vk::Extent3D(destination->GetSize().width,
destination->GetSize().height, 1));
if (!dst.SetLayout(dst_barrier)) {
VALIDATION_LOG << "Could not encode layout transition.";
return false;
}
cmd_buffer.copyBufferToImage(src.GetBuffer(), //
dst.GetImage(), //
dst_barrier.new_layout, //
image_copy //
);
return true;
}
//------------------------------------------------------------------------------
/// BlitGenerateMipmapCommandVK
///
BlitGenerateMipmapCommandVK::~BlitGenerateMipmapCommandVK() = default;
std::string BlitGenerateMipmapCommandVK::GetLabel() const {
return label;
}
bool BlitGenerateMipmapCommandVK::Encode(CommandEncoderVK& encoder) const {
auto& src = TextureVK::Cast(*texture);
const auto size = src.GetTextureDescriptor().size;
uint32_t mip_count = src.GetTextureDescriptor().mip_count;
if (mip_count < 2u) {
return true;
}
const auto& image = src.GetImage();
const auto& cmd = encoder.GetCommandBuffer();
if (!encoder.Track(texture)) {
return false;
}
// Initialize all mip levels to be in TransferDst mode. Later, in a loop,
// after writing to that mip level, we'll first switch its layout to
// TransferSrc to prepare the mip level after it, use the image as the source
// of the blit, before finally switching it to ShaderReadOnly so its available
// for sampling in a shader.
InsertImageMemoryBarrier(
cmd, // command buffer
image, // image
vk::AccessFlagBits::eTransferWrite, // src access mask
vk::AccessFlagBits::eTransferRead, // dst access mask
src.GetLayout(), // old layout
vk::ImageLayout::eTransferDstOptimal, // new layout
vk::PipelineStageFlagBits::eTransfer, // src stage
vk::PipelineStageFlagBits::eTransfer, // dst stage
0u, // mip level
mip_count // mip level count
);
vk::ImageMemoryBarrier barrier;
barrier.image = image;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
barrier.subresourceRange.levelCount = 1;
// Blit from the mip level N - 1 to mip level N.
size_t width = size.width;
size_t height = size.height;
for (size_t mip_level = 1u; mip_level < mip_count; mip_level++) {
barrier.subresourceRange.baseMipLevel = mip_level - 1;
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
// We just finished writing to the previous (N-1) mip level or it was the
// base mip level. These were initialized to TransferDst earler. We are now
// going to read from it to write to the current level (N) . So it must be
// converted to TransferSrc.
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer, {}, {}, {},
{barrier});
vk::ImageBlit blit;
blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
blit.srcSubresource.baseArrayLayer = 0u;
blit.srcSubresource.layerCount = 1u;
blit.srcSubresource.mipLevel = mip_level - 1;
blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
blit.dstSubresource.baseArrayLayer = 0u;
blit.dstSubresource.layerCount = 1u;
blit.dstSubresource.mipLevel = mip_level;
// offsets[0] is origin.
blit.srcOffsets[1].x = std::max<int32_t>(width, 1u);
blit.srcOffsets[1].y = std::max<int32_t>(height, 1u);
blit.srcOffsets[1].z = 1u;
width = width / 2;
height = height / 2;
// offsets[0] is origin.
blit.dstOffsets[1].x = std::max<int32_t>(width, 1u);
blit.dstOffsets[1].y = std::max<int32_t>(height, 1u);
blit.dstOffsets[1].z = 1u;
cmd.blitImage(image, // src image
vk::ImageLayout::eTransferSrcOptimal, // src layout
image, // dst image
vk::ImageLayout::eTransferDstOptimal, // dst layout
1u, // region count
&blit, // regions
vk::Filter::eLinear // filter
);
barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
// Now that the blit is done, the image at the previous level (N-1)
// is done reading from (TransferSrc)/ Now we must prepare it to be read
// from a shader (ShaderReadOnly).
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
{barrier});
}
barrier.subresourceRange.baseMipLevel = mip_count - 1;
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
{barrier});
// We modified the layouts of this image from underneath it. Tell it its new
// state so it doesn't try to perform redundant transitions under the hood.
src.SetLayoutWithoutEncoding(vk::ImageLayout::eShaderReadOnlyOptimal);
src.SetMipMapGenerated();
return true;
}
} // namespace impeller