unplugged-system/device/google/cuttlefish/host/libs/graphics_detector/vk.cpp

1060 lines
40 KiB
C++

/*
* Copyright (C) 2021 The Android Open Source Project
*
* 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "host/libs/graphics_detector/vk.h"
#include <string>
#include <unordered_set>
#include <vector>
#include <android-base/logging.h>
#include <android-base/strings.h>
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
namespace cuttlefish {
namespace {
constexpr const bool kEnableValidationLayers = false;
static VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void*) {
if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
LOG(VERBOSE) << pCallbackData->pMessage;
} else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
LOG(INFO) << pCallbackData->pMessage;
} else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
LOG(ERROR) << pCallbackData->pMessage;
} else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
LOG(ERROR) << pCallbackData->pMessage;
}
return VK_FALSE;
}
uint32_t GetMemoryType(const vk::raii::PhysicalDevice& physical_device,
uint32_t memory_type_mask,
vk::MemoryPropertyFlags memory_properties) {
const auto props = physical_device.getMemoryProperties();
for (uint32_t i = 0; i < props.memoryTypeCount; i++) {
if (!(memory_type_mask & (1 << i))) {
continue;
}
if ((props.memoryTypes[i].propertyFlags & memory_properties) !=
memory_properties) {
continue;
}
return i;
}
return -1;
}
VkExpected<Vk::BufferWithMemory> DoCreateBuffer(
const vk::raii::PhysicalDevice& physical_device,
const vk::raii::Device& device, vk::DeviceSize buffer_size,
vk::BufferUsageFlags buffer_usages,
vk::MemoryPropertyFlags buffer_memory_properties) {
const vk::BufferCreateInfo buffer_create_info = {
.size = static_cast<VkDeviceSize>(buffer_size),
.usage = buffer_usages,
.sharingMode = vk::SharingMode::eExclusive,
};
auto buffer = VK_EXPECT(vk::raii::Buffer::create(device, buffer_create_info));
const auto buffer_memory_requirements = buffer.getMemoryRequirements();
const auto buffer_memory_type =
GetMemoryType(physical_device, buffer_memory_requirements.memoryTypeBits,
buffer_memory_properties);
const vk::MemoryAllocateInfo buffer_memory_allocate_info = {
.allocationSize = buffer_memory_requirements.size,
.memoryTypeIndex = buffer_memory_type,
};
auto buffer_memory = VK_EXPECT(
vk::raii::DeviceMemory::create(device, buffer_memory_allocate_info));
buffer.bindMemory(*buffer_memory, 0);
return Vk::BufferWithMemory{
.buffer = std::move(buffer),
.buffer_memory = std::move(buffer_memory),
};
}
} // namespace
/*static*/
std::optional<Vk> Vk::Load(
const std::vector<std::string>& requested_instance_extensions,
const std::vector<std::string>& requested_instance_layers,
const std::vector<std::string>& requested_device_extensions) {
VkExpected<Vk> vk =
LoadImpl(requested_instance_extensions, requested_instance_layers,
requested_device_extensions);
if (vk.ok()) {
return std::move(vk.value());
}
return std::nullopt;
}
/*static*/
VkExpected<Vk> Vk::LoadImpl(
const std::vector<std::string>& requested_instance_extensions,
const std::vector<std::string>& requested_instance_layers,
const std::vector<std::string>& requested_device_extensions) {
vk::DynamicLoader loader;
VULKAN_HPP_DEFAULT_DISPATCHER.init(
loader.getProcAddress<PFN_vkGetInstanceProcAddr>(
"vkGetInstanceProcAddr"));
vk::raii::Context context;
const auto available_instance_layers =
context.enumerateInstanceLayerProperties();
LOG(VERBOSE) << "Available instance layers:";
for (const vk::LayerProperties& layer : available_instance_layers) {
LOG(VERBOSE) << layer.layerName;
}
LOG(VERBOSE) << "";
std::vector<const char*> requested_instance_extensions_chars;
requested_instance_extensions_chars.reserve(
requested_instance_extensions.size());
for (const auto& e : requested_instance_extensions) {
requested_instance_extensions_chars.push_back(e.c_str());
}
if (kEnableValidationLayers) {
requested_instance_extensions_chars.push_back(
VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
std::vector<const char*> requested_instance_layers_chars;
requested_instance_layers_chars.reserve(requested_instance_layers.size());
for (const auto& l : requested_instance_layers) {
requested_instance_layers_chars.push_back(l.c_str());
}
const vk::ApplicationInfo applicationInfo{
.pApplicationName = "Cuttlefish Graphics Detector",
.applicationVersion = 1,
.pEngineName = "Cuttlefish Graphics Detector",
.engineVersion = 1,
.apiVersion = VK_API_VERSION_1_2,
};
const vk::InstanceCreateInfo instance_create_info{
.pApplicationInfo = &applicationInfo,
.enabledLayerCount =
static_cast<uint32_t>(requested_instance_layers_chars.size()),
.ppEnabledLayerNames = requested_instance_layers_chars.data(),
.enabledExtensionCount =
static_cast<uint32_t>(requested_instance_extensions_chars.size()),
.ppEnabledExtensionNames = requested_instance_extensions_chars.data(),
};
auto instance =
VK_EXPECT(vk::raii::Instance::create(context, instance_create_info));
std::optional<vk::raii::DebugUtilsMessengerEXT> debug_messenger;
if (kEnableValidationLayers) {
const vk::DebugUtilsMessengerCreateInfoEXT debug_create_info = {
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
vk::DebugUtilsMessageSeverityFlagBitsEXT::eError,
.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
.pfnUserCallback = VulkanDebugCallback,
.pUserData = nullptr,
};
debug_messenger = VK_EXPECT(
vk::raii::DebugUtilsMessengerEXT::create(instance, debug_create_info));
}
auto physical_devices =
VK_EXPECT(vk::raii::PhysicalDevices::create(instance));
LOG(VERBOSE) << "Available physical devices:";
for (const auto& physical_device : physical_devices) {
const auto physical_device_props = physical_device.getProperties();
LOG(VERBOSE) << physical_device_props.deviceName;
}
LOG(VERBOSE) << "";
vk::raii::PhysicalDevice physical_device = std::move(physical_devices[0]);
{
const auto props = physical_device.getProperties();
LOG(VERBOSE) << "Selected physical device: " << props.deviceName;
LOG(VERBOSE) << "";
}
std::unordered_set<std::string> available_device_extensions;
{
const auto exts = physical_device.enumerateDeviceExtensionProperties();
LOG(VERBOSE) << "Available physical device extensions:";
for (const auto& ext : exts) {
LOG(VERBOSE) << ext.extensionName;
available_device_extensions.emplace(ext.extensionName);
}
LOG(VERBOSE) << "";
}
const auto features2 =
physical_device
.getFeatures2<vk::PhysicalDeviceFeatures2, //
vk::PhysicalDeviceSamplerYcbcrConversionFeatures>();
bool ycbcr_conversion_needed = false;
std::vector<const char*> requested_device_extensions_chars;
requested_device_extensions_chars.reserve(requested_device_extensions.size());
for (const auto& e : requested_device_extensions) {
if (e == std::string(VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME)) {
// The interface of VK_KHR_sampler_ycbcr_conversion was promoted to core
// in Vulkan 1.1 but the feature/functionality is still optional. Check
// here:
const auto& sampler_features =
features2.get<vk::PhysicalDeviceSamplerYcbcrConversionFeatures>();
if (sampler_features.samplerYcbcrConversion == VK_FALSE) {
LOG(VERBOSE) << "Requested device extension " << e
<< " feature not available.";
return android::base::unexpected(vk::Result::eErrorExtensionNotPresent);
}
ycbcr_conversion_needed = true;
} else {
if (available_device_extensions.find(e) ==
available_device_extensions.end()) {
LOG(VERBOSE) << "Requested device extensions " << e
<< " not available.";
return android::base::unexpected(vk::Result::eErrorExtensionNotPresent);
}
requested_device_extensions_chars.push_back(e.c_str());
}
}
uint32_t queue_family_index = -1;
{
const auto props = physical_device.getQueueFamilyProperties();
for (uint32_t i = 0; i < props.size(); i++) {
const auto& prop = props[i];
if (prop.queueFlags & vk::QueueFlagBits::eGraphics) {
queue_family_index = i;
break;
}
}
}
LOG(VERBOSE) << "Graphics queue family index: " << queue_family_index;
const float queue_priority = 1.0f;
const vk::DeviceQueueCreateInfo device_queue_create_info = {
.queueFamilyIndex = queue_family_index,
.queueCount = 1,
.pQueuePriorities = &queue_priority,
};
const vk::PhysicalDeviceVulkan11Features device_enable_features = {
.samplerYcbcrConversion = ycbcr_conversion_needed,
};
const vk::DeviceCreateInfo device_create_info = {
.pNext = &device_enable_features,
.pQueueCreateInfos = &device_queue_create_info,
.queueCreateInfoCount = 1,
.enabledLayerCount =
static_cast<uint32_t>(requested_instance_layers_chars.size()),
.ppEnabledLayerNames = requested_instance_layers_chars.data(),
.enabledExtensionCount =
static_cast<uint32_t>(requested_device_extensions_chars.size()),
.ppEnabledExtensionNames = requested_device_extensions_chars.data(),
};
auto device =
VK_EXPECT(vk::raii::Device::create(physical_device, device_create_info));
auto queue = vk::raii::Queue(device, queue_family_index, 0);
const vk::CommandPoolCreateInfo command_pool_create_info = {
.queueFamilyIndex = queue_family_index,
};
auto command_pool = VK_EXPECT(
vk::raii::CommandPool::create(device, command_pool_create_info));
auto staging_buffer =
VK_EXPECT(DoCreateBuffer(physical_device, device, kStagingBufferSize,
vk::BufferUsageFlagBits::eTransferDst |
vk::BufferUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eHostVisible |
vk::MemoryPropertyFlagBits::eHostCoherent));
return Vk(std::move(loader), std::move(context), std::move(instance),
std::move(debug_messenger), std::move(physical_device),
std::move(device), std::move(queue), queue_family_index,
std::move(command_pool), std::move(staging_buffer.buffer),
std::move(staging_buffer.buffer_memory));
}
VkExpected<Vk::BufferWithMemory> Vk::CreateBuffer(
vk::DeviceSize buffer_size, vk::BufferUsageFlags buffer_usages,
vk::MemoryPropertyFlags buffer_memory_properties) {
return DoCreateBuffer(vk_physical_device, vk_device, buffer_size,
buffer_usages, buffer_memory_properties);
}
VkExpected<Vk::BufferWithMemory> Vk::CreateBufferWithData(
vk::DeviceSize buffer_size, vk::BufferUsageFlags buffer_usages,
vk::MemoryPropertyFlags buffer_memory_properties,
const uint8_t* buffer_data) {
auto buffer = VK_EXPECT(CreateBuffer(
buffer_size, buffer_usages | vk::BufferUsageFlagBits::eTransferDst,
buffer_memory_properties));
void* mapped = vk_staging_buffer_memory_.mapMemory(0, kStagingBufferSize);
if (mapped == nullptr) {
LOG(FATAL) << "Failed to map staging buffer.";
}
std::memcpy(mapped, buffer_data, buffer_size);
vk_staging_buffer_memory_.unmapMemory();
DoCommandsImmediate([&](vk::raii::CommandBuffer& cmd) {
const std::vector<vk::BufferCopy> regions = {
vk::BufferCopy{
.srcOffset = 0,
.dstOffset = 0,
.size = buffer_size,
},
};
cmd.copyBuffer(*vk_staging_buffer_, *buffer.buffer, regions);
return vk::Result::eSuccess;
});
return std::move(buffer);
}
VkExpected<Vk::ImageWithMemory> Vk::CreateImage(
uint32_t width, uint32_t height, vk::Format format,
vk::ImageUsageFlags usages, vk::MemoryPropertyFlags memory_properties,
vk::ImageLayout returned_layout) {
const vk::ImageCreateInfo image_create_info = {
.imageType = vk::ImageType::e2D,
.extent.width = width,
.extent.height = height,
.extent.depth = 1,
.mipLevels = 1,
.arrayLayers = 1,
.format = format,
.tiling = vk::ImageTiling::eOptimal,
.initialLayout = vk::ImageLayout::eUndefined,
.usage = usages,
.sharingMode = vk::SharingMode::eExclusive,
.samples = vk::SampleCountFlagBits::e1,
};
auto image = VK_EXPECT(vk::raii::Image::create(vk_device, image_create_info));
vk::MemoryRequirements memory_requirements = image.getMemoryRequirements();
const uint32_t memory_index =
GetMemoryType(vk_physical_device, memory_requirements.memoryTypeBits,
memory_properties);
const vk::MemoryAllocateInfo image_memory_allocate_info = {
.allocationSize = memory_requirements.size,
.memoryTypeIndex = memory_index,
};
auto image_memory = VK_EXPECT(
vk::raii::DeviceMemory::create(vk_device, image_memory_allocate_info));
image.bindMemory(*image_memory, 0);
const vk::ImageViewCreateInfo image_view_create_info = {
.image = *image,
.viewType = vk::ImageViewType::e2D,
.format = format,
.components =
{
.r = vk::ComponentSwizzle::eIdentity,
.g = vk::ComponentSwizzle::eIdentity,
.b = vk::ComponentSwizzle::eIdentity,
.a = vk::ComponentSwizzle::eIdentity,
},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
auto image_view =
VK_EXPECT(vk::raii::ImageView::create(vk_device, image_view_create_info));
VK_ASSERT(DoCommandsImmediate([&](vk::raii::CommandBuffer& command_buffer) {
const std::vector<vk::ImageMemoryBarrier> image_memory_barriers = {
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = returned_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = {},
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
},
};
command_buffer.pipelineBarrier(
/*srcStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/image_memory_barriers);
return vk::Result::eSuccess;
}));
return ImageWithMemory{
.image_memory = std::move(image_memory),
.image = std::move(image),
.image_view = std::move(image_view),
};
}
vk::Result Vk::DownloadImage(uint32_t width, uint32_t height,
const vk::raii::Image& image,
vk::ImageLayout current_layout,
vk::ImageLayout returned_layout,
std::vector<uint8_t>* out_pixels) {
VK_RETURN_IF_NOT_SUCCESS(
DoCommandsImmediate([&](vk::raii::CommandBuffer& command_buffer) {
if (current_layout != vk::ImageLayout::eTransferSrcOptimal) {
const std::vector<vk::ImageMemoryBarrier> image_memory_barriers = {
vk::ImageMemoryBarrier{
.oldLayout = current_layout,
.newLayout = vk::ImageLayout::eTransferSrcOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = vk::AccessFlagBits::eMemoryRead |
vk::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vk::AccessFlagBits::eTransferRead,
},
};
command_buffer.pipelineBarrier(
/*srcStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/image_memory_barriers);
}
const std::vector<vk::BufferImageCopy> regions = {
vk::BufferImageCopy{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset =
{
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent =
{
.width = width,
.height = height,
.depth = 1,
},
},
};
command_buffer.copyImageToBuffer(*image,
vk::ImageLayout::eTransferSrcOptimal,
*vk_staging_buffer_, regions);
if (returned_layout != vk::ImageLayout::eTransferSrcOptimal) {
const std::vector<vk::ImageMemoryBarrier> image_memory_barriers = {
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eTransferSrcOptimal,
.newLayout = returned_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = vk::AccessFlagBits::eTransferRead,
.dstAccessMask = vk::AccessFlagBits::eMemoryRead |
vk::AccessFlagBits::eMemoryWrite,
},
};
command_buffer.pipelineBarrier(
/*srcStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/image_memory_barriers);
}
return vk::Result::eSuccess;
}));
auto* mapped = reinterpret_cast<uint8_t*>(
vk_staging_buffer_memory_.mapMemory(0, kStagingBufferSize));
if (mapped == nullptr) {
LOG(ERROR) << "Failed to map staging buffer.";
return vk::Result::eErrorMemoryMapFailed;
}
out_pixels->clear();
out_pixels->resize(width * height * 4);
std::memcpy(out_pixels->data(), mapped, out_pixels->size());
vk_staging_buffer_memory_.unmapMemory();
return vk::Result::eSuccess;
}
VkExpected<Vk::YuvImageWithMemory> Vk::CreateYuvImage(
uint32_t width, uint32_t height, vk::ImageUsageFlags usages,
vk::MemoryPropertyFlags memory_properties, vk::ImageLayout layout) {
const vk::SamplerYcbcrConversionCreateInfo conversion_create_info = {
.format = vk::Format::eG8B8R83Plane420Unorm,
.ycbcrModel = vk::SamplerYcbcrModelConversion::eYcbcr601,
.ycbcrRange = vk::SamplerYcbcrRange::eItuNarrow,
.components =
{
.r = vk::ComponentSwizzle::eIdentity,
.g = vk::ComponentSwizzle::eIdentity,
.b = vk::ComponentSwizzle::eIdentity,
.a = vk::ComponentSwizzle::eIdentity,
},
.xChromaOffset = vk::ChromaLocation::eMidpoint,
.yChromaOffset = vk::ChromaLocation::eMidpoint,
.chromaFilter = vk::Filter::eLinear,
.forceExplicitReconstruction = VK_FALSE,
};
auto image_sampler_conversion =
VK_EXPECT(vk::raii::SamplerYcbcrConversion::create(
vk_device, conversion_create_info));
const vk::SamplerYcbcrConversionInfo sampler_conversion_info = {
.conversion = *image_sampler_conversion,
};
const vk::SamplerCreateInfo sampler_create_info = {
.pNext = &sampler_conversion_info,
.magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eNearest,
.addressModeU = vk::SamplerAddressMode::eClampToEdge,
.addressModeV = vk::SamplerAddressMode::eClampToEdge,
.addressModeW = vk::SamplerAddressMode::eClampToEdge,
.mipLodBias = 0.0f,
.anisotropyEnable = VK_FALSE,
.maxAnisotropy = 1.0f,
.compareEnable = VK_FALSE,
.compareOp = vk::CompareOp::eLessOrEqual,
.minLod = 0.0f,
.maxLod = 0.25f,
.borderColor = vk::BorderColor::eIntTransparentBlack,
.unnormalizedCoordinates = VK_FALSE,
};
auto image_sampler =
VK_EXPECT(vk::raii::Sampler::create(vk_device, sampler_create_info));
const vk::ImageCreateInfo image_create_info = {
.imageType = vk::ImageType::e2D,
.extent.width = width,
.extent.height = height,
.extent.depth = 1,
.mipLevels = 1,
.arrayLayers = 1,
.format = vk::Format::eG8B8R83Plane420Unorm,
.tiling = vk::ImageTiling::eOptimal,
.initialLayout = vk::ImageLayout::eUndefined,
.usage = usages,
.sharingMode = vk::SharingMode::eExclusive,
.samples = vk::SampleCountFlagBits::e1,
};
auto image = VK_EXPECT(vk::raii::Image::create(vk_device, image_create_info));
vk::MemoryRequirements memory_requirements = image.getMemoryRequirements();
const uint32_t memory_index =
GetMemoryType(vk_physical_device, memory_requirements.memoryTypeBits,
memory_properties);
const vk::MemoryAllocateInfo image_memory_allocate_info = {
.allocationSize = memory_requirements.size,
.memoryTypeIndex = memory_index,
};
auto image_memory = VK_EXPECT(
vk::raii::DeviceMemory::create(vk_device, image_memory_allocate_info));
image.bindMemory(*image_memory, 0);
const vk::ImageViewCreateInfo image_view_create_info = {
.pNext = &sampler_conversion_info,
.image = *image,
.viewType = vk::ImageViewType::e2D,
.format = vk::Format::eG8B8R83Plane420Unorm,
.components =
{
.r = vk::ComponentSwizzle::eIdentity,
.g = vk::ComponentSwizzle::eIdentity,
.b = vk::ComponentSwizzle::eIdentity,
.a = vk::ComponentSwizzle::eIdentity,
},
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
auto image_view =
VK_EXPECT(vk::raii::ImageView::create(vk_device, image_view_create_info));
VK_ASSERT(DoCommandsImmediate([&](vk::raii::CommandBuffer& command_buffer) {
const std::vector<vk::ImageMemoryBarrier> image_memory_barriers = {
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eUndefined,
.newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = {},
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
},
};
command_buffer.pipelineBarrier(
/*srcStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/image_memory_barriers);
return vk::Result::eSuccess;
}));
return YuvImageWithMemory{
.image_sampler_conversion = std::move(image_sampler_conversion),
.image_sampler = std::move(image_sampler),
.image_memory = std::move(image_memory),
.image = std::move(image),
.image_view = std::move(image_view),
};
}
vk::Result Vk::LoadYuvImage(const vk::raii::Image& image, uint32_t width,
uint32_t height,
const std::vector<uint8_t>& image_data_y,
const std::vector<uint8_t>& image_data_u,
const std::vector<uint8_t>& image_data_v,
vk::ImageLayout current_layout,
vk::ImageLayout returned_layout) {
auto* mapped = reinterpret_cast<uint8_t*>(
vk_staging_buffer_memory_.mapMemory(0, kStagingBufferSize));
if (mapped == nullptr) {
LOG(ERROR) << "Failed to map staging buffer.";
return vk::Result::eErrorMemoryMapFailed;
}
const VkDeviceSize y_offset = 0;
const VkDeviceSize u_offset = image_data_y.size();
const VkDeviceSize v_offset = image_data_y.size() + image_data_u.size();
std::memcpy(mapped + y_offset, image_data_y.data(), image_data_y.size());
std::memcpy(mapped + u_offset, image_data_u.data(), image_data_u.size());
std::memcpy(mapped + v_offset, image_data_v.data(), image_data_v.size());
vk_staging_buffer_memory_.unmapMemory();
return DoCommandsImmediate([&](vk::raii::CommandBuffer& command_buffer) {
if (current_layout != vk::ImageLayout::eTransferDstOptimal) {
const std::vector<vk::ImageMemoryBarrier> image_memory_barriers = {
vk::ImageMemoryBarrier{
.oldLayout = current_layout,
.newLayout = vk::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = vk::AccessFlagBits::eMemoryRead |
vk::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vk::AccessFlagBits::eTransferWrite,
},
};
command_buffer.pipelineBarrier(
/*srcStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/image_memory_barriers);
}
const std::vector<vk::BufferImageCopy> image_copy_regions = {
vk::BufferImageCopy{
.bufferOffset = y_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::ePlane0,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset =
{
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent =
{
.width = width,
.height = height,
.depth = 1,
},
},
vk::BufferImageCopy{
.bufferOffset = u_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::ePlane1,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset =
{
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent =
{
.width = width / 2,
.height = height / 2,
.depth = 1,
},
},
vk::BufferImageCopy{
.bufferOffset = v_offset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = vk::ImageAspectFlagBits::ePlane2,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset =
{
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent =
{
.width = width / 2,
.height = height / 2,
.depth = 1,
},
},
};
command_buffer.copyBufferToImage(*vk_staging_buffer_, *image,
vk::ImageLayout::eTransferDstOptimal,
image_copy_regions);
if (returned_layout != vk::ImageLayout::eTransferDstOptimal) {
const std::vector<vk::ImageMemoryBarrier> image_memory_barriers = {
vk::ImageMemoryBarrier{
.oldLayout = vk::ImageLayout::eTransferDstOptimal,
.newLayout = returned_layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange =
{
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
.srcAccessMask = vk::AccessFlagBits::eTransferWrite,
.dstAccessMask = vk::AccessFlagBits::eMemoryRead |
vk::AccessFlagBits::eMemoryWrite,
},
};
command_buffer.pipelineBarrier(
/*srcStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vk::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/image_memory_barriers);
}
return vk::Result::eSuccess;
});
}
VkExpected<Vk::FramebufferWithAttachments> Vk::CreateFramebuffer(
uint32_t width, uint32_t height, vk::Format color_format,
vk::Format depth_format) {
std::optional<Vk::ImageWithMemory> color_attachment;
if (color_format != vk::Format::eUndefined) {
color_attachment =
VK_EXPECT(CreateImage(width, height, color_format,
vk::ImageUsageFlagBits::eColorAttachment |
vk::ImageUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eDeviceLocal,
vk::ImageLayout::eColorAttachmentOptimal));
}
std::optional<Vk::ImageWithMemory> depth_attachment;
if (depth_format != vk::Format::eUndefined) {
depth_attachment =
VK_EXPECT(CreateImage(width, height, depth_format,
vk::ImageUsageFlagBits::eDepthStencilAttachment |
vk::ImageUsageFlagBits::eTransferSrc,
vk::MemoryPropertyFlagBits::eDeviceLocal,
vk::ImageLayout::eDepthStencilAttachmentOptimal));
}
std::vector<vk::AttachmentDescription> attachments;
std::optional<vk::AttachmentReference> color_attachment_reference;
if (color_format != vk::Format::eUndefined) {
attachments.push_back(vk::AttachmentDescription{
.format = color_format,
.samples = vk::SampleCountFlagBits::e1,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.stencilLoadOp = vk::AttachmentLoadOp::eClear,
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
.initialLayout = vk::ImageLayout::eColorAttachmentOptimal,
.finalLayout = vk::ImageLayout::eColorAttachmentOptimal,
});
color_attachment_reference = vk::AttachmentReference{
.attachment = static_cast<uint32_t>(attachments.size() - 1),
.layout = vk::ImageLayout::eColorAttachmentOptimal,
};
}
std::optional<vk::AttachmentReference> depth_attachment_reference;
if (depth_format != vk::Format::eUndefined) {
attachments.push_back(vk::AttachmentDescription{
.format = depth_format,
.samples = vk::SampleCountFlagBits::e1,
.loadOp = vk::AttachmentLoadOp::eClear,
.storeOp = vk::AttachmentStoreOp::eStore,
.stencilLoadOp = vk::AttachmentLoadOp::eClear,
.stencilStoreOp = vk::AttachmentStoreOp::eStore,
.initialLayout = vk::ImageLayout::eColorAttachmentOptimal,
.finalLayout = vk::ImageLayout::eColorAttachmentOptimal,
});
depth_attachment_reference = vk::AttachmentReference{
.attachment = static_cast<uint32_t>(attachments.size() - 1),
.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal,
};
}
vk::SubpassDependency dependency = {
.srcSubpass = 0,
.dstSubpass = 0,
.srcStageMask = {},
.dstStageMask = vk::PipelineStageFlagBits::eFragmentShader,
.srcAccessMask = {},
.dstAccessMask = vk::AccessFlagBits::eInputAttachmentRead,
.dependencyFlags = vk::DependencyFlagBits::eByRegion,
};
if (color_format != vk::Format::eUndefined) {
dependency.srcStageMask |=
vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.dstStageMask |=
vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.srcAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite;
}
if (depth_format != vk::Format::eUndefined) {
dependency.srcStageMask |=
vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.dstStageMask |=
vk::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.srcAccessMask |= vk::AccessFlagBits::eColorAttachmentWrite;
}
vk::SubpassDescription subpass = {
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = 0,
.pColorAttachments = nullptr,
.pResolveAttachments = nullptr,
.pDepthStencilAttachment = nullptr,
.pPreserveAttachments = nullptr,
};
if (color_format != vk::Format::eUndefined) {
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &*color_attachment_reference;
}
if (depth_format != vk::Format::eUndefined) {
subpass.pDepthStencilAttachment = &*depth_attachment_reference;
}
const vk::RenderPassCreateInfo renderpass_create_info = {
.attachmentCount = static_cast<uint32_t>(attachments.size()),
.pAttachments = attachments.data(),
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
.pDependencies = &dependency,
};
auto renderpass = VK_EXPECT(
vk::raii::RenderPass::create(vk_device, renderpass_create_info));
std::vector<vk::ImageView> frammebuffer_attachments;
if (color_attachment) {
frammebuffer_attachments.push_back(*color_attachment->image_view);
}
if (depth_attachment) {
frammebuffer_attachments.push_back(*depth_attachment->image_view);
}
const vk::FramebufferCreateInfo framebuffer_create_info = {
.renderPass = *renderpass,
.attachmentCount = static_cast<uint32_t>(frammebuffer_attachments.size()),
.pAttachments = frammebuffer_attachments.data(),
.width = width,
.height = height,
.layers = 1,
};
auto framebuffer = VK_EXPECT(
vk::raii::Framebuffer::create(vk_device, framebuffer_create_info));
return Vk::FramebufferWithAttachments{
.color_attachment = std::move(color_attachment),
.depth_attachment = std::move(depth_attachment),
.renderpass = std::move(renderpass),
.framebuffer = std::move(framebuffer),
};
}
vk::Result Vk::DoCommandsImmediate(
const std::function<vk::Result(vk::raii::CommandBuffer&)>& func,
const std::vector<vk::raii::Semaphore>& semaphores_wait,
const std::vector<vk::raii::Semaphore>& semaphores_signal) {
const vk::CommandBufferAllocateInfo command_buffer_allocate_info = {
.level = vk::CommandBufferLevel::ePrimary,
.commandPool = *vk_command_pool_,
.commandBufferCount = 1,
};
auto command_buffers = VK_EXPECT_RESULT(vk::raii::CommandBuffers::create(
vk_device, command_buffer_allocate_info));
auto command_buffer = std::move(command_buffers[0]);
const vk::CommandBufferBeginInfo command_buffer_begin_info = {
.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit,
};
command_buffer.begin(command_buffer_begin_info);
VK_RETURN_IF_NOT_SUCCESS(func(command_buffer));
command_buffer.end();
std::vector<vk::CommandBuffer> command_buffer_handles;
command_buffer_handles.push_back(*command_buffer);
std::vector<vk::Semaphore> semaphores_handles_wait;
semaphores_handles_wait.reserve(semaphores_wait.size());
for (const auto& s : semaphores_wait) {
semaphores_handles_wait.emplace_back(*s);
}
std::vector<vk::Semaphore> semaphores_handles_signal;
semaphores_handles_signal.reserve(semaphores_signal.size());
for (const auto& s : semaphores_signal) {
semaphores_handles_signal.emplace_back(*s);
}
vk::SubmitInfo submit_info = {
.commandBufferCount =
static_cast<uint32_t>(command_buffer_handles.size()),
.pCommandBuffers = command_buffer_handles.data(),
};
if (!semaphores_handles_wait.empty()) {
submit_info.waitSemaphoreCount =
static_cast<uint32_t>(semaphores_handles_wait.size());
submit_info.pWaitSemaphores = semaphores_handles_wait.data();
}
if (!semaphores_handles_signal.empty()) {
submit_info.signalSemaphoreCount =
static_cast<uint32_t>(semaphores_handles_signal.size());
submit_info.pSignalSemaphores = semaphores_handles_signal.data();
}
vk_queue.submit(submit_info);
vk_queue.waitIdle();
return vk::Result::eSuccess;
}
} // namespace cuttlefish