• 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/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