Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d6a48e9

Browse files
author
Jonah Williams
authored
[Impeller] Allocate exact descriptor count, populate in one go. (#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.
1 parent 2dde7ad commit d6a48e9

13 files changed

+396
-410
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,6 +2153,8 @@ ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/android_hardware_buffe
21532153
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/android_hardware_buffer_texture_source_vk.h + ../../../flutter/LICENSE
21542154
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.cc + ../../../flutter/LICENSE
21552155
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.h + ../../../flutter/LICENSE
2156+
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.cc + ../../../flutter/LICENSE
2157+
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.h + ../../../flutter/LICENSE
21562158
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.cc + ../../../flutter/LICENSE
21572159
ORIGIN: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.h + ../../../flutter/LICENSE
21582160
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_
49254927
FILE: ../../../flutter/impeller/renderer/backend/vulkan/android_hardware_buffer_texture_source_vk.h
49264928
FILE: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.cc
49274929
FILE: ../../../flutter/impeller/renderer/backend/vulkan/barrier_vk.h
4930+
FILE: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.cc
4931+
FILE: ../../../flutter/impeller/renderer/backend/vulkan/binding_helpers_vk.h
49284932
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.cc
49294933
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_command_vk.h
49304934
FILE: ../../../flutter/impeller/renderer/backend/vulkan/blit_pass_vk.cc

fml/status_or.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <optional>
99

10+
#include "flutter/fml/logging.h"
1011
#include "flutter/fml/status.h"
1112

1213
namespace fml {

impeller/renderer/backend/vulkan/BUILD.gn

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ impeller_component("vulkan") {
3434
"android_hardware_buffer_texture_source_vk.h",
3535
"barrier_vk.cc",
3636
"barrier_vk.h",
37+
"binding_helpers_vk.cc",
38+
"binding_helpers_vk.h",
3739
"blit_command_vk.cc",
3840
"blit_command_vk.h",
3941
"blit_pass_vk.cc",
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "impeller/renderer/backend/vulkan/binding_helpers_vk.h"
6+
#include "fml/status.h"
7+
#include "impeller/renderer/backend/vulkan/command_buffer_vk.h"
8+
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
9+
#include "impeller/renderer/backend/vulkan/command_pool_vk.h"
10+
#include "impeller/renderer/backend/vulkan/compute_pipeline_vk.h"
11+
#include "impeller/renderer/backend/vulkan/context_vk.h"
12+
#include "impeller/renderer/backend/vulkan/sampler_vk.h"
13+
#include "impeller/renderer/backend/vulkan/texture_vk.h"
14+
#include "impeller/renderer/backend/vulkan/vk.h"
15+
#include "impeller/renderer/command.h"
16+
#include "impeller/renderer/compute_command.h"
17+
18+
namespace impeller {
19+
20+
static bool BindImages(const Bindings& bindings,
21+
Allocator& allocator,
22+
const std::shared_ptr<CommandEncoderVK>& encoder,
23+
vk::DescriptorSet& vk_desc_set,
24+
std::vector<vk::DescriptorImageInfo>& images,
25+
std::vector<vk::WriteDescriptorSet>& writes) {
26+
for (const auto& [index, data] : bindings.sampled_images) {
27+
auto texture = data.texture.resource;
28+
const auto& texture_vk = TextureVK::Cast(*texture);
29+
const SamplerVK& sampler = SamplerVK::Cast(*data.sampler.resource);
30+
31+
if (!encoder->Track(texture) ||
32+
!encoder->Track(sampler.GetSharedSampler())) {
33+
return false;
34+
}
35+
36+
const SampledImageSlot& slot = data.slot;
37+
38+
vk::DescriptorImageInfo image_info;
39+
image_info.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
40+
image_info.sampler = sampler.GetSampler();
41+
image_info.imageView = texture_vk.GetImageView();
42+
images.push_back(image_info);
43+
44+
vk::WriteDescriptorSet write_set;
45+
write_set.dstSet = vk_desc_set;
46+
write_set.dstBinding = slot.binding;
47+
write_set.descriptorCount = 1u;
48+
write_set.descriptorType = vk::DescriptorType::eCombinedImageSampler;
49+
write_set.pImageInfo = &images.back();
50+
51+
writes.push_back(write_set);
52+
}
53+
54+
return true;
55+
};
56+
57+
static bool BindBuffers(const Bindings& bindings,
58+
Allocator& allocator,
59+
const std::shared_ptr<CommandEncoderVK>& encoder,
60+
vk::DescriptorSet& vk_desc_set,
61+
const std::vector<DescriptorSetLayout>& desc_set,
62+
std::vector<vk::DescriptorBufferInfo>& buffers,
63+
std::vector<vk::WriteDescriptorSet>& writes) {
64+
for (const auto& [buffer_index, data] : bindings.buffers) {
65+
const auto& buffer_view = data.view.resource.buffer;
66+
67+
auto device_buffer = buffer_view->GetDeviceBuffer(allocator);
68+
if (!device_buffer) {
69+
VALIDATION_LOG << "Failed to get device buffer for vertex binding";
70+
return false;
71+
}
72+
73+
auto buffer = DeviceBufferVK::Cast(*device_buffer).GetBuffer();
74+
if (!buffer) {
75+
return false;
76+
}
77+
78+
if (!encoder->Track(device_buffer)) {
79+
return false;
80+
}
81+
82+
uint32_t offset = data.view.resource.range.offset;
83+
84+
vk::DescriptorBufferInfo buffer_info;
85+
buffer_info.buffer = buffer;
86+
buffer_info.offset = offset;
87+
buffer_info.range = data.view.resource.range.length;
88+
buffers.push_back(buffer_info);
89+
90+
// TODO(jonahwilliams): remove this part by storing more data in
91+
// ShaderUniformSlot.
92+
const ShaderUniformSlot& uniform = data.slot;
93+
auto layout_it =
94+
std::find_if(desc_set.begin(), desc_set.end(),
95+
[&uniform](const DescriptorSetLayout& layout) {
96+
return layout.binding == uniform.binding;
97+
});
98+
if (layout_it == desc_set.end()) {
99+
VALIDATION_LOG << "Failed to get descriptor set layout for binding "
100+
<< uniform.binding;
101+
return false;
102+
}
103+
auto layout = *layout_it;
104+
105+
vk::WriteDescriptorSet write_set;
106+
write_set.dstSet = vk_desc_set;
107+
write_set.dstBinding = uniform.binding;
108+
write_set.descriptorCount = 1u;
109+
write_set.descriptorType = ToVKDescriptorType(layout.descriptor_type);
110+
write_set.pBufferInfo = &buffers.back();
111+
112+
writes.push_back(write_set);
113+
}
114+
return true;
115+
}
116+
117+
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
118+
const ContextVK& context,
119+
const std::shared_ptr<CommandEncoderVK>& encoder,
120+
const std::vector<Command>& commands) {
121+
if (commands.empty()) {
122+
return std::vector<vk::DescriptorSet>{};
123+
}
124+
125+
// Step 1: Determine the total number of buffer and sampler descriptor
126+
// sets required. Collect this information along with the layout information
127+
// to allocate a correctly sized descriptor pool.
128+
size_t buffer_count = 0;
129+
size_t samplers_count = 0;
130+
std::vector<vk::DescriptorSetLayout> layouts;
131+
layouts.reserve(commands.size());
132+
133+
for (const auto& command : commands) {
134+
buffer_count += command.vertex_bindings.buffers.size();
135+
buffer_count += command.fragment_bindings.buffers.size();
136+
samplers_count += command.fragment_bindings.sampled_images.size();
137+
138+
layouts.emplace_back(
139+
PipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout());
140+
}
141+
auto descriptor_result =
142+
encoder->AllocateDescriptorSets(buffer_count, samplers_count, layouts);
143+
if (!descriptor_result.ok()) {
144+
return descriptor_result.status();
145+
}
146+
auto descriptor_sets = descriptor_result.value();
147+
if (descriptor_sets.empty()) {
148+
return fml::Status();
149+
}
150+
151+
// Step 2: Update the descriptors for all image and buffer descriptors used
152+
// in the render pass.
153+
std::vector<vk::DescriptorImageInfo> images;
154+
std::vector<vk::DescriptorBufferInfo> buffers;
155+
std::vector<vk::WriteDescriptorSet> writes;
156+
images.reserve(samplers_count);
157+
buffers.reserve(buffer_count);
158+
writes.reserve(samplers_count + buffer_count);
159+
160+
auto& allocator = *context.GetResourceAllocator();
161+
auto desc_index = 0u;
162+
for (const auto& command : commands) {
163+
auto desc_set = command.pipeline->GetDescriptor()
164+
.GetVertexDescriptor()
165+
->GetDescriptorSetLayouts();
166+
167+
if (!BindBuffers(command.vertex_bindings, allocator, encoder,
168+
descriptor_sets[desc_index], desc_set, buffers, writes) ||
169+
!BindBuffers(command.fragment_bindings, allocator, encoder,
170+
descriptor_sets[desc_index], desc_set, buffers, writes) ||
171+
!BindImages(command.fragment_bindings, allocator, encoder,
172+
descriptor_sets[desc_index], images, writes)) {
173+
return fml::Status(fml::StatusCode::kUnknown,
174+
"Failed to bind texture or buffer.");
175+
}
176+
desc_index += 1;
177+
}
178+
179+
context.GetDevice().updateDescriptorSets(writes, {});
180+
return descriptor_sets;
181+
}
182+
183+
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
184+
const ContextVK& context,
185+
const std::shared_ptr<CommandEncoderVK>& encoder,
186+
const std::vector<ComputeCommand>& commands) {
187+
if (commands.empty()) {
188+
return std::vector<vk::DescriptorSet>{};
189+
}
190+
// Step 1: Determine the total number of buffer and sampler descriptor
191+
// sets required. Collect this information along with the layout information
192+
// to allocate a correctly sized descriptor pool.
193+
size_t buffer_count = 0;
194+
size_t samplers_count = 0;
195+
std::vector<vk::DescriptorSetLayout> layouts;
196+
layouts.reserve(commands.size());
197+
198+
for (const auto& command : commands) {
199+
buffer_count += command.bindings.buffers.size();
200+
samplers_count += command.bindings.sampled_images.size();
201+
202+
layouts.emplace_back(
203+
ComputePipelineVK::Cast(*command.pipeline).GetDescriptorSetLayout());
204+
}
205+
auto descriptor_result =
206+
encoder->AllocateDescriptorSets(buffer_count, samplers_count, layouts);
207+
if (!descriptor_result.ok()) {
208+
return descriptor_result.status();
209+
}
210+
auto descriptor_sets = descriptor_result.value();
211+
if (descriptor_sets.empty()) {
212+
return fml::Status();
213+
}
214+
// Step 2: Update the descriptors for all image and buffer descriptors used
215+
// in the render pass.
216+
std::vector<vk::DescriptorImageInfo> images;
217+
std::vector<vk::DescriptorBufferInfo> buffers;
218+
std::vector<vk::WriteDescriptorSet> writes;
219+
images.reserve(samplers_count);
220+
buffers.reserve(buffer_count);
221+
writes.reserve(samplers_count + buffer_count);
222+
223+
auto& allocator = *context.GetResourceAllocator();
224+
auto desc_index = 0u;
225+
for (const auto& command : commands) {
226+
auto desc_set = command.pipeline->GetDescriptor().GetDescriptorSetLayouts();
227+
228+
if (!BindBuffers(command.bindings, allocator, encoder,
229+
descriptor_sets[desc_index], desc_set, buffers, writes) ||
230+
!BindImages(command.bindings, allocator, encoder,
231+
descriptor_sets[desc_index], images, writes)) {
232+
return fml::Status(fml::StatusCode::kUnknown,
233+
"Failed to bind texture or buffer.");
234+
}
235+
desc_index += 1;
236+
}
237+
238+
context.GetDevice().updateDescriptorSets(writes, {});
239+
return descriptor_sets;
240+
}
241+
242+
} // namespace impeller
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#pragma once
6+
7+
#include <vector>
8+
9+
#include "fml/status_or.h"
10+
#include "impeller/renderer/backend/vulkan/context_vk.h"
11+
#include "impeller/renderer/command.h"
12+
#include "impeller/renderer/compute_command.h"
13+
14+
namespace impeller {
15+
16+
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
17+
const ContextVK& context,
18+
const std::shared_ptr<CommandEncoderVK>& encoder,
19+
const std::vector<Command>& commands);
20+
21+
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateAndBindDescriptorSets(
22+
const ContextVK& context,
23+
const std::shared_ptr<CommandEncoderVK>& encoder,
24+
const std::vector<ComputeCommand>& commands);
25+
26+
} // namespace impeller

