android_frameworks_base/libs/hwui/utils/TestWindowContext.cpp
Tom Hudson 58862c9f6f Fix leak of file descriptors in test code
skia_dm rendering through HWUI was hanging after roughly 300 tests.
logcat reports some process was unable to get any file descriptors.

When we migrated TestWindowContext into HWUI and started using PIMPL
I didn't clean up the implementation struct in the destructor. Doing
so solved the hang for me.

My guess was that BufferQueue was being leaked, and that gralloc is
backed by file descriptors, but some research suggests that Android
ought to be able to handle far more than 1k fds (ulimit -Hn returns
4096 on this device).

R=djsollen@google.com

Change-Id: I2cd9f8945cee9b22f838002e1ad687d5fe29cb97
2015-12-10 22:19:17 +00:00

214 lines
7.6 KiB
C++

/*
* Copyright (C) 2015 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 "TestWindowContext.h"
#include "AnimationContext.h"
#include "DisplayListCanvas.h"
#include "IContextFactory.h"
#include "RenderNode.h"
#include "SkTypes.h"
#include "gui/BufferQueue.h"
#include "gui/CpuConsumer.h"
#include "gui/IGraphicBufferConsumer.h"
#include "gui/IGraphicBufferProducer.h"
#include "gui/Surface.h"
#include "renderthread/RenderProxy.h"
namespace {
/**
* Helper class for setting up android::uirenderer::renderthread::RenderProxy.
*/
class ContextFactory : public android::uirenderer::IContextFactory {
public:
android::uirenderer::AnimationContext* createAnimationContext
(android::uirenderer::renderthread::TimeLord& clock) override {
return new android::uirenderer::AnimationContext(clock);
}
};
} // anonymous namespace
namespace android {
namespace uirenderer {
/**
Android strong pointers (android::sp) can't hold forward-declared classes,
so we have to use pointer-to-implementation here if we want to hide the
details from our non-framework users.
*/
class TestWindowContext::TestWindowData {
public:
TestWindowData(SkISize size) : mSize(size) {
android::BufferQueue::createBufferQueue(&mProducer, &mConsumer);
mCpuConsumer = new android::CpuConsumer(mConsumer, 1);
mCpuConsumer->setName(android::String8("TestWindowContext"));
mCpuConsumer->setDefaultBufferSize(mSize.width(), mSize.height());
mAndroidSurface = new android::Surface(mProducer);
native_window_set_buffers_dimensions(mAndroidSurface.get(),
mSize.width(), mSize.height());
native_window_set_buffers_format(mAndroidSurface.get(),
android::PIXEL_FORMAT_RGBA_8888);
native_window_set_usage(mAndroidSurface.get(),
GRALLOC_USAGE_SW_READ_OFTEN |
GRALLOC_USAGE_SW_WRITE_NEVER |
GRALLOC_USAGE_HW_RENDER);
mRootNode.reset(new android::uirenderer::RenderNode());
mRootNode->incStrong(nullptr);
mRootNode->mutateStagingProperties().setLeftTopRightBottom
(0, 0, mSize.width(), mSize.height());
mRootNode->mutateStagingProperties().setClipToBounds(false);
mRootNode->setPropertyFieldsDirty(android::uirenderer::RenderNode::GENERIC);
ContextFactory factory;
mProxy.reset
(new android::uirenderer::renderthread::RenderProxy(false,
mRootNode.get(),
&factory));
mProxy->loadSystemProperties();
mProxy->initialize(mAndroidSurface.get());
float lightX = mSize.width() / 2.0f;
android::uirenderer::Vector3 lightVector { lightX, -200.0f, 800.0f };
mProxy->setup(mSize.width(), mSize.height(), 800.0f,
255 * 0.075f, 255 * 0.15f);
mProxy->setLightCenter(lightVector);
mCanvas.reset(new
android::uirenderer::DisplayListCanvas(mSize.width(),
mSize.height()));
}
SkCanvas* prepareToDraw() {
//mCanvas->reset(mSize.width(), mSize.height());
mCanvas->clipRect(0, 0, mSize.width(), mSize.height(),
SkRegion::Op::kReplace_Op);
return mCanvas->asSkCanvas();
}
void finishDrawing() {
mRootNode->setStagingDisplayList(mCanvas->finishRecording());
mProxy->syncAndDrawFrame();
// Surprisingly, calling mProxy->fence() here appears to make no difference to
// the timings we record.
}
void fence() {
mProxy->fence();
}
bool capturePixels(SkBitmap* bmp) {
SkImageInfo destinationConfig =
SkImageInfo::Make(mSize.width(), mSize.height(),
kRGBA_8888_SkColorType, kPremul_SkAlphaType);
bmp->allocPixels(destinationConfig);
sk_memset32((uint32_t*) bmp->getPixels(), SK_ColorRED,
mSize.width() * mSize.height());
android::CpuConsumer::LockedBuffer nativeBuffer;
android::status_t retval = mCpuConsumer->lockNextBuffer(&nativeBuffer);
if (retval == android::BAD_VALUE) {
SkDebugf("write_canvas_png() got no buffer; returning transparent");
// No buffer ready to read - commonly triggered by dm sending us
// a no-op source, or calling code that doesn't do anything on this
// backend.
bmp->eraseColor(SK_ColorTRANSPARENT);
return false;
} else if (retval) {
SkDebugf("Failed to lock buffer to read pixels: %d.", retval);
return false;
}
// Move the pixels into the destination SkBitmap
SK_ALWAYSBREAK(nativeBuffer.format == android::PIXEL_FORMAT_RGBA_8888 &&
"Native buffer not RGBA!");
SkImageInfo nativeConfig =
SkImageInfo::Make(nativeBuffer.width, nativeBuffer.height,
kRGBA_8888_SkColorType, kPremul_SkAlphaType);
// Android stride is in pixels, Skia stride is in bytes
SkBitmap nativeWrapper;
bool success =
nativeWrapper.installPixels(nativeConfig, nativeBuffer.data, nativeBuffer.stride * 4);
if (!success) {
SkDebugf("Failed to wrap HWUI buffer in a SkBitmap");
return false;
}
SK_ALWAYSBREAK(bmp->colorType() == kRGBA_8888_SkColorType &&
"Destination buffer not RGBA!");
success =
nativeWrapper.readPixels(destinationConfig, bmp->getPixels(), bmp->rowBytes(), 0, 0);
if (!success) {
SkDebugf("Failed to extract pixels from HWUI buffer");
return false;
}
mCpuConsumer->unlockBuffer(nativeBuffer);
return true;
}
private:
std::unique_ptr<android::uirenderer::RenderNode> mRootNode;
std::unique_ptr<android::uirenderer::renderthread::RenderProxy> mProxy;
std::unique_ptr<android::uirenderer::DisplayListCanvas> mCanvas;
android::sp<android::IGraphicBufferProducer> mProducer;
android::sp<android::IGraphicBufferConsumer> mConsumer;
android::sp<android::CpuConsumer> mCpuConsumer;
android::sp<android::Surface> mAndroidSurface;
SkISize mSize;
};
TestWindowContext::TestWindowContext() :
mData (nullptr) { }
TestWindowContext::~TestWindowContext() {
delete mData;
}
void TestWindowContext::initialize(int width, int height) {
mData = new TestWindowData(SkISize::Make(width, height));
}
SkCanvas* TestWindowContext::prepareToDraw() {
return mData ? mData->prepareToDraw() : nullptr;
}
void TestWindowContext::finishDrawing() {
if (mData) {
mData->finishDrawing();
}
}
void TestWindowContext::fence() {
if (mData) {
mData->fence();
}
}
bool TestWindowContext::capturePixels(SkBitmap* bmp) {
return mData ? mData->capturePixels(bmp) : false;
}
} // namespace uirenderer
} // namespace android