sf-latency: Write a benchmark for RenderEngine

Bug: 193240340
Test: mmma -j frameworks/native/libs/renderengine/bench/
      && adb push out/.../librenderengine_bench
                  /data/.../librenderengine_bench
      && adb shell /data/.../librenderengine_bench

Write a benchmark using Google-Benchmark to track the performance of
RenderEngine.

RenderEngineBench.cpp:
- Contains the benchmarks to run.
- Write a helper function that times calls to RenderEngine::drawLayers.
- Write a single benchmark that times drawing a mock homescreen plus a
  blur layer, using a blur radius used in practice.
  Decode the homescreen into a buffer with CPU support, and then copy it
  to one without, which better represents actual use.
- Use RenderEngineType to determine which subclass to benchmark. The
  only existing benchmark uses SKIA_GL, since we intend to time just
  drawLayers, but future benchmarks will want to time threading aspects
  with SKIA_GL_THREADED, and we may add more RenderEngineTypes in the
  future.

Codec.cpp:
- Write methods for decoding and encoding a buffer.

Flags.cpp:
- Extra flags that can be passed to the executable.
- --save allows encoding the result to a file to verify it looks as
  intended.
- parse --help to describe --save and any future flags specific to
  RenderEngineBench.

RenderEngineBench.h:
- header file for methods used across cpp files. Use a single common
  header since otherwise we would have several small headers.

homescreen.png:
- Mock homescreen to draw in drawLayers

