Remove direct dependency of external/skia on frameworks/native
Moves all code that needs to link with libgui or libui into
frameworks/native/hwui/utils/TestWindowContext. This allows us to
run Skia automated tests against a HWUI backend to watch for
performance or correctness regressions on Android.
BUG=23556017
TEST=After this change, we can remove libui and frameworks/native/include
from external/skia/Android.mk
R=djsollen@google.com
Change-Id: I6f02a5ff98101e448606a1e892686e2c648b6c8a
diff --git a/libs/hwui/utils/TestWindowContext.cpp b/libs/hwui/utils/TestWindowContext.cpp
new file mode 100644
index 0000000..84aae75
--- /dev/null
+++ b/libs/hwui/utils/TestWindowContext.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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) { }
+
+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
+
diff --git a/libs/hwui/utils/TestWindowContext.h b/libs/hwui/utils/TestWindowContext.h
new file mode 100644
index 0000000..445a11b
--- /dev/null
+++ b/libs/hwui/utils/TestWindowContext.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+#ifndef TESTWINDOWCONTEXT_H_
+#define TESTWINDOWCONTEXT_H_
+
+#include <cutils/compiler.h>
+
+class SkBitmap;
+class SkCanvas;
+
+namespace android {
+
+namespace uirenderer {
+
+/**
+ Wraps all libui/libgui classes and types that external tests depend on,
+ exposing only primitive Skia types.
+*/
+
+class ANDROID_API TestWindowContext {
+
+public:
+
+ TestWindowContext();
+
+ /// We need to know the size of the window.
+ void initialize(int width, int height);
+
+ /// Returns a canvas to draw into; NULL if not yet initialize()d.
+ SkCanvas* prepareToDraw();
+
+ /// Flushes all drawing commands to HWUI; no-op if not yet initialize()d.
+ void finishDrawing();
+
+ /// Blocks until HWUI has processed all pending drawing commands;
+ /// no-op if not yet initialize()d.
+ void fence();
+
+ /// Returns false if not yet initialize()d.
+ bool capturePixels(SkBitmap* bmp);
+
+private:
+ /// Hidden implementation.
+ class TestWindowData;
+
+ TestWindowData* mData;
+
+};
+
+} // namespace uirenderer
+} // namespace android
+
+#endif // TESTWINDOWCONTEXT_H_
+