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