[Lut NDK] Define ASurfaceTransaction_setLuts API
- And also ADisplayLuts, ADisplayLutsEntry struct and correspsonding NDK
APIs.
Bug: 377329333
Test: android.view.surfacecontrol.cts.ASurfaceControlTest#testSurfaceTransaction_setLuts_*
Flag: EXEMPT NDK
Change-Id: I23eaef36725a0d63ceba557811812b82b157f83e
diff --git a/native/android/Android.bp b/native/android/Android.bp
index da29c49..cd6de5a 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -55,6 +55,7 @@
"surface_control_input_receiver.cpp",
"choreographer.cpp",
"configuration.cpp",
+ "display_luts.cpp",
"dynamic_instrumentation_manager.cpp",
"hardware_buffer_jni.cpp",
"input.cpp",
diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp
new file mode 100644
index 0000000..179a32b
--- /dev/null
+++ b/native/android/display_luts.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+#define LOG_TAG "DisplayLuts"
+
+#include <android/display_luts.h>
+#include <display_luts_private.h>
+#include <utils/Log.h>
+
+#include <cmath>
+
+#define ADISPLAYLUTS_BUFFER_LENGTH_LIMIT (100000)
+
+#define CHECK_NOT_NULL(name) \
+ LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, int32_t dimension,
+ int32_t key) {
+ CHECK_NOT_NULL(buffer);
+ LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT,
+ "the lut raw buffer length is too big to handle");
+ if (dimension != ADISPLAYLUTS_ONE_DIMENSION && dimension != ADISPLAYLUTS_THREE_DIMENSION) {
+ LOG_ALWAYS_FATAL("the lut dimension is be either 1 or 3");
+ }
+ int32_t size = 0;
+ if (dimension == ADISPLAYLUTS_THREE_DIMENSION) {
+ LOG_ALWAYS_FATAL_IF(length % 3 != 0, "the 3d lut raw buffer is not divisible by 3");
+ int32_t lengthPerChannel = length / 3;
+ float sizeForDim = std::cbrt(static_cast<float>(lengthPerChannel));
+ LOG_ALWAYS_FATAL_IF(sizeForDim != (int)(sizeForDim),
+ "the 3d lut buffer length is incorrect");
+ size = (int)sizeForDim;
+ } else {
+ size = length;
+ }
+ LOG_ALWAYS_FATAL_IF(size < 2, "the lut size for each dimension is too small");
+
+ ADisplayLutsEntry* entry = new ADisplayLutsEntry();
+ entry->buffer.data.resize(length);
+ std::copy(buffer, buffer + length, entry->buffer.data.begin());
+ entry->properties = {dimension, size, key};
+
+ entry->incStrong((void*)ADisplayLutsEntry_createEntry);
+ return static_cast<ADisplayLutsEntry*>(entry);
+}
+
+void ADisplayLutsEntry_destroy(ADisplayLutsEntry* entry) {
+ if (entry != NULL) {
+ entry->decStrong((void*)ADisplayLutsEntry_createEntry);
+ }
+}
+
+ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) {
+ CHECK_NOT_NULL(entry);
+ return static_cast<ADisplayLuts_Dimension>(entry->properties.dimension);
+}
+
+int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) {
+ CHECK_NOT_NULL(entry);
+ return entry->properties.size;
+}
+
+ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) {
+ CHECK_NOT_NULL(entry);
+ return static_cast<ADisplayLuts_SamplingKey>(entry->properties.samplingKey);
+}
+
+const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) {
+ CHECK_NOT_NULL(entry);
+ return entry->buffer.data.data();
+}
+
+ADisplayLuts* ADisplayLuts_create() {
+ ADisplayLuts* luts = new ADisplayLuts();
+ if (luts == NULL) {
+ delete luts;
+ return NULL;
+ }
+ luts->incStrong((void*)ADisplayLuts_create);
+ return static_cast<ADisplayLuts*>(luts);
+}
+
+void ADisplayLuts_clearLuts(ADisplayLuts* luts) {
+ for (auto& entry : luts->entries) {
+ entry->decStrong((void*)ADisplayLuts_setEntries); // Decrement ref count
+ }
+ luts->entries.clear();
+ luts->offsets.clear();
+ luts->totalBufferSize = 0;
+}
+
+void ADisplayLuts_destroy(ADisplayLuts* luts) {
+ if (luts != NULL) {
+ ADisplayLuts_clearLuts(luts);
+ luts->decStrong((void*)ADisplayLuts_create);
+ }
+}
+
+void ADisplayLuts_setEntries(ADisplayLuts* luts, ADisplayLutsEntry** entries, int32_t numEntries) {
+ CHECK_NOT_NULL(luts);
+ // always clear the previously set lut(s)
+ ADisplayLuts_clearLuts(luts);
+
+ // do nothing
+ if (!entries || numEntries == 0) {
+ return;
+ }
+
+ LOG_ALWAYS_FATAL_IF(numEntries > 2, "The number of entries should be not over 2!");
+ if (numEntries == 2 && entries[0]->properties.dimension != ADISPLAYLUTS_ONE_DIMENSION &&
+ entries[1]->properties.dimension != ADISPLAYLUTS_THREE_DIMENSION) {
+ LOG_ALWAYS_FATAL("The entries should be 1D and 3D in order!");
+ }
+
+ luts->offsets.reserve(numEntries);
+ luts->entries.reserve(numEntries);
+ for (int32_t i = 0; i < numEntries; i++) {
+ luts->offsets.emplace_back(luts->totalBufferSize);
+ luts->totalBufferSize += entries[i]->buffer.data.size();
+ luts->entries.emplace_back(entries[i]);
+ luts->entries.back()->incStrong((void*)ADisplayLuts_setEntries);
+ }
+}
\ No newline at end of file
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 2d1fbf9..5dd4542 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -95,6 +95,15 @@
AConfiguration_setTouchscreen;
AConfiguration_setUiModeNight;
AConfiguration_setUiModeType;
+ ADisplayLuts_create; # introduced=36
+ ADisplayLuts_setEntries; # introduced=36
+ ADisplayLuts_destroy; # introduced=36
+ ADisplayLutsEntry_createEntry; # introduced=36
+ ADisplayLutsEntry_getDimension; # introduced=36
+ ADisplayLutsEntry_getSize; # introduced=36
+ ADisplayLutsEntry_getSamplingKey; # introduced=36
+ ADisplayLutsEntry_getBuffer; # introduced=36
+ ADisplayLutsEntry_destroy; # introduced=36
AInputEvent_getDeviceId;
AInputEvent_getSource;
AInputEvent_getType;
@@ -300,6 +309,7 @@
ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake
ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream
+ ASurfaceTransaction_setLuts; # introduced=36
ASurfaceTransaction_setOnComplete; # introduced=29
ASurfaceTransaction_setOnCommit; # introduced=31
ASurfaceTransaction_setPosition; # introduced=31
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 698bc84..fc64e9b 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+#include <android/gui/LutProperties.h>
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/native_window.h>
#include <android/surface_control.h>
#include <android/surface_control_jni.h>
#include <android_runtime/android_view_SurfaceControl.h>
#include <configstore/Utils.h>
+#include <cutils/ashmem.h>
+#include <display_luts_private.h>
#include <gui/HdrMetadata.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
@@ -53,6 +56,14 @@
static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) ==
static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+static_assert(static_cast<int>(ADISPLAYLUTS_ONE_DIMENSION) ==
+ static_cast<int>(android::gui::LutProperties::Dimension::ONE_D));
+static_assert(static_cast<int>(ADISPLAYLUTS_THREE_DIMENSION) ==
+ static_cast<int>(android::gui::LutProperties::Dimension::THREE_D));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_RGB) ==
+ static_cast<int>(android::gui::LutProperties::SamplingKey::RGB));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) ==
+ static_cast<int>(android::gui::LutProperties::SamplingKey::MAX_RGB));
Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
return reinterpret_cast<Transaction*>(aSurfaceTransaction);
@@ -693,6 +704,58 @@
transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio);
}
+void ASurfaceTransaction_setLuts(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ const struct ADisplayLuts* luts) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ int fd = -1;
+ std::vector<int32_t> offsets;
+ std::vector<int32_t> dimensions;
+ std::vector<int32_t> sizes;
+ std::vector<int32_t> samplingKeys;
+
+ if (luts) {
+ std::vector<float> buffer(luts->totalBufferSize);
+ int32_t count = luts->offsets.size();
+ offsets = luts->offsets;
+
+ dimensions.reserve(count);
+ sizes.reserve(count);
+ samplingKeys.reserve(count);
+ for (int32_t i = 0; i < count; i++) {
+ dimensions.emplace_back(luts->entries[i]->properties.dimension);
+ sizes.emplace_back(luts->entries[i]->properties.size);
+ samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey);
+ std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(),
+ buffer.begin() + offsets[i]);
+ }
+
+ // mmap
+ fd = ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float));
+ if (fd < 0) {
+ LOG_ALWAYS_FATAL("setLuts, ashmem_create_region() failed");
+ return;
+ }
+ void* ptr = mmap(nullptr, luts->totalBufferSize * sizeof(float), PROT_READ | PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (ptr == MAP_FAILED) {
+ LOG_ALWAYS_FATAL("setLuts, Failed to map the shared memory");
+ return;
+ }
+
+ memcpy(ptr, buffer.data(), luts->totalBufferSize * sizeof(float));
+ munmap(ptr, luts->totalBufferSize * sizeof(float));
+ }
+
+ transaction->setLuts(surfaceControl, base::unique_fd(fd), offsets, dimensions, sizes,
+ samplingKeys);
+}
+
void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
float r, float g, float b, float alpha,