/* * 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 #include #include #include 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 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(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::Load( const std::vector& requestedInstanceExtensions, const std::vector& requestedInstanceLayers, const std::vector& requestedDeviceExtensions) { vkhpp::DynamicLoader loader; VULKAN_HPP_DEFAULT_DISPATCHER.init( loader.getProcAddress( "vkGetInstanceProcAddr")); std::vector 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 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(requestedInstanceLayersChars.size()), .ppEnabledLayerNames = requestedInstanceLayersChars.data(), .enabledExtensionCount = static_cast(requestedInstanceExtensionsChars.size()), .ppEnabledExtensionNames = requestedInstanceExtensionsChars.data(), }; auto instance = VK_EXPECT_RV(vkhpp::createInstanceUnique(instanceCreateInfo)); VULKAN_HPP_DEFAULT_DISPATCHER.init(*instance); std::optional 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 availableDeviceExtensions; { const auto exts = VK_EXPECT_RV(physicalDevice.enumerateDeviceExtensionProperties()); for (const auto& ext : exts) { availableDeviceExtensions.emplace(ext.extensionName); } } const auto features2 = physicalDevice .getFeatures2(); bool ycbcr_conversion_needed = false; std::vector 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(); 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(requestedInstanceLayersChars.size()), .ppEnabledLayerNames = requestedInstanceLayersChars.data(), .enabledExtensionCount = static_cast(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::CreateBuffer( vkhpp::DeviceSize bufferSize, vkhpp::BufferUsageFlags bufferUsages, vkhpp::MemoryPropertyFlags bufferMemoryProperties) { return DoCreateBuffer(mPhysicalDevice, mDevice, bufferSize, bufferUsages, bufferMemoryProperties); } gfxstream::expected 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 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::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 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, 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 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 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 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( VK_EXPECT_RV(mDevice->mapMemory(*mStagingBufferMemory, 0, kStagingBufferSize))); std::vector outPixels; outPixels.resize(width * height * 4); std::memcpy(outPixels.data(), mapped, outPixels.size()); mDevice->unmapMemory(*mStagingBufferMemory); return outPixels; } gfxstream::expected 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 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& imageDataY, const std::vector& imageDataU, const std::vector& imageDataV, vkhpp::ImageLayout currentLayout, vkhpp::ImageLayout returnedLayout) { auto* mapped = reinterpret_cast(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 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 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 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::CreateFramebuffer( uint32_t width, uint32_t height, vkhpp::Format color_format, vkhpp::Format depth_format) { std::optional 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 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 attachments; std::optional 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(attachments.size() - 1), .layout = vkhpp::ImageLayout::eColorAttachmentOptimal, }; } std::optional 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(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(attachments.size()), .pAttachments = attachments.data(), .subpassCount = 1, .pSubpasses = &subpass, .dependencyCount = 1, .pDependencies = &dependency, }; auto renderpass = VK_EXPECT_RV(mDevice->createRenderPassUnique(renderpassCreateInfo)); std::vector framebufferAttachments; if (colorAttachment) { framebufferAttachments.push_back(*colorAttachment->imageView); } if (depthAttachment) { framebufferAttachments.push_back(*depthAttachment->imageView); } const vkhpp::FramebufferCreateInfo framebufferCreateInfo = { .renderPass = *renderpass, .attachmentCount = static_cast(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& func, const std::vector& semaphores_wait, const std::vector& 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 commandBufferHandles; commandBufferHandles.push_back(*commandBuffer); std::vector semaphoreHandlesWait; semaphoreHandlesWait.reserve(semaphores_wait.size()); for (const auto& s : semaphores_wait) { semaphoreHandlesWait.emplace_back(*s); } std::vector semaphoreHandlesSignal; semaphoreHandlesSignal.reserve(semaphores_signal.size()); for (const auto& s : semaphores_signal) { semaphoreHandlesSignal.emplace_back(*s); } vkhpp::SubmitInfo submitInfo = { .commandBufferCount = static_cast(commandBufferHandles.size()), .pCommandBuffers = commandBufferHandles.data(), }; if (!semaphoreHandlesWait.empty()) { submitInfo.waitSemaphoreCount = static_cast(semaphoreHandlesWait.size()); submitInfo.pWaitSemaphores = semaphoreHandlesWait.data(); } if (!semaphoreHandlesSignal.empty()) { submitInfo.signalSemaphoreCount = static_cast(semaphoreHandlesSignal.size()); submitInfo.pSignalSemaphores = semaphoreHandlesSignal.data(); } mQueue.submit(submitInfo); mQueue.waitIdle(); return vkhpp::Result::eSuccess; } } // namespace gfxstream