• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // VulkanUniformUpdatesTest:
7 //   Tests to validate our Vulkan dynamic uniform updates are working as expected.
8 //
9 
10 #include "test_utils/ANGLETest.h"
11 #include "test_utils/angle_test_instantiate.h"
12 // 'None' is defined as 'struct None {};' in
13 // third_party/googletest/src/googletest/include/gtest/internal/gtest-type-util.h.
14 // But 'None' is also defined as a numeric constant 0L in <X11/X.h>.
15 // So we need to include ANGLETest.h first to avoid this conflict.
16 
17 #include "libANGLE/Context.h"
18 #include "libANGLE/angletypes.h"
19 #include "libANGLE/renderer/vulkan/ContextVk.h"
20 #include "libANGLE/renderer/vulkan/ProgramVk.h"
21 #include "libANGLE/renderer/vulkan/TextureVk.h"
22 #include "test_utils/gl_raii.h"
23 #include "util/EGLWindow.h"
24 #include "util/shader_utils.h"
25 
26 using namespace angle;
27 
28 namespace
29 {
30 
31 class VulkanUniformUpdatesTest : public ANGLETest
32 {
33   protected:
hackANGLE() const34     rx::ContextVk *hackANGLE() const
35     {
36         // Hack the angle!
37         const gl::Context *context = static_cast<gl::Context *>(getEGLWindow()->getContext());
38         return rx::GetImplAs<rx::ContextVk>(context);
39     }
40 
hackProgram(GLuint handle) const41     rx::ProgramVk *hackProgram(GLuint handle) const
42     {
43         // Hack the angle!
44         const gl::Context *context = static_cast<gl::Context *>(getEGLWindow()->getContext());
45         const gl::Program *program = context->getProgramResolveLink({handle});
46         return rx::vk::GetImpl(program);
47     }
48 
hackTexture(GLuint handle) const49     rx::TextureVk *hackTexture(GLuint handle) const
50     {
51         // Hack the angle!
52         const gl::Context *context = static_cast<gl::Context *>(getEGLWindow()->getContext());
53         const gl::Texture *texture = context->getTexture({handle});
54         return rx::vk::GetImpl(texture);
55     }
56 
57     static constexpr uint32_t kMaxSetsForTesting = 32;
58 
limitMaxSets(GLuint program)59     void limitMaxSets(GLuint program)
60     {
61         rx::ContextVk *contextVk = hackANGLE();
62         rx::ProgramVk *programVk = hackProgram(program);
63 
64         // Force a small limit on the max sets per pool to more easily trigger a new allocation.
65         rx::vk::DynamicDescriptorPool *uniformPool =
66             programVk->getDynamicDescriptorPool(rx::kUniformsAndXfbDescriptorSetIndex);
67         uniformPool->setMaxSetsPerPoolForTesting(kMaxSetsForTesting);
68         VkDescriptorPoolSize uniformSetSize = {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
69                                                rx::kReservedDefaultUniformBindingCount};
70         (void)uniformPool->init(contextVk, &uniformSetSize, 1);
71 
72         uint32_t textureCount =
73             static_cast<uint32_t>(programVk->getState().getSamplerBindings().size());
74 
75         // To support the bindEmptyForUnusedDescriptorSets workaround.
76         textureCount = std::max(textureCount, 1u);
77 
78         rx::vk::DynamicDescriptorPool *texturePool =
79             programVk->getDynamicDescriptorPool(rx::kTextureDescriptorSetIndex);
80         texturePool->setMaxSetsPerPoolForTesting(kMaxSetsForTesting);
81         VkDescriptorPoolSize textureSetSize = {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
82                                                textureCount};
83         (void)texturePool->init(contextVk, &textureSetSize, 1);
84     }
85 
86     static constexpr size_t kTextureStagingBufferSizeForTesting = 128;
87 
limitTextureStagingBufferSize(GLuint texture)88     void limitTextureStagingBufferSize(GLuint texture)
89     {
90         rx::TextureVk *textureVk = hackTexture(texture);
91         textureVk->overrideStagingBufferSizeForTesting(kTextureStagingBufferSizeForTesting);
92     }
93 };
94 
95 // This test updates a uniform until a new buffer is allocated and then make sure the uniform
96 // updates still work.
TEST_P(VulkanUniformUpdatesTest,UpdateUntilNewBufferIsAllocated)97 TEST_P(VulkanUniformUpdatesTest, UpdateUntilNewBufferIsAllocated)
98 {
99     ASSERT_TRUE(IsVulkan());
100 
101     constexpr char kPositionUniformVertexShader[] = R"(attribute vec2 position;
102 uniform vec2 uniPosModifier;
103 void main()
104 {
105     gl_Position = vec4(position + uniPosModifier, 0, 1);
106 })";
107 
108     constexpr char kColorUniformFragmentShader[] = R"(precision mediump float;
109 uniform vec4 uniColor;
110 void main()
111 {
112     gl_FragColor = uniColor;
113 })";
114 
115     ANGLE_GL_PROGRAM(program, kPositionUniformVertexShader, kColorUniformFragmentShader);
116     glUseProgram(program);
117 
118     limitMaxSets(program);
119 
120     rx::ProgramVk *programVk = hackProgram(program);
121 
122     // Set a really small min size so that uniform updates often allocates a new buffer.
123     programVk->setDefaultUniformBlocksMinSizeForTesting(128);
124 
125     GLint posUniformLocation = glGetUniformLocation(program, "uniPosModifier");
126     ASSERT_NE(posUniformLocation, -1);
127     GLint colorUniformLocation = glGetUniformLocation(program, "uniColor");
128     ASSERT_NE(colorUniformLocation, -1);
129 
130     // Sets both uniforms 10 times, it should certainly trigger new buffers creations by the
131     // underlying StreamingBuffer.
132     for (int i = 0; i < 100; i++)
133     {
134         glUniform2f(posUniformLocation, -0.5, 0.0);
135         glUniform4f(colorUniformLocation, 1.0, 0.0, 0.0, 1.0);
136         drawQuad(program, "position", 0.5f, 1.0f);
137         swapBuffers();
138         ASSERT_GL_NO_ERROR();
139     }
140 }
141 
InitTexture(GLColor color,GLTexture * texture)142 void InitTexture(GLColor color, GLTexture *texture)
143 {
144     const std::vector<GLColor> colors(4, color);
145     glBindTexture(GL_TEXTURE_2D, *texture);
146     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
147     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
148     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
149 }
150 
151 // Force uniform updates until the dynamic descriptor pool wraps into a new pool allocation.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUpdates)152 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUpdates)
153 {
154     ASSERT_TRUE(IsVulkan());
155 
156     // Initialize texture program.
157     GLuint program = get2DTexturedQuadProgram();
158     ASSERT_NE(0u, program);
159     glUseProgram(program);
160 
161     // Force a small limit on the max sets per pool to more easily trigger a new allocation.
162     limitMaxSets(program);
163 
164     GLint texLoc = glGetUniformLocation(program, "tex");
165     ASSERT_NE(-1, texLoc);
166 
167     // Initialize basic red texture.
168     const std::vector<GLColor> redColors(4, GLColor::red);
169     GLTexture texture;
170     glBindTexture(GL_TEXTURE_2D, texture);
171     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, redColors.data());
172     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
173     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
174     ASSERT_GL_NO_ERROR();
175 
176     // Draw multiple times, each iteration will create a new descriptor set.
177     for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 8; ++iteration)
178     {
179         glUniform1i(texLoc, 0);
180         drawQuad(program, "position", 0.5f, 1.0f, true);
181         swapBuffers();
182         ASSERT_GL_NO_ERROR();
183     }
184 }
185 
186 // Uniform updates along with Texture updates.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUniformAndTextureUpdates)187 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdates)
188 {
189     ASSERT_TRUE(IsVulkan());
190 
191     // Initialize texture program.
192     constexpr char kFS[] = R"(varying mediump vec2 v_texCoord;
193 uniform sampler2D tex;
194 uniform mediump vec4 colorMask;
195 void main()
196 {
197     gl_FragColor = texture2D(tex, v_texCoord) * colorMask;
198 })";
199 
200     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
201     glUseProgram(program);
202 
203     limitMaxSets(program);
204 
205     // Get uniform locations.
206     GLint texLoc = glGetUniformLocation(program, "tex");
207     ASSERT_NE(-1, texLoc);
208 
209     GLint colorMaskLoc = glGetUniformLocation(program, "colorMask");
210     ASSERT_NE(-1, colorMaskLoc);
211 
212     // Initialize white texture.
213     GLTexture whiteTexture;
214     InitTexture(GLColor::white, &whiteTexture);
215     ASSERT_GL_NO_ERROR();
216 
217     glActiveTexture(GL_TEXTURE0);
218     glBindTexture(GL_TEXTURE_2D, whiteTexture);
219 
220     // Initialize magenta texture.
221     GLTexture magentaTexture;
222     InitTexture(GLColor::magenta, &magentaTexture);
223     ASSERT_GL_NO_ERROR();
224 
225     glActiveTexture(GL_TEXTURE1);
226     glBindTexture(GL_TEXTURE_2D, magentaTexture);
227 
228     // Draw multiple times, each iteration will create a new descriptor set.
229     for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
230     {
231         // Draw with white.
232         glUniform1i(texLoc, 0);
233         glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
234         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
235 
236         // Draw with white masking out red.
237         glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
238         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
239 
240         // Draw with magenta.
241         glUniform1i(texLoc, 1);
242         glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
243         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
244 
245         // Draw with magenta masking out red.
246         glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
247         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
248 
249         swapBuffers();
250         ASSERT_GL_NO_ERROR();
251     }
252 }
253 
254 // Uniform updates along with Texture regeneration.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUniformAndTextureRegeneration)255 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureRegeneration)
256 {
257     ASSERT_TRUE(IsVulkan());
258 
259     // Initialize texture program.
260     constexpr char kFS[] = R"(varying mediump vec2 v_texCoord;
261 uniform sampler2D tex;
262 uniform mediump vec4 colorMask;
263 void main()
264 {
265     gl_FragColor = texture2D(tex, v_texCoord) * colorMask;
266 })";
267 
268     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), kFS);
269     glUseProgram(program);
270 
271     limitMaxSets(program);
272 
273     // Initialize large arrays of textures.
274     std::vector<GLTexture> whiteTextures;
275     std::vector<GLTexture> magentaTextures;
276 
277     for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
278     {
279         // Initialize white texture.
280         GLTexture whiteTexture;
281         InitTexture(GLColor::white, &whiteTexture);
282         ASSERT_GL_NO_ERROR();
283         whiteTextures.emplace_back(std::move(whiteTexture));
284 
285         // Initialize magenta texture.
286         GLTexture magentaTexture;
287         InitTexture(GLColor::magenta, &magentaTexture);
288         ASSERT_GL_NO_ERROR();
289         magentaTextures.emplace_back(std::move(magentaTexture));
290     }
291 
292     // Get uniform locations.
293     GLint texLoc = glGetUniformLocation(program, "tex");
294     ASSERT_NE(-1, texLoc);
295 
296     GLint colorMaskLoc = glGetUniformLocation(program, "colorMask");
297     ASSERT_NE(-1, colorMaskLoc);
298 
299     // Draw multiple times, each iteration will create a new descriptor set.
300     for (int outerIteration = 0; outerIteration < 2; ++outerIteration)
301     {
302         for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
303         {
304             glActiveTexture(GL_TEXTURE0);
305             glBindTexture(GL_TEXTURE_2D, whiteTextures[iteration]);
306 
307             glActiveTexture(GL_TEXTURE1);
308             glBindTexture(GL_TEXTURE_2D, magentaTextures[iteration]);
309 
310             // Draw with white.
311             glUniform1i(texLoc, 0);
312             glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
313             drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
314 
315             // Draw with white masking out red.
316             glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
317             drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
318 
319             // Draw with magenta.
320             glUniform1i(texLoc, 1);
321             glUniform4f(colorMaskLoc, 1.0f, 1.0f, 1.0f, 1.0f);
322             drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
323 
324             // Draw with magenta masking out red.
325             glUniform4f(colorMaskLoc, 0.0f, 1.0f, 1.0f, 1.0f);
326             drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
327 
328             swapBuffers();
329             ASSERT_GL_NO_ERROR();
330         }
331     }
332 }
333 
334 // Uniform updates along with Texture updates.
TEST_P(VulkanUniformUpdatesTest,DescriptorPoolUniformAndTextureUpdatesTwoShaders)335 TEST_P(VulkanUniformUpdatesTest, DescriptorPoolUniformAndTextureUpdatesTwoShaders)
336 {
337     ASSERT_TRUE(IsVulkan());
338 
339     // Initialize program.
340     constexpr char kVS[] = R"(attribute vec2 position;
341 varying mediump vec2 texCoord;
342 void main()
343 {
344     gl_Position = vec4(position, 0, 1);
345     texCoord = position * 0.5 + vec2(0.5);
346 })";
347 
348     constexpr char kFS[] = R"(varying mediump vec2 texCoord;
349 uniform mediump vec4 colorMask;
350 void main()
351 {
352     gl_FragColor = colorMask;
353 })";
354 
355     ANGLE_GL_PROGRAM(program1, kVS, kFS);
356     ANGLE_GL_PROGRAM(program2, kVS, kFS);
357     glUseProgram(program1);
358 
359     // Force a small limit on the max sets per pool to more easily trigger a new allocation.
360     limitMaxSets(program1);
361     limitMaxSets(program2);
362 
363     rx::ProgramVk *program1Vk = hackProgram(program1);
364     rx::ProgramVk *program2Vk = hackProgram(program2);
365 
366     // Set a really small min size so that uniform updates often allocates a new buffer.
367     program1Vk->setDefaultUniformBlocksMinSizeForTesting(128);
368     program2Vk->setDefaultUniformBlocksMinSizeForTesting(128);
369 
370     // Get uniform locations.
371     GLint colorMaskLoc1 = glGetUniformLocation(program1, "colorMask");
372     ASSERT_NE(-1, colorMaskLoc1);
373     GLint colorMaskLoc2 = glGetUniformLocation(program2, "colorMask");
374     ASSERT_NE(-1, colorMaskLoc2);
375 
376     // Draw with white using program1.
377     glUniform4f(colorMaskLoc1, 1.0f, 1.0f, 1.0f, 1.0f);
378     drawQuad(program1, "position", 0.5f, 1.0f, true);
379     swapBuffers();
380     ASSERT_GL_NO_ERROR();
381 
382     // Now switch to use program2
383     glUseProgram(program2);
384     // Draw multiple times w/ program2, each iteration will create a new descriptor set.
385     // This will cause the first descriptor pool to be cleaned up
386     for (uint32_t iteration = 0; iteration < kMaxSetsForTesting * 2; ++iteration)
387     {
388         // Draw with white.
389         glUniform4f(colorMaskLoc2, 1.0f, 1.0f, 1.0f, 1.0f);
390         drawQuad(program2, "position", 0.5f, 1.0f, true);
391 
392         // Draw with white masking out red.
393         glUniform4f(colorMaskLoc2, 0.0f, 1.0f, 1.0f, 1.0f);
394         drawQuad(program2, "position", 0.5f, 1.0f, true);
395 
396         // Draw with magenta.
397         glUniform4f(colorMaskLoc2, 1.0f, 1.0f, 1.0f, 1.0f);
398         drawQuad(program2, "position", 0.5f, 1.0f, true);
399 
400         // Draw with magenta masking out red.
401         glUniform4f(colorMaskLoc2, 0.0f, 1.0f, 1.0f, 1.0f);
402         drawQuad(program2, "position", 0.5f, 1.0f, true);
403 
404         swapBuffers();
405         ASSERT_GL_NO_ERROR();
406     }
407     // Finally, attempt to draw again with program1, with original uniform values.
408     glUseProgram(program1);
409     drawQuad(program1, "position", 0.5f, 1.0f, true);
410     swapBuffers();
411     ASSERT_GL_NO_ERROR();
412 }
413 
414 // Verify that overflowing a Texture's staging buffer doesn't overwrite current data.
TEST_P(VulkanUniformUpdatesTest,TextureStagingBufferRecycling)415 TEST_P(VulkanUniformUpdatesTest, TextureStagingBufferRecycling)
416 {
417     ASSERT_TRUE(IsVulkan());
418 
419     // Fails on older MESA drivers.  http://crbug.com/979349
420     ANGLE_SKIP_TEST_IF(IsAMD() && IsLinux());
421 
422     GLTexture tex;
423     glBindTexture(GL_TEXTURE_2D, tex);
424     limitTextureStagingBufferSize(tex);
425     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
426                  GL_UNSIGNED_BYTE, nullptr);
427     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
428     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
429 
430     const GLColor kColors[4] = {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow};
431 
432     // Repeatedly update the staging buffer to trigger multiple recyclings.
433     const GLsizei kHalfX      = getWindowWidth() / 2;
434     const GLsizei kHalfY      = getWindowHeight() / 2;
435     constexpr int kIterations = 4;
436     for (int x = 0; x < 2; ++x)
437     {
438         for (int y = 0; y < 2; ++y)
439         {
440             const int kColorIndex = x + y * 2;
441             const GLColor kColor  = kColors[kColorIndex];
442 
443             for (int iteration = 0; iteration < kIterations; ++iteration)
444             {
445                 for (int subX = 0; subX < kHalfX; ++subX)
446                 {
447                     for (int subY = 0; subY < kHalfY; ++subY)
448                     {
449                         const GLsizei xoffset = x * kHalfX + subX;
450                         const GLsizei yoffset = y * kHalfY + subY;
451 
452                         // Update a single pixel.
453                         glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, yoffset, 1, 1, GL_RGBA,
454                                         GL_UNSIGNED_BYTE, kColor.data());
455                     }
456                 }
457             }
458         }
459     }
460 
461     draw2DTexturedQuad(0.5f, 1.0f, true);
462     ASSERT_GL_NO_ERROR();
463 
464     // Verify pixels.
465     for (int x = 0; x < 2; ++x)
466     {
467         for (int y = 0; y < 2; ++y)
468         {
469             const GLsizei xoffset = x * kHalfX;
470             const GLsizei yoffset = y * kHalfY;
471             const int kColorIndex = x + y * 2;
472             const GLColor kColor  = kColors[kColorIndex];
473             EXPECT_PIXEL_RECT_EQ(xoffset, yoffset, kHalfX, kHalfY, kColor);
474         }
475     }
476 }
477 
478 ANGLE_INSTANTIATE_TEST(VulkanUniformUpdatesTest, ES2_VULKAN(), ES3_VULKAN());
479 
480 }  // anonymous namespace
481