diff options
| author | 2019-03-13 12:23:45 -0700 | |
|---|---|---|
| committer | 2019-03-15 17:58:10 -0700 | |
| commit | d43ccab5df937fd99a17a45ef437bb26d0f529e2 (patch) | |
| tree | f2c5276fee2c645d79fc2962da89eb361951f3ab /libs | |
| parent | f0497270f6cf378d0d8b3517a6cdd76bf61bd6ae (diff) | |
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
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/renderengine/gl/GLESRenderEngine.cpp | 43 | ||||
| -rw-r--r-- | libs/renderengine/gl/GLESRenderEngine.h | 8 | ||||
| -rw-r--r-- | libs/renderengine/tests/RenderEngineTest.cpp | 111 |
3 files changed, 144 insertions, 18 deletions
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 48854e2d69..f65130906d 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -432,10 +432,15 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG } GLESRenderEngine::~GLESRenderEngine() { - for (const auto& image : mFramebufferImageCache) { - eglDestroyImageKHR(mEGLDisplay, image.second); - } - mFramebufferImageCache.clear(); + 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); + } + mImageCache.clear(); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mEGLDisplay); } @@ -634,6 +639,9 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<Graphi 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 @@ bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, 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 @@ status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, } } - *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 @@ bool GLESRenderEngine::needsXYZTransformMatrix() const { 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 dcf5acc31e..efb6ef0043 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -56,7 +56,7 @@ public: 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 @@ public: // 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 a2bbaff359..8c93cf432c 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 @@ constexpr int DEFAULT_DISPLAY_OFFSET = 64; 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 @@ struct RenderEngineTest : public ::testing::Test { 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 @@ struct RenderEngineTest : public ::testing::Test { } 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 @@ struct RenderEngineTest : public ::testing::Test { 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 @@ struct BufferSourceVariant { 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 @@ TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) { 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 @@ TEST_F(RenderEngineTest, drawLayers_clearRegion) { 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 |