ATOM-16625 [RHI][Vulkan] Swapchain creation issue on viewports (#4854)

* Refactor Vulkan swapchain so it can recreate when error occurs

Signed-off-by: jiaweig <jiaweig@amazon.com>

* revert the workaround

Signed-off-by: jiaweig <jiaweig@amazon.com>

* Move semaphore. Revert some viewport changes.

Signed-off-by: jiaweig <jiaweig@amazon.com>

* Added comments. Moved recreation out of AcquireNewImage.

Signed-off-by: jiaweig <jiaweig@amazon.com>
monroegm-disable-blank-issue-2
jiaweig 4 years ago committed by GitHub
parent 2f057f15c8
commit 0dfa08cac8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -14,92 +14,86 @@ namespace AZ
{ {
namespace RHI namespace RHI
{ {
/** //! The platform-independent swap chain base class. Swap chains contain a "chain" of images which
* The platform-independent swap chain base class. Swap chains contain a "chain" of images which //! map to a platform-specific window, displayed on a physical monitor. The user is allowed
* map to a platform-specific window, displayed on a physical monitor. The user is allowed //! to adjust the swap chain outside of the current FrameScheduler frame. Doing so within a frame scheduler
* to adjust the swap chain outside of the current FrameScheduler frame. Doing so within a frame scheduler //! frame results in undefined behavior.
* frame results in undefined behavior. //!
* //! The frame scheduler controls presentation of the swap chain. The user may attach a swap chain to a scope
* The frame scheduler controls presentation of the swap chain. The user may attach a swap chain to a scope //! in order to render to the current image.
* in order to render to the current image.
*/
class SwapChain class SwapChain
: public ImagePoolBase : public ImagePoolBase
{ {
public: public:
AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object);
virtual ~SwapChain(); virtual ~SwapChain();
/// Initializes the swap chain, making it ready for attachment. //! Initializes the swap chain, making it ready for attachment.
ResultCode Init(RHI::Device& device, const SwapChainDescriptor& descriptor); ResultCode Init(RHI::Device& device, const SwapChainDescriptor& descriptor);
/// Presents the swap chain to the display, and rotates the images. //! Presents the swap chain to the display, and rotates the images.
void Present(); void Present();
/** //! Sets the vertical sync interval for the swap chain.
* Sets the vertical sync interval for the swap chain. //! 0 - No vsync.
* 0 - No vsync. //! N - Sync to every N vertical refresh.
* N - Sync to every N vertical refresh. //!
* //! A value of 1 syncs to the refresh rate of the monitor.
* A value of 1 syncs to the refresh rate of the monitor.
*/
void SetVerticalSyncInterval(uint32_t verticalSyncInterval); void SetVerticalSyncInterval(uint32_t verticalSyncInterval);
/** //! Resizes the display resolution of the swap chain. Ideally, this matches the platform window
* Resizes the display resolution of the swap chain. Ideally, this matches the platform window //! resolution. Typically, the resize operation will occur in reaction to a platform window size
* resolution. Typically, the resize operation will occur in reaction to a platform window size //! change. Takes effect immediately and results in a GPU pipeline flush.
* change. Takes effect immediately and results in a GPU pipeline flush.
*/
ResultCode Resize(const SwapChainDimensions& dimensions); ResultCode Resize(const SwapChainDimensions& dimensions);
/// Returns the number of images in the swap chain. //! Returns the number of images in the swap chain.
uint32_t GetImageCount() const; uint32_t GetImageCount() const;
/// Returns the current image index of the swap chain. //! Returns the current image index of the swap chain.
uint32_t GetCurrentImageIndex() const; uint32_t GetCurrentImageIndex() const;
/// Returns the current image of the swap chain. //! Returns the current image of the swap chain.
Image* GetCurrentImage() const; Image* GetCurrentImage() const;
/// Returns the image associated with the provided index, where the total number of images //! Returns the image associated with the provided index, where the total number of images
/// is given by GetImageCount(). //! is given by GetImageCount().
Image* GetImage(uint32_t index) const; Image* GetImage(uint32_t index) const;
/// Returns the ID used for the SwapChain's attachment //! Returns the ID used for the SwapChain's attachment
const AttachmentId& GetAttachmentId() const; const AttachmentId& GetAttachmentId() const;
/// Returns the descriptor provided when initializing the swap chain. //! Returns the descriptor provided when initializing the swap chain.
const RHI::SwapChainDescriptor& GetDescriptor() const override final; const RHI::SwapChainDescriptor& GetDescriptor() const override final;
//! \return True if the swap chain prefers to use exclusive full screen mode. //! Returns True if the swap chain prefers to use exclusive full screen mode.
virtual bool IsExclusiveFullScreenPreferred() const { return false; } virtual bool IsExclusiveFullScreenPreferred() const { return false; }
//! \return True if the swap chain prefers exclusive full screen mode and it is currently true, false otherwise. //! Returns True if the swap chain prefers exclusive full screen mode and it is currently true, false otherwise.
virtual bool GetExclusiveFullScreenState() const { return false; } virtual bool GetExclusiveFullScreenState() const { return false; }
//! \return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise. //! Return True if the swap chain prefers exclusive full screen mode and a transition happened, false otherwise.
virtual bool SetExclusiveFullScreenState([[maybe_unused]]bool fullScreenState) { return false; } virtual bool SetExclusiveFullScreenState([[maybe_unused]]bool fullScreenState) { return false; }
AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object);
protected: protected:
SwapChain(); SwapChain();
struct InitImageRequest struct InitImageRequest
{ {
/// Pointer to the image to initialize. //! Pointer to the image to initialize.
Image* m_image = nullptr; Image* m_image = nullptr;
/// Index of the image in the swap chain. //! Index of the image in the swap chain.
uint32_t m_imageIndex = 0; uint32_t m_imageIndex = 0;
/// Descriptor for the image. //! Descriptor for the image.
ImageDescriptor m_descriptor; ImageDescriptor m_descriptor;
}; };
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// ResourcePool Overrides // ResourcePool Overrides
/// Called when the pool is shutting down. //! Called when the pool is shutting down.
void ShutdownInternal() override; void ShutdownInternal() override;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -111,32 +105,29 @@ namespace AZ
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Platform API // Platform API
/// Called when the swap chain is initializing. //! Called when the swap chain is initializing.
virtual ResultCode InitInternal(RHI::Device& device, const SwapChainDescriptor& descriptor, SwapChainDimensions* nativeDimensions) = 0; virtual ResultCode InitInternal(RHI::Device& device, const SwapChainDescriptor& descriptor, SwapChainDimensions* nativeDimensions) = 0;
/// called when the swap chain is initializing an image. //! called when the swap chain is initializing an image.
virtual ResultCode InitImageInternal(const InitImageRequest& request) = 0; virtual ResultCode InitImageInternal(const InitImageRequest& request) = 0;
/// Called when the swap chain is resizing. //! Called when the swap chain is resizing.
virtual ResultCode ResizeInternal(const SwapChainDimensions& dimensions, SwapChainDimensions* nativeDimensions) = 0; virtual ResultCode ResizeInternal(const SwapChainDimensions& dimensions, SwapChainDimensions* nativeDimensions) = 0;
/// Called when the swap chain is presenting the currently swap image. //! Called when the swap chain is presenting the currently swap image.
/// Returns the index of the current image after the swap. //! Returns the index of the current image after the swap.
virtual uint32_t PresentInternal() = 0; virtual uint32_t PresentInternal() = 0;
virtual void SetVerticalSyncIntervalInternal(uint32_t previousVerticalSyncInterval) virtual void SetVerticalSyncIntervalInternal([[maybe_unused]]uint32_t previousVerticalSyncInterval) {}
{
AZ_UNUSED(previousVerticalSyncInterval);
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SwapChainDescriptor m_descriptor; SwapChainDescriptor m_descriptor;
/// Images corresponding to each image in the swap chain. //! Images corresponding to each image in the swap chain.
AZStd::vector<Ptr<Image>> m_images; AZStd::vector<Ptr<Image>> m_images;
/// The current image index. //! The current image index.
uint32_t m_currentImageIndex = 0; uint32_t m_currentImageIndex = 0;
}; };
} }

@ -55,7 +55,7 @@ namespace AZ
if (resultCode == ResultCode::Success) if (resultCode == ResultCode::Success)
{ {
m_descriptor = descriptor; m_descriptor = descriptor;
// Ovewrite descriptor dimensions with the native ones (the ones assigned by the platform) returned by InitInternal. // Overwrite descriptor dimensions with the native ones (the ones assigned by the platform) returned by InitInternal.
m_descriptor.m_dimensions = nativeDimensions; m_descriptor.m_dimensions = nativeDimensions;
m_images.reserve(m_descriptor.m_dimensions.m_imageCount); m_images.reserve(m_descriptor.m_dimensions.m_imageCount);

@ -61,13 +61,12 @@ namespace AZ
void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval) void SwapChain::SetVerticalSyncIntervalInternal(uint32_t previousVsyncInterval)
{ {
uint32_t verticalSyncInterval = GetDescriptor().m_verticalSyncInterval; if (GetDescriptor().m_verticalSyncInterval == 0 || previousVsyncInterval == 0)
if (verticalSyncInterval == 0 || previousVsyncInterval == 0)
{ {
// The presentation mode may change when transitioning to or from a vsynced presentation mode // The presentation mode may change when transitioning to or from a vsynced presentation mode
// In this case, the swapchain must be recreated. // In this case, the swapchain must be recreated.
InvalidateNativeSwapChain(); InvalidateNativeSwapChain();
BuildNativeSwapChain(GetDescriptor().m_dimensions, verticalSyncInterval); CreateSwapchain();
} }
} }
@ -85,46 +84,21 @@ namespace AZ
RHI::DeviceObject::Init(baseDevice); RHI::DeviceObject::Init(baseDevice);
auto& device = static_cast<Device&>(GetDevice()); auto& device = static_cast<Device&>(GetDevice());
RHI::SwapChainDimensions swapchainDimensions = descriptor.m_dimensions; m_dimensions = descriptor.m_dimensions;
result = BuildSurface(descriptor); result = BuildSurface(descriptor);
RETURN_RESULT_IF_UNSUCCESSFUL(result); 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); auto& presentationQueue = device.GetCommandQueueContext().GetOrCreatePresentationCommandQueue(*this);
m_presentationQueue = &presentationQueue; 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 result = CreateSwapchain();
// 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);
RETURN_RESULT_IF_UNSUCCESSFUL(result); RETURN_RESULT_IF_UNSUCCESSFUL(result);
if (nativeDimensions) if (nativeDimensions)
{ {
// Fill out the real swapchain dimensions to return // Fill out the real swapchain dimensions to return
nativeDimensions->m_imageCount = imageCount; *nativeDimensions = m_dimensions;
nativeDimensions->m_imageHeight = swapchainDimensions.m_imageHeight;
nativeDimensions->m_imageWidth = swapchainDimensions.m_imageWidth;
nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format); nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format);
} }
@ -165,51 +139,24 @@ namespace AZ
RHI::ResultCode SwapChain::ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions) RHI::ResultCode SwapChain::ResizeInternal(const RHI::SwapChainDimensions& dimensions, RHI::SwapChainDimensions* nativeDimensions)
{ {
auto& device = static_cast<Device&>(GetDevice()); auto& device = static_cast<Device&>(GetDevice());
m_dimensions = dimensions;
InvalidateNativeSwapChain(); 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); auto& presentationQueue = device.GetCommandQueueContext().GetOrCreatePresentationCommandQueue(*this);
m_presentationQueue = &presentationQueue; 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); CreateSwapchain();
// 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);
if (nativeDimensions) if (nativeDimensions)
{ {
*nativeDimensions = resizeDimensions; *nativeDimensions = m_dimensions;
// [ATOM-4840] This is a workaround when the windows is minimized (0x0 size). // [ATOM-4840] This is a workaround when the windows is minimized (0x0 size).
// Add proper support to handle this case. // Add proper support to handle this case.
nativeDimensions->m_imageHeight = AZStd::max(resizeDimensions.m_imageHeight, 1u); nativeDimensions->m_imageHeight = AZStd::max(m_dimensions.m_imageHeight, 1u);
nativeDimensions->m_imageWidth = AZStd::max(resizeDimensions.m_imageWidth, 1u); nativeDimensions->m_imageWidth = AZStd::max(m_dimensions.m_imageWidth, 1u);
nativeDimensions->m_imageFormat = ConvertFormat(m_surfaceFormat.format);
} }
return RHI::ResultCode::Success; return RHI::ResultCode::Success;
@ -271,21 +218,49 @@ namespace AZ
info.pImageIndices = &imageIndex; info.pImageIndices = &imageIndex;
info.pResults = nullptr; 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, // Vulkan's definition of the two types of errors.
// so VK_SUBOPTIMAL_KHR or VK_ERROR_OUT_OF_DATE_KHR should not happen at this point. // VK_ERROR_OUT_OF_DATE_KHR: "A surface has changed in such a way that it is no longer compatible with the swapchain,
AZ_Assert(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR, "Failed to present swapchain %s", GetName().GetCStr()); // and further presentation requests using the swapchain will fail. Applications must query the new surface
AZ_Warning("Vulkan", result != VK_SUBOPTIMAL_KHR, "Suboptimal presentation of swapchain %s", GetName().GetCStr()); // 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)); m_presentationQueue->QueueCommand(AZStd::move(presentCommand));
uint32_t acquiredImageIndex = GetCurrentImageIndex(); uint32_t acquiredImageIndex = GetCurrentImageIndex();
AcquireNewImage(&acquiredImageIndex); RHI::ResultCode result = AcquireNewImage(&acquiredImageIndex);
if (result == RHI::ResultCode::Fail)
{
InvalidateNativeSwapChain();
CreateSwapchain();
return 0;
}
else
{
return acquiredImageIndex; return acquiredImageIndex;
} }
}
RHI::ResultCode SwapChain::BuildSurface(const RHI::SwapChainDescriptor& descriptor) RHI::ResultCode SwapChain::BuildSurface(const RHI::SwapChainDescriptor& descriptor)
{ {
@ -293,15 +268,8 @@ namespace AZ
surfaceDesc.m_windowHandle = descriptor.m_window; surfaceDesc.m_windowHandle = descriptor.m_window;
RHI::Ptr<WSISurface> surface = WSISurface::Create(); RHI::Ptr<WSISurface> surface = WSISurface::Create();
const RHI::ResultCode result = surface->Init(surfaceDesc); const RHI::ResultCode result = surface->Init(surfaceDesc);
if (result == RHI::ResultCode::Success) RETURN_RESULT_IF_UNSUCCESSFUL(result);
{
m_surface = surface; 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; return result;
} }
@ -373,6 +341,21 @@ namespace AZ
return supportedModes[0]; 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 VkCompositeAlphaFlagBitsKHR SwapChain::GetSupportedCompositeAlpha() const
{ {
VkFlags supportedModesBits = m_surfaceCapabilities.supportedCompositeAlpha; VkFlags supportedModesBits = m_surfaceCapabilities.supportedCompositeAlpha;
@ -394,15 +377,9 @@ namespace AZ
return VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; 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."); 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."); AZ_Assert(m_surface, "Surface is null.");
if (!ValidateSurfaceDimensions(dimensions)) if (!ValidateSurfaceDimensions(dimensions))
@ -410,7 +387,11 @@ namespace AZ
AZ_Assert(false, "Swapchain dimensions are not supported."); AZ_Assert(false, "Swapchain dimensions are not supported.");
return RHI::ResultCode::InvalidArgument; 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 // 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 // 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) // presents the image)
@ -441,11 +422,11 @@ namespace AZ
createInfo.imageArrayLayers = 1; // non-stereoscopic 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.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.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = static_cast<uint32_t>(familyIndices.size()); createInfo.queueFamilyIndexCount = aznumeric_cast<uint32_t>(familyIndices.size());
createInfo.pQueueFamilyIndices = familyIndices.empty() ? nullptr : familyIndices.data(); createInfo.pQueueFamilyIndices = familyIndices.empty() ? nullptr : familyIndices.data();
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = GetSupportedCompositeAlpha(); createInfo.compositeAlpha = m_compositeAlphaFlagBits;
createInfo.presentMode = GetSupportedPresentMode(verticalSyncInterval); createInfo.presentMode = m_presentMode;
createInfo.clipped = VK_FALSE; createInfo.clipped = VK_FALSE;
createInfo.oldSwapchain = VK_NULL_HANDLE; createInfo.oldSwapchain = VK_NULL_HANDLE;
@ -467,9 +448,6 @@ namespace AZ
VK_NULL_HANDLE, VK_NULL_HANDLE,
acquiredImageIndex); 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); RHI::ResultCode result = ConvertResult(vkResult);
RETURN_RESULT_IF_UNSUCCESSFUL(result); RETURN_RESULT_IF_UNSUCCESSFUL(result);
@ -484,6 +462,7 @@ namespace AZ
} }
m_currentFrameContext.m_imageAvailableSemaphore = imageAvailableSemaphore; m_currentFrameContext.m_imageAvailableSemaphore = imageAvailableSemaphore;
m_currentFrameContext.m_presentableSemaphore = semaphoreAllocator.Allocate(); m_currentFrameContext.m_presentableSemaphore = semaphoreAllocator.Allocate();
return result; return result;
} }
@ -502,5 +481,70 @@ namespace AZ
m_nativeSwapChain = VK_NULL_HANDLE; 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;
}
} }
} }

