1 // Copyright 2018 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "dawn_native/vulkan/NativeSwapChainImplVk.h"
16 
17 #include "dawn_native/vulkan/DeviceVk.h"
18 #include "dawn_native/vulkan/FencedDeleter.h"
19 #include "dawn_native/vulkan/TextureVk.h"
20 
21 #include <limits>
22 
23 namespace dawn_native { namespace vulkan {
24 
25     namespace {
26 
chooseSwapPresentMode(const std::vector<VkPresentModeKHR> & availablePresentModes,bool turnOffVsync,VkPresentModeKHR * presentMode)27         bool chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes,
28                                    bool turnOffVsync,
29                                    VkPresentModeKHR* presentMode) {
30             if (turnOffVsync) {
31                 for (const auto& availablePresentMode : availablePresentModes) {
32                     if (availablePresentMode == VK_PRESENT_MODE_IMMEDIATE_KHR) {
33                         *presentMode = availablePresentMode;
34                         return true;
35                     }
36                 }
37                 return false;
38             }
39 
40             *presentMode = VK_PRESENT_MODE_FIFO_KHR;
41             return true;
42         }
43 
ChooseSurfaceConfig(const VulkanSurfaceInfo & info,NativeSwapChainImpl::ChosenConfig * config,bool turnOffVsync)44         bool ChooseSurfaceConfig(const VulkanSurfaceInfo& info,
45                                  NativeSwapChainImpl::ChosenConfig* config,
46                                  bool turnOffVsync) {
47             VkPresentModeKHR presentMode;
48             if (!chooseSwapPresentMode(info.presentModes, turnOffVsync, &presentMode)) {
49                 return false;
50             }
51             // TODO(cwallez@chromium.org): For now this is hardcoded to what works with one NVIDIA
52             // driver. Need to generalize
53             config->nativeFormat = VK_FORMAT_B8G8R8A8_UNORM;
54             config->colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
55             config->format = wgpu::TextureFormat::BGRA8Unorm;
56             config->minImageCount = 3;
57             // TODO(cwallez@chromium.org): This is upside down compared to what we want, at least
58             // on Linux
59             config->preTransform = info.capabilities.currentTransform;
60             config->presentMode = presentMode;
61             config->compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
62 
63             return true;
64         }
65     }  // anonymous namespace
66 
NativeSwapChainImpl(Device * device,VkSurfaceKHR surface)67     NativeSwapChainImpl::NativeSwapChainImpl(Device* device, VkSurfaceKHR surface)
68         : mSurface(surface), mDevice(device) {
69         // Call this immediately, so that BackendBinding::GetPreferredSwapChainTextureFormat
70         // will return a correct result before a SwapChain is created.
71         UpdateSurfaceConfig();
72     }
73 
~NativeSwapChainImpl()74     NativeSwapChainImpl::~NativeSwapChainImpl() {
75         if (mSwapChain != VK_NULL_HANDLE) {
76             mDevice->GetFencedDeleter()->DeleteWhenUnused(mSwapChain);
77             mSwapChain = VK_NULL_HANDLE;
78         }
79         if (mSurface != VK_NULL_HANDLE) {
80             mDevice->GetFencedDeleter()->DeleteWhenUnused(mSurface);
81             mSurface = VK_NULL_HANDLE;
82         }
83     }
84 
UpdateSurfaceConfig()85     void NativeSwapChainImpl::UpdateSurfaceConfig() {
86         if (mDevice->ConsumedError(GatherSurfaceInfo(*ToBackend(mDevice->GetAdapter()), mSurface),
87                                    &mInfo)) {
88             ASSERT(false);
89         }
90 
91         if (!ChooseSurfaceConfig(mInfo, &mConfig, mDevice->IsToggleEnabled(Toggle::TurnOffVsync))) {
92             ASSERT(false);
93         }
94     }
95 
Init(DawnWSIContextVulkan *)96     void NativeSwapChainImpl::Init(DawnWSIContextVulkan* /*context*/) {
97         UpdateSurfaceConfig();
98     }
99 
Configure(WGPUTextureFormat format,WGPUTextureUsage usage,uint32_t width,uint32_t height)100     DawnSwapChainError NativeSwapChainImpl::Configure(WGPUTextureFormat format,
101                                                       WGPUTextureUsage usage,
102                                                       uint32_t width,
103                                                       uint32_t height) {
104         UpdateSurfaceConfig();
105 
106         ASSERT(mInfo.capabilities.minImageExtent.width <= width);
107         ASSERT(mInfo.capabilities.maxImageExtent.width >= width);
108         ASSERT(mInfo.capabilities.minImageExtent.height <= height);
109         ASSERT(mInfo.capabilities.maxImageExtent.height >= height);
110 
111         ASSERT(format == static_cast<WGPUTextureFormat>(GetPreferredFormat()));
112         // TODO(cwallez@chromium.org): need to check usage works too
113 
114         // Create the swapchain with the configuration we chose
115         VkSwapchainKHR oldSwapchain = mSwapChain;
116         VkSwapchainCreateInfoKHR createInfo;
117         createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
118         createInfo.pNext = nullptr;
119         createInfo.flags = 0;
120         createInfo.surface = mSurface;
121         createInfo.minImageCount = mConfig.minImageCount;
122         createInfo.imageFormat = mConfig.nativeFormat;
123         createInfo.imageColorSpace = mConfig.colorSpace;
124         createInfo.imageExtent.width = width;
125         createInfo.imageExtent.height = height;
126         createInfo.imageArrayLayers = 1;
127         createInfo.imageUsage = VulkanImageUsage(static_cast<wgpu::TextureUsage>(usage),
128                                                  mDevice->GetValidInternalFormat(mConfig.format));
129         createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
130         createInfo.queueFamilyIndexCount = 0;
131         createInfo.pQueueFamilyIndices = nullptr;
132         createInfo.preTransform = mConfig.preTransform;
133         createInfo.compositeAlpha = mConfig.compositeAlpha;
134         createInfo.presentMode = mConfig.presentMode;
135         createInfo.clipped = false;
136         createInfo.oldSwapchain = oldSwapchain;
137 
138         if (mDevice->fn.CreateSwapchainKHR(mDevice->GetVkDevice(), &createInfo, nullptr,
139                                            &*mSwapChain) != VK_SUCCESS) {
140             ASSERT(false);
141         }
142 
143         // Gather the swapchain's images. Implementations are allowed to return more images than the
144         // number we asked for.
145         uint32_t count = 0;
146         if (mDevice->fn.GetSwapchainImagesKHR(mDevice->GetVkDevice(), mSwapChain, &count,
147                                               nullptr) != VK_SUCCESS) {
148             ASSERT(false);
149         }
150 
151         ASSERT(count >= mConfig.minImageCount);
152         mSwapChainImages.resize(count);
153         if (mDevice->fn.GetSwapchainImagesKHR(mDevice->GetVkDevice(), mSwapChain, &count,
154                                               AsVkArray(mSwapChainImages.data())) != VK_SUCCESS) {
155             ASSERT(false);
156         }
157 
158         if (oldSwapchain != VK_NULL_HANDLE) {
159             mDevice->GetFencedDeleter()->DeleteWhenUnused(oldSwapchain);
160         }
161 
162         return DAWN_SWAP_CHAIN_NO_ERROR;
163     }
164 
GetNextTexture(DawnSwapChainNextTexture * nextTexture)165     DawnSwapChainError NativeSwapChainImpl::GetNextTexture(DawnSwapChainNextTexture* nextTexture) {
166         // Transiently create a semaphore that will be signaled when the presentation engine is done
167         // with the swapchain image. Further operations on the image will wait for this semaphore.
168         VkSemaphore semaphore = VK_NULL_HANDLE;
169         {
170             VkSemaphoreCreateInfo createInfo;
171             createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
172             createInfo.pNext = nullptr;
173             createInfo.flags = 0;
174             if (mDevice->fn.CreateSemaphore(mDevice->GetVkDevice(), &createInfo, nullptr,
175                                             &*semaphore) != VK_SUCCESS) {
176                 ASSERT(false);
177             }
178         }
179 
180         if (mDevice->fn.AcquireNextImageKHR(mDevice->GetVkDevice(), mSwapChain,
181                                             std::numeric_limits<uint64_t>::max(), semaphore,
182                                             VkFence{}, &mLastImageIndex) != VK_SUCCESS) {
183             ASSERT(false);
184         }
185 
186         nextTexture->texture.u64 =
187 #if defined(DAWN_PLATFORM_64_BIT)
188             reinterpret_cast<uint64_t>
189 #endif
190             (*mSwapChainImages[mLastImageIndex]);
191         mDevice->GetPendingRecordingContext()->waitSemaphores.push_back(semaphore);
192 
193         return DAWN_SWAP_CHAIN_NO_ERROR;
194     }
195 
Present()196     DawnSwapChainError NativeSwapChainImpl::Present() {
197         // This assumes that the image has already been transitioned to the PRESENT layout and
198         // writes were made available to the stage.
199 
200         // Assuming that the present queue is the same as the graphics queue, the proper
201         // synchronization has already been done on the queue so we don't need to wait on any
202         // semaphores.
203         VkPresentInfoKHR presentInfo;
204         presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
205         presentInfo.pNext = nullptr;
206         presentInfo.waitSemaphoreCount = 0;
207         presentInfo.pWaitSemaphores = nullptr;
208         presentInfo.swapchainCount = 1;
209         presentInfo.pSwapchains = &*mSwapChain;
210         presentInfo.pImageIndices = &mLastImageIndex;
211         presentInfo.pResults = nullptr;
212 
213         VkQueue queue = mDevice->GetQueue();
214         if (mDevice->fn.QueuePresentKHR(queue, &presentInfo) != VK_SUCCESS) {
215             ASSERT(false);
216         }
217 
218         return DAWN_SWAP_CHAIN_NO_ERROR;
219     }
220 
GetPreferredFormat() const221     wgpu::TextureFormat NativeSwapChainImpl::GetPreferredFormat() const {
222         return mConfig.format;
223     }
224 
225 }}  // namespace dawn_native::vulkan
226