blob: 8222b572fd7e0057e7072d61974999422c4fe122 [file] [log] [blame]
Jamie Madillfa05f602015-05-07 13:47:11 -04001//
2// Copyright 2015 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
Corentin Wallezd3970de2015-05-14 11:07:48 -04007#include "test_utils/ANGLETest.h"
Jamie Madillfa05f602015-05-07 13:47:11 -04008
Brandon Jones993d08d2014-10-10 14:32:57 -07009#include <memory>
10#include <stdint.h>
Jamie Madill7a29e4a2014-05-02 10:41:48 -040011
Austin Kinross137b1512015-06-17 16:14:53 -070012#include "EGLWindow.h"
13#include "OSWindow.h"
14#include "test_utils/angle_test_configs.h"
15
Jamie Madillfa05f602015-05-07 13:47:11 -040016using namespace angle;
Austin Kinross18b931d2014-09-29 12:58:31 -070017
Jamie Madill7a29e4a2014-05-02 10:41:48 -040018class ProgramBinaryTest : public ANGLETest
19{
Geoff Lang0d3683c2014-10-23 11:08:16 -040020 protected:
Jamie Madillfa05f602015-05-07 13:47:11 -040021 ProgramBinaryTest()
Jamie Madill7a29e4a2014-05-02 10:41:48 -040022 {
23 setWindowWidth(128);
24 setWindowHeight(128);
25 setConfigRedBits(8);
26 setConfigGreenBits(8);
27 setConfigBlueBits(8);
28 setConfigAlphaBits(8);
29 }
30
31 virtual void SetUp()
32 {
33 ANGLETest::SetUp();
34
35 const std::string vertexShaderSource = SHADER_SOURCE
36 (
37 attribute vec4 inputAttribute;
38 void main()
39 {
40 gl_Position = inputAttribute;
41 }
42 );
43
44 const std::string fragmentShaderSource = SHADER_SOURCE
45 (
46 void main()
47 {
48 gl_FragColor = vec4(1,0,0,1);
49 }
50 );
51
Jamie Madill5599c8f2014-08-26 13:16:39 -040052 mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
Jamie Madill7a29e4a2014-05-02 10:41:48 -040053 if (mProgram == 0)
54 {
55 FAIL() << "shader compilation failed.";
56 }
57
58 glGenBuffers(1, &mBuffer);
59 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
60 glBufferData(GL_ARRAY_BUFFER, 128, NULL, GL_STATIC_DRAW);
61 glBindBuffer(GL_ARRAY_BUFFER, 0);
62
63 ASSERT_GL_NO_ERROR();
64 }
65
66 virtual void TearDown()
67 {
68 glDeleteProgram(mProgram);
69 glDeleteBuffers(1, &mBuffer);
70
71 ANGLETest::TearDown();
72 }
73
Geoff Lang90208e92015-10-05 15:40:36 -040074 GLint getAvailableProgramBinaryFormatCount() const
75 {
76 GLint formatCount;
77 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &formatCount);
78 return formatCount;
79 }
80
Jamie Madill7a29e4a2014-05-02 10:41:48 -040081 GLuint mProgram;
82 GLuint mBuffer;
83};
84
85// This tests the assumption that float attribs of different size
86// should not internally cause a vertex shader recompile (for conversion).
Jamie Madillfa05f602015-05-07 13:47:11 -040087TEST_P(ProgramBinaryTest, FloatDynamicShaderSize)
Jamie Madill7a29e4a2014-05-02 10:41:48 -040088{
Geoff Lang90208e92015-10-05 15:40:36 -040089 if (!extensionEnabled("GL_OES_get_program_binary"))
90 {
91 std::cout << "Test skipped because GL_OES_get_program_binary is not available."
92 << std::endl;
93 return;
94 }
95
96 if (getAvailableProgramBinaryFormatCount() == 0)
97 {
98 std::cout << "Test skipped because no program binary formats are available." << std::endl;
99 return;
100 }
101
Jamie Madill7a29e4a2014-05-02 10:41:48 -0400102 glUseProgram(mProgram);
103 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
104
105 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, NULL);
106 glEnableVertexAttribArray(0);
107 glDrawArrays(GL_POINTS, 0, 1);
108
109 GLint programLength;
110 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
111
112 EXPECT_GL_NO_ERROR();
113
114 for (GLsizei size = 1; size <= 3; size++)
115 {
116 glVertexAttribPointer(0, size, GL_FLOAT, GL_FALSE, 8, NULL);
117 glEnableVertexAttribArray(0);
118 glDrawArrays(GL_POINTS, 0, 1);
119
120 GLint newProgramLength;
121 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &newProgramLength);
122 EXPECT_GL_NO_ERROR();
123 EXPECT_EQ(programLength, newProgramLength);
124 }
125}
Brandon Jones993d08d2014-10-10 14:32:57 -0700126
127// This tests the ability to successfully save and load a program binary.
Jamie Madillfa05f602015-05-07 13:47:11 -0400128TEST_P(ProgramBinaryTest, SaveAndLoadBinary)
Brandon Jones993d08d2014-10-10 14:32:57 -0700129{
Geoff Lang90208e92015-10-05 15:40:36 -0400130 if (!extensionEnabled("GL_OES_get_program_binary"))
131 {
132 std::cout << "Test skipped because GL_OES_get_program_binary is not available."
133 << std::endl;
134 return;
135 }
136
137 if (getAvailableProgramBinaryFormatCount() == 0)
138 {
139 std::cout << "Test skipped because no program binary formats are available." << std::endl;
140 return;
141 }
142
Brandon Jones993d08d2014-10-10 14:32:57 -0700143 GLint programLength = 0;
144 GLint writtenLength = 0;
145 GLenum binaryFormat = 0;
146
147 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
148 EXPECT_GL_NO_ERROR();
149
150 std::vector<uint8_t> binary(programLength);
151 glGetProgramBinaryOES(mProgram, programLength, &writtenLength, &binaryFormat, binary.data());
152 EXPECT_GL_NO_ERROR();
153
154 // The lengths reported by glGetProgramiv and glGetProgramBinaryOES should match
155 EXPECT_EQ(programLength, writtenLength);
156
157 if (writtenLength)
158 {
159 GLuint program2 = glCreateProgram();
160 glProgramBinaryOES(program2, binaryFormat, binary.data(), writtenLength);
161
162 EXPECT_GL_NO_ERROR();
163
164 GLint linkStatus;
165 glGetProgramiv(program2, GL_LINK_STATUS, &linkStatus);
166 if (linkStatus == 0)
167 {
168 GLint infoLogLength;
169 glGetProgramiv(program2, GL_INFO_LOG_LENGTH, &infoLogLength);
170
Geoff Lang981afd72014-11-05 16:30:36 -0500171 if (infoLogLength > 0)
172 {
173 std::vector<GLchar> infoLog(infoLogLength);
Cooper Partin4d61f7e2015-08-12 10:56:50 -0700174 glGetProgramInfoLog(program2, static_cast<GLsizei>(infoLog.size()), NULL,
175 &infoLog[0]);
Geoff Lang981afd72014-11-05 16:30:36 -0500176 FAIL() << "program link failed: " << &infoLog[0];
177 }
178 else
179 {
180 FAIL() << "program link failed.";
181 }
Brandon Jones993d08d2014-10-10 14:32:57 -0700182 }
183 else
184 {
185 glUseProgram(program2);
186 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
187
188 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, NULL);
189 glEnableVertexAttribArray(0);
190 glDrawArrays(GL_POINTS, 0, 1);
191
192 EXPECT_GL_NO_ERROR();
193 }
194
195 glDeleteProgram(program2);
196 }
197}
Jamie Madillfa05f602015-05-07 13:47:11 -0400198
199// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
Geoff Lang90208e92015-10-05 15:40:36 -0400200ANGLE_INSTANTIATE_TEST(ProgramBinaryTest,
201 ES2_D3D9(),
202 ES2_D3D11(),
203 ES3_D3D11(),
204 ES2_OPENGL(),
205 ES3_OPENGL());
Austin Kinross137b1512015-06-17 16:14:53 -0700206
Brandon Jones1048ea72015-10-06 15:34:52 -0700207class ProgramBinaryTransformFeedbackTest : public ANGLETest
208{
209 protected:
210 ProgramBinaryTransformFeedbackTest()
211 {
212 setWindowWidth(128);
213 setWindowHeight(128);
214 setConfigRedBits(8);
215 setConfigGreenBits(8);
216 setConfigBlueBits(8);
217 setConfigAlphaBits(8);
218 }
219
220 void SetUp() override
221 {
222 ANGLETest::SetUp();
223
224 const std::string vertexShaderSource = SHADER_SOURCE
225 ( #version 300 es\n
226 in vec4 inputAttribute;
227 out vec4 outputVarying;
228 void main()
229 {
230 outputVarying = inputAttribute;
231 }
232 );
233
234 const std::string fragmentShaderSource = SHADER_SOURCE
235 ( #version 300 es\n
236 precision highp float;
237 out vec4 outputColor;
238 void main()
239 {
240 outputColor = vec4(1,0,0,1);
241 }
242 );
243
244 std::vector<std::string> transformFeedbackVaryings;
245 transformFeedbackVaryings.push_back("outputVarying");
246
247 mProgram = CompileProgramWithTransformFeedback(
248 vertexShaderSource, fragmentShaderSource, transformFeedbackVaryings,
249 GL_SEPARATE_ATTRIBS);
250 if (mProgram == 0)
251 {
252 FAIL() << "shader compilation failed.";
253 }
254
255 ASSERT_GL_NO_ERROR();
256 }
257
258 virtual void TearDown()
259 {
260 glDeleteProgram(mProgram);
261
262 ANGLETest::TearDown();
263 }
264
265 GLint getAvailableProgramBinaryFormatCount() const
266 {
267 GLint formatCount;
268 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS_OES, &formatCount);
269 return formatCount;
270 }
271
272 GLuint mProgram;
273};
274
275// This tests the assumption that float attribs of different size
276// should not internally cause a vertex shader recompile (for conversion).
277TEST_P(ProgramBinaryTransformFeedbackTest, GetTransformFeedbackVarying)
278{
279 if (!extensionEnabled("GL_OES_get_program_binary"))
280 {
281 std::cout << "Test skipped because GL_OES_get_program_binary is not available."
282 << std::endl;
283 return;
284 }
285
286 if (getAvailableProgramBinaryFormatCount() == 0)
287 {
288 std::cout << "Test skipped because no program binary formats are available." << std::endl;
289 return;
290 }
291
292 std::vector<uint8_t> binary(0);
293 GLint programLength = 0;
294 GLint writtenLength = 0;
295 GLenum binaryFormat = 0;
296
297 // Save the program binary out
298 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
299 ASSERT_GL_NO_ERROR();
300 binary.resize(programLength);
301 glGetProgramBinaryOES(mProgram, programLength, &writtenLength, &binaryFormat, binary.data());
302 ASSERT_GL_NO_ERROR();
303
304 glDeleteProgram(mProgram);
305
306 // Load program binary
307 mProgram = glCreateProgram();
308 glProgramBinaryOES(mProgram, binaryFormat, binary.data(), writtenLength);
309
310 // Ensure the loaded binary is linked
311 GLint linkStatus;
312 glGetProgramiv(mProgram, GL_LINK_STATUS, &linkStatus);
313 EXPECT_TRUE(linkStatus != 0);
314
315 // Query information about the transform feedback varying
316 char varyingName[64];
317 GLsizei varyingSize = 0;
318 GLenum varyingType = GL_NONE;
319
320 glGetTransformFeedbackVarying(mProgram, 0, 64, &writtenLength, &varyingSize, &varyingType, varyingName);
321 EXPECT_GL_NO_ERROR();
322
323 EXPECT_EQ(13, writtenLength);
324 EXPECT_STREQ("outputVarying", varyingName);
325 EXPECT_EQ(1, varyingSize);
326 EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varyingType);
327
328 EXPECT_GL_NO_ERROR();
329}
330
331// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
332ANGLE_INSTANTIATE_TEST(ProgramBinaryTransformFeedbackTest,
333 ES3_D3D11(),
334 ES3_OPENGL());
335
Austin Kinross137b1512015-06-17 16:14:53 -0700336// For the ProgramBinariesAcrossPlatforms tests, we need two sets of params:
337// - a set to save the program binary
338// - a set to load the program binary
339// We combine these into one struct extending PlatformParameters so we can reuse existing ANGLE test macros
340struct PlatformsWithLinkResult : PlatformParameters
341{
342 PlatformsWithLinkResult(PlatformParameters saveParams, PlatformParameters loadParamsIn, bool expectedLinkResultIn)
343 {
344 majorVersion = saveParams.majorVersion;
345 minorVersion = saveParams.minorVersion;
346 eglParameters = saveParams.eglParameters;
347 loadParams = loadParamsIn;
348 expectedLinkResult = expectedLinkResultIn;
349 }
350
351 PlatformParameters loadParams;
352 bool expectedLinkResult;
353};
354
Corentin Wallez33585c72015-09-03 14:41:23 -0400355// Provide a custom gtest parameter name function for PlatformsWithLinkResult
356// to avoid returning the same parameter name twice. Such a conflict would happen
357// between ES2_D3D11_to_ES2D3D11 and ES2_D3D11_to_ES3D3D11 as they were both
358// named ES2_D3D11
359std::ostream &operator<<(std::ostream& stream, const PlatformsWithLinkResult &platform)
360{
361 const PlatformParameters &platform1 = platform;
362 const PlatformParameters &platform2 = platform.loadParams;
363 stream << platform1 << "_to_" << platform2;
364 return stream;
365}
366
Austin Kinross137b1512015-06-17 16:14:53 -0700367class ProgramBinariesAcrossPlatforms : public testing::TestWithParam<PlatformsWithLinkResult>
368{
369 public:
370 void SetUp() override
371 {
372 mOSWindow = CreateOSWindow();
373 bool result = mOSWindow->initialize("ProgramBinariesAcrossRenderersTests", 100, 100);
374
375 if (result == false)
376 {
377 FAIL() << "Failed to create OS window";
378 }
379 }
380
381 EGLWindow *createAndInitEGLWindow(angle::PlatformParameters &param)
382 {
Geoff Lang5ade8452015-09-02 11:00:30 -0400383 EGLWindow *eglWindow =
384 new EGLWindow(param.majorVersion, param.minorVersion, param.eglParameters);
Austin Kinross137b1512015-06-17 16:14:53 -0700385 bool result = eglWindow->initializeGL(mOSWindow);
386 if (result == false)
387 {
388 SafeDelete(eglWindow);
389 eglWindow = nullptr;
390 }
391
392 return eglWindow;
393 }
394
395 void destroyEGLWindow(EGLWindow **eglWindow)
396 {
397 ASSERT(*eglWindow != nullptr);
398 (*eglWindow)->destroyGL();
399 SafeDelete(*eglWindow);
400 *eglWindow = nullptr;
401 }
402
403 GLuint createES2ProgramFromSource()
404 {
405 const std::string testVertexShaderSource = SHADER_SOURCE
406 (
407 attribute highp vec4 position;
408
409 void main(void)
410 {
411 gl_Position = position;
412 }
413 );
414
415 const std::string testFragmentShaderSource = SHADER_SOURCE
416 (
417 void main(void)
418 {
419 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
420 }
421 );
422
423 return CompileProgram(testVertexShaderSource, testFragmentShaderSource);
424 }
425
426 GLuint createES3ProgramFromSource()
427 {
428 const std::string testVertexShaderSource = SHADER_SOURCE
429 ( #version 300 es\n
430 precision highp float;
431 in highp vec4 position;
432
433 void main(void)
434 {
435 gl_Position = position;
436 }
437 );
438
439 const std::string testFragmentShaderSource = SHADER_SOURCE
440 ( #version 300 es \n
441 precision highp float;
442 out vec4 out_FragColor;
443
444 void main(void)
445 {
446 out_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
447 }
448 );
449
450 return CompileProgram(testVertexShaderSource, testFragmentShaderSource);
451 }
452
453 void drawWithProgram(GLuint program)
454 {
455 glClearColor(0, 0, 0, 1);
456 glClear(GL_COLOR_BUFFER_BIT);
457
458 GLint positionLocation = glGetAttribLocation(program, "position");
459
460 glUseProgram(program);
461
462 const GLfloat vertices[] =
463 {
464 -1.0f, 1.0f, 0.5f,
465 -1.0f, -1.0f, 0.5f,
466 1.0f, -1.0f, 0.5f,
467
468 -1.0f, 1.0f, 0.5f,
469 1.0f, -1.0f, 0.5f,
470 1.0f, 1.0f, 0.5f,
471 };
472
473 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
474 glEnableVertexAttribArray(positionLocation);
475
476 glDrawArrays(GL_TRIANGLES, 0, 6);
477
478 glDisableVertexAttribArray(positionLocation);
479 glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
480
481 EXPECT_PIXEL_EQ(mOSWindow->getWidth() / 2, mOSWindow->getHeight() / 2, 255, 0, 0, 255);
482 }
483
484 void TearDown() override
485 {
486 mOSWindow->destroy();
487 SafeDelete(mOSWindow);
488 }
489
490 OSWindow *mOSWindow;
491};
492
493// Tries to create a program binary using one set of platform params, then load it using a different sent of params
494TEST_P(ProgramBinariesAcrossPlatforms, CreateAndReloadBinary)
495{
496 angle::PlatformParameters firstRenderer = GetParam();
497 angle::PlatformParameters secondRenderer = GetParam().loadParams;
498 bool expectedLinkResult = GetParam().expectedLinkResult;
499
500 if (!(IsPlatformAvailable(firstRenderer)))
501 {
502 std::cout << "First renderer not supported, skipping test";
503 return;
504 }
505
506 if (!(IsPlatformAvailable(secondRenderer)))
507 {
508 std::cout << "Second renderer not supported, skipping test";
509 return;
510 }
511
512 EGLWindow *eglWindow = nullptr;
513 std::vector<uint8_t> binary(0);
514 GLuint program = 0;
515
516 GLint programLength = 0;
517 GLint writtenLength = 0;
518 GLenum binaryFormat = 0;
519
520 // Create a EGL window with the first renderer
521 eglWindow = createAndInitEGLWindow(firstRenderer);
522 if (eglWindow == nullptr)
523 {
524 FAIL() << "Failed to create EGL window";
525 return;
526 }
527
528 // If the test is trying to use both the default GPU and WARP, but the default GPU *IS* WARP,
529 // then our expectations for the test results will be invalid.
530 if (firstRenderer.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE &&
531 secondRenderer.eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE)
532 {
533 std::string rendererString = std::string(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
534 std::transform(rendererString.begin(), rendererString.end(), rendererString.begin(), ::tolower);
535
536 auto basicRenderPos = rendererString.find(std::string("microsoft basic render"));
537 auto softwareAdapterPos = rendererString.find(std::string("software adapter"));
538
539 if (basicRenderPos != std::string::npos || softwareAdapterPos != std::string::npos)
540 {
541 // The first renderer is using WARP, even though we didn't explictly request it
542 // We should skip this test
543 std::cout << "Test skipped on when default GPU is WARP." << std::endl;
544 return;
545 }
546 }
547
548 // Create a program
549 if (firstRenderer.majorVersion == 3)
550 {
551 program = createES3ProgramFromSource();
552 }
553 else
554 {
555 program = createES2ProgramFromSource();
556 }
557
558 if (program == 0)
559 {
Austin Kinross137b1512015-06-17 16:14:53 -0700560 destroyEGLWindow(&eglWindow);
Corentin Wallezefb6ac62015-09-02 08:10:09 -0700561 FAIL() << "Failed to create program from source";
Austin Kinross137b1512015-06-17 16:14:53 -0700562 }
563
564 // Draw using the program to ensure it works as expected
565 drawWithProgram(program);
566 EXPECT_GL_NO_ERROR();
567
568 // Save the program binary out from this renderer
569 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
570 EXPECT_GL_NO_ERROR();
571 binary.resize(programLength);
572 glGetProgramBinaryOES(program, programLength, &writtenLength, &binaryFormat, binary.data());
573 EXPECT_GL_NO_ERROR();
574
575 // Destroy the first renderer
576 glDeleteProgram(program);
577 destroyEGLWindow(&eglWindow);
578
579 // Create an EGL window with the second renderer
580 eglWindow = createAndInitEGLWindow(secondRenderer);
581 if (eglWindow == nullptr)
582 {
583 FAIL() << "Failed to create EGL window";
584 return;
585 }
586
587 program = glCreateProgram();
588 glProgramBinaryOES(program, binaryFormat, binary.data(), writtenLength);
589
590 GLint linkStatus;
591 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
592 EXPECT_EQ(expectedLinkResult, (linkStatus != 0));
593
594 if (linkStatus != 0)
595 {
596 // If the link was successful, then we should try to draw using the program to ensure it works as expected
597 drawWithProgram(program);
598 EXPECT_GL_NO_ERROR();
599 }
600
601 // Destroy the second renderer
602 glDeleteProgram(program);
603 destroyEGLWindow(&eglWindow);
604}
605
606ANGLE_INSTANTIATE_TEST(ProgramBinariesAcrossPlatforms,
607 // | Save the program | Load the program | Expected
608 // | using these params | using these params | link result
609 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D11(), true ), // Loading + reloading binary should work
610 PlatformsWithLinkResult(ES3_D3D11(), ES3_D3D11(), true ), // Loading + reloading binary should work
611 PlatformsWithLinkResult(ES2_D3D11_FL11_0(), ES2_D3D11_FL9_3(), false ), // Switching feature level shouldn't work
612 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D11_WARP(), false ), // Switching from hardware to software shouldn't work
613 PlatformsWithLinkResult(ES2_D3D11_FL9_3(), ES2_D3D11_FL9_3_WARP(), false ), // Switching from hardware to software shouldn't work for FL9 either
614 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D9(), false ), // Switching from D3D11 to D3D9 shouldn't work
615 PlatformsWithLinkResult(ES2_D3D9(), ES2_D3D11(), false ), // Switching from D3D9 to D3D11 shouldn't work
616 PlatformsWithLinkResult(ES2_D3D11(), ES3_D3D11(), true ) // Switching to newer client version should work
617
618 // TODO: ANGLE issue 523
619 // Compiling a program with client version 3, saving the binary, then loading it with client version 2 should not work
620 // PlatformsWithLinkResult(ES3_D3D11(), ES2_D3D11(), false )
Corentin Wallezf3357ee2015-07-22 14:10:19 -0400621 );