1 //
2 // Copyright 2018 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 // VulkanUniformUpdatesTest:
7 // Tests to validate our Vulkan dynamic uniform updates are working as expected.
8 //
9
10 #include "test_utils/ANGLETest.h"
11 #include "test_utils/angle_test_instantiate.h"
12 // 'None' is defined as 'struct None {};' in
13 // third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h.
14 // But 'None' is also defined as a numeric constant 0L in <X11/X.h>.
15 // So we need to include ANGLETest.h first to avoid this conflict.
16
17 #include "libANGLE/Context.h"
18 #include "libANGLE/Display.h"
19 #include "libANGLE/angletypes.h"
20 #include "libANGLE/renderer/vulkan/ContextVk.h"
21 #include "libANGLE/renderer/vulkan/ProgramVk.h"
22 #include "libANGLE/renderer/vulkan/TextureVk.h"
23 #include "test_utils/gl_raii.h"
24 #include "util/EGLWindow.h"
25 #include "util/random_utils.h"
26 #include "util/shader_utils.h"
27
28 using namespace angle;
29
30 namespace
31 {
32
33 class VulkanUniformUpdatesTest : public ANGLETest<>
34 {
35 protected:
VulkanUniformUpdatesTest()36 VulkanUniformUpdatesTest() : mLastContext(nullptr) {}
37
testSetUp()38 virtual void testSetUp() override
39 {
40 // Some of the tests bellow forces uniform buffer size to 128 bytes which may affect other
41 // tests. This is to ensure that the assumption that each TEST_P will recreate context.
42 ASSERT(mLastContext != getEGLWindow()->getContext());
43 mLastContext = getEGLWindow()->getContext();
44
45 mMaxSetsPerPool = rx::vk::DynamicDescriptorPool::GetMaxSetsPerPoolForTesting();
46 mMaxSetsPerPoolMultiplier =
47 rx::vk::DynamicDescriptorPool::GetMaxSetsPerPoolMultiplierForTesting();
48 }
49
testTearDown()50 void testTearDown() override
51 {
52 rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolForTesting(mMaxSetsPerPool);
53 rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolMultiplierForTesting(
54 mMaxSetsPerPoolMultiplier);
55 }
56
hackContext() const57 gl::Context *hackContext() const
58 {
59 egl::Display *display = static_cast<egl::Display *>(getEGLWindow()->getDisplay());
60 gl::ContextID contextID = {
61 static_cast<GLuint>(reinterpret_cast<uintptr_t>(getEGLWindow()->getContext()))};
62 return display->getContext(contextID);
63 }
64
hackANGLE() const65 rx::ContextVk *hackANGLE() const
66 {
67 // Hack the angle!
68 return rx::GetImplAs<rx::ContextVk>(hackContext());
69 }
70
hackTexture(GLuint handle) const71 rx::TextureVk *hackTexture(GLuint handle) const
72 {
73 // Hack the angle!
74 const gl::Context *context = hackContext();
75 const gl::Texture *texture = context->getTexture({handle});
76 return rx::vk::GetImpl(texture);
77 }
78
79 static constexpr uint32_t kMaxSetsForTesting = 1;
80 static constexpr uint32_t kMaxSetsMultiplierForTesting = 1;
81
limitMaxSets()82 void limitMaxSets()
83 {
84 rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolForTesting(kMaxSetsForTesting);
85 rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolMultiplierForTesting(
86 kMaxSetsMultiplierForTesting);
87 }
88
setExplicitMaxSetsLimit(uint32_t limit)89 void setExplicitMaxSetsLimit(uint32_t limit)
90 {
91 rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolForTesting(limit);
92 rx::vk::DynamicDescriptorPool::SetMaxSetsPerPoolMultiplierForTesting(
93 kMaxSetsMultiplierForTesting);
94 }
95
96 private:
97 EGLContext mLastContext;
98 uint32_t mMaxSetsPerPool;
99 uint32_t mMaxSetsPerPoolMultiplier;
100 };
101
102 // This test updates a uniform until a new buffer is allocated and then make sure the uniform
103 // updates still work.
TEST_P(VulkanUniformUpdatesTest,UpdateUntilNewBufferIsAllocated)104 TEST_P(VulkanUniformUpdatesTest, UpdateUntilNewBufferIsAllocated)
105 {
106 ASSERT_TRUE(IsVulkan());
107
108 constexpr char kPositionUniformVertexShader[] = R"(attribute vec2 position;
109 uniform vec2 uniPosModifier;
110 void main()
111 {
112 gl_Position = vec4(position + uniPosModifier, 0, 1);
113 })";
114
115 constexpr char kColorUniformFragmentShader[] = R"(precision mediump float;
116 uniform vec4 uniColor;
117 void main()
118 {
119 gl_FragColor = uniColor;
120 })";
121
122 ANGLE_GL_PROGRAM(program, kPositionUniformVertexShader, kColorUniformFragmentShader);
123 glUseProgram(program);
124
125 limitMaxSets();
126
127 // Set a really small min size so that uniform updates often allocates a new buffer.
128 rx::ContextVk *contextVk = hackANGLE();
129 contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
130
131 GLint posUniformLocation = glGetUniformLocation(program, "uniPosModifier");
132 ASSERT_NE(posUniformLocation, -1);
133 GLint colorUniformLocation = glGetUniformLocation(program, "uniColor");
134 ASSERT_NE(colorUniformLocation, -1);
135
136 // Sets both uniforms 10 times, it should certainly trigger new buffers creations by the
137 // underlying StreamingBuffer.
138 for (int i = 0; i < 100; i++)
139 {
140 glUniform2f(posUniformLocation, -0.5, 0.0);
141 glUniform4f(colorUniformLocation, 1.0, 0.0, 0.0, 1.0);
142 drawQuad(program, "position", 0.5f, 1.0f);
143 swapBuffers();
144 ASSERT_GL_NO_ERROR();
145 }
146 }
147
InitTexture(GLColor color,GLTexture * texture)148 void InitTexture(GLColor color, GLTexture *texture)
149 {
150 const std::vector<GLColor> colors(4, color);
151 glBindTexture(GL_TEXTURE_2D, *texture);
152 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
153 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
155 }
156
157 // Force uniform updates until the dynamic descriptor pool wraps into a new pool allocation.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUpdates)158 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUpdates)
159 {
160 ASSERT_TRUE(IsVulkan());
161
162 // Initialize texture program.
163 GLuint program = get2DTexturedQuadProgram();
164 ASSERT_NE(0u, program);
165 glUseProgram(program);
166
167 // Force a small limit on the max sets per pool to more easily trigger a new allocation.
168 limitMaxSets();
169
170 GLint texLoc = glGetUniformLocation(program, "tex");
171 ASSERT_NE(-1, texLoc);
172
173 // Initialize basic red texture.
174 const std::vector<GLColor> redColors(4, GLColor::red);
175 GLTexture texture;
176 glBindTexture(GL_TEXTURE_2D, texture);
177 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, redColors.data());
178 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
179 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
180 ASSERT_GL_NO_ERROR();
181
182 // Draw multiple times, each iteration will create a new descriptor set.
183 for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 8; ++iteration)
184 {
185 glUniform1i(texLoc, 0);
186 drawQuad(program, "position", 0.5f, 1.0f, true);
187 swapBuffers();
188 ASSERT_GL_NO_ERROR();
189 }
190 }
191
192 // Uniform updates along with Texture updates.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUniformAndTextureUpdates)193 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdates)
194 {
195 ASSERT_TRUE(IsVulkan());
196
197 // Initialize texture program.
198 constexpr char kFS[] = R"(varying mediump vec2 v_texCoord;
199 uniform sampler2D tex;
200 uniform mediump vec4 colorMask;
201 void main()
202 {
203 gl_FragColor = texture2D(tex, v_texCoord) * colorMask;
204 })";
205
206 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
207 glUseProgram(program);
208
209 limitMaxSets();
210
211 // Get uniform locations.
212 GLint texLoc = glGetUniformLocation(program, "tex");
213 ASSERT_NE(-1, texLoc);
214
215 GLint colorMaskLoc = glGetUniformLocation(program, "colorMask");
216 ASSERT_NE(-1, colorMaskLoc);
217
218 // Initialize white texture.
219 GLTexture whiteTexture;
220 InitTexture(GLColor::white, &whiteTexture);
221 ASSERT_GL_NO_ERROR();
222
223 glActiveTexture(GL_TEXTURE0);
224 glBindTexture(GL_TEXTURE_2D, whiteTexture);
225
226 // Initialize magenta texture.
227 GLTexture magentaTexture;
228 InitTexture(GLColor::magenta, &magentaTexture);
229 ASSERT_GL_NO_ERROR();
230
231 glActiveTexture(GL_TEXTURE1);
232 glBindTexture(GL_TEXTURE_2D, magentaTexture);
233
234 // Draw multiple times, each iteration will create a new descriptor set.
235 for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
236 {
237 // Draw with white.
238 glUniform1i(texLoc, 0);
239 glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
240 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
241
242 // Draw with white masking out red.
243 glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
244 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
245
246 // Draw with magenta.
247 glUniform1i(texLoc, 1);
248 glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
249 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
250
251 // Draw with magenta masking out red.
252 glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
253 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
254
255 swapBuffers();
256 ASSERT_GL_NO_ERROR();
257 }
258 }
259
260 // Uniform updates along with Texture regeneration.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUniformAndTextureRegeneration)261 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureRegeneration)
262 {
263 ASSERT_TRUE(IsVulkan());
264
265 // Initialize texture program.
266 constexpr char kFS[] = R"(varying mediump vec2 v_texCoord;
267 uniform sampler2D tex;
268 uniform mediump vec4 colorMask;
269 void main()
270 {
271 gl_FragColor = texture2D(tex, v_texCoord) * colorMask;
272 })";
273
274 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
275 glUseProgram(program);
276
277 limitMaxSets();
278
279 // Initialize large arrays of textures.
280 std::vector<GLTexture> whiteTextures;
281 std::vector<GLTexture> magentaTextures;
282
283 for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
284 {
285 // Initialize white texture.
286 GLTexture whiteTexture;
287 InitTexture(GLColor::white, &whiteTexture);
288 ASSERT_GL_NO_ERROR();
289 whiteTextures.emplace_back(std::move(whiteTexture));
290
291 // Initialize magenta texture.
292 GLTexture magentaTexture;
293 InitTexture(GLColor::magenta, &magentaTexture);
294 ASSERT_GL_NO_ERROR();
295 magentaTextures.emplace_back(std::move(magentaTexture));
296 }
297
298 // Get uniform locations.
299 GLint texLoc = glGetUniformLocation(program, "tex");
300 ASSERT_NE(-1, texLoc);
301
302 GLint colorMaskLoc = glGetUniformLocation(program, "colorMask");
303 ASSERT_NE(-1, colorMaskLoc);
304
305 // Draw multiple times, each iteration will create a new descriptor set.
306 for (int outerIteration = 0; outerIteration < 2; ++outerIteration)
307 {
308 for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
309 {
310 glActiveTexture(GL_TEXTURE0);
311 glBindTexture(GL_TEXTURE_2D, whiteTextures[iteration]);
312
313 glActiveTexture(GL_TEXTURE1);
314 glBindTexture(GL_TEXTURE_2D, magentaTextures[iteration]);
315
316 // Draw with white.
317 glUniform1i(texLoc, 0);
318 glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
319 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
320
321 // Draw with white masking out red.
322 glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
323 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
324
325 // Draw with magenta.
326 glUniform1i(texLoc, 1);
327 glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
328 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
329
330 // Draw with magenta masking out red.
331 glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
332 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
333
334 swapBuffers();
335 ASSERT_GL_NO_ERROR();
336 }
337 }
338 }
339
340 // Uniform updates along with Texture updates.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUniformAndTextureUpdatesTwoShaders)341 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdatesTwoShaders)
342 {
343 ASSERT_TRUE(IsVulkan());
344
345 // Initialize program.
346 constexpr char kVS[] = R"(attribute vec2 position;
347 varying mediump vec2 texCoord;
348 void main()
349 {
350 gl_Position = vec4(position, 0, 1);
351 texCoord = position * 0.5 + vec2(0.5);
352 })";
353
354 constexpr char kFS[] = R"(varying mediump vec2 texCoord;
355 uniform mediump vec4 colorMask;
356 void main()
357 {
358 gl_FragColor = colorMask;
359 })";
360
361 ANGLE_GL_PROGRAM(program1, kVS, kFS);
362 ANGLE_GL_PROGRAM(program2, kVS, kFS);
363 glUseProgram(program1);
364
365 // Force a small limit on the max sets per pool to more easily trigger a new allocation.
366 limitMaxSets();
367 limitMaxSets();
368
369 // Set a really small min size so that uniform updates often allocates a new buffer.
370 rx::ContextVk *contextVk = hackANGLE();
371 contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
372
373 // Get uniform locations.
374 GLint colorMaskLoc1 = glGetUniformLocation(program1, "colorMask");
375 ASSERT_NE(-1, colorMaskLoc1);
376 GLint colorMaskLoc2 = glGetUniformLocation(program2, "colorMask");
377 ASSERT_NE(-1, colorMaskLoc2);
378
379 // Draw with white using program1.
380 glUniform4f(colorMaskLoc1, 1.0f, 1.0f, 1.0f, 1.0f);
381 drawQuad(program1, "position", 0.5f, 1.0f, true);
382 swapBuffers();
383 ASSERT_GL_NO_ERROR();
384
385 // Now switch to use program2
386 glUseProgram(program2);
387 // Draw multiple times w/ program2, each iteration will create a new descriptor set.
388 // This will cause the first descriptor pool to be cleaned up
389 for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
390 {
391 // Draw with white.
392 glUniform4f(colorMaskLoc2, 1.0f, 1.0f, 1.0f, 1.0f);
393 drawQuad(program2, "position", 0.5f, 1.0f, true);
394
395 // Draw with white masking out red.
396 glUniform4f(colorMaskLoc2, 0.0f, 1.0f, 1.0f, 1.0f);
397 drawQuad(program2, "position", 0.5f, 1.0f, true);
398
399 // Draw with magenta.
400 glUniform4f(colorMaskLoc2, 1.0f, 1.0f, 1.0f, 1.0f);
401 drawQuad(program2, "position", 0.5f, 1.0f, true);
402
403 // Draw with magenta masking out red.
404 glUniform4f(colorMaskLoc2, 0.0f, 1.0f, 1.0f, 1.0f);
405 drawQuad(program2, "position", 0.5f, 1.0f, true);
406
407 swapBuffers();
408 ASSERT_GL_NO_ERROR();
409 }
410 // Finally, attempt to draw again with program1, with original uniform values.
411 glUseProgram(program1);
412 drawQuad(program1, "position", 0.5f, 1.0f, true);
413 swapBuffers();
414 ASSERT_GL_NO_ERROR();
415 }
416
417 // Verify that overflowing a Texture's staging buffer doesn't overwrite current data.
TEST_P(VulkanUniformUpdatesTest,TextureStagingBufferRecycling)418 TEST_P(VulkanUniformUpdatesTest, TextureStagingBufferRecycling)
419 {
420 ASSERT_TRUE(IsVulkan());
421
422 // Fails on older MESA drivers. http://crbug.com/979349
423 ANGLE_SKIP_TEST_IF(IsAMD() && IsLinux());
424
425 GLTexture tex;
426 glBindTexture(GL_TEXTURE_2D, tex);
427 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
428 GL_UNSIGNED_BYTE, nullptr);
429 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
430 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
431
432 const GLColor kColors[4] = {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow};
433
434 // Repeatedly update the staging buffer to trigger multiple recyclings.
435 const GLsizei kHalfX = getWindowWidth() / 2;
436 const GLsizei kHalfY = getWindowHeight() / 2;
437 constexpr int kIterations = 4;
438 for (int x = 0; x < 2; ++x)
439 {
440 for (int y = 0; y < 2; ++y)
441 {
442 const int kColorIndex = x + y * 2;
443 const GLColor kColor = kColors[kColorIndex];
444
445 for (int iteration = 0; iteration < kIterations; ++iteration)
446 {
447 for (int subX = 0; subX < kHalfX; ++subX)
448 {
449 for (int subY = 0; subY < kHalfY; ++subY)
450 {
451 const GLsizei xoffset = x * kHalfX + subX;
452 const GLsizei yoffset = y * kHalfY + subY;
453
454 // Update a single pixel.
455 glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, 1, 1, GL_RGBA,
456 GL_UNSIGNED_BYTE, kColor.data());
457 }
458 }
459 }
460 }
461 }
462
463 draw2DTexturedQuad(0.5f, 1.0f, true);
464 ASSERT_GL_NO_ERROR();
465
466 // Verify pixels.
467 for (int x = 0; x < 2; ++x)
468 {
469 for (int y = 0; y < 2; ++y)
470 {
471 const GLsizei xoffset = x * kHalfX;
472 const GLsizei yoffset = y * kHalfY;
473 const int kColorIndex = x + y * 2;
474 const GLColor kColor = kColors[kColorIndex];
475 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, kColor);
476 }
477 }
478 }
479
480 // This test tries to create a situation that VS and FS's uniform data might get placed in
481 // different buffers and verify uniforms not getting stale data.
TEST_P(VulkanUniformUpdatesTest,UpdateAfterNewBufferIsAllocated)482 TEST_P(VulkanUniformUpdatesTest, UpdateAfterNewBufferIsAllocated)
483 {
484 ASSERT_TRUE(IsVulkan());
485
486 constexpr char kPositionUniformVertexShader[] = R"(attribute vec2 position;
487 uniform float uniformVS;
488 varying vec4 outVS;
489 void main()
490 {
491 outVS = vec4(uniformVS, uniformVS, uniformVS, uniformVS);
492 gl_Position = vec4(position, 0, 1);
493 })";
494
495 constexpr char kColorUniformFragmentShader[] = R"(precision mediump float;
496 varying vec4 outVS;
497 uniform float uniformFS;
498 void main()
499 {
500 if(outVS[0] > uniformFS)
501 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
502 else
503 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
504 })";
505
506 ANGLE_GL_PROGRAM(program, kPositionUniformVertexShader, kColorUniformFragmentShader);
507 glUseProgram(program);
508
509 limitMaxSets();
510
511 // Set a really small min size so that every uniform update actually allocates a new buffer.
512 rx::ContextVk *contextVk = hackANGLE();
513 contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
514
515 GLint uniformVSLocation = glGetUniformLocation(program, "uniformVS");
516 ASSERT_NE(uniformVSLocation, -1);
517 GLint uniformFSLocation = glGetUniformLocation(program, "uniformFS");
518 ASSERT_NE(uniformFSLocation, -1);
519
520 glUniform1f(uniformVSLocation, 10.0);
521 glUniform1f(uniformFSLocation, 11.0);
522 drawQuad(program, "position", 0.5f, 1.0f);
523 ASSERT_GL_NO_ERROR();
524
525 const GLsizei kHalfX = getWindowWidth() / 2;
526 const GLsizei kHalfY = getWindowHeight() / 2;
527 const GLsizei xoffset = kHalfX;
528 const GLsizei yoffset = kHalfY;
529 // 10.0f < 11.0f, should see green
530 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
531
532 // Now only update FS's uniform
533 for (int i = 0; i < 3; i++)
534 {
535 glUniform1f(uniformFSLocation, 1.0f + i / 10.0f);
536 drawQuad(program, "position", 0.5f, 1.0f);
537 ASSERT_GL_NO_ERROR();
538 }
539 // 10.0f > 9.0f, should see red
540 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::red);
541
542 // 10.0f < 11.0f, should see green again
543 glUniform1f(uniformFSLocation, 11.0f);
544 drawQuad(program, "position", 0.5f, 1.0f);
545 ASSERT_GL_NO_ERROR();
546 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
547
548 // Now only update VS's uniform and flush the draw and readback and verify for every iteration.
549 // This will ensure the old buffers are finished and possibly recycled.
550 for (int i = 0; i < 100; i++)
551 {
552 // Make VS uniform value ping pong across FS uniform value
553 float vsUniformValue = (i % 2) == 0 ? (11.0 + (i - 50)) : (11.0 - (i - 50));
554 GLColor expectedColor = vsUniformValue > 11.0f ? GLColor::red : GLColor::green;
555 glUniform1f(uniformVSLocation, vsUniformValue);
556 drawQuad(program, "position", 0.5f, 1.0f);
557 ASSERT_GL_NO_ERROR();
558 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, expectedColor);
559 }
560 }
561
562 // Covers a usage pattern where two programs share a descriptor pool.
TEST_P(VulkanUniformUpdatesTest,MultipleProgramsShareDescriptors)563 TEST_P(VulkanUniformUpdatesTest, MultipleProgramsShareDescriptors)
564 {
565 setExplicitMaxSetsLimit(2);
566
567 // Set a min size so uniform updates allocate a new buffer every 2nd time.
568 rx::ContextVk *contextVk = hackANGLE();
569 contextVk->setDefaultUniformBlocksMinSizeForTesting(512);
570
571 static constexpr size_t kNumPrograms = 2;
572 static constexpr size_t kDrawIterations = 4;
573 static constexpr GLint kPosLoc = 0;
574
575 std::array<GLuint, kNumPrograms> programs = {};
576
577 for (GLuint &program : programs)
578 {
579 auto preLinkCallback = [](GLuint program) {
580 glBindAttribLocation(program, kPosLoc, essl1_shaders::PositionAttrib());
581 };
582
583 program = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor(),
584 preLinkCallback);
585 ASSERT_NE(program, 0u);
586 }
587
588 const std::array<Vector3, 6> &quadVerts = GetQuadVertices();
589
590 GLBuffer vbo;
591 glBindBuffer(GL_ARRAY_BUFFER, vbo);
592 glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(),
593 GL_STATIC_DRAW);
594
595 glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
596 glEnableVertexAttribArray(kPosLoc);
597
598 ASSERT_GL_NO_ERROR();
599
600 RNG rng;
601
602 for (size_t drawIteration = 0; drawIteration < kDrawIterations; ++drawIteration)
603 {
604 for (GLuint program : programs)
605 {
606 Vector3 randVec = RandomVec3(rng.randomInt(), 0.0f, 1.0f);
607
608 glUseProgram(program);
609 glUniform4f(0, randVec.x(), randVec.y(), randVec.z(), 1.0f);
610 glDrawArrays(GL_TRIANGLES, 0, 6);
611 EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(randVec), 5);
612 }
613 }
614
615 ASSERT_GL_NO_ERROR();
616
617 for (GLuint &program : programs)
618 {
619 glDeleteProgram(program);
620 }
621 }
622
623 ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN(), ES3_VULKAN());
624
625 // This test tries to test uniform data update while switching between PPO and monolithic program.
626 // The uniform data update occurred on one should carry over to the other. Also buffers are hacked
627 // to smallest size to force updates occur in the new buffer so that any bug related to buffer
628 // recycling will be exposed.
629 class PipelineProgramUniformUpdatesTest : public VulkanUniformUpdatesTest
630 {};
TEST_P(PipelineProgramUniformUpdatesTest,ToggleBetweenPPOAndProgramVKWithUniformUpdate)631 TEST_P(PipelineProgramUniformUpdatesTest, ToggleBetweenPPOAndProgramVKWithUniformUpdate)
632 {
633 ASSERT_TRUE(IsVulkan());
634
635 const GLchar *kPositionUniformVertexShader = R"(attribute vec2 position;
636 uniform float uniformVS;
637 varying vec4 outVS;
638 void main()
639 {
640 outVS = vec4(uniformVS, uniformVS, uniformVS, uniformVS);
641 gl_Position = vec4(position, 0, 1);
642 })";
643
644 const GLchar *kColorUniformFragmentShader = R"(precision mediump float;
645 varying vec4 outVS;
646 uniform float uniformFS;
647 void main()
648 {
649 if(outVS[0] > uniformFS)
650 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
651 else
652 gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
653 })";
654
655 // Compile and link a separable vertex shader
656 GLShader vertShader(GL_VERTEX_SHADER);
657 glShaderSource(vertShader, 1, &kPositionUniformVertexShader, nullptr);
658 glCompileShader(vertShader);
659 GLShader fragShader(GL_FRAGMENT_SHADER);
660 glShaderSource(fragShader, 1, &kColorUniformFragmentShader, nullptr);
661 glCompileShader(fragShader);
662 GLuint program = glCreateProgram();
663 glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
664 glAttachShader(program, vertShader);
665 glAttachShader(program, fragShader);
666 glLinkProgram(program);
667 EXPECT_GL_NO_ERROR();
668
669 glUseProgram(program);
670 limitMaxSets();
671 // Set a really small min size so that every uniform update actually allocates a new buffer.
672 rx::ContextVk *contextVk = hackANGLE();
673 contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
674
675 // Setup vertices
676 std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices();
677 GLint positionLocation = glGetAttribLocation(program, "position");
678 glBindBuffer(GL_ARRAY_BUFFER, 0);
679 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
680 glEnableVertexAttribArray(positionLocation);
681
682 GLint uniformVSLocation = glGetUniformLocation(program, "uniformVS");
683 ASSERT_NE(uniformVSLocation, -1);
684 GLint uniformFSLocation = glGetUniformLocation(program, "uniformFS");
685 ASSERT_NE(uniformFSLocation, -1);
686
687 glUseProgram(0);
688
689 // Generate a pipeline program out of the monolithic program
690 GLuint pipeline;
691 glGenProgramPipelines(1, &pipeline);
692 EXPECT_GL_NO_ERROR();
693 glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, program);
694 EXPECT_GL_NO_ERROR();
695 glBindProgramPipeline(pipeline);
696 EXPECT_GL_NO_ERROR();
697
698 // First use monolithic program and update uniforms
699 glUseProgram(program);
700 glUniform1f(uniformVSLocation, 10.0);
701 glUniform1f(uniformFSLocation, 11.0);
702 glDrawArrays(GL_TRIANGLES, 0, 6);
703 ASSERT_GL_NO_ERROR();
704 const GLsizei kHalfX = getWindowWidth() / 2;
705 const GLsizei kHalfY = getWindowHeight() / 2;
706 const GLsizei xoffset = kHalfX;
707 const GLsizei yoffset = kHalfY;
708 // 10.0f < 11.0f, should see green
709 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
710
711 // Now use PPO and only update FS's uniform
712 glUseProgram(0);
713 for (int i = 0; i < 3; i++)
714 {
715 glActiveShaderProgram(pipeline, program);
716 glUniform1f(uniformFSLocation, 1.0f + i / 10.0f);
717 glDrawArrays(GL_TRIANGLES, 0, 6);
718 ASSERT_GL_NO_ERROR();
719 }
720 // 10.0f > 9.0f, should see red
721 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::red);
722
723 // Now switch back to monolithic program and only update FS's uniform.
724 // 10.0f < 11.0f, should see green again
725 glUseProgram(program);
726 glUniform1f(uniformFSLocation, 11.0f);
727 glDrawArrays(GL_TRIANGLES, 0, 6);
728 ASSERT_GL_NO_ERROR();
729 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
730
731 // Now only update VS's uniform and flush the draw and readback and verify for every iteration.
732 // This will ensure the old buffers are finished and possibly recycled.
733 for (int i = 0; i < 100; i++)
734 {
735 bool iteration_even = (i % 2) == 0 ? true : false;
736 float vsUniformValue;
737
738 // Make VS uniform value ping pong across FS uniform value and also pin pong between
739 // monolithic program and PPO
740 if (iteration_even)
741 {
742 vsUniformValue = 11.0 + (i - 50);
743 glUseProgram(program);
744 glUniform1f(uniformVSLocation, vsUniformValue);
745 }
746 else
747 {
748 vsUniformValue = 11.0 - (i - 50);
749 glUseProgram(0);
750 glActiveShaderProgram(pipeline, program);
751 glUniform1f(uniformVSLocation, vsUniformValue);
752 }
753
754 GLColor expectedColor = vsUniformValue > 11.0f ? GLColor::red : GLColor::green;
755 glDrawArrays(GL_TRIANGLES, 0, 6);
756 ASSERT_GL_NO_ERROR();
757 EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, expectedColor);
758 }
759 }
760
761 ANGLE_INSTANTIATE_TEST(PipelineProgramUniformUpdatesTest, ES31_VULKAN());
762
763 } // anonymous namespace
764