Add extra tests for renderengine

* Test buffer binding methods
* Test caching, and fix a bug caught by these tests.

Test: librenderengine_test
Change-Id: I50623d3a3f0b77f541044354c70db9fb536e66c3
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 48854e2..f651309 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -432,10 +432,15 @@
 }
 
 GLESRenderEngine::~GLESRenderEngine() {
-    for (const auto& image : mFramebufferImageCache) {
-        eglDestroyImageKHR(mEGLDisplay, image.second);
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    unbindFrameBuffer(mDrawingBuffer.get());
+    mDrawingBuffer = nullptr;
+    while (!mFramebufferImageCache.empty()) {
+        EGLImageKHR expired = mFramebufferImageCache.front().second;
+        mFramebufferImageCache.pop_front();
+        eglDestroyImageKHR(mEGLDisplay, expired);
     }
-    mFramebufferImageCache.clear();
+    mImageCache.clear();
     eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
     eglTerminate(mEGLDisplay);
 }
@@ -634,6 +639,9 @@
 status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName,
                                                            sp<GraphicBuffer> buffer,
                                                            sp<Fence> bufferFence) {
+    if (buffer == nullptr) {
+        return BAD_VALUE;
+    }
     ATRACE_CALL();
     auto cachedImage = mImageCache.find(buffer->getId());
 
@@ -779,7 +787,7 @@
 EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer,
                                                              bool isProtected) {
     sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer);
-    uint32_t bufferId = graphicBuffer->getId();
+    uint64_t bufferId = graphicBuffer->getId();
     for (const auto& image : mFramebufferImageCache) {
         if (image.first == bufferId) {
             return image.second;
@@ -818,6 +826,11 @@
         sync_wait(bufferFence.get(), -1);
     }
 
+    if (buffer == nullptr) {
+        ALOGE("No output buffer provided. Aborting GPU composition.");
+        return BAD_VALUE;
+    }
+
     {
         std::lock_guard<std::mutex> lock(mRenderingMutex);
 
@@ -914,10 +927,12 @@
             }
         }
 
-        *drawFence = flush();
+        if (drawFence != nullptr) {
+            *drawFence = flush();
+        }
         // If flush failed or we don't support native fences, we need to force the
         // gl command stream to be executed.
-        if (drawFence->get() < 0) {
+        if (drawFence == nullptr || drawFence->get() < 0) {
             bool success = finish();
             if (!success) {
                 ALOGE("Failed to flush RenderEngine commands");
@@ -1343,6 +1358,20 @@
     return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
 }
 
+bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    const auto& cachedImage = mImageCache.find(bufferId);
+    return cachedImage != mImageCache.end();
+}
+
+bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) {
+    std::lock_guard<std::mutex> lock(mRenderingMutex);
+    return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(),
+                       [=](std::pair<uint64_t, EGLImageKHR> image) {
+                           return image.first == bufferId;
+                       });
+}
+
 // FlushTracer implementation
 GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) {
     mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this);
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index dcf5acc..efb6ef0 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -56,7 +56,7 @@
                      EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy,
                      EGLContext protectedContext, EGLSurface protectedDummy,
                      uint32_t imageCacheSize);
-    ~GLESRenderEngine() override;
+    ~GLESRenderEngine() override EXCLUDES(mRenderingMutex);
 
     std::unique_ptr<Framebuffer> createFramebuffer() override;
     std::unique_ptr<Image> createImage() override;
@@ -94,6 +94,12 @@
     // Creates an output image for rendering to
     EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected);
 
+    // Test-only methods
+    // Returns true iff mImageCache contains an image keyed by bufferId
+    bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+    // Returns true iff mFramebufferImageCache contains an image keyed by bufferId
+    bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex);
+
 protected:
     Framebuffer* getFramebufferForDrawing() override;
     void dump(std::string& result) override;
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index a2bbaff..8c93cf4 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -19,6 +19,7 @@
 #include <renderengine/RenderEngine.h>
 #include <sync/sync.h>
 #include <ui/PixelFormat.h>
+#include "../gl/GLESRenderEngine.h"
 
 constexpr int DEFAULT_DISPLAY_WIDTH = 128;
 constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
@@ -27,6 +28,19 @@
 namespace android {
 
 struct RenderEngineTest : public ::testing::Test {
+    static void SetUpTestSuite() {
+        sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>(
+                                                                 ui::PixelFormat::RGBA_8888),
+                                                         0, 1);
+    }
+
+    static void TearDownTestSuite() {
+        // The ordering here is important - sCurrentBuffer must live longer
+        // than RenderEngine to avoid a null reference on tear-down.
+        sRE = nullptr;
+        sCurrentBuffer = nullptr;
+    }
+
     static sp<GraphicBuffer> allocateDefaultBuffer() {
         return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT,
                                  HAL_PIXEL_FORMAT_RGBA_8888, 1,
@@ -101,12 +115,12 @@
                     DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET);
     }
 
