blob: c35082afac2cb09ed5a3062461d5fb2b6671118c [file] [log] [blame]
/*
* Copyright (c) 2023-2025 Valve Corporation
* Copyright (c) 2023-2025 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include "sync_helper.h"
VkDependencyInfo DependencyInfo(const VkMemoryBarrier2 &barrier, VkDependencyFlags dependency_flags) {
VkDependencyInfo dep_info = vku::InitStructHelper();
dep_info.dependencyFlags = dependency_flags;
dep_info.memoryBarrierCount = 1;
dep_info.pMemoryBarriers = &barrier;
return dep_info;
}
VkDependencyInfo DependencyInfo(const VkBufferMemoryBarrier2 &buffer_barrier, VkDependencyFlags dependency_flags) {
VkDependencyInfo dep_info = vku::InitStructHelper();
dep_info.dependencyFlags = dependency_flags;
dep_info.bufferMemoryBarrierCount = 1;
dep_info.pBufferMemoryBarriers = &buffer_barrier;
return dep_info;
}
VkDependencyInfo DependencyInfo(const VkImageMemoryBarrier2 &image_barrier, VkDependencyFlags dependency_flags) {
VkDependencyInfo dep_info = vku::InitStructHelper();
dep_info.dependencyFlags = dependency_flags;
dep_info.imageMemoryBarrierCount = 1;
dep_info.pImageMemoryBarriers = &image_barrier;
return dep_info;
}
BarrierQueueFamilyBase::QueueFamilyObjs::~QueueFamilyObjs() {
delete command_buffer2;
delete command_buffer;
delete command_pool;
delete queue;
}
void BarrierQueueFamilyBase::QueueFamilyObjs::Init(vkt::Device *device, uint32_t qf_index, VkQueue qf_queue,
VkCommandPoolCreateFlags cp_flags) {
index = qf_index;
queue = new vkt::Queue(qf_queue, qf_index);
command_pool = new vkt::CommandPool(*device, qf_index, cp_flags);
command_buffer = new vkt::CommandBuffer(*device, *command_pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
command_buffer2 = new vkt::CommandBuffer(*device, *command_pool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
}
BarrierQueueFamilyBase::Context::Context(VkLayerTest *test, const std::vector<uint32_t> &queue_family_indices) : layer_test(test) {
if (0 == queue_family_indices.size()) {
return; // This is invalid
}
vkt::Device *device_obj = layer_test->DeviceObj();
queue_families.reserve(queue_family_indices.size());
default_index = queue_family_indices[0];
for (auto qfi : queue_family_indices) {
VkQueue queue = device_obj->QueuesFromFamily(qfi)[0]->handle();
queue_families.emplace(std::make_pair(qfi, QueueFamilyObjs()));
queue_families[qfi].Init(device_obj, qfi, queue, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT);
}
Reset();
}
void BarrierQueueFamilyBase::Context::Reset() {
layer_test->DeviceObj()->Wait();
for (auto &qf : queue_families) {
vk::ResetCommandPool(layer_test->device(), qf.second.command_pool->handle(), 0);
}
}
void BarrierQueueFamilyTestHelper::Init(std::vector<uint32_t> *families, bool image_memory, bool buffer_memory) {
vkt::Device *device_obj = context_->layer_test->DeviceObj();
auto image_ci = vkt::Image::ImageCreateInfo2D(
32, 32, 1, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_TILING_OPTIMAL,
families ? vvl::make_span(families->data(), families->size()) : vvl::span<uint32_t>{});
VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
if (image_memory) {
image_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
image_.Init(*device_obj, image_ci);
image_.SetLayout(image_layout);
} else {
image_.InitNoMemory(*device_obj, image_ci);
}
image_barrier_ = image_.ImageMemoryBarrier(VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, image_layout, image_layout,
VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
VkMemoryPropertyFlags mem_prop = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
auto buffer_ci = vkt::Buffer::CreateInfo(256, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
families ? vvl::make_span(families->data(), families->size()) : vvl::span<uint32_t>{});
if (buffer_memory) {
buffer_.Init(*device_obj, buffer_ci, mem_prop);
} else {
buffer_.InitNoMemory(*device_obj, buffer_ci);
}
ASSERT_TRUE(buffer_.initialized());
buffer_barrier_ = buffer_.BufferMemoryBarrier(VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0, VK_WHOLE_SIZE);
}
void Barrier2QueueFamilyTestHelper::Init(bool image_memory, bool buffer_memory) {
vkt::Device *device_obj = context_->layer_test->DeviceObj();
auto image_ci = vkt::Image::ImageCreateInfo2D(32, 32, 1, 1, VK_FORMAT_B8G8R8A8_UNORM, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
VkImageLayout image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
if (image_memory) {
image_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
image_.Init(*device_obj, image_ci);
image_.SetLayout(image_layout);
} else {
image_.InitNoMemory(*device_obj, image_ci);
}
image_barrier_ = vku::InitStructHelper();
image_barrier_.srcStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
image_barrier_.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
image_barrier_.dstStageMask = VK_PIPELINE_STAGE_TRANSFER_BIT;
image_barrier_.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
image_barrier_.oldLayout = image_layout;
image_barrier_.newLayout = image_layout;
image_barrier_.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_barrier_.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_barrier_.image = image_;
image_barrier_.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
VkMemoryPropertyFlags mem_prop = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
auto buffer_ci = vkt::Buffer::CreateInfo(256, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
if (buffer_memory) {
buffer_.Init(*device_obj, buffer_ci, mem_prop);
} else {
buffer_.InitNoMemory(*device_obj, buffer_ci);
}
ASSERT_TRUE(buffer_.initialized());
buffer_barrier_ = buffer_.BufferMemoryBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, 0, VK_WHOLE_SIZE);
}
BarrierQueueFamilyBase::QueueFamilyObjs *BarrierQueueFamilyBase::GetQueueFamilyInfo(Context *context, uint32_t qfi) {
QueueFamilyObjs *qf;
auto qf_it = context->queue_families.find(qfi);
if (qf_it != context->queue_families.end()) {
qf = &(qf_it->second);
} else {
qf = &(context->queue_families[context->default_index]);
}
return qf;
}
void BarrierQueueFamilyTestHelper::operator()(const std::string &img_err, const std::string &buf_err, uint32_t src, uint32_t dst,
uint32_t queue_family_index, Modifier mod) {
auto &monitor = context_->layer_test->Monitor();
const bool has_img_err = img_err.size() > 0;
const bool has_buf_err = buf_err.size() > 0;
bool positive = !has_img_err && !has_buf_err;
if (has_img_err) monitor.SetDesiredFailureMsg(kErrorBit | kWarningBit, img_err);
if (has_buf_err) monitor.SetDesiredFailureMsg(kErrorBit | kWarningBit, buf_err);
image_barrier_.srcQueueFamilyIndex = src;
image_barrier_.dstQueueFamilyIndex = dst;
buffer_barrier_.srcQueueFamilyIndex = src;
buffer_barrier_.dstQueueFamilyIndex = dst;
QueueFamilyObjs *qf = GetQueueFamilyInfo(context_, queue_family_index);
vkt::CommandBuffer *command_buffer = qf->command_buffer;
for (int cb_repeat = 0; cb_repeat < (mod == Modifier::DOUBLE_COMMAND_BUFFER ? 2 : 1); cb_repeat++) {
command_buffer->Begin();
for (int repeat = 0; repeat < (mod == Modifier::DOUBLE_RECORD ? 2 : 1); repeat++) {
vk::CmdPipelineBarrier(command_buffer->handle(), VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_DEPENDENCY_BY_REGION_BIT, 0, nullptr, 1, &buffer_barrier_, 1, &image_barrier_);
}
command_buffer->End();
command_buffer = qf->command_buffer2; // Second pass (if any) goes to the secondary command_buffer.
}
if (queue_family_index != kInvalidQueueFamily) {
if (mod == Modifier::DOUBLE_COMMAND_BUFFER) {
// no wait after submit
qf->queue->Submit({qf->command_buffer, qf->command_buffer2});
} else {
qf->queue->Submit(*qf->command_buffer);
qf->queue->Wait();
}
}
if (!positive) {
monitor.VerifyFound();
}
context_->Reset();
}
void Barrier2QueueFamilyTestHelper::operator()(const std::string &img_err, const std::string &buf_err, uint32_t src, uint32_t dst,
uint32_t queue_family_index, Modifier mod) {
auto &monitor = context_->layer_test->Monitor();
bool positive = true;
if (img_err.length()) {
monitor.SetDesiredFailureMsg(kErrorBit | kWarningBit, img_err);
positive = false;
}
if (buf_err.length()) {
monitor.SetDesiredFailureMsg(kErrorBit | kWarningBit, buf_err);
positive = false;
}
image_barrier_.srcQueueFamilyIndex = src;
image_barrier_.dstQueueFamilyIndex = dst;
buffer_barrier_.srcQueueFamilyIndex = src;
buffer_barrier_.dstQueueFamilyIndex = dst;
VkDependencyInfo dep_info = vku::InitStructHelper();
dep_info.bufferMemoryBarrierCount = 1;
dep_info.pBufferMemoryBarriers = &buffer_barrier_;
dep_info.imageMemoryBarrierCount = 1;
dep_info.pImageMemoryBarriers = &image_barrier_;
QueueFamilyObjs *qf = GetQueueFamilyInfo(context_, queue_family_index);
vkt::CommandBuffer *command_buffer = qf->command_buffer;
for (int cb_repeat = 0; cb_repeat < (mod == Modifier::DOUBLE_COMMAND_BUFFER ? 2 : 1); cb_repeat++) {
command_buffer->Begin();
for (int repeat = 0; repeat < (mod == Modifier::DOUBLE_RECORD ? 2 : 1); repeat++) {
vk::CmdPipelineBarrier2KHR(command_buffer->handle(), &dep_info);
}
command_buffer->End();
command_buffer = qf->command_buffer2; // Second pass (if any) goes to the secondary command_buffer.
}
if (queue_family_index != kInvalidQueueFamily) {
if (mod == Modifier::DOUBLE_COMMAND_BUFFER) {
// no wait after submit
qf->queue->Submit({qf->command_buffer, qf->command_buffer2});
} else {
qf->queue->Submit(*qf->command_buffer);
qf->queue->Wait();
}
}
if (!positive) {
monitor.VerifyFound();
}
context_->Reset();
}
void ValidOwnershipTransferOp(vkt::Queue *queue, vkt::CommandBuffer &cb, VkPipelineStageFlags src_stages,
VkPipelineStageFlags dst_stages, const VkBufferMemoryBarrier *buf_barrier,
const VkImageMemoryBarrier *img_barrier) {
cb.Begin();
uint32_t num_buf_barrier = (buf_barrier) ? 1 : 0;
uint32_t num_img_barrier = (img_barrier) ? 1 : 0;
vk::CmdPipelineBarrier(cb, src_stages, dst_stages, 0, 0, nullptr, num_buf_barrier, buf_barrier, num_img_barrier, img_barrier);
cb.End();
queue->Submit(cb);
queue->Wait();
}
void ValidOwnershipTransfer(vkt::Queue *queue_from, vkt::CommandBuffer &cb_from, vkt::Queue *queue_to, vkt::CommandBuffer &cb_to,
VkPipelineStageFlags src_stages, VkPipelineStageFlags dst_stages,
const VkBufferMemoryBarrier *buf_barrier, const VkImageMemoryBarrier *img_barrier) {
ValidOwnershipTransferOp(queue_from, cb_from, src_stages, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, buf_barrier, img_barrier);
ValidOwnershipTransferOp(queue_to, cb_to, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, dst_stages, buf_barrier, img_barrier);
}
void ValidOwnershipTransferOp(vkt::Queue *queue, vkt::CommandBuffer &cb, const VkBufferMemoryBarrier2 *buf_barrier,
const VkImageMemoryBarrier2 *img_barrier) {
cb.Begin();
VkDependencyInfo dep_info = vku::InitStructHelper();
dep_info.bufferMemoryBarrierCount = (buf_barrier) ? 1 : 0;
dep_info.pBufferMemoryBarriers = buf_barrier;
dep_info.imageMemoryBarrierCount = (img_barrier) ? 1 : 0;
dep_info.pImageMemoryBarriers = img_barrier;
vk::CmdPipelineBarrier2KHR(cb, &dep_info);
cb.End();
queue->Submit(cb);
queue->Wait();
}
void ValidOwnershipTransfer(vkt::Queue *queue_from, vkt::CommandBuffer &cb_from, vkt::Queue *queue_to, vkt::CommandBuffer &cb_to,
const VkBufferMemoryBarrier2 *buf_barrier, const VkImageMemoryBarrier2 *img_barrier) {
VkBufferMemoryBarrier2 fixup_buf_barrier;
VkImageMemoryBarrier2 fixup_img_barrier;
if (buf_barrier) {
fixup_buf_barrier = *buf_barrier;
fixup_buf_barrier.dstStageMask = VK_PIPELINE_STAGE_2_NONE;
fixup_buf_barrier.dstAccessMask = 0;
}
if (img_barrier) {
fixup_img_barrier = *img_barrier;
fixup_img_barrier.dstStageMask = VK_PIPELINE_STAGE_2_NONE;
fixup_img_barrier.dstAccessMask = 0;
}
ValidOwnershipTransferOp(queue_from, cb_from, buf_barrier ? &fixup_buf_barrier : nullptr,
img_barrier ? &fixup_img_barrier : nullptr);
if (buf_barrier) {
fixup_buf_barrier = *buf_barrier;
fixup_buf_barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE;
fixup_buf_barrier.srcAccessMask = 0;
}
if (img_barrier) {
fixup_img_barrier = *img_barrier;
fixup_img_barrier.srcStageMask = VK_PIPELINE_STAGE_2_NONE;
fixup_img_barrier.srcAccessMask = 0;
}
ValidOwnershipTransferOp(queue_to, cb_to, buf_barrier ? &fixup_buf_barrier : nullptr,
img_barrier ? &fixup_img_barrier : nullptr);
}