• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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