vulkan: Fix validation errors (#6717)

- Depth attachment layout has generated a lot of error due to
   it being read-only. But the store-ops for the attachment during
   the renderpass are all write ops. We set the depth attachment
   layout as VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
 - Enable extra blitting step for SSAO because of the above layout
   conundrum.
 - Index buffers did not have a pipeline barriers after loading
   them.
 - Remove `assert_invariant(utils::popcount(sampleCount) == 1);`
   from `reduceSampleCount`. This assert fails when enabling the
   duplicate pass for SSAO.
This commit is contained in:
Powei Feng
2023-04-06 17:32:27 -07:00
committed by GitHub
parent 944031af14
commit 498a355fb2
7 changed files with 157 additions and 180 deletions

View File

@@ -32,11 +32,101 @@ using namespace utils;
namespace filament::backend {
namespace {
inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, VkFilter filter,
const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst,
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) {
const VkImageBlit blitRegions[1] = {{.srcSubresource = {aspect, src.level, src.layer, 1},
.srcOffsets = {srcRect[0], srcRect[1]},
.dstSubresource = {aspect, dst.level, dst.layer, 1},
.dstOffsets = {dstRect[0], dstRect[1]}}};
const VkImageResolve resolveRegions[1] = {{.srcSubresource = {aspect, src.level, src.layer, 1},
.srcOffset = srcRect[0],
.dstSubresource = {aspect, dst.level, dst.layer, 1},
.dstOffset = dstRect[0],
.extent = {srcExtent.width, srcExtent.height, 1}}};
const VkImageSubresourceRange srcRange = {
.aspectMask = aspect,
.baseMipLevel = src.level,
.levelCount = 1,
.baseArrayLayer = src.layer,
.layerCount = 1,
};
const VkImageSubresourceRange dstRange = {
.aspectMask = aspect,
.baseMipLevel = dst.level,
.levelCount = 1,
.baseArrayLayer = dst.layer,
.layerCount = 1,
};
const VkImageLayout srcLayout = getDefaultImageLayout(src.texture->usage);
transitionImageLayout(cmdbuffer, {
src.getImage(),
srcLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
srcRange,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT,
});
transitionImageLayout(cmdbuffer, {
dst.getImage(),
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
dstRange,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_WRITE_BIT,
});
if (src.texture->samples > 1 && dst.texture->samples == 1) {
assert_invariant(
aspect != VK_IMAGE_ASPECT_DEPTH_BIT && "Resolve with depth is not yet supported.");
vkCmdResolveImage(cmdbuffer, src.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dst.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, resolveRegions);
} else {
vkCmdBlitImage(cmdbuffer, src.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
dst.getImage(), VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blitRegions, filter);
}
VkImageLayout newSrcLayout = getDefaultImageLayout(src.texture->usage);
VkImageLayout const newDestLayout = getDefaultImageLayout(dst.texture->usage);
// In the case of blitting the depth attachment, we transition the source into GENERAL (for
// sampling) and set the copy as ATTACHMENT_OPTIMAL (to be set as the attachment).
if (any(src.texture->usage & TextureUsage::DEPTH_ATTACHMENT)) {
newSrcLayout = VK_IMAGE_LAYOUT_GENERAL;
}
transitionImageLayout(cmdbuffer, textureTransitionHelper({
.image = src.getImage(),
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = newSrcLayout,
.subresources = srcRange,
}));
transitionImageLayout(cmdbuffer, textureTransitionHelper({
.image = dst.getImage(),
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = newDestLayout,
.subresources = dstRange,
}));
}
struct BlitterUniforms {
int sampleCount;
float inverseSampleCount;
};
}// anonymous namespace
void VulkanBlitter::blitColor(BlitArgs args) {
const VulkanAttachment src = args.srcTarget->getColor(args.targetIndex);
const VulkanAttachment dst = args.dstTarget->getColor(0);
@@ -56,8 +146,8 @@ void VulkanBlitter::blitColor(BlitArgs args) {
return;
}
#endif
blitFast(aspect, args.filter, args.srcTarget->getExtent(), src, dst,
VkCommandBuffer const cmdbuffer = mContext.commands->get().cmdbuffer;
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst,
args.srcRectPair, args.dstRectPair);
}
@@ -88,90 +178,11 @@ void VulkanBlitter::blitDepth(BlitArgs args) {
args.dstRectPair);
return;
}
blitFast(aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
VkCommandBuffer const cmdbuffer = mContext.commands->get().cmdbuffer;
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
args.dstRectPair);
}
void VulkanBlitter::blitFast(VkImageAspectFlags aspect, VkFilter filter,
const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst,
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) {
const VkImageBlit blitRegions[1] = {{
.srcSubresource = { aspect, src.level, src.layer, 1 },
.srcOffsets = { srcRect[0], srcRect[1] },
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
.dstOffsets = { dstRect[0], dstRect[1] }
}};
const VkImageResolve resolveRegions[1] = {{
.srcSubresource = { aspect, src.level, src.layer, 1 },
.srcOffset = srcRect[0],
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
.dstOffset = dstRect[0],
.extent = { srcExtent.width, srcExtent.height, 1 }
}};
const VkImageSubresourceRange srcRange = {
.aspectMask = aspect,
.baseMipLevel = src.level,
.levelCount = 1,
.baseArrayLayer = src.layer,
.layerCount = 1,
};
const VkImageSubresourceRange dstRange = {
.aspectMask = aspect,
.baseMipLevel = dst.level,
.levelCount = 1,
.baseArrayLayer = dst.layer,
.layerCount = 1,
};
const VkCommandBuffer cmdbuffer = mContext.commands->get().cmdbuffer;
const VkImageLayout srcLayout = getDefaultImageLayout(src.texture->usage);
transitionImageLayout(cmdbuffer, {
src.getImage(),
srcLayout,
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
srcRange,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_READ_BIT
});
transitionImageLayout(cmdbuffer, {
dst.getImage(),
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
dstRange,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0,
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_TRANSFER_WRITE_BIT,
});
if (src.texture->samples > 1 && dst.texture->samples == 1) {
assert_invariant(aspect != VK_IMAGE_ASPECT_DEPTH_BIT && "Resolve with depth is not yet supported.");
vkCmdResolveImage(cmdbuffer, src.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.getImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, resolveRegions);
} else {
vkCmdBlitImage(cmdbuffer, src.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst.getImage(),
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, blitRegions, filter);
}
transitionImageLayout(cmdbuffer, blitterTransitionHelper({
.image = src.getImage(),
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
.newLayout = srcLayout,
.subresources = srcRange
}));
transitionImageLayout(cmdbuffer, blitterTransitionHelper({
.image = dst.getImage(),
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
.newLayout = getDefaultImageLayout(dst.texture->usage),
.subresources = dstRange,
}));
}
void VulkanBlitter::shutdown() noexcept {
if (mContext.device) {

View File

@@ -53,10 +53,6 @@ public:
private:
void lazyInit() noexcept;
void blitFast(VkImageAspectFlags aspect, VkFilter filter, const VkExtent2D srcExtent,
VulkanAttachment src, VulkanAttachment dst, const VkOffset3D srcRect[2],
const VkOffset3D dstRect[2]);
void blitSlowDepth(VkImageAspectFlags aspect, VkFilter filter,
const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst,
const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]);

View File

@@ -652,7 +652,26 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
// early exit condition is flattened in EASU code
return deviceProperties.vendorID == 0x5143; // Qualcomm
case Workaround::ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP:
return true;
// Supporting depth attachment as both sampler and attachment is only possible if we set
// the depth attachment as read-only (e.g. during SSAO pass), however note that the
// store-ops for attachments wrt VkRenderPass only has VK_ATTACHMENT_STORE_OP_DONT_CARE
// and VK_ATTACHMENT_STORE_OP_STORE for versions below 1.3. Only at 1.3 and above do we
// have a true read-only choice VK_ATTACHMENT_STORE_OP_NONE. That means for < 1.3, we
// will trigger a validation sync error if we use the depth attachment also as a
// sampler. See full error below:
//
// SYNC-HAZARD-WRITE-AFTER-READ(ERROR / SPEC): msgNum: 929810911 - Validation Error:
// [ SYNC-HAZARD-WRITE-AFTER-READ ] Object 0: handle = 0x6160000c3680,
// type = VK_OBJECT_TYPE_RENDER_PASS; | MessageID = 0x376bc9df | vkCmdEndRenderPass:
// Hazard WRITE_AFTER_READ in subpass 0 for attachment 1 depth aspect during store with
// storeOp VK_ATTACHMENT_STORE_OP_STORE. Access info (usage:
// SYNC_LATE_FRAGMENT_TESTS_DEPTH_STENCIL_ATTACHMENT_WRITE, prior_usage:
// SYNC_FRAGMENT_SHADER_SHADER_STORAGE_READ, read_barriers: VK_PIPELINE_STAGE_2_NONE,
// command: vkCmdDrawIndexed, seq_no: 177, reset_no: 1)
//
// Therefore we apply the existing workaround of an extra blit until a better
// resolution.
return false;
case Workaround::ADRENO_UNIFORM_ARRAY_CRASH:
return false;
}
@@ -871,39 +890,27 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
VulkanAttachment depth = rt->getSamples() == 1 ? rt->getDepth() : rt->getMsaaDepth();
VulkanDepthLayout initialDepthLayout = fromVkImageLayout(depth.getLayout());
VulkanDepthLayout renderPassDepthLayout =
fromVkImageLayout(getDefaultImageLayout(TextureUsage::DEPTH_ATTACHMENT));
VulkanDepthLayout finalDepthLayout = renderPassDepthLayout;
// Sometimes we need to permit the shader to sample the depth attachment by transitioning the
// layout of all its subresources to a read-only layout. This is especially crucial for SSAO.
//
// We cannot perform this transition using the render pass because the shaders in this render
// pass might sample from multiple miplevels.
//
// We do not use GENERAL here due to the following validation message:
//
// The Vulkan spec states: Image subresources used as attachments in the current render pass
// must not be accessed in any way other than as an attachment by this command, except for
// cases involving read-only access to depth/stencil attachments as described in the Render
// Pass chapter.
//
// https://vulkan.lunarg.com/doc/view/1.2.182.0/mac/1.2-extensions/vkspec.html#VUID-vkCmdDrawIndexed-None-04584)
//
if (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) {
VkImageSubresourceRange range = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = depth.texture->levels,
.baseArrayLayer = 0,
.layerCount = depth.texture->depth,
};
depth.texture->transitionLayout(cmdbuffer, range, VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL);
initialDepthLayout = renderPassDepthLayout = finalDepthLayout = VulkanDepthLayout::READ_ONLY;
}
VulkanDepthLayout renderPassDepthLayout = VulkanDepthLayout::ATTACHMENT;
VulkanDepthLayout finalDepthLayout = VulkanDepthLayout::ATTACHMENT;
TargetBufferFlags clearVal = params.flags.clear;
TargetBufferFlags discardEndVal = params.flags.discardEnd;
if (depth.texture) {
depth.texture->trackLayout(depth.level, depth.layer, toVkImageLayout(renderPassDepthLayout));
if (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) {
discardEndVal &= ~TargetBufferFlags::DEPTH;
clearVal &= ~TargetBufferFlags::DEPTH;
}
if (initialDepthLayout != VulkanDepthLayout::ATTACHMENT) {
VkImageSubresourceRange subresources{
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = depth.texture->levels,
.baseArrayLayer = 0,
.layerCount = depth.texture->depth,
};
depth.texture->transitionLayout(cmdbuffer, subresources,
toVkImageLayout(renderPassDepthLayout));
}
}
// Create the VkRenderPass or fetch it from cache.
@@ -913,9 +920,9 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.renderPassDepthLayout = renderPassDepthLayout,
.finalDepthLayout = finalDepthLayout,
.depthFormat = depth.getFormat(),
.clear = params.flags.clear,
.clear = clearVal,
.discardStart = discardStart,
.discardEnd = params.flags.discardEnd,
.discardEnd = discardEndVal,
.samples = rt->getSamples(),
.subpassMask = uint8_t(params.subpassMask),
};
@@ -1070,21 +1077,6 @@ void VulkanDriver::endRenderPass(int) {
VulkanRenderTarget* rt = mContext.currentRenderPass.renderTarget;
assert_invariant(rt);
// In some cases, depth needs to be transitioned from DEPTH_STENCIL_READ_ONLY_OPTIMAL back to
// GENERAL. We did not do this using the render pass because we need to change multiple mips.
if (mContext.currentRenderPass.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) {
const VulkanAttachment& depth = rt->getDepth();
VkImageSubresourceRange range = {
.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT,
.baseMipLevel = 0,
.levelCount = depth.texture->levels,
.baseArrayLayer = 0,
.layerCount = depth.texture->depth,
};
depth.texture->transitionLayout(cmdbuffer, range,
getDefaultImageLayout(TextureUsage::DEPTH_ATTACHMENT));
}
// Since we might soon be sampling from the render target that we just wrote to, we need a
// pipeline barrier between framebuffer writes and shader reads. This is a memory barrier rather
// than an image barrier. If we were to use image barriers here, we would potentially need to

View File

@@ -30,8 +30,7 @@ namespace filament::backend {
// Avoid using VkImageLayout since it requires 4 bytes.
enum class VulkanDepthLayout : uint8_t {
UNDEFINED, // VK_IMAGE_LAYOUT_UNDEFINED
GENERAL, // VK_IMAGE_LAYOUT_GENERAL
READ_ONLY, // VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL.
ATTACHMENT, // VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
};
// Simple manager for VkFramebuffer and VkRenderPass objects.
@@ -134,17 +133,19 @@ private:
inline VulkanDepthLayout fromVkImageLayout(VkImageLayout layout) {
switch (layout) {
case VK_IMAGE_LAYOUT_GENERAL: return VulkanDepthLayout::GENERAL;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL: return VulkanDepthLayout::READ_ONLY;
default: return VulkanDepthLayout::UNDEFINED;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
return VulkanDepthLayout::ATTACHMENT;
default:
return VulkanDepthLayout::UNDEFINED;
}
}
inline VkImageLayout toVkImageLayout(VulkanDepthLayout layout) {
switch (layout) {
case VulkanDepthLayout::GENERAL: return VK_IMAGE_LAYOUT_GENERAL;
case VulkanDepthLayout::READ_ONLY: return VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL;
default: return VK_IMAGE_LAYOUT_UNDEFINED;
case VulkanDepthLayout::ATTACHMENT:
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
default:
return VK_IMAGE_LAYOUT_UNDEFINED;
}
}

View File

@@ -116,7 +116,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
// VK_IMAGE_LAYOUT_PREINITIALIZED or VK_IMAGE_LAYOUT_GENERAL layout. Calling
// vkGetImageSubresourceLayout for a linear image returns a subresource layout mapping that is
// valid for either of those image layouts."
transitionImageLayout(cmdbuffer, blitterTransitionHelper({
transitionImageLayout(cmdbuffer, textureTransitionHelper({
.image = image->image,
.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,

View File

@@ -632,11 +632,8 @@ VkImageViewType getImageViewType(SamplerType target) {
// exceptions for depth and for transient use of specialized layouts, which is why VulkanTexture
// tracks actual layout at the subresource level.
VkImageLayout getDefaultImageLayout(TextureUsage usage) {
// Filament sometimes samples from depth while it is bound to the current render target, (e.g.
// SSAO does this while depth writes are disabled) so let's keep it simple and use GENERAL for
// all depth textures.
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
return VK_IMAGE_LAYOUT_GENERAL;
return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
}
// Filament sometimes samples from one miplevel while writing to another level in the same
@@ -676,29 +673,8 @@ void transitionImageLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition tra
nullptr, 1, &barrier);
}
VulkanLayoutTransition blitterTransitionHelper(VulkanLayoutTransition transition) {
switch (transition.newLayout) {
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
transition.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
default:
transition.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
transition.dstAccessMask = 0;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
break;
}
return transition;
}
VulkanLayoutTransition textureTransitionHelper(VulkanLayoutTransition transition) {
const bool isTransferSrc = transition.oldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
switch (transition.newLayout) {
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
transition.srcAccessMask = 0;
@@ -714,13 +690,20 @@ VulkanLayoutTransition textureTransitionHelper(VulkanLayoutTransition transition
break;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
case VK_IMAGE_LAYOUT_GENERAL:
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL:
transition.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
transition.srcAccessMask
= isTransferSrc ? VK_ACCESS_TRANSFER_READ_BIT : VK_ACCESS_TRANSFER_WRITE_BIT;
transition.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
break;
case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
transition.srcAccessMask
= isTransferSrc ? VK_ACCESS_TRANSFER_READ_BIT : VK_ACCESS_TRANSFER_WRITE_BIT;
transition.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
| VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
transition.srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
transition.dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
break;
// We support PRESENT as a target layout to allow blitting from the swap chain.
// See also SwapChain::makePresentable().
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
@@ -765,7 +748,6 @@ bool isDepthFormat(VkFormat format) {
static uint32_t mostSignificantBit(uint32_t x) { return 1ul << (31ul - utils::clz(x)); }
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
assert_invariant(utils::popcount(sampleCount) == 1);
if (sampleCount & mask) {
return sampleCount;
}

View File

@@ -56,11 +56,6 @@ VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags);
void transitionImageLayout(VkCommandBuffer cmdbuffer, VulkanLayoutTransition transition);
// Helper function for populating barrier fields based on the desired image layout.
// This logic is specific to blitting.
VulkanLayoutTransition blitterTransitionHelper(VulkanLayoutTransition transition);
// Helper function for populating barrier fields based on the desired image layout.
// This logic is specific to texturing.
VulkanLayoutTransition textureTransitionHelper(VulkanLayoutTransition transition);
bool equivalent(const VkRect2D& a, const VkRect2D& b);