@ -61,13 +61,12 @@ namespace AZ
void SwapChain : : SetVerticalSyncIntervalInternal ( uint32_t previousVsyncInterval )
{
uint32_t verticalSyncInterval = GetDescriptor ( ) . m_verticalSyncInterval ;
if ( verticalSyncInterval = = 0 | | previousVsyncInterval = = 0 )
if ( GetDescriptor ( ) . m_verticalSyncInterval = = 0 | | previousVsyncInterval = = 0 )
{
// The presentation mode may change when transitioning to or from a vsynced presentation mode
// In this case, the swapchain must be recreated.
InvalidateNativeSwapChain ( ) ;
BuildNativeSwapChain( GetDescriptor ( ) . m_dimensions , verticalSyncInterval ) ;
CreateSwapchain( ) ;
}
}
@ -85,46 +84,21 @@ namespace AZ
RHI : : DeviceObject : : Init ( baseDevice ) ;
auto & device = static_cast < Device & > ( GetDevice ( ) ) ;
RHI : : SwapChainDimensions swapchainDimensions = descriptor . m_dimensions ;
m_dimensions = descriptor . m_dimensions ;
result = BuildSurface ( descriptor ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( result ) ;
if ( ! ValidateSurfaceDimensions ( swapchainDimensions ) )
{
swapchainDimensions . m_imageHeight = AZStd : : clamp ( swapchainDimensions . m_imageHeight , m_surfaceCapabilities . minImageExtent . height , m_surfaceCapabilities . maxImageExtent . height ) ;
swapchainDimensions . m_imageWidth = AZStd : : clamp ( swapchainDimensions . m_imageWidth , m_surfaceCapabilities . minImageExtent . width , m_surfaceCapabilities . maxImageExtent . width ) ;
AZ_Printf ( " Vulkan " , " Resizing swapchain from (%d, %d) to (%d, %d). " ,
static_cast < int > ( descriptor . m_dimensions . m_imageWidth ) , static_cast < int > ( descriptor . m_dimensions . m_imageHeight ) ,
static_cast < int > ( swapchainDimensions . m_imageWidth ) , static_cast < int > ( swapchainDimensions . m_imageHeight ) ) ;
}
auto & presentationQueue = device . GetCommandQueueContext ( ) . GetOrCreatePresentationCommandQueue ( * this ) ;
m_presentationQueue = & presentationQueue ;
result = BuildNativeSwapChain ( swapchainDimensions , descriptor . m_verticalSyncInterval ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( result ) ;
uint32_t imageCount = 0 ;
VkResult vkResult = vkGetSwapchainImagesKHR ( device . GetNativeDevice ( ) , m_nativeSwapChain , & imageCount , nullptr ) ;
AssertSuccess ( vkResult ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( ConvertResult ( vkResult ) ) ;
m_swapchainNativeImages . resize ( imageCount ) ;
// Retrieve the native images of the swapchain so they are
// available when we init the Images in InitImageInternal
vkResult = vkGetSwapchainImagesKHR ( device . GetNativeDevice ( ) , m_nativeSwapChain , & imageCount , m_swapchainNativeImages . data ( ) ) ;
AssertSuccess ( vkResult ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( ConvertResult ( vkResult ) ) ;
// Acquire the first image
uint32_t imageIndex = 0 ;
result = AcquireNewImage ( & imageIndex ) ;
result = CreateSwapchain ( ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( result ) ;
if ( nativeDimensions )
{
// Fill out the real swapchain dimensions to return
nativeDimensions - > m_imageCount = imageCount ;
nativeDimensions - > m_imageHeight = swapchainDimensions . m_imageHeight ;
nativeDimensions - > m_imageWidth = swapchainDimensions . m_imageWidth ;
* nativeDimensions = m_dimensions ;
nativeDimensions - > m_imageFormat = ConvertFormat ( m_surfaceFormat . format ) ;
}
@ -165,51 +139,24 @@ namespace AZ
RHI : : ResultCode SwapChain : : ResizeInternal ( const RHI : : SwapChainDimensions & dimensions , RHI : : SwapChainDimensions * nativeDimensions )
{
auto & device = static_cast < Device & > ( GetDevice ( ) ) ;
m_dimensions = dimensions ;
InvalidateNativeSwapChain ( ) ;
InvalidateSurface ( ) ;
RHI : : SwapChainDimensions resizeDimensions = dimensions ;
BuildSurface ( GetDescriptor ( ) ) ;
if ( ! ValidateSurfaceDimensions ( dimensions ) )
{
resizeDimensions . m_imageHeight = AZStd : : clamp ( dimensions . m_imageHeight , m_surfaceCapabilities . minImageExtent . height , m_surfaceCapabilities . maxImageExtent . height ) ;
resizeDimensions . m_imageWidth = AZStd : : clamp ( dimensions . m_imageWidth , m_surfaceCapabilities . minImageExtent . width , m_surfaceCapabilities . maxImageExtent . width ) ;
AZ_Printf ( " Vulkan " , " Resizing swapchain from (%d, %d) to (%d, %d). " ,
static_cast < int > ( dimensions . m_imageWidth ) , static_cast < int > ( dimensions . m_imageHeight ) ,
static_cast < int > ( resizeDimensions . m_imageWidth ) , static_cast < int > ( resizeDimensions . m_imageHeight ) ) ;
}
auto & presentationQueue = device . GetCommandQueueContext ( ) . GetOrCreatePresentationCommandQueue ( * this ) ;
m_presentationQueue = & presentationQueue ;
BuildNativeSwapChain ( resizeDimensions , GetDescriptor ( ) . m_verticalSyncInterval ) ;
resizeDimensions . m_imageCount = 0 ;
VkResult vkResult = vkGetSwapchainImagesKHR ( device . GetNativeDevice ( ) , m_nativeSwapChain , & resizeDimensions . m_imageCount , nullptr ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( ConvertResult ( vkResult ) ) ;
m_swapchainNativeImages . resize ( resizeDimensions . m_imageCount ) ;
// Retrieve the native images of the swapchain so they are
// available when we init the Images in InitImageInternal
vkResult = vkGetSwapchainImagesKHR ( device . GetNativeDevice ( ) , m_nativeSwapChain , & resizeDimensions . m_imageCount , m_swapchainNativeImages . data ( ) ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( ConvertResult ( vkResult ) ) ;
// Do not recycle the semaphore because they may not ever get signaled and since
// we can't recycle Vulkan semaphores we just delete them.
m_currentFrameContext . m_imageAvailableSemaphore - > SetRecycleValue ( false ) ;
m_currentFrameContext . m_presentableSemaphore - > SetRecycleValue ( false ) ;
// Acquire the first image
uint32_t imageIndex = 0 ;
AcquireNewImage ( & imageIndex ) ;
CreateSwapchain ( ) ;
if ( nativeDimensions )
{
* nativeDimensions = resizeD imensions;
* nativeDimensions = m_dimensions ;
// [ATOM-4840] This is a workaround when the windows is minimized (0x0 size).
// Add proper support to handle this case.
nativeDimensions - > m_imageHeight = AZStd : : max ( resizeDimensions . m_imageHeight , 1u ) ;
nativeDimensions - > m_imageWidth = AZStd : : max ( resizeDimensions . m_imageWidth , 1u ) ;
nativeDimensions - > m_imageHeight = AZStd : : max ( m_dimensions . m_imageHeight , 1u ) ;
nativeDimensions - > m_imageWidth = AZStd : : max ( m_dimensions . m_imageWidth , 1u ) ;
nativeDimensions - > m_imageFormat = ConvertFormat ( m_surfaceFormat . format ) ;
}
return RHI : : ResultCode : : Success ;
@ -271,21 +218,49 @@ namespace AZ
info . pImageIndices = & imageIndex ;
info . pResults = nullptr ;
[[maybe_unused]] const VkResult result = vkQueuePresentKHR ( vulkanQueue - > GetNativeQueue ( ) , & info ) ;
const VkResult result = vkQueuePresentKHR ( vulkanQueue - > GetNativeQueue ( ) , & info ) ;
// Resizing window cause recreation of SwapChain after calling this method,
// so VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR should not happen at this point.
AZ_Assert ( result = = VK_SUCCESS | | result = = VK_SUBOPTIMAL_KHR , " Failed to present swapchain %s " , GetName ( ) . GetCStr ( ) ) ;
AZ_Warning ( " Vulkan " , result ! = VK_SUBOPTIMAL_KHR , " Suboptimal presentation of swapchain %s " , GetName ( ) . GetCStr ( ) ) ;
// Vulkan's definition of the two types of errors.
// VK_ERROR_OUT_OF_DATE_KHR: "A surface has changed in such a way that it is no longer compatible with the swapchain,
// and further presentation requests using the swapchain will fail. Applications must query the new surface
// properties and recreate their swapchain if they wish to continue presenting to the surface."
// VK_SUBOPTIMAL_KHR: "A swapchain no longer matches the surface properties exactly, but can still be used to
// present to the surface successfully."
//
// These result values may occur after resizing or some window operation. We should update the surface info and recreate the swapchain.
// VK_SUBOPTIMAL_KHR is treated as success, but we better update the surface info as well.
if ( result = = VK_ERROR_OUT_OF_DATE_KHR | | result = = VK_SUBOPTIMAL_KHR )
{
InvalidateNativeSwapChain ( ) ;
CreateSwapchain ( ) ;
}
else
{
// Other errors are:
// VK_ERROR_OUT_OF_HOST_MEMORY
// VK_ERROR_OUT_OF_DEVICE_MEMORY
// VK_ERROR_DEVICE_LOST
// VK_ERROR_SURFACE_LOST_KHR
// VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT
AZ_Assert ( result = = VK_SUCCESS , " Unhandled error for swapchain presentation. " ) ;
}
} ;
m_presentationQueue - > QueueCommand ( AZStd : : move ( presentCommand ) ) ;
uint32_t acquiredImageIndex = GetCurrentImageIndex ( ) ;
AcquireNewImage ( & acquiredImageIndex ) ;
RHI : : ResultCode result = AcquireNewImage ( & acquiredImageIndex ) ;
if ( result = = RHI : : ResultCode : : Fail )
{
InvalidateNativeSwapChain ( ) ;
CreateSwapchain ( ) ;
return 0 ;
}
else
{
return acquiredImageIndex ;
}
}
RHI : : ResultCode SwapChain : : BuildSurface ( const RHI : : SwapChainDescriptor & descriptor )
{
@ -293,15 +268,8 @@ namespace AZ
surfaceDesc . m_windowHandle = descriptor . m_window ;
RHI : : Ptr < WSISurface > surface = WSISurface : : Create ( ) ;
const RHI : : ResultCode result = surface - > Init ( surfaceDesc ) ;
if ( result = = RHI : : ResultCode : : Success )
{
RETURN_RESULT_IF_UNSUCCESSFUL ( result ) ;
m_surface = surface ;
auto & device = static_cast < Device & > ( GetDevice ( ) ) ;
const auto & physicalDevice = static_cast < const PhysicalDevice & > ( device . GetPhysicalDevice ( ) ) ;
VkResult vkResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR ( physicalDevice . GetNativePhysicalDevice ( ) , m_surface - > GetNativeSurface ( ) , & m_surfaceCapabilities ) ;
AssertSuccess ( vkResult ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( ConvertResult ( vkResult ) ) ;
}
return result ;
}
@ -373,6 +341,21 @@ namespace AZ
return supportedModes [ 0 ] ;
}
VkSurfaceCapabilitiesKHR SwapChain : : GetSurfaceCapabilities ( )
{
AZ_Assert ( m_surface , " Surface has not been initialized. " ) ;
auto & device = static_cast < Device & > ( GetDevice ( ) ) ;
const auto & physicalDevice = static_cast < const PhysicalDevice & > ( device . GetPhysicalDevice ( ) ) ;
VkSurfaceCapabilitiesKHR surfaceCapabilities ;
VkResult vkResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR (
physicalDevice . GetNativePhysicalDevice ( ) , m_surface - > GetNativeSurface ( ) , & surfaceCapabilities ) ;
AssertSuccess ( vkResult ) ;
return surfaceCapabilities ;
}
VkCompositeAlphaFlagBitsKHR SwapChain : : GetSupportedCompositeAlpha ( ) const
{
VkFlags supportedModesBits = m_surfaceCapabilities . supportedCompositeAlpha ;
@ -394,15 +377,9 @@ namespace AZ
return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR ;
}
RHI : : ResultCode SwapChain : : BuildNativeSwapChain ( const RHI : : SwapChainDimensions & dimensions , uint32_t verticalSyncInterval )
RHI : : ResultCode SwapChain : : BuildNativeSwapChain ( const RHI : : SwapChainDimensions & dimensions )
{
AZ_Assert ( m_nativeSwapChain = = VK_NULL_HANDLE , " Vulkan's native SwapChain has been initialized already. " ) ;
auto & device = static_cast < Device & > ( GetDevice ( ) ) ;
auto & queueContext = device . GetCommandQueueContext ( ) ;
const VkExtent2D extent = {
dimensions . m_imageWidth ,
dimensions . m_imageHeight
} ;
AZ_Assert ( m_surface , " Surface is null. " ) ;
if ( ! ValidateSurfaceDimensions ( dimensions ) )
@ -410,7 +387,11 @@ namespace AZ
AZ_Assert ( false , " Swapchain dimensions are not supported. " ) ;
return RHI : : ResultCode : : InvalidArgument ;
}
m_surfaceFormat = GetSupportedSurfaceFormat ( dimensions . m_imageFormat ) ;
auto & device = static_cast < Vulkan : : Device & > ( GetDevice ( ) ) ;
auto & queueContext = device . GetCommandQueueContext ( ) ;
const VkExtent2D extent = { dimensions . m_imageWidth , dimensions . m_imageHeight } ;
// If the graphic queue is the same as the presentation queue, then we will always acquire
// 1 image at the same time. If it's another queue, we will have 2 at the same time (while the other queue
// presents the image)
@ -441,11 +422,11 @@ namespace AZ
createInfo . imageArrayLayers = 1 ; // non-stereoscopic
createInfo . imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT ;
createInfo . imageSharingMode = VK_SHARING_MODE_EXCLUSIVE ;
createInfo . queueFamilyIndexCount = stat ic_cast< uint32_t > ( familyIndices . size ( ) ) ;
createInfo . queueFamilyIndexCount = aznumer ic_cast< uint32_t > ( familyIndices . size ( ) ) ;
createInfo . pQueueFamilyIndices = familyIndices . empty ( ) ? nullptr : familyIndices . data ( ) ;
createInfo . preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ;
createInfo . compositeAlpha = GetSupportedCompositeAlpha( ) ;
createInfo . presentMode = GetSupportedPresentMode( verticalSyncInterval ) ;
createInfo . compositeAlpha = m_compositeAlphaFlagBits ;
createInfo . presentMode = m_presentMode ;
createInfo . clipped = VK_FALSE ;
createInfo . oldSwapchain = VK_NULL_HANDLE ;
@ -467,9 +448,6 @@ namespace AZ
VK_NULL_HANDLE ,
acquiredImageIndex ) ;
// Resizing window cause recreation of SwapChain before calling this method,
// so VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR should not happen.
AssertSuccess ( vkResult ) ;
RHI : : ResultCode result = ConvertResult ( vkResult ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( result ) ;
@ -484,6 +462,7 @@ namespace AZ
}
m_currentFrameContext . m_imageAvailableSemaphore = imageAvailableSemaphore ;
m_currentFrameContext . m_presentableSemaphore = semaphoreAllocator . Allocate ( ) ;
return result ;
}
@ -502,5 +481,70 @@ namespace AZ
m_nativeSwapChain = VK_NULL_HANDLE ;
}
}
RHI : : ResultCode SwapChain : : CreateSwapchain ( )
{
auto & device = static_cast < Device & > ( GetDevice ( ) ) ;
m_surfaceCapabilities = GetSurfaceCapabilities ( ) ;
m_surfaceFormat = GetSupportedSurfaceFormat ( GetDescriptor ( ) . m_dimensions . m_imageFormat ) ;
m_presentMode = GetSupportedPresentMode ( GetDescriptor ( ) . m_verticalSyncInterval ) ;
m_compositeAlphaFlagBits = GetSupportedCompositeAlpha ( ) ;
if ( ! ValidateSurfaceDimensions ( m_dimensions ) )
{
uint32_t oldHeight = m_dimensions . m_imageHeight ;
uint32_t oldWidth = m_dimensions . m_imageWidth ;
m_dimensions . m_imageHeight = AZStd : : clamp (
m_dimensions . m_imageHeight ,
m_surfaceCapabilities . minImageExtent . height ,
m_surfaceCapabilities . maxImageExtent . height ) ;
m_dimensions . m_imageWidth = AZStd : : clamp (
m_dimensions . m_imageWidth ,
m_surfaceCapabilities . minImageExtent . width ,
m_surfaceCapabilities . maxImageExtent . width ) ;
AZ_Printf (
" Vulkan " , " Resizing swapchain from (%u, %u) to (%u, %u). " ,
oldWidth , oldHeight , m_dimensions . m_imageWidth , m_dimensions . m_imageHeight ) ;
}
RHI : : ResultCode result = BuildNativeSwapChain ( m_dimensions ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( result ) ;
AZ_TracePrintf ( " Swapchain " , " Swapchain created. Width: %u, Height: %u. " , m_dimensions . m_imageWidth , m_dimensions . m_imageHeight ) ;
// Do not recycle the semaphore because they may not ever get signaled and since
// we can't recycle Vulkan semaphores we just delete them.
if ( m_currentFrameContext . m_imageAvailableSemaphore )
{
m_currentFrameContext . m_imageAvailableSemaphore - > SetRecycleValue ( false ) ;
}
if ( m_currentFrameContext . m_presentableSemaphore )
{
m_currentFrameContext . m_presentableSemaphore - > SetRecycleValue ( false ) ;
}
m_dimensions . m_imageCount = 0 ;
VkResult vkResult = vkGetSwapchainImagesKHR ( device . GetNativeDevice ( ) , m_nativeSwapChain , & m_dimensions . m_imageCount , nullptr ) ;
AssertSuccess ( vkResult ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( ConvertResult ( vkResult ) ) ;
m_swapchainNativeImages . resize ( m_dimensions . m_imageCount ) ;
// Retrieve the native images of the swapchain so they are
// available when we init the images in InitImageInternal
vkResult = vkGetSwapchainImagesKHR (
device . GetNativeDevice ( ) , m_nativeSwapChain , & m_dimensions . m_imageCount , m_swapchainNativeImages . data ( ) ) ;
AssertSuccess ( vkResult ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( ConvertResult ( vkResult ) ) ;
AZ_TracePrintf ( " Swapchain " , " Obtained presentable images. " ) ;
// Acquire the first image
uint32_t imageIndex = 0 ;
result = AcquireNewImage ( & imageIndex ) ;
RETURN_RESULT_IF_UNSUCCESSFUL ( result ) ;
AZ_TracePrintf ( " Swapchain " , " Acquired the first image. " ) ;
return RHI : : ResultCode : : Success ;
}
}
}