diff options
Diffstat (limited to 'libs/hwui/tests')
28 files changed, 752 insertions, 247 deletions
diff --git a/libs/hwui/tests/common/LeakChecker.cpp b/libs/hwui/tests/common/LeakChecker.cpp new file mode 100644 index 000000000000..d935382cc9a4 --- /dev/null +++ b/libs/hwui/tests/common/LeakChecker.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2016 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 "LeakChecker.h" + +#include "Caches.h" +#include "TestUtils.h" + +#include <cstdio> +#include <iostream> +#include <memunreachable/memunreachable.h> +#include <unistd.h> +#include <unordered_set> + +using namespace std; + +namespace android { +namespace uirenderer { +namespace test { + +static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) { + // merge them all + UnreachableMemoryInfo merged; + unordered_set<uintptr_t> addrs; + merged.allocation_bytes = 0; + merged.leak_bytes = 0; + merged.num_allocations = 0; + merged.num_leaks = 0; + for (auto& info : infolist) { + // We'll be a little hazzy about these ones and just hope the biggest + // is the most accurate + merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes); + merged.num_allocations = max(merged.num_allocations, info.num_allocations); + for (auto& leak : info.leaks) { + if (addrs.find(leak.begin) == addrs.end()) { + merged.leaks.push_back(leak); + merged.num_leaks++; + merged.leak_bytes += leak.size; + addrs.insert(leak.begin); + } + } + } + + // Now log the result + if (merged.num_leaks) { + cout << endl << "Leaked memory!" << endl; + if (!merged.leaks[0].backtrace.num_frames) { + cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'" + << endl << "and 'setprop libc.debug.malloc.options backtrace=8'" + << " to get backtraces" << endl; + } + cout << merged.ToString(false); + } +} + +void LeakChecker::checkForLeaks() { + // TODO: Until we can shutdown the RT thread we need to do this in + // two passes as GetUnreachableMemory has limited insight into + // thread-local caches so some leaks will not be properly tagged as leaks + UnreachableMemoryInfo rtMemInfo; + TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { + if (Caches::hasInstance()) { + Caches::getInstance().tasks.stop(); + } + // Check for leaks + if (!GetUnreachableMemory(rtMemInfo)) { + cerr << "Failed to get unreachable memory!" << endl; + return; + } + }); + UnreachableMemoryInfo uiMemInfo; + if (!GetUnreachableMemory(uiMemInfo)) { + cerr << "Failed to get unreachable memory!" << endl; + return; + } + logUnreachable({rtMemInfo, uiMemInfo}); +} + +} /* namespace test */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/tests/common/LeakChecker.h b/libs/hwui/tests/common/LeakChecker.h new file mode 100644 index 000000000000..cdf47d6bda80 --- /dev/null +++ b/libs/hwui/tests/common/LeakChecker.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2016 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. + */ +#pragma once + +namespace android { +namespace uirenderer { +namespace test { + +class LeakChecker { +public: + static void checkForLeaks(); +}; // class TestUtils + +} /* namespace test */ +} /* namespace uirenderer */ +} /* namespace android */
\ No newline at end of file diff --git a/libs/hwui/tests/common/TestContext.cpp b/libs/hwui/tests/common/TestContext.cpp index 146e735839d1..1c7e7eef7626 100644 --- a/libs/hwui/tests/common/TestContext.cpp +++ b/libs/hwui/tests/common/TestContext.cpp @@ -62,20 +62,53 @@ TestContext::TestContext() { TestContext::~TestContext() {} sp<Surface> TestContext::surface() { - if (!mSurfaceControl.get()) { - mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), - gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888); - - SurfaceComposerClient::openGlobalTransaction(); - mSurfaceControl->setLayer(0x7FFFFFF); - mSurfaceControl->show(); - SurfaceComposerClient::closeGlobalTransaction(); + if (!mSurface.get()) { + createSurface(); } + return mSurface; +} + +void TestContext::createSurface() { + if (mRenderOffscreen) { + createOffscreenSurface(); + } else { + createWindowSurface(); + } +} - return mSurfaceControl->getSurface(); +void TestContext::createWindowSurface() { + mSurfaceControl = mSurfaceComposerClient->createSurface(String8("HwuiTest"), + gDisplay.w, gDisplay.h, PIXEL_FORMAT_RGBX_8888); + + SurfaceComposerClient::openGlobalTransaction(); + mSurfaceControl->setLayer(0x7FFFFFF); + mSurfaceControl->show(); + SurfaceComposerClient::closeGlobalTransaction(); + mSurface = mSurfaceControl->getSurface(); +} + +void TestContext::createOffscreenSurface() { + sp<IGraphicBufferProducer> producer; + sp<IGraphicBufferConsumer> consumer; + BufferQueue::createBufferQueue(&producer, &consumer); + producer->setMaxDequeuedBufferCount(3); + producer->setAsyncMode(true); + mConsumer = new BufferItemConsumer(consumer, GRALLOC_USAGE_HW_COMPOSER, 4); + mConsumer->setDefaultBufferSize(gDisplay.w, gDisplay.h); + mSurface = new Surface(producer); } void TestContext::waitForVsync() { + if (mConsumer.get()) { + BufferItem buffer; + if (mConsumer->acquireBuffer(&buffer, 0, false) == OK) { + // We assume the producer is internally ordered enough such that + // it is unneccessary to set a release fence + mConsumer->releaseBuffer(buffer); + } + // We running free, go go go! + return; + } #if !HWUI_NULL_GPU // Request vsync mDisplayEventReceiver.requestNextVsync(); diff --git a/libs/hwui/tests/common/TestContext.h b/libs/hwui/tests/common/TestContext.h index 2bbe5dffd9b8..312988b968de 100644 --- a/libs/hwui/tests/common/TestContext.h +++ b/libs/hwui/tests/common/TestContext.h @@ -19,12 +19,16 @@ #include <gui/DisplayEventReceiver.h> #include <gui/ISurfaceComposer.h> +#include <gui/BufferItemConsumer.h> #include <gui/SurfaceComposerClient.h> #include <gui/SurfaceControl.h> #include <gui/Surface.h> #include <ui/DisplayInfo.h> #include <utils/Looper.h> +#include <thread> +#include <atomic> + namespace android { namespace uirenderer { namespace test { @@ -39,15 +43,29 @@ public: TestContext(); ~TestContext(); + // Must be called before surface(); + void setRenderOffscreen(bool renderOffscreen) { + LOG_ALWAYS_FATAL_IF(mSurface.get(), + "Must be called before surface is created"); + mRenderOffscreen = renderOffscreen; + } + sp<Surface> surface(); void waitForVsync(); private: + void createSurface(); + void createWindowSurface(); + void createOffscreenSurface(); + sp<SurfaceComposerClient> mSurfaceComposerClient; sp<SurfaceControl> mSurfaceControl; + sp<BufferItemConsumer> mConsumer; DisplayEventReceiver mDisplayEventReceiver; sp<Looper> mLooper; + sp<Surface> mSurface; + bool mRenderOffscreen; }; } // namespace test diff --git a/libs/hwui/tests/common/TestListViewSceneBase.cpp b/libs/hwui/tests/common/TestListViewSceneBase.cpp new file mode 100644 index 000000000000..847a6ff1181d --- /dev/null +++ b/libs/hwui/tests/common/TestListViewSceneBase.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 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 "TestListViewSceneBase.h" + +#include "TestContext.h" +#include "TestUtils.h" + +#include <utils/Color.h> + +namespace android { +namespace uirenderer { +namespace test { + +void TestListViewSceneBase::createContent(int width, int height, TestCanvas& canvas) { + srand(0); + mItemHeight = dp(60); + mItemSpacing = dp(16); + mItemWidth = std::min((height - mItemSpacing * 2), (int)dp(300)); + mItemLeft = (width - mItemWidth) / 2; + int heightWithSpacing = mItemHeight + mItemSpacing; + for (int y = 0; y < height + (heightWithSpacing - 1); y += heightWithSpacing) { + int id = mListItems.size(); + auto setup = std::bind(&TestListViewSceneBase::createListItem, this, std::placeholders::_1, + std::placeholders::_2, id, mItemWidth, mItemHeight); + auto node = TestUtils::createNode(mItemLeft, y, mItemLeft + mItemWidth, + y + mItemHeight, setup); + mListItems.push_back(node); + } + mListView = TestUtils::createNode(0, 0, width, height, + [this](RenderProperties& props, TestCanvas& canvas) { + for (size_t ci = 0; ci < mListItems.size(); ci++) { + canvas.drawRenderNode(mListItems[ci].get()); + } + }); + + canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode); + canvas.drawRenderNode(mListView.get()); +} + +void TestListViewSceneBase::doFrame(int frameNr) { + int scrollPx = dp(frameNr) * 3; + int itemIndexOffset = scrollPx / (mItemSpacing + mItemHeight); + int pxOffset = -(scrollPx % (mItemSpacing + mItemHeight)); + + TestCanvas canvas( + mListView->stagingProperties().getWidth(), + mListView->stagingProperties().getHeight()); + for (size_t ci = 0; ci < mListItems.size(); ci++) { + // update item position + auto listItem = mListItems[(ci + itemIndexOffset) % mListItems.size()]; + int top = ((int)ci) * (mItemSpacing + mItemHeight) + pxOffset; + listItem->mutateStagingProperties().setLeftTopRightBottom( + mItemLeft, top, mItemLeft + mItemWidth, top + mItemHeight); + listItem->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + + // draw it to parent DisplayList + canvas.drawRenderNode(mListItems[ci].get()); + } + mListView->setStagingDisplayList(canvas.finishRecording(), nullptr); +} + +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestListViewSceneBase.h b/libs/hwui/tests/common/TestListViewSceneBase.h new file mode 100644 index 000000000000..8ffe9929bbd9 --- /dev/null +++ b/libs/hwui/tests/common/TestListViewSceneBase.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016 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. + */ +#pragma once + +#include "TestScene.h" +#include <RenderNode.h> +#include <RenderProperties.h> + +namespace android { +namespace uirenderer { +namespace test { + +class TestListViewSceneBase : public TestScene { +public: + virtual void createListItem(RenderProperties& props, TestCanvas& canvas, int id, + int itemWidth, int itemHeight) = 0; +private: + int mItemHeight; + int mItemSpacing; + int mItemWidth; + int mItemLeft; + sp<RenderNode> mListView; + std::vector< sp<RenderNode> > mListItems; + + void createContent(int width, int height, TestCanvas& canvas) override; + void doFrame(int frameNr) override; +}; + +} // namespace test +} // namespace uirenderer +} // namespace android diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h index 706f2ff75222..4813ff0de174 100644 --- a/libs/hwui/tests/common/TestScene.h +++ b/libs/hwui/tests/common/TestScene.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef TESTS_TESTSCENE_H -#define TESTS_TESTSCENE_H + +#pragma once #include <string> #include <unordered_map> @@ -22,14 +22,9 @@ namespace android { namespace uirenderer { class RenderNode; - -#if HWUI_NEW_OPS class RecordingCanvas; + typedef RecordingCanvas TestCanvas; -#else -class DisplayListCanvas; -typedef DisplayListCanvas TestCanvas; -#endif namespace test { @@ -38,6 +33,7 @@ public: struct Options { int count = 0; int reportFrametimeWeight = 0; + bool renderOffscreen = false; }; template <class T> @@ -75,5 +71,3 @@ public: } // namespace test } // namespace uirenderer } // namespace android - -#endif /* TESTS_TESTSCENE_H */ diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index c762eed616e4..930067a9b2cc 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -20,6 +20,9 @@ #include "DeferredLayerUpdater.h" #include "LayerRenderer.h" +#include <renderthread/EglManager.h> +#include <utils/Unicode.h> + namespace android { namespace uirenderer { @@ -68,7 +71,10 @@ void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text, SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry); SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I()); while (*text != '\0') { - SkUnichar unichar = SkUTF8_NextUnichar(&text); + size_t nextIndex = 0; + int32_t unichar = utf32_from_utf8_at(text, 4, 0, &nextIndex); + text += nextIndex; + glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar); autoCache.getCache()->unicharToGlyph(unichar); @@ -107,12 +113,18 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, void TestUtils::TestTask::run() { // RenderState only valid once RenderThread is running, so queried here - RenderState& renderState = renderthread::RenderThread::getInstance().renderState(); + renderthread::RenderThread& renderThread = renderthread::RenderThread::getInstance(); + bool hasEglContext = renderThread.eglManager().hasEglContext(); + RenderState& renderState = renderThread.renderState(); + if (!hasEglContext) { + renderState.onGLContextCreated(); + } - renderState.onGLContextCreated(); - rtCallback(renderthread::RenderThread::getInstance()); - renderState.flush(Caches::FlushMode::Full); - renderState.onGLContextDestroyed(); + rtCallback(renderThread); + if (!hasEglContext) { + renderState.flush(Caches::FlushMode::Full); + renderState.onGLContextDestroyed(); + } } std::unique_ptr<uint16_t[]> TestUtils::asciiToUtf16(const char* str) { diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 4536bef6d391..9f7fee262acf 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef TEST_UTILS_H -#define TEST_UTILS_H + +#pragma once #include <DeviceInfo.h> #include <DisplayList.h> @@ -25,24 +25,15 @@ #include <renderthread/RenderThread.h> #include <Snapshot.h> -#if HWUI_NEW_OPS #include <RecordedOp.h> #include <RecordingCanvas.h> -#else -#include <DisplayListOp.h> -#include <DisplayListCanvas.h> -#endif #include <memory> namespace android { namespace uirenderer { -#if HWUI_NEW_OPS typedef RecordingCanvas TestCanvas; -#else -typedef DisplayListCanvas TestCanvas; -#endif #define EXPECT_MATRIX_APPROX_EQ(a, b) \ EXPECT_TRUE(TestUtils::matricesAreApproxEqual(a, b)) @@ -251,5 +242,3 @@ private: } /* namespace uirenderer */ } /* namespace android */ - -#endif /* TEST_UTILS_H */ diff --git a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp index f184411b4139..a61f6d0f1e7c 100644 --- a/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp +++ b/libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp @@ -17,8 +17,8 @@ #include "TestSceneBase.h" #include "utils/Color.h" -#include <minikin/Layout.h> #include <hwui/Paint.h> +#include <minikin/Layout.h> #include <cstdio> @@ -56,7 +56,7 @@ public: for (int i = 0; i < 5; i++) { paint.setTextSize(10 + (frameNr % 20) + i * 20); canvas.drawText(text.get(), 0, textLength, textLength, - 0, 100 * (i + 2), kBidi_Force_LTR, paint, nullptr); + 0, 100 * (i + 2), minikin::kBidi_Force_LTR, paint, nullptr); } container->setStagingDisplayList(canvas.finishRecording(), nullptr); diff --git a/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp new file mode 100644 index 000000000000..ba6074fa7840 --- /dev/null +++ b/libs/hwui/tests/common/scenes/ListOfFadedTextAnimation.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 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 "TestSceneBase.h" +#include "tests/common/TestListViewSceneBase.h" + +#include <SkGradientShader.h> + +class ListOfFadedTextAnimation; + +static TestScene::Registrar _ListOfFadedTextAnimation(TestScene::Info{ + "fadingedges", + "A mock ListView of scrolling text with faded edge. Doesn't re-bind/re-record views" + "as they are recycled, so won't upload much content (either glyphs, or bitmaps).", + TestScene::simpleCreateScene<ListOfFadedTextAnimation> +}); + +class ListOfFadedTextAnimation : public TestListViewSceneBase { + void createListItem(RenderProperties& props, TestCanvas& canvas, int id, + int itemWidth, int itemHeight) override { + canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); + int length = dp(100); + canvas.saveLayer(0, 0, length, itemHeight, nullptr, SaveFlags::HasAlphaLayer); + SkPaint textPaint; + textPaint.setTextSize(dp(20)); + textPaint.setAntiAlias(true); + TestUtils::drawUtf8ToCanvas(&canvas, "not that long long text", textPaint, dp(10), dp(30)); + + SkPoint pts[2]; + pts[0].set(0, 0); + pts[1].set(0, 1); + + SkColor colors[2] = {Color::Black, Color::Transparent}; + SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts, colors, NULL, 2, + SkShader::kClamp_TileMode)); + + SkMatrix matrix; + matrix.setScale(1, length); + matrix.postRotate(-90); + SkPaint fadingPaint; + fadingPaint.setShader(s->newWithLocalMatrix(matrix))->unref(); + SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstOut_Mode); + fadingPaint.setXfermode(mode); + canvas.drawRect(0, 0, length, itemHeight, fadingPaint); + canvas.restore(); + } +}; diff --git a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp index 8035dc45f23c..a614044b2468 100644 --- a/libs/hwui/tests/common/scenes/ListViewAnimation.cpp +++ b/libs/hwui/tests/common/scenes/ListViewAnimation.cpp @@ -15,7 +15,7 @@ */ #include "TestSceneBase.h" -#include "utils/Color.h" +#include "tests/common/TestListViewSceneBase.h" #include <cstdio> @@ -28,58 +28,8 @@ static TestScene::Registrar _ListView(TestScene::Info{ TestScene::simpleCreateScene<ListViewAnimation> }); -class ListViewAnimation : public TestScene { -public: - int cardHeight; - int cardSpacing; - int cardWidth; - int cardLeft; - sp<RenderNode> listView; - std::vector< sp<RenderNode> > cards; - void createContent(int width, int height, TestCanvas& canvas) override { - srand(0); - cardHeight = dp(60); - cardSpacing = dp(16); - cardWidth = std::min((height - cardSpacing * 2), (int)dp(300)); - cardLeft = (width - cardWidth) / 2; - - for (int y = 0; y < height + (cardHeight + cardSpacing - 1); y += (cardHeight + cardSpacing)) { - cards.push_back(createCard(cards.size(), y)); - } - listView = TestUtils::createNode(0, 0, width, height, - [this](RenderProperties& props, TestCanvas& canvas) { - for (size_t ci = 0; ci < cards.size(); ci++) { - canvas.drawRenderNode(cards[ci].get()); - } - }); - - canvas.drawColor(Color::Grey_500, SkXfermode::kSrcOver_Mode); - canvas.drawRenderNode(listView.get()); - } - - void doFrame(int frameNr) override { - int scrollPx = dp(frameNr) * 3; - int cardIndexOffset = scrollPx / (cardSpacing + cardHeight); - int pxOffset = -(scrollPx % (cardSpacing + cardHeight)); - - TestCanvas canvas( - listView->stagingProperties().getWidth(), - listView->stagingProperties().getHeight()); - for (size_t ci = 0; ci < cards.size(); ci++) { - // update card position - auto card = cards[(ci + cardIndexOffset) % cards.size()]; - int top = ((int)ci) * (cardSpacing + cardHeight) + pxOffset; - card->mutateStagingProperties().setLeftTopRightBottom( - cardLeft, top, cardLeft + cardWidth, top + cardHeight); - card->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); - - // draw it to parent DisplayList - canvas.drawRenderNode(cards[ci].get()); - } - listView->setStagingDisplayList(canvas.finishRecording(), nullptr); - } -private: - SkBitmap createRandomCharIcon() { +class ListViewAnimation : public TestListViewSceneBase { + SkBitmap createRandomCharIcon(int cardHeight) { int size = cardHeight - (dp(10) * 2); SkBitmap bitmap = TestUtils::createSkBitmap(size, size); SkCanvas canvas(bitmap); @@ -97,7 +47,10 @@ private: paint.setTextAlign(SkPaint::kCenter_Align); paint.setTextSize(size / 2); char charToShow = 'A' + (rand() % 26); - canvas.drawText(&charToShow, 1, size / 2, /*approximate centering*/ size * 0.7, paint); + const SkPoint pos[] = {{ + SkIntToScalar(size / 2), + /*approximate centering*/ SkFloatToScalar(size * 0.7f)}}; + canvas.drawPosText(&charToShow, 1, pos, paint); return bitmap; } @@ -117,34 +70,31 @@ private: return bitmap; } - sp<RenderNode> createCard(int cardId, int top) { - return TestUtils::createNode(cardLeft, top, cardLeft + cardWidth, top + cardHeight, - [this, cardId](RenderProperties& props, TestCanvas& canvas) { - static SkBitmap filledBox = createBoxBitmap(true); - static SkBitmap strokedBox = createBoxBitmap(false); - - // TODO: switch to using round rect clipping, once merging correctly handles that - SkPaint roundRectPaint; - roundRectPaint.setAntiAlias(true); - roundRectPaint.setColor(Color::White); - canvas.drawRoundRect(0, 0, cardWidth, cardHeight, dp(6), dp(6), roundRectPaint); - - SkPaint textPaint; - textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); - textPaint.setTextSize(dp(20)); - textPaint.setAntiAlias(true); - char buf[256]; - snprintf(buf, sizeof(buf), "This card is #%d", cardId); - TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, cardHeight, dp(25)); - textPaint.setTextSize(dp(15)); - TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint, - cardHeight, dp(45)); - - canvas.drawBitmap(createRandomCharIcon(), dp(10), dp(10), nullptr); - - const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox; - canvas.drawBitmap(boxBitmap, cardWidth - dp(10) - boxBitmap.width(), dp(10), nullptr); - }); + void createListItem(RenderProperties& props, TestCanvas& canvas, int cardId, + int itemWidth, int itemHeight) override { + static SkBitmap filledBox = createBoxBitmap(true); + static SkBitmap strokedBox = createBoxBitmap(false); + // TODO: switch to using round rect clipping, once merging correctly handles that + SkPaint roundRectPaint; + roundRectPaint.setAntiAlias(true); + roundRectPaint.setColor(Color::White); + canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint); + + SkPaint textPaint; + textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + textPaint.setColor(rand() % 2 ? Color::Black : Color::Grey_500); + textPaint.setTextSize(dp(20)); + textPaint.setAntiAlias(true); + char buf[256]; + snprintf(buf, sizeof(buf), "This card is #%d", cardId); + TestUtils::drawUtf8ToCanvas(&canvas, buf, textPaint, itemHeight, dp(25)); + textPaint.setTextSize(dp(15)); + TestUtils::drawUtf8ToCanvas(&canvas, "This is some more text on the card", textPaint, + itemHeight, dp(45)); + + canvas.drawBitmap(createRandomCharIcon(itemHeight), dp(10), dp(10), nullptr); + + const SkBitmap& boxBitmap = rand() % 2 ? filledBox : strokedBox; + canvas.drawBitmap(boxBitmap, itemWidth - dp(10) - boxBitmap.width(), dp(10), nullptr); } }; diff --git a/libs/hwui/tests/common/scenes/TestSceneBase.h b/libs/hwui/tests/common/scenes/TestSceneBase.h index 935ddcf9212d..792312a6a7a4 100644 --- a/libs/hwui/tests/common/scenes/TestSceneBase.h +++ b/libs/hwui/tests/common/scenes/TestSceneBase.h @@ -13,10 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef TESTS_SCENES_TESTSCENEBASE_H -#define TESTS_SCENES_TESTSCENEBASE_H -#include "DisplayListCanvas.h" +#pragma once + #include "RecordingCanvas.h" #include "RenderNode.h" #include "tests/common/TestContext.h" @@ -30,5 +29,3 @@ using namespace android; using namespace android::uirenderer; using namespace android::uirenderer::renderthread; using namespace android::uirenderer::test; - -#endif /* TESTS_SCENES_TESTSCENEBASE_H_ */ diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp index c5af06160b62..f03dcbf4c24c 100644 --- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp +++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp @@ -22,6 +22,7 @@ #include "renderthread/RenderProxy.h" #include "renderthread/RenderTask.h" +#include <benchmark/benchmark.h> #include <cutils/log.h> #include <gui/Surface.h> #include <ui/PixelFormat.h> @@ -41,7 +42,7 @@ public: template<class T> class ModifiedMovingAverage { public: - ModifiedMovingAverage(int weight) : mWeight(weight) {} + explicit ModifiedMovingAverage(int weight) : mWeight(weight) {} T add(T today) { if (!mHasValue) { @@ -62,13 +63,62 @@ private: T mAverage; }; -void run(const TestScene::Info& info, const TestScene::Options& opts) { +void outputBenchmarkReport(const TestScene::Info& info, const TestScene::Options& opts, + benchmark::BenchmarkReporter* reporter, RenderProxy* proxy, + double durationInS) { + using namespace benchmark; + + struct ReportInfo { + int percentile; + const char* suffix; + }; + + static std::array<ReportInfo, 4> REPORTS = { + ReportInfo { 50, "_50th" }, + ReportInfo { 90, "_90th" }, + ReportInfo { 95, "_95th" }, + ReportInfo { 99, "_99th" }, + }; + + // Although a vector is used, it must stay with only a single element + // otherwise the BenchmarkReporter will automatically compute + // mean and stddev which doesn't make sense for our usage + std::vector<BenchmarkReporter::Run> reports; + BenchmarkReporter::Run report; + report.benchmark_name = info.name; + report.iterations = static_cast<int64_t>(opts.count); + report.real_accumulated_time = durationInS; + report.cpu_accumulated_time = durationInS; + report.items_per_second = opts.count / durationInS; + reports.push_back(report); + reporter->ReportRuns(reports); + + // Pretend the percentiles are single-iteration runs of the test + // If rendering offscreen skip this as it's fps that's more interesting + // in that test case than percentiles. + if (!opts.renderOffscreen) { + for (auto& ri : REPORTS) { + reports[0].benchmark_name = info.name; + reports[0].benchmark_name += ri.suffix; + durationInS = proxy->frameTimePercentile(ri.percentile) / 1000.0; + reports[0].real_accumulated_time = durationInS; + reports[0].cpu_accumulated_time = durationInS; + reports[0].iterations = 1; + reports[0].items_per_second = 0; + reporter->ReportRuns(reports); + } + } +} + +void run(const TestScene::Info& info, const TestScene::Options& opts, + benchmark::BenchmarkReporter* reporter) { // Switch to the real display gDisplay = getBuiltInDisplay(); std::unique_ptr<TestScene> scene(info.createScene(opts)); TestContext testContext; + testContext.setRenderOffscreen(opts.renderOffscreen); // create the native surface const int width = gDisplay.w; @@ -87,11 +137,16 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { proxy->loadSystemProperties(); proxy->initialize(surface); float lightX = width / 2.0; - proxy->setup(width, height, dp(800.0f), 255 * 0.075, 255 * 0.15); + proxy->setup(dp(800.0f), 255 * 0.075, 255 * 0.15); proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)}); // Do a few cold runs then reset the stats so that the caches are all hot - for (int i = 0; i < 5; i++) { + int warmupFrameCount = 5; + if (opts.renderOffscreen) { + // Do a few more warmups to try and boost the clocks up + warmupFrameCount = 10; + } + for (int i = 0; i < warmupFrameCount; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync); @@ -103,6 +158,7 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight); + nsecs_t start = systemTime(CLOCK_MONOTONIC); for (int i = 0; i < opts.count; i++) { testContext.waitForVsync(); nsecs_t vsync = systemTime(CLOCK_MONOTONIC); @@ -121,6 +177,13 @@ void run(const TestScene::Info& info, const TestScene::Options& opts) { } } } + proxy->fence(); + nsecs_t end = systemTime(CLOCK_MONOTONIC); - proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); + if (reporter) { + outputBenchmarkReport(info, opts, reporter, proxy.get(), + (end - start) / (double) s2ns(1)); + } else { + proxy->dumpProfileInfo(STDOUT_FILENO, DumpFlags::JankStats); + } } diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 02a39501e647..ffeef4599774 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -14,11 +14,14 @@ * limitations under the License. */ +#include "tests/common/LeakChecker.h" #include "tests/common/TestScene.h" #include "protos/hwui.pb.h" #include "Properties.h" +#include <benchmark/benchmark.h> +#include <../src/sysinfo.h> #include <getopt.h> #include <stdio.h> #include <string> @@ -39,8 +42,10 @@ using namespace android::uirenderer::test; static int gRepeatCount = 1; static std::vector<TestScene::Info> gRunTests; static TestScene::Options gOpts; +std::unique_ptr<benchmark::BenchmarkReporter> gBenchmarkReporter; -void run(const TestScene::Info& info, const TestScene::Options& opts); +void run(const TestScene::Info& info, const TestScene::Options& opts, + benchmark::BenchmarkReporter* reporter); static void printHelp() { printf(R"( @@ -121,6 +126,20 @@ static void moveToCpuSet(const char* cpusetName) { close(fd); } +static bool setBenchmarkFormat(const char* format) { + if (!strcmp(format, "tabular")) { + gBenchmarkReporter.reset(new benchmark::ConsoleReporter()); + } else if (!strcmp(format, "json")) { + gBenchmarkReporter.reset(new benchmark::JSONReporter()); + } else if (!strcmp(format, "csv")) { + gBenchmarkReporter.reset(new benchmark::CSVReporter()); + } else { + fprintf(stderr, "Unknown format '%s'", format); + return false; + } + return true; +} + // For options that only exist in long-form. Anything in the // 0-255 range is reserved for short options (which just use their ASCII value) namespace LongOpts { @@ -130,6 +149,8 @@ enum { WaitForGpu, ReportFrametime, CpuSet, + BenchmarkFormat, + Offscreen, }; } @@ -141,6 +162,8 @@ static const struct option LONG_OPTIONS[] = { { "wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu }, { "report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime }, { "cpuset", required_argument, nullptr, LongOpts::CpuSet }, + { "benchmark_format", required_argument, nullptr, LongOpts::BenchmarkFormat }, + { "offscreen", no_argument, nullptr, LongOpts::Offscreen }, { 0, 0, 0, 0 } }; @@ -214,6 +237,20 @@ void parseOptions(int argc, char* argv[]) { moveToCpuSet(optarg); break; + case LongOpts::BenchmarkFormat: + if (!optarg) { + error = true; + break; + } + if (!setBenchmarkFormat(optarg)) { + error = true; + } + break; + + case LongOpts::Offscreen: + gOpts.renderOffscreen = true; + break; + case 'h': printHelp(); exit(EXIT_SUCCESS); @@ -237,12 +274,18 @@ void parseOptions(int argc, char* argv[]) { if (optind < argc) { do { const char* test = argv[optind++]; - auto pos = TestScene::testMap().find(test); - if (pos == TestScene::testMap().end()) { - fprintf(stderr, "Unknown test '%s'\n", test); - exit(EXIT_FAILURE); + if (!strcmp(test, "all")) { + for (auto& iter : TestScene::testMap()) { + gRunTests.push_back(iter.second); + } } else { - gRunTests.push_back(pos->second); + auto pos = TestScene::testMap().find(test); + if (pos == TestScene::testMap().end()) { + fprintf(stderr, "Unknown test '%s'\n", test); + exit(EXIT_FAILURE); + } else { + gRunTests.push_back(pos->second); + } } } while (optind < argc); } else { @@ -255,12 +298,36 @@ int main(int argc, char* argv[]) { gOpts.count = 150; parseOptions(argc, argv); + if (!gBenchmarkReporter && gOpts.renderOffscreen) { + gBenchmarkReporter.reset(new benchmark::ConsoleReporter()); + } + + if (gBenchmarkReporter) { + size_t name_field_width = 10; + for (auto&& test : gRunTests) { + name_field_width = std::max<size_t>(name_field_width, test.name.size()); + } + // _50th, _90th, etc... + name_field_width += 5; + + benchmark::BenchmarkReporter::Context context; + context.num_cpus = benchmark::NumCPUs(); + context.mhz_per_cpu = benchmark::CyclesPerSecond() / 1000000.0f; + context.cpu_scaling_enabled = benchmark::CpuScalingEnabled(); + context.name_field_width = name_field_width; + gBenchmarkReporter->ReportContext(context); + } for (int i = 0; i < gRepeatCount; i++) { for (auto&& test : gRunTests) { - run(test, gOpts); + run(test, gOpts, gBenchmarkReporter.get()); } } - printf("Success!\n"); + + if (gBenchmarkReporter) { + gBenchmarkReporter->Finalize(); + } + + LeakChecker::checkForLeaks(); return 0; } diff --git a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp index 06b68d1dea8f..ed3b84753eeb 100644 --- a/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp +++ b/libs/hwui/tests/microbench/DisplayListCanvasBench.cpp @@ -17,21 +17,13 @@ #include <benchmark/benchmark.h> #include "DisplayList.h" -#if HWUI_NEW_OPS #include "RecordingCanvas.h" -#else -#include "DisplayListCanvas.h" -#endif #include "tests/common/TestUtils.h" using namespace android; using namespace android::uirenderer; -#if HWUI_NEW_OPS typedef RecordingCanvas TestCanvas; -#else -typedef DisplayListCanvas TestCanvas; -#endif void BM_DisplayList_alloc(benchmark::State& benchState) { while (benchState.KeepRunning()) { @@ -169,3 +161,37 @@ void BM_CanvasState_translate(benchmark::State& benchState) { } } BENCHMARK(BM_CanvasState_translate); + +void BM_DisplayListCanvas_basicViewGroupDraw(benchmark::State& benchState) { + sp<RenderNode> child = TestUtils::createNode(50, 50, 100, 100, + [](auto& props, auto& canvas) { + canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + }); + + TestCanvas canvas(100, 100); + delete canvas.finishRecording(); + + while (benchState.KeepRunning()) { + canvas.resetRecording(200, 200); + canvas.setHighContrastText(false); + canvas.translate(0, 0); // mScrollX, mScrollY + + // Clip to padding + // Can expect ~25% of views to have clip to padding with a non-null padding + int clipRestoreCount = canvas.save(SaveFlags::MatrixClip); + canvas.clipRect(1, 1, 199, 199, SkRegion::kIntersect_Op); + + canvas.insertReorderBarrier(true); + + // Draw child loop + for (int i = 0; i < benchState.range_x(); i++) { + canvas.drawRenderNode(child.get()); + } + + canvas.insertReorderBarrier(false); + canvas.restoreToCount(clipRestoreCount); + + delete canvas.finishRecording(); + } +} +BENCHMARK(BM_DisplayListCanvas_basicViewGroupDraw)->Arg(1)->Arg(5)->Arg(10); diff --git a/libs/hwui/tests/microbench/TaskManagerBench.cpp b/libs/hwui/tests/microbench/TaskManagerBench.cpp index c6b9f3bca55f..cf47f273c144 100644 --- a/libs/hwui/tests/microbench/TaskManagerBench.cpp +++ b/libs/hwui/tests/microbench/TaskManagerBench.cpp @@ -29,7 +29,7 @@ class TrivialTask : public Task<char> {}; class TrivialProcessor : public TaskProcessor<char> { public: - TrivialProcessor(TaskManager* manager) + explicit TrivialProcessor(TaskManager* manager) : TaskProcessor(manager) {} virtual ~TrivialProcessor() {} virtual void onProcess(const sp<Task<char> >& task) override { diff --git a/libs/hwui/tests/microbench/how_to_run.txt b/libs/hwui/tests/microbench/how_to_run.txt index e6f80b278276..915fe5d959f9 100755 --- a/libs/hwui/tests/microbench/how_to_run.txt +++ b/libs/hwui/tests/microbench/how_to_run.txt @@ -1,4 +1,3 @@ mmm -j8 frameworks/base/libs/hwui && -adb push $ANDROID_PRODUCT_OUT/data/local/tmp/hwuimicro \ - /data/local/tmp/hwuimicro && - adb shell /data/local/tmp/hwuimicro +adb push $OUT/data/benchmarktest/hwuimicro/hwuimicro /data/benchmarktest/hwuimicro/hwuimicro && +adb shell /data/benchmarktest/hwuimicro/hwuimicro diff --git a/libs/hwui/tests/microbench/main.cpp b/libs/hwui/tests/microbench/main.cpp index a0157bc4f9ef..9771c85382b4 100644 --- a/libs/hwui/tests/microbench/main.cpp +++ b/libs/hwui/tests/microbench/main.cpp @@ -14,6 +14,19 @@ * limitations under the License. */ +#include "debug/GlesDriver.h" +#include "debug/NullGlesDriver.h" + #include <benchmark/benchmark.h> -BENCHMARK_MAIN(); +#include <memory> + +using namespace android; +using namespace android::uirenderer; + +int main(int argc, char** argv) { + debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>()); + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); + return 0; +} diff --git a/libs/hwui/tests/scripts/prep_buller.sh b/libs/hwui/tests/scripts/prep_buller.sh new file mode 100755 index 000000000000..b2f68bd44e7d --- /dev/null +++ b/libs/hwui/tests/scripts/prep_buller.sh @@ -0,0 +1,49 @@ +#buller is bullhead & angler (☞゚ヮ゚)☞ + +nr=$(adb shell cat /proc/cpuinfo | grep processor | wc -l) +cpubase=/sys/devices/system/cpu +gov=cpufreq/scaling_governor + +adb root +adb wait-for-device +adb shell stop thermal-engine +adb shell stop perfd + +# LITTLE cores +# 384000 460800 600000 672000 787200 864000 960000 1248000 1440000 +# BIG cores +# 384000 480000 633600 768000 864000 960000 1248000 1344000 1440000 +# 1536000 1632000 1689600 1824000 + +cpu=0 +S=960000 +while [ $((cpu < 4)) -eq 1 ]; do + echo "Setting cpu $cpu to $S hz" + adb shell "echo 1 > $cpubase/cpu${cpu}/online" + adb shell "echo userspace > $cpubase/cpu${cpu}/$gov" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_max_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_min_freq" + adb shell "echo $S > $cpubase/cpu${cpu}/cpufreq/scaling_setspeed" + cpu=$(($cpu + 1)) +done + +while [ $((cpu < $nr)) -eq 1 ]; do + echo "disable cpu $cpu" + adb shell "echo 0 > $cpubase/cpu${cpu}/online" + cpu=$(($cpu + 1)) +done + +echo "setting GPU bus and idle timer" +adb shell "echo 0 > /sys/class/kgsl/kgsl-3d0/bus_split" +adb shell "echo 1 > /sys/class/kgsl/kgsl-3d0/force_clk_on" +adb shell "echo 10000 > /sys/class/kgsl/kgsl-3d0/idle_timer" + +#0 762 1144 1525 2288 3509 4173 5271 5928 7904 9887 11863 +adb shell "echo 11863 > /sys/class/devfreq/qcom,gpubw.70/min_freq" &> /dev/null +adb shell "echo 11863 > /sys/class/devfreq/qcom,gpubw.19/min_freq" &> /dev/null + +#600000000 510000000 450000000 390000000 305000000 180000000 +echo "performance mode, 305 MHz" +adb shell "echo performance > /sys/class/kgsl/kgsl-3d0/devfreq/governor" +adb shell "echo 305000000 > /sys/class/kgsl/kgsl-3d0/devfreq/min_freq" +adb shell "echo 305000000 > /sys/class/kgsl/kgsl-3d0/devfreq/max_freq" diff --git a/libs/hwui/tests/scripts/prep_volantis.sh b/libs/hwui/tests/scripts/prep_volantis.sh index 0572ee55c9b9..6407844afa90 100755 --- a/libs/hwui/tests/scripts/prep_volantis.sh +++ b/libs/hwui/tests/scripts/prep_volantis.sh @@ -16,16 +16,8 @@ adb root adb wait-for-device -adb shell stop mpdecision adb shell stop perfd -adb shell stop -for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do - adb shell kill $pid -done -adb shell setprop debug.egl.traceGpuCompletion 1 -adb shell daemonize surfaceflinger -sleep 3 -adb shell setprop service.bootanim.exit 1 +adb shell stop thermal-engine # cpu possible frequencies # 204000 229500 255000 280500 306000 331500 357000 382500 408000 433500 459000 diff --git a/libs/hwui/tests/scripts/stopruntime.sh b/libs/hwui/tests/scripts/stopruntime.sh new file mode 100644 index 000000000000..bb8fcb6f3ee2 --- /dev/null +++ b/libs/hwui/tests/scripts/stopruntime.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Copyright (C) 2016 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. + +adb root +adb wait-for-device +adb shell stop + +for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do + adb shell kill $pid +done +adb shell setprop debug.egl.traceGpuCompletion 1 +adb shell daemonize surfaceflinger +sleep 3 +adb shell setprop service.bootanim.exit 1 diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index af54e079daab..e5fc55611be3 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -1953,7 +1953,7 @@ static void testProperty(std::function<void(RenderProperties&)> propSetupCallbac std::function<void(const RectOp&, const BakedOpState&)> opValidateCallback) { class PropertyTestRenderer : public TestRendererBase { public: - PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) + explicit PropertyTestRenderer(std::function<void(const RectOp&, const BakedOpState&)> callback) : mCallback(callback) {} void onRectOp(const RectOp& op, const BakedOpState& state) override { EXPECT_EQ(mIndex++, 0); @@ -2075,7 +2075,7 @@ void testSaveLayerAlphaClip(SaveLayerAlphaData* outObservedData, std::function<void(RenderProperties&)> propSetupCallback) { class SaveLayerAlphaClipTestRenderer : public TestRendererBase { public: - SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) + explicit SaveLayerAlphaClipTestRenderer(SaveLayerAlphaData* outData) : mOutData(outData) {} OffscreenBuffer* startTemporaryLayer(uint32_t width, uint32_t height) override { diff --git a/libs/hwui/tests/unit/GlopBuilderTests.cpp b/libs/hwui/tests/unit/GlopBuilderTests.cpp index 95543d33b1ef..67e58e2aba0d 100644 --- a/libs/hwui/tests/unit/GlopBuilderTests.cpp +++ b/libs/hwui/tests/unit/GlopBuilderTests.cpp @@ -85,9 +85,6 @@ static void expectTransformEq(Glop::Transform& expectedTransform, Glop::Transfor } static void expectGlopEq(Glop& expectedGlop, Glop& builtGlop) { -#if !HWUI_NEW_OPS - EXPECT_EQ(expectedGlop.bounds, builtGlop.bounds); -#endif expectBlendEq(expectedGlop.blend, builtGlop.blend); expectFillEq(expectedGlop.fill, builtGlop.fill); expectMeshEq(expectedGlop.mesh, builtGlop.mesh); @@ -138,9 +135,6 @@ RENDERTHREAD_TEST(GlopBuilder, rectSnapTest) { // unit quad also should be translate by additional (0.3, 0.3) to snap to exact pixels. goldenGlop->transform.modelView.loadTranslate(1.3, 1.3, 0); goldenGlop->transform.modelView.scale(99, 99, 1); -#if !HWUI_NEW_OPS - goldenGlop->bounds = android::uirenderer::Rect(1.70, 1.70, 100.70, 100.70); -#endif goldenGlop->transform.canvas = simpleTranslate; goldenGlop->fill.texture.filter = GL_NEAREST; expectGlopEq(*goldenGlop, glop); diff --git a/libs/hwui/tests/unit/MeshStateTests.cpp b/libs/hwui/tests/unit/MeshStateTests.cpp new file mode 100644 index 000000000000..0881fa246afd --- /dev/null +++ b/libs/hwui/tests/unit/MeshStateTests.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2016 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 <debug/MockGlesDriver.h> +#include <debug/ScopedReplaceDriver.h> +#include <gtest/gtest.h> +#include <gmock/gmock.h> +#include <renderstate/MeshState.h> +#include <tests/common/TestUtils.h> + +using namespace android::uirenderer; +using namespace testing; + +RENDERTHREAD_TEST(MeshState, genOrUpdate) { + debug::ScopedReplaceDriver<debug::MockGlesDriver> driverRef; + auto& mockGlDriver = driverRef.get(); + EXPECT_CALL(mockGlDriver, glGenBuffers_(_, _)).WillOnce(SetArgPointee<1>(35)); + EXPECT_CALL(mockGlDriver, glBindBuffer_(_, 35)); + EXPECT_CALL(mockGlDriver, glBufferData_(_, _, _, _)); + + GLuint buffer = 0; + renderThread.renderState().meshState().genOrUpdateMeshBuffer(&buffer, 10, nullptr, GL_DYNAMIC_DRAW); +}
\ No newline at end of file diff --git a/libs/hwui/tests/unit/RecordingCanvasTests.cpp b/libs/hwui/tests/unit/RecordingCanvasTests.cpp index 9cd504ec0af7..d75ceb200a88 100644 --- a/libs/hwui/tests/unit/RecordingCanvasTests.cpp +++ b/libs/hwui/tests/unit/RecordingCanvasTests.cpp @@ -746,7 +746,7 @@ TEST(RecordingCanvas, drawText) { paint.setTextSize(20); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO"); - canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL); + canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL); }); int count = 0; @@ -770,7 +770,7 @@ TEST(RecordingCanvas, drawTextInHighContrast) { paint.setTextSize(20); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); std::unique_ptr<uint16_t[]> dst = TestUtils::asciiToUtf16("HELLO"); - canvas.drawText(dst.get(), 0, 5, 5, 25, 25, kBidi_Force_LTR, paint, NULL); + canvas.drawText(dst.get(), 0, 5, 5, 25, 25, minikin::kBidi_Force_LTR, paint, NULL); }); int count = 0; diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index cf76a8691dcd..132601efb543 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -68,7 +68,7 @@ TEST(RenderNode, hasParents) { TEST(RenderNode, releasedCallback) { class DecRefOnReleased : public GlFunctorLifecycleListener { public: - DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {} + explicit DecRefOnReleased(int* refcnt) : mRefCnt(refcnt) {} void onGlFunctorReleased(Functor* functor) override { *mRefCnt -= 1; } @@ -105,8 +105,9 @@ TEST(RenderNode, releasedCallback) { RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { ContextFactory contextFactory; - CanvasContext canvasContext(renderThread, false, nullptr, &contextFactory); - TreeInfo info(TreeInfo::MODE_RT_ONLY, canvasContext); + std::unique_ptr<CanvasContext> canvasContext(CanvasContext::create( + renderThread, false, nullptr, &contextFactory)); + TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get()); DamageAccumulator damageAccumulator; info.damageAccumulator = &damageAccumulator; info.observer = nullptr; @@ -128,5 +129,5 @@ RENDERTHREAD_TEST(RenderNode, prepareTree_nullableDisplayList) { nullDLNode->prepareTree(info); } - canvasContext.destroy(nullptr); + canvasContext->destroy(nullptr); } diff --git a/libs/hwui/tests/unit/main.cpp b/libs/hwui/tests/unit/main.cpp index 409a12d37693..d05bdbf1709e 100644 --- a/libs/hwui/tests/unit/main.cpp +++ b/libs/hwui/tests/unit/main.cpp @@ -15,19 +15,15 @@ */ #include "gtest/gtest.h" +#include "gmock/gmock.h" #include "Caches.h" +#include "debug/GlesDriver.h" +#include "debug/NullGlesDriver.h" #include "thread/TaskManager.h" -#include "tests/common/TestUtils.h" +#include "tests/common/LeakChecker.h" -#include <memunreachable/memunreachable.h> - -#include <cstdio> -#include <iostream> -#include <map> -#include <unordered_set> #include <signal.h> -#include <unistd.h> using namespace std; using namespace android; @@ -54,67 +50,6 @@ static void gtestSigHandler(int sig, siginfo_t* siginfo, void* context) { raise(sig); } -static void logUnreachable(initializer_list<UnreachableMemoryInfo> infolist) { - // merge them all - UnreachableMemoryInfo merged; - unordered_set<uintptr_t> addrs; - merged.allocation_bytes = 0; - merged.leak_bytes = 0; - merged.num_allocations = 0; - merged.num_leaks = 0; - for (auto& info : infolist) { - // We'll be a little hazzy about these ones and just hope the biggest - // is the most accurate - merged.allocation_bytes = max(merged.allocation_bytes, info.allocation_bytes); - merged.num_allocations = max(merged.num_allocations, info.num_allocations); - for (auto& leak : info.leaks) { - if (addrs.find(leak.begin) == addrs.end()) { - merged.leaks.push_back(leak); - merged.num_leaks++; - merged.leak_bytes += leak.size; - addrs.insert(leak.begin); - } - } - } - - // Now log the result - if (merged.num_leaks) { - cout << endl << "Leaked memory!" << endl; - if (!merged.leaks[0].backtrace.num_frames) { - cout << "Re-run with 'setprop libc.debug.malloc.program hwui_unit_test'" - << endl << "and 'setprop libc.debug.malloc.options backtrace=8'" - << " to get backtraces" << endl; - } - cout << merged.ToString(false); - } -} - -static void checkForLeaks() { - // TODO: Until we can shutdown the RT thread we need to do this in - // two passes as GetUnreachableMemory has limited insight into - // thread-local caches so some leaks will not be properly tagged as leaks - nsecs_t before = systemTime(); - UnreachableMemoryInfo rtMemInfo; - TestUtils::runOnRenderThread([&rtMemInfo](renderthread::RenderThread& thread) { - if (Caches::hasInstance()) { - Caches::getInstance().tasks.stop(); - } - // Check for leaks - if (!GetUnreachableMemory(rtMemInfo)) { - cerr << "Failed to get unreachable memory!" << endl; - return; - } - }); - UnreachableMemoryInfo uiMemInfo; - if (!GetUnreachableMemory(uiMemInfo)) { - cerr << "Failed to get unreachable memory!" << endl; - return; - } - logUnreachable({rtMemInfo, uiMemInfo}); - nsecs_t after = systemTime(); - cout << "Leak check took " << ns2ms(after - before) << "ms" << endl; -} - int main(int argc, char* argv[]) { // Register a crash handler struct sigaction sa; @@ -127,10 +62,15 @@ int main(int argc, char* argv[]) { gSigChain.insert(pair<int, struct sigaction>(sig, old_sa)); } + // Replace the default GLES driver + debug::GlesDriver::replace(std::make_unique<debug::NullGlesDriver>()); + // Run the tests testing::InitGoogleTest(&argc, argv); + testing::InitGoogleMock(&argc, argv); + int ret = RUN_ALL_TESTS(); - checkForLeaks(); + test::LeakChecker::checkForLeaks(); return ret; } |