blob: 7006dd244bf2a00ce3c3589cd5345bae9f2d9853 [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/render_pass_vk.h"
#include <array>
#include <vector>
#include "fml/logging.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/device_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/formats_vk.h"
#include "impeller/renderer/backend/vulkan/pipeline_vk.h"
#include "impeller/renderer/backend/vulkan/sampler_vk.h"
#include "impeller/renderer/backend/vulkan/surface_producer_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "impeller/renderer/sampler.h"
#include "impeller/renderer/shader_types.h"
#include "vulkan/vulkan_enums.hpp"
#include "vulkan/vulkan_structs.hpp"
namespace impeller {
static uint32_t color_flash = 0;
RenderPassVK::RenderPassVK(
std::weak_ptr<const Context> context,
vk::Device device,
const RenderTarget& target,
std::shared_ptr<FencedCommandBufferVK> command_buffer,
vk::RenderPass render_pass)
: RenderPass(std::move(context), target),
device_(device),
command_buffer_(std::move(command_buffer)),
render_pass_(render_pass) {
is_valid_ = true;
}
RenderPassVK::~RenderPassVK() = default;
bool RenderPassVK::IsValid() const {
return is_valid_;
}
void RenderPassVK::OnSetLabel(std::string label) {
label_ = std::move(label);
}
bool RenderPassVK::OnEncodeCommands(const Context& context) const {
if (!IsValid()) {
return false;
}
const auto& render_target = GetRenderTarget();
if (!render_target.HasColorAttachment(0u)) {
return false;
}
vk::CommandBufferBeginInfo begin_info;
auto res = command_buffer_->Get().begin(begin_info);
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to begin command buffer: " << vk::to_string(res);
return false;
}
const auto& color0 = render_target.GetColorAttachments().at(0u);
const auto& depth0 = render_target.GetDepthAttachment();
const auto& stencil0 = render_target.GetStencilAttachment();
auto& texture = TextureVK::Cast(*color0.texture);
vk::Framebuffer framebuffer = CreateFrameBuffer(texture);
command_buffer_->GetDeletionQueue()->Push(
[device = device_, fbo = framebuffer]() {
device.destroyFramebuffer(fbo);
});
// layout transition.
if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal)) {
return false;
}
vk::ClearValue clear_value;
clear_value.color =
vk::ClearColorValue(std::array<float, 4>{0.0f, 0.0f, 0.0, 0.0f});
const auto& size = texture.GetTextureDescriptor().size;
vk::Rect2D render_area =
vk::Rect2D()
.setOffset(vk::Offset2D(0, 0))
.setExtent(vk::Extent2D(size.width, size.height));
auto rp_begin_info = vk::RenderPassBeginInfo()
.setRenderPass(render_pass_)
.setFramebuffer(framebuffer)
.setRenderArea(render_area)
.setClearValues(clear_value);
command_buffer_->Get().beginRenderPass(rp_begin_info,
vk::SubpassContents::eInline);
const auto& transients_allocator = context.GetResourceAllocator();
// encode the commands.
for (const auto& command : commands_) {
if (command.index_count == 0u) {
continue;
}
if (command.instance_count == 0u) {
continue;
}
if (!command.pipeline) {
continue;
}
if (!EncodeCommand(context, command)) {
return false;
}
}
if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined,
vk::ImageLayout::eColorAttachmentOptimal)) {
return false;
}
command_buffer_->Get().endRenderPass();
return const_cast<RenderPassVK*>(this)->EndCommandBuffer();
}
bool RenderPassVK::EndCommandBuffer() {
if (command_buffer_) {
auto res = command_buffer_->Get().end();
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
return false;
}
const auto& context_vk = GetContextVK();
command_buffer_->GetDeletionQueue()->Push(
[device = device_, render_pass = render_pass_]() {
device.destroyRenderPass(render_pass);
});
return true;
}
return false;
}
bool RenderPassVK::EncodeCommand(const Context& context,
const Command& command) const {
SetViewportAndScissor(command);
auto& pipeline_vk = PipelineVK::Cast(*command.pipeline);
PipelineCreateInfoVK* pipeline_create_info = pipeline_vk.GetCreateInfo();
if (!AllocateAndBindDescriptorSets(context, command, pipeline_create_info)) {
return false;
}
command_buffer_->Get().bindPipeline(vk::PipelineBindPoint::eGraphics,
pipeline_create_info->GetVKPipeline());
auto vertex_buffer_view = command.GetVertexBuffer();
auto index_buffer_view = command.index_buffer;
if (!vertex_buffer_view || !index_buffer_view) {
return false;
}
auto& allocator = *context.GetResourceAllocator();
const auto& pipeline_desc = command.pipeline->GetDescriptor();
auto vertex_buffer = vertex_buffer_view.buffer->GetDeviceBuffer(allocator);
auto index_buffer = index_buffer_view.buffer->GetDeviceBuffer(allocator);
if (!vertex_buffer || !index_buffer) {
VALIDATION_LOG << "Failed to acquire device buffers"
<< " for vertex and index buffer views";
return false;
}
// bind vertex buffer
auto vertex_buffer_handle =
DeviceBufferVK::Cast(*vertex_buffer).GetVKBufferHandle();
vk::Buffer vertex_buffers[] = {vertex_buffer_handle};
vk::DeviceSize vertex_buffer_offsets[] = {vertex_buffer_view.range.offset};
command_buffer_->Get().bindVertexBuffers(0, 1, vertex_buffers,
vertex_buffer_offsets);
// index buffer
auto index_buffer_handle =
DeviceBufferVK::Cast(*index_buffer).GetVKBufferHandle();
command_buffer_->Get().bindIndexBuffer(index_buffer_handle,
index_buffer_view.range.offset,
ToVKIndexType(command.index_type));
// execute draw
command_buffer_->Get().drawIndexed(command.index_count,
command.instance_count, 0, 0, 0);
return true;
}
bool RenderPassVK::AllocateAndBindDescriptorSets(
const Context& context,
const Command& command,
PipelineCreateInfoVK* pipeline_create_info) const {
auto& allocator = *context.GetResourceAllocator();
vk::PipelineLayout pipeline_layout =
pipeline_create_info->GetPipelineLayout();
const auto& context_vk = GetContextVK();
const auto& pool = context_vk.CreateDescriptorPool();
command_buffer_->GetDeletionQueue()->Push(
[pool = pool->GetPool(), device = device_]() {
device.destroyDescriptorPool(pool);
});
vk::DescriptorSetAllocateInfo alloc_info;
std::array<vk::DescriptorSetLayout, 1> dsls = {
pipeline_create_info->GetDescriptorSetLayout(),
};
alloc_info.setDescriptorPool(pool->GetPool());
alloc_info.setSetLayouts(dsls);
auto desc_sets_res = device_.allocateDescriptorSets(alloc_info);
if (desc_sets_res.result != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to allocate descriptor sets: "
<< vk::to_string(desc_sets_res.result);
return false;
}
auto desc_sets = desc_sets_res.value;
bool update_vertex_descriptors = UpdateDescriptorSets(
"vertex_bindings", command.vertex_bindings, allocator, desc_sets[0]);
if (!update_vertex_descriptors) {
return false;
}
bool update_frag_descriptors = UpdateDescriptorSets(
"fragment_bindings", command.fragment_bindings, allocator, desc_sets[0]);
if (!update_frag_descriptors) {
return false;
}
command_buffer_->Get().bindDescriptorSets(
vk::PipelineBindPoint::eGraphics, pipeline_layout, 0, desc_sets, nullptr);
return true;
}
bool RenderPassVK::UpdateDescriptorSets(const char* label,
const Bindings& bindings,
Allocator& allocator,
vk::DescriptorSet desc_set) const {
std::vector<vk::WriteDescriptorSet> writes;
std::vector<vk::DescriptorBufferInfo> buffer_infos;
std::vector<vk::DescriptorImageInfo> image_infos;
for (const auto& [buffer_index, view] : bindings.buffers) {
const auto& buffer_view = view.resource.buffer;
auto device_buffer = buffer_view->GetDeviceBuffer(allocator);
if (!device_buffer) {
VALIDATION_LOG << "Failed to get device buffer for vertex binding";
return false;
}
auto buffer = DeviceBufferVK::Cast(*device_buffer).GetVKBufferHandle();
if (!buffer) {
return false;
}
// reserved index used for per-vertex data.
if (buffer_index == VertexDescriptor::kReservedVertexBufferIndex) {
continue;
}
uint32_t offset = view.resource.range.offset;
vk::DescriptorBufferInfo desc_buffer_info;
desc_buffer_info.setBuffer(buffer);
desc_buffer_info.setOffset(offset);
desc_buffer_info.setRange(view.resource.range.length);
buffer_infos.push_back(desc_buffer_info);
const ShaderUniformSlot& uniform = bindings.uniforms.at(buffer_index);
vk::WriteDescriptorSet setWrite;
setWrite.setDstSet(desc_set);
setWrite.setDstBinding(uniform.binding);
setWrite.setDescriptorCount(1);
setWrite.setDescriptorType(vk::DescriptorType::eUniformBuffer);
setWrite.setPBufferInfo(&buffer_infos.back());
writes.push_back(setWrite);
}
for (const auto& [index, sampler_handle] : bindings.samplers) {
if (bindings.textures.find(index) == bindings.textures.end()) {
VALIDATION_LOG << "Missing texture for sampler: " << index;
return false;
}
const auto& texture_vk =
TextureVK::Cast(*bindings.textures.at(index).resource);
const Sampler& sampler = *sampler_handle.resource;
const SamplerVK& sampler_vk = SamplerVK::Cast(sampler);
const SampledImageSlot& slot = bindings.sampled_images.at(index);
if (!TransitionImageLayout(texture_vk.GetImage(),
vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal)) {
return false;
}
CopyBufferToImage(texture_vk);
if (!TransitionImageLayout(texture_vk.GetImage(),
vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eShaderReadOnlyOptimal)) {
return false;
}
vk::DescriptorImageInfo desc_image_info;
desc_image_info.setImageLayout(vk::ImageLayout::eShaderReadOnlyOptimal);
desc_image_info.setSampler(sampler_vk.GetSamplerVK());
desc_image_info.setImageView(texture_vk.GetImageView());
image_infos.push_back(desc_image_info);
vk::WriteDescriptorSet setWrite;
setWrite.setDstSet(desc_set);
setWrite.setDstBinding(slot.binding);
setWrite.setDescriptorCount(1);
setWrite.setDescriptorType(vk::DescriptorType::eCombinedImageSampler);
setWrite.setPImageInfo(&image_infos.back());
writes.push_back(setWrite);
}
std::array<vk::CopyDescriptorSet, 0> copies;
device_.updateDescriptorSets(writes, copies);
return true;
}
void RenderPassVK::SetViewportAndScissor(const Command& command) const {
// set viewport.
const auto& vp = command.viewport.value_or<Viewport>(
{.rect = Rect::MakeSize(GetRenderTargetSize())});
vk::Viewport viewport = vk::Viewport()
.setWidth(vp.rect.size.width)
.setHeight(-vp.rect.size.height)
.setY(vp.rect.size.height)
.setMinDepth(0.0f)
.setMaxDepth(1.0f);
command_buffer_->Get().setViewport(0, 1, &viewport);
// scissor
const auto& sc =
command.scissor.value_or(IRect::MakeSize(GetRenderTargetSize()));
vk::Rect2D scissor =
vk::Rect2D()
.setOffset(vk::Offset2D(sc.origin.x, sc.origin.y))
.setExtent(vk::Extent2D(sc.size.width, sc.size.height));
command_buffer_->Get().setScissor(0, 1, &scissor);
}
vk::Framebuffer RenderPassVK::CreateFrameBuffer(
const TextureVK& texture) const {
auto img_view = texture.GetImageView();
auto size = texture.GetTextureDescriptor().size;
vk::FramebufferCreateInfo fb_create_info = vk::FramebufferCreateInfo()
.setRenderPass(render_pass_)
.setAttachmentCount(1)
.setPAttachments(&img_view)
.setWidth(size.width)
.setHeight(size.height)
.setLayers(1);
auto res = device_.createFramebuffer(fb_create_info);
FML_CHECK(res.result == vk::Result::eSuccess);
return res.value;
}
bool RenderPassVK::TransitionImageLayout(vk::Image image,
vk::ImageLayout layout_old,
vk::ImageLayout layout_new) const {
auto transition_cmd = command_buffer_->GetSingleUseChild();
vk::CommandBufferBeginInfo begin_info;
begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
auto res = transition_cmd.begin(begin_info);
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to begin command buffer: " << vk::to_string(res);
return false;
}
vk::ImageMemoryBarrier barrier =
vk::ImageMemoryBarrier()
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite |
vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentRead |
vk::AccessFlagBits::eShaderRead)
.setOldLayout(layout_old)
.setNewLayout(layout_new)
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED)
.setImage(image)
.setSubresourceRange(
vk::ImageSubresourceRange()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setBaseMipLevel(0)
.setLevelCount(1)
.setBaseArrayLayer(0)
.setLayerCount(1));
transition_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eAllGraphics,
vk::PipelineStageFlagBits::eAllGraphics, {},
nullptr, nullptr, barrier);
res = transition_cmd.end();
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
return false;
}
return true;
}
bool RenderPassVK::CopyBufferToImage(const TextureVK& texture_vk) const {
auto copy_cmd = command_buffer_->GetSingleUseChild();
const auto& size = texture_vk.GetTextureDescriptor().size;
// actual copy happens here
vk::BufferImageCopy region =
vk::BufferImageCopy()
.setBufferOffset(0)
.setBufferRowLength(0)
.setBufferImageHeight(0)
.setImageSubresource(
vk::ImageSubresourceLayers()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setMipLevel(0)
.setBaseArrayLayer(0)
.setLayerCount(1))
.setImageOffset(vk::Offset3D(0, 0, 0))
.setImageExtent(vk::Extent3D(size.width, size.height, 1));
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;
}
copy_cmd.copyBufferToImage(texture_vk.GetStagingBuffer(),
texture_vk.GetImage(),
vk::ImageLayout::eTransferDstOptimal, region);
res = copy_cmd.end();
if (res != vk::Result::eSuccess) {
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
return false;
}
return true;
}
const ContextVK& RenderPassVK::GetContextVK() const {
if (auto context = context_.lock()) {
const auto& context_vk = ContextVK::Cast(*context);
return context_vk;
}
FML_UNREACHABLE();
}
} // namespace impeller