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