| // 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 "aemu/base/files/PathUtils.h" |
| #include "aemu/base/files/StdioStream.h" |
| #include "aemu/base/GLObjectCounter.h" |
| #include "aemu/base/system/System.h" |
| #include "aemu/base/testing/TestSystem.h" |
| #include "host-common/GraphicsAgentFactory.h" |
| #include "host-common/multi_display_agent.h" |
| #include "host-common/testing/MockGraphicsAgentFactory.h" |
| #include "host-common/window_agent.h" |
| #include "host-common/MultiDisplay.h" |
| #include "host-common/opengl/misc.h" |
| #include "snapshot/TextureLoader.h" |
| #include "snapshot/TextureSaver.h" |
| |
| #include "GLSnapshotTesting.h" |
| #include "GLTestUtils.h" |
| #include "Standalone.h" |
| |
| #include <gtest/gtest.h> |
| #include <memory> |
| |
| #ifdef _MSC_VER |
| #include "aemu/base/msvc.h" |
| #else |
| #include <sys/time.h> |
| #endif |
| |
| #ifdef __linux__ |
| #include "X11TestingSupport.h" |
| #endif |
| |
| namespace gfxstream { |
| namespace { |
| |
| using android::base::StdioStream; |
| using android::snapshot::TextureLoader; |
| using android::snapshot::TextureSaver; |
| using gl::EGLDispatch; |
| using gl::EmulatedEglConfigList; |
| using gl::GLESApi_3_0; |
| using gl::LazyLoadedEGLDispatch; |
| using gl::LazyLoadedGLESv2Dispatch; |
| |
| class FrameBufferTest : public ::testing::Test { |
| public: |
| FrameBufferTest() = default; |
| |
| protected: |
| |
| static void SetUpTestSuite() { |
| android::emulation::injectGraphicsAgents( |
| android::emulation::MockGraphicsAgentFactory()); |
| } |
| |
| static void TearDownTestSuite() { } |
| |
| virtual void SetUp() override { |
| // setupStandaloneLibrarySearchPaths(); |
| emugl::setGLObjectCounter(android::base::GLObjectCounter::get()); |
| emugl::set_emugl_window_operations(*getGraphicsAgents()->emu); |
| emugl::set_emugl_multi_display_operations(*getGraphicsAgents()->multi_display); |
| const EGLDispatch* egl = LazyLoadedEGLDispatch::get(); |
| ASSERT_NE(nullptr, egl); |
| ASSERT_NE(nullptr, LazyLoadedGLESv2Dispatch::get()); |
| |
| bool useHostGpu = shouldUseHostGpu(); |
| mWindow = createOrGetTestWindow(mXOffset, mYOffset, mWidth, mHeight); |
| mUseSubWindow = mWindow != nullptr; |
| |
| if (mUseSubWindow) { |
| ASSERT_NE(nullptr, mWindow->getFramebufferNativeWindow()); |
| |
| EXPECT_TRUE( |
| FrameBuffer::initialize( |
| mWidth, mHeight, {}, |
| mUseSubWindow, |
| !useHostGpu /* egl2egl */)); |
| mFb = FrameBuffer::getFB(); |
| EXPECT_NE(nullptr, mFb); |
| |
| mFb->setupSubWindow( |
| (FBNativeWindowType)(uintptr_t) |
| mWindow->getFramebufferNativeWindow(), |
| 0, 0, |
| mWidth, mHeight, mWidth, mHeight, |
| mWindow->getDevicePixelRatio(), 0, false, false); |
| mWindow->messageLoop(); |
| } else { |
| EXPECT_TRUE( |
| FrameBuffer::initialize( |
| mWidth, mHeight, {}, |
| mUseSubWindow, |
| !useHostGpu /* egl2egl */)); |
| mFb = FrameBuffer::getFB(); |
| ASSERT_NE(nullptr, mFb); |
| } |
| EXPECT_EQ(EGL_SUCCESS, egl->eglGetError()); |
| |
| mRenderThreadInfo = new RenderThreadInfo(); |
| mRenderThreadInfo->initGl(); |
| |
| // Snapshots |
| mTestSystem.getTempRoot()->makeSubDir("Snapshots"); |
| mSnapshotPath = mTestSystem.getTempRoot()->makeSubPath("Snapshots"); |
| mTimeStamp = std::to_string(android::base::getUnixTimeUs()); |
| mSnapshotFile = android::base::pj({mSnapshotPath, std::string("snapshot_") + mTimeStamp + ".snap"}); |
| mTextureFile = android::base::pj({mSnapshotPath, std::string("textures_") + mTimeStamp + ".stex"}); |
| } |
| |
| virtual void TearDown() override { |
| FrameBuffer::finalize(); |
| mFb = nullptr; |
| |
| delete mRenderThreadInfo; |
| EXPECT_EQ(EGL_SUCCESS, LazyLoadedEGLDispatch::get()->eglGetError()) |
| << "FrameBufferTest TearDown found EGL error"; |
| } |
| |
| void saveSnapshot() { |
| std::unique_ptr<StdioStream> m_stream(new StdioStream( |
| android_fopen(mSnapshotFile.c_str(), "wb"), StdioStream::kOwner)); |
| std::shared_ptr<TextureSaver> m_texture_saver(new TextureSaver(StdioStream( |
| android_fopen(mTextureFile.c_str(), "wb"), StdioStream::kOwner))); |
| mFb->onSave(m_stream.get(), m_texture_saver); |
| |
| m_stream->close(); |
| m_texture_saver->done(); |
| } |
| |
| void loadSnapshot() { |
| // unbind so load will destroy previous objects |
| mFb->bindContext(0, 0, 0); |
| |
| std::unique_ptr<StdioStream> m_stream(new StdioStream( |
| android_fopen(mSnapshotFile.c_str(), "rb"), StdioStream::kOwner)); |
| std::shared_ptr<TextureLoader> m_texture_loader( |
| new TextureLoader(StdioStream(android_fopen(mTextureFile.c_str(), "rb"), |
| StdioStream::kOwner))); |
| mFb->onLoad(m_stream.get(), m_texture_loader); |
| m_stream->close(); |
| m_texture_loader->join(); |
| } |
| |
| bool mUseSubWindow = false; |
| OSWindow* mWindow = nullptr; |
| FrameBuffer* mFb = nullptr; |
| RenderThreadInfo* mRenderThreadInfo = nullptr; |
| |
| int mWidth = 256; |
| int mHeight = 256; |
| int mXOffset= 400; |
| int mYOffset= 400; |
| |
| android::base::TestSystem mTestSystem; |
| std::string mSnapshotPath; |
| std::string mTimeStamp; |
| std::string mSnapshotFile; |
| std::string mTextureFile; |
| }; |
| |
| // Tests that framebuffer initialization and finalization works. |
| TEST_F(FrameBufferTest, FrameBufferBasic) { |
| } |
| |
| // Tests the creation of a single color buffer for the framebuffer. |
| TEST_F(FrameBufferTest, CreateColorBuffer) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| // FramBuffer::finalize handles color buffer destruction here |
| } |
| |
| // Tests both creation and closing a color buffer. |
| TEST_F(FrameBufferTest, CreateCloseColorBuffer) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| mFb->closeColorBuffer(handle); |
| } |
| |
| // Tests create, open, and close color buffer. |
| TEST_F(FrameBufferTest, CreateOpenCloseColorBuffer) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle)); |
| mFb->closeColorBuffer(handle); |
| } |
| |
| // Tests that the color buffer can be update with a test pattern and that |
| // the test pattern can be read back from the color buffer. |
| TEST_F(FrameBufferTest, CreateOpenUpdateCloseColorBuffer) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle)); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| TestTexture forRead = createTestTextureRGBA8888SingleColor(mWidth, mHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| mFb->readColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| TEST_F(FrameBufferTest, CreateOpenUpdateCloseColorBuffer_ReadYUV420) { |
| HandleType handle = mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, |
| FRAMEWORK_FORMAT_YUV_420_888); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle)); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, |
| GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| TestTexture forRead = createTestPatternRGBA8888(mWidth, mHeight); |
| memset(forRead.data(), 0x0, mWidth * mHeight * 3 / 2); |
| mFb->readColorBufferYUV(handle, 0, 0, mWidth, mHeight, forRead.data(), |
| mWidth * mHeight * 3 / 2); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), |
| forRead.data())); |
| memset(forRead.data(), 0xff, mWidth * mHeight * 3 / 2); |
| mFb->readColorBufferYUV(handle, 0, 0, mWidth, mHeight, forRead.data(), |
| mWidth * mHeight * 3 / 2); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), |
| forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| TEST_F(FrameBufferTest, CreateOpenUpdateCloseColorBuffer_ReadNV12) { |
| HandleType handle = mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, |
| FRAMEWORK_FORMAT_NV12); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle)); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, |
| GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| TestTexture forRead = createTestPatternRGBA8888(mWidth, mHeight); |
| memset(forRead.data(), 0x0, mWidth * mHeight * 3 / 2); |
| mFb->readColorBufferYUV(handle, 0, 0, mWidth, mHeight, forRead.data(), |
| mWidth * mHeight * 3 / 2); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), |
| forRead.data())); |
| memset(forRead.data(), 0xff, mWidth * mHeight * 3 / 2); |
| mFb->readColorBufferYUV(handle, 0, 0, mWidth, mHeight, forRead.data(), |
| mWidth * mHeight * 3 / 2); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), |
| forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| TEST_F(FrameBufferTest, CreateOpenUpdateCloseColorBuffer_ReadNV12TOYUV420) { |
| // nv12 |
| mWidth = 8; |
| mHeight = 8; |
| HandleType handle_nv12 = mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, |
| FRAMEWORK_FORMAT_NV12); |
| EXPECT_NE(0, handle_nv12); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle_nv12)); |
| |
| uint8_t forUpdate[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, |
| 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3}; |
| |
| uint8_t golden[] = { |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, |
| 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, |
| 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, |
| }; |
| |
| mFb->updateColorBuffer(handle_nv12, 0, 0, mWidth, mHeight, GL_RGBA, |
| GL_UNSIGNED_BYTE, forUpdate); |
| |
| // yuv420 |
| HandleType handle_yuv420 = mFb->createColorBuffer( |
| mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_YUV_420_888); |
| EXPECT_NE(0, handle_yuv420); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle_yuv420)); |
| |
| uint32_t textures[2] = {1, 2}; |
| |
| mFb->swapTexturesAndUpdateColorBuffer(handle_nv12, 0, 0, mWidth, mHeight, |
| GL_RGBA, GL_UNSIGNED_BYTE, |
| FRAMEWORK_FORMAT_NV12, textures); |
| mFb->swapTexturesAndUpdateColorBuffer(handle_yuv420, 0, 0, mWidth, mHeight, |
| GL_RGBA, GL_UNSIGNED_BYTE, |
| FRAMEWORK_FORMAT_NV12, textures); |
| |
| uint8_t forRead[sizeof(golden)]; |
| memset(forRead, 0x0, mWidth * mHeight * 3 / 2); |
| mFb->readColorBufferYUV(handle_yuv420, 0, 0, mWidth, mHeight, forRead, |
| mWidth * mHeight * 3 / 2); |
| |
| EXPECT_TRUE( |
| ImageMatches(mWidth, mHeight * 3 / 2, 1, mWidth, golden, forRead)); |
| |
| mFb->closeColorBuffer(handle_nv12); |
| mFb->closeColorBuffer(handle_yuv420); |
| } |
| |
| TEST_F(FrameBufferTest, CreateOpenUpdateCloseColorBuffer_ReadYV12) { |
| mWidth = 20 * 16; |
| HandleType handle = mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, |
| FRAMEWORK_FORMAT_YV12); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle)); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, |
| GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| TestTexture forRead = createTestPatternRGBA8888(mWidth, mHeight); |
| memset(forRead.data(), 0x0, mWidth * mHeight * 3 / 2); |
| mFb->readColorBufferYUV(handle, 0, 0, mWidth, mHeight, forRead.data(), |
| mWidth * mHeight * 3 / 2); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), |
| forRead.data())); |
| memset(forRead.data(), 0xff, mWidth * mHeight * 3 / 2); |
| mFb->readColorBufferYUV(handle, 0, 0, mWidth, mHeight, forRead.data(), |
| mWidth * mHeight * 3 / 2); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), |
| forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| // bug: 110105029 |
| // Tests that color buffer updates should not fail if there is a format change. |
| // Needed to accomodate format-changing behavior from the guest gralloc. |
| TEST_F(FrameBufferTest, CreateOpenUpdateCloseColorBuffer_FormatChange) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle)); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| TestTexture forRead = createTestTextureRGBA8888SingleColor(mWidth, mHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| mFb->readColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), |
| forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| // Tests obtaining EGL configs from FrameBuffer. |
| TEST_F(FrameBufferTest, Configs) { |
| const EmulatedEglConfigList* configs = mFb->getConfigs(); |
| EXPECT_GE(configs->size(), 0); |
| } |
| |
| // Tests creating GL context from FrameBuffer. |
| TEST_F(FrameBufferTest, CreateEmulatedEglContext) { |
| HandleType handle = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| EXPECT_NE(0, handle); |
| } |
| |
| // Tests creating window surface from FrameBuffer. |
| TEST_F(FrameBufferTest, CreateEmulatedEglWindowSurface) { |
| HandleType handle = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| EXPECT_NE(0, handle); |
| } |
| |
| // Tests eglMakeCurrent from FrameBuffer. |
| TEST_F(FrameBufferTest, CreateBindEmulatedEglContext) { |
| HandleType context = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| HandleType surface = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| } |
| |
| // A basic blit test that simulates what the guest system does in one pass |
| // of draw + eglSwapBuffers: |
| // 1. Draws in OpenGL with glClear. |
| // 2. Calls flushEmulatedEglWindowSurfaceColorBuffer(), which is the "backing operation" of |
| // ANativeWindow::queueBuffer in the guest. |
| // 3. Calls post() with the resulting color buffer, the backing operation of fb device "post" |
| // in the guest. |
| TEST_F(FrameBufferTest, BasicBlit) { |
| auto gl = LazyLoadedGLESv2Dispatch::get(); |
| |
| HandleType colorBuffer = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| HandleType context = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| HandleType surface = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| EXPECT_TRUE(mFb->setEmulatedEglWindowSurfaceColorBuffer(surface, colorBuffer)); |
| |
| float colors[3][4] = { |
| { 1.0f, 0.0f, 0.0f, 1.0f}, |
| { 0.0f, 1.0f, 0.0f, 1.0f}, |
| { 0.0f, 0.0f, 1.0f, 1.0f}, |
| }; |
| |
| for (int i = 0; i < 3; i++) { |
| float* color = colors[i]; |
| |
| gl->glClearColor(color[0], color[1], color[2], color[3]); |
| gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| mFb->flushEmulatedEglWindowSurfaceColorBuffer(surface); |
| |
| TestTexture targetBuffer = |
| createTestTextureRGBA8888SingleColor( |
| mWidth, mHeight, color[0], color[1], color[2], color[3]); |
| |
| TestTexture forRead = |
| createTestTextureRGBA8888SingleColor( |
| mWidth, mHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| |
| mFb->readColorBuffer( |
| colorBuffer, 0, 0, mWidth, mHeight, |
| GL_RGBA, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| EXPECT_TRUE( |
| ImageMatches( |
| mWidth, mHeight, 4, mWidth, |
| targetBuffer.data(), forRead.data())); |
| |
| if (mUseSubWindow) { |
| mFb->post(colorBuffer); |
| mWindow->messageLoop(); |
| } |
| } |
| |
| EXPECT_TRUE(mFb->bindContext(0, 0, 0)); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->destroyEmulatedEglWindowSurface(surface); |
| } |
| |
| // Tests that snapshot works with an empty FrameBuffer. |
| TEST_F(FrameBufferTest, SnapshotSmokeTest) { |
| saveSnapshot(); |
| loadSnapshot(); |
| } |
| |
| // Tests that the snapshot restores the clear color state, by changing the clear |
| // color in between save and load. If this fails, it means failure to restore a |
| // number of different states from GL contexts. |
| TEST_F(FrameBufferTest, SnapshotPreserveColorClear) { |
| HandleType context = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| HandleType surface = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| |
| auto gl = LazyLoadedGLESv2Dispatch::get(); |
| gl->glClearColor(1, 1, 1, 1); |
| gl->glClear(GL_COLOR_BUFFER_BIT); |
| EXPECT_TRUE(compareGlobalGlFloatv(gl, GL_COLOR_CLEAR_VALUE, {1, 1, 1, 1})); |
| |
| saveSnapshot(); |
| |
| gl->glClearColor(0.5, 0.5, 0.5, 0.5); |
| EXPECT_TRUE(compareGlobalGlFloatv(gl, GL_COLOR_CLEAR_VALUE, |
| {0.5, 0.5, 0.5, 0.5})); |
| |
| loadSnapshot(); |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| |
| EXPECT_TRUE(compareGlobalGlFloatv(gl, GL_COLOR_CLEAR_VALUE, {1, 1, 1, 1})); |
| } |
| |
| // Tests that snapshot works to save the state of a single ColorBuffer; we |
| // upload a test pattern to the ColorBuffer, take a snapshot, load it, and |
| // verify that the contents are the same. |
| TEST_F(FrameBufferTest, SnapshotSingleColorBuffer) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| saveSnapshot(); |
| loadSnapshot(); |
| |
| TestTexture forRead = createTestTextureRGBA8888SingleColor(mWidth, mHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| mFb->readColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| // bug: 111360779 |
| // Tests that the ColorBuffer is successfully updated even if a reformat happens |
| // on restore; the reformat may mess up the texture restore logic. |
| // In ColorBuffer::subUpdate, this test is known to fail if touch() is moved after the reformat. |
| TEST_F(FrameBufferTest, SnapshotColorBufferSubUpdateRestore) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| |
| saveSnapshot(); |
| loadSnapshot(); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| TestTexture forRead = createTestTextureRGBA8888SingleColor(mWidth, mHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| mFb->readColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| // bug: 111558407 |
| // Tests that ColorBuffer's blit path is retained on save/restore. |
| TEST_F(FrameBufferTest, SnapshotFastBlitRestore) { |
| HandleType handle = mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, |
| FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| |
| EXPECT_TRUE(mFb->isFastBlitSupported()); |
| |
| mFb->lock(); |
| EXPECT_EQ(mFb->isFastBlitSupported(), mFb->findColorBuffer(handle)->glOpIsFastBlitSupported()); |
| mFb->unlock(); |
| |
| saveSnapshot(); |
| loadSnapshot(); |
| |
| mFb->lock(); |
| EXPECT_EQ(mFb->isFastBlitSupported(), mFb->findColorBuffer(handle)->glOpIsFastBlitSupported()); |
| mFb->unlock(); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| // Tests rate of draw calls with no guest/host communication, but with translator. |
| static constexpr uint32_t kDrawCallLimit = 50000; |
| |
| TEST_F(FrameBufferTest, DrawCallRate) { |
| HandleType colorBuffer = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| HandleType context = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| HandleType surface = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| EXPECT_TRUE(mFb->setEmulatedEglWindowSurfaceColorBuffer(surface, colorBuffer)); |
| |
| auto gl = LazyLoadedGLESv2Dispatch::get(); |
| |
| constexpr char vshaderSrc[] = R"(#version 300 es |
| precision highp float; |
| |
| layout (location = 0) in vec2 pos; |
| layout (location = 1) in vec3 color; |
| |
| uniform mat4 transform; |
| |
| out vec3 color_varying; |
| |
| void main() { |
| gl_Position = transform * vec4(pos, 0.0, 1.0); |
| color_varying = (transform * vec4(color, 1.0)).xyz; |
| } |
| )"; |
| constexpr char fshaderSrc[] = R"(#version 300 es |
| precision highp float; |
| |
| in vec3 color_varying; |
| |
| out vec4 fragColor; |
| |
| void main() { |
| fragColor = vec4(color_varying, 1.0); |
| } |
| )"; |
| |
| GLuint program = compileAndLinkShaderProgram(vshaderSrc, fshaderSrc); |
| |
| GLint transformLoc = gl->glGetUniformLocation(program, "transform"); |
| |
| struct VertexAttributes { |
| float position[2]; |
| float color[3]; |
| }; |
| |
| const VertexAttributes vertexAttrs[] = { |
| { { -0.5f, -0.5f,}, { 0.2, 0.1, 0.9, }, }, |
| { { 0.5f, -0.5f,}, { 0.8, 0.3, 0.1,}, }, |
| { { 0.0f, 0.5f,}, { 0.1, 0.9, 0.6,}, }, |
| }; |
| |
| GLuint buffer; |
| gl->glGenBuffers(1, &buffer); |
| gl->glBindBuffer(GL_ARRAY_BUFFER, buffer); |
| gl->glBufferData(GL_ARRAY_BUFFER, sizeof(vertexAttrs), vertexAttrs, |
| GL_STATIC_DRAW); |
| |
| gl->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, |
| sizeof(VertexAttributes), 0); |
| gl->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, |
| sizeof(VertexAttributes), |
| (GLvoid*)offsetof(VertexAttributes, color)); |
| gl->glEnableVertexAttribArray(0); |
| gl->glEnableVertexAttribArray(1); |
| |
| gl->glUseProgram(program); |
| |
| gl->glClearColor(0.2f, 0.2f, 0.3f, 0.0f); |
| gl->glViewport(0, 0, 1, 1); |
| |
| float matrix[16] = { |
| 1.0f, 0.0f, 0.0f, 0.0f, |
| 0.0f, 1.0f, 0.0f, 0.0f, |
| 0.0f, 0.0f, 1.0f, 0.0f, |
| 0.0f, 0.0f, 0.0f, 1.0f, |
| }; |
| |
| gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| |
| uint32_t drawCount = 0; |
| |
| auto cpuTimeStart = android::base::cpuTime(); |
| |
| fprintf(stderr, "%s: transform loc %d\n", __func__, transformLoc); |
| |
| while (drawCount < kDrawCallLimit) { |
| gl->glUniformMatrix4fv(transformLoc, 1, GL_FALSE, matrix); |
| gl->glBindBuffer(GL_ARRAY_BUFFER, buffer); |
| gl->glDrawArrays(GL_TRIANGLES, 0, 3); |
| ++drawCount; |
| } |
| |
| gl->glFinish(); |
| |
| auto cpuTime = android::base::cpuTime() - cpuTimeStart; |
| |
| uint64_t duration_us = cpuTime.wall_time_us; |
| // uint64_t duration_cpu_us = cpuTime.usageUs(); |
| |
| float ms = duration_us / 1000.0f; |
| float sec = duration_us / 1000000.0f; |
| float drawCallHz = (float)kDrawCallLimit / sec; |
| |
| printf("Drew %u times in %f ms. Rate: %f Hz\n", kDrawCallLimit, ms, drawCallHz); |
| |
| // android::perflogger::logDrawCallOverheadTest( |
| // (const char*)gl->glGetString(GL_VENDOR), |
| // (const char*)gl->glGetString(GL_RENDERER), |
| // (const char*)gl->glGetString(GL_VERSION), |
| // "drawArrays", "decoder", |
| // kDrawCallLimit, |
| // (long)drawCallHz, |
| // duration_us, |
| // duration_cpu_us); |
| |
| EXPECT_TRUE(mFb->bindContext(0, 0, 0)); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->destroyEmulatedEglWindowSurface(surface); |
| } |
| |
| // Tests rate of draw calls with only the host driver and no translator. |
| TEST_F(FrameBufferTest, HostDrawCallRate) { |
| HandleType colorBuffer = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| HandleType context = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| HandleType surface = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| EXPECT_TRUE(mFb->setEmulatedEglWindowSurfaceColorBuffer(surface, colorBuffer)); |
| |
| auto gl = LazyLoadedGLESv2Dispatch::get(); |
| |
| uint64_t duration_us, duration_cpu_us; |
| gl->glTestHostDriverPerformance(kDrawCallLimit, &duration_us, &duration_cpu_us); |
| |
| float ms = duration_us / 1000.0f; |
| float sec = duration_us / 1000000.0f; |
| float drawCallHz = kDrawCallLimit / sec; |
| |
| printf("Drew %u times in %f ms. Rate: %f Hz\n", kDrawCallLimit, ms, drawCallHz); |
| |
| // android::perflogger::logDrawCallOverheadTest( |
| // (const char*)gl->glGetString(GL_VENDOR), |
| // (const char*)gl->glGetString(GL_RENDERER), |
| // (const char*)gl->glGetString(GL_VERSION), |
| // "drawArrays", "host", |
| // kDrawCallLimit, |
| // (long)drawCallHz, |
| // duration_us, |
| // duration_cpu_us); |
| |
| EXPECT_TRUE(mFb->bindContext(0, 0, 0)); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->destroyEmulatedEglWindowSurface(surface); |
| } |
| |
| // Tests Vulkan interop query. |
| TEST_F(FrameBufferTest, VulkanInteropQuery) { |
| auto egl = LazyLoadedEGLDispatch::get(); |
| |
| EXPECT_NE(nullptr, egl->eglQueryVulkanInteropSupportANDROID); |
| |
| EGLBoolean supported = |
| egl->eglQueryVulkanInteropSupportANDROID(); |
| |
| // Disregard the result for now |
| (void)supported; |
| } |
| |
| // Tests ColorBuffer with GL_BGRA input. |
| TEST_F(FrameBufferTest, CreateColorBufferBGRA) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_BGRA_EXT, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| // FramBuffer::finalize handles color buffer destruction here |
| } |
| |
| // Test ColorBuffer with GL_RGBA, but read back as GL_BGRA, so that R/B are switched. |
| // TODO: This doesn't work on NVIDIA EGL, it issues GL_INVALID_OPERATION if the format doesn't match. |
| TEST_F(FrameBufferTest, DISABLED_ReadColorBufferSwitchRedBlue) { |
| HandleType handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->openColorBuffer(handle)); |
| |
| TestTexture forUpdate = createTestPatternRGBA8888(mWidth, mHeight); |
| mFb->updateColorBuffer(handle, 0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| TestTexture forRead = createTestTextureRGBA8888SingleColor(mWidth, mHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| // Switch red and blue |
| mFb->readColorBuffer(handle, 0, 0, mWidth, mHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| // Switch them back, so we get the original image |
| uint8_t* forReadBytes = forRead.data(); |
| |
| for (uint32_t row = 0; row < mHeight; ++row) { |
| for (uint32_t col = 0; col < mWidth; ++col) { |
| uint8_t* pixel = forReadBytes + mWidth * 4 * row + col * 4; |
| // In RGBA8: |
| // 3 2 1 0 |
| // 0xAABBGGRR on little endian systems |
| // R component: pixel[0] |
| // B component: pixel[2] |
| uint8_t r = pixel[0]; |
| uint8_t b = pixel[2]; |
| pixel[0] = b; |
| pixel[2] = r; |
| } |
| } |
| |
| EXPECT_TRUE(ImageMatches(mWidth, mHeight, 4, mWidth, forUpdate.data(), forRead.data())); |
| |
| mFb->closeColorBuffer(handle); |
| } |
| |
| TEST_F(FrameBufferTest, CreateMultiDisplay) { |
| uint32_t id = 1; |
| mFb->createDisplay(&id); |
| EXPECT_EQ(0, mFb->createDisplay(&id)); |
| EXPECT_EQ(0, mFb->destroyDisplay(id)); |
| } |
| |
| TEST_F(FrameBufferTest, BindMultiDisplayColorBuffer) { |
| uint32_t id = 2; |
| EXPECT_EQ(0, mFb->createDisplay(&id)); |
| uint32_t handle = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_NE(0, handle); |
| EXPECT_EQ(0, mFb->setDisplayColorBuffer(id, handle)); |
| uint32_t getHandle = 0; |
| mFb->getDisplayColorBuffer(id, &getHandle); |
| EXPECT_EQ(handle, getHandle); |
| uint32_t getId = 0; |
| mFb->getColorBufferDisplay(handle, &getId); |
| EXPECT_EQ(id, getId); |
| mFb->closeColorBuffer(handle); |
| EXPECT_EQ(0, mFb->destroyDisplay(id)); |
| } |
| |
| TEST_F(FrameBufferTest, SetMultiDisplayPosition) { |
| uint32_t id = FrameBuffer::s_invalidIdMultiDisplay; |
| mFb->createDisplay(&id); |
| EXPECT_NE(0, id); |
| uint32_t w = mWidth / 2, h = mHeight / 2; |
| EXPECT_EQ(0, mFb->setDisplayPose(id, -1, -1, w, h)); |
| int32_t x, y; |
| uint32_t width, height; |
| EXPECT_EQ(0, mFb->getDisplayPose(id, &x, &y, &width, &height)); |
| EXPECT_EQ(w, width); |
| EXPECT_EQ(h, height); |
| EXPECT_EQ(0, mFb->destroyDisplay(id)); |
| } |
| |
| TEST_F(FrameBufferTest, ComposeMultiDisplay) { |
| LazyLoadedGLESv2Dispatch::get(); |
| |
| HandleType context = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| HandleType surface = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| |
| HandleType cb0 = |
| mFb->createColorBuffer(mWidth/2, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| TestTexture forUpdate0 = createTestTextureRGBA8888SingleColor(mWidth/2, mHeight, 1.0f, 1.0f, 1.0f, 1.0f); |
| EXPECT_EQ(0, mFb->openColorBuffer(cb0)); |
| mFb->updateColorBuffer(cb0, 0, 0, mWidth/2, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate0.data()); |
| |
| uint32_t cb1 = |
| mFb->createColorBuffer(mWidth/2, mHeight/2, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_EQ(0, mFb->openColorBuffer(cb1)); |
| TestTexture forUpdate1 = createTestTextureRGBA8888SingleColor(mWidth/2, mHeight/2, 1.0f, 0.0f, 0.0f, 1.0f); |
| mFb->updateColorBuffer(cb1, 0, 0, mWidth/2, mHeight/2, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate1.data()); |
| |
| uint32_t cb2 = |
| mFb->createColorBuffer(mWidth/4, mHeight/2, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_EQ(0, mFb->openColorBuffer(cb2)); |
| TestTexture forUpdate2 = createTestTextureRGBA8888SingleColor(mWidth/4, mHeight/2, 0.0f, 1.0f, 0.0f, 1.0f); |
| mFb->updateColorBuffer(cb2, 0, 0, mWidth/4, mHeight/2, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate2.data()); |
| |
| uint32_t cb3 = |
| mFb->createColorBuffer(mWidth/4, mHeight/4, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| EXPECT_EQ(0, mFb->openColorBuffer(cb3)); |
| TestTexture forUpdate3 = createTestTextureRGBA8888SingleColor(mWidth/4, mHeight/4, 0.0f, 0.0f, 1.0f, 1.0f); |
| mFb->updateColorBuffer(cb3, 0, 0, mWidth/4, mHeight/4, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate3.data()); |
| |
| FrameBuffer::DisplayInfo info[] = |
| {{cb1, -1, -1, (uint32_t)mWidth/2, (uint32_t)mHeight/2, 240}, |
| {cb2, -1, -1, (uint32_t)mWidth/4, (uint32_t)mHeight/2, 240}, |
| {cb3, -1, -1, (uint32_t)mWidth/4, (uint32_t)mHeight/4, 240}}; |
| |
| uint32_t ids[] = {1, 2, 3}; |
| for (uint32_t i = 0; i < 3 ; i++) { |
| EXPECT_EQ(0, mFb->createDisplay(&ids[i])); |
| EXPECT_EQ(0, mFb->setDisplayPose(ids[i], info[i].pos_x, info[i].pos_y, |
| info[i].width, info[i].height)); |
| EXPECT_EQ(0, mFb->setDisplayColorBuffer(ids[i], info[i].cb)); |
| } |
| |
| if (mUseSubWindow) { |
| mFb->post(cb0); |
| mWindow->messageLoop(); |
| } |
| |
| EXPECT_TRUE(mFb->bindContext(0, 0, 0)); |
| mFb->closeColorBuffer(cb0); |
| mFb->closeColorBuffer(cb1); |
| mFb->closeColorBuffer(cb2); |
| mFb->closeColorBuffer(cb3); |
| mFb->destroyDisplay(ids[0]); |
| mFb->destroyDisplay(ids[1]); |
| mFb->destroyDisplay(ids[2]); |
| mFb->destroyEmulatedEglWindowSurface(surface); |
| } |
| |
| #ifdef GFXSTREAM_HAS_X11 |
| // Tests basic pixmap import. Can we import a native pixmap and successfully |
| // upload and read back some color? |
| TEST_F(FrameBufferTest, PixmapImport_Basic) { |
| const int kWidth = 16; |
| const int kHeight = 16; |
| const int kBytesPerPixel = 4; |
| |
| // Only run this test on display :0. |
| auto disp = android::base::getEnvironmentVariable("DISPLAY"); |
| if (disp != ":0" ) { |
| fprintf(stderr, "%s: Wawrning: Skipping test because DISPLAY is [%s] (not :0)\n", __func__, |
| disp.c_str()); |
| return; |
| } |
| |
| void* pixmap = createNativePixmap(kWidth, kHeight, kBytesPerPixel); |
| EXPECT_NE(nullptr, pixmap); |
| |
| HandleType cb = |
| mFb->createColorBuffer(kWidth, kHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| TestTexture forUpdate = createTestTextureRGBA8888SingleColor(kWidth, kHeight, 1.0f, 0.0f, 1.0f, 1.0f); |
| EXPECT_EQ(0, mFb->openColorBuffer(cb)); |
| mFb->updateColorBuffer(cb, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, forUpdate.data()); |
| |
| EXPECT_TRUE(mFb->platformImportResource(cb, RESOURCE_TYPE_EGL_NATIVE_PIXMAP|RESOURCE_USE_PRESERVE, pixmap)); |
| |
| TestTexture forRead = createTestTextureRGBA8888SingleColor(kWidth, kHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| mFb->readColorBuffer(cb, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| EXPECT_TRUE(ImageMatches(kWidth, kHeight, 4, kWidth, forUpdate.data(), forRead.data())); |
| |
| mFb->closeColorBuffer(cb); |
| |
| freeNativePixmap(pixmap); |
| } |
| |
| // Similar to BasicBlit, except the color buffer is backed by a pixmap. |
| // Can we render to the pixmap and read back contents? |
| TEST_F(FrameBufferTest, PixmapImport_Blit) { |
| // Only run this test on display :0. |
| auto disp = android::base::getEnvironmentVariable("DISPLAY"); |
| if (disp != ":0" ) { |
| fprintf(stderr, "%s: Wawrning: Skipping test because DISPLAY is [%s] (not :0)\n", __func__, |
| disp.c_str()); |
| return; |
| } |
| |
| auto gl = LazyLoadedGLESv2Dispatch::get(); |
| |
| void* pixmap = createNativePixmap(mWidth, mHeight, 4); |
| EXPECT_NE(nullptr, pixmap); |
| |
| HandleType colorBuffer = |
| mFb->createColorBuffer(mWidth, mHeight, GL_RGBA, FRAMEWORK_FORMAT_GL_COMPATIBLE); |
| |
| EXPECT_TRUE(mFb->platformImportResource(colorBuffer, RESOURCE_TYPE_EGL_NATIVE_PIXMAP, pixmap)); |
| |
| HandleType context = mFb->createEmulatedEglContext(0, 0, GLESApi_3_0); |
| HandleType surface = mFb->createEmulatedEglWindowSurface(0, mWidth, mHeight); |
| |
| EXPECT_TRUE(mFb->bindContext(context, surface, surface)); |
| EXPECT_TRUE(mFb->setEmulatedEglWindowSurfaceColorBuffer(surface, colorBuffer)); |
| |
| float colors[3][4] = { |
| { 1.0f, 0.0f, 0.0f, 1.0f}, |
| { 0.0f, 1.0f, 0.0f, 1.0f}, |
| { 0.0f, 0.0f, 1.0f, 1.0f}, |
| }; |
| |
| for (int i = 0; i < 3; i++) { |
| float* color = colors[i]; |
| |
| gl->glClearColor(color[0], color[1], color[2], color[3]); |
| gl->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); |
| mFb->flushEmulatedEglWindowSurfaceColorBuffer(surface); |
| |
| TestTexture targetBuffer = |
| createTestTextureRGBA8888SingleColor( |
| mWidth, mHeight, color[0], color[1], color[2], color[3]); |
| |
| TestTexture forRead = |
| createTestTextureRGBA8888SingleColor( |
| mWidth, mHeight, 0.0f, 0.0f, 0.0f, 0.0f); |
| |
| mFb->readColorBuffer( |
| colorBuffer, 0, 0, mWidth, mHeight, |
| GL_RGBA, GL_UNSIGNED_BYTE, forRead.data()); |
| |
| EXPECT_TRUE( |
| ImageMatches( |
| mWidth, mHeight, 4, mWidth, |
| targetBuffer.data(), forRead.data())); |
| |
| if (mUseSubWindow) { |
| mFb->post(colorBuffer); |
| mWindow->messageLoop(); |
| } |
| } |
| |
| EXPECT_TRUE(mFb->bindContext(0, 0, 0)); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->closeColorBuffer(colorBuffer); |
| mFb->destroyEmulatedEglWindowSurface(surface); |
| |
| freeNativePixmap(pixmap); |
| } |
| #endif |
| |
| } // namespace |
| } // namespace gfxstream |