blob: 676980689ce652b783544107d2952ce57f822592 [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/binding_helpers_vk.h"
#include "fml/status.h"
#include "impeller/core/shader_types.h"
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
#include "impeller/renderer/backend/vulkan/command_pool_vk.h"
#include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/sampler_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/compute_command.h"
#include "vulkan/vulkan_core.h"
namespace impeller {
// Warning: if any of the constant values or layouts are changed in the
// framebuffer fetch shader, then this input binding may need to be
// manually changed.
static constexpr size_t kMagicSubpassInputBinding = 64;
static bool BindImages(const Bindings& bindings,
Allocator& allocator,
const std::shared_ptr<CommandEncoderVK>& encoder,
vk::DescriptorSet& vk_desc_set,
std::vector<vk::DescriptorImageInfo>& images,
std::vector<vk::WriteDescriptorSet>& writes) {
for (const TextureAndSampler& data : bindings.sampled_images) {
auto texture = data.texture.resource;
const auto& texture_vk = TextureVK::Cast(*texture);
const SamplerVK& sampler = SamplerVK::Cast(*data.sampler);
if (!encoder->Track(texture) ||
!encoder->Track(sampler.GetSharedSampler())) {
return false;
}
const SampledImageSlot& slot = data.slot;
vk::DescriptorImageInfo image_info;
image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
image_info.sampler = sampler.GetSampler();
image_info.imageView = texture_vk.GetImageView();
images.push_back(image_info);
vk::WriteDescriptorSet write_set;
write_set.dstSet = vk_desc_set;
write_set.dstBinding = slot.binding;
write_set.descriptorCount = 1u;
write_set.descriptorType = vk::DescriptorType::eCombinedImageSampler;
write_set.pImageInfo = &images.back();
writes.push_back(write_set);
}
return true;
};
static bool BindBuffers(const Bindings& bindings,
Allocator& allocator,
const std::shared_ptr<CommandEncoderVK>& encoder,
vk::DescriptorSet& vk_desc_set,
const std::vector<DescriptorSetLayout>& desc_set,
std::vector<vk::DescriptorBufferInfo>& buffers,
std::vector<vk::WriteDescriptorSet>& writes) {
for (const BufferAndUniformSlot& data : bindings.buffers) {
const auto& buffer_view = data.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).GetBuffer();
if (!buffer) {
return false;
}
if (!encoder->Track(device_buffer)) {
return false;
}
uint32_t offset = data.view.resource.range.offset;
vk::DescriptorBufferInfo buffer_info;
buffer_info.buffer = buffer;
buffer_info.offset = offset;
buffer_info.range = data.view.resource.range.length;
buffers.push_back(buffer_info);
// TODO(jonahwilliams): remove this part by storing more data in
// ShaderUniformSlot.
const ShaderUniformSlot& uniform = data.slot;
auto layout_it =
std::find_if(desc_set.begin(), desc_set.end(),
[&uniform](const DescriptorSetLayout& layout) {
return layout.binding == uniform.binding;
});
if (layout_it == desc_set.end()) {
VALIDATION_LOG << "Failed to get descriptor set layout for binding "
<< uniform.binding;
return false;
}
auto layout = *layout_it;
vk::WriteDescriptorSet write_set;
write_set.dstSet = vk_desc_set;
write_set.dstBinding = uniform.binding;
write_set.descriptorCount = 1u;
write_set.descriptorType = ToVKDescriptorType(layout.descriptor_type);
write_set.pBufferInfo = &buffers.back();
writes.push_back(write_set);
}
return true;
}
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
const ContextVK& context,
const std::shared_ptr<CommandEncoderVK>& encoder,
const std::vector<Command>& commands,
const TextureVK& input_attachment) {
if (commands.empty()) {
return std::vector<vk::DescriptorSet>{};
}
// Step 1: Determine the total number of buffer and sampler descriptor
// sets required. Collect this information along with the layout information
// to allocate a correctly sized descriptor pool.
size_t buffer_count = 0;
size_t samplers_count = 0;
size_t subpass_count = 0;
std::vector<vk::DescriptorSetLayout> layouts;
layouts.reserve(commands.size());
for (const auto& command : commands) {
buffer_count += command.vertex_bindings.buffers.size();
buffer_count += command.fragment_bindings.buffers.size();
samplers_count += command.fragment_bindings.sampled_images.size();
subpass_count +=
command.pipeline->GetDescriptor().UsesSubpassInput() ? 1 : 0;
layouts.emplace_back(
PipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout());
}
auto descriptor_result = encoder->AllocateDescriptorSets(
buffer_count, samplers_count, subpass_count, layouts);
if (!descriptor_result.ok()) {
return descriptor_result.status();
}
auto descriptor_sets = descriptor_result.value();
if (descriptor_sets.empty()) {
return fml::Status();
}
// Step 2: Update the descriptors for all image and buffer descriptors used
// in the render pass.
std::vector<vk::DescriptorImageInfo> images;
std::vector<vk::DescriptorBufferInfo> buffers;
std::vector<vk::WriteDescriptorSet> writes;
images.reserve(samplers_count + subpass_count);
buffers.reserve(buffer_count);
writes.reserve(samplers_count + buffer_count + subpass_count);
auto& allocator = *context.GetResourceAllocator();
auto desc_index = 0u;
for (const auto& command : commands) {
auto desc_set = command.pipeline->GetDescriptor()
.GetVertexDescriptor()
->GetDescriptorSetLayouts();
if (!BindBuffers(command.vertex_bindings, allocator, encoder,
descriptor_sets[desc_index], desc_set, buffers, writes) ||
!BindBuffers(command.fragment_bindings, allocator, encoder,
descriptor_sets[desc_index], desc_set, buffers, writes) ||
!BindImages(command.fragment_bindings, allocator, encoder,
descriptor_sets[desc_index], images, writes)) {
return fml::Status(fml::StatusCode::kUnknown,
"Failed to bind texture or buffer.");
}
if (command.pipeline->GetDescriptor().UsesSubpassInput()) {
vk::DescriptorImageInfo image_info;
image_info.imageLayout = vk::ImageLayout::eGeneral;
image_info.sampler = VK_NULL_HANDLE;
image_info.imageView = input_attachment.GetImageView();
images.push_back(image_info);
vk::WriteDescriptorSet write_set;
write_set.dstSet = descriptor_sets[desc_index];
write_set.dstBinding = kMagicSubpassInputBinding;
write_set.descriptorCount = 1u;
write_set.descriptorType = vk::DescriptorType::eInputAttachment;
write_set.pImageInfo = &images.back();
writes.push_back(write_set);
}
desc_index += 1;
}
context.GetDevice().updateDescriptorSets(writes, {});
return descriptor_sets;
}
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
const ContextVK& context,
const std::shared_ptr<CommandEncoderVK>& encoder,
const std::vector<ComputeCommand>& commands) {
if (commands.empty()) {
return std::vector<vk::DescriptorSet>{};
}
// Step 1: Determine the total number of buffer and sampler descriptor
// sets required. Collect this information along with the layout information
// to allocate a correctly sized descriptor pool.
size_t buffer_count = 0;
size_t samplers_count = 0;
std::vector<vk::DescriptorSetLayout> layouts;
layouts.reserve(commands.size());
for (const auto& command : commands) {
buffer_count += command.bindings.buffers.size();
samplers_count += command.bindings.sampled_images.size();
layouts.emplace_back(
ComputePipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout());
}
auto descriptor_result =
encoder->AllocateDescriptorSets(buffer_count, samplers_count, 0, layouts);
if (!descriptor_result.ok()) {
return descriptor_result.status();
}
auto descriptor_sets = descriptor_result.value();
if (descriptor_sets.empty()) {
return fml::Status();
}
// Step 2: Update the descriptors for all image and buffer descriptors used
// in the render pass.
std::vector<vk::DescriptorImageInfo> images;
std::vector<vk::DescriptorBufferInfo> buffers;
std::vector<vk::WriteDescriptorSet> writes;
images.reserve(samplers_count);
buffers.reserve(buffer_count);
writes.reserve(samplers_count + buffer_count);
auto& allocator = *context.GetResourceAllocator();
auto desc_index = 0u;
for (const auto& command : commands) {
auto desc_set = command.pipeline->GetDescriptor().GetDescriptorSetLayouts();
if (!BindBuffers(command.bindings, allocator, encoder,
descriptor_sets[desc_index], desc_set, buffers, writes) ||
!BindImages(command.bindings, allocator, encoder,
descriptor_sets[desc_index], images, writes)) {
return fml::Status(fml::StatusCode::kUnknown,
"Failed to bind texture or buffer.");
}
desc_index += 1;
}
context.GetDevice().updateDescriptorSets(writes, {});
return descriptor_sets;
}
} // namespace impeller