Change-Id: I81a1a8a30a1c20985bbf066d2ba4d5f1fd1f6dc3
diff --git a/libs/renderengine/benchmark/Android.bp b/libs/renderengine/benchmark/Android.bp
new file mode 100644
index 0000000..5968399
--- /dev/null
+++ b/libs/renderengine/benchmark/Android.bp
@@ -0,0 +1,54 @@
+// Copyright 2021 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+cc_benchmark {
+    name: "librenderengine_bench",
+    defaults: ["skia_deps", "surfaceflinger_defaults"],
+    srcs: [
+        "main.cpp",
+        "Codec.cpp",
+        "Flags.cpp",
+        "RenderEngineBench.cpp",
+    ],
+    static_libs: [
+        "librenderengine",
+    ],
+    cflags: [
+        "-DLOG_TAG=\"RenderEngineBench\"",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libjnigraphics",
+        "libgui",
+        "liblog",
+        "libnativewindow",
+        "libprocessgroup",
+        "libsync",
+        "libui",
+        "libutils",
+    ],
+
+    data: [ "resources/*"],
+}
diff --git a/libs/renderengine/benchmark/Codec.cpp b/libs/renderengine/benchmark/Codec.cpp
new file mode 100644
index 0000000..80e4fc4
--- /dev/null
+++ b/libs/renderengine/benchmark/Codec.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021 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 <RenderEngineBench.h>
+#include <android/bitmap.h>
+#include <android/data_space.h>
+#include <android/imagedecoder.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/RenderEngine.h>
+#include <sys/types.h>
+
+using namespace android;
+using namespace android::renderengine;
+
+namespace {
+struct DecoderDeleter {
+    void operator()(AImageDecoder* decoder) { AImageDecoder_delete(decoder); }
+};
+
+using AutoDecoderDeleter = std::unique_ptr<AImageDecoder, DecoderDeleter>;
+
+bool ok(int aImageDecoderResult, const char* path, const char* method) {
+    if (aImageDecoderResult == ANDROID_IMAGE_DECODER_SUCCESS) {
+        return true;
+    }
+
+    ALOGE("Failed AImageDecoder_%s on '%s' with error '%s'", method, path,
+          AImageDecoder_resultToString(aImageDecoderResult));
+    return false;
+}
+} // namespace
+
+namespace renderenginebench {
+
+void decode(const char* path, const sp<GraphicBuffer>& buffer) {
+    base::unique_fd fd{open(path, O_RDONLY)};
+    if (fd.get() < 0) {
+        ALOGE("Failed to open %s", path);
+        return;
+    }
+
+    AImageDecoder* decoder{nullptr};
+    auto result = AImageDecoder_createFromFd(fd.get(), &decoder);
+    if (!ok(result, path, "createFromFd")) {
+        return;
+    }
+
+    AutoDecoderDeleter deleter(decoder);
+
+    LOG_ALWAYS_FATAL_IF(buffer->getWidth() <= 0 || buffer->getHeight() <= 0,
+                        "Impossible buffer size!");
+    auto width = static_cast<int32_t>(buffer->getWidth());
+    auto height = static_cast<int32_t>(buffer->getHeight());
+    result = AImageDecoder_setTargetSize(decoder, width, height);
+    if (!ok(result, path, "setTargetSize")) {
+        return;
+    }
+
+    void* pixels{nullptr};
+    int32_t stride{0};
+    if (auto status = buffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, &pixels,
+                                   nullptr /*outBytesPerPixel*/, &stride);
+        status < 0) {
+        ALOGE("Failed to lock pixels!");
+        return;
+    }
+
+    result = AImageDecoder_decodeImage(decoder, pixels, static_cast<size_t>(stride),
+                                       static_cast<size_t>(stride * height));
+    if (auto status = buffer->unlock(); status < 0) {
+        ALOGE("Failed to unlock pixels!");
+    }
+
+    // For the side effect of logging.
+    (void)ok(result, path, "decodeImage");
+}
+
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer) {
+    base::unique_fd fd{open(path, O_WRONLY | O_CREAT, S_IWUSR)};
+    if (fd.get() < 0) {
+        ALOGE("Failed to open %s", path);
+        return;
+    }
+
+    void* pixels{nullptr};
+    int32_t stride{0};
+    if (auto status = buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &pixels,
+                                   nullptr /*outBytesPerPixel*/, &stride);
+        status < 0) {
+        ALOGE("Failed to lock pixels!");
+        return;
+    }
+
+    AndroidBitmapInfo info{
+            .width = buffer->getWidth(),
+            .height = buffer->getHeight(),
+            .stride = static_cast<uint32_t>(stride),
+            .format = ANDROID_BITMAP_FORMAT_RGBA_8888,
+            .flags = ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE,
+    };
+    int result = AndroidBitmap_compress(&info, ADATASPACE_SRGB, pixels,
+                                        ANDROID_BITMAP_COMPRESS_FORMAT_JPEG, 80, &fd,
+                                        [](void* fdPtr, const void* data, size_t size) -> bool {
+                                            const ssize_t bytesWritten =
+                                                    write(reinterpret_cast<base::unique_fd*>(fdPtr)
+                                                                  ->get(),
+                                                          data, size);
+                                            return bytesWritten > 0 &&
+                                                    static_cast<size_t>(bytesWritten) == size;
+                                        });
+    if (result == ANDROID_BITMAP_RESULT_SUCCESS) {
+        ALOGD("Successfully encoded to '%s'", path);
+    } else {
+        ALOGE("Failed to encode to %s with error %d", path, result);
+    }
+
+    if (auto status = buffer->unlock(); status < 0) {
+        ALOGE("Failed to unlock pixels!");
+    }
+}
+
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/Flags.cpp b/libs/renderengine/benchmark/Flags.cpp
new file mode 100644
index 0000000..c5d5156
--- /dev/null
+++ b/libs/renderengine/benchmark/Flags.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021 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 <log/log.h>
+#include <stdio.h>
+#include <string.h>
+
+namespace {
+bool gSave = false;
+}
+
+namespace renderenginebench {
+
+void parseFlagsForHelp(int argc, char** argv) {
+    for (int i = 0; i < argc; i++) {
+        if (!strcmp(argv[i], "--help")) {
+            printf("RenderEngineBench-specific flags:\n");
+            printf("[--save]: Save the output to the device to confirm drawing result.\n");
+            break;
+        }
+    }
+}
+
+void parseFlags(int argc, char** argv) {
+    for (int i = 0; i < argc; i++) {
+        if (!strcmp(argv[i], "--save")) {
+            gSave = true;
+        }
+    }
+}
+
+bool save() {
+    return gSave;
+}
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/RenderEngineBench.cpp b/libs/renderengine/benchmark/RenderEngineBench.cpp
new file mode 100644
index 0000000..719b855
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2021 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 <RenderEngineBench.h>
+#include <android-base/file.h>
+#include <benchmark/benchmark.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+#include <renderengine/ExternalTexture.h>
+#include <renderengine/LayerSettings.h>
+#include <renderengine/RenderEngine.h>
+
+#include <mutex>
+
+using namespace android;
+using namespace android::renderengine;
+
+///////////////////////////////////////////////////////////////////////////////
+//  Helpers for Benchmark::Apply
+///////////////////////////////////////////////////////////////////////////////
+
+std::string RenderEngineTypeName(RenderEngine::RenderEngineType type) {
+    switch (type) {
+        case RenderEngine::RenderEngineType::SKIA_GL_THREADED:
+            return "skiaglthreaded";
+        case RenderEngine::RenderEngineType::SKIA_GL:
+            return "skiagl";
+        case RenderEngine::RenderEngineType::GLES:
+        case RenderEngine::RenderEngineType::THREADED:
+            LOG_ALWAYS_FATAL("GLESRenderEngine is deprecated - why time it?");
+            return "unused";
+    }
+}
+
+/**
+ * Passed (indirectly - see RunSkiaGL) to Benchmark::Apply to create a Benchmark
+ * which specifies which RenderEngineType it uses.
+ *
+ * This simplifies calling ->Arg(type)->Arg(type) and provides strings to make
+ * it obvious which version is being run.
+ *
+ * @param b The benchmark family
+ * @param type The type of RenderEngine to use.
+ */
+static void AddRenderEngineType(benchmark::internal::Benchmark* b,
+                                RenderEngine::RenderEngineType type) {
+    b->Arg(static_cast<int64_t>(type));
+    b->ArgName(RenderEngineTypeName(type));
+}
+
+/**
+ * Run a benchmark once using SKIA_GL.
+ */
+static void RunSkiaGL(benchmark::internal::Benchmark* b) {
+    AddRenderEngineType(b, RenderEngine::RenderEngineType::SKIA_GL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//  Helpers for calling drawLayers
+///////////////////////////////////////////////////////////////////////////////
+
+std::pair<uint32_t, uint32_t> getDisplaySize() {
+    // These will be retrieved from a ui::Size, which stores int32_t, but they will be passed
+    // to GraphicBuffer, which wants uint32_t.
+    static uint32_t width, height;
+    std::once_flag once;
+    std::call_once(once, []() {
+        auto surfaceComposerClient = SurfaceComposerClient::getDefault();
+        auto displayToken = surfaceComposerClient->getInternalDisplayToken();
+        ui::DisplayMode displayMode;
+        if (surfaceComposerClient->getActiveDisplayMode(displayToken, &displayMode) < 0) {
+            LOG_ALWAYS_FATAL("Failed to get active display mode!");
+        }
+        auto w = displayMode.resolution.width;
+        auto h = displayMode.resolution.height;
+        LOG_ALWAYS_FATAL_IF(w <= 0 || h <= 0, "Invalid display size!");
+        width = static_cast<uint32_t>(w);
+        height = static_cast<uint32_t>(h);
+    });
+    return std::pair<uint32_t, uint32_t>(width, height);
+}
+
+// This value doesn't matter, as it's not read. TODO(b/199918329): Once we remove
+// GLESRenderEngine we can remove this, too.
+static constexpr const bool kUseFrameBufferCache = false;
+
+static std::unique_ptr<RenderEngine> createRenderEngine(RenderEngine::RenderEngineType type) {
+    auto args = RenderEngineCreationArgs::Builder()
+                        .setPixelFormat(static_cast<int>(ui::PixelFormat::RGBA_8888))
+                        .setImageCacheSize(1)
+                        .setEnableProtectedContext(true)
+                        .setPrecacheToneMapperShaderOnly(false)
+                        .setSupportsBackgroundBlur(true)
+                        .setContextPriority(RenderEngine::ContextPriority::REALTIME)
+                        .setRenderEngineType(type)
+                        .setUseColorManagerment(true)
+                        .build();
+    return RenderEngine::create(args);
+}
+
+static std::shared_ptr<ExternalTexture> allocateBuffer(RenderEngine& re,
+                                                       uint32_t width,
+                                                       uint32_t height,
+                                                       uint64_t extraUsageFlags = 0,
+                                                       std::string name = "output") {
+    return std::make_shared<ExternalTexture>(new GraphicBuffer(width, height,
+                                                               HAL_PIXEL_FORMAT_RGBA_8888, 1,
+                                                               GRALLOC_USAGE_HW_RENDER |
+                                                                       GRALLOC_USAGE_HW_TEXTURE |
+                                                                       extraUsageFlags,
+                                                               std::move(name)),
+                                             re,
+                                             ExternalTexture::Usage::READABLE |
+                                                     ExternalTexture::Usage::WRITEABLE);
+}
+
+static std::shared_ptr<ExternalTexture> copyBuffer(RenderEngine& re,
+                                                   std::shared_ptr<ExternalTexture> original,
+                                                   uint64_t extraUsageFlags, std::string name) {
+    const uint32_t width = original->getBuffer()->getWidth();
+    const uint32_t height = original->getBuffer()->getHeight();
+    auto texture = allocateBuffer(re, width, height, extraUsageFlags, name);
+
+    const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+    DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .maxLuminance = 500,
+    };
+
+    const FloatRect layerRect(0, 0, width, height);
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = layerRect,
+                    },
+            .source =
+                    PixelSource{
+                            .buffer =
+                                    Buffer{
+                                            .buffer = original,
+                                    },
+                    },
+            .alpha = half(1.0f),
+    };
+    auto layers = std::vector<LayerSettings>{layer};
+
+    auto [status, drawFence] =
+            re.drawLayers(display, layers, texture, kUseFrameBufferCache, base::unique_fd()).get();
+    sp<Fence> waitFence = new Fence(std::move(drawFence));
+    waitFence->waitForever(LOG_TAG);
+    return texture;
+}
+
+static void benchDrawLayers(RenderEngine& re, const std::vector<LayerSettings>& layers,
+                            benchmark::State& benchState, const char* saveFileName) {
+    auto [width, height] = getDisplaySize();
+    auto outputBuffer = allocateBuffer(re, width, height);
+
+    const Rect displayRect(0, 0, static_cast<int32_t>(width), static_cast<int32_t>(height));
+    DisplaySettings display{
+            .physicalDisplay = displayRect,
+            .clip = displayRect,
+            .maxLuminance = 500,
+    };
+
+    base::unique_fd fence;
+    for (auto _ : benchState) {
+        auto [status, drawFence] =
+                re.drawLayers(display, layers, outputBuffer, kUseFrameBufferCache, std::move(fence))
+                        .get();
+        fence = std::move(drawFence);
+    }
+
+    if (renderenginebench::save() && saveFileName) {
+        sp<Fence> waitFence = new Fence(std::move(fence));
+        waitFence->waitForever(LOG_TAG);
+
+        // Copy to a CPU-accessible buffer so we can encode it.
+        outputBuffer = copyBuffer(re, outputBuffer, GRALLOC_USAGE_SW_READ_OFTEN, "to_encode");
+
+        std::string outFile = base::GetExecutableDirectory();
+        outFile.append("/");
+        outFile.append(saveFileName);
+        outFile.append(".jpg");
+        renderenginebench::encodeToJpeg(outFile.c_str(), outputBuffer->getBuffer());
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//  Benchmarks
+///////////////////////////////////////////////////////////////////////////////
+
+void BM_blur(benchmark::State& benchState) {
+    auto re = createRenderEngine(static_cast<RenderEngine::RenderEngineType>(benchState.range()));
+
+    // Initially use cpu access so we can decode into it with AImageDecoder.
+    auto [width, height] = getDisplaySize();
+    auto srcBuffer = allocateBuffer(*re, width, height, GRALLOC_USAGE_SW_WRITE_OFTEN,
+                                    "decoded_source");
+    {
+        std::string srcImage = base::GetExecutableDirectory();
+        srcImage.append("/resources/homescreen.png");
+        renderenginebench::decode(srcImage.c_str(), srcBuffer->getBuffer());
+
+        // Now copy into GPU-only buffer for more realistic timing.
+        srcBuffer = copyBuffer(*re, srcBuffer, 0, "source");
+    }
+
+    const FloatRect layerRect(0, 0, width, height);
+    LayerSettings layer{
+            .geometry =
+                    Geometry{
+                            .boundaries = layerRect,
+                    },
+            .source =
+                    PixelSource{
+                            .buffer =
+                                    Buffer{
+                                            .buffer = srcBuffer,
+                                    },
+                    },
+            .alpha = half(1.0f),
+    };
+    LayerSettings blurLayer{
+            .geometry =
+                    Geometry{
+                            .boundaries = layerRect,
+                    },
+            .alpha = half(1.0f),
+            .skipContentDraw = true,
+            .backgroundBlurRadius = 60,
+    };
+
+    auto layers = std::vector<LayerSettings>{layer, blurLayer};
+    benchDrawLayers(*re, layers, benchState, "blurred");
+}
+
+BENCHMARK(BM_blur)->Apply(RunSkiaGL);
diff --git a/libs/renderengine/benchmark/RenderEngineBench.h b/libs/renderengine/benchmark/RenderEngineBench.h
new file mode 100644
index 0000000..1a25d77
--- /dev/null
+++ b/libs/renderengine/benchmark/RenderEngineBench.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2021 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 <ui/GraphicBuffer.h>
+
+using namespace android;
+
+/**
+ * Utilities for running benchmarks.
+ */
+namespace renderenginebench {
+/**
+ * Parse RenderEngineBench-specific flags from the command line.
+ *
+ * --save Save the output buffer to a file to verify that it drew as
+ *  expected.
+ */
+void parseFlags(int argc, char** argv);
+
+/**
+ * Parse flags for '--help'
+ */
+void parseFlagsForHelp(int argc, char** argv);
+
+/**
+ * Whether to save the drawing result to a file.
+ *
+ * True if --save was used on the command line.
+ */
+bool save();
+
+/**
+ * Decode the image at 'path' into 'buffer'.
+ *
+ * Currently only used for debugging. The image will be scaled to fit the
+ * buffer if necessary.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void decode(const char* path, const sp<GraphicBuffer>& buffer);
+
+/**
+ * Encode the buffer to a jpeg.
+ *
+ * This assumes the buffer matches ANDROID_BITMAP_FORMAT_RGBA_8888.
+ *
+ * @param path Relative to the directory holding the executable.
+ */
+void encodeToJpeg(const char* path, const sp<GraphicBuffer>& buffer);
+} // namespace renderenginebench
diff --git a/libs/renderengine/benchmark/main.cpp b/libs/renderengine/benchmark/main.cpp
new file mode 100644
index 0000000..7a62853
--- /dev/null
+++ b/libs/renderengine/benchmark/main.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021 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 <RenderEngineBench.h>
+#include <benchmark/benchmark.h>
+
+int main(int argc, char** argv) {
+    // Initialize will exit if it sees '--help', so check for it and print info
+    // about our flags first.
+    renderenginebench::parseFlagsForHelp(argc, argv);
+    benchmark::Initialize(&argc, argv);
+
+    // Calling this separately from parseFlagsForHelp prevents collisions with
+    // google-benchmark's flags, since Initialize will consume and remove flags
+    // it recognizes.
+    renderenginebench::parseFlags(argc, argv);
+    benchmark::RunSpecifiedBenchmarks();
+    return 0;
+}
diff --git a/libs/renderengine/benchmark/resources/homescreen.png b/libs/renderengine/benchmark/resources/homescreen.png
new file mode 100644
index 0000000..997b72d
--- /dev/null
+++ b/libs/renderengine/benchmark/resources/homescreen.png
Binary files differ