blob: bde1d0338c3949488555f972cb87f38a869e1830 [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 "impeller/renderer/backend/vulkan/commands_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
namespace impeller {
BlitEncodeVK::~BlitEncodeVK() = default;
//------------------------------------------------------------------------------
/// BlitCopyTextureToTextureCommandVK
///
BlitCopyTextureToTextureCommandVK::~BlitCopyTextureToTextureCommandVK() =
default;
std::string BlitCopyTextureToTextureCommandVK::GetLabel() const {
return label;
}
[[nodiscard]] bool BlitCopyTextureToTextureCommandVK::Encode(
FencedCommandBufferVK* fenced_command_buffer) const {
// cast source and destination to TextureVK
const auto& source_tex_vk = TextureVK::Cast(*source);
const auto& dest_tex_vk = TextureVK::Cast(*destination);
// get the vulkan image and image view
const auto source_image = source_tex_vk.GetImage();
const auto dest_image = dest_tex_vk.GetImage();
// copy the source image to the destination image, from source_region to
// destination_origin.
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.origin.x, source_region.origin.y, 0);
image_copy.dstOffset =
vk::Offset3D(destination_origin.x, destination_origin.y, 0);
image_copy.extent =
vk::Extent3D(source_region.size.width, source_region.size.height, 1);
// get single use command buffer
auto copy_cmd = fenced_command_buffer->GetSingleUseChild();
vk::CommandBufferBeginInfo begin_info;
begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
auto res = copy_cmd.begin(begin_info);
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to begin command buffer: " << vk::to_string(res);
return false;
}
uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count;
// transition the source image to transfer source optimal
TransitionImageLayoutCommandVK transition_source_cmd =
TransitionImageLayoutCommandVK(source_image, vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferSrcOptimal,
mip_count);
bool success = transition_source_cmd.Submit(fenced_command_buffer);
if (!success) {
VALIDATION_LOG << "Failed to transition source image layout";
return false;
}
// transition the destination image to transfer destination optimal
TransitionImageLayoutCommandVK transition_dest_cmd =
TransitionImageLayoutCommandVK(dest_image, vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal,
mip_count);
success = transition_dest_cmd.Submit(fenced_command_buffer);
if (!success) {
VALIDATION_LOG << "Failed to transition destination image layout";
return false;
}
// issue the copy command
copy_cmd.copyImage(source_image, vk::ImageLayout::eTransferSrcOptimal,
dest_image, vk::ImageLayout::eTransferDstOptimal,
image_copy);
res = copy_cmd.end();
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
return false;
}
return true;
}
//------------------------------------------------------------------------------
/// BlitCopyTextureToBufferCommandVK
///
BlitCopyTextureToBufferCommandVK::~BlitCopyTextureToBufferCommandVK() = default;
std::string BlitCopyTextureToBufferCommandVK::GetLabel() const {
return label;
}
[[nodiscard]] bool BlitCopyTextureToBufferCommandVK::Encode(
FencedCommandBufferVK* fenced_command_buffer) const {
// cast source and destination to TextureVK
const auto& source_tex_vk = TextureVK::Cast(*source);
const auto& dest_buf_vk = DeviceBufferVK::Cast(*destination);
// get the vulkan image and image view
const auto source_image = source_tex_vk.GetImage();
// get buffer image handle
const auto dest_buffer = dest_buf_vk.GetVKBufferHandle();
// copy the source image to the destination buffer, from source_region to
// destination_origin.
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.origin.x, source_region.origin.y, 0));
image_copy.setImageExtent(
vk::Extent3D(source_region.size.width, source_region.size.height, 1));
uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count;
// transition the source image to transfer source optimal
TransitionImageLayoutCommandVK transition_source_cmd =
TransitionImageLayoutCommandVK(source_image, vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferSrcOptimal,
mip_count);
bool success = transition_source_cmd.Submit(fenced_command_buffer);
if (!success) {
return false;
}
// get single use command buffer
auto copy_cmd = fenced_command_buffer->GetSingleUseChild();
vk::CommandBufferBeginInfo begin_info;
begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
auto res = copy_cmd.begin(begin_info);
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to begin command buffer: " << vk::to_string(res);
return false;
}
// issue the copy command
copy_cmd.copyImageToBuffer(source_image, vk::ImageLayout::eTransferSrcOptimal,
dest_buffer, image_copy);
res = copy_cmd.end();
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
}
return true;
}
//------------------------------------------------------------------------------
/// BlitGenerateMipmapCommandVK
///
BlitGenerateMipmapCommandVK::~BlitGenerateMipmapCommandVK() = default;
std::string BlitGenerateMipmapCommandVK::GetLabel() const {
return label;
}
[[nodiscard]] bool BlitGenerateMipmapCommandVK::Encode(
FencedCommandBufferVK* fenced_command_buffer) const {
const auto& source_tex_vk = TextureVK::Cast(*texture);
const auto source_image = source_tex_vk.GetImage();
const auto size = source_tex_vk.GetTextureDescriptor().size;
uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count;
uint32_t mip_width = size.width;
uint32_t mip_height = size.height;
// create the subresource range
vk::ImageSubresourceRange subresource_range{};
subresource_range.setAspectMask(vk::ImageAspectFlagBits::eColor);
subresource_range.setBaseArrayLayer(0);
subresource_range.setLayerCount(1);
subresource_range.setLevelCount(1);
// create a barrier to transition the image to transfer source optimal
vk::ImageMemoryBarrier barrier{};
barrier.setImage(source_image);
barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
barrier.setSubresourceRange(subresource_range);
auto gen_mip_cmd = fenced_command_buffer->Get();
vk::CommandBufferBeginInfo begin_info;
begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
auto begin_res = gen_mip_cmd.begin(begin_info);
if (begin_res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to begin command buffer: "
<< vk::to_string(begin_res);
return false;
}
// transition all layers to transfer dst optimal
for (uint32_t i = 0; i < mip_count; i++) {
barrier.subresourceRange.baseMipLevel = i;
barrier.oldLayout = vk::ImageLayout::eUndefined;
barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer, {},
nullptr, nullptr, barrier);
}
for (uint32_t i = 1; i < mip_count; i++) {
barrier.subresourceRange.baseMipLevel = i - 1;
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eTransfer, {},
nullptr, nullptr, barrier);
vk::ImageBlit blit{};
// src
blit.srcOffsets[0] = vk::Offset3D(0, 0, 0);
blit.srcOffsets[1] = vk::Offset3D(mip_width, mip_height, 1);
blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
blit.srcSubresource.mipLevel = i - 1;
blit.srcSubresource.baseArrayLayer = 0;
blit.srcSubresource.layerCount = 1;
// dst
blit.dstOffsets[0] = vk::Offset3D(0, 0, 0);
blit.dstOffsets[1] = vk::Offset3D(mip_width > 1 ? mip_width / 2 : 1,
mip_height > 1 ? mip_height / 2 : 1, 1);
blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
blit.dstSubresource.mipLevel = i;
blit.dstSubresource.baseArrayLayer = 0;
blit.dstSubresource.layerCount = 1;
gen_mip_cmd.blitImage(source_image, vk::ImageLayout::eTransferSrcOptimal,
source_image, vk::ImageLayout::eTransferDstOptimal,
blit, vk::Filter::eLinear);
// transition the previous mip level to shader read only optimal
barrier.oldLayout = vk::ImageLayout::eUndefined;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader, {},
nullptr, nullptr, barrier);
if (mip_width > 1) {
mip_width /= 2;
}
if (mip_height > 1) {
mip_height /= 2;
}
}
// transition the last mip level to shader read only optimal
barrier.subresourceRange.baseMipLevel = mip_count - 1;
barrier.oldLayout = vk::ImageLayout::eUndefined;
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eFragmentShader, {},
nullptr, nullptr, barrier);
// submit the command buffer
auto res = gen_mip_cmd.end();
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
return false;
}
return true;
}
// END: BlitGenerateMipmapCommandVK
} // namespace impeller