| // Copyright (C) 2018 The Android Open Source Project | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include <gtest/gtest.h> | |
| #include "GLTestUtils.h" | |
| #include "OpenGLTestContext.h" | |
| #include "TextureDraw.h" | |
| namespace gfxstream { | |
| namespace gl { | |
| namespace { | |
| void TestTextureDrawBasic(const GLESv2Dispatch* gl, GLenum internalformat, | |
| GLenum format, bool should_work) { | |
| GLint viewport[4] = {}; | |
| gl->glGetIntegerv(GL_VIEWPORT, viewport); | |
| EXPECT_EQ(0, viewport[0]); | |
| EXPECT_EQ(0, viewport[1]); | |
| const int width = viewport[2]; | |
| const int height = viewport[3]; | |
| const GLenum type = GL_UNSIGNED_BYTE; | |
| const int bpp = 4; | |
| const int bytes = width * height * bpp; | |
| GLuint textureToDraw; | |
| gl->glGenTextures(1, &textureToDraw); | |
| gl->glActiveTexture(GL_TEXTURE0); | |
| gl->glBindTexture(GL_TEXTURE_2D, textureToDraw); | |
| gl->glPixelStorei(GL_PACK_ALIGNMENT, 1); | |
| gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |
| gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| std::vector<unsigned char> pixels(bytes); | |
| for (int i = 0; i < height; i++) { | |
| for (int j = 0; j < width; j++) { | |
| pixels[i * width * bpp + j * bpp + 0] = (0xaa + i) % 0x100; | |
| pixels[i * width * bpp + j * bpp + 1] = (0x00 + j) % 0x100; | |
| pixels[i * width * bpp + j * bpp + 2] = (0x11 + i) % 0x100; | |
| // Use 0xff for alpha blending. | |
| pixels[i * width * bpp + j * bpp + 3] = 0xff; | |
| } | |
| } | |
| GLenum err = gl->glGetError(); | |
| EXPECT_EQ(GL_NO_ERROR, err); | |
| gl->glTexImage2D(GL_TEXTURE_2D, 0, internalformat, width, height, 0, | |
| format, type, pixels.data()); | |
| err = gl->glGetError(); | |
| if (should_work) { | |
| EXPECT_EQ(GL_NO_ERROR, err); | |
| } else { | |
| EXPECT_NE(GL_NO_ERROR, err); | |
| return; | |
| } | |
| gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| GLint fbStatus = gl->glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
| EXPECT_EQ((GLint)GL_FRAMEBUFFER_COMPLETE, fbStatus); | |
| TextureDraw textureDraw; | |
| textureDraw.draw(textureToDraw, 0, 0, 0); | |
| std::vector<unsigned char> pixelsOut(bytes, 0xff); | |
| gl->glReadPixels(0, 0, width, height, format, type, pixelsOut.data()); | |
| // Check that the texture is drawn upside down (because that's what SurfaceFlinger wants) | |
| for (int i = 0; i < height; i++) { | |
| size_t rowBytes = width * bpp; | |
| EXPECT_TRUE(RowMatches(i, width * bpp, | |
| pixels.data() + i * rowBytes, | |
| pixelsOut.data() + (height - i - 1) * rowBytes)); | |
| } | |
| } | |
| void TestTextureDrawLayer(const GLESv2Dispatch* gl) { | |
| GLint viewport[4] = {}; | |
| gl->glGetIntegerv(GL_VIEWPORT, viewport); | |
| EXPECT_EQ(0, viewport[0]); | |
| EXPECT_EQ(0, viewport[1]); | |
| const int width = viewport[2]; | |
| const int height = viewport[3]; | |
| const GLenum type = GL_UNSIGNED_BYTE; | |
| const int bpp = 4; | |
| const int bytes = width * height * bpp; | |
| GLuint textureToDraw; | |
| gl->glGenTextures(1, &textureToDraw); | |
| gl->glActiveTexture(GL_TEXTURE0); | |
| gl->glBindTexture(GL_TEXTURE_2D, textureToDraw); | |
| gl->glPixelStorei(GL_PACK_ALIGNMENT, 1); | |
| gl->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); | |
| gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
| std::vector<unsigned char> pixels(bytes); | |
| for (int i = 0; i < height; i++) { | |
| for (int j = 0; j < width; j++) { | |
| pixels[i * width * bpp + j * bpp + 0] = 0xff; | |
| pixels[i * width * bpp + j * bpp + 1] = 0x0; | |
| pixels[i * width * bpp + j * bpp + 2] = 0x0; | |
| pixels[i * width * bpp + j * bpp + 3] = 0xff; | |
| } | |
| } | |
| GLenum err = gl->glGetError(); | |
| EXPECT_EQ(GL_NO_ERROR, err); | |
| gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, | |
| GL_RGBA, type, pixels.data()); | |
| err = gl->glGetError(); | |
| EXPECT_EQ(GL_NO_ERROR, err); | |
| gl->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
| GLint fbStatus = gl->glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
| EXPECT_EQ((GLint)GL_FRAMEBUFFER_COMPLETE, fbStatus); | |
| TextureDraw textureDraw; | |
| // Test HWC2_COMPOSITION_SOLID_COLOR mode, red color | |
| ComposeLayer l = {0, | |
| HWC2_COMPOSITION_SOLID_COLOR, | |
| {0, 0, width, height}, | |
| {0.0, 0.0, (float)width, (float)height}, | |
| HWC2_BLEND_MODE_NONE, | |
| 1.0, | |
| {255, 0, 0, 255}, | |
| (hwc_transform_t)0}; | |
| textureDraw.prepareForDrawLayer(); | |
| textureDraw.drawLayer(l, width, height, width, height, textureToDraw); | |
| std::vector<unsigned char> pixelsOut(bytes, 0xff); | |
| gl->glReadPixels(0, 0, width, height, GL_RGBA, type, pixelsOut.data()); | |
| EXPECT_TRUE(ImageMatches(width, height, bpp, width, | |
| pixels.data(), pixelsOut.data())); | |
| // Test HWC2_COMPOSITION_DEVICE mode, blue texture | |
| for (int i = 0; i < height; i++) { | |
| for (int j = 0; j < width; j++) { | |
| pixels[i * width * bpp + j * bpp + 0] = 0x0; | |
| pixels[i * width * bpp + j * bpp + 1] = 0x0; | |
| pixels[i * width * bpp + j * bpp + 2] = 0xff; | |
| pixels[i * width * bpp + j * bpp + 3] = 0xff; | |
| } | |
| } | |
| err = gl->glGetError(); | |
| EXPECT_EQ(GL_NO_ERROR, err); | |
| gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, | |
| GL_RGBA, type, pixels.data()); | |
| l.composeMode = HWC2_COMPOSITION_DEVICE; | |
| textureDraw.drawLayer(l, width, height, width, height, textureToDraw); | |
| gl->glReadPixels(0, 0, width, height, GL_RGBA, type, pixelsOut.data()); | |
| EXPECT_TRUE(ImageMatches(width, height, bpp, width, | |
| pixels.data(), pixelsOut.data())); | |
| // Test composing 2 layers, layer1 draws blue to the upper half frame; | |
| // layer2 draws the bottom half of the texture to the bottom half frame | |
| ComposeLayer l1 = {0, | |
| HWC2_COMPOSITION_SOLID_COLOR, | |
| {0, 0, width, height/2}, | |
| {0.0, 0.0, (float)width, (float)height/2}, | |
| HWC2_BLEND_MODE_NONE, | |
| 1.0, | |
| {0, 0, 255, 255}, | |
| (hwc_transform_t)0}; | |
| ComposeLayer l2 = {0, | |
| HWC2_COMPOSITION_DEVICE, | |
| {0, height/2, width, height}, | |
| {0.0, (float)height/2, (float)width, (float)height}, | |
| HWC2_BLEND_MODE_NONE, | |
| 1.0, | |
| {0, 0, 0, 0}, | |
| (hwc_transform_t)0}; | |
| for (int i = 0; i < height; i++) { | |
| for (int j = 0; j < width; j++) { | |
| // texture bottom half green | |
| if (i >= height/2) { | |
| pixels[i * width * bpp + j * bpp + 0] = 0x0; | |
| pixels[i * width * bpp + j * bpp + 2] = 0xff; | |
| } | |
| } | |
| } | |
| gl->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, | |
| GL_RGBA, type, pixels.data()); | |
| textureDraw.drawLayer(l1, width, height, width, height, textureToDraw); | |
| textureDraw.drawLayer(l2, width, height, width, height, textureToDraw); | |
| gl->glReadPixels(0, 0, width, height, GL_RGBA, type, pixelsOut.data()); | |
| EXPECT_TRUE(ImageMatches(width, height, bpp, width, | |
| pixels.data(), pixelsOut.data())); | |
| } | |
| #define GL_BGRA_EXT 0x80E1 | |
| TEST_F(GLTest, TextureDrawBasic) { | |
| TestTextureDrawBasic(gl, GL_RGBA, GL_RGBA, true); | |
| // Assumes BGRA is supported | |
| // Note: On NVIDIA EGL, the format mismatch with RGBA cauases a failure. | |
| // TestTextureDrawBasic(gl, GL_BGRA_EXT, GL_BGRA_EXT, true); | |
| // TestTextureDrawBasic(gl, GL_RGBA, GL_BGRA_EXT, false); | |
| TestTextureDrawLayer(gl); | |
| } | |
| } // namespace | |
| } // namespace gl | |
| } // namespace gfxstream |