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