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