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
214 lines
7.6 KiB
C++
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
|
|
|