• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <log/log.h>
16 
17 #include "GfxstreamEnd2EndTests.h"
18 
19 namespace gfxstream {
20 namespace tests {
21 namespace {
22 
23 using testing::Eq;
24 using testing::Gt;
25 using testing::HasSubstr;
26 using testing::IsEmpty;
27 using testing::IsTrue;
28 using testing::Le;
29 using testing::Not;
30 
31 struct PixelR8G8B8A8 {
PixelR8G8B8A8gfxstream::tests::__anonae118ccb0111::PixelR8G8B8A832     PixelR8G8B8A8(uint8_t rr, uint8_t gg, uint8_t bb, uint8_t aa) : r(rr), g(gg), b(bb), a(aa) {}
33 
PixelR8G8B8A8gfxstream::tests::__anonae118ccb0111::PixelR8G8B8A834     PixelR8G8B8A8(int xx, int yy, uint8_t rr, uint8_t gg, uint8_t bb, uint8_t aa)
35         : x(xx), y(yy), r(rr), g(gg), b(bb), a(aa) {}
36 
37     std::optional<int> x;
38     std::optional<int> y;
39 
40     uint8_t r;
41     uint8_t g;
42     uint8_t b;
43     uint8_t a;
44 
ToStringgfxstream::tests::__anonae118ccb0111::PixelR8G8B8A845     std::string ToString() const {
46         std::string ret = std::string("Pixel");
47         if (x) {
48             ret += std::string(" x:") + std::to_string(*x);
49         }
50         if (y) {
51             ret += std::string(" y:") + std::to_string(*y);
52         }
53         ret += std::string(" {");
54         ret += std::string(" r:") + std::to_string(static_cast<int>(r));
55         ret += std::string(" g:") + std::to_string(static_cast<int>(g));
56         ret += std::string(" b:") + std::to_string(static_cast<int>(b));
57         ret += std::string(" a:") + std::to_string(static_cast<int>(a));
58         ret += std::string(" }");
59         return ret;
60     }
61 
PrintTo(const PixelR8G8B8A8 & pixel,std::ostream * os)62     friend void PrintTo(const PixelR8G8B8A8& pixel, std::ostream* os) { *os << pixel.ToString(); }
63 };
64 
65 MATCHER_P4(IsOkWithRGBA, r, g, b, a,
66            std::string(" equals ") + PixelR8G8B8A8(r, g, b, a).ToString()) {
67     const auto& actual = arg;
68 
69     if (actual.ok() && actual.value().r == r && actual.value().g == g && actual.value().b == b &&
70         actual.value().a == a) {
71         return true;
72     }
73 
74     if (actual.ok()) {
75         *result_listener << "actual: " << actual.value().ToString();
76     } else {
77         *result_listener << "actual: {" << " error: " << actual.error() << " };";
78     }
79     return false;
80 }
81 
82 class GfxstreamEnd2EndGlTest : public GfxstreamEnd2EndTest {
83    protected:
GetPixelAt(GLint x,GLint y)84     GlExpected<PixelR8G8B8A8> GetPixelAt(GLint x, GLint y) {
85         if (!mGl) {
86             return android::base::unexpected("GL not available, running with `with_gl = false`?");
87         }
88 
89         GLubyte rgba[4] = {0, 0, 0, 0};
90         mGl->glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
91 
92         if (GLenum error = mGl->glGetError(); error != GL_NO_ERROR) {
93             return android::base::unexpected("Failed to glReadPixels() with error " +
94                                              std::to_string(error));
95         }
96 
97         return PixelR8G8B8A8(x, y, rgba[0], rgba[1], rgba[2], rgba[3]);
98     }
99 
SetUp()100     void SetUp() override {
101         GfxstreamEnd2EndTest::SetUp();
102 
103         SetUpEglContextAndSurface(2, mSurfaceWidth, mSurfaceHeight, &mDisplay, &mContext,
104                                   &mSurface);
105     }
106 
TearDown()107     void TearDown() override {
108         TearDownEglContextAndSurface(mDisplay, mContext, mSurface);
109 
110         GfxstreamEnd2EndTest::TearDown();
111     }
112 
113     int mSurfaceWidth = 32;
114     int mSurfaceHeight = 32;
115     EGLDisplay mDisplay;
116     EGLContext mContext;
117     EGLSurface mSurface;
118 };
119 
TEST_P(GfxstreamEnd2EndGlTest,BasicViewport)120 TEST_P(GfxstreamEnd2EndGlTest, BasicViewport) {
121     GLint viewport[4] = {};
122     mGl->glGetIntegerv(GL_VIEWPORT, viewport);
123 
124     EXPECT_THAT(viewport[0], Eq(0));
125     EXPECT_THAT(viewport[1], Eq(0));
126     EXPECT_THAT(viewport[2], Eq(mSurfaceWidth));
127     EXPECT_THAT(viewport[3], Eq(mSurfaceHeight));
128 }
129 
TEST_P(GfxstreamEnd2EndGlTest,CreateWindowSurface)130 TEST_P(GfxstreamEnd2EndGlTest, CreateWindowSurface) {
131     // clang-format off
132     static const EGLint configAttributes[] = {
133         EGL_SURFACE_TYPE,    EGL_PBUFFER_BIT,
134         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
135         EGL_NONE,
136     };
137     // clang-format on
138 
139     int numConfigs = 0;
140     ASSERT_THAT(mGl->eglChooseConfig(mDisplay, configAttributes, nullptr, 1, &numConfigs),
141                 IsTrue());
142     ASSERT_THAT(numConfigs, Gt(0));
143 
144     EGLConfig config = nullptr;
145     ASSERT_THAT(mGl->eglChooseConfig(mDisplay, configAttributes, &config, 1, &numConfigs),
146                 IsTrue());
147     ASSERT_THAT(config, Not(Eq(nullptr)));
148 
149     // clang-format off
150     static const EGLint contextAttribs[] = {
151         EGL_CONTEXT_CLIENT_VERSION, 3,
152         EGL_NONE,
153     };
154     // clang-format on
155 
156     EGLContext context = mGl->eglCreateContext(mDisplay, config, EGL_NO_CONTEXT, contextAttribs);
157     ASSERT_THAT(context, Not(Eq(EGL_NO_CONTEXT)));
158 
159     constexpr const int width = 32;
160     constexpr const int height = 32;
161 
162     auto anw = mAnwHelper->createNativeWindowForTesting(mGralloc.get(), width, height);
163 
164     EGLSurface surface = mGl->eglCreateWindowSurface(mDisplay, config, anw, nullptr);
165     ASSERT_THAT(surface, Not(Eq(EGL_NO_SURFACE)));
166 
167     ASSERT_THAT(mGl->eglMakeCurrent(mDisplay, surface, surface, context), IsTrue());
168 
169     constexpr const int iterations = 120;
170     for (int i = 0; i < iterations; i++) {
171         mGl->glViewport(0, 0, width, height);
172         mGl->glClearColor(1.0f, 0.0f, static_cast<float>(i) / static_cast<float>(iterations), 1.0f);
173         mGl->glClear(GL_COLOR_BUFFER_BIT);
174         mGl->glFinish();
175         mGl->eglSwapBuffers(mDisplay, surface);
176     }
177 
178     ASSERT_THAT(mGl->eglDestroyContext(mDisplay, context), IsTrue());
179     ASSERT_THAT(mGl->eglDestroySurface(mDisplay, surface), IsTrue());
180 
181     mAnwHelper->release(anw);
182 }
183 
TEST_P(GfxstreamEnd2EndGlTest,SwitchContext)184 TEST_P(GfxstreamEnd2EndGlTest, SwitchContext) {
185     ASSERT_THAT(mGl->eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT),
186                 IsTrue());
187     for (int i = 0; i < 100; i++) {
188         ASSERT_THAT(mGl->eglMakeCurrent(mDisplay, mSurface, mSurface, mContext), IsTrue());
189         ASSERT_THAT(mGl->eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT),
190                     IsTrue());
191     }
192 }
193 
TEST_P(GfxstreamEnd2EndGlTest,MappedMemory)194 TEST_P(GfxstreamEnd2EndGlTest, MappedMemory) {
195     constexpr GLsizei kBufferSize = 64;
196 
197     ScopedGlBuffer buffer(*mGl);
198     mGl->glBindBuffer(GL_ARRAY_BUFFER, buffer);
199     mGl->glBufferData(GL_ARRAY_BUFFER, kBufferSize, 0, GL_DYNAMIC_DRAW);
200 
201     std::vector<uint8_t> bufferData(kBufferSize);
202     for (uint8_t i = 0; i < kBufferSize; ++i) {
203         bufferData[i] = i;
204     }
205 
206     {
207         auto* mappedBufferData = reinterpret_cast<uint8_t*>(
208             mGl->glMapBufferRange(GL_ARRAY_BUFFER, 0, kBufferSize,
209                                   GL_MAP_WRITE_BIT | GL_MAP_FLUSH_EXPLICIT_BIT));
210 
211         for (uint8_t i = 0; i < kBufferSize; ++i) {
212             mappedBufferData[i] = bufferData[i];
213         }
214 
215         mGl->glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, kBufferSize);
216         mGl->glUnmapBuffer(GL_ARRAY_BUFFER);
217     }
218 
219     {
220         auto* mappedBufferData = reinterpret_cast<uint8_t*>(
221             mGl->glMapBufferRange(GL_ARRAY_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT));
222 
223         for (uint8_t i = 0; i < kBufferSize; ++i) {
224             EXPECT_THAT(mappedBufferData[i], Eq(bufferData[i]));
225         }
226 
227         mGl->glUnmapBuffer(GL_ARRAY_BUFFER);
228     }
229 
230     mGl->glBindBuffer(GL_ARRAY_BUFFER, 0);
231 }
232 
TEST_P(GfxstreamEnd2EndGlTest,ContextStrings)233 TEST_P(GfxstreamEnd2EndGlTest, ContextStrings) {
234     EGLDisplay display = mGl->eglGetDisplay(EGL_DEFAULT_DISPLAY);
235     ASSERT_THAT(display, Not(Eq(EGL_NO_DISPLAY)));
236 
237     int versionMajor = 0;
238     int versionMinor = 0;
239     ASSERT_THAT(mGl->eglInitialize(display, &versionMajor, &versionMinor), IsTrue());
240 
241     ASSERT_THAT(mGl->eglBindAPI(EGL_OPENGL_ES_API), IsTrue());
242 
243     // clang-format off
244     static const EGLint configAttributes[] = {
245         EGL_SURFACE_TYPE,    EGL_PBUFFER_BIT,
246         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
247         EGL_NONE,
248     };
249     // clang-format on
250 
251     int numConfigs = 0;
252     ASSERT_THAT(mGl->eglChooseConfig(display, configAttributes, nullptr, 1, &numConfigs), IsTrue());
253     ASSERT_THAT(numConfigs, Gt(0));
254 
255     EGLConfig config = nullptr;
256     ASSERT_THAT(mGl->eglChooseConfig(display, configAttributes, &config, 1, &numConfigs), IsTrue());
257     ASSERT_THAT(config, Not(Eq(nullptr)));
258 
259     // clang-format off
260     static const EGLint gles1ContextAttribs[] = {
261         EGL_CONTEXT_CLIENT_VERSION, 1,
262         EGL_NONE,
263     };
264     // clang-format on
265 
266     EGLContext gles1Context = mGl->eglCreateContext(display, config, EGL_NO_CONTEXT, gles1ContextAttribs);
267     ASSERT_THAT(gles1Context, Not(Eq(EGL_NO_CONTEXT)));
268 
269     // clang-format off
270     static const EGLint gles2ContextAttribs[] = {
271         EGL_CONTEXT_CLIENT_VERSION, 2,
272         EGL_NONE,
273     };
274     // clang-format on
275 
276     EGLContext gles2Context = mGl->eglCreateContext(display, config, EGL_NO_CONTEXT, gles2ContextAttribs);
277     ASSERT_THAT(gles2Context, Not(Eq(EGL_NO_CONTEXT)));
278 
279     constexpr const int width = 32;
280     constexpr const int height = 32;
281 
282     // clang-format off
283     static const EGLint surfaceAttributes[] = {
284         EGL_WIDTH,  width,
285         EGL_HEIGHT, height,
286         EGL_NONE,
287     };
288     // clang-format on
289 
290     EGLSurface surface = mGl->eglCreatePbufferSurface(display, config, surfaceAttributes);
291     ASSERT_THAT(surface, Not(Eq(EGL_NO_SURFACE)));
292 
293     {
294         ASSERT_THAT(mGl->eglMakeCurrent(display, surface, surface, gles2Context), IsTrue());
295         const auto versionString = (const char*)mGl->glGetString(GL_VERSION);
296         const auto extensionString = (const char*)mGl->glGetString(GL_EXTENSIONS);
297         EXPECT_THAT(versionString, HasSubstr("ES 3"));
298         EXPECT_THAT(extensionString, Not(HasSubstr("OES_draw_texture")));
299     }
300     {
301         ASSERT_THAT(mGl->eglMakeCurrent(display, surface, surface, gles1Context), IsTrue());
302         const auto versionString = (const char*)mGl->glGetString(GL_VERSION);
303         const auto extensionString = (const char*)mGl->glGetString(GL_EXTENSIONS);
304         EXPECT_THAT(versionString, HasSubstr("ES-CM"));
305         EXPECT_THAT(extensionString, HasSubstr("OES_draw_texture"));
306     }
307     {
308         ASSERT_THAT(mGl->eglMakeCurrent(display, surface, surface, gles2Context), IsTrue());
309         const auto versionString = (const char*)mGl->glGetString(GL_VERSION);
310         const auto extensionString = (const char*)mGl->glGetString(GL_EXTENSIONS);
311         EXPECT_THAT(versionString, HasSubstr("ES 3"));
312         EXPECT_THAT(extensionString, Not(HasSubstr("OES_draw_texture")));
313     }
314 
315     ASSERT_THAT(mGl->eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT), IsTrue());
316     ASSERT_THAT(mGl->eglDestroyContext(display, gles1Context), IsTrue());
317     ASSERT_THAT(mGl->eglDestroyContext(display, gles2Context), IsTrue());
318     ASSERT_THAT(mGl->eglDestroySurface(display, surface), IsTrue());
319 }
320 
TEST_P(GfxstreamEnd2EndGlTest,FramebufferFetchShader)321 TEST_P(GfxstreamEnd2EndGlTest, FramebufferFetchShader) {
322     const std::string extensionsString = (const char*)mGl->glGetString(GL_EXTENSIONS);
323     ASSERT_THAT(extensionsString, Not(IsEmpty()));
324 
325     const bool supportsFramebufferFetch =
326         extensionsString.find("GL_EXT_shader_framebuffer_fetch") != std::string::npos;
327 
328     const std::string shaderSource = R"(\
329 #version 300 es
330 #extension GL_EXT_shader_framebuffer_fetch : require
331 precision highp float;
332 in vec3 color_varying;
333 out vec4 fragColor;
334 void main() {
335     fragColor = vec4(color_varying, 1.0);
336 }
337     )";
338     auto result = SetUpShader(GL_FRAGMENT_SHADER, shaderSource);
339     if (result.ok()) {
340         ASSERT_THAT(supportsFramebufferFetch, Eq(GL_TRUE));
341     } else {
342         ASSERT_THAT(supportsFramebufferFetch, Eq(GL_FALSE));
343     }
344 }
345 
TEST_P(GfxstreamEnd2EndGlTest,ConstantMatrixShader)346 TEST_P(GfxstreamEnd2EndGlTest, ConstantMatrixShader) {
347     const std::string shaderSource = R"(\
348 #version 300 es
349 precision mediump float;
350 in highp vec4 dEQP_Position;
351 out vec2 out0;
352 
353 void main() {
354     const mat4x2 matA = mat4x2( 2.0,  4.0,   8.0,  16.0,
355                                32.0, 64.0, 128.0, 256.0);
356     const mat4x2 matB = mat4x2(1.0 /  2.0, 1.0 /  4.0, 1.0 /   8.0, 1.0 /  16.0,
357                                1.0 / 32.0, 1.0 / 64.0, 1.0 / 128.0, 1.0 / 256.0);
358     mat4x2 result = matrixCompMult(matA, matB);
359 
360     out0 = result * vec4(1.0, 1.0, 1.0, 1.0);
361     gl_Position = dEQP_Position;
362 }
363     )";
364 
365     auto result = SetUpShader(GL_VERTEX_SHADER, shaderSource);
366     ASSERT_THAT(result, IsOk());
367 }
368 
TEST_P(GfxstreamEnd2EndGlTest,Draw)369 TEST_P(GfxstreamEnd2EndGlTest, Draw) {
370     const std::string vertSource = R"(\
371 #version 300 es
372 precision highp float;
373 
374 layout (location = 0) in vec2 pos;
375 layout (location = 1) in vec3 color;
376 
377 uniform mat4 transform;
378 
379 out vec3 color_varying;
380 
381 void main() {
382     gl_Position = transform * vec4(pos, 0.0, 1.0);
383     color_varying = (transform * vec4(color, 1.0)).xyz;
384 }
385     )";
386 
387     const std::string fragSource = R"(\
388 #version 300 es
389 precision highp float;
390 
391 in vec3 color_varying;
392 
393 out vec4 fragColor;
394 
395 void main() {
396     fragColor = vec4(color_varying, 1.0);
397 }
398     )";
399 
400     ScopedGlProgram program = GL_ASSERT(SetUpProgram(vertSource, fragSource));
401 
402     GLint transformUniformLocation = mGl->glGetUniformLocation(program, "transform");
403     mGl->glEnableVertexAttribArray(0);
404     mGl->glEnableVertexAttribArray(1);
405 
406     struct VertexAttributes {
407         float position[2];
408         float color[3];
409     };
410     const VertexAttributes vertexAttrs[] = {
411         // clang-format off
412         { { -0.5f, -0.5f,}, { 0.2, 0.1, 0.9, }, },
413         { {  0.5f, -0.5f,}, { 0.8, 0.3, 0.1, }, },
414         { {  0.0f,  0.5f,}, { 0.1, 0.9, 0.6, }, },
415         // clang-format on
416     };
417 
418     ScopedGlBuffer buffer(*mGl);
419     mGl->glBindBuffer(GL_ARRAY_BUFFER, buffer);
420     mGl->glBufferData(GL_ARRAY_BUFFER, sizeof(vertexAttrs), vertexAttrs, GL_STATIC_DRAW);
421 
422     mGl->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexAttributes), 0);
423     mGl->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexAttributes), (GLvoid*)offsetof(VertexAttributes, color));
424 
425     mGl->glUseProgram(program);
426 
427     mGl->glViewport(0, 0, 1, 1);
428 
429     mGl->glClearColor(0.2f, 0.2f, 0.3f, 0.0f);
430     mGl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
431 
432     const float matrix[16] = {
433         // clang-format off
434         1.0f, 0.0f, 0.0f, 0.0f,
435         0.0f, 1.0f, 0.0f, 0.0f,
436         0.0f, 0.0f, 1.0f, 0.0f,
437         0.0f, 0.0f, 0.0f, 1.0f,
438         // clang-format on
439     };
440 
441     constexpr uint32_t kDrawIterations = 200;
442     for (uint32_t i = 0; i < kDrawIterations; i++) {
443         mGl->glUniformMatrix4fv(transformUniformLocation, 1, GL_FALSE, matrix);
444         mGl->glBindBuffer(GL_ARRAY_BUFFER, buffer);
445         mGl->glDrawArrays(GL_TRIANGLES, 0, 3);
446     }
447 
448     mGl->glFinish();
449     mGl->glBindBuffer(GL_ARRAY_BUFFER, 0);
450     mGl->glUseProgram(0);
451 }
452 
TEST_P(GfxstreamEnd2EndGlTest,ProgramBinaryWithAHB)453 TEST_P(GfxstreamEnd2EndGlTest, ProgramBinaryWithAHB) {
454     const uint32_t width = 2;
455     const uint32_t height = 2;
456     auto ahb =
457         GL_ASSERT(ScopedAHardwareBuffer::Allocate(*mGralloc, width, height, GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM));
458 
459     {
460         uint8_t* mapped = GL_ASSERT(ahb.Lock());
461         uint32_t pos = 0;
462         for (uint32_t h = 0; h < height; h++) {
463             for (uint32_t w = 0; w < width; w++) {
464                 mapped[pos++] = 0;
465                 mapped[pos++] = 0;
466                 mapped[pos++] = 128;
467                 mapped[pos++] = 255;
468             }
469         }
470         ahb.Unlock();
471     }
472 
473     const EGLint ahbImageAttribs[] = {
474         // clang-format off
475         EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
476         EGL_NONE,
477         // clang-format on
478     };
479     EGLImageKHR ahbImage = mGl->eglCreateImageKHR(mDisplay, EGL_NO_CONTEXT,
480                                                   EGL_NATIVE_BUFFER_ANDROID, ahb, ahbImageAttribs);
481     ASSERT_THAT(ahbImage, Not(Eq(EGL_NO_IMAGE_KHR)));
482 
483     ScopedGlTexture ahbTexture(*mGl);
484     mGl->glBindTexture(GL_TEXTURE_EXTERNAL_OES, ahbTexture);
485     mGl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
486     mGl->glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
487     mGl->glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, ahbImage);
488     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
489 
490     GLenum programBinaryFormat = GL_NONE;
491     std::vector<uint8_t> programBinaryData;
492     {
493         const std::string vertSource = R"(\
494             #version 300 es
495 
496             layout (location = 0) in vec2 pos;
497             layout (location = 1) in vec2 tex;
498 
499             out vec2 vTex;
500 
501             void main() {
502                 gl_Position = vec4(pos, 0.0, 1.0);
503                 vTex = tex;
504             })";
505 
506         const std::string fragSource = R"(\
507             #version 300 es
508 
509             precision highp float;
510 
511             uniform float uMultiplier;
512             uniform sampler2D uTexture;
513 
514             in vec2 vTex;
515 
516             out vec4 oColor;
517 
518             void main() {
519                 oColor = texture(uTexture, vTex) * uMultiplier;
520             })";
521 
522         ScopedGlProgram program = GL_ASSERT(SetUpProgram(vertSource, fragSource));
523 
524         GLint programBinaryLength = 0;
525         mGl->glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinaryLength);
526         ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
527 
528         programBinaryData.resize(programBinaryLength);
529 
530         GLint readProgramBinaryLength = 0;
531         mGl->glGetProgramBinary(program, programBinaryLength, &readProgramBinaryLength,
532                                 &programBinaryFormat, programBinaryData.data());
533         ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
534         ASSERT_THAT(programBinaryLength, Eq(readProgramBinaryLength));
535     }
536 
537     ScopedGlProgram program = GL_ASSERT(SetUpProgram(programBinaryFormat, programBinaryData));
538     ASSERT_THAT(program, Not(Eq(0)));
539 
540     GLint textureUniformLoc = mGl->glGetUniformLocation(program, "uTexture");
541     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
542     ASSERT_THAT(textureUniformLoc, Not(Eq(-1)));
543 
544     GLint multiplierUniformLoc = mGl->glGetUniformLocation(program, "uMultiplier");
545     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
546     ASSERT_THAT(multiplierUniformLoc, Not(Eq(-1)));
547 
548     const GLsizei kFramebufferWidth = 4;
549     const GLsizei kFramebufferHeight = 4;
550     ScopedGlFramebuffer framebuffer(*mGl);
551     mGl->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
552     ScopedGlTexture framebufferTexture(*mGl);
553     mGl->glBindTexture(GL_TEXTURE_2D, framebufferTexture);
554     mGl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kFramebufferWidth, kFramebufferHeight, 0, GL_RGBA,
555                       GL_UNSIGNED_BYTE, nullptr);
556     mGl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
557                                 framebufferTexture, 0);
558     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
559     ASSERT_THAT(mGl->glCheckFramebufferStatus(GL_FRAMEBUFFER), Eq(GL_FRAMEBUFFER_COMPLETE));
560     mGl->glBindTexture(GL_TEXTURE_2D, 0);
561     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
562 
563     struct VertexAttributes {
564         float pos[2];
565         float tex[2];
566     };
567     const VertexAttributes vertexAttrs[] = {
568         // clang-format off
569         { { -1.0f, -1.0f,}, { 0.0f, 0.0f }, },
570         { {  3.0f, -1.0f,}, { 2.0f, 0.0f }, },
571         { { -1.0f,  3.0f,}, { 0.0f, 2.0f }, },
572         // clang-format on
573     };
574     ScopedGlBuffer buffer(*mGl);
575     mGl->glBindBuffer(GL_ARRAY_BUFFER, buffer);
576     mGl->glBufferData(GL_ARRAY_BUFFER, sizeof(vertexAttrs), vertexAttrs, GL_STATIC_DRAW);
577 
578     mGl->glUseProgram(program);
579     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
580 
581     mGl->glBindBuffer(GL_ARRAY_BUFFER, buffer);
582     mGl->glEnableVertexAttribArray(0);
583     mGl->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexAttributes),
584                                (GLvoid*)offsetof(VertexAttributes, pos));
585     mGl->glEnableVertexAttribArray(1);
586     mGl->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(VertexAttributes),
587                                (GLvoid*)offsetof(VertexAttributes, tex));
588     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
589 
590     mGl->glActiveTexture(GL_TEXTURE0);
591     mGl->glBindTexture(GL_TEXTURE_2D, ahbTexture);
592     mGl->glUniform1i(textureUniformLoc, 0);
593     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
594 
595     mGl->glUniform1f(multiplierUniformLoc, 2.0f);
596     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
597 
598     mGl->glDrawArrays(GL_TRIANGLES, 0, 3);
599     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
600 
601     for (int x = 0; x < kFramebufferWidth; x++) {
602         for (int y = 0; y < kFramebufferHeight; y++) {
603             EXPECT_THAT(GetPixelAt(x, y), IsOkWithRGBA(0, 0, 255, 255));
604         }
605     }
606 
607     mGl->glBindFramebuffer(GL_FRAMEBUFFER, 0);
608 }
609 
TEST_P(GfxstreamEnd2EndGlTest,ProgramBinaryWithTexture)610 TEST_P(GfxstreamEnd2EndGlTest, ProgramBinaryWithTexture) {
611     const GLsizei kTextureWidth = 2;
612     const GLsizei kTextureHeight = 2;
613     const GLubyte kTextureData[16] = {
614         // clang-format off
615         0, 0, 128, 255,   0, 0, 128, 255,
616 
617         0, 0, 128, 255,   0, 0, 128, 255,
618         // clang-format on
619     };
620     ScopedGlTexture texture(*mGl);
621     mGl->glBindTexture(GL_TEXTURE_2D, texture);
622     mGl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
623     mGl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
624     mGl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0, GL_RGBA,
625                       GL_UNSIGNED_BYTE, kTextureData);
626     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
627 
628     GLenum programBinaryFormat = GL_NONE;
629     std::vector<uint8_t> programBinaryData;
630     {
631         const std::string vertSource = R"(\
632             #version 300 es
633 
634             layout (location = 0) in vec2 pos;
635             layout (location = 1) in vec2 tex;
636 
637             out vec2 vTex;
638 
639             void main() {
640                 gl_Position = vec4(pos, 0.0, 1.0);
641                 vTex = tex;
642             })";
643 
644         const std::string fragSource = R"(\
645             #version 300 es
646 
647             precision highp float;
648 
649             uniform float uMultiplier;
650             uniform sampler2D uTexture;
651 
652             in vec2 vTex;
653 
654             out vec4 oColor;
655 
656             void main() {
657                 oColor = texture(uTexture, vTex) * uMultiplier;
658             })";
659 
660         ScopedGlProgram program = GL_ASSERT(SetUpProgram(vertSource, fragSource));
661 
662         GLint programBinaryLength = 0;
663         mGl->glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinaryLength);
664         ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
665 
666         programBinaryData.resize(programBinaryLength);
667 
668         GLint readProgramBinaryLength = 0;
669         mGl->glGetProgramBinary(program, programBinaryLength, &readProgramBinaryLength,
670                                 &programBinaryFormat, programBinaryData.data());
671         ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
672         ASSERT_THAT(programBinaryLength, Eq(readProgramBinaryLength));
673     }
674 
675     ScopedGlProgram program = GL_ASSERT(SetUpProgram(programBinaryFormat, programBinaryData));
676     ASSERT_THAT(program, Not(Eq(0)));
677 
678     GLint textureUniformLoc = mGl->glGetUniformLocation(program, "uTexture");
679     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
680     ASSERT_THAT(textureUniformLoc, Not(Eq(-1)));
681 
682     GLint multiplierUniformLoc = mGl->glGetUniformLocation(program, "uMultiplier");
683     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
684     ASSERT_THAT(multiplierUniformLoc, Not(Eq(-1)));
685 
686     const GLsizei kFramebufferWidth = 4;
687     const GLsizei kFramebufferHeight = 4;
688     ScopedGlFramebuffer framebuffer(*mGl);
689     mGl->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
690     ScopedGlTexture framebufferTexture(*mGl);
691     mGl->glBindTexture(GL_TEXTURE_2D, framebufferTexture);
692     mGl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kFramebufferWidth, kFramebufferHeight, 0, GL_RGBA,
693                       GL_UNSIGNED_BYTE, nullptr);
694     mGl->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
695                                 framebufferTexture, 0);
696     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
697     ASSERT_THAT(mGl->glCheckFramebufferStatus(GL_FRAMEBUFFER), Eq(GL_FRAMEBUFFER_COMPLETE));
698     mGl->glBindTexture(GL_TEXTURE_2D, 0);
699     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
700 
701     struct VertexAttributes {
702         float pos[2];
703         float tex[2];
704     };
705     const VertexAttributes vertexAttrs[] = {
706         // clang-format off
707         { { -1.0f, -1.0f,}, { 0.0f, 0.0f }, },
708         { {  3.0f, -1.0f,}, { 2.0f, 0.0f }, },
709         { { -1.0f,  3.0f,}, { 0.0f, 2.0f }, },
710         // clang-format on
711     };
712     ScopedGlBuffer buffer(*mGl);
713     mGl->glBindBuffer(GL_ARRAY_BUFFER, buffer);
714     mGl->glBufferData(GL_ARRAY_BUFFER, sizeof(vertexAttrs), vertexAttrs, GL_STATIC_DRAW);
715 
716     mGl->glUseProgram(program);
717     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
718 
719     mGl->glBindBuffer(GL_ARRAY_BUFFER, buffer);
720     mGl->glEnableVertexAttribArray(0);
721     mGl->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(VertexAttributes),
722                                (GLvoid*)offsetof(VertexAttributes, pos));
723     mGl->glEnableVertexAttribArray(1);
724     mGl->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(VertexAttributes),
725                                (GLvoid*)offsetof(VertexAttributes, tex));
726     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
727 
728     mGl->glActiveTexture(GL_TEXTURE0);
729     mGl->glBindTexture(GL_TEXTURE_2D, texture);
730     mGl->glUniform1i(textureUniformLoc, 0);
731     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
732 
733     mGl->glUniform1f(multiplierUniformLoc, 2.0f);
734     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
735 
736     mGl->glDrawArrays(GL_TRIANGLES, 0, 3);
737     ASSERT_THAT(mGl->glGetError(), Eq(GL_NO_ERROR));
738 
739     for (int x = 0; x < kFramebufferWidth; x++) {
740         for (int y = 0; y < kFramebufferHeight; y++) {
741             EXPECT_THAT(GetPixelAt(x, y), IsOkWithRGBA(0, 0, 255, 255));
742         }
743     }
744 
745     mGl->glBindFramebuffer(GL_FRAMEBUFFER, 0);
746 }
747 
748 INSTANTIATE_TEST_CASE_P(GfxstreamEnd2EndTests, GfxstreamEnd2EndGlTest,
749                         ::testing::ValuesIn({
750                             TestParams{
751                                 .with_gl = true,
752                                 .with_vk = false,
753                             },
754                             TestParams{
755                                 .with_gl = true,
756                                 .with_vk = true,
757                             },
758                         }),
759                         &GetTestName);
760 
761 }  // namespace
762 }  // namespace tests
763 }  // namespace gfxstream
764