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