#include "dxvk_image.h" namespace dxvk { DxvkImage::DxvkImage( const Rc& vkd, const DxvkImageCreateInfo& createInfo, DxvkMemoryAllocator& memAlloc, VkMemoryPropertyFlags memFlags) : m_vkd(vkd), m_info(createInfo), m_memFlags(memFlags) { // Copy the compatible view formats to a persistent array m_viewFormats.resize(createInfo.viewFormatCount); for (uint32_t i = 0; i < createInfo.viewFormatCount; i++) m_viewFormats[i] = createInfo.viewFormats[i]; m_info.viewFormats = m_viewFormats.data(); // If defined, we should provide a format list, which // allows some drivers to enable image compression VkImageFormatListCreateInfoKHR formatList; formatList.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR; formatList.pNext = nullptr; formatList.viewFormatCount = createInfo.viewFormatCount; formatList.pViewFormats = createInfo.viewFormats; VkImageCreateInfo info; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.pNext = &formatList; info.flags = createInfo.flags; info.imageType = createInfo.type; info.format = createInfo.format; info.extent = createInfo.extent; info.mipLevels = createInfo.mipLevels; info.arrayLayers = createInfo.numLayers; info.samples = createInfo.sampleCount; info.tiling = createInfo.tiling; info.usage = createInfo.usage; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.queueFamilyIndexCount = 0; info.pQueueFamilyIndices = nullptr; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; if (m_vkd->vkCreateImage(m_vkd->device(), &info, nullptr, &m_image) != VK_SUCCESS) { throw DxvkError(str::format( "DxvkImage: Failed to create image:", "\n Type: ", info.imageType, "\n Format: ", info.format, "\n Extent: ", "(", info.extent.width, ",", info.extent.height, ",", info.extent.depth, ")", "\n Mip levels: ", info.mipLevels, "\n Array layers: ", info.arrayLayers, "\n Samples: ", info.samples, "\n Usage: ", info.usage, "\n Tiling: ", info.tiling)); } // Get memory requirements for the image. We may enforce strict // alignment on non-linear images in order not to violate the // bufferImageGranularity limit, which may be greater than the // required resource memory alignment on some GPUs. VkMemoryDedicatedRequirementsKHR dedicatedRequirements; dedicatedRequirements.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR; dedicatedRequirements.pNext = VK_NULL_HANDLE; dedicatedRequirements.prefersDedicatedAllocation = VK_FALSE; dedicatedRequirements.requiresDedicatedAllocation = VK_FALSE; VkMemoryRequirements2KHR memReq; memReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR; memReq.pNext = &dedicatedRequirements; VkImageMemoryRequirementsInfo2KHR memReqInfo; memReqInfo.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR; memReqInfo.image = m_image; memReqInfo.pNext = VK_NULL_HANDLE; VkMemoryDedicatedAllocateInfoKHR dedMemoryAllocInfo; dedMemoryAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR; dedMemoryAllocInfo.pNext = VK_NULL_HANDLE; dedMemoryAllocInfo.buffer = VK_NULL_HANDLE; dedMemoryAllocInfo.image = m_image; m_vkd->vkGetImageMemoryRequirements2KHR( m_vkd->device(), &memReqInfo, &memReq); if (info.tiling != VK_IMAGE_TILING_LINEAR) { memReq.memoryRequirements.size = align(memReq.memoryRequirements.size, memAlloc.bufferImageGranularity()); memReq.memoryRequirements.alignment = align(memReq.memoryRequirements.alignment , memAlloc.bufferImageGranularity()); } bool useDedicated = dedicatedRequirements.prefersDedicatedAllocation; m_memory = memAlloc.alloc(&memReq.memoryRequirements, useDedicated ? &dedMemoryAllocInfo : nullptr, memFlags); // Try to bind the allocated memory slice to the image if (m_vkd->vkBindImageMemory(m_vkd->device(), m_image, m_memory.memory(), m_memory.offset()) != VK_SUCCESS) throw DxvkError("DxvkImage::DxvkImage: Failed to bind device memory"); } DxvkImage::DxvkImage( const Rc& vkd, const DxvkImageCreateInfo& info, VkImage image) : m_vkd(vkd), m_info(info), m_image(image) { } DxvkImage::~DxvkImage() { // This is a bit of a hack to determine whether // the image is implementation-handled or not if (m_memory.memory() != VK_NULL_HANDLE) m_vkd->vkDestroyImage(m_vkd->device(), m_image, nullptr); } DxvkImageView::DxvkImageView( const Rc& vkd, const Rc& image, const DxvkImageViewCreateInfo& info) : m_vkd(vkd), m_image(image), m_info(info) { // Since applications tend to bind views for (uint32_t i = 0; i < ViewCount; i++) m_views[i] = VK_NULL_HANDLE; switch (info.type) { case VK_IMAGE_VIEW_TYPE_1D: case VK_IMAGE_VIEW_TYPE_1D_ARRAY: { this->createView(VK_IMAGE_VIEW_TYPE_1D, 1); this->createView(VK_IMAGE_VIEW_TYPE_1D_ARRAY, info.numLayers); } break; case VK_IMAGE_VIEW_TYPE_2D: case VK_IMAGE_VIEW_TYPE_2D_ARRAY: case VK_IMAGE_VIEW_TYPE_CUBE: case VK_IMAGE_VIEW_TYPE_CUBE_ARRAY: { this->createView(VK_IMAGE_VIEW_TYPE_2D, 1); this->createView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, info.numLayers); if (m_image->info().flags & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT) { uint32_t cubeCount = info.numLayers / 6; if (cubeCount > 0) { this->createView(VK_IMAGE_VIEW_TYPE_CUBE, 6); this->createView(VK_IMAGE_VIEW_TYPE_CUBE_ARRAY, 6 * cubeCount); } } } break; case VK_IMAGE_VIEW_TYPE_3D: { this->createView(VK_IMAGE_VIEW_TYPE_3D, 1); if (m_image->info().flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT_KHR && info.numLevels == 1) { this->createView(VK_IMAGE_VIEW_TYPE_2D, 1); this->createView(VK_IMAGE_VIEW_TYPE_2D_ARRAY, m_image->mipLevelExtent(info.minLevel).depth); } } break; default: throw DxvkError(str::format("DxvkImageView: Invalid view type: ", info.type)); } } DxvkImageView::~DxvkImageView() { for (uint32_t i = 0; i < ViewCount; i++) m_vkd->vkDestroyImageView(m_vkd->device(), m_views[i], nullptr); } void DxvkImageView::createView(VkImageViewType type, uint32_t numLayers) { VkImageSubresourceRange subresourceRange; subresourceRange.aspectMask = m_info.aspect; subresourceRange.baseMipLevel = m_info.minLevel; subresourceRange.levelCount = m_info.numLevels; subresourceRange.baseArrayLayer = m_info.minLayer; subresourceRange.layerCount = numLayers; VkImageViewUsageCreateInfoKHR viewUsage; viewUsage.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR; viewUsage.pNext = nullptr; viewUsage.usage = m_info.usage; VkImageViewCreateInfo viewInfo; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; viewInfo.pNext = &viewUsage; viewInfo.flags = 0; viewInfo.image = m_image->handle(); viewInfo.viewType = type; viewInfo.format = m_info.format; viewInfo.components = m_info.swizzle; viewInfo.subresourceRange = subresourceRange; if (m_info.usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) { viewInfo.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; } if (m_vkd->vkCreateImageView(m_vkd->device(), &viewInfo, nullptr, &m_views[type]) != VK_SUCCESS) { throw DxvkError(str::format( "DxvkImageView: Failed to create image view:" "\n View type: ", viewInfo.viewType, "\n View format: ", viewInfo.format, "\n Subresources: ", "\n Aspect mask: ", std::hex, viewInfo.subresourceRange.aspectMask, "\n Mip levels: ", viewInfo.subresourceRange.baseMipLevel, " - ", viewInfo.subresourceRange.levelCount, "\n Array layers: ", viewInfo.subresourceRange.baseArrayLayer, " - ", viewInfo.subresourceRange.layerCount, "\n Image properties:", "\n Type: ", m_image->info().type, "\n Format: ", m_image->info().format, "\n Extent: ", "(", m_image->info().extent.width, ",", m_image->info().extent.height, ",", m_image->info().extent.depth, ")", "\n Mip levels: ", m_image->info().mipLevels, "\n Array layers: ", m_image->info().numLayers, "\n Samples: ", m_image->info().sampleCount, "\n Usage: ", std::hex, m_image->info().usage, "\n Tiling: ", m_image->info().tiling)); } } }