Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 771cb6ce authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "sf-latency: Write a benchmark for RenderEngine"

parents 389fbd16 8fcfa66d
Loading
Loading
Loading
Loading
+54 −0
Original line number Diff line number Diff line
// 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/*"],
}
+136 −0
Original line number Diff line number Diff line
/*
 * 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
+48 −0
Original line number Diff line number Diff line
/*
 * 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
+252 −0
Original line number Diff line number Diff line
/*
 * 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);
+65 −0
Original line number Diff line number Diff line
/*
 * 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
Loading