• 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 "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     constexpr size_t kNumPrograms                       = 2;
567     constexpr size_t kDrawIterations                    = 4;
568     constexpr GLint kPosLoc                             = 0;
569     const std::array<Vector3, kDrawIterations> uniforms = {
570         Vector3(0.1f, 0.2f, 0.3f), Vector3(0.4f, 0.5f, 0.6f), Vector3(0.7f, 0.8f, 0.9f),
571         Vector3(0.1f, 0.5f, 0.9f)};
572     const std::array<GLColor, kDrawIterations> expectedColors = {
573         GLColor(25, 51, 76, 255), GLColor(102, 127, 153, 255), GLColor(178, 204, 229, 255),
574         GLColor(25, 127, 229, 255)};
575 
576     std::array<GLuint, kNumPrograms> programs = {};
577 
578     for (GLuint &program : programs)
579     {
580         auto preLinkCallback = [](GLuint program) {
581             glBindAttribLocation(program, kPosLoc, essl1_shaders::PositionAttrib());
582         };
583 
584         program = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor(),
585                                  preLinkCallback);
586         ASSERT_NE(program, 0u);
587     }
588 
589     const std::array<Vector3, 6> &quadVerts = GetQuadVertices();
590 
591     GLBuffer vbo;
592     glBindBuffer(GL_ARRAY_BUFFER, vbo);
593     glBufferData(GL_ARRAY_BUFFER, quadVerts.size() * sizeof(quadVerts[0]), quadVerts.data(),
594                  GL_STATIC_DRAW);
595 
596     glVertexAttribPointer(kPosLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
597     glEnableVertexAttribArray(kPosLoc);
598 
599     ASSERT_GL_NO_ERROR();
600 
601     for (size_t drawIteration = 0; drawIteration < kDrawIterations; ++drawIteration)
602     {
603         for (GLuint program : programs)
604         {
605             glUseProgram(program);
606             glUniform4f(0, uniforms[drawIteration].x(), uniforms[drawIteration].y(),
607                         uniforms[drawIteration].z(), 1.0f);
608             glDrawArrays(GL_TRIANGLES, 0, 6);
609             EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedColors[drawIteration], 5);
610         }
611     }
612 
613     ASSERT_GL_NO_ERROR();
614 
615     for (GLuint &program : programs)
616     {
617         glDeleteProgram(program);
618     }
619 }
620 
621 ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN(), ES3_VULKAN());
622 
623 // This test tries to test uniform data update while switching between PPO and monolithic program.
624 // The uniform data update occurred on one should carry over to the other. Also buffers are hacked
625 // to smallest size to force updates occur in the new buffer so that any bug related to buffer
626 // recycling will be exposed.
627 class PipelineProgramUniformUpdatesTest : public VulkanUniformUpdatesTest
628 {};
TEST_P(PipelineProgramUniformUpdatesTest,ToggleBetweenPPOAndProgramVKWithUniformUpdate)629 TEST_P(PipelineProgramUniformUpdatesTest, ToggleBetweenPPOAndProgramVKWithUniformUpdate)
630 {
631     ASSERT_TRUE(IsVulkan());
632 
633     const GLchar *kPositionUniformVertexShader = R"(attribute vec2 position;
634 uniform float uniformVS;
635 varying vec4 outVS;
636 void main()
637 {
638     outVS = vec4(uniformVS, uniformVS, uniformVS, uniformVS);
639     gl_Position = vec4(position, 0, 1);
640 })";
641 
642     const GLchar *kColorUniformFragmentShader = R"(precision mediump float;
643 varying vec4 outVS;
644 uniform float uniformFS;
645 void main()
646 {
647     if(outVS[0] > uniformFS)
648         gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
649     else
650         gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
651 })";
652 
653     // Compile and link a separable vertex shader
654     GLShader vertShader(GL_VERTEX_SHADER);
655     glShaderSource(vertShader, 1, &kPositionUniformVertexShader, nullptr);
656     glCompileShader(vertShader);
657     GLShader fragShader(GL_FRAGMENT_SHADER);
658     glShaderSource(fragShader, 1, &kColorUniformFragmentShader, nullptr);
659     glCompileShader(fragShader);
660     GLuint program = glCreateProgram();
661     glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
662     glAttachShader(program, vertShader);
663     glAttachShader(program, fragShader);
664     glLinkProgram(program);
665     EXPECT_GL_NO_ERROR();
666 
667     glUseProgram(program);
668     limitMaxSets();
669     // Set a really small min size so that every uniform update actually allocates a new buffer.
670     rx::ContextVk *contextVk = hackANGLE();
671     contextVk->setDefaultUniformBlocksMinSizeForTesting(128);
672 
673     // Setup vertices
674     std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices();
675     GLint positionLocation              = glGetAttribLocation(program, "position");
676     glBindBuffer(GL_ARRAY_BUFFER, 0);
677     glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
678     glEnableVertexAttribArray(positionLocation);
679 
680     GLint uniformVSLocation = glGetUniformLocation(program, "uniformVS");
681     ASSERT_NE(uniformVSLocation, -1);
682     GLint uniformFSLocation = glGetUniformLocation(program, "uniformFS");
683     ASSERT_NE(uniformFSLocation, -1);
684 
685     glUseProgram(0);
686 
687     // Generate a pipeline program out of the monolithic program
688     GLuint pipeline;
689     glGenProgramPipelines(1, &pipeline);
690     EXPECT_GL_NO_ERROR();
691     glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT | GL_FRAGMENT_SHADER_BIT, program);
692     EXPECT_GL_NO_ERROR();
693     glBindProgramPipeline(pipeline);
694     EXPECT_GL_NO_ERROR();
695 
696     // First use monolithic program and update uniforms
697     glUseProgram(program);
698     glUniform1f(uniformVSLocation, 10.0);
699     glUniform1f(uniformFSLocation, 11.0);
700     glDrawArrays(GL_TRIANGLES, 0, 6);
701     ASSERT_GL_NO_ERROR();
702     const GLsizei kHalfX  = getWindowWidth() / 2;
703     const GLsizei kHalfY  = getWindowHeight() / 2;
704     const GLsizei xoffset = kHalfX;
705     const GLsizei yoffset = kHalfY;
706     // 10.0f < 11.0f, should see green
707     EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
708 
709     // Now use PPO and only update FS's uniform
710     glUseProgram(0);
711     for (int i = 0; i < 3; i++)
712     {
713         glActiveShaderProgram(pipeline, program);
714         glUniform1f(uniformFSLocation, 1.0f + i / 10.0f);
715         glDrawArrays(GL_TRIANGLES, 0, 6);
716         ASSERT_GL_NO_ERROR();
717     }
718     // 10.0f > 9.0f, should see red
719     EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::red);
720 
721     // Now switch back to monolithic program and only update FS's uniform.
722     // 10.0f < 11.0f, should see green again
723     glUseProgram(program);
724     glUniform1f(uniformFSLocation, 11.0f);
725     glDrawArrays(GL_TRIANGLES, 0, 6);
726     ASSERT_GL_NO_ERROR();
727     EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, GLColor::green);
728 
729     // Now only update VS's uniform and flush the draw and readback and verify for every iteration.
730     // This will ensure the old buffers are finished and possibly recycled.
731     for (int i = 0; i < 100; i++)
732     {
733         bool iteration_even = (i % 2) == 0 ? true : false;
734         float vsUniformValue;
735 
736         // Make VS uniform value ping pong across FS uniform value and also pin pong between
737         // monolithic program and PPO
738         if (iteration_even)
739         {
740             vsUniformValue = 11.0 + (i - 50);
741             glUseProgram(program);
742             glUniform1f(uniformVSLocation, vsUniformValue);
743         }
744         else
745         {
746             vsUniformValue = 11.0 - (i - 50);
747             glUseProgram(0);
748             glActiveShaderProgram(pipeline, program);
749             glUniform1f(uniformVSLocation, vsUniformValue);
750         }
751 
752         GLColor expectedColor = vsUniformValue > 11.0f ? GLColor::red : GLColor::green;
753         glDrawArrays(GL_TRIANGLES, 0, 6);
754         ASSERT_GL_NO_ERROR();
755         EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, expectedColor);
756     }
757 }
758 
759 ANGLE_INSTANTIATE_TEST(PipelineProgramUniformUpdatesTest, ES31_VULKAN());
760 
761 }  // anonymous namespace
762