f07d00df65
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
263 lines
12 KiB
C++
263 lines
12 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
#include <RHI/Conversion.h>
|
|
#include <RHI/Device.h>
|
|
#include <RHI/Image.h>
|
|
#include <RHI/ImageView.h>
|
|
#include <RHI/ReleaseContainer.h>
|
|
|
|
namespace AZ
|
|
{
|
|
namespace Vulkan
|
|
{
|
|
RHI::Ptr<ImageView> ImageView::Create()
|
|
{
|
|
return aznew ImageView();
|
|
}
|
|
|
|
VkImageView ImageView::GetNativeImageView() const
|
|
{
|
|
return m_vkImageView;
|
|
}
|
|
|
|
RHI::Format ImageView::GetFormat() const
|
|
{
|
|
return m_format;
|
|
}
|
|
|
|
const RHI::ImageSubresourceRange& ImageView::GetImageSubresourceRange() const
|
|
{
|
|
return m_imageSubresourceRange;
|
|
}
|
|
|
|
void ImageView::SetNameInternal(const AZStd::string_view& name)
|
|
{
|
|
if (IsInitialized() && !name.empty())
|
|
{
|
|
Debug::SetNameToObject(reinterpret_cast<uint64_t>(m_vkImageView), name.data(), VK_OBJECT_TYPE_IMAGE_VIEW, static_cast<Device&>(GetDevice()));
|
|
}
|
|
}
|
|
|
|
RHI::ResultCode ImageView::InitInternal(RHI::Device& deviceBase, const RHI::Resource& resourceBase)
|
|
{
|
|
DeviceObject::Init(deviceBase);
|
|
auto& device = static_cast<Device&>(deviceBase);
|
|
const auto& image = static_cast<const Image&>(resourceBase);
|
|
const RHI::ImageViewDescriptor& descriptor = GetDescriptor();
|
|
|
|
// this can happen when image has been invalidated/released right before re-compiling the image
|
|
if (image.GetNativeImage() == VK_NULL_HANDLE)
|
|
{
|
|
return RHI::ResultCode::Fail;
|
|
}
|
|
|
|
RHI::Format viewFormat = descriptor.m_overrideFormat;
|
|
// If an image is not owner of native image, it is a swapchain image.
|
|
// Swapchain images are not mutable, so we can not change the format for the view.
|
|
if (viewFormat == RHI::Format::Unknown || !image.IsOwnerOfNativeImage())
|
|
{
|
|
viewFormat = image.GetDescriptor().m_format;
|
|
}
|
|
m_format = viewFormat;
|
|
|
|
VkImageAspectFlags aspectFlags = ConvertImageAspectFlags(RHI::FilterBits(descriptor.m_aspectFlags, image.GetAspectFlags()));
|
|
|
|
const VkImageViewType imageViewType = GetImageViewType(image);
|
|
BuildImageSubresourceRange(imageViewType, aspectFlags);
|
|
const RHI::ImageSubresourceRange& range = GetImageSubresourceRange();
|
|
VkImageSubresourceRange vkRange{};
|
|
vkRange.baseMipLevel = range.m_mipSliceMin;
|
|
vkRange.levelCount = range.m_mipSliceMax - range.m_mipSliceMin + 1;
|
|
vkRange.baseArrayLayer = range.m_arraySliceMin;
|
|
vkRange.layerCount = range.m_arraySliceMax - range.m_arraySliceMin + 1;
|
|
vkRange.aspectMask = aspectFlags;
|
|
VkImageViewCreateInfo createInfo{};
|
|
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
createInfo.pNext = nullptr;
|
|
createInfo.flags = 0;
|
|
createInfo.image = image.GetNativeImage();
|
|
createInfo.viewType = imageViewType;
|
|
createInfo.format = ConvertFormat(m_format);
|
|
createInfo.components = VkComponentMapping{}; // identity mapping
|
|
createInfo.subresourceRange = vkRange;
|
|
|
|
const VkResult result = vkCreateImageView(device.GetNativeDevice(), &createInfo, nullptr, &m_vkImageView);
|
|
AssertSuccess(result);
|
|
RETURN_RESULT_IF_UNSUCCESSFUL(ConvertResult(result));
|
|
|
|
SetName(GetName());
|
|
return RHI::ResultCode::Success;
|
|
}
|
|
|
|
RHI::ResultCode ImageView::InvalidateInternal()
|
|
{
|
|
ShutdownInternal();
|
|
return InitInternal(GetDevice(), GetResource());
|
|
}
|
|
|
|
void ImageView::ShutdownInternal()
|
|
{
|
|
if (m_vkImageView != VK_NULL_HANDLE)
|
|
{
|
|
auto& device = static_cast<Device&>(GetDevice());
|
|
device.QueueForRelease(new ReleaseContainer<VkImageView>(device.GetNativeDevice(), m_vkImageView, vkDestroyImageView));
|
|
m_vkImageView = VK_NULL_HANDLE;
|
|
}
|
|
}
|
|
|
|
VkImageViewType ImageView::GetImageViewType(const Image& image) const
|
|
{
|
|
const RHI::ImageDescriptor& imgDesc = image.GetDescriptor();
|
|
const RHI::ImageViewDescriptor& imgViewDesc = GetDescriptor();
|
|
const auto& device = static_cast<const Device&>(GetDevice());
|
|
const auto& physicalDevice = static_cast<const PhysicalDevice&>(GetDevice().GetPhysicalDevice());
|
|
|
|
const uint16_t width = imgDesc.m_size.m_width;
|
|
const uint16_t height = imgDesc.m_size.m_height;
|
|
const uint16_t depth = AZStd::min(static_cast<uint16_t>(imgViewDesc.m_depthSliceMax - imgViewDesc.m_depthSliceMin), static_cast<uint16_t>(imgDesc.m_size.m_depth - 1)) + 1;
|
|
const uint16_t samples = imgDesc.m_multisampleState.m_samples;
|
|
const uint16_t arrayLayers = AZStd::min(static_cast<uint16_t>(imgViewDesc.m_arraySliceMax - imgViewDesc.m_arraySliceMin), static_cast<uint16_t>(imgDesc.m_arraySize - 1)) + 1;
|
|
// We cannot only use the number of layers of the ImageView to determinate if is a texture array. You can have a texture array with only 1 layer and the shader expects
|
|
// an array type instead of a normal image type.
|
|
const bool isArray = arrayLayers > 1 || imgViewDesc.m_isArray;
|
|
AZ_Assert(width * height * depth * samples * arrayLayers != 0, "One of width, height, depth, samples, or arrayLayers = 0.");
|
|
|
|
switch (imgDesc.m_dimension)
|
|
{
|
|
case RHI::ImageDimension::Image1D:
|
|
{
|
|
AZ_Assert(height == 1 && depth == 1, "Both height and depth must be 1 for Image1D or Image1DArray.");
|
|
return isArray ? VK_IMAGE_VIEW_TYPE_1D_ARRAY : VK_IMAGE_VIEW_TYPE_1D;
|
|
case RHI::ImageDimension::Image2D:
|
|
AZ_Assert(depth == 1, "Depth must be 1 for Image2D or Image2DArray.");
|
|
if (!isArray)
|
|
{
|
|
return VkImageViewType::VK_IMAGE_VIEW_TYPE_2D;
|
|
}
|
|
else
|
|
{
|
|
if (imgViewDesc.m_isCubemap)
|
|
{
|
|
AZ_Assert(width == height, "Image of Cube or CubeArray form a square.");
|
|
AZ_Assert(depth == 1, "Depth of Cube or CubeArray = 1.");
|
|
AZ_Assert(samples == 1, "Sample of Cube or CubeArray = 1.");
|
|
AZ_Assert(arrayLayers % 6 == 0, "ArrayLayers % 6 == 0 for Cube or CubeArray.");
|
|
if (arrayLayers == 6)
|
|
{
|
|
return VK_IMAGE_VIEW_TYPE_CUBE;
|
|
}
|
|
else
|
|
{
|
|
if (device.GetEnabledDevicesFeatures().imageCubeArray)
|
|
{
|
|
return VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
|
|
}
|
|
else
|
|
{
|
|
AZ_Assert(false, "This device does not support Image Cube Array.");
|
|
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
}
|
|
}
|
|
}
|
|
case RHI::ImageDimension::Image3D:
|
|
{
|
|
// Vulkan does not have capability to give a view of proper subset of depth slices of a base 3D Image
|
|
// as an ImageView of type 3D, unlike Direct3D. (We can still specify a proper subset of mip slices.)
|
|
// Instead of it, we can get it as an ImageView of type 2D or 2D array for a proper depth slice subset.
|
|
// One of the main usage of depth slices of a 3D Image would be to set the slice to a Framebuffer,
|
|
// and here we give an image view of 2D or 2D array.
|
|
AZ_Assert(arrayLayers == 1 && samples == 1, "Both of arrayLayers and samples must be 1 for Image3D.");
|
|
if (physicalDevice.IsFeatureSupported(DeviceFeature::Compatible2dArrayTexture))
|
|
{
|
|
if (imgViewDesc.m_depthSliceMin == imgViewDesc.m_depthSliceMax)
|
|
{
|
|
return VK_IMAGE_VIEW_TYPE_2D;
|
|
}
|
|
else if (imgViewDesc.m_depthSliceMin == 0 && imgViewDesc.m_depthSliceMax + 1 >= depth)
|
|
{
|
|
// If ImageView's depth slice range covers the one of the base 3D Image,
|
|
// it returns the view of the entire depth slices.
|
|
return VK_IMAGE_VIEW_TYPE_3D;
|
|
}
|
|
else
|
|
{
|
|
return VK_IMAGE_VIEW_TYPE_2D_ARRAY;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return VK_IMAGE_VIEW_TYPE_3D;
|
|
}
|
|
}
|
|
default:
|
|
{
|
|
AZ_Assert(false, "Image Dimension is illegal.");
|
|
return VK_IMAGE_VIEW_TYPE_2D;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ImageView::BuildImageSubresourceRange(VkImageViewType imageViewType, VkImageAspectFlags aspectFlags)
|
|
{
|
|
const Image& image = static_cast<const Image&>(GetImage());
|
|
const RHI::ImageDescriptor& imageDesc = image.GetDescriptor();
|
|
const RHI::ImageViewDescriptor& descriptor = GetDescriptor();
|
|
|
|
RHI::ImageSubresourceRange& range = m_imageSubresourceRange;
|
|
range.m_mipSliceMin = AZStd::max(descriptor.m_mipSliceMin, image.GetStreamedMipLevel());
|
|
range.m_mipSliceMax = AZStd::min(descriptor.m_mipSliceMax, static_cast<uint16_t>(imageDesc.m_mipLevels - 1));
|
|
if (imageDesc.m_dimension != RHI::ImageDimension::Image3D)
|
|
{
|
|
range.m_arraySliceMin = descriptor.m_arraySliceMin;
|
|
range.m_arraySliceMax = AZStd::min(descriptor.m_arraySliceMax, static_cast<uint16_t>(imageDesc.m_arraySize - 1));
|
|
}
|
|
else
|
|
{
|
|
switch (imageViewType)
|
|
{
|
|
case VK_IMAGE_VIEW_TYPE_2D:
|
|
case VK_IMAGE_VIEW_TYPE_2D_ARRAY:
|
|
// To create 2D or 2D array image view of 3D image, we specify its depth slice range by
|
|
// baseArrayLayer and layerCount of VkImageSubresourceRange.
|
|
// https://www.khronos.org/registry/vulkan/specs/1.1/html/chap11.html#VkImageSubresourceRange
|
|
{
|
|
range.m_arraySliceMin = descriptor.m_depthSliceMin;
|
|
range.m_arraySliceMax = AZStd::GetMin<uint32_t>(descriptor.m_depthSliceMax, imageDesc.m_size.m_depth - 1);
|
|
break;
|
|
}
|
|
case VK_IMAGE_VIEW_TYPE_3D:
|
|
{
|
|
range.m_arraySliceMin = 0;
|
|
range.m_arraySliceMax = 0;
|
|
break;
|
|
}
|
|
default:
|
|
AZ_Assert(false, "Invalid image view type.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_vkImageSubResourceRange.aspectMask = aspectFlags;
|
|
m_vkImageSubResourceRange.baseArrayLayer = range.m_arraySliceMin;
|
|
m_vkImageSubResourceRange.layerCount = range.m_arraySliceMax - range.m_arraySliceMin + 1;
|
|
m_vkImageSubResourceRange.baseMipLevel = range.m_mipSliceMin;
|
|
m_vkImageSubResourceRange.levelCount = range.m_mipSliceMax - range.m_mipSliceMin + 1;
|
|
}
|
|
|
|
const VkImageSubresourceRange& ImageView::GetVkImageSubresourceRange() const
|
|
{
|
|
return m_vkImageSubResourceRange;
|
|
}
|
|
}
|
|
}
|