Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 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: |
Austin Eng | 40dc5d3 | 2020-05-15 22:06:35 +0000 | [diff] [blame] | 26 | void SetUp() override { |
| 27 | DawnTest::SetUp(); |
Jiawei Shao | 44fc6e3 | 2021-05-26 01:04:32 +0000 | [diff] [blame] | 28 | DAWN_TEST_UNSUPPORTED_IF(UsesWire()); |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 29 | |
| 30 | glfwSetErrorCallback([](int code, const char* message) { |
| 31 | dawn::ErrorLog() << "GLFW error " << code << " " << message; |
| 32 | }); |
Corentin Wallez | 2ce1d92 | 2020-10-22 17:07:49 +0000 | [diff] [blame] | 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 | } |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 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; |
Corentin Wallez | 6b08781 | 2020-10-27 15:35:56 +0000 | [diff] [blame] | 56 | baseDescriptor.usage = wgpu::TextureUsage::RenderAttachment; |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 57 | baseDescriptor.format = wgpu::TextureFormat::BGRA8Unorm; |
| 58 | baseDescriptor.presentMode = wgpu::PresentMode::Mailbox; |
| 59 | } |
| 60 | |
| 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 | } |
Austin Eng | 40dc5d3 | 2020-05-15 22:06:35 +0000 | [diff] [blame] | 67 | DawnTest::TearDown(); |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 68 | } |
| 69 | |
| 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. |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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. |
| 138 | TEST_P(SwapChainTests, SwitchPresentMode) { |
Corentin Wallez | 25eeaa3 | 2020-10-27 11:31:26 +0000 | [diff] [blame] | 139 | // Fails with "internal drawable creation failed" on the Windows NVIDIA CQ builders but not |
| 140 | // locally. |
Jiawei Shao | 44fc6e3 | 2021-05-26 01:04:32 +0000 | [diff] [blame] | 141 | DAWN_SUPPRESS_TEST_IF(IsWindows() && IsVulkan() && IsNvidia()); |
Corentin Wallez | 2ce1d92 | 2020-10-22 17:07:49 +0000 | [diff] [blame] | 142 | |
Jiawei Shao | dad44f4 | 2020-11-19 09:19:43 +0000 | [diff] [blame] | 143 | // TODO(jiawei.shao@intel.com): find out why this test sometimes hangs on the latest Linux Intel |
| 144 | // Vulkan drivers. |
Jiawei Shao | 44fc6e3 | 2021-05-26 01:04:32 +0000 | [diff] [blame] | 145 | DAWN_SUPPRESS_TEST_IF(IsLinux() && IsVulkan() && IsIntel()); |
Jiawei Shao | dad44f4 | 2020-11-19 09:19:43 +0000 | [diff] [blame] | 146 | |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 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. |
| 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); |
Austin Eng | 80a1868 | 2020-03-23 20:10:53 +0000 | [diff] [blame] | 178 | ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f}); |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 179 | swapchain.Present(); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | // Test resizing the window but not the swapchain. |
| 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 | |
Austin Eng | 80a1868 | 2020-03-23 20:10:53 +0000 | [diff] [blame] | 191 | ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f}); |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 192 | swapchain.Present(); |
| 193 | } |
| 194 | } |
| 195 | |
| 196 | // Test resizing both the window and the swapchain at the same time. |
| 197 | TEST_P(SwapChainTests, ResizingWindowAndSwapChain) { |
| 198 | for (int i = 0; i < 10; i++) { |
| 199 | glfwSetWindowSize(window, 400 - 10 * i, 400 + 10 * i); |
| 200 | glfwPollEvents(); |
| 201 | |
| 202 | int width; |
| 203 | int height; |
| 204 | glfwGetFramebufferSize(window, &width, &height); |
| 205 | |
| 206 | wgpu::SwapChainDescriptor desc = baseDescriptor; |
| 207 | desc.width = width; |
| 208 | desc.height = height; |
| 209 | |
| 210 | wgpu::SwapChain swapchain = device.CreateSwapChain(surface, &desc); |
Austin Eng | 80a1868 | 2020-03-23 20:10:53 +0000 | [diff] [blame] | 211 | ClearTexture(swapchain.GetCurrentTextureView(), {0.05f * i, 0.0f, 0.0f, 1.0f}); |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 212 | swapchain.Present(); |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | // Test switching devices on the same adapter. |
| 217 | TEST_P(SwapChainTests, SwitchingDevice) { |
Corentin Wallez | 25eeaa3 | 2020-10-27 11:31:26 +0000 | [diff] [blame] | 218 | // The Vulkan Validation Layers incorrectly disallow gracefully passing a swapchain between two |
| 219 | // VkDevices using "vkSwapchainCreateInfoKHR::oldSwapchain". |
| 220 | // See https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/2256 |
Jiawei Shao | 44fc6e3 | 2021-05-26 01:04:32 +0000 | [diff] [blame] | 221 | DAWN_SUPPRESS_TEST_IF(IsVulkan() && IsBackendValidationEnabled()); |
Corentin Wallez | 2ce1d92 | 2020-10-22 17:07:49 +0000 | [diff] [blame] | 222 | |
| 223 | wgpu::Device device2 = wgpu::Device::Acquire(GetAdapter().CreateDevice()); |
Corentin Wallez | 11652ff | 2020-03-20 17:07:20 +0000 | [diff] [blame] | 224 | |
| 225 | for (int i = 0; i < 3; i++) { |
| 226 | wgpu::Device deviceToUse; |
| 227 | if (i % 2 == 0) { |
| 228 | deviceToUse = device; |
| 229 | } else { |
| 230 | deviceToUse = device2; |
| 231 | } |
| 232 | |
| 233 | wgpu::SwapChain swapchain = deviceToUse.CreateSwapChain(surface, &baseDescriptor); |
| 234 | swapchain.GetCurrentTextureView(); |
| 235 | swapchain.Present(); |
| 236 | } |
| 237 | } |
| 238 | |
Corentin Wallez | 2ce1d92 | 2020-10-22 17:07:49 +0000 | [diff] [blame] | 239 | DAWN_INSTANTIATE_TEST(SwapChainTests, MetalBackend(), VulkanBackend()); |