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

Commit 671d0f54 authored by Alec Mouri's avatar Alec Mouri
Browse files

Add a C-api for retrieving display info.

As HWUI will need to depend on ABI-stable interfaces moving forward, we
need such an interface against SurfaceComposer for retrieving display
metadata.

This CL won't contain a map file to stabilize the ABI yet, as I expect
that there will be some flux due to refresh rate apis.

Bug: 136263392
Test: builds
Change-Id: I7227e2e72789d36a21534840eac88817135251c3
parent 91615fb4
Loading
Loading
Loading
Loading
+262 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 <apex/display.h>
#include <gui/SurfaceComposerClient.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>

#include <algorithm>
#include <optional>
#include <type_traits>
#include <vector>

namespace android::display::impl {

/**
 * Implementation of ADisplayConfig
 */
struct DisplayConfigImpl {
    /**
     * The width in pixels of the display configuration.
     */
    int32_t width{0};

    /**
     * The height in pixels of the display configuration.
     */

    int32_t height{0};

    /**
     * The display density.
     */
    float density{0};

    /**
     * The refresh rate of the display configuration, in frames per second.
     */
    float fps{0.0};

    /**
     * The vsync offset at which surfaceflinger runs, in nanoseconds.
     */
    int64_t sfOffset{0};

    /**
     * The vsync offset at which applications run, in nanoseconds.
     */
    int64_t appOffset{0};
};

// DisplayConfigImpl allocation is not managed through C++ memory apis, so
// preventing calling the destructor here.
static_assert(std::is_trivially_destructible<DisplayConfigImpl>::value);

/**
 * Implementation of ADisplay
 */
struct DisplayImpl {
    /**
     * A physical display ID, unique to this display.
     */
    PhysicalDisplayId id;

    /**
     * The type of the display, i.e. whether it is an internal or external
     * display.
     */
    ADisplayType type;

    /**
     * Number of supported configs
     */
    size_t numConfigs;

    /**
     * Set of supported configs by this display.
     */
    DisplayConfigImpl* configs;
};

// DisplayImpl allocation is not managed through C++ memory apis, so
// preventing calling the destructor here.
static_assert(std::is_trivially_destructible<DisplayImpl>::value);

} // namespace android::display::impl

using namespace android;
using namespace android::display::impl;

#define CHECK_NOT_NULL(name) \
    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");

namespace {
sp<IBinder> getToken(ADisplay* display) {
    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
    return SurfaceComposerClient::getPhysicalDisplayToken(impl->id);
}

int64_t computeSfOffset(const DisplayInfo& info) {
    // This should probably be part of the config instead of extrapolated from
    // the presentation deadline and fudged here, but the way the math works out
    // here we do get the right offset.
    return static_cast<int64_t>((1000000000 / info.fps) - info.presentationDeadline + 1000000);
}
} // namespace

int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays) {
    const std::vector<PhysicalDisplayId> ids = SurfaceComposerClient::getPhysicalDisplayIds();
    const size_t size = ids.size();
    if (size == 0) {
        return NO_INIT;
    }

    std::vector<DisplayConfigImpl> configsPerDisplay[size];
    int numConfigs = 0;
    for (int i = 0; i < size; ++i) {
        const sp<IBinder> token = SurfaceComposerClient::getPhysicalDisplayToken(ids[i]);
        Vector<DisplayInfo> configs;
        const status_t status = SurfaceComposerClient::getDisplayConfigs(token, &configs);
        if (status != OK) {
            return status;
        }
        if (configs.empty()) {
            return NO_INIT;
        }

        numConfigs += configs.size();
        configsPerDisplay[i].reserve(configs.size());
        for (int j = 0; j < configs.size(); ++j) {
            const DisplayInfo config = configs[j];
            configsPerDisplay[i].emplace_back(
                    DisplayConfigImpl{static_cast<int32_t>(config.w),
                                      static_cast<int32_t>(config.h), config.density, config.fps,
                                      computeSfOffset(config), config.appVsyncOffset});
        }
    }

    const std::optional<PhysicalDisplayId> internalId =
            SurfaceComposerClient::getInternalDisplayId();

    // Here we allocate all our required memory in one block. The layout is as
    // follows:
    // ------------------------------------------------------------
    // | DisplayImpl pointers | DisplayImpls | DisplayConfigImpls |
    // ------------------------------------------------------------
    //
    // The caller will be given a DisplayImpl** which points to the beginning of
    // the block of DisplayImpl pointers.
    // Each DisplayImpl* points to a DisplayImpl in the second block.
    // Each DisplayImpl contains a DisplayConfigImpl*, which points to a
    // contiguous block of DisplayConfigImpls specific to that display.
    DisplayImpl** const impls = reinterpret_cast<DisplayImpl**>(
            malloc((sizeof(DisplayImpl) + sizeof(DisplayImpl*)) * size +
                   sizeof(DisplayConfigImpl) * numConfigs));
    DisplayImpl* const displayData = reinterpret_cast<DisplayImpl*>(impls + size);
    DisplayConfigImpl* configData = reinterpret_cast<DisplayConfigImpl*>(displayData + size);

    for (size_t i = 0; i < size; ++i) {
        const PhysicalDisplayId id = ids[i];
        const ADisplayType type = (internalId == id) ? ADisplayType::DISPLAY_TYPE_INTERNAL
                                                     : ADisplayType::DISPLAY_TYPE_EXTERNAL;
        const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
        memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());

        displayData[i] = DisplayImpl{id, type, configs.size(), configData};
        impls[i] = displayData + i;
        // Advance the configData pointer so that future configs are written to
        // the correct display.
        configData += configs.size();
    }

    *outDisplays = reinterpret_cast<ADisplay**>(impls);
    return size;
}