-    static void invokeDraw(renderengine::DisplaySettings settings,
-                           std::vector<renderengine::LayerSettings> layers,
-                           sp<GraphicBuffer> buffer) {
+    void invokeDraw(renderengine::DisplaySettings settings,
+                    std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) {
         base::unique_fd fence;
         status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(),
                                           base::unique_fd(), &fence);
+        sCurrentBuffer = buffer;
 
         int fd = fence.release();
         if (fd >= 0) {
@@ -115,9 +129,12 @@
         }
 
         ASSERT_EQ(NO_ERROR, status);
+        if (layers.size() > 0) {
+            ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId()));
+        }
     }
 
-    static void drawEmptyLayers() {
+    void drawEmptyLayers() {
         renderengine::DisplaySettings settings;
         std::vector<renderengine::LayerSettings> layers;
         // Meaningless buffer since we don't do any drawing
@@ -200,17 +217,22 @@
 
     void clearRegion();
 
-    // Dumb hack to get aroud the fact that tear-down for renderengine isn't
-    // well defined right now, so we can't create multiple instances
-    static std::unique_ptr<renderengine::RenderEngine> sRE;
+    // Keep around the same renderengine object to save on initialization time.
+    // For now, exercise the GL backend directly so that some caching specifics
+    // can be tested without changing the interface.
+    static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE;
+    // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to
+    // be freed *after* RenderEngine is destroyed, so that the EGL image is
+    // destroyed first.
+    static sp<GraphicBuffer> sCurrentBuffer;
 
     sp<GraphicBuffer> mBuffer;
 
     std::vector<uint32_t> mTexNames;
 };
 
-std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE =
-        renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0, 1);
+std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr;
+sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr;
 
 struct ColorSourceVariant {
     static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b,
@@ -245,7 +267,7 @@
                           RenderEngineTest* fixture) {
         sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1);
         uint32_t texName;
-        RenderEngineTest::sRE->genTextures(1, &texName);
+        fixture->sRE->genTextures(1, &texName);
         fixture->mTexNames.push_back(texName);
 
         uint8_t* pixels;
@@ -740,6 +762,38 @@
     drawEmptyLayers();
 }
 
+TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) {
+    renderengine::DisplaySettings settings;
+    std::vector<renderengine::LayerSettings> layers;
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layers.push_back(layer);
+    base::unique_fd fence;
+    status_t status = sRE->drawLayers(settings, layers, nullptr, base::unique_fd(), &fence);
+
+    ASSERT_EQ(BAD_VALUE, status);
+}
+
+TEST_F(RenderEngineTest, drawLayers_nullOutputFence) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<renderengine::LayerSettings> layers;
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+    layer.alpha = 1.0;
+    layers.push_back(layer);
+
+    status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(),
+                                      base::unique_fd(), nullptr);
+    sCurrentBuffer = mBuffer;
+    ASSERT_EQ(NO_ERROR, status);
+    expectBufferColor(fullscreenRect(), 255, 0, 0, 255);
+}
+
 TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) {
     fillRedBuffer<ColorSourceVariant>();
 }
@@ -912,4 +966,41 @@
     clearRegion();
 }
 
+TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) {
+    renderengine::DisplaySettings settings;
+    settings.physicalDisplay = fullscreenRect();
+    settings.clip = fullscreenRect();
+
+    std::vector<renderengine::LayerSettings> layers;
+
+    renderengine::LayerSettings layer;
+    layer.geometry.boundaries = fullscreenRect().toFloatRect();
+    BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this);
+
+    layers.push_back(layer);
+    invokeDraw(settings, layers, mBuffer);
+    uint64_t bufferId = layer.source.buffer.buffer->getId();
+    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+    sRE->unbindExternalTextureBuffer(bufferId);
+    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) {
+    status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr);
+    ASSERT_EQ(BAD_VALUE, result);
+}
+
+TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) {
+    sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1);
+    uint32_t texName;
+    sRE->genTextures(1, &texName);
+    mTexNames.push_back(texName);
+
+    sRE->bindExternalTextureBuffer(texName, buf, nullptr);
+    uint64_t bufferId = buf->getId();
+    EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId));
+    sRE->unbindExternalTextureBuffer(bufferId);
+    EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
+}
+
 } // namespace android