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>
This commit is contained in:
@@ -14,92 +14,86 @@ namespace AZ
|
||||
{
|
||||
namespace RHI
|
||||
{
|
||||
/**
|
||||
* 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
|
||||
* to adjust the swap chain outside of the current FrameScheduler frame. Doing so within a frame scheduler
|
||||
* frame results in undefined behavior.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
//! 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
|
||||
//! to adjust the swap chain outside of the current FrameScheduler frame. Doing so within a frame scheduler
|
||||
//! frame results in undefined behavior.
|
||||
//!
|
||||
//! 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.
|
||||
class SwapChain
|
||||
: public ImagePoolBase
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object);
|
||||
|
||||
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);
|
||||
|
||||
/// Presents the swap chain to the display, and rotates the images.
|
||||
//! Presents the swap chain to the display, and rotates the images.
|
||||
void Present();
|
||||
|
||||
/**
|
||||
* Sets the vertical sync interval for the swap chain.
|
||||
* 0 - No vsync.
|
||||
* N - Sync to every N vertical refresh.
|
||||
*
|
||||
* A value of 1 syncs to the refresh rate of the monitor.
|
||||
*/
|
||||
//! Sets the vertical sync interval for the swap chain.
|
||||
//! 0 - No vsync.
|
||||
//! N - Sync to every N vertical refresh.
|
||||
//!
|
||||
//! A value of 1 syncs to the refresh rate of the monitor.
|
||||
void SetVerticalSyncInterval(uint32_t verticalSyncInterval);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* change. Takes effect immediately and results in a GPU pipeline flush.
|
||||
*/
|
||||
//! 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
|
||||
//! change. Takes effect immediately and results in a GPU pipeline flush.
|
||||
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;
|
||||
|
||||
/// Returns the current image index of the swap chain.
|
||||
//! Returns the current image index of the swap chain.
|
||||
uint32_t GetCurrentImageIndex() const;
|
||||
|
||||
/// Returns the current image of the swap chain.
|
||||
//! Returns the current image of the swap chain.
|
||||
Image* GetCurrentImage() const;
|
||||
|
||||
/// Returns the image associated with the provided index, where the total number of images
|
||||
/// is given by GetImageCount().
|
||||
//! Returns the image associated with the provided index, where the total number of images
|
||||
//! is given by GetImageCount().
|
||||
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;
|
||||
|
||||
/// 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;
|
||||
|
||||
//! \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; }
|
||||
|
||||
//! \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; }
|
||||
|
||||
//! \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; }
|
||||
|
||||
AZ_RTTI(SwapChain, "{888B64A5-D956-406F-9C33-CF6A54FC41B0}", Object);
|
||||
|
||||
protected:
|
||||
SwapChain();
|
||||
|
||||
struct InitImageRequest
|
||||
{
|
||||
/// Pointer to the image to initialize.
|
||||
//! Pointer to the image to initialize.
|
||||
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;
|
||||
|
||||
/// Descriptor for the image.
|
||||
//! Descriptor for the image.
|
||||
ImageDescriptor m_descriptor;
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// ResourcePool Overrides
|
||||
|
||||
/// Called when the pool is shutting down.
|
||||
//! Called when the pool is shutting down.
|
||||
void ShutdownInternal() override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -111,32 +105,29 @@ namespace AZ
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// 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;
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Called when the swap chain is resizing.
|
||||
//! Called when the swap chain is resizing.
|
||||
virtual ResultCode ResizeInternal(const SwapChainDimensions& dimensions, SwapChainDimensions* nativeDimensions) = 0;
|
||||
|
||||
/// Called when the swap chain is presenting the currently swap image.
|
||||
/// Returns the index of the current image after the swap.
|
||||
//! Called when the swap chain is presenting the currently swap image.
|
||||
//! Returns the index of the current image after the swap.
|
||||
virtual uint32_t PresentInternal() = 0;
|
||||
|
||||
virtual void SetVerticalSyncIntervalInternal(uint32_t previousVerticalSyncInterval)
|
||||
{
|
||||
AZ_UNUSED(previousVerticalSyncInterval);
|
||||
}
|
||||
virtual void SetVerticalSyncIntervalInternal([[maybe_unused]]uint32_t previousVerticalSyncInterval) {}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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;
|
||||
|
||||
/// The current image index.
|
||||
//! The current image index.
|
||||
uint32_t m_currentImageIndex = 0;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace AZ
|
||||
if (resultCode == ResultCode::Success)
|
||||
{
|
||||
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_images.reserve(m_descriptor.m_dimensions.m_imageCount);
|
||||
@@ -129,8 +129,8 @@ namespace AZ
|
||||
while (m_images.size() > static_cast<size_t>(m_descriptor.m_dimensions.m_imageCount))
|
||||
{
|
||||
m_images.pop_back();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
InitImageRequest request;
|
||||
|
||||
RHI::ImageDescriptor& imageDescriptor = request.m_descriptor;
|
||||
|
||||
@@ -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 = resizeDimensions;
|
||||
*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,20 +218,48 @@ 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);
|
||||
|
||||
return 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)
|
||||
{
|
||||
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_IF_UNSUCCESSFUL(result);
|
||||
m_surface = surface;
|
||||
|
||||
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 = static_cast<uint32_t>(familyIndices.size());
|
||||
createInfo.queueFamilyIndexCount = aznumeric_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,24 +70,48 @@ namespace AZ
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
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);
|
||||
//! 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;
|
||||
//! 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;
|
||||
//! Returns the preferred alpha compositing modes if they are supported.
|
||||
//! If not, error will be reported.
|
||||
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);
|
||||
|
||||
//! Destroy the surface.
|
||||
void InvalidateSurface();
|
||||
//! Destroy the old swapchain.
|
||||
void InvalidateNativeSwapChain();
|
||||
|
||||
VkSwapchainKHR m_nativeSwapChain = VK_NULL_HANDLE;
|
||||
RHI::Ptr<WSISurface> m_surface;
|
||||
VkSwapchainKHR m_nativeSwapChain = VK_NULL_HANDLE;
|
||||
CommandQueue* m_presentationQueue = nullptr;
|
||||
VkSurfaceFormatKHR m_surfaceFormat = {};
|
||||
VkSurfaceCapabilitiesKHR m_surfaceCapabilities;
|
||||
|
||||
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
|
||||
{
|
||||
VkPipelineStageFlags m_srcPipelineStages = 0;
|
||||
@@ -95,8 +119,6 @@ namespace AZ
|
||||
VkImageMemoryBarrier m_barrier = {};
|
||||
bool m_isValid = false;
|
||||
} m_swapChainBarrier;
|
||||
|
||||
AZStd::vector<VkImage> m_swapchainNativeImages;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,12 +196,8 @@ namespace AtomToolsFramework
|
||||
|
||||
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())
|
||||
{
|
||||
case QEvent::ScreenChangeInternal:
|
||||
case QEvent::UpdateLater:
|
||||
case QEvent::Resize:
|
||||
SendWindowResizeEvent();
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user