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

Commit 0378b34

Browse files
authored
[impeller] support generating mip-maps on Vulkan (#39689)
* [impeller] support generating mip-maps on Vulkan fix: flutter/flutter#120134 * various bug fixes * revert to gl surface
1 parent adc0ebd commit 0378b34

File tree

6 files changed

+159
-22
lines changed

6 files changed

+159
-22
lines changed

impeller/renderer/backend/vulkan/allocator_vk.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ std::shared_ptr<Texture> AllocatorVK::OnCreateTexture(
116116
image_create_info.initialLayout = vk::ImageLayout::eUndefined;
117117
image_create_info.usage = vk::ImageUsageFlagBits::eSampled |
118118
vk::ImageUsageFlagBits::eColorAttachment |
119+
vk::ImageUsageFlagBits::eTransferSrc |
119120
vk::ImageUsageFlagBits::eTransferDst;
120121

121122
VmaAllocationCreateInfo alloc_create_info = {};

impeller/renderer/backend/vulkan/blit_command_vk.cc

Lines changed: 129 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,13 @@ std::string BlitCopyTextureToTextureCommandVK::GetLabel() const {
5959
return false;
6060
}
6161

62+
uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count;
63+
6264
// transition the source image to transfer source optimal
6365
TransitionImageLayoutCommandVK transition_source_cmd =
6466
TransitionImageLayoutCommandVK(source_image, vk::ImageLayout::eUndefined,
65-
vk::ImageLayout::eTransferSrcOptimal);
67+
vk::ImageLayout::eTransferSrcOptimal,
68+
mip_count);
6669
bool success = transition_source_cmd.Submit(fenced_command_buffer);
6770
if (!success) {
6871
VALIDATION_LOG << "Failed to transition source image layout";
@@ -72,7 +75,8 @@ std::string BlitCopyTextureToTextureCommandVK::GetLabel() const {
7275
// transition the destination image to transfer destination optimal
7376
TransitionImageLayoutCommandVK transition_dest_cmd =
7477
TransitionImageLayoutCommandVK(dest_image, vk::ImageLayout::eUndefined,
75-
vk::ImageLayout::eTransferDstOptimal);
78+
vk::ImageLayout::eTransferDstOptimal,
79+
mip_count);
7680
success = transition_dest_cmd.Submit(fenced_command_buffer);
7781
if (!success) {
7882
VALIDATION_LOG << "Failed to transition destination image layout";
@@ -127,10 +131,13 @@ std::string BlitCopyTextureToBufferCommandVK::GetLabel() const {
127131
image_copy.setImageExtent(
128132
vk::Extent3D(source_region.size.width, source_region.size.height, 1));
129133

134+
uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count;
135+
130136
// transition the source image to transfer source optimal
131137
TransitionImageLayoutCommandVK transition_source_cmd =
132138
TransitionImageLayoutCommandVK(source_image, vk::ImageLayout::eUndefined,
133-
vk::ImageLayout::eTransferSrcOptimal);
139+
vk::ImageLayout::eTransferSrcOptimal,
140+
mip_count);
134141
bool success = transition_source_cmd.Submit(fenced_command_buffer);
135142
if (!success) {
136143
return false;
@@ -171,9 +178,125 @@ std::string BlitGenerateMipmapCommandVK::GetLabel() const {
171178

172179
[[nodiscard]] bool BlitGenerateMipmapCommandVK::Encode(
173180
FencedCommandBufferVK* fenced_command_buffer) const {
174-
// TODO(https://github.com/flutter/flutter/issues/120134): Support generating
175-
// mipmaps on Vulkan.
176-
IMPELLER_UNIMPLEMENTED;
181+
const auto& source_tex_vk = TextureVK::Cast(*texture);
182+
const auto source_image = source_tex_vk.GetImage();
183+
184+
const auto size = source_tex_vk.GetTextureDescriptor().size;
185+
uint32_t mip_count = source_tex_vk.GetTextureDescriptor().mip_count;
186+
187+
uint32_t mip_width = size.width;
188+
uint32_t mip_height = size.height;
189+
190+
// create the subresource range
191+
vk::ImageSubresourceRange subresource_range{};
192+
subresource_range.setAspectMask(vk::ImageAspectFlagBits::eColor);
193+
subresource_range.setBaseArrayLayer(0);
194+
subresource_range.setLayerCount(1);
195+
subresource_range.setLevelCount(1);
196+
197+
// create a barrier to transition the image to transfer source optimal
198+
vk::ImageMemoryBarrier barrier{};
199+
barrier.setImage(source_image);
200+
barrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
201+
barrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
202+
barrier.setSubresourceRange(subresource_range);
203+
204+
auto gen_mip_cmd = fenced_command_buffer->Get();
205+
206+
vk::CommandBufferBeginInfo begin_info;
207+
begin_info.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
208+
auto begin_res = gen_mip_cmd.begin(begin_info);
209+
210+
if (begin_res != vk::Result::eSuccess) {
211+
VALIDATION_LOG << "Failed to begin command buffer: "
212+
<< vk::to_string(begin_res);
213+
return false;
214+
}
215+
216+
// transition all layers to transfer dst optimal
217+
for (uint32_t i = 0; i < mip_count; i++) {
218+
barrier.subresourceRange.baseMipLevel = i;
219+
barrier.oldLayout = vk::ImageLayout::eUndefined;
220+
barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
221+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
222+
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
223+
224+
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
225+
vk::PipelineStageFlagBits::eTransfer, {},
226+
nullptr, nullptr, barrier);
227+
}
228+
229+
for (uint32_t i = 1; i < mip_count; i++) {
230+
barrier.subresourceRange.baseMipLevel = i - 1;
231+
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
232+
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
233+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
234+
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
235+
236+
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
237+
vk::PipelineStageFlagBits::eTransfer, {},
238+
nullptr, nullptr, barrier);
239+
240+
vk::ImageBlit blit{};
241+
242+
// src
243+
blit.srcOffsets[0] = vk::Offset3D(0, 0, 0);
244+
blit.srcOffsets[1] = vk::Offset3D(mip_width, mip_height, 1);
245+
blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
246+
blit.srcSubresource.mipLevel = i - 1;
247+
blit.srcSubresource.baseArrayLayer = 0;
248+
blit.srcSubresource.layerCount = 1;
249+
250+
// dst
251+
blit.dstOffsets[0] = vk::Offset3D(0, 0, 0);
252+
blit.dstOffsets[1] = vk::Offset3D(mip_width > 1 ? mip_width / 2 : 1,
253+
mip_height > 1 ? mip_height / 2 : 1, 1);
254+
blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
255+
blit.dstSubresource.mipLevel = i;
256+
blit.dstSubresource.baseArrayLayer = 0;
257+
blit.dstSubresource.layerCount = 1;
258+
259+
gen_mip_cmd.blitImage(source_image, vk::ImageLayout::eTransferSrcOptimal,
260+
source_image, vk::ImageLayout::eTransferDstOptimal,
261+
blit, vk::Filter::eLinear);
262+
263+
// transition the previous mip level to shader read only optimal
264+
barrier.oldLayout = vk::ImageLayout::eUndefined;
265+
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
266+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
267+
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
268+
269+
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
270+
vk::PipelineStageFlagBits::eFragmentShader, {},
271+
nullptr, nullptr, barrier);
272+
273+
if (mip_width > 1) {
274+
mip_width /= 2;
275+
}
276+
277+
if (mip_height > 1) {
278+
mip_height /= 2;
279+
}
280+
}
281+
282+
// transition the last mip level to shader read only optimal
283+
barrier.subresourceRange.baseMipLevel = mip_count - 1;
284+
barrier.oldLayout = vk::ImageLayout::eUndefined;
285+
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
286+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
287+
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
288+
289+
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
290+
vk::PipelineStageFlagBits::eFragmentShader, {},
291+
nullptr, nullptr, barrier);
292+
293+
// submit the command buffer
294+
auto res = gen_mip_cmd.end();
295+
if (res != vk::Result::eSuccess) {
296+
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
297+
return false;
298+
}
299+
177300
return true;
178301
}
179302

impeller/renderer/backend/vulkan/commands_vk.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ namespace impeller {
99
TransitionImageLayoutCommandVK::TransitionImageLayoutCommandVK(
1010
vk::Image image,
1111
vk::ImageLayout old_layout,
12-
vk::ImageLayout new_layout)
13-
: image_(image), old_layout_(old_layout), new_layout_(new_layout) {}
12+
vk::ImageLayout new_layout,
13+
uint32_t mip_levels)
14+
: image_(image),
15+
old_layout_(old_layout),
16+
new_layout_(new_layout),
17+
mip_levels_(mip_levels) {}
1418

1519
TransitionImageLayoutCommandVK::~TransitionImageLayoutCommandVK() = default;
1620

@@ -35,7 +39,7 @@ bool TransitionImageLayoutCommandVK::Submit(
3539
vk::ImageSubresourceRange()
3640
.setAspectMask(vk::ImageAspectFlagBits::eColor)
3741
.setBaseMipLevel(0)
38-
.setLevelCount(1)
42+
.setLevelCount(mip_levels_)
3943
.setBaseArrayLayer(0)
4044
.setLayerCount(1));
4145

impeller/renderer/backend/vulkan/commands_vk.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class TransitionImageLayoutCommandVK {
1414
public:
1515
TransitionImageLayoutCommandVK(vk::Image image,
1616
vk::ImageLayout old_layout,
17-
vk::ImageLayout new_layout);
17+
vk::ImageLayout new_layout,
18+
uint32_t mip_levels);
1819

1920
~TransitionImageLayoutCommandVK();
2021

@@ -24,6 +25,7 @@ class TransitionImageLayoutCommandVK {
2425
vk::Image image_;
2526
vk::ImageLayout old_layout_;
2627
vk::ImageLayout new_layout_;
28+
uint32_t mip_levels_;
2729

2830
FML_DISALLOW_COPY_AND_ASSIGN(TransitionImageLayoutCommandVK);
2931
};

impeller/renderer/backend/vulkan/render_pass_vk.cc

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

77
#include <array>
8+
#include <cstdint>
89
#include <vector>
910

1011
#include "fml/logging.h"
@@ -71,7 +72,9 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
7172
const auto& stencil0 = render_target.GetStencilAttachment();
7273

7374
auto& texture = TextureVK::Cast(*color0.texture);
75+
const auto& size = texture.GetTextureDescriptor().size;
7476
vk::Framebuffer framebuffer = CreateFrameBuffer(texture);
77+
uint32_t mip_count = texture.GetTextureDescriptor().mip_count;
7578

7679
command_buffer_->GetDeletionQueue()->Push(
7780
[device = device_, fbo = framebuffer]() {
@@ -80,15 +83,15 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
8083

8184
// layout transition.
8285
if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined,
83-
vk::ImageLayout::eColorAttachmentOptimal)) {
86+
vk::ImageLayout::eColorAttachmentOptimal,
87+
mip_count)) {
8488
return false;
8589
}
8690

8791
vk::ClearValue clear_value;
8892
clear_value.color =
8993
vk::ClearColorValue(std::array<float, 4>{0.0f, 0.0f, 0.0, 0.0f});
9094

91-
const auto& size = texture.GetTextureDescriptor().size;
9295
vk::Rect2D render_area =
9396
vk::Rect2D()
9497
.setOffset(vk::Offset2D(0, 0))
@@ -124,7 +127,8 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
124127
}
125128

126129
if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined,
127-
vk::ImageLayout::eColorAttachmentOptimal)) {
130+
vk::ImageLayout::eColorAttachmentOptimal,
131+
mip_count)) {
128132
return false;
129133
}
130134

@@ -315,18 +319,19 @@ bool RenderPassVK::UpdateDescriptorSets(const char* label,
315319
const SamplerVK& sampler_vk = SamplerVK::Cast(sampler);
316320

317321
const SampledImageSlot& slot = bindings.sampled_images.at(index);
322+
uint32_t mip_levels = texture_vk.GetTextureDescriptor().mip_count;
318323

319-
if (!TransitionImageLayout(texture_vk.GetImage(),
320-
vk::ImageLayout::eUndefined,
321-
vk::ImageLayout::eTransferDstOptimal)) {
324+
if (!TransitionImageLayout(
325+
texture_vk.GetImage(), vk::ImageLayout::eUndefined,
326+
vk::ImageLayout::eTransferDstOptimal, mip_levels)) {
322327
return false;
323328
}
324329

325330
CopyBufferToImage(texture_vk);
326331

327-
if (!TransitionImageLayout(texture_vk.GetImage(),
328-
vk::ImageLayout::eTransferDstOptimal,
329-
vk::ImageLayout::eShaderReadOnlyOptimal)) {
332+
if (!TransitionImageLayout(
333+
texture_vk.GetImage(), vk::ImageLayout::eTransferDstOptimal,
334+
vk::ImageLayout::eShaderReadOnlyOptimal, mip_levels)) {
330335
return false;
331336
}
332337

@@ -394,9 +399,10 @@ vk::Framebuffer RenderPassVK::CreateFrameBuffer(
394399

395400
bool RenderPassVK::TransitionImageLayout(vk::Image image,
396401
vk::ImageLayout layout_old,
397-
vk::ImageLayout layout_new) const {
402+
vk::ImageLayout layout_new,
403+
uint32_t mip_levels) const {
398404
auto transition_cmd =
399-
TransitionImageLayoutCommandVK(image, layout_old, layout_new);
405+
TransitionImageLayoutCommandVK(image, layout_old, layout_new, mip_levels);
400406
return transition_cmd.Submit(command_buffer_.get());
401407
}
402408

impeller/renderer/backend/vulkan/render_pass_vk.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ class RenderPassVK final : public RenderPass {
6565

6666
bool TransitionImageLayout(vk::Image image,
6767
vk::ImageLayout layout_old,
68-
vk::ImageLayout layout_new) const;
68+
vk::ImageLayout layout_new,
69+
uint32_t mip_levels) const;
6970

7071
bool CopyBufferToImage(const TextureVK& texture_vk) const;
7172

0 commit comments

Comments
 (0)