blob: c38cbf59addbce12d94348a5e8a646c737a0aeae [file] [log] [blame]
/*
* 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 "Vulkan.h"
#include <iostream>
#include <string>
#include <unordered_set>
#include <vector>
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE
namespace gfxstream {
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) {
std::cout << pCallbackData->pMessage << std::endl;
} else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
std::cout << pCallbackData->pMessage << std::endl;
} else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
std::cout << pCallbackData->pMessage << std::endl;
} else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
std::cout << pCallbackData->pMessage << std::endl;
}
return VK_FALSE;
}
uint32_t GetMemoryType(const vkhpp::PhysicalDevice& physical_device,
uint32_t memory_type_mask,
vkhpp::MemoryPropertyFlags memoryProperties) {
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 & memoryProperties) != memoryProperties) {
continue;
}
return i;
}
return -1;
}
} // namespace
gfxstream::expected<Vk::BufferWithMemory, vkhpp::Result> DoCreateBuffer(
const vkhpp::PhysicalDevice& physical_device,
const vkhpp::UniqueDevice& device, vkhpp::DeviceSize buffer_size,
vkhpp::BufferUsageFlags buffer_usages,
vkhpp::MemoryPropertyFlags bufferMemoryProperties) {
const vkhpp::BufferCreateInfo bufferCreateInfo = {
.size = static_cast<VkDeviceSize>(buffer_size),
.usage = buffer_usages,
.sharingMode = vkhpp::SharingMode::eExclusive,
};
auto buffer = VK_EXPECT_RV(device->createBufferUnique(bufferCreateInfo));
vkhpp::MemoryRequirements bufferMemoryRequirements{};
device->getBufferMemoryRequirements(*buffer, &bufferMemoryRequirements);
const auto bufferMemoryType =
GetMemoryType(physical_device,
bufferMemoryRequirements.memoryTypeBits,
bufferMemoryProperties);
const vkhpp::MemoryAllocateInfo bufferMemoryAllocateInfo = {
.allocationSize = bufferMemoryRequirements.size,
.memoryTypeIndex = bufferMemoryType,
};
auto bufferMemory = VK_EXPECT_RV(device->allocateMemoryUnique(bufferMemoryAllocateInfo));
VK_EXPECT_RESULT(device->bindBufferMemory(*buffer, *bufferMemory, 0));
return Vk::BufferWithMemory{
.buffer = std::move(buffer),
.bufferMemory = std::move(bufferMemory),
};
}
/*static*/
gfxstream::expected<Vk, vkhpp::Result> Vk::Load(
const std::vector<std::string>& requestedInstanceExtensions,
const std::vector<std::string>& requestedInstanceLayers,
const std::vector<std::string>& requestedDeviceExtensions) {
vkhpp::DynamicLoader loader;
VULKAN_HPP_DEFAULT_DISPATCHER.init(
loader.getProcAddress<PFN_vkGetInstanceProcAddr>(
"vkGetInstanceProcAddr"));
std::vector<const char*> requestedInstanceExtensionsChars;
requestedInstanceExtensionsChars.reserve(requestedInstanceExtensions.size());
for (const auto& e : requestedInstanceExtensions) {
requestedInstanceExtensionsChars.push_back(e.c_str());
}
if (kEnableValidationLayers) {
requestedInstanceExtensionsChars.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
std::vector<const char*> requestedInstanceLayersChars;
requestedInstanceLayersChars.reserve(requestedInstanceLayers.size());
for (const auto& l : requestedInstanceLayers) {
requestedInstanceLayersChars.push_back(l.c_str());
}
const vkhpp::ApplicationInfo applicationInfo = {
.pApplicationName = "Cuttlefish Graphics Detector",
.applicationVersion = 1,
.pEngineName = "Cuttlefish Graphics Detector",
.engineVersion = 1,
.apiVersion = VK_API_VERSION_1_2,
};
const vkhpp::InstanceCreateInfo instanceCreateInfo = {
.pApplicationInfo = &applicationInfo,
.enabledLayerCount = static_cast<uint32_t>(requestedInstanceLayersChars.size()),
.ppEnabledLayerNames = requestedInstanceLayersChars.data(),
.enabledExtensionCount = static_cast<uint32_t>(requestedInstanceExtensionsChars.size()),
.ppEnabledExtensionNames = requestedInstanceExtensionsChars.data(),
};
auto instance = VK_EXPECT_RV(vkhpp::createInstanceUnique(instanceCreateInfo));
VULKAN_HPP_DEFAULT_DISPATCHER.init(*instance);
std::optional<vkhpp::UniqueDebugUtilsMessengerEXT> debugMessenger;
if (kEnableValidationLayers) {
const vkhpp::DebugUtilsMessengerCreateInfoEXT debugCreateInfo = {
.messageSeverity = vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose |
vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
vkhpp::DebugUtilsMessageSeverityFlagBitsEXT::eError,
.messageType = vkhpp::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
vkhpp::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
vkhpp::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
.pfnUserCallback = VulkanDebugCallback,
.pUserData = nullptr,
};
debugMessenger = VK_EXPECT_RV(instance->createDebugUtilsMessengerEXTUnique(debugCreateInfo));
}
const auto physicalDevices = VK_EXPECT_RV(instance->enumeratePhysicalDevices());
vkhpp::PhysicalDevice physicalDevice = std::move(physicalDevices[0]);
std::unordered_set<std::string> availableDeviceExtensions;
{
const auto exts = VK_EXPECT_RV(physicalDevice.enumerateDeviceExtensionProperties());
for (const auto& ext : exts) {
availableDeviceExtensions.emplace(ext.extensionName);
}
}
const auto features2 =
physicalDevice
.getFeatures2<vkhpp::PhysicalDeviceFeatures2, //
vkhpp::PhysicalDeviceSamplerYcbcrConversionFeatures>();
bool ycbcr_conversion_needed = false;
std::vector<const char*> requestedDeviceExtensionsChars;
requestedDeviceExtensionsChars.reserve(requestedDeviceExtensions.size());
for (const auto& e : requestedDeviceExtensions) {
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<vkhpp::PhysicalDeviceSamplerYcbcrConversionFeatures>();
if (sampler_features.samplerYcbcrConversion == VK_FALSE) {
return gfxstream::unexpected(vkhpp::Result::eErrorExtensionNotPresent);
}
ycbcr_conversion_needed = true;
} else {
if (availableDeviceExtensions.find(e) == availableDeviceExtensions.end()) {
return gfxstream::unexpected(vkhpp::Result::eErrorExtensionNotPresent);
}
requestedDeviceExtensionsChars.push_back(e.c_str());
}
}
uint32_t queueFamilyIndex = -1;
{
const auto props = physicalDevice.getQueueFamilyProperties();
for (uint32_t i = 0; i < props.size(); i++) {
const auto& prop = props[i];
if (prop.queueFlags & vkhpp::QueueFlagBits::eGraphics) {
queueFamilyIndex = i;
break;
}
}
}
const float queue_priority = 1.0f;
const vkhpp::DeviceQueueCreateInfo device_queue_create_info = {
.queueFamilyIndex = queueFamilyIndex,
.queueCount = 1,
.pQueuePriorities = &queue_priority,
};
const vkhpp::PhysicalDeviceVulkan11Features device_enable_features = {
.samplerYcbcrConversion = ycbcr_conversion_needed,
};
const vkhpp::DeviceCreateInfo deviceCreateInfo = {
.pNext = &device_enable_features,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &device_queue_create_info,
.enabledLayerCount = static_cast<uint32_t>(requestedInstanceLayersChars.size()),
.ppEnabledLayerNames = requestedInstanceLayersChars.data(),
.enabledExtensionCount = static_cast<uint32_t>(requestedDeviceExtensionsChars.size()),
.ppEnabledExtensionNames = requestedDeviceExtensionsChars.data(),
};
auto device = VK_EXPECT_RV(physicalDevice.createDeviceUnique(deviceCreateInfo));
auto queue = device->getQueue(queueFamilyIndex, 0);
const vkhpp::CommandPoolCreateInfo commandPoolCreateInfo = {
.queueFamilyIndex = queueFamilyIndex,
};
auto commandPool = VK_EXPECT_RV(device->createCommandPoolUnique(commandPoolCreateInfo));
auto stagingBuffer =
VK_EXPECT(DoCreateBuffer(physicalDevice, device, kStagingBufferSize,
vkhpp::BufferUsageFlagBits::eTransferDst |
vkhpp::BufferUsageFlagBits::eTransferSrc,
vkhpp::MemoryPropertyFlagBits::eHostVisible |
vkhpp::MemoryPropertyFlagBits::eHostCoherent));
return Vk(std::move(loader),
std::move(instance),
std::move(debugMessenger),
std::move(physicalDevice),
std::move(device),
std::move(queue),
queueFamilyIndex,
std::move(commandPool),
std::move(stagingBuffer.buffer),
std::move(stagingBuffer.bufferMemory));
}
gfxstream::expected<Vk::BufferWithMemory, vkhpp::Result> Vk::CreateBuffer(
vkhpp::DeviceSize bufferSize,
vkhpp::BufferUsageFlags bufferUsages,
vkhpp::MemoryPropertyFlags bufferMemoryProperties) {
return DoCreateBuffer(mPhysicalDevice,
mDevice,
bufferSize,
bufferUsages,
bufferMemoryProperties);
}
gfxstream::expected<Vk::BufferWithMemory, vkhpp::Result> Vk::CreateBufferWithData(
vkhpp::DeviceSize bufferSize,
vkhpp::BufferUsageFlags bufferUsages,
vkhpp::MemoryPropertyFlags bufferMemoryProperties,
const uint8_t* buffer_data) {
auto buffer = VK_EXPECT(CreateBuffer(
bufferSize,
bufferUsages | vkhpp::BufferUsageFlagBits::eTransferDst,
bufferMemoryProperties));
void* mapped = VK_EXPECT_RV(mDevice->mapMemory(*mStagingBufferMemory, 0, kStagingBufferSize));
std::memcpy(mapped, buffer_data, bufferSize);
mDevice->unmapMemory(*mStagingBufferMemory);
DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
const std::vector<vkhpp::BufferCopy> regions = {
vkhpp::BufferCopy{
.srcOffset = 0,
.dstOffset = 0,
.size = bufferSize,
},
};
cmd->copyBuffer(*mStagingBuffer, *buffer.buffer, regions);
return vkhpp::Result::eSuccess;
});
return std::move(buffer);
}
gfxstream::expected<Vk::ImageWithMemory, vkhpp::Result> Vk::CreateImage(
uint32_t width,
uint32_t height,
vkhpp::Format format,
vkhpp::ImageUsageFlags usages,
vkhpp::MemoryPropertyFlags memoryProperties,
vkhpp::ImageLayout returnedLayout) {
const vkhpp::ImageCreateInfo imageCreateInfo = {
.imageType = vkhpp::ImageType::e2D,
.format = format,
.extent = {
.width = width,
.height = height,
.depth = 1,
},
.mipLevels = 1,
.arrayLayers = 1,
.samples = vkhpp::SampleCountFlagBits::e1,
.tiling = vkhpp::ImageTiling::eOptimal,
.usage = usages,
.sharingMode = vkhpp::SharingMode::eExclusive,
.initialLayout = vkhpp::ImageLayout::eUndefined,
};
auto image = VK_EXPECT_RV(mDevice->createImageUnique(imageCreateInfo));
const auto memoryRequirements = mDevice->getImageMemoryRequirements(*image);
const uint32_t memoryIndex =
GetMemoryType(mPhysicalDevice,
memoryRequirements.memoryTypeBits,
memoryProperties);
const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
.allocationSize = memoryRequirements.size,
.memoryTypeIndex = memoryIndex,
};
auto imageMemory = VK_EXPECT_RV(mDevice->allocateMemoryUnique(imageMemoryAllocateInfo));
mDevice->bindImageMemory(*image, *imageMemory, 0);
const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
.image = *image,
.viewType = vkhpp::ImageViewType::e2D,
.format = format,
.components = {
.r = vkhpp::ComponentSwizzle::eIdentity,
.g = vkhpp::ComponentSwizzle::eIdentity,
.b = vkhpp::ComponentSwizzle::eIdentity,
.a = vkhpp::ComponentSwizzle::eIdentity,
},
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
auto imageView = VK_EXPECT_RV(mDevice->createImageViewUnique(imageViewCreateInfo));
VK_EXPECT_RESULT(DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
vkhpp::ImageMemoryBarrier{
.srcAccessMask = {},
.dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
.oldLayout = vkhpp::ImageLayout::eUndefined,
.newLayout = returnedLayout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
cmd->pipelineBarrier(
/*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/imageMemoryBarriers);
return vkhpp::Result::eSuccess;
}));
return ImageWithMemory{
.image = std::move(image),
.imageMemory = std::move(imageMemory),
.imageView = std::move(imageView),
};
}
gfxstream::expected<std::vector<uint8_t>, vkhpp::Result> Vk::DownloadImage(
uint32_t width,
uint32_t height,
const vkhpp::UniqueImage& image,
vkhpp::ImageLayout currentLayout,
vkhpp::ImageLayout returnedLayout) {
VK_EXPECT_RESULT(
DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
if (currentLayout != vkhpp::ImageLayout::eTransferSrcOptimal) {
const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
vkhpp::ImageMemoryBarrier{
.srcAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
vkhpp::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vkhpp::AccessFlagBits::eTransferRead,
.oldLayout = currentLayout,
.newLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
cmd->pipelineBarrier(
/*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/imageMemoryBarriers);
}
const std::vector<vkhpp::BufferImageCopy> regions = {
vkhpp::BufferImageCopy{
.bufferOffset = 0,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource =
{
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset =
{
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent =
{
.width = width,
.height = height,
.depth = 1,
},
},
};
cmd->copyImageToBuffer(*image,
vkhpp::ImageLayout::eTransferSrcOptimal,
*mStagingBuffer, regions);
if (returnedLayout != vkhpp::ImageLayout::eTransferSrcOptimal) {
const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
vkhpp::ImageMemoryBarrier{
.srcAccessMask = vkhpp::AccessFlagBits::eTransferRead,
.dstAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
vkhpp::AccessFlagBits::eMemoryWrite,
.oldLayout = vkhpp::ImageLayout::eTransferSrcOptimal,
.newLayout = returnedLayout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
cmd->pipelineBarrier(
/*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/imageMemoryBarriers);
}
return vkhpp::Result::eSuccess;
}));
auto* mapped = reinterpret_cast<uint8_t*>(
VK_EXPECT_RV(mDevice->mapMemory(*mStagingBufferMemory, 0, kStagingBufferSize)));
std::vector<uint8_t> outPixels;
outPixels.resize(width * height * 4);
std::memcpy(outPixels.data(), mapped, outPixels.size());
mDevice->unmapMemory(*mStagingBufferMemory);
return outPixels;
}
gfxstream::expected<Vk::YuvImageWithMemory, vkhpp::Result> Vk::CreateYuvImage(
uint32_t width,
uint32_t height,
vkhpp::ImageUsageFlags usages,
vkhpp::MemoryPropertyFlags memoryProperties,
vkhpp::ImageLayout layout) {
const vkhpp::SamplerYcbcrConversionCreateInfo conversionCreateInfo = {
.format = vkhpp::Format::eG8B8R83Plane420Unorm,
.ycbcrModel = vkhpp::SamplerYcbcrModelConversion::eYcbcr601,
.ycbcrRange = vkhpp::SamplerYcbcrRange::eItuNarrow,
.components = {
.r = vkhpp::ComponentSwizzle::eIdentity,
.g = vkhpp::ComponentSwizzle::eIdentity,
.b = vkhpp::ComponentSwizzle::eIdentity,
.a = vkhpp::ComponentSwizzle::eIdentity,
},
.xChromaOffset = vkhpp::ChromaLocation::eMidpoint,
.yChromaOffset = vkhpp::ChromaLocation::eMidpoint,
.chromaFilter = vkhpp::Filter::eLinear,
.forceExplicitReconstruction = VK_FALSE,
};
auto imageSamplerConversion = VK_EXPECT_RV(mDevice->createSamplerYcbcrConversionUnique(conversionCreateInfo));
const vkhpp::SamplerYcbcrConversionInfo samplerConversionInfo = {
.conversion = *imageSamplerConversion,
};
const vkhpp::SamplerCreateInfo samplerCreateInfo = {
.pNext = &samplerConversionInfo,
.magFilter = vkhpp::Filter::eLinear,
.minFilter = vkhpp::Filter::eLinear,
.mipmapMode = vkhpp::SamplerMipmapMode::eNearest,
.addressModeU = vkhpp::SamplerAddressMode::eClampToEdge,
.addressModeV = vkhpp::SamplerAddressMode::eClampToEdge,
.addressModeW = vkhpp::SamplerAddressMode::eClampToEdge,
.mipLodBias = 0.0f,
.anisotropyEnable = VK_FALSE,
.maxAnisotropy = 1.0f,
.compareEnable = VK_FALSE,
.compareOp = vkhpp::CompareOp::eLessOrEqual,
.minLod = 0.0f,
.maxLod = 0.25f,
.borderColor = vkhpp::BorderColor::eIntTransparentBlack,
.unnormalizedCoordinates = VK_FALSE,
};
auto imageSampler = VK_EXPECT_RV(mDevice->createSamplerUnique(samplerCreateInfo));
const vkhpp::ImageCreateInfo imageCreateInfo = {
.imageType = vkhpp::ImageType::e2D,
.format = vkhpp::Format::eG8B8R83Plane420Unorm,
.extent = {
.width = width,
.height = height,
.depth = 1,
},
.mipLevels = 1,
.arrayLayers = 1,
.samples = vkhpp::SampleCountFlagBits::e1,
.tiling = vkhpp::ImageTiling::eOptimal,
.usage = usages,
.sharingMode = vkhpp::SharingMode::eExclusive,
.initialLayout = vkhpp::ImageLayout::eUndefined,
};
auto image = VK_EXPECT_RV(mDevice->createImageUnique(imageCreateInfo));
const auto memoryRequirements = mDevice->getImageMemoryRequirements(*image);
const uint32_t memoryIndex =
GetMemoryType(mPhysicalDevice,
memoryRequirements.memoryTypeBits,
memoryProperties);
const vkhpp::MemoryAllocateInfo imageMemoryAllocateInfo = {
.allocationSize = memoryRequirements.size,
.memoryTypeIndex = memoryIndex,
};
auto imageMemory = VK_EXPECT_RV(mDevice->allocateMemoryUnique(imageMemoryAllocateInfo));
mDevice->bindImageMemory(*image, *imageMemory, 0);
const vkhpp::ImageViewCreateInfo imageViewCreateInfo = {
.pNext = &samplerConversionInfo,
.image = *image,
.viewType = vkhpp::ImageViewType::e2D,
.format = vkhpp::Format::eG8B8R83Plane420Unorm,
.components = {
.r = vkhpp::ComponentSwizzle::eIdentity,
.g = vkhpp::ComponentSwizzle::eIdentity,
.b = vkhpp::ComponentSwizzle::eIdentity,
.a = vkhpp::ComponentSwizzle::eIdentity,
},
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
};
auto imageView = VK_EXPECT_RV(mDevice->createImageViewUnique(imageViewCreateInfo));
VK_EXPECT_RESULT(DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
vkhpp::ImageMemoryBarrier{
.srcAccessMask = {},
.dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
.oldLayout = vkhpp::ImageLayout::eUndefined,
.newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
cmd->pipelineBarrier(
/*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/imageMemoryBarriers);
return vkhpp::Result::eSuccess;
}));
return YuvImageWithMemory{
.imageSamplerConversion = std::move(imageSamplerConversion),
.imageSampler = std::move(imageSampler),
.imageMemory = std::move(imageMemory),
.image = std::move(image),
.imageView = std::move(imageView),
};
}
vkhpp::Result Vk::LoadYuvImage(
const vkhpp::UniqueImage& image,
uint32_t width,
uint32_t height,
const std::vector<uint8_t>& imageDataY,
const std::vector<uint8_t>& imageDataU,
const std::vector<uint8_t>& imageDataV,
vkhpp::ImageLayout currentLayout,
vkhpp::ImageLayout returnedLayout) {
auto* mapped = reinterpret_cast<uint8_t*>(VK_TRY_RV(mDevice->mapMemory(*mStagingBufferMemory, 0, kStagingBufferSize)));
const VkDeviceSize yOffset = 0;
const VkDeviceSize uOffset = imageDataY.size();
const VkDeviceSize vOffset = imageDataY.size() + imageDataU.size();
std::memcpy(mapped + yOffset, imageDataY.data(), imageDataY.size());
std::memcpy(mapped + uOffset, imageDataU.data(), imageDataU.size());
std::memcpy(mapped + vOffset, imageDataV.data(), imageDataV.size());
mDevice->unmapMemory(*mStagingBufferMemory);
return DoCommandsImmediate([&](vkhpp::UniqueCommandBuffer& cmd) {
if (currentLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
vkhpp::ImageMemoryBarrier{
.srcAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
vkhpp::AccessFlagBits::eMemoryWrite,
.dstAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
.oldLayout = currentLayout,
.newLayout = vkhpp::ImageLayout::eTransferDstOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
cmd->pipelineBarrier(
/*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/imageMemoryBarriers);
}
const std::vector<vkhpp::BufferImageCopy> imageCopyRegions = {
vkhpp::BufferImageCopy{
.bufferOffset = yOffset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = {
.aspectMask = vkhpp::ImageAspectFlagBits::ePlane0,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent = {
.width = width,
.height = height,
.depth = 1,
},
},
vkhpp::BufferImageCopy{
.bufferOffset = uOffset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = {
.aspectMask = vkhpp::ImageAspectFlagBits::ePlane1,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent = {
.width = width / 2,
.height = height / 2,
.depth = 1,
},
},
vkhpp::BufferImageCopy{
.bufferOffset = vOffset,
.bufferRowLength = 0,
.bufferImageHeight = 0,
.imageSubresource = {
.aspectMask = vkhpp::ImageAspectFlagBits::ePlane2,
.mipLevel = 0,
.baseArrayLayer = 0,
.layerCount = 1,
},
.imageOffset = {
.x = 0,
.y = 0,
.z = 0,
},
.imageExtent = {
.width = width / 2,
.height = height / 2,
.depth = 1,
},
},
};
cmd->copyBufferToImage(*mStagingBuffer,
*image,
vkhpp::ImageLayout::eTransferDstOptimal,
imageCopyRegions);
if (returnedLayout != vkhpp::ImageLayout::eTransferDstOptimal) {
const std::vector<vkhpp::ImageMemoryBarrier> imageMemoryBarriers = {
vkhpp::ImageMemoryBarrier{
.srcAccessMask = vkhpp::AccessFlagBits::eTransferWrite,
.dstAccessMask = vkhpp::AccessFlagBits::eMemoryRead |
vkhpp::AccessFlagBits::eMemoryWrite,
.oldLayout = vkhpp::ImageLayout::eTransferDstOptimal,
.newLayout = returnedLayout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = *image,
.subresourceRange = {
.aspectMask = vkhpp::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
},
},
};
cmd->pipelineBarrier(
/*srcStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dstStageMask=*/vkhpp::PipelineStageFlagBits::eAllCommands,
/*dependencyFlags=*/{},
/*memoryBarriers=*/{},
/*bufferMemoryBarriers=*/{},
/*imageMemoryBarriers=*/imageMemoryBarriers);
}
return vkhpp::Result::eSuccess;
});
}
gfxstream::expected<Vk::FramebufferWithAttachments, vkhpp::Result>
Vk::CreateFramebuffer(
uint32_t width,
uint32_t height,
vkhpp::Format color_format,
vkhpp::Format depth_format) {
std::optional<Vk::ImageWithMemory> colorAttachment;
if (color_format != vkhpp::Format::eUndefined) {
colorAttachment =
GFXSTREAM_EXPECT(CreateImage(width, height, color_format,
vkhpp::ImageUsageFlagBits::eColorAttachment |
vkhpp::ImageUsageFlagBits::eTransferSrc,
vkhpp::MemoryPropertyFlagBits::eDeviceLocal,
vkhpp::ImageLayout::eColorAttachmentOptimal));
}
std::optional<Vk::ImageWithMemory> depthAttachment;
if (depth_format != vkhpp::Format::eUndefined) {
depthAttachment =
GFXSTREAM_EXPECT(CreateImage(width, height, depth_format,
vkhpp::ImageUsageFlagBits::eDepthStencilAttachment |
vkhpp::ImageUsageFlagBits::eTransferSrc,
vkhpp::MemoryPropertyFlagBits::eDeviceLocal,
vkhpp::ImageLayout::eDepthStencilAttachmentOptimal));
}
std::vector<vkhpp::AttachmentDescription> attachments;
std::optional<vkhpp::AttachmentReference> colorAttachment_reference;
if (color_format != vkhpp::Format::eUndefined) {
attachments.push_back(vkhpp::AttachmentDescription{
.format = color_format,
.samples = vkhpp::SampleCountFlagBits::e1,
.loadOp = vkhpp::AttachmentLoadOp::eClear,
.storeOp = vkhpp::AttachmentStoreOp::eStore,
.stencilLoadOp = vkhpp::AttachmentLoadOp::eClear,
.stencilStoreOp = vkhpp::AttachmentStoreOp::eStore,
.initialLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
.finalLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
});
colorAttachment_reference = vkhpp::AttachmentReference{
.attachment = static_cast<uint32_t>(attachments.size() - 1),
.layout = vkhpp::ImageLayout::eColorAttachmentOptimal,
};
}
std::optional<vkhpp::AttachmentReference> depthAttachment_reference;
if (depth_format != vkhpp::Format::eUndefined) {
attachments.push_back(vkhpp::AttachmentDescription{
.format = depth_format,
.samples = vkhpp::SampleCountFlagBits::e1,
.loadOp = vkhpp::AttachmentLoadOp::eClear,
.storeOp = vkhpp::AttachmentStoreOp::eStore,
.stencilLoadOp = vkhpp::AttachmentLoadOp::eClear,
.stencilStoreOp = vkhpp::AttachmentStoreOp::eStore,
.initialLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
.finalLayout = vkhpp::ImageLayout::eColorAttachmentOptimal,
});
depthAttachment_reference = vkhpp::AttachmentReference{
.attachment = static_cast<uint32_t>(attachments.size() - 1),
.layout = vkhpp::ImageLayout::eDepthStencilAttachmentOptimal,
};
}
vkhpp::SubpassDependency dependency = {
.srcSubpass = 0,
.dstSubpass = 0,
.srcStageMask = {},
.dstStageMask = vkhpp::PipelineStageFlagBits::eFragmentShader,
.srcAccessMask = {},
.dstAccessMask = vkhpp::AccessFlagBits::eInputAttachmentRead,
.dependencyFlags = vkhpp::DependencyFlagBits::eByRegion,
};
if (color_format != vkhpp::Format::eUndefined) {
dependency.srcStageMask |=
vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.dstStageMask |=
vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.srcAccessMask |= vkhpp::AccessFlagBits::eColorAttachmentWrite;
}
if (depth_format != vkhpp::Format::eUndefined) {
dependency.srcStageMask |=
vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.dstStageMask |=
vkhpp::PipelineStageFlagBits::eColorAttachmentOutput;
dependency.srcAccessMask |= vkhpp::AccessFlagBits::eColorAttachmentWrite;
}
vkhpp::SubpassDescription subpass = {
.pipelineBindPoint = vkhpp::PipelineBindPoint::eGraphics,
.inputAttachmentCount = 0,
.pInputAttachments = nullptr,
.colorAttachmentCount = 0,
.pColorAttachments = nullptr,
.pResolveAttachments = nullptr,
.pDepthStencilAttachment = nullptr,
.pPreserveAttachments = nullptr,
};
if (color_format != vkhpp::Format::eUndefined) {
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &*colorAttachment_reference;
}
if (depth_format != vkhpp::Format::eUndefined) {
subpass.pDepthStencilAttachment = &*depthAttachment_reference;
}
const vkhpp::RenderPassCreateInfo renderpassCreateInfo = {
.attachmentCount = static_cast<uint32_t>(attachments.size()),
.pAttachments = attachments.data(),
.subpassCount = 1,
.pSubpasses = &subpass,
.dependencyCount = 1,
.pDependencies = &dependency,
};
auto renderpass = VK_EXPECT_RV(mDevice->createRenderPassUnique(renderpassCreateInfo));
std::vector<vkhpp::ImageView> framebufferAttachments;
if (colorAttachment) {
framebufferAttachments.push_back(*colorAttachment->imageView);
}
if (depthAttachment) {
framebufferAttachments.push_back(*depthAttachment->imageView);
}
const vkhpp::FramebufferCreateInfo framebufferCreateInfo = {
.renderPass = *renderpass,
.attachmentCount = static_cast<uint32_t>(framebufferAttachments.size()),
.pAttachments = framebufferAttachments.data(),
.width = width,
.height = height,
.layers = 1,
};
auto framebuffer = VK_EXPECT_RV(mDevice->createFramebufferUnique(framebufferCreateInfo));
return Vk::FramebufferWithAttachments{
.colorAttachment = std::move(colorAttachment),
.depthAttachment = std::move(depthAttachment),
.renderpass = std::move(renderpass),
.framebuffer = std::move(framebuffer),
};
}
vkhpp::Result Vk::DoCommandsImmediate(
const std::function<vkhpp::Result(vkhpp::UniqueCommandBuffer&)>& func,
const std::vector<vkhpp::UniqueSemaphore>& semaphores_wait,
const std::vector<vkhpp::UniqueSemaphore>& semaphores_signal) {
const vkhpp::CommandBufferAllocateInfo commandBufferAllocateInfo = {
.commandPool = *mCommandPool,
.level = vkhpp::CommandBufferLevel::ePrimary,
.commandBufferCount = 1,
};
auto commandBuffers = VK_TRY_RV(mDevice->allocateCommandBuffersUnique(commandBufferAllocateInfo));
auto commandBuffer = std::move(commandBuffers[0]);
const vkhpp::CommandBufferBeginInfo commandBufferBeginInfo = {
.flags = vkhpp::CommandBufferUsageFlagBits::eOneTimeSubmit,
};
commandBuffer->begin(commandBufferBeginInfo);
VK_TRY(func(commandBuffer));
commandBuffer->end();
std::vector<vkhpp::CommandBuffer> commandBufferHandles;
commandBufferHandles.push_back(*commandBuffer);
std::vector<vkhpp::Semaphore> semaphoreHandlesWait;
semaphoreHandlesWait.reserve(semaphores_wait.size());
for (const auto& s : semaphores_wait) {
semaphoreHandlesWait.emplace_back(*s);
}
std::vector<vkhpp::Semaphore> semaphoreHandlesSignal;
semaphoreHandlesSignal.reserve(semaphores_signal.size());
for (const auto& s : semaphores_signal) {
semaphoreHandlesSignal.emplace_back(*s);
}
vkhpp::SubmitInfo submitInfo = {
.commandBufferCount = static_cast<uint32_t>(commandBufferHandles.size()),
.pCommandBuffers = commandBufferHandles.data(),
};
if (!semaphoreHandlesWait.empty()) {
submitInfo.waitSemaphoreCount = static_cast<uint32_t>(semaphoreHandlesWait.size());
submitInfo.pWaitSemaphores = semaphoreHandlesWait.data();
}
if (!semaphoreHandlesSignal.empty()) {
submitInfo.signalSemaphoreCount = static_cast<uint32_t>(semaphoreHandlesSignal.size());
submitInfo.pSignalSemaphores = semaphoreHandlesSignal.data();
}
mQueue.submit(submitInfo);
mQueue.waitIdle();
return vkhpp::Result::eSuccess;
}
} // namespace gfxstream