impeller/renderer/backend/vulkan/command_encoder_vk.cc

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
66

77
#include "flutter/fml/closure.h"
8+
#include "fml/status.h"
9+
#include "fml/status_or.h"
810
#include "impeller/renderer/backend/vulkan/context_vk.h"
911
#include "impeller/renderer/backend/vulkan/fence_waiter_vk.h"
1012
#include "impeller/renderer/backend/vulkan/gpu_tracer_vk.h"
@@ -294,15 +296,17 @@ bool CommandEncoderVK::IsTracking(
294296
return tracked_objects_->IsTracking(source);
295297
}
296298

297-
std::optional<vk::DescriptorSet> CommandEncoderVK::AllocateDescriptorSet(
298-
const vk::DescriptorSetLayout& layout,
299-
size_t command_count) {
299+
fml::StatusOr<std::vector<vk::DescriptorSet>>
300+
CommandEncoderVK::AllocateDescriptorSets(
301+
uint32_t buffer_count,
302+
uint32_t sampler_count,
303+
const std::vector<vk::DescriptorSetLayout>& layouts) {
300304
if (!IsValid()) {
301-
return std::nullopt;
305+
return fml::Status(fml::StatusCode::kUnknown, "command encoder invalid");
302306
}
303307

304-
return tracked_objects_->GetDescriptorPool().AllocateDescriptorSet(
305-
layout, command_count);
308+
return tracked_objects_->GetDescriptorPool().AllocateDescriptorSets(
309+
buffer_count, sampler_count, layouts);
306310
}
307311

308312
void CommandEncoderVK::PushDebugGroup(const char* label) const {

impeller/renderer/backend/vulkan/command_encoder_vk.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,10 @@ class CommandEncoderVK {
7979

8080
void InsertDebugMarker(const char* label) const;
8181

82-
std::optional<vk::DescriptorSet> AllocateDescriptorSet(
83-
const vk::DescriptorSetLayout& layout,
84-
size_t command_count);
82+
fml::StatusOr<std::vector<vk::DescriptorSet>> AllocateDescriptorSets(
83+
uint32_t buffer_count,
84+
uint32_t sampler_count,
85+
const std::vector<vk::DescriptorSetLayout>& layouts);
8586

8687
private:
8788
friend class ContextVK;

0 commit comments

Comments
 (0)