blob: 0d52dc1c95cc6dbd18d68dd00ab1187f7166ddca [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
74 GLuint mProgram;
75 GLuint mBuffer;
76};
77
78// This tests the assumption that float attribs of different size
79// should not internally cause a vertex shader recompile (for conversion).
Jamie Madillfa05f602015-05-07 13:47:11 -040080TEST_P(ProgramBinaryTest, FloatDynamicShaderSize)
Jamie Madill7a29e4a2014-05-02 10:41:48 -040081{
82 glUseProgram(mProgram);
83 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
84
85 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, NULL);
86 glEnableVertexAttribArray(0);
87 glDrawArrays(GL_POINTS, 0, 1);
88
89 GLint programLength;
90 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
91
92 EXPECT_GL_NO_ERROR();
93
94 for (GLsizei size = 1; size <= 3; size++)
95 {
96 glVertexAttribPointer(0, size, GL_FLOAT, GL_FALSE, 8, NULL);
97 glEnableVertexAttribArray(0);
98 glDrawArrays(GL_POINTS, 0, 1);
99
100 GLint newProgramLength;
101 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &newProgramLength);
102 EXPECT_GL_NO_ERROR();
103 EXPECT_EQ(programLength, newProgramLength);
104 }
105}
Brandon Jones993d08d2014-10-10 14:32:57 -0700106
107// This tests the ability to successfully save and load a program binary.
Jamie Madillfa05f602015-05-07 13:47:11 -0400108TEST_P(ProgramBinaryTest, SaveAndLoadBinary)
Brandon Jones993d08d2014-10-10 14:32:57 -0700109{
110 GLint programLength = 0;
111 GLint writtenLength = 0;
112 GLenum binaryFormat = 0;
113
114 glGetProgramiv(mProgram, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
115 EXPECT_GL_NO_ERROR();
116
117 std::vector<uint8_t> binary(programLength);
118 glGetProgramBinaryOES(mProgram, programLength, &writtenLength, &binaryFormat, binary.data());
119 EXPECT_GL_NO_ERROR();
120
121 // The lengths reported by glGetProgramiv and glGetProgramBinaryOES should match
122 EXPECT_EQ(programLength, writtenLength);
123
124 if (writtenLength)
125 {
126 GLuint program2 = glCreateProgram();
127 glProgramBinaryOES(program2, binaryFormat, binary.data(), writtenLength);
128
129 EXPECT_GL_NO_ERROR();
130
131 GLint linkStatus;
132 glGetProgramiv(program2, GL_LINK_STATUS, &linkStatus);
133 if (linkStatus == 0)
134 {
135 GLint infoLogLength;
136 glGetProgramiv(program2, GL_INFO_LOG_LENGTH, &infoLogLength);
137
Geoff Lang981afd72014-11-05 16:30:36 -0500138 if (infoLogLength > 0)
139 {
140 std::vector<GLchar> infoLog(infoLogLength);
Cooper Partin4d61f7e2015-08-12 10:56:50 -0700141 glGetProgramInfoLog(program2, static_cast<GLsizei>(infoLog.size()), NULL,
142 &infoLog[0]);
Geoff Lang981afd72014-11-05 16:30:36 -0500143 FAIL() << "program link failed: " << &infoLog[0];
144 }
145 else
146 {
147 FAIL() << "program link failed.";
148 }
Brandon Jones993d08d2014-10-10 14:32:57 -0700149 }
150 else
151 {
152 glUseProgram(program2);
153 glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
154
155 glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8, NULL);
156 glEnableVertexAttribArray(0);
157 glDrawArrays(GL_POINTS, 0, 1);
158
159 EXPECT_GL_NO_ERROR();
160 }
161
162 glDeleteProgram(program2);
163 }
164}
Jamie Madillfa05f602015-05-07 13:47:11 -0400165
166// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
167ANGLE_INSTANTIATE_TEST(ProgramBinaryTest, ES2_D3D9(), ES2_D3D11());
Austin Kinross137b1512015-06-17 16:14:53 -0700168
169// For the ProgramBinariesAcrossPlatforms tests, we need two sets of params:
170// - a set to save the program binary
171// - a set to load the program binary
172// We combine these into one struct extending PlatformParameters so we can reuse existing ANGLE test macros
173struct PlatformsWithLinkResult : PlatformParameters
174{
175 PlatformsWithLinkResult(PlatformParameters saveParams, PlatformParameters loadParamsIn, bool expectedLinkResultIn)
176 {
177 majorVersion = saveParams.majorVersion;
178 minorVersion = saveParams.minorVersion;
179 eglParameters = saveParams.eglParameters;
180 loadParams = loadParamsIn;
181 expectedLinkResult = expectedLinkResultIn;
182 }
183
184 PlatformParameters loadParams;
185 bool expectedLinkResult;
186};
187
Corentin Wallez33585c72015-09-03 14:41:23 -0400188// Provide a custom gtest parameter name function for PlatformsWithLinkResult
189// to avoid returning the same parameter name twice. Such a conflict would happen
190// between ES2_D3D11_to_ES2D3D11 and ES2_D3D11_to_ES3D3D11 as they were both
191// named ES2_D3D11
192std::ostream &operator<<(std::ostream& stream, const PlatformsWithLinkResult &platform)
193{
194 const PlatformParameters &platform1 = platform;
195 const PlatformParameters &platform2 = platform.loadParams;
196 stream << platform1 << "_to_" << platform2;
197 return stream;
198}
199
Austin Kinross137b1512015-06-17 16:14:53 -0700200class ProgramBinariesAcrossPlatforms : public testing::TestWithParam<PlatformsWithLinkResult>
201{
202 public:
203 void SetUp() override
204 {
205 mOSWindow = CreateOSWindow();
206 bool result = mOSWindow->initialize("ProgramBinariesAcrossRenderersTests", 100, 100);
207
208 if (result == false)
209 {
210 FAIL() << "Failed to create OS window";
211 }
212 }
213
214 EGLWindow *createAndInitEGLWindow(angle::PlatformParameters &param)
215 {
Geoff Lang5ade8452015-09-02 11:00:30 -0400216 EGLWindow *eglWindow =
217 new EGLWindow(param.majorVersion, param.minorVersion, param.eglParameters);
Austin Kinross137b1512015-06-17 16:14:53 -0700218 bool result = eglWindow->initializeGL(mOSWindow);
219 if (result == false)
220 {
221 SafeDelete(eglWindow);
222 eglWindow = nullptr;
223 }
224
225 return eglWindow;
226 }
227
228 void destroyEGLWindow(EGLWindow **eglWindow)
229 {
230 ASSERT(*eglWindow != nullptr);
231 (*eglWindow)->destroyGL();
232 SafeDelete(*eglWindow);
233 *eglWindow = nullptr;
234 }
235
236 GLuint createES2ProgramFromSource()
237 {
238 const std::string testVertexShaderSource = SHADER_SOURCE
239 (
240 attribute highp vec4 position;
241
242 void main(void)
243 {
244 gl_Position = position;
245 }
246 );
247
248 const std::string testFragmentShaderSource = SHADER_SOURCE
249 (
250 void main(void)
251 {
252 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
253 }
254 );
255
256 return CompileProgram(testVertexShaderSource, testFragmentShaderSource);
257 }
258
259 GLuint createES3ProgramFromSource()
260 {
261 const std::string testVertexShaderSource = SHADER_SOURCE
262 ( #version 300 es\n
263 precision highp float;
264 in highp vec4 position;
265
266 void main(void)
267 {
268 gl_Position = position;
269 }
270 );
271
272 const std::string testFragmentShaderSource = SHADER_SOURCE
273 ( #version 300 es \n
274 precision highp float;
275 out vec4 out_FragColor;
276
277 void main(void)
278 {
279 out_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
280 }
281 );
282
283 return CompileProgram(testVertexShaderSource, testFragmentShaderSource);
284 }
285
286 void drawWithProgram(GLuint program)
287 {
288 glClearColor(0, 0, 0, 1);
289 glClear(GL_COLOR_BUFFER_BIT);
290
291 GLint positionLocation = glGetAttribLocation(program, "position");
292
293 glUseProgram(program);
294
295 const GLfloat vertices[] =
296 {
297 -1.0f, 1.0f, 0.5f,
298 -1.0f, -1.0f, 0.5f,
299 1.0f, -1.0f, 0.5f,
300
301 -1.0f, 1.0f, 0.5f,
302 1.0f, -1.0f, 0.5f,
303 1.0f, 1.0f, 0.5f,
304 };
305
306 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
307 glEnableVertexAttribArray(positionLocation);
308
309 glDrawArrays(GL_TRIANGLES, 0, 6);
310
311 glDisableVertexAttribArray(positionLocation);
312 glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
313
314 EXPECT_PIXEL_EQ(mOSWindow->getWidth() / 2, mOSWindow->getHeight() / 2, 255, 0, 0, 255);
315 }
316
317 void TearDown() override
318 {
319 mOSWindow->destroy();
320 SafeDelete(mOSWindow);
321 }
322
323 OSWindow *mOSWindow;
324};
325
326// Tries to create a program binary using one set of platform params, then load it using a different sent of params
327TEST_P(ProgramBinariesAcrossPlatforms, CreateAndReloadBinary)
328{
329 angle::PlatformParameters firstRenderer = GetParam();
330 angle::PlatformParameters secondRenderer = GetParam().loadParams;
331 bool expectedLinkResult = GetParam().expectedLinkResult;
332
333 if (!(IsPlatformAvailable(firstRenderer)))
334 {
335 std::cout << "First renderer not supported, skipping test";
336 return;
337 }
338
339 if (!(IsPlatformAvailable(secondRenderer)))
340 {
341 std::cout << "Second renderer not supported, skipping test";
342 return;
343 }
344
345 EGLWindow *eglWindow = nullptr;
346 std::vector<uint8_t> binary(0);
347 GLuint program = 0;
348
349 GLint programLength = 0;
350 GLint writtenLength = 0;
351 GLenum binaryFormat = 0;
352
353 // Create a EGL window with the first renderer
354 eglWindow = createAndInitEGLWindow(firstRenderer);
355 if (eglWindow == nullptr)
356 {
357 FAIL() << "Failed to create EGL window";
358 return;
359 }
360
361 // If the test is trying to use both the default GPU and WARP, but the default GPU *IS* WARP,
362 // then our expectations for the test results will be invalid.
363 if (firstRenderer.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE &&
364 secondRenderer.eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE)
365 {
366 std::string rendererString = std::string(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
367 std::transform(rendererString.begin(), rendererString.end(), rendererString.begin(), ::tolower);
368
369 auto basicRenderPos = rendererString.find(std::string("microsoft basic render"));
370 auto softwareAdapterPos = rendererString.find(std::string("software adapter"));
371
372 if (basicRenderPos != std::string::npos || softwareAdapterPos != std::string::npos)
373 {
374 // The first renderer is using WARP, even though we didn't explictly request it
375 // We should skip this test
376 std::cout << "Test skipped on when default GPU is WARP." << std::endl;
377 return;
378 }
379 }
380
381 // Create a program
382 if (firstRenderer.majorVersion == 3)
383 {
384 program = createES3ProgramFromSource();
385 }
386 else
387 {
388 program = createES2ProgramFromSource();
389 }
390
391 if (program == 0)
392 {
Austin Kinross137b1512015-06-17 16:14:53 -0700393 destroyEGLWindow(&eglWindow);
Corentin Wallezefb6ac62015-09-02 08:10:09 -0700394 FAIL() << "Failed to create program from source";
Austin Kinross137b1512015-06-17 16:14:53 -0700395 }
396
397 // Draw using the program to ensure it works as expected
398 drawWithProgram(program);
399 EXPECT_GL_NO_ERROR();
400
401 // Save the program binary out from this renderer
402 glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH_OES, &programLength);
403 EXPECT_GL_NO_ERROR();
404 binary.resize(programLength);
405 glGetProgramBinaryOES(program, programLength, &writtenLength, &binaryFormat, binary.data());
406 EXPECT_GL_NO_ERROR();
407
408 // Destroy the first renderer
409 glDeleteProgram(program);
410 destroyEGLWindow(&eglWindow);
411
412 // Create an EGL window with the second renderer
413 eglWindow = createAndInitEGLWindow(secondRenderer);
414 if (eglWindow == nullptr)
415 {
416 FAIL() << "Failed to create EGL window";
417 return;
418 }
419
420 program = glCreateProgram();
421 glProgramBinaryOES(program, binaryFormat, binary.data(), writtenLength);
422
423 GLint linkStatus;
424 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
425 EXPECT_EQ(expectedLinkResult, (linkStatus != 0));
426
427 if (linkStatus != 0)
428 {
429 // If the link was successful, then we should try to draw using the program to ensure it works as expected
430 drawWithProgram(program);
431 EXPECT_GL_NO_ERROR();
432 }
433
434 // Destroy the second renderer
435 glDeleteProgram(program);
436 destroyEGLWindow(&eglWindow);
437}
438
439ANGLE_INSTANTIATE_TEST(ProgramBinariesAcrossPlatforms,
440 // | Save the program | Load the program | Expected
441 // | using these params | using these params | link result
442 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D11(), true ), // Loading + reloading binary should work
443 PlatformsWithLinkResult(ES3_D3D11(), ES3_D3D11(), true ), // Loading + reloading binary should work
444 PlatformsWithLinkResult(ES2_D3D11_FL11_0(), ES2_D3D11_FL9_3(), false ), // Switching feature level shouldn't work
445 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D11_WARP(), false ), // Switching from hardware to software shouldn't work
446 PlatformsWithLinkResult(ES2_D3D11_FL9_3(), ES2_D3D11_FL9_3_WARP(), false ), // Switching from hardware to software shouldn't work for FL9 either
447 PlatformsWithLinkResult(ES2_D3D11(), ES2_D3D9(), false ), // Switching from D3D11 to D3D9 shouldn't work
448 PlatformsWithLinkResult(ES2_D3D9(), ES2_D3D11(), false ), // Switching from D3D9 to D3D11 shouldn't work
449 PlatformsWithLinkResult(ES2_D3D11(), ES3_D3D11(), true ) // Switching to newer client version should work
450
451 // TODO: ANGLE issue 523
452 // Compiling a program with client version 3, saving the binary, then loading it with client version 2 should not work
453 // PlatformsWithLinkResult(ES3_D3D11(), ES2_D3D11(), false )
Corentin Wallezf3357ee2015-07-22 14:10:19 -0400454 );