void ADisplay_release(ADisplay** displays) {
    if (displays == nullptr) {
        return;
    }
    free(displays);
}

float ADisplay_getMaxSupportedFps(ADisplay* display) {
    CHECK_NOT_NULL(display);
    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
    float maxFps = 0.0;
    for (int i = 0; i < impl->numConfigs; ++i) {
        maxFps = std::max(maxFps, impl->configs[i].fps);
    }
    return maxFps;
}

ADisplayType ADisplay_getDisplayType(ADisplay* display) {
    CHECK_NOT_NULL(display);

    return reinterpret_cast<DisplayImpl*>(display)->type;
}

int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
    CHECK_NOT_NULL(display);

    sp<IBinder> token = getToken(display);
    const int index = SurfaceComposerClient::getActiveConfig(token);
    if (index < 0) {
        return index;
    }

    DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);

    *outConfig = reinterpret_cast<ADisplayConfig*>(impl->configs + index);
    return OK;
}

float ADisplayConfig_getDensity(ADisplayConfig* config) {
    CHECK_NOT_NULL(config);

    return reinterpret_cast<DisplayConfigImpl*>(config)->density;
}

int32_t ADisplayConfig_getWidth(ADisplayConfig* config) {
    CHECK_NOT_NULL(config);

    return reinterpret_cast<DisplayConfigImpl*>(config)->width;
}

int32_t ADisplayConfig_getHeight(ADisplayConfig* config) {
    CHECK_NOT_NULL(config);

    return reinterpret_cast<DisplayConfigImpl*>(config)->height;
}

float ADisplayConfig_getFps(ADisplayConfig* config) {
    CHECK_NOT_NULL(config);

    return reinterpret_cast<DisplayConfigImpl*>(config)->fps;
}

int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config) {
    CHECK_NOT_NULL(config);

    return reinterpret_cast<DisplayConfigImpl*>(config)->sfOffset;
}

int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config) {
    CHECK_NOT_NULL(config);

    return reinterpret_cast<DisplayConfigImpl*>(config)->appOffset;
}
+50 −0
Original line number Diff line number Diff line
// Copyright 2019 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.

cc_library_headers {
    name: "libnativedisplay_headers",
    export_include_dirs: ["include"],
}

cc_library {
    name: "libnativedisplay",
    export_include_dirs: [
        "include",
    ],

    clang: true,

    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-enum-compare",
        "-Wno-unused-function",
    ],

    srcs: [
        "ADisplay.cpp",
    ],

    shared_libs: [
        "libgui",
        "liblog",
        "libui",
        "libutils",
    ],

    header_libs: [
        "libnativedisplay_headers",
    ],

}
+125 −0
Original line number Diff line number Diff line
/*
 * Copyright 2019 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 <inttypes.h>

__BEGIN_DECLS

/**
 * Opaque handle for a native display
 */
typedef struct ADisplay ADisplay;

/**
 * Enum describing the possible types of a display
 */
enum ADisplayType {
    /**
     * A display that is the internal, or "primary" display for a device.
     */
    DISPLAY_TYPE_INTERNAL = 0,

    /**
     * A display that is externally connected for a device.
     */
    DISPLAY_TYPE_EXTERNAL = 1,
};

/**
 * Opaque handle for display metadata
 */
typedef struct ADisplayConfig ADisplayConfig;

/**
 * Acquires a list of display handles. Memory is allocated for the list and is
 * owned by the caller. The caller is responsible for freeing this memory by
 * calling ADisplayList_release.
 *
 * Returns the size of the returned list on success.
 * Returns -errno on error.
 */
int ADisplay_acquirePhysicalDisplays(ADisplay*** outDisplays);

/**
 * Releases a list of display handles created by
 * ADisplayList_acquirePhysicalDisplays.
 */
void ADisplay_release(ADisplay** displays);

/**
 * Queries the maximum supported fps for the given display.
 */
float ADisplay_getMaxSupportedFps(ADisplay* display);

/**
 * Queries the display's type.
 */
ADisplayType ADisplay_getDisplayType(ADisplay* display);

/**
 * Gets the current display configuration for the given display.
 *
 * Memory is *not* allocated for the caller. As such, the returned output
 * configuration's lifetime will not be longer than the ADisplay* passed to this
 * function - if ADisplay_release is called destroying the ADisplay object then
 * it is invalid to access the ADisplayConfig returned here.
 *
 * Note that the current display configuration can change. Listening to updates
 * to the current display configuration should be done via Choreographer. If
 * such an update is observed, then this method should be recalled to get the
 * new current configuration.
 *
 * Returns OK on success, -errno on failure.
 */
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig);

/**
 * Queries the density for a given display configuration.
 */
float ADisplayConfig_getDensity(ADisplayConfig* config);

/**
 * Queries the width in pixels for a given display configuration.
 */
int32_t ADisplayConfig_getWidth(ADisplayConfig* config);

/**
 * Queries the height in pixels for a given display configuration.
 */
int32_t ADisplayConfig_getHeight(ADisplayConfig* config);

/**
 * Queries the display refresh rate for a given display configuration.
 */
float ADisplayConfig_getFps(ADisplayConfig* config);

/**
 * Queries the vsync offset from which the system compositor is scheduled to
 * run. If a vsync occurs at time T, and the compositor runs at time T + S, then
 * this returns S in nanoseconds.
 */
int64_t ADisplayConfig_getCompositorOffsetNanos(ADisplayConfig* config);

/**
 * Queries the vsync offset from which applications are scheduled to run. If a
 * vsync occurs at time T, and applications run at time T + S, then this returns
 * S in nanoseconds.
 */
int64_t ADisplayConfig_getAppVsyncOffsetNanos(ADisplayConfig* config);

__END_DECLS