| // Copyright 2018 The Dawn Authors | |
| // | |
| // 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. | |
| #include "dawn_native/vulkan/NativeSwapChainImplVk.h" | |
| #include "dawn_native/vulkan/DeviceVk.h" | |
| #include "dawn_native/vulkan/FencedDeleter.h" | |
| #include "dawn_native/vulkan/TextureVk.h" | |
| #include <limits> | |
| namespace dawn_native { namespace vulkan { | |
| namespace { | |
| bool chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes, | |
| bool turnOffVsync, | |
| VkPresentModeKHR* presentMode) { | |
| if (turnOffVsync) { | |
| for (const auto& availablePresentMode : availablePresentModes) { | |
| if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) { | |
| *presentMode = availablePresentMode; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| *presentMode = VK_PRESENT_MODE_FIFO_KHR; | |
| return true; | |
| } | |
| bool ChooseSurfaceConfig(const VulkanSurfaceInfo& info, | |
| NativeSwapChainImpl::ChosenConfig* config, | |
| bool turnOffVsync) { | |
| VkPresentModeKHR presentMode; | |
| if (!chooseSwapPresentMode(info.presentModes, turnOffVsync, &presentMode)) { | |
| return false; | |
| } | |
| // TODO(crbug.com/dawn/269): For now this is hardcoded to what works with one NVIDIA | |
| // driver. Need to generalize | |
| config->nativeFormat = VK_FORMAT_B8G8R8A8_UNORM; | |
| config->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; | |
| config->format = wgpu::TextureFormat::BGRA8Unorm; | |
| config->minImageCount = 3; | |
| // TODO(crbug.com/dawn/269): This is upside down compared to what we want, at least | |
| // on Linux | |
| config->preTransform = info.capabilities.currentTransform; | |
| config->presentMode = presentMode; | |
| config->compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
| return true; | |
| } | |
| } // anonymous namespace | |
| NativeSwapChainImpl::NativeSwapChainImpl(Device* device, VkSurfaceKHR surface) | |
| : mSurface(surface), mDevice(device) { | |
| // Call this immediately, so that BackendBinding::GetPreferredSwapChainTextureFormat | |
| // will return a correct result before a SwapChain is created. | |
| UpdateSurfaceConfig(); | |
| } | |
| NativeSwapChainImpl::~NativeSwapChainImpl() { | |
| if (mSwapChain != VK_NULL_HANDLE) { | |
| mDevice->GetFencedDeleter()->DeleteWhenUnused(mSwapChain); | |
| mSwapChain = VK_NULL_HANDLE; | |
| } | |
| if (mSurface != VK_NULL_HANDLE) { | |
| mDevice->GetFencedDeleter()->DeleteWhenUnused(mSurface); | |
| mSurface = VK_NULL_HANDLE; | |
| } | |
| } | |
| void NativeSwapChainImpl::UpdateSurfaceConfig() { | |
| if (mDevice->ConsumedError(GatherSurfaceInfo(*ToBackend(mDevice->GetAdapter()), mSurface), | |
| &mInfo)) { | |
| ASSERT(false); | |
| } | |
| if (!ChooseSurfaceConfig(mInfo, &mConfig, mDevice->IsToggleEnabled(Toggle::TurnOffVsync))) { | |
| ASSERT(false); | |
| } | |
| } | |
| void NativeSwapChainImpl::Init(DawnWSIContextVulkan* /*context*/) { | |
| UpdateSurfaceConfig(); | |
| } | |
| DawnSwapChainError NativeSwapChainImpl::Configure(WGPUTextureFormat format, | |
| WGPUTextureUsage usage, | |
| uint32_t width, | |
| uint32_t height) { | |
| UpdateSurfaceConfig(); | |
| ASSERT(mInfo.capabilities.minImageExtent.width <= width); | |
| ASSERT(mInfo.capabilities.maxImageExtent.width >= width); | |
| ASSERT(mInfo.capabilities.minImageExtent.height <= height); | |
| ASSERT(mInfo.capabilities.maxImageExtent.height >= height); | |
| ASSERT(format == static_cast<WGPUTextureFormat>(GetPreferredFormat())); | |
| // TODO(crbug.com/dawn/269): need to check usage works too | |
| // Create the swapchain with the configuration we chose | |
| VkSwapchainKHR oldSwapchain = mSwapChain; | |
| VkSwapchainCreateInfoKHR createInfo; | |
| createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
| createInfo.pNext = nullptr; | |
| createInfo.flags = 0; | |
| createInfo.surface = mSurface; | |
| createInfo.minImageCount = mConfig.minImageCount; | |
| createInfo.imageFormat = mConfig.nativeFormat; | |
| createInfo.imageColorSpace = mConfig.colorSpace; | |
| createInfo.imageExtent.width = width; | |
| createInfo.imageExtent.height = height; | |
| createInfo.imageArrayLayers = 1; | |
| createInfo.imageUsage = VulkanImageUsage(static_cast<wgpu::TextureUsage>(usage), | |
| mDevice->GetValidInternalFormat(mConfig.format)); | |
| createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
| createInfo.queueFamilyIndexCount = 0; | |
| createInfo.pQueueFamilyIndices = nullptr; | |
| createInfo.preTransform = mConfig.preTransform; | |
| createInfo.compositeAlpha = mConfig.compositeAlpha; | |
| createInfo.presentMode = mConfig.presentMode; | |
| createInfo.clipped = false; | |
| createInfo.oldSwapchain = oldSwapchain; | |
| if (mDevice->fn.CreateSwapchainKHR(mDevice->GetVkDevice(), &createInfo, nullptr, | |
| &*mSwapChain) != VK_SUCCESS) { | |
| ASSERT(false); | |
| } | |
| // Gather the swapchain's images. Implementations are allowed to return more images than the | |
| // number we asked for. | |
| uint32_t count = 0; | |
| if (mDevice->fn.GetSwapchainImagesKHR(mDevice->GetVkDevice(), mSwapChain, &count, | |
| nullptr) != VK_SUCCESS) { | |
| ASSERT(false); | |
| } | |
| ASSERT(count >= mConfig.minImageCount); | |
| mSwapChainImages.resize(count); | |
| if (mDevice->fn.GetSwapchainImagesKHR(mDevice->GetVkDevice(), mSwapChain, &count, | |
| AsVkArray(mSwapChainImages.data())) != VK_SUCCESS) { | |
| ASSERT(false); | |
| } | |
| if (oldSwapchain != VK_NULL_HANDLE) { | |
| mDevice->GetFencedDeleter()->DeleteWhenUnused(oldSwapchain); | |
| } | |
| return DAWN_SWAP_CHAIN_NO_ERROR; | |
| } | |
| DawnSwapChainError NativeSwapChainImpl::GetNextTexture(DawnSwapChainNextTexture* nextTexture) { | |
| // Transiently create a semaphore that will be signaled when the presentation engine is done | |
| // with the swapchain image. Further operations on the image will wait for this semaphore. | |
| VkSemaphore semaphore = VK_NULL_HANDLE; | |
| { | |
| VkSemaphoreCreateInfo createInfo; | |
| createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; | |
| createInfo.pNext = nullptr; | |
| createInfo.flags = 0; | |
| if (mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &createInfo, nullptr, | |
| &*semaphore) != VK_SUCCESS) { | |
| ASSERT(false); | |
| } | |
| } | |
| if (mDevice->fn.AcquireNextImageKHR(mDevice->GetVkDevice(), mSwapChain, | |
| std::numeric_limits<uint64_t>::max(), semaphore, | |
| VkFence{}, &mLastImageIndex) != VK_SUCCESS) { | |
| ASSERT(false); | |
| } | |
| nextTexture->texture.u64 = | |
| #if defined(DAWN_PLATFORM_64_BIT) | |
| reinterpret_cast<uint64_t> | |
| #endif | |
| (*mSwapChainImages[mLastImageIndex]); | |
| mDevice->GetPendingRecordingContext()->waitSemaphores.push_back(semaphore); | |
| return DAWN_SWAP_CHAIN_NO_ERROR; | |
| } | |
| DawnSwapChainError NativeSwapChainImpl::Present() { | |
| // This assumes that the image has already been transitioned to the PRESENT layout and | |
| // writes were made available to the stage. | |
| // Assuming that the present queue is the same as the graphics queue, the proper | |
| // synchronization has already been done on the queue so we don't need to wait on any | |
| // semaphores. | |
| VkPresentInfoKHR presentInfo; | |
| presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
| presentInfo.pNext = nullptr; | |
| presentInfo.waitSemaphoreCount = 0; | |
| presentInfo.pWaitSemaphores = nullptr; | |
| presentInfo.swapchainCount = 1; | |
| presentInfo.pSwapchains = &*mSwapChain; | |
| presentInfo.pImageIndices = &mLastImageIndex; | |
| presentInfo.pResults = nullptr; | |
| VkQueue queue = mDevice->GetQueue(); | |
| if (mDevice->fn.QueuePresentKHR(queue, &presentInfo) != VK_SUCCESS) { | |
| ASSERT(false); | |
| } | |
| return DAWN_SWAP_CHAIN_NO_ERROR; | |
| } | |
| wgpu::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const { | |
| return mConfig.format; | |
| } | |
| }} // namespace dawn_native::vulkan |