• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2020 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // VulkanMultithreadingTest.cpp : Tests of multithreaded rendering specific to the Vulkan back end.
7 
8 #include "test_utils/ANGLETest.h"
9 #include "test_utils/gl_raii.h"
10 #include "util/EGLWindow.h"
11 
12 #include <atomic>
13 #include <mutex>
14 #include <thread>
15 
16 #include "libANGLE/renderer/vulkan/vk_helpers.h"
17 
18 namespace angle
19 {
20 
21 constexpr char kExtensionName[] = "GL_ANGLE_get_image";
22 static constexpr int kSize      = 256;
23 
24 class VulkanMultithreadingTest : public ANGLETest<>
25 {
26   protected:
VulkanMultithreadingTest()27     VulkanMultithreadingTest()
28     {
29         setWindowWidth(kSize);
30         setWindowHeight(kSize);
31         setConfigRedBits(8);
32         setConfigGreenBits(8);
33         setConfigBlueBits(8);
34         setConfigAlphaBits(8);
35     }
36 
testSetUp()37     void testSetUp() override
38     {
39         mMaxSetsPerPool = rx::vk::DynamicDescriptorPool::GetMaxSetsPerPoolForTesting();
40         mMaxSetsPerPoolMultiplier =
41             rx::vk::DynamicDescriptorPool::GetMaxSetsPerPoolMultiplierForTesting();
42     }
43 
testTearDown()44     void testTearDown() override
45     {
46         rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolForTesting(mMaxSetsPerPool);
47         rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolMultiplierForTesting(
48             mMaxSetsPerPoolMultiplier);
49     }
50 
51     static constexpr uint32_t kMaxSetsForTesting           = 1;
52     static constexpr uint32_t kMaxSetsMultiplierForTesting = 1;
53 
limitMaxSets()54     void limitMaxSets()
55     {
56         rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolForTesting(kMaxSetsForTesting);
57         rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolMultiplierForTesting(
58             kMaxSetsMultiplierForTesting);
59     }
60 
runMultithreadedGLTest(std::function<void (EGLSurface surface,size_t threadIndex)> testBody,size_t threadCount)61     void runMultithreadedGLTest(
62         std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
63         size_t threadCount)
64     {
65         std::mutex mutex;
66 
67         EGLWindow *window = getEGLWindow();
68         EGLDisplay dpy    = window->getDisplay();
69         EGLConfig config  = window->getConfig();
70 
71         constexpr EGLint kPBufferSize = kSize;
72 
73         std::vector<std::thread> threads(threadCount);
74         for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
75         {
76             threads[threadIdx] = std::thread([&, threadIdx]() {
77                 EGLSurface surface = EGL_NO_SURFACE;
78                 EGLContext ctx     = EGL_NO_CONTEXT;
79 
80                 {
81                     std::lock_guard<decltype(mutex)> lock(mutex);
82 
83                     // Initialize the pbuffer and context
84                     EGLint pbufferAttributes[] = {
85                         EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
86                     };
87                     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
88                     EXPECT_EGL_SUCCESS();
89 
90                     ctx = window->createContext(EGL_NO_CONTEXT, nullptr);
91                     EXPECT_NE(EGL_NO_CONTEXT, ctx);
92 
93                     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
94                     EXPECT_EGL_SUCCESS();
95                 }
96 
97                 testBody(surface, threadIdx);
98 
99                 {
100                     std::lock_guard<decltype(mutex)> lock(mutex);
101 
102                     // Clean up
103                     EXPECT_EGL_TRUE(
104                         eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
105                     EXPECT_EGL_SUCCESS();
106 
107                     eglDestroySurface(dpy, surface);
108                     eglDestroyContext(dpy, ctx);
109                 }
110             });
111         }
112 
113         for (std::thread &thread : threads)
114         {
115             thread.join();
116         }
117     }
118 
119   private:
120     uint32_t mMaxSetsPerPool;
121     uint32_t mMaxSetsPerPoolMultiplier;
122 };
123 
124 // Test that multiple threads can draw and readback pixels successfully at the same time with small
125 // descriptor pools.
TEST_P(VulkanMultithreadingTest,MultiContextDrawSmallDescriptorPools)126 TEST_P(VulkanMultithreadingTest, MultiContextDrawSmallDescriptorPools)
127 {
128     // TODO(http://anglebug.com/6633: Flaky on linux.
129     ANGLE_SKIP_TEST_IF(IsLinux());
130     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
131     // Verify the extension is enabled.
132     ASSERT_TRUE(IsGLExtensionEnabled(kExtensionName));
133 
134     // Must be before program creation to limit the descriptor pool sizes when creating the pipeline
135     // layout.
136     limitMaxSets();
137 
138     auto testBody = [](EGLSurface surface, size_t thread) {
139         constexpr size_t kIterationsPerThread = 16;
140         constexpr size_t kDrawsPerIteration   = 16;
141 
142         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
143         glUseProgram(program);
144 
145         GLTexture copyTexture;
146         glBindTexture(GL_TEXTURE_2D, copyTexture);
147         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
148                      nullptr);
149         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
150         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
151         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
152         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
153         ASSERT_GL_NO_ERROR();
154 
155         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
156 
157         auto quadVertices = GetQuadVertices();
158 
159         GLBuffer vertexBuffer;
160         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
161         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
162 
163         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
164         glEnableVertexAttribArray(positionLocation);
165         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
166 
167         // Pack pixels tightly.
168         glPixelStorei(GL_PACK_ALIGNMENT, 1);
169 
170         std::vector<GLColor> actualData(kSize * kSize);
171 
172         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
173         {
174             // Base the clear color on the thread and iteration indexes so every clear color is
175             // unique
176             const GLColor color(static_cast<GLubyte>(thread % 255),
177                                 static_cast<GLubyte>(iteration % 255), 0, 255);
178             const angle::Vector4 floatColor = color.toNormalizedVector();
179             glUniform4fv(colorLocation, 1, floatColor.data());
180 
181             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
182             {
183                 glDrawArrays(GL_TRIANGLES, 0, 6);
184                 ASSERT_GL_NO_ERROR();
185 
186                 // Perform CopyTexImage2D
187                 glBindTexture(GL_TEXTURE_2D, copyTexture);
188                 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kSize, kSize, 0);
189                 ASSERT_GL_NO_ERROR();
190             }
191 
192             // There's a good chance this test will crash before failing, but if not we'll try and
193             // verify the contents of the copied texture.
194             // TODO(http://anglebug.com/5204): Need to re-enable for Linux/Windows.
195             if (IsGLExtensionEnabled(kExtensionName) && !(IsLinux() || IsWindows()))
196             {
197                 // Verify glCopyTexImage2D() was successful.
198                 glGetTexImageANGLE(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, actualData.data());
199                 EXPECT_GL_NO_ERROR();
200                 EXPECT_EQ(color, actualData[0]);
201             }
202         }
203     };
204     runMultithreadedGLTest(testBody, 4);
205 }
206 
207 ANGLE_INSTANTIATE_TEST(VulkanMultithreadingTest, ES2_VULKAN(), ES3_VULKAN());
208 
209 }  // namespace angle
210