[Impeller] Allocate exact descriptor count, populate in one go. (flutter/engine#47200)

Rather than doing a guess and check, since we have all of our cmds already stored we can add up the binding counts and allocate the exact descriptor size and populate them in one call.

Also makes render_pass and compute_pass share more (though not all) code.
This commit is contained in:
Jonah Williams
2023-10-24 12:22:38 -07:00
committed by GitHub
parent ce34c72049
commit 792bf6d2e2
13 changed files with 395 additions and 409 deletions

View File

@@ -2153,6 +2153,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/android_hardware_buffe
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/android_hardware_buffer_texture_source_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/blit_pass_vk.cc + ../../../flutter/LICENSE
@@ -4925,6 +4927,8 @@ FILE: ../../../flutter/impeller/renderer/backend/vulkan/android_hardware_buffer_
FILE: ../../../flutter/impeller/renderer/backend/vulkan/android_hardware_buffer_texture_source_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.cc
FILE: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.cc
FILE: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.cc
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.h
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_pass_vk.cc

View File

@@ -7,6 +7,7 @@
#include <optional>
#include "flutter/fml/logging.h"
#include "flutter/fml/status.h"
namespace fml {

View File

@@ -34,6 +34,8 @@ impeller_component("vulkan") {
"android_hardware_buffer_texture_source_vk.h",
"barrier_vk.cc",
"barrier_vk.h",
"binding_helpers_vk.cc",
"binding_helpers_vk.h",
"blit_command_vk.cc",
"blit_command_vk.h",
"blit_pass_vk.cc",

View File

@@ -0,0 +1,242 @@
// 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/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/backend/vulkan/vk.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/compute_command.h"
namespace impeller {
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 auto& [index, data] : bindings.sampled_images) {
auto texture = data.texture.resource;
const auto& texture_vk = TextureVK::Cast(*texture);
const SamplerVK& sampler = SamplerVK::Cast(*data.sampler.resource);
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 auto& [buffer_index, 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) {
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.vertex_bindings.buffers.size();
buffer_count += command.fragment_bindings.buffers.size();
samplers_count += command.fragment_bindings.sampled_images.size();
layouts.emplace_back(
PipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout());
}
auto descriptor_result =
encoder->AllocateDescriptorSets(buffer_count, samplers_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);
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()
.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.");
}
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, 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

View File

@@ -0,0 +1,26 @@
// 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.
#pragma once
#include <vector>
#include "fml/status_or.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/compute_command.h"
namespace impeller {
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
const ContextVK& context,
const std::shared_ptr<CommandEncoderVK>& encoder,
const std::vector<Command>& commands);
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
const ContextVK& context,
const std::shared_ptr<CommandEncoderVK>& encoder,
const std::vector<ComputeCommand>& commands);
} // namespace impeller

View File

@@ -5,6 +5,8 @@
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
#include "flutter/fml/closure.h"
#include "fml/status.h"
#include "fml/status_or.h"
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h"
#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"
@@ -294,15 +296,17 @@ bool CommandEncoderVK::IsTracking(
return tracked_objects_->IsTracking(source);
}
std::optional<vk::DescriptorSet> CommandEncoderVK::AllocateDescriptorSet(
const vk::DescriptorSetLayout& layout,
size_t command_count) {
fml::StatusOr<std::vector<vk::DescriptorSet>>
CommandEncoderVK::AllocateDescriptorSets(
uint32_t buffer_count,
uint32_t sampler_count,
const std::vector<vk::DescriptorSetLayout>& layouts) {
if (!IsValid()) {
return std::nullopt;
return fml::Status(fml::StatusCode::kUnknown, "command encoder invalid");
}
return tracked_objects_->GetDescriptorPool().AllocateDescriptorSet(
layout, command_count);
return tracked_objects_->GetDescriptorPool().AllocateDescriptorSets(
buffer_count, sampler_count, layouts);
}
void CommandEncoderVK::PushDebugGroup(const char* label) const {

View File

@@ -79,9 +79,10 @@ class CommandEncoderVK {
void InsertDebugMarker(const char* label) const;
std::optional<vk::DescriptorSet> AllocateDescriptorSet(
const vk::DescriptorSetLayout& layout,
size_t command_count);
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateDescriptorSets(
uint32_t buffer_count,
uint32_t sampler_count,
const std::vector<vk::DescriptorSetLayout>& layouts);
private:
friend class ContextVK;

View File

@@ -5,9 +5,9 @@
#include "impeller/renderer/backend/vulkan/compute_pass_vk.h"
#include "flutter/fml/trace_event.h"
#include "impeller/renderer/backend/vulkan/binding_helpers_vk.h"
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h"
#include "impeller/renderer/backend/vulkan/sampler_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
namespace impeller {
@@ -66,130 +66,6 @@ static bool UpdateBindingLayouts(const std::vector<ComputeCommand>& commands,
return true;
}
static bool AllocateAndBindDescriptorSets(const ContextVK& context,
const ComputeCommand& command,
CommandEncoderVK& encoder,
const ComputePipelineVK& pipeline,
size_t command_count) {
auto desc_set = pipeline.GetDescriptor().GetDescriptorSetLayouts();
auto vk_desc_set = encoder.AllocateDescriptorSet(
pipeline.GetDescriptorSetLayout(), command_count);
if (!vk_desc_set) {
return false;
}
auto& allocator = *context.GetResourceAllocator();
std::unordered_map<uint32_t, vk::DescriptorBufferInfo> buffers;
std::unordered_map<uint32_t, vk::DescriptorImageInfo> images;
std::vector<vk::WriteDescriptorSet> writes;
auto bind_images = [&encoder, //
&images, //
&writes, //
&vk_desc_set //
](const Bindings& bindings) -> bool {
for (const auto& [index, data] : bindings.sampled_images) {
auto texture = data.texture.resource;
const auto& texture_vk = TextureVK::Cast(*texture);
const SamplerVK& sampler = SamplerVK::Cast(*data.sampler.resource);
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();
vk::WriteDescriptorSet write_set;
write_set.dstSet = vk_desc_set.value();
write_set.dstBinding = slot.binding;
write_set.descriptorCount = 1u;
write_set.descriptorType = vk::DescriptorType::eCombinedImageSampler;
write_set.pImageInfo = &(images[slot.binding] = image_info);
writes.push_back(write_set);
}
return true;
};
auto bind_buffers = [&allocator, //
&encoder, //
&buffers, //
&writes, //
&desc_set, //
&vk_desc_set //
](const Bindings& bindings) -> bool {
for (const auto& [buffer_index, 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;
const ShaderUniformSlot& uniform = data.slot;
auto layout_it = std::find_if(desc_set.begin(), desc_set.end(),
[&uniform](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.value();
write_set.dstBinding = uniform.binding;
write_set.descriptorCount = 1u;
write_set.descriptorType = ToVKDescriptorType(layout.descriptor_type);
write_set.pBufferInfo = &(buffers[uniform.binding] = buffer_info);
writes.push_back(write_set);
}
return true;
};
if (!bind_buffers(command.bindings) || !bind_images(command.bindings)) {
return false;
}
context.GetDevice().updateDescriptorSets(writes, {});
encoder.GetCommandBuffer().bindDescriptorSets(
vk::PipelineBindPoint::eCompute, // bind point
pipeline.GetPipelineLayout(), // layout
0, // first set
{vk::DescriptorSet{*vk_desc_set}}, // sets
nullptr // offsets
);
return true;
}
bool ComputePassVK::OnEncodeCommands(const Context& context,
const ISize& grid_size,
const ISize& thread_group_size) const {
@@ -224,54 +100,54 @@ bool ComputePassVK::OnEncodeCommands(const Context& context,
VALIDATION_LOG << "Could not update binding layouts for compute pass.";
return false;
}
auto desc_sets_result =
AllocateAndBindDescriptorSets(vk_context, encoder, commands_);
if (!desc_sets_result.ok()) {
return false;
}
auto desc_sets = desc_sets_result.value();
{
TRACE_EVENT0("impeller", "EncodeComputePassCommands");
TRACE_EVENT0("impeller", "EncodeComputePassCommands");
size_t desc_index = 0;
for (const auto& command : commands_) {
const auto& pipeline_vk = ComputePipelineVK::Cast(*command.pipeline);
for (const auto& command : commands_) {
if (!command.pipeline) {
continue;
cmd_buffer.bindPipeline(vk::PipelineBindPoint::eCompute,
pipeline_vk.GetPipeline());
cmd_buffer.bindDescriptorSets(
vk::PipelineBindPoint::eCompute, // bind point
pipeline_vk.GetPipelineLayout(), // layout
0, // first set
{vk::DescriptorSet{desc_sets[desc_index]}}, // sets
nullptr // offsets
);
// TOOD(dnfield): This should be moved to caps. But for now keeping this
// in parallel with Metal.
auto device_properties = vk_context.GetPhysicalDevice().getProperties();
auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize;
int64_t width = grid_size.width;
int64_t height = grid_size.height;
// Special case for linear processing.
if (height == 1) {
int64_t minimum = 1;
int64_t threadGroups = std::max(
static_cast<int64_t>(std::ceil(width * 1.0 / max_wg_size[0] * 1.0)),
minimum);
cmd_buffer.dispatch(threadGroups, 1, 1);
} else {
while (width > max_wg_size[0]) {
width = std::max(static_cast<int64_t>(1), width / 2);
}
const auto& pipeline_vk = ComputePipelineVK::Cast(*command.pipeline);
cmd_buffer.bindPipeline(vk::PipelineBindPoint::eCompute,
pipeline_vk.GetPipeline());
if (!AllocateAndBindDescriptorSets(vk_context, //
command, //
*encoder, //
pipeline_vk, //
commands_.size() //
)) {
return false;
}
// TOOD(dnfield): This should be moved to caps. But for now keeping this
// in parallel with Metal.
auto device_properties = vk_context.GetPhysicalDevice().getProperties();
auto max_wg_size = device_properties.limits.maxComputeWorkGroupSize;
int64_t width = grid_size.width;
int64_t height = grid_size.height;
// Special case for linear processing.
if (height == 1) {
int64_t minimum = 1;
int64_t threadGroups = std::max(
static_cast<int64_t>(std::ceil(width * 1.0 / max_wg_size[0] * 1.0)),
minimum);
cmd_buffer.dispatch(threadGroups, 1, 1);
} else {
while (width > max_wg_size[0]) {
width = std::max(static_cast<int64_t>(1), width / 2);
}
while (height > max_wg_size[1]) {
height = std::max(static_cast<int64_t>(1), height / 2);
}
cmd_buffer.dispatch(width, height, 1);
while (height > max_wg_size[1]) {
height = std::max(static_cast<int64_t>(1), height / 2);
}
cmd_buffer.dispatch(width, height, 1);
}
desc_index += 1;
}
return true;

View File

@@ -5,7 +5,6 @@
#include "impeller/renderer/backend/vulkan/descriptor_pool_vk.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/allocation.h"
#include "impeller/base/validation.h"
namespace impeller {
@@ -19,17 +18,23 @@ DescriptorPoolVK::DescriptorPoolVK(
DescriptorPoolVK::~DescriptorPoolVK() = default;
static vk::UniqueDescriptorPool CreatePool(const vk::Device& device,
uint32_t pool_count) {
uint32_t image_count,
uint32_t buffer_count) {
TRACE_EVENT0("impeller", "CreateDescriptorPool");
std::vector<vk::DescriptorPoolSize> pools = {
{vk::DescriptorType::eCombinedImageSampler, pool_count},
{vk::DescriptorType::eUniformBuffer, pool_count},
{vk::DescriptorType::eStorageBuffer, pool_count}};
std::vector<vk::DescriptorPoolSize> pools = {};
if (image_count > 0) {
pools.emplace_back(vk::DescriptorPoolSize{
vk::DescriptorType::eCombinedImageSampler, image_count});
}
if (buffer_count > 0) {
pools.emplace_back(vk::DescriptorPoolSize{
vk::DescriptorType::eUniformBuffer, buffer_count});
pools.emplace_back(vk::DescriptorPoolSize{
vk::DescriptorType::eStorageBuffer, buffer_count});
}
vk::DescriptorPoolCreateInfo pool_info;
pool_info.setMaxSets(pools.size() * pool_count);
pool_info.setMaxSets(image_count + buffer_count);
pool_info.setPoolSizes(pools);
auto [result, pool] = device.createDescriptorPoolUnique(pool_info);
if (result != vk::Result::eSuccess) {
VALIDATION_LOG << "Unable to create a descriptor pool";
@@ -37,61 +42,36 @@ static vk::UniqueDescriptorPool CreatePool(const vk::Device& device,
return std::move(pool);
}
std::optional<vk::DescriptorSet> DescriptorPoolVK::AllocateDescriptorSet(
const vk::DescriptorSetLayout& layout,
size_t command_count) {
if (pools_.empty()) {
pool_size_ = command_count;
}
return AllocateDescriptorSet(layout);
}
std::optional<vk::DescriptorSet> DescriptorPoolVK::AllocateDescriptorSet(
const vk::DescriptorSetLayout& layout) {
auto pool = GetDescriptorPool();
if (!pool) {
return std::nullopt;
}
vk::DescriptorSetAllocateInfo set_info;
set_info.setDescriptorPool(pool.value());
set_info.setSetLayouts(layout);
fml::StatusOr<std::vector<vk::DescriptorSet>>
DescriptorPoolVK::AllocateDescriptorSets(
uint32_t buffer_count,
uint32_t sampler_count,
const std::vector<vk::DescriptorSetLayout>& layouts) {
std::shared_ptr<const DeviceHolder> strong_device = device_holder_.lock();
if (!strong_device) {
return std::nullopt;
return fml::Status(fml::StatusCode::kUnknown, "No device");
}
auto new_pool =
CreatePool(strong_device->GetDevice(), sampler_count, buffer_count);
if (!new_pool) {
return fml::Status(fml::StatusCode::kUnknown,
"Failed to create descriptor pool");
}
pool_ = std::move(new_pool);
vk::DescriptorSetAllocateInfo set_info;
set_info.setDescriptorPool(pool_.get());
set_info.setSetLayouts(layouts);
auto [result, sets] =
strong_device->GetDevice().allocateDescriptorSets(set_info);
if (result == vk::Result::eErrorOutOfPoolMemory) {
return GrowPool() ? AllocateDescriptorSet(layout) : std::nullopt;
}
if (result != vk::Result::eSuccess) {
VALIDATION_LOG << "Could not allocate descriptor sets: "
<< vk::to_string(result);
return std::nullopt;
return fml::Status(fml::StatusCode::kUnknown, "");
}
return sets[0];
}
std::optional<vk::DescriptorPool> DescriptorPoolVK::GetDescriptorPool() {
if (pools_.empty()) {
return GrowPool() ? GetDescriptorPool() : std::nullopt;
}
return *pools_.back();
}
bool DescriptorPoolVK::GrowPool() {
const auto new_pool_size = Allocation::NextPowerOfTwoSize(pool_size_ + 1u);
std::shared_ptr<const DeviceHolder> strong_device = device_holder_.lock();
if (!strong_device) {
return false;
}
auto new_pool = CreatePool(strong_device->GetDevice(), new_pool_size);
if (!new_pool) {
return false;
}
pool_size_ = new_pool_size;
pools_.push(std::move(new_pool));
return true;
return sets;
}
} // namespace impeller

View File

@@ -4,17 +4,14 @@
#pragma once
#include <optional>
#include <queue>
#include "flutter/fml/macros.h"
#include "fml/status_or.h"
#include "impeller/renderer/backend/vulkan/device_holder.h"
#include "impeller/renderer/backend/vulkan/vk.h"
namespace impeller {
//------------------------------------------------------------------------------
/// @brief A short-lived dynamically-sized descriptor pool. Descriptors
/// @brief A short-lived fixed-sized descriptor pool. Descriptors
/// from this pool don't need to be freed individually. Instead, the
/// pool must be collected after all the descriptors allocated from
/// it are done being used.
@@ -24,7 +21,6 @@ namespace impeller {
///
/// Encoders create pools as necessary as they have the same
/// threading and lifecycle restrictions.
///
class DescriptorPoolVK {
public:
explicit DescriptorPoolVK(
@@ -32,21 +28,14 @@ class DescriptorPoolVK {
~DescriptorPoolVK();
std::optional<vk::DescriptorSet> AllocateDescriptorSet(
const vk::DescriptorSetLayout& layout,
size_t command_count);
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateDescriptorSets(
uint32_t buffer_count,
uint32_t sampler_count,
const std::vector<vk::DescriptorSetLayout>& layouts);
private:
std::optional<vk::DescriptorSet> AllocateDescriptorSet(
const vk::DescriptorSetLayout& layout);
std::weak_ptr<const DeviceHolder> device_holder_;
uint32_t pool_size_ = 31u;
std::queue<vk::UniqueDescriptorPool> pools_;
std::optional<vk::DescriptorPool> GetDescriptorPool();
bool GrowPool();
vk::UniqueDescriptorPool pool_ = {};
FML_DISALLOW_COPY_AND_ASSIGN(DescriptorPoolVK);
};

View File

@@ -4,7 +4,6 @@
#include "impeller/renderer/backend/vulkan/device_buffer_vk.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
namespace impeller {

View File

@@ -8,22 +8,20 @@
#include <cstdint>
#include <vector>
#include "flutter/fml/logging.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/core/formats.h"
#include "impeller/core/sampler.h"
#include "impeller/core/shader_types.h"
#include "impeller/renderer/backend/vulkan/barrier_vk.h"
#include "impeller/renderer/backend/vulkan/binding_helpers_vk.h"
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
#include "impeller/renderer/backend/vulkan/command_encoder_vk.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/shared_object_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "vulkan/vulkan_handles.hpp"
#include "vulkan/vulkan_to_string.hpp"
namespace impeller {
@@ -323,142 +321,6 @@ static bool UpdateBindingLayouts(const std::vector<Command>& commands,
return true;
}
static bool AllocateAndBindDescriptorSets(const ContextVK& context,
const Command& command,
CommandEncoderVK& encoder,
const PipelineVK& pipeline,
size_t command_count) {
auto desc_set =
pipeline.GetDescriptor().GetVertexDescriptor()->GetDescriptorSetLayouts();
auto vk_desc_set = encoder.AllocateDescriptorSet(
pipeline.GetDescriptorSetLayout(), command_count);
if (!vk_desc_set) {
return false;
}
auto& allocator = *context.GetResourceAllocator();
std::vector<vk::DescriptorImageInfo> images;
std::vector<vk::DescriptorBufferInfo> buffers;
std::vector<vk::WriteDescriptorSet> writes;
writes.reserve(command.vertex_bindings.buffers.size() +
command.fragment_bindings.buffers.size() +
command.fragment_bindings.sampled_images.size());
images.reserve(command.fragment_bindings.sampled_images.size());
buffers.reserve(command.vertex_bindings.buffers.size() +
command.fragment_bindings.buffers.size());
auto bind_images = [&encoder, //
&images, //
&writes, //
&vk_desc_set //
](const Bindings& bindings) -> bool {
for (const auto& [index, data] : bindings.sampled_images) {
auto texture = data.texture.resource;
const auto& texture_vk = TextureVK::Cast(*texture);
const SamplerVK& sampler = SamplerVK::Cast(*data.sampler.resource);
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.value();
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;
};
auto bind_buffers = [&allocator, //
&encoder, //
&buffers, //
&writes, //
&desc_set, //
&vk_desc_set //
](const Bindings& bindings) -> bool {
for (const auto& [buffer_index, 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);
const ShaderUniformSlot& uniform = data.slot;
auto layout_it = std::find_if(desc_set.begin(), desc_set.end(),
[&uniform](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.value();
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;
};
if (!bind_buffers(command.vertex_bindings) ||
!bind_buffers(command.fragment_bindings) ||
!bind_images(command.fragment_bindings)) {
return false;
}
context.GetDevice().updateDescriptorSets(writes, {});
encoder.GetCommandBuffer().bindDescriptorSets(
vk::PipelineBindPoint::eGraphics, // bind point
pipeline.GetPipelineLayout(), // layout
0, // first set
{vk::DescriptorSet{*vk_desc_set}}, // sets
nullptr // offsets
);
return true;
}
static void SetViewportAndScissor(const Command& command,
const vk::CommandBuffer& cmd_buffer,
PassBindingsCache& cmd_buffer_cache,
@@ -488,7 +350,7 @@ static bool EncodeCommand(const Context& context,
CommandEncoderVK& encoder,
PassBindingsCache& command_buffer_cache,
const ISize& target_size,
size_t command_count) {
const vk::DescriptorSet vk_desc_set) {
if (command.vertex_count == 0u || command.instance_count == 0u) {
return true;
}
@@ -504,17 +366,15 @@ static bool EncodeCommand(const Context& context,
#endif // IMPELLER_DEBUG
const auto& cmd_buffer = encoder.GetCommandBuffer();
const auto& pipeline_vk = PipelineVK::Cast(*command.pipeline);
if (!AllocateAndBindDescriptorSets(ContextVK::Cast(context), //
command, //
encoder, //
pipeline_vk, //
command_count //
)) {
return false;
}
encoder.GetCommandBuffer().bindDescriptorSets(
vk::PipelineBindPoint::eGraphics, // bind point
pipeline_vk.GetPipelineLayout(), // layout
0, // first set
{vk::DescriptorSet{vk_desc_set}}, // sets
nullptr // offsets
);
command_buffer_cache.BindPipeline(
cmd_buffer, vk::PipelineBindPoint::eGraphics, pipeline_vk.GetPipeline());
@@ -661,6 +521,13 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
static_cast<uint32_t>(target_size.height);
pass_info.setClearValues(clear_values);
auto desc_sets_result =
AllocateAndBindDescriptorSets(vk_context, encoder, commands_);
if (!desc_sets_result.ok()) {
return false;
}
auto desc_sets = desc_sets_result.value();
{
TRACE_EVENT0("impeller", "EncodeRenderPassCommands");
cmd_buffer.beginRenderPass(pass_info, vk::SubpassContents::eInline);
@@ -668,15 +535,13 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
fml::ScopedCleanupClosure end_render_pass(
[cmd_buffer]() { cmd_buffer.endRenderPass(); });
auto desc_index = 0u;
for (const auto& command : commands_) {
if (!command.pipeline) {
continue;
}
if (!EncodeCommand(context, command, *encoder, pass_bindings_cache_,
target_size, commands_.size())) {
target_size, desc_sets[desc_index])) {
return false;
}
desc_index += 1;
}
}

View File

@@ -8,9 +8,6 @@
#include "impeller/renderer/backend/vulkan/context_vk.h"
#include "impeller/renderer/backend/vulkan/pass_bindings_cache.h"
#include "impeller/renderer/backend/vulkan/shared_object_vk.h"
#include "impeller/renderer/backend/vulkan/texture_vk.h"
#include "impeller/renderer/backend/vulkan/vk.h"
#include "impeller/renderer/command.h"
#include "impeller/renderer/render_pass.h"
#include "impeller/renderer/render_target.h"