1 // Copyright 2020 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 "tests/DawnTest.h"
16
17 #include "common/Constants.h"
18 #include "common/Log.h"
19 #include "utils/GLFWUtils.h"
20 #include "utils/WGPUHelpers.h"
21
22 #include "GLFW/glfw3.h"
23
24 class SwapChainTests : public DawnTest {
25 public:
SetUp()26 void SetUp() override {
27 DawnTest::SetUp();
28 DAWN_TEST_UNSUPPORTED_IF(UsesWire());
29
30 glfwSetErrorCallback([](int code, const char* message) {
31 dawn::ErrorLog() << "GLFW error " << code << " " << message;
32 });
33
34 // GLFW can fail to start in headless environments, in which SwapChainTests are
35 // inapplicable. Skip this cases without producing a test failure.
36 if (glfwInit() == GLFW_FALSE) {
37 GTEST_SKIP();
38 }
39
40 // The SwapChainTests don't create OpenGL contexts so we don't need to call
41 // SetupGLFWWindowHintsForBackend. Set GLFW_NO_API anyway to avoid GLFW bringing up a GL
42 // context that we won't use.
43 ASSERT_TRUE(!IsOpenGL());
44 glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
45 window = glfwCreateWindow(400, 400, "SwapChainValidationTests window", nullptr, nullptr);
46
47 int width;
48 int height;
49 glfwGetFramebufferSize(window, &width, &height);
50
51 surface = utils::CreateSurfaceForWindow(GetInstance(), window);
52 ASSERT_NE(surface, nullptr);
53
54 baseDescriptor.width = width;
55 baseDescriptor.height = height;
56 baseDescriptor.usage = wgpu::TextureUsage::RenderAttachment;
57 baseDescriptor.format = wgpu::TextureFormat::BGRA8Unorm;
58 baseDescriptor.presentMode = wgpu::PresentMode::Mailbox;
59 }
60
TearDown()61 void TearDown() override {
62 // Destroy the surface before the window as required by webgpu-native.
63 surface = wgpu::Surface();
64 if (window != nullptr) {
65 glfwDestroyWindow(window);
66 }
67 DawnTest::TearDown();
68 }
69
ClearTexture(wgpu::TextureView view,wgpu::Color color)70 void ClearTexture(wgpu::TextureView view, wgpu::Color color) {
71 utils::ComboRenderPassDescriptor desc({view});
72 desc.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
73 desc.cColorAttachments[0].clearColor = color;
74
75 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
76 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&desc);
77 pass.EndPass();
78
79 wgpu::CommandBuffer commands = encoder.Finish();
80 queue.Submit(1, &commands);
81 }
82
83 protected:
84 GLFWwindow* window = nullptr;
85 wgpu::Surface surface;
86
87 wgpu::SwapChainDescriptor baseDescriptor;
88 };
89
90 // Basic test for creating a swapchain and presenting one frame.
TEST_P(SwapChainTests,Basic)91 TEST_P(SwapChainTests, Basic) {
92 wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor);
93 ClearTexture(swapchain.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0});
94 swapchain.Present();
95 }
96
97 // Test replacing the swapchain
TEST_P(SwapChainTests,ReplaceBasic)98 TEST_P(SwapChainTests, ReplaceBasic) {
99 wgpu::SwapChain swapchain1 = device.CreateSwapChain(surface, &baseDescriptor);
100 ClearTexture(swapchain1.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0});
101 swapchain1.Present();
102
103 wgpu::SwapChain swapchain2 = device.CreateSwapChain(surface, &baseDescriptor);
104 ClearTexture(swapchain2.GetCurrentTextureView(), {0.0, 1.0, 0.0, 1.0});
105 swapchain2.Present();
106 }
107
108 // Test replacing the swapchain after GetCurrentTextureView
TEST_P(SwapChainTests,ReplaceAfterGet)109 TEST_P(SwapChainTests, ReplaceAfterGet) {
110 wgpu::SwapChain swapchain1 = device.CreateSwapChain(surface, &baseDescriptor);
111 ClearTexture(swapchain1.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0});
112
113 wgpu::SwapChain swapchain2 = device.CreateSwapChain(surface, &baseDescriptor);
114 ClearTexture(swapchain2.GetCurrentTextureView(), {0.0, 1.0, 0.0, 1.0});
115 swapchain2.Present();
116 }
117
118 // Test destroying the swapchain after GetCurrentTextureView
TEST_P(SwapChainTests,DestroyAfterGet)119 TEST_P(SwapChainTests, DestroyAfterGet) {
120 wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor);
121 ClearTexture(swapchain.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0});
122 }
123
124 // Test destroying the surface before the swapchain
TEST_P(SwapChainTests,DestroySurface)125 TEST_P(SwapChainTests, DestroySurface) {
126 wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor);
127 surface = nullptr;
128 }
129
130 // Test destroying the surface before the swapchain but after GetCurrentTextureView
TEST_P(SwapChainTests,DestroySurfaceAfterGet)131 TEST_P(SwapChainTests, DestroySurfaceAfterGet) {
132 wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor);
133 ClearTexture(swapchain.GetCurrentTextureView(), {1.0, 0.0, 0.0, 1.0});
134 surface = nullptr;
135 }
136
137 // Test switching between present modes.
TEST_P(SwapChainTests,SwitchPresentMode)138 TEST_P(SwapChainTests, SwitchPresentMode) {
139 // Fails with "internal drawable creation failed" on the Windows NVIDIA CQ builders but not
140 // locally.
141 DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsNvidia());
142
143 // TODO(jiawei.shao@intel.com): find out why this test sometimes hangs on the latest Linux Intel
144 // Vulkan drivers.
145 DAWN_SUPPRESS_TEST_IF(IsLinux() && IsVulkan() && IsIntel());
146
147 constexpr wgpu::PresentMode kAllPresentModes[] = {
148 wgpu::PresentMode::Immediate,
149 wgpu::PresentMode::Fifo,
150 wgpu::PresentMode::Mailbox,
151 };
152
153 for (wgpu::PresentMode mode1 : kAllPresentModes) {
154 for (wgpu::PresentMode mode2 : kAllPresentModes) {
155 wgpu::SwapChainDescriptor desc = baseDescriptor;
156
157 desc.presentMode = mode1;
158 wgpu::SwapChain swapchain1 = device.CreateSwapChain(surface, &desc);
159 ClearTexture(swapchain1.GetCurrentTextureView(), {0.0, 0.0, 0.0, 1.0});
160 swapchain1.Present();
161
162 desc.presentMode = mode2;
163 wgpu::SwapChain swapchain2 = device.CreateSwapChain(surface, &desc);
164 ClearTexture(swapchain2.GetCurrentTextureView(), {0.0, 0.0, 0.0, 1.0});
165 swapchain2.Present();
166 }
167 }
168 }
169
170 // Test resizing the swapchain and without resizing the window.
TEST_P(SwapChainTests,ResizingSwapChainOnly)171 TEST_P(SwapChainTests, ResizingSwapChainOnly) {
172 for (int i = 0; i < 10; i++) {
173 wgpu::SwapChainDescriptor desc = baseDescriptor;
174 desc.width += i * 10;
175 desc.height -= i * 10;
176
177 wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &desc);
178 ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f});
179 swapchain.Present();
180 }
181 }
182
183 // Test resizing the window but not the swapchain.
TEST_P(SwapChainTests,ResizingWindowOnly)184 TEST_P(SwapChainTests, ResizingWindowOnly) {
185 wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &baseDescriptor);
186
187 for (int i = 0; i < 10; i++) {
188 glfwSetWindowSize(window, 400 - 10 * i, 400 + 10 * i);
189 glfwPollEvents();
190
191 ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f});
192 swapchain.Present();
193 }
194 }
195
196 // Test resizing both the window and the swapchain at the same time.
TEST_P(SwapChainTests,ResizingWindowAndSwapChain)197 TEST_P(SwapChainTests, ResizingWindowAndSwapChain) {
198 // TODO(crbug.com/dawn/1205) Currently failing on new NVIDIA GTX 1660s on Linux/Vulkan.
199 DAWN_SUPPRESS_TEST_IF(IsLinux() && IsVulkan() && IsNvidia());
200 for (int i = 0; i < 10; i++) {
201 glfwSetWindowSize(window, 400 - 10 * i, 400 + 10 * i);
202 glfwPollEvents();
203
204 int width;
205 int height;
206 glfwGetFramebufferSize(window, &width, &height);
207
208 wgpu::SwapChainDescriptor desc = baseDescriptor;
209 desc.width = width;
210 desc.height = height;
211
212 wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &desc);
213 ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f});
214 swapchain.Present();
215 }
216 }
217
218 // Test switching devices on the same adapter.
TEST_P(SwapChainTests,SwitchingDevice)219 TEST_P(SwapChainTests, SwitchingDevice) {
220 // The Vulkan Validation Layers incorrectly disallow gracefully passing a swapchain between two
221 // VkDevices using "vkSwapchainCreateInfoKHR::oldSwapchain".
222 // See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2256
223 DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsBackendValidationEnabled());
224
225 wgpu::Device device2 = wgpu::Device::Acquire(GetAdapter().CreateDevice());
226
227 for (int i = 0; i < 3; i++) {
228 wgpu::Device deviceToUse;
229 if (i % 2 == 0) {
230 deviceToUse = device;
231 } else {
232 deviceToUse = device2;
233 }
234
235 wgpu::SwapChain swapchain = deviceToUse.CreateSwapChain(surface, &baseDescriptor);
236 swapchain.GetCurrentTextureView();
237 swapchain.Present();
238 }
239 }
240
241 DAWN_INSTANTIATE_TEST(SwapChainTests, MetalBackend(), VulkanBackend());
242