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