Skip to content

Commit 7176173

Browse files
author
Jonah Williams
authored
[Impeller] generate mip level N from N-1 in Vulkan backend. (#51749)
Uses the mipmap implementation from https://vulkan-tutorial.com/Generating_Mipmaps . Today we generate all mip levels from mip level 0, which results in lost data. Instead we need to use the previous mip level
1 parent b16c0f1 commit 7176173

1 file changed

Lines changed: 97 additions & 79 deletions

File tree

impeller/renderer/backend/vulkan/blit_command_vk.cc

Lines changed: 97 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,46 @@
66

77
#include <cstdint>
88

9+
#include "impeller/renderer/backend/vulkan/barrier_vk.h"
910
#include "impeller/renderer/backend/vulkan/command_encoder_vk.h"
1011
#include "impeller/renderer/backend/vulkan/texture_vk.h"
12+
#include "vulkan/vulkan_core.h"
13+
#include "vulkan/vulkan_enums.hpp"
14+
#include "vulkan/vulkan_structs.hpp"
1115

1216
namespace impeller {
1317

18+
static void InsertImageMemoryBarrier(const vk::CommandBuffer& cmd,
19+
const vk::Image& image,
20+
vk::AccessFlags src_access_mask,
21+
vk::AccessFlags dst_access_mask,
22+
vk::ImageLayout old_layout,
23+
vk::ImageLayout new_layout,
24+
vk::PipelineStageFlags src_stage,
25+
vk::PipelineStageFlags dst_stage,
26+
uint32_t base_mip_level,
27+
uint32_t mip_level_count = 1u) {
28+
if (old_layout == new_layout) {
29+
return;
30+
}
31+
32+
vk::ImageMemoryBarrier barrier;
33+
barrier.srcAccessMask = src_access_mask;
34+
barrier.dstAccessMask = dst_access_mask;
35+
barrier.oldLayout = old_layout;
36+
barrier.newLayout = new_layout;
37+
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
38+
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
39+
barrier.image = image;
40+
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
41+
barrier.subresourceRange.baseMipLevel = base_mip_level;
42+
barrier.subresourceRange.levelCount = mip_level_count;
43+
barrier.subresourceRange.baseArrayLayer = 0u;
44+
barrier.subresourceRange.layerCount = 1u;
45+
46+
cmd.pipelineBarrier(src_stage, dst_stage, {}, nullptr, nullptr, barrier);
47+
}
48+
1449
BlitEncodeVK::~BlitEncodeVK() = default;
1550

1651
//------------------------------------------------------------------------------
@@ -240,37 +275,6 @@ std::string BlitGenerateMipmapCommandVK::GetLabel() const {
240275
return label;
241276
}
242277

243-
static void InsertImageMemoryBarrier(const vk::CommandBuffer& cmd,
244-
const vk::Image& image,
245-
vk::AccessFlags src_access_mask,
246-
vk::AccessFlags dst_access_mask,
247-
vk::ImageLayout old_layout,
248-
vk::ImageLayout new_layout,
249-
vk::PipelineStageFlags src_stage,
250-
vk::PipelineStageFlags dst_stage,
251-
uint32_t base_mip_level,
252-
uint32_t mip_level_count = 1u) {
253-
if (old_layout == new_layout) {
254-
return;
255-
}
256-
257-
vk::ImageMemoryBarrier barrier;
258-
barrier.srcAccessMask = src_access_mask;
259-
barrier.dstAccessMask = dst_access_mask;
260-
barrier.oldLayout = old_layout;
261-
barrier.newLayout = new_layout;
262-
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
263-
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
264-
barrier.image = image;
265-
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
266-
barrier.subresourceRange.baseMipLevel = base_mip_level;
267-
barrier.subresourceRange.levelCount = mip_level_count;
268-
barrier.subresourceRange.baseArrayLayer = 0u;
269-
barrier.subresourceRange.layerCount = 1u;
270-
271-
cmd.pipelineBarrier(src_stage, dst_stage, {}, nullptr, nullptr, barrier);
272-
}
273-
274278
bool BlitGenerateMipmapCommandVK::Encode(CommandEncoderVK& encoder) const {
275279
auto& src = TextureVK::Cast(*texture);
276280

@@ -288,55 +292,73 @@ bool BlitGenerateMipmapCommandVK::Encode(CommandEncoderVK& encoder) const {
288292
return false;
289293
}
290294

291-
// Transition the base mip level to transfer-src layout so we can read from
292-
// it and transition the rest to dst-optimal since they are going to be
293-
// written to.
295+
// Initialize all mip levels to be in TransferDst mode. Later, in a loop,
296+
// after writing to that mip level, we'll first switch its layout to
297+
// TransferSrc to prepare the mip level after it, use the image as the source
298+
// of the blit, before finally switching it to ShaderReadOnly so its available
299+
// for sampling in a shader.
294300
InsertImageMemoryBarrier(
295301
cmd, // command buffer
296302
image, // image
297303
vk::AccessFlagBits::eTransferWrite, // src access mask
298304
vk::AccessFlagBits::eTransferRead, // dst access mask
299305
src.GetLayout(), // old layout
300-
vk::ImageLayout::eTransferSrcOptimal, // new layout
301-
vk::PipelineStageFlagBits::eTransfer, // src stage
302-
vk::PipelineStageFlagBits::eTransfer, // dst stage
303-
0u // mip level
304-
);
305-
InsertImageMemoryBarrier(
306-
cmd, // command buffer
307-
image, // image
308-
{}, // src access mask
309-
vk::AccessFlagBits::eTransferWrite, // dst access mask
310-
vk::ImageLayout::eUndefined, // old layout
311306
vk::ImageLayout::eTransferDstOptimal, // new layout
312307
vk::PipelineStageFlagBits::eTransfer, // src stage
313308
vk::PipelineStageFlagBits::eTransfer, // dst stage
314-
1u, // mip level
315-
mip_count - 1 // mip level count
309+
0u, // mip level
310+
mip_count // mip level count
316311
);
317312

318-
// Blit from the base mip level to all other levels.
313+
vk::ImageMemoryBarrier barrier;
314+
barrier.image = image;
315+
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
316+
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
317+
barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
318+
barrier.subresourceRange.baseArrayLayer = 0;
319+
barrier.subresourceRange.layerCount = 1;
320+
barrier.subresourceRange.levelCount = 1;
321+
322+
// Blit from the mip level N - 1 to mip level N.
323+
size_t width = size.width;
324+
size_t height = size.height;
319325
for (size_t mip_level = 1u; mip_level < mip_count; mip_level++) {
320-
vk::ImageBlit blit;
326+
barrier.subresourceRange.baseMipLevel = mip_level - 1;
327+
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
328+
barrier.newLayout = vk::ImageLayout::eTransferSrcOptimal;
329+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
330+
barrier.dstAccessMask = vk::AccessFlagBits::eTransferRead;
331+
332+
// We just finished writing to the previous (N-1) mip level or it was the
333+
// base mip level. These were initialized to TransferDst earler. We are now
334+
// going to read from it to write to the current level (N) . So it must be
335+
// converted to TransferSrc.
336+
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
337+
vk::PipelineStageFlagBits::eTransfer, {}, {}, {},
338+
{barrier});
321339

340+
vk::ImageBlit blit;
322341
blit.srcSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
323342
blit.srcSubresource.baseArrayLayer = 0u;
324343
blit.srcSubresource.layerCount = 1u;
325-
blit.srcSubresource.mipLevel = 0u;
344+
blit.srcSubresource.mipLevel = mip_level - 1;
326345

327346
blit.dstSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
328347
blit.dstSubresource.baseArrayLayer = 0u;
329348
blit.dstSubresource.layerCount = 1u;
330349
blit.dstSubresource.mipLevel = mip_level;
331350

332351
// offsets[0] is origin.
333-
blit.srcOffsets[1].x = size.width;
334-
blit.srcOffsets[1].y = size.height;
352+
blit.srcOffsets[1].x = std::max<int32_t>(width, 1u);
353+
blit.srcOffsets[1].y = std::max<int32_t>(height, 1u);
335354
blit.srcOffsets[1].z = 1u;
336355

356+
width = width / 2;
357+
height = height / 2;
358+
337359
// offsets[0] is origin.
338-
blit.dstOffsets[1].x = std::max<int32_t>(size.width >> mip_level, 1u);
339-
blit.dstOffsets[1].y = std::max<int32_t>(size.height >> mip_level, 1u);
360+
blit.dstOffsets[1].x = std::max<int32_t>(width, 1u);
361+
blit.dstOffsets[1].y = std::max<int32_t>(height, 1u);
340362
blit.dstOffsets[1].z = 1u;
341363

342364
cmd.blitImage(image, // src image
@@ -347,33 +369,29 @@ bool BlitGenerateMipmapCommandVK::Encode(CommandEncoderVK& encoder) const {
347369
&blit, // regions
348370
vk::Filter::eLinear // filter
349371
);
372+
373+
barrier.oldLayout = vk::ImageLayout::eTransferSrcOptimal;
374+
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
375+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferRead;
376+
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
377+
378+
// Now that the blit is done, the image at the previous level (N-1)
379+
// is done reading from (TransferSrc)/ Now we must prepare it to be read
380+
// from a shader (ShaderReadOnly).
381+
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
382+
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
383+
{barrier});
350384
}
351385

352-
// Transition all mip levels to shader read. The base mip level has a
353-
// different "old" layout than the rest now.
354-
InsertImageMemoryBarrier(
355-
cmd, // command buffer
356-
image, // image
357-
vk::AccessFlagBits::eTransferWrite, // src access mask
358-
vk::AccessFlagBits::eShaderRead, // dst access mask
359-
vk::ImageLayout::eTransferSrcOptimal, // old layout
360-
vk::ImageLayout::eShaderReadOnlyOptimal, // new layout
361-
vk::PipelineStageFlagBits::eTransfer, // src stage
362-
vk::PipelineStageFlagBits::eFragmentShader, // dst stage
363-
0u // mip level
364-
);
365-
InsertImageMemoryBarrier(
366-
cmd, // command buffer
367-
image, // image
368-
vk::AccessFlagBits::eTransferWrite, // src access mask
369-
vk::AccessFlagBits::eShaderRead, // dst access mask
370-
vk::ImageLayout::eTransferDstOptimal, // old layout
371-
vk::ImageLayout::eShaderReadOnlyOptimal, // new layout
372-
vk::PipelineStageFlagBits::eTransfer, // src stage
373-
vk::PipelineStageFlagBits::eFragmentShader, // dst stage
374-
1u, // mip level
375-
mip_count - 1 // mip level count
376-
);
386+
barrier.subresourceRange.baseMipLevel = mip_count - 1;
387+
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
388+
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
389+
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
390+
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
391+
392+
cmd.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
393+
vk::PipelineStageFlagBits::eFragmentShader, {}, {}, {},
394+
{barrier});
377395

378396
// We modified the layouts of this image from underneath it. Tell it its new
379397
// state so it doesn't try to perform redundant transitions under the hood.

0 commit comments

Comments
 (0)