/* * Copyright 2019-2021 Diligent Graphics LLC * Copyright 2015-2019 Egor Yusov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * In no event and under no legal theory, whether in tort (including negligence), * contract, or otherwise, unless required by applicable law (such as deliberate * and grossly negligent acts) or agreed to in writing, shall any Contributor be * liable for any damages, including any direct, indirect, special, incidental, * or consequential damages of any character arising as a result of this License or * out of the use or inability to use the software (including but not limited to damages * for loss of goodwill, work stoppage, computer failure or malfunction, or any and * all other commercial damages or losses), even if such Contributor has been advised * of the possibility of such damages. */ #include "pch.h" #include "SwapChainVkImpl.hpp" #include "RenderDeviceVkImpl.hpp" #include "DeviceContextVkImpl.hpp" #include "TextureVkImpl.hpp" #include "VulkanTypeConversions.hpp" #include "EngineMemory.h" namespace Diligent { SwapChainVkImpl::SwapChainVkImpl(IReferenceCounters* pRefCounters, const SwapChainDesc& SCDesc, RenderDeviceVkImpl* pRenderDeviceVk, DeviceContextVkImpl* pDeviceContextVk, uint32_t SwapChainImageCount, const VkImage* pSwapChainImages, SwapChainImageCallbacks SwapChainCallbacks) : // clang-format off TSwapChainBase {pRefCounters, pRenderDeviceVk, pDeviceContextVk, SCDesc}, m_VulkanInstance {pRenderDeviceVk->GetVulkanInstance()}, m_pBackBufferRTV (STD_ALLOCATOR_RAW_MEM(RefCntAutoPtr, GetRawAllocator(), "Allocator for vector>")), m_SwapChainImagesInitialized (STD_ALLOCATOR_RAW_MEM(bool, GetRawAllocator(), "Allocator for vector")), m_Cookie (SwapChainCallbacks.Cookie), m_AcquireImageCallback (SwapChainCallbacks.ImageCallbackAcquire), m_ReleaseImageCallback (SwapChainCallbacks.ImageCallbackRelease) // clang-format on { InitBuffersAndViews(SwapChainImageCount, pSwapChainImages); } SwapChainVkImpl::~SwapChainVkImpl() { auto pDeviceContext = m_wpDeviceContext.Lock(); auto* pImmediateCtxVk = pDeviceContext.RawPtr(); ReleaseSwapChainResources(pImmediateCtxVk); } void SwapChainVkImpl::InitBuffersAndViews(uint32_t SwapChainImageCount, const VkImage *pSwapChainImages) { auto* pDeviceVkImpl = m_pRenderDevice.RawPtr(); auto LogicalVkDevice = pDeviceVkImpl->GetVkDevice(); m_pBackBufferRTV.resize(m_SwapChainDesc.BufferCount); m_SwapChainImagesInitialized.resize(m_pBackBufferRTV.size(), false); for (uint32_t i = 0; i < SwapChainImageCount; i++) { TextureDesc BackBufferDesc; std::stringstream name_ss; name_ss << "Main back buffer " << i; auto name = name_ss.str(); BackBufferDesc.Name = name.c_str(); BackBufferDesc.Type = RESOURCE_DIM_TEX_2D; BackBufferDesc.Width = m_SwapChainDesc.Width; BackBufferDesc.Height = m_SwapChainDesc.Height; BackBufferDesc.Format = m_SwapChainDesc.ColorBufferFormat; BackBufferDesc.BindFlags = BIND_RENDER_TARGET; BackBufferDesc.MipLevels = 1; RefCntAutoPtr pBackBufferTex; m_pRenderDevice.RawPtr()->CreateTexture(BackBufferDesc, pSwapChainImages[i], RESOURCE_STATE_UNDEFINED, &pBackBufferTex); TextureViewDesc RTVDesc; RTVDesc.ViewType = TEXTURE_VIEW_RENDER_TARGET; RefCntAutoPtr pRTV; pBackBufferTex->CreateView(RTVDesc, &pRTV); m_pBackBufferRTV[i] = RefCntAutoPtr(pRTV, IID_TextureViewVk); } if (m_SwapChainDesc.DepthBufferFormat != TEX_FORMAT_UNKNOWN) { TextureDesc DepthBufferDesc; DepthBufferDesc.Type = RESOURCE_DIM_TEX_2D; DepthBufferDesc.Width = m_SwapChainDesc.Width; DepthBufferDesc.Height = m_SwapChainDesc.Height; DepthBufferDesc.Format = m_SwapChainDesc.DepthBufferFormat; DepthBufferDesc.SampleCount = 1; DepthBufferDesc.Usage = USAGE_DEFAULT; DepthBufferDesc.BindFlags = BIND_DEPTH_STENCIL; DepthBufferDesc.ClearValue.Format = DepthBufferDesc.Format; DepthBufferDesc.ClearValue.DepthStencil.Depth = m_SwapChainDesc.DefaultDepthValue; DepthBufferDesc.ClearValue.DepthStencil.Stencil = m_SwapChainDesc.DefaultStencilValue; DepthBufferDesc.Name = "Main depth buffer"; RefCntAutoPtr pDepthBufferTex; m_pRenderDevice->CreateTexture(DepthBufferDesc, nullptr, static_cast(&pDepthBufferTex)); auto pDSV = pDepthBufferTex->GetDefaultView(TEXTURE_VIEW_DEPTH_STENCIL); m_pDepthBufferDSV = RefCntAutoPtr(pDSV, IID_TextureViewVk); } } VkResult SwapChainVkImpl::AcquireNextImage() { auto pDeviceContext = m_wpDeviceContext.Lock(); if (!pDeviceContext) { LOG_ERROR_MESSAGE("Immediate context has been released"); return VK_ERROR_UNKNOWN; } auto* pDeviceCtxVk = pDeviceContext.RawPtr(); auto* pDeviceVk = m_pRenderDevice.RawPtr(); // Explicitly make sure that there are no more pending frames in the command queue // than the number of the swap chain images. // // Nsc = 3 - number of the swap chain images // // N-Ns N-2 N-1 N (Current frame) // | | | | // | // Wait for this fence // // When acquiring swap chain image for frame N, we need to make sure that // frame N-Nsc has completed. To achieve that, we wait for the image acquire // fence for frame N-Nsc-1. Thus we will have no more than Nsc frames in the queue. pDeviceVk->LockCommandQueue(0); VkResult res = m_AcquireImageCallback(m_Cookie, &m_BackBufferIndex); pDeviceVk->UnlockCommandQueue(0); if (res == VK_SUCCESS) { // Next command in the device context must wait for the next image to be acquired. // Unlike fences or events, the act of waiting for a semaphore also unsignals that semaphore (6.4.2). // Swapchain image may be used as render target or as destination for copy command. if (!m_SwapChainImagesInitialized[m_BackBufferIndex]) { // Vulkan validation layers do not like uninitialized memory. // Clear back buffer first time we acquire it. ITextureView* pRTV = GetCurrentBackBufferRTV(); ITextureView* pDSV = GetDepthBufferDSV(); pDeviceCtxVk->SetRenderTargets(1, &pRTV, pDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); pDeviceCtxVk->ClearRenderTarget(GetCurrentBackBufferRTV(), nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); m_SwapChainImagesInitialized[m_BackBufferIndex] = true; } pDeviceCtxVk->SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_NONE); } return res; } void SwapChainVkImpl::Present(Uint32 SyncInterval) { if (SyncInterval != 0 && SyncInterval != 1) LOG_WARNING_MESSAGE_ONCE("Vulkan only supports 0 and 1 present intervals"); PresentImage(); } void SwapChainVkImpl::PresentImage() { auto pDeviceContext = m_wpDeviceContext.Lock(); if (!pDeviceContext) { LOG_ERROR_MESSAGE("Immediate context has been released"); return; } auto* pImmediateCtxVk = pDeviceContext.RawPtr(); auto* pDeviceVk = m_pRenderDevice.RawPtr(); auto* pBackBuffer = GetCurrentBackBufferRTV()->GetTexture(); pImmediateCtxVk->UnbindTextureFromFramebuffer(ValidatedCast(pBackBuffer), false); if (!m_IsMinimized) { // TransitionImageLayout() never triggers flush pImmediateCtxVk->TransitionImageLayout(pBackBuffer, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); // The context can be empty if no render commands were issued by the app //VERIFY(pImmediateCtxVk->GetNumCommandsInCtx() != 0, "The context must not be flushed"); } pImmediateCtxVk->Flush(); if (!m_IsMinimized) { pDeviceVk->LockCommandQueue(0); m_ReleaseImageCallback(m_Cookie); pDeviceVk->UnlockCommandQueue(0); } if (m_SwapChainDesc.IsPrimary) { pImmediateCtxVk->FinishFrame(); pDeviceVk->ReleaseStaleResources(); } } void SwapChainVkImpl::ReleaseSwapChainResources(DeviceContextVkImpl* pImmediateCtxVk) { if (pImmediateCtxVk != nullptr) { // Flush to submit all pending commands and semaphores to the queue. pImmediateCtxVk->Flush(); bool RenderTargetsReset = false; for (Uint32 i = 0; i < m_pBackBufferRTV.size() && !RenderTargetsReset; ++i) { auto* pCurrentBackBuffer = ValidatedCast(m_pBackBufferRTV[i]->GetTexture()); RenderTargetsReset = pImmediateCtxVk->UnbindTextureFromFramebuffer(pCurrentBackBuffer, false); } if (RenderTargetsReset) { LOG_INFO_MESSAGE_ONCE("The swap chain's back and depth-stencil buffers were unbound from the device context because " "the swap chain is being destroyed. An application should use SetRenderTargets() to restore them."); } } RenderDeviceVkImpl* pDeviceVk = m_pRenderDevice.RawPtr(); // This will release references to Vk swap chain buffers hold by // m_pBackBufferRTV[]. pDeviceVk->IdleGPU(); // All references to the swap chain must be released before it can be destroyed m_pBackBufferRTV.clear(); m_SwapChainImagesInitialized.clear(); m_pDepthBufferDSV.Release(); } void SwapChainVkImpl::Resize(Uint32 NewWidth, Uint32 NewHeight, SURFACE_TRANSFORM NewPreTransform) { m_IsMinimized = (NewWidth == 0 && NewHeight == 0); } void SwapChainVkImpl::SetFullscreenMode(const DisplayModeAttribs& DisplayMode) { } void SwapChainVkImpl::SetWindowedMode() { } } // namespace Diligent