@ -70,24 +70,48 @@ namespace AZ
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
RHI::ResultCode BuildSurface(const RHI::SwapChainDescriptor& descriptor); RHI::ResultCode BuildSurface(const RHI::SwapChainDescriptor& descriptor);
//! Returns true is the swapchain dimensions are supported by the current surface.
bool ValidateSurfaceDimensions(const RHI::SwapChainDimensions& dimensions); bool ValidateSurfaceDimensions(const RHI::SwapChainDimensions& dimensions);
//! Returns the corresponding Vulkan format that is supported by the surface.
//! If such format is not found, return the first supported format from the surface.
VkSurfaceFormatKHR GetSupportedSurfaceFormat(const RHI::Format format) const; VkSurfaceFormatKHR GetSupportedSurfaceFormat(const RHI::Format format) const;
//! Returns the correct presentation mode.
//! If verticalSyncInterval is non-zero, returns VK_PRESENT_MODE_FIFO_KHR.
//! Otherwise, choose preferred mode if they are supported.
//! If not, the first supported present mode is returned.
VkPresentModeKHR GetSupportedPresentMode(uint32_t verticalSyncInterval) const; VkPresentModeKHR GetSupportedPresentMode(uint32_t verticalSyncInterval) const;
//! Returns the preferred alpha compositing modes if they are supported.
//! If not, error will be reported.
VkCompositeAlphaFlagBitsKHR GetSupportedCompositeAlpha() const; VkCompositeAlphaFlagBitsKHR GetSupportedCompositeAlpha() const;
RHI::ResultCode BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions, uint32_t verticalSyncInterval); //! Returns the current surface capabilities.
VkSurfaceCapabilitiesKHR GetSurfaceCapabilities();
//! Create the swapchain when initializing, or
//! swapchain is no longer compatible or is sub-optimal with the surface.
RHI::ResultCode CreateSwapchain();
//! Build underlying Vulkan swapchain.
RHI::ResultCode BuildNativeSwapChain(const RHI::SwapChainDimensions& dimensions);
//! Retrieve the index of the next available presentable image.
RHI::ResultCode AcquireNewImage(uint32_t* acquiredImageIndex); RHI::ResultCode AcquireNewImage(uint32_t* acquiredImageIndex);
//! Destroy the surface.
void InvalidateSurface(); void InvalidateSurface();
//! Destroy the old swapchain.
void InvalidateNativeSwapChain(); void InvalidateNativeSwapChain();
VkSwapchainKHR m_nativeSwapChain = VK_NULL_HANDLE;
RHI::Ptr<WSISurface> m_surface; RHI::Ptr<WSISurface> m_surface;
VkSwapchainKHR m_nativeSwapChain = VK_NULL_HANDLE;
CommandQueue* m_presentationQueue = nullptr; CommandQueue* m_presentationQueue = nullptr;
VkSurfaceFormatKHR m_surfaceFormat = {};
VkSurfaceCapabilitiesKHR m_surfaceCapabilities;
FrameContext m_currentFrameContext; FrameContext m_currentFrameContext;
//! Swapchain data
VkSurfaceFormatKHR m_surfaceFormat = {};
VkSurfaceCapabilitiesKHR m_surfaceCapabilities = {};
VkPresentModeKHR m_presentMode = {};
VkCompositeAlphaFlagBitsKHR m_compositeAlphaFlagBits = {};
AZStd::vector<VkImage> m_swapchainNativeImages;
RHI::SwapChainDimensions m_dimensions;
struct SwapChainBarrier struct SwapChainBarrier
{ {
VkPipelineStageFlags m_srcPipelineStages = 0; VkPipelineStageFlags m_srcPipelineStages = 0;
@ -95,8 +119,6 @@ namespace AZ
VkImageMemoryBarrier m_barrier = {}; VkImageMemoryBarrier m_barrier = {};
bool m_isValid = false; bool m_isValid = false;
} m_swapChainBarrier; } m_swapChainBarrier;
AZStd::vector<VkImage> m_swapchainNativeImages;
}; };
} }
} }

@ -196,12 +196,8 @@ namespace AtomToolsFramework
bool RenderViewportWidget::event(QEvent* event) bool RenderViewportWidget::event(QEvent* event)
{ {
// On some types of QEvents, a resize event is needed to make sure that the current viewport window
// needs to be updated based on a potential new surface dimensions.
switch (event->type()) switch (event->type())
{ {
case QEvent::ScreenChangeInternal:
case QEvent::UpdateLater:
case QEvent::Resize: case QEvent::Resize:
SendWindowResizeEvent(); SendWindowResizeEvent();
break; break;

Loading…
Cancel
Save