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

Commit 59bc057

Browse files
committed
[impeller] support generating mip-maps on Vulkan
fix: flutter/flutter#120134
1 parent 9ee3221 commit 59bc057

File tree

5 files changed

+134
-22
lines changed

5 files changed

+134
-22
lines changed

impeller/renderer/backend/vulkan/blit_command_vk.cc

Lines changed: 106 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,102 @@ 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->GetSingleUseChild();
205+
206+
for (uint32_t i = 1; i < mip_count; i++) {
207+
barrier.subresourceRange.baseMipLevel = i - 1;
208+
barrier.oldLayout = vk::ImageLayout::eUndefined;
209+
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
210+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
211+
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
212+
213+
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
214+
vk::PipelineStageFlagBits::eTransfer, {},
215+
nullptr, nullptr, barrier);
216+
217+
vk::ImageBlit blit{};
218+
219+
// src
220+
blit.srcOffsets[0] = vk::Offset3D(0, 0, 0);
221+
blit.srcOffsets[1] = vk::Offset3D(mip_width, mip_height, 1);
222+
blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
223+
blit.srcSubresource.mipLevel = i - 1;
224+
blit.srcSubresource.baseArrayLayer = 0;
225+
blit.srcSubresource.layerCount = 1;
226+
227+
// dst
228+
blit.dstOffsets[0] = vk::Offset3D(0, 0, 0);
229+
blit.dstOffsets[1] = vk::Offset3D(mip_width > 1 ? mip_width / 2 : 1,
230+
mip_height > 1 ? mip_height / 2 : 1, 1);
231+
blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
232+
blit.dstSubresource.mipLevel = i;
233+
blit.dstSubresource.baseArrayLayer = 0;
234+
blit.dstSubresource.layerCount = 1;
235+
236+
gen_mip_cmd.blitImage(source_image, vk::ImageLayout::eTransferSrcOptimal,
237+
source_image, vk::ImageLayout::eTransferDstOptimal,
238+
blit, vk::Filter::eLinear);
239+
240+
// transition the previous mip level to shader read only optimal
241+
barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
242+
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
243+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
244+
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
245+
246+
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
247+
vk::PipelineStageFlagBits::eFragmentShader, {},
248+
nullptr, nullptr, barrier);
249+
250+
if (mip_width > 1) {
251+
mip_width /= 2;
252+
}
253+
254+
if (mip_height > 1) {
255+
mip_height /= 2;
256+
}
257+
}
258+
259+
// transition the last mip level to shader read only optimal
260+
barrier.subresourceRange.baseMipLevel = mip_count - 1;
261+
barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
262+
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
263+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
264+
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
265+
266+
gen_mip_cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
267+
vk::PipelineStageFlagBits::eFragmentShader, {},
268+
nullptr, nullptr, barrier);
269+
270+
// submit the command buffer
271+
auto res = gen_mip_cmd.end();
272+
if (res != vk::Result::eSuccess) {
273+
VALIDATION_LOG << "Failed to end command buffer: " << vk::to_string(res);
274+
return false;
275+
}
276+
177277
return true;
178278
}
179279

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: 16 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,6 +72,7 @@ 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);
7577

7678
command_buffer_->GetDeletionQueue()->Push(
@@ -80,15 +82,15 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
8082

8183
// layout transition.
8284
if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined,
83-
vk::ImageLayout::eColorAttachmentOptimal)) {
85+
vk::ImageLayout::eColorAttachmentOptimal,
86+
size.MipCount())) {
8487
return false;
8588
}
8689

8790
vk::ClearValue clear_value;
8891
clear_value.color =
8992
vk::ClearColorValue(std::array<float, 4>{0.0f, 0.0f, 0.0, 0.0f});
9093

91-
const auto& size = texture.GetTextureDescriptor().size;
9294
vk::Rect2D render_area =
9395
vk::Rect2D()
9496
.setOffset(vk::Offset2D(0, 0))
@@ -124,7 +126,8 @@ bool RenderPassVK::OnEncodeCommands(const Context& context) const {
124126
}
125127

126128
if (!TransitionImageLayout(texture.GetImage(), vk::ImageLayout::eUndefined,
127-
vk::ImageLayout::eColorAttachmentOptimal)) {
129+
vk::ImageLayout::eColorAttachmentOptimal,
130+
size.MipCount())) {
128131
return false;
129132
}
130133

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

317320
const SampledImageSlot& slot = bindings.sampled_images.at(index);
321+
uint32_t mip_levels = texture_vk.GetTextureDescriptor().mip_count;
318322

319-
if (!TransitionImageLayout(texture_vk.GetImage(),
320-
vk::ImageLayout::eUndefined,
321-
vk::ImageLayout::eTransferDstOptimal)) {
323+
if (!TransitionImageLayout(
324+
texture_vk.GetImage(), vk::ImageLayout::eUndefined,
325+
vk::ImageLayout::eTransferDstOptimal, mip_levels)) {
322326
return false;
323327
}
324328

325329
CopyBufferToImage(texture_vk);
326330

327-
if (!TransitionImageLayout(texture_vk.GetImage(),
328-
vk::ImageLayout::eTransferDstOptimal,
329-
vk::ImageLayout::eShaderReadOnlyOptimal)) {
331+
if (!TransitionImageLayout(
332+
texture_vk.GetImage(), vk::ImageLayout::eTransferDstOptimal,
333+
vk::ImageLayout::eShaderReadOnlyOptimal, mip_levels)) {
330334
return false;
331335
}
332336

@@ -394,9 +398,10 @@ vk::Framebuffer RenderPassVK::CreateFrameBuffer(
394398

395399
bool RenderPassVK::TransitionImageLayout(vk::Image image,
396400
vk::ImageLayout layout_old,
397-
vk::ImageLayout layout_new) const {
401+
vk::ImageLayout layout_new,
402+
uint32_t mip_levels) const {
398403
auto transition_cmd =
399-
TransitionImageLayoutCommandVK(image, layout_old, layout_new);
404+
TransitionImageLayoutCommandVK(image, layout_old, layout_new, mip_levels);
400405
return transition_cmd.Submit(command_buffer_.get());
401406
}
402407

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)