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