[RenderEngine] Move RenderEngine to libs/renderengine
To do side-by-side comparison between readback buffer from hardware composer
and client target of RenderEngine, we need RenderEngine to be accessible in
VTS, which means RenderEngine should be part of VNDK. This patch moves
RenderEngine out of SurfaceFlinger to libs/renderengine.
BUG: 112585051
Test: build, flash, boot and do some display validation
Change-Id: Ib6b302eaad04c7cc6c5bae39b1d25b38be188d01
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
new file mode 100644
index 0000000..674659c
--- /dev/null
+++ b/libs/renderengine/Android.bp
@@ -0,0 +1,80 @@
+// TODO(b/112585051) Add to VNDK once moved to libs/
+cc_defaults {
+ name: "renderengine_defaults",
+ cflags: [
+ "-DLOG_TAG=\"RenderEngine\"",
+ "-Wall",
+ "-Werror",
+ "-Wthread-safety",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+ cppflags: ["-std=c++1z"],
+}
+
+cc_defaults {
+ name: "librenderengine_defaults",
+ defaults: ["renderengine_defaults"],
+ cflags: [
+ "-DGL_GLEXT_PROTOTYPES",
+ "-DEGL_EGLEXT_PROTOTYPES",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libEGL",
+ "libGLESv1_CM",
+ "libGLESv2",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libui",
+ "libutils",
+ ],
+ local_include_dirs: ["include"],
+ export_include_dirs: ["include"],
+}
+
+filegroup {
+ name: "librenderengine_sources",
+ srcs: [
+ "Description.cpp",
+ "Mesh.cpp",
+ "RenderEngine.cpp",
+ "Texture.cpp",
+ ],
+}
+
+filegroup {
+ name: "librenderengine_gl_sources",
+ srcs: [
+ "gl/GLES20RenderEngine.cpp",
+ "gl/GLExtensions.cpp",
+ "gl/GLFramebuffer.cpp",
+ "gl/GLImage.cpp",
+ "gl/GLSurface.cpp",
+ "gl/Program.cpp",
+ "gl/ProgramCache.cpp",
+ ],
+}
+
+cc_library_static {
+ name: "librenderengine",
+ defaults: ["librenderengine_defaults"],
+ double_loadable: true,
+
+ clang: true,
+ cflags: [
+ "-fvisibility=hidden",
+ "-Werror=format",
+ ],
+ cppflags: [
+ "-fwhole-program-vtables", // requires ThinLTO
+ ],
+ srcs: [
+ ":librenderengine_sources",
+ ":librenderengine_gl_sources",
+ ],
+ lto: {
+ thin: true,
+ },
+}
diff --git a/libs/renderengine/Description.cpp b/libs/renderengine/Description.cpp
new file mode 100644
index 0000000..b9cea10
--- /dev/null
+++ b/libs/renderengine/Description.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 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 <renderengine/private/Description.h>
+
+#include <stdint.h>
+
+#include <utils/TypeHelpers.h>
+
+namespace android {
+namespace renderengine {
+
+Description::TransferFunction Description::dataSpaceToTransferFunction(ui::Dataspace dataSpace) {
+ ui::Dataspace transfer = static_cast<ui::Dataspace>(dataSpace & ui::Dataspace::TRANSFER_MASK);
+ switch (transfer) {
+ case ui::Dataspace::TRANSFER_ST2084:
+ return Description::TransferFunction::ST2084;
+ case ui::Dataspace::TRANSFER_HLG:
+ return Description::TransferFunction::HLG;
+ case ui::Dataspace::TRANSFER_LINEAR:
+ return Description::TransferFunction::LINEAR;
+ default:
+ return Description::TransferFunction::SRGB;
+ }
+}
+
+bool Description::hasInputTransformMatrix() const {
+ const mat4 identity;
+ return inputTransformMatrix != identity;
+}
+
+bool Description::hasOutputTransformMatrix() const {
+ const mat4 identity;
+ return outputTransformMatrix != identity;
+}
+
+bool Description::hasColorMatrix() const {
+ const mat4 identity;
+ return colorMatrix != identity;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
new file mode 100644
index 0000000..6a40c6c
--- /dev/null
+++ b/libs/renderengine/Mesh.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2013 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 <renderengine/Mesh.h>
+
+#include <utils/Log.h>
+
+namespace android {
+namespace renderengine {
+
+Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize)
+ : mVertexCount(vertexCount),
+ mVertexSize(vertexSize),
+ mTexCoordsSize(texCoordSize),
+ mPrimitive(primitive) {
+ if (vertexCount == 0) {
+ mVertices.resize(1);
+ mVertices[0] = 0.0f;
+ mStride = 0;
+ return;
+ }
+
+ size_t stride = vertexSize + texCoordSize;
+ size_t remainder = (stride * vertexCount) / vertexCount;
+ // Since all of the input parameters are unsigned, if stride is less than
+ // either vertexSize or texCoordSize, it must have overflowed. remainder
+ // will be equal to stride as long as stride * vertexCount doesn't overflow.
+ if ((stride < vertexSize) || (remainder != stride)) {
+ ALOGE("Overflow in Mesh(..., %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize);
+ mVertices.resize(1);
+ mVertices[0] = 0.0f;
+ mVertexCount = 0;
+ mVertexSize = 0;
+ mTexCoordsSize = 0;
+ mStride = 0;
+ return;
+ }
+
+ mVertices.resize(stride * vertexCount);
+ mStride = stride;
+}
+
+Mesh::Primitive Mesh::getPrimitive() const {
+ return mPrimitive;
+}
+
+float const* Mesh::getPositions() const {
+ return mVertices.data();
+}
+float* Mesh::getPositions() {
+ return mVertices.data();
+}
+
+float const* Mesh::getTexCoords() const {
+ return mVertices.data() + mVertexSize;
+}
+float* Mesh::getTexCoords() {
+ return mVertices.data() + mVertexSize;
+}
+
+size_t Mesh::getVertexCount() const {
+ return mVertexCount;
+}
+
+size_t Mesh::getVertexSize() const {
+ return mVertexSize;
+}
+
+size_t Mesh::getTexCoordsSize() const {
+ return mTexCoordsSize;
+}
+
+size_t Mesh::getByteStride() const {
+ return mStride * sizeof(float);
+}
+
+size_t Mesh::getStride() const {
+ return mStride;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp
new file mode 100644
index 0000000..8be1c3c
--- /dev/null
+++ b/libs/renderengine/RenderEngine.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2013 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 <renderengine/RenderEngine.h>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+#include <private/gui/SyncFeatures.h>
+#include "gl/GLES20RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+
+std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) {
+ char prop[PROPERTY_VALUE_MAX];
+ property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles");
+ if (strcmp(prop, "gles") == 0) {
+ ALOGD("RenderEngine GLES Backend");
+ return renderengine::gl::GLES20RenderEngine::create(hwcFormat, featureFlags);
+ }
+ ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop);
+ return renderengine::gl::GLES20RenderEngine::create(hwcFormat, featureFlags);
+}
+
+RenderEngine::~RenderEngine() = default;
+
+namespace impl {
+
+RenderEngine::RenderEngine(uint32_t featureFlags) : mFeatureFlags(featureFlags) {}
+
+RenderEngine::~RenderEngine() = default;
+
+bool RenderEngine::useNativeFenceSync() const {
+ return SyncFeatures::getInstance().useNativeFenceSync();
+}
+
+bool RenderEngine::useWaitSync() const {
+ return SyncFeatures::getInstance().useWaitSync();
+}
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/TEST_MAPPING b/libs/renderengine/TEST_MAPPING
new file mode 100644
index 0000000..995dba1
--- /dev/null
+++ b/libs/renderengine/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "librenderengine_test"
+ }
+ ]
+}
diff --git a/libs/renderengine/Texture.cpp b/libs/renderengine/Texture.cpp
new file mode 100644
index 0000000..154cde8
--- /dev/null
+++ b/libs/renderengine/Texture.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2013 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 <renderengine/Texture.h>
+
+namespace android {
+namespace renderengine {
+
+Texture::Texture()
+ : mTextureName(0), mTextureTarget(TEXTURE_2D), mWidth(0), mHeight(0), mFiltering(false) {}
+
+Texture::Texture(Target textureTarget, uint32_t textureName)
+ : mTextureName(textureName),
+ mTextureTarget(textureTarget),
+ mWidth(0),
+ mHeight(0),
+ mFiltering(false) {}
+
+void Texture::init(Target textureTarget, uint32_t textureName) {
+ mTextureName = textureName;
+ mTextureTarget = textureTarget;
+}
+
+Texture::~Texture() {}
+
+void Texture::setMatrix(float const* matrix) {
+ mTextureMatrix = mat4(matrix);
+}
+
+void Texture::setFiltering(bool enabled) {
+ mFiltering = enabled;
+}
+
+void Texture::setDimensions(size_t width, size_t height) {
+ mWidth = width;
+ mHeight = height;
+}
+
+uint32_t Texture::getTextureName() const {
+ return mTextureName;
+}
+
+uint32_t Texture::getTextureTarget() const {
+ return mTextureTarget;
+}
+
+const mat4& Texture::getMatrix() const {
+ return mTextureMatrix;
+}
+
+bool Texture::getFiltering() const {
+ return mFiltering;
+}
+
+size_t Texture::getWidth() const {
+ return mWidth;
+}
+
+size_t Texture::getHeight() const {
+ return mHeight;
+}
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLES20RenderEngine.cpp b/libs/renderengine/gl/GLES20RenderEngine.cpp
new file mode 100644
index 0000000..026b151
--- /dev/null
+++ b/libs/renderengine/gl/GLES20RenderEngine.cpp
@@ -0,0 +1,952 @@
+/*
+ * Copyright 2013 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_NDEBUG 0
+#undef LOG_TAG
+#define LOG_TAG "RenderEngine"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "GLES20RenderEngine.h"
+
+#include <math.h>
+#include <fstream>
+#include <sstream>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <cutils/compiler.h>
+#include <renderengine/Mesh.h>
+#include <renderengine/Texture.h>
+#include <renderengine/private/Description.h>
+#include <ui/ColorSpace.h>
+#include <ui/DebugUtils.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+#include "GLExtensions.h"
+#include "GLFramebuffer.h"
+#include "GLImage.h"
+#include "GLSurface.h"
+#include "Program.h"
+#include "ProgramCache.h"
+
+extern "C" EGLAPI const char* eglQueryStringImplementationANDROID(EGLDisplay dpy, EGLint name);
+
+bool checkGlError(const char* op, int lineNumber) {
+ bool errorFound = false;
+ GLint error = glGetError();
+ while (error != GL_NO_ERROR) {
+ errorFound = true;
+ error = glGetError();
+ ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
+ }
+ return errorFound;
+}
+
+static constexpr bool outputDebugPPMs = false;
+
+void writePPM(const char* basename, GLuint width, GLuint height) {
+ ALOGV("writePPM #%s: %d x %d", basename, width, height);
+
+ std::vector<GLubyte> pixels(width * height * 4);
+ std::vector<GLubyte> outBuffer(width * height * 3);
+
+ // TODO(courtneygo): We can now have float formats, need
+ // to remove this code or update to support.
+ // Make returned pixels fit in uint32_t, one byte per component
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
+ if (checkGlError(__FUNCTION__, __LINE__)) {
+ return;
+ }
+
+ std::string filename(basename);
+ filename.append(".ppm");
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ if (!file.is_open()) {
+ ALOGE("Unable to open file: %s", filename.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+ "surfaceflinger to write debug images");
+ return;
+ }
+
+ file << "P6\n";
+ file << width << "\n";
+ file << height << "\n";
+ file << 255 << "\n";
+
+ auto ptr = reinterpret_cast<char*>(pixels.data());
+ auto outPtr = reinterpret_cast<char*>(outBuffer.data());
+ for (int y = height - 1; y >= 0; y--) {
+ char* data = ptr + y * width * sizeof(uint32_t);
+
+ for (GLuint x = 0; x < width; x++) {
+ // Only copy R, G and B components
+ outPtr[0] = data[0];
+ outPtr[1] = data[1];
+ outPtr[2] = data[2];
+ data += sizeof(uint32_t);
+ outPtr += 3;
+ }
+ }
+ file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+}
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+using ui::Dataspace;
+
+static status_t selectConfigForAttribute(EGLDisplay dpy, EGLint const* attrs, EGLint attribute,
+ EGLint wanted, EGLConfig* outConfig) {
+ EGLint numConfigs = -1, n = 0;
+ eglGetConfigs(dpy, nullptr, 0, &numConfigs);
+ std::vector<EGLConfig> configs(numConfigs, EGL_NO_CONFIG_KHR);
+ eglChooseConfig(dpy, attrs, configs.data(), configs.size(), &n);
+ configs.resize(n);
+
+ if (!configs.empty()) {
+ if (attribute != EGL_NONE) {
+ for (EGLConfig config : configs) {
+ EGLint value = 0;
+ eglGetConfigAttrib(dpy, config, attribute, &value);
+ if (wanted == value) {
+ *outConfig = config;
+ return NO_ERROR;
+ }
+ }
+ } else {
+ // just pick the first one
+ *outConfig = configs[0];
+ return NO_ERROR;
+ }
+ }
+
+ return NAME_NOT_FOUND;
+}
+
+class EGLAttributeVector {
+ struct Attribute;
+ class Adder;
+ friend class Adder;
+ KeyedVector<Attribute, EGLint> mList;
+ struct Attribute {
+ Attribute() : v(0){};
+ explicit Attribute(EGLint v) : v(v) {}
+ EGLint v;
+ bool operator<(const Attribute& other) const {
+ // this places EGL_NONE at the end
+ EGLint lhs(v);
+ EGLint rhs(other.v);
+ if (lhs == EGL_NONE) lhs = 0x7FFFFFFF;
+ if (rhs == EGL_NONE) rhs = 0x7FFFFFFF;
+ return lhs < rhs;
+ }
+ };
+ class Adder {
+ friend class EGLAttributeVector;
+ EGLAttributeVector& v;
+ EGLint attribute;
+ Adder(EGLAttributeVector& v, EGLint attribute) : v(v), attribute(attribute) {}
+
+ public:
+ void operator=(EGLint value) {
+ if (attribute != EGL_NONE) {
+ v.mList.add(Attribute(attribute), value);
+ }
+ }
+ operator EGLint() const { return v.mList[attribute]; }
+ };
+
+public:
+ EGLAttributeVector() { mList.add(Attribute(EGL_NONE), EGL_NONE); }
+ void remove(EGLint attribute) {
+ if (attribute != EGL_NONE) {
+ mList.removeItem(Attribute(attribute));
+ }
+ }
+ Adder operator[](EGLint attribute) { return Adder(*this, attribute); }
+ EGLint operator[](EGLint attribute) const { return mList[attribute]; }
+ // cast-operator to (EGLint const*)
+ operator EGLint const*() const { return &mList.keyAt(0).v; }
+};
+
+static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint renderableType,
+ EGLConfig* config) {
+ // select our EGLConfig. It must support EGL_RECORDABLE_ANDROID if
+ // it is to be used with WIFI displays
+ status_t err;
+ EGLint wantedAttribute;
+ EGLint wantedAttributeValue;
+
+ EGLAttributeVector attribs;
+ if (renderableType) {
+ attribs[EGL_RENDERABLE_TYPE] = renderableType;
+ attribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
+ attribs[EGL_SURFACE_TYPE] = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
+ attribs[EGL_FRAMEBUFFER_TARGET_ANDROID] = EGL_TRUE;
+ attribs[EGL_RED_SIZE] = 8;
+ attribs[EGL_GREEN_SIZE] = 8;
+ attribs[EGL_BLUE_SIZE] = 8;
+ attribs[EGL_ALPHA_SIZE] = 8;
+ wantedAttribute = EGL_NONE;
+ wantedAttributeValue = EGL_NONE;
+ } else {
+ // if no renderable type specified, fallback to a simplified query
+ wantedAttribute = EGL_NATIVE_VISUAL_ID;
+ wantedAttributeValue = format;
+ }
+
+ err = selectConfigForAttribute(display, attribs, wantedAttribute, wantedAttributeValue, config);
+ if (err == NO_ERROR) {
+ EGLint caveat;
+ if (eglGetConfigAttrib(display, *config, EGL_CONFIG_CAVEAT, &caveat))
+ ALOGW_IF(caveat == EGL_SLOW_CONFIG, "EGL_SLOW_CONFIG selected!");
+ }
+
+ return err;
+}
+
+std::unique_ptr<GLES20RenderEngine> GLES20RenderEngine::create(int hwcFormat,
+ uint32_t featureFlags) {
+ // initialize EGL for the default display
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (!eglInitialize(display, nullptr, nullptr)) {
+ LOG_ALWAYS_FATAL("failed to initialize EGL");
+ }
+
+ GLExtensions& extensions = GLExtensions::getInstance();
+ extensions.initWithEGLStrings(eglQueryStringImplementationANDROID(display, EGL_VERSION),
+ eglQueryStringImplementationANDROID(display, EGL_EXTENSIONS));
+
+ // The code assumes that ES2 or later is available if this extension is
+ // supported.
+ EGLConfig config = EGL_NO_CONFIG;
+ if (!extensions.hasNoConfigContext()) {
+ config = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+
+ EGLint renderableType = 0;
+ if (config == EGL_NO_CONFIG) {
+ renderableType = EGL_OPENGL_ES2_BIT;
+ } else if (!eglGetConfigAttrib(display, config, EGL_RENDERABLE_TYPE, &renderableType)) {
+ LOG_ALWAYS_FATAL("can't query EGLConfig RENDERABLE_TYPE");
+ }
+ EGLint contextClientVersion = 0;
+ if (renderableType & EGL_OPENGL_ES2_BIT) {
+ contextClientVersion = 2;
+ } else if (renderableType & EGL_OPENGL_ES_BIT) {
+ contextClientVersion = 1;
+ } else {
+ LOG_ALWAYS_FATAL("no supported EGL_RENDERABLE_TYPEs");
+ }
+
+ std::vector<EGLint> contextAttributes;
+ contextAttributes.reserve(6);
+ contextAttributes.push_back(EGL_CONTEXT_CLIENT_VERSION);
+ contextAttributes.push_back(contextClientVersion);
+ bool useContextPriority = extensions.hasContextPriority() &&
+ (featureFlags & RenderEngine::USE_HIGH_PRIORITY_CONTEXT);
+ if (useContextPriority) {
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_LEVEL_IMG);
+ contextAttributes.push_back(EGL_CONTEXT_PRIORITY_HIGH_IMG);
+ }
+ contextAttributes.push_back(EGL_NONE);
+
+ EGLContext ctxt = eglCreateContext(display, config, nullptr, contextAttributes.data());
+
+ // if can't create a GL context, we can only abort.
+ LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed");
+
+ // now figure out what version of GL did we actually get
+ // NOTE: a dummy surface is not needed if KHR_create_context is supported
+
+ EGLConfig dummyConfig = config;
+ if (dummyConfig == EGL_NO_CONFIG) {
+ dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true);
+ }
+ EGLint attribs[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
+ EGLSurface dummy = eglCreatePbufferSurface(display, dummyConfig, attribs);
+ LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer");
+ EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt);
+ LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current");
+
+ extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER),
+ glGetString(GL_VERSION), glGetString(GL_EXTENSIONS));
+
+ GlesVersion version = parseGlesVersion(extensions.getVersion());
+
+ // initialize the renderer while GL is current
+
+ std::unique_ptr<GLES20RenderEngine> engine;
+ switch (version) {
+ case GLES_VERSION_1_0:
+ case GLES_VERSION_1_1:
+ LOG_ALWAYS_FATAL("SurfaceFlinger requires OpenGL ES 2.0 minimum to run.");
+ break;
+ case GLES_VERSION_2_0:
+ case GLES_VERSION_3_0:
+ engine = std::make_unique<GLES20RenderEngine>(featureFlags);
+ break;
+ }
+ engine->setEGLHandles(display, config, ctxt);
+
+ ALOGI("OpenGL ES informations:");
+ ALOGI("vendor : %s", extensions.getVendor());
+ ALOGI("renderer : %s", extensions.getRenderer());
+ ALOGI("version : %s", extensions.getVersion());
+ ALOGI("extensions: %s", extensions.getExtensions());
+ ALOGI("GL_MAX_TEXTURE_SIZE = %zu", engine->getMaxTextureSize());
+ ALOGI("GL_MAX_VIEWPORT_DIMS = %zu", engine->getMaxViewportDims());
+
+ eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglDestroySurface(display, dummy);
+
+ return engine;
+}
+
+EGLConfig GLES20RenderEngine::chooseEglConfig(EGLDisplay display, int format, bool logConfig) {
+ status_t err;
+ EGLConfig config;
+
+ // First try to get an ES2 config
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES2_BIT, &config);
+ if (err != NO_ERROR) {
+ // If ES2 fails, try ES1
+ err = selectEGLConfig(display, format, EGL_OPENGL_ES_BIT, &config);
+ if (err != NO_ERROR) {
+ // still didn't work, probably because we're on the emulator...
+ // try a simplified query
+ ALOGW("no suitable EGLConfig found, trying a simpler query");
+ err = selectEGLConfig(display, format, 0, &config);
+ if (err != NO_ERROR) {
+ // this EGL is too lame for android
+ LOG_ALWAYS_FATAL("no suitable EGLConfig found, giving up");
+ }
+ }
+ }
+
+ if (logConfig) {
+ // print some debugging info
+ EGLint r, g, b, a;
+ eglGetConfigAttrib(display, config, EGL_RED_SIZE, &r);
+ eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &g);
+ eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &b);
+ eglGetConfigAttrib(display, config, EGL_ALPHA_SIZE, &a);
+ ALOGI("EGL information:");
+ ALOGI("vendor : %s", eglQueryString(display, EGL_VENDOR));
+ ALOGI("version : %s", eglQueryString(display, EGL_VERSION));
+ ALOGI("extensions: %s", eglQueryString(display, EGL_EXTENSIONS));
+ ALOGI("Client API: %s", eglQueryString(display, EGL_CLIENT_APIS) ?: "Not Supported");
+ ALOGI("EGLSurface: %d-%d-%d-%d, config=%p", r, g, b, a, config);
+ }
+
+ return config;
+}
+
+GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags)
+ : renderengine::impl::RenderEngine(featureFlags),
+ mEGLDisplay(EGL_NO_DISPLAY),
+ mEGLConfig(nullptr),
+ mEGLContext(EGL_NO_CONTEXT),
+ mVpWidth(0),
+ mVpHeight(0),
+ mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) {
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
+ glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
+
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+ const uint16_t protTexData[] = {0};
+ glGenTextures(1, &mProtectedTexName);
+ glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
+
+ // mColorBlindnessCorrection = M;
+
+ if (mUseColorManagement) {
+ ColorSpace srgb(ColorSpace::sRGB());
+ ColorSpace displayP3(ColorSpace::DisplayP3());
+ ColorSpace bt2020(ColorSpace::BT2020());
+
+ // Compute sRGB to Display P3 transform matrix.
+ // NOTE: For now, we are limiting output wide color space support to
+ // Display-P3 only.
+ mSrgbToDisplayP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform());
+
+ // Compute Display P3 to sRGB transform matrix.
+ mDisplayP3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform());
+
+ // no chromatic adaptation needed since all color spaces use D65 for their white points.
+ mSrgbToXyz = mat4(srgb.getRGBtoXYZ());
+ mDisplayP3ToXyz = mat4(displayP3.getRGBtoXYZ());
+ mBt2020ToXyz = mat4(bt2020.getRGBtoXYZ());
+ mXyzToSrgb = mat4(srgb.getXYZtoRGB());
+ mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
+ mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
+ }
+}
+
+GLES20RenderEngine::~GLES20RenderEngine() {
+ eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEGLDisplay);
+}
+
+std::unique_ptr<Framebuffer> GLES20RenderEngine::createFramebuffer() {
+ return std::make_unique<GLFramebuffer>(*this);
+}
+
+std::unique_ptr<Surface> GLES20RenderEngine::createSurface() {
+ return std::make_unique<GLSurface>(*this);
+}
+
+std::unique_ptr<Image> GLES20RenderEngine::createImage() {
+ return std::make_unique<GLImage>(*this);
+}
+
+void GLES20RenderEngine::primeCache() const {
+ ProgramCache::getInstance().primeCache(mFeatureFlags & USE_COLOR_MANAGEMENT);
+}
+
+bool GLES20RenderEngine::isCurrent() const {
+ return mEGLDisplay == eglGetCurrentDisplay() && mEGLContext == eglGetCurrentContext();
+}
+
+bool GLES20RenderEngine::setCurrentSurface(const Surface& surface) {
+ // Surface is an abstract interface. GLES20RenderEngine only ever
+ // creates GLSurface's, so it is safe to just cast to the actual
+ // type.
+ bool success = true;
+ const GLSurface& glSurface = static_cast<const GLSurface&>(surface);
+ EGLSurface eglSurface = glSurface.getEGLSurface();
+ if (eglSurface != eglGetCurrentSurface(EGL_DRAW)) {
+ success = eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext) == EGL_TRUE;
+ if (success && glSurface.getAsync()) {
+ eglSwapInterval(mEGLDisplay, 0);
+ }
+ if (success) {
+ mSurfaceHeight = glSurface.getHeight();
+ }
+ }
+
+ return success;
+}
+
+void GLES20RenderEngine::resetCurrentSurface() {
+ eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ mSurfaceHeight = 0;
+}
+
+base::unique_fd GLES20RenderEngine::flush() {
+ if (!GLExtensions::getInstance().hasNativeFenceSync()) {
+ return base::unique_fd();
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+ return base::unique_fd();
+ }
+
+ // native fence fd will not be populated until flush() is done.
+ glFlush();
+
+ // get the fence fd
+ base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+ }
+
+ return fenceFd;
+}
+
+bool GLES20RenderEngine::finish() {
+ if (!GLExtensions::getInstance().hasFenceSync()) {
+ ALOGW("no synchronization support");
+ return false;
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, nullptr);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+ 2000000000 /*2 sec*/);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (result != EGL_CONDITION_SATISFIED_KHR) {
+ if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGW("fence wait timed out");
+ } else {
+ ALOGW("error waiting on EGL fence: %#x", error);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool GLES20RenderEngine::waitFence(base::unique_fd fenceFd) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+ !GLExtensions::getInstance().hasWaitSync()) {
+ return false;
+ }
+
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ // fenceFd is now owned by EGLSync
+ (void)fenceFd.release();
+
+ // XXX: The spec draft is inconsistent as to whether this should return an
+ // EGLint or void. Ignore the return value for now, as it's not strictly
+ // needed.
+ eglWaitSyncKHR(mEGLDisplay, sync, 0);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (error != EGL_SUCCESS) {
+ ALOGE("failed to wait for EGL native fence sync: %#x", error);
+ return false;
+ }
+
+ return true;
+}
+
+void GLES20RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
+ glClearColor(red, green, blue, alpha);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void GLES20RenderEngine::fillRegionWithColor(const Region& region, float red, float green,
+ float blue, float alpha) {
+ size_t c;
+ Rect const* r = region.getArray(&c);
+ Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
+ Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
+ for (size_t i = 0; i < c; i++, r++) {
+ position[i * 6 + 0].x = r->left;
+ position[i * 6 + 0].y = r->top;
+ position[i * 6 + 1].x = r->left;
+ position[i * 6 + 1].y = r->bottom;
+ position[i * 6 + 2].x = r->right;
+ position[i * 6 + 2].y = r->bottom;
+ position[i * 6 + 3].x = r->left;
+ position[i * 6 + 3].y = r->top;
+ position[i * 6 + 4].x = r->right;
+ position[i * 6 + 4].y = r->bottom;
+ position[i * 6 + 5].x = r->right;
+ position[i * 6 + 5].y = r->top;
+ }
+ setupFillWithColor(red, green, blue, alpha);
+ drawMesh(mesh);
+}
+
+void GLES20RenderEngine::setScissor(const Rect& region) {
+ // Invert y-coordinate to map to GL-space.
+ int32_t canvasHeight = mRenderToFbo ? mFboHeight : mSurfaceHeight;
+ int32_t glBottom = canvasHeight - region.bottom;
+
+ glScissor(region.left, glBottom, region.getWidth(), region.getHeight());
+ glEnable(GL_SCISSOR_TEST);
+}
+
+void GLES20RenderEngine::disableScissor() {
+ glDisable(GL_SCISSOR_TEST);
+}
+
+void GLES20RenderEngine::genTextures(size_t count, uint32_t* names) {
+ glGenTextures(count, names);
+}
+
+void GLES20RenderEngine::deleteTextures(size_t count, uint32_t const* names) {
+ glDeleteTextures(count, names);
+}
+
+void GLES20RenderEngine::bindExternalTextureImage(uint32_t texName, const Image& image) {
+ const GLImage& glImage = static_cast<const GLImage&>(image);
+ const GLenum target = GL_TEXTURE_EXTERNAL_OES;
+
+ glBindTexture(target, texName);
+ if (glImage.getEGLImage() != EGL_NO_IMAGE_KHR) {
+ glEGLImageTargetTexture2DOES(target, static_cast<GLeglImageOES>(glImage.getEGLImage()));
+ }
+}
+
+status_t GLES20RenderEngine::bindFrameBuffer(Framebuffer* framebuffer) {
+ GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(framebuffer);
+ EGLImageKHR eglImage = glFramebuffer->getEGLImage();
+ uint32_t textureName = glFramebuffer->getTextureName();
+ uint32_t framebufferName = glFramebuffer->getFramebufferName();
+
+ // Bind the texture and turn our EGLImage into a texture
+ glBindTexture(GL_TEXTURE_2D, textureName);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)eglImage);
+
+ // Bind the Framebuffer to render into
+ glBindFramebuffer(GL_FRAMEBUFFER, framebufferName);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureName, 0);
+
+ mRenderToFbo = true;
+ mFboHeight = glFramebuffer->getBufferHeight();
+
+ uint32_t glStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+
+ ALOGE_IF(glStatus != GL_FRAMEBUFFER_COMPLETE_OES, "glCheckFramebufferStatusOES error %d",
+ glStatus);
+
+ return glStatus == GL_FRAMEBUFFER_COMPLETE_OES ? NO_ERROR : BAD_VALUE;
+}
+
+void GLES20RenderEngine::unbindFrameBuffer(Framebuffer* /* framebuffer */) {
+ mRenderToFbo = false;
+ mFboHeight = 0;
+
+ // back to main framebuffer
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ // Workaround for b/77935566 to force the EGL driver to release the
+ // screenshot buffer
+ setScissor(Rect::EMPTY_RECT);
+ clearWithColor(0.0, 0.0, 0.0, 0.0);
+ disableScissor();
+}
+
+void GLES20RenderEngine::checkErrors() const {
+ do {
+ // there could be more than one error flag
+ GLenum error = glGetError();
+ if (error == GL_NO_ERROR) break;
+ ALOGE("GL error 0x%04x", int(error));
+ } while (true);
+}
+
+status_t GLES20RenderEngine::drawLayers(const DisplaySettings& /*settings*/,
+ const std::vector<LayerSettings>& /*layers*/,
+ ANativeWindowBuffer* const /*buffer*/,
+ base::unique_fd* /*displayFence*/) const {
+ return NO_ERROR;
+}
+
+void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+ ui::Transform::orientation_flags rotation) {
+ int32_t l = sourceCrop.left;
+ int32_t r = sourceCrop.right;
+ int32_t b = sourceCrop.bottom;
+ int32_t t = sourceCrop.top;
+ if (mRenderToFbo) {
+ std::swap(t, b);
+ }
+ mat4 m = mat4::ortho(l, r, b, t, 0, 1);
+
+ // Apply custom rotation to the projection.
+ float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
+ switch (rotation) {
+ case ui::Transform::ROT_0:
+ break;
+ case ui::Transform::ROT_90:
+ m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m;
+ break;
+ case ui::Transform::ROT_180:
+ m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m;
+ break;
+ case ui::Transform::ROT_270:
+ m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m;
+ break;
+ default:
+ break;
+ }
+
+ glViewport(0, 0, vpw, vph);
+ mState.projectionMatrix = m;
+ mVpWidth = vpw;
+ mVpHeight = vph;
+}
+
+void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque,
+ bool disableTexture, const half4& color) {
+ mState.isPremultipliedAlpha = premultipliedAlpha;
+ mState.isOpaque = opaque;
+ mState.color = color;
+
+ if (disableTexture) {
+ mState.textureEnabled = false;
+ }
+
+ if (color.a < 1.0f || !opaque) {
+ glEnable(GL_BLEND);
+ glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ } else {
+ glDisable(GL_BLEND);
+ }
+}
+
+void GLES20RenderEngine::setSourceY410BT2020(bool enable) {
+ mState.isY410BT2020 = enable;
+}
+
+void GLES20RenderEngine::setSourceDataSpace(Dataspace source) {
+ mDataSpace = source;
+}
+
+void GLES20RenderEngine::setOutputDataSpace(Dataspace dataspace) {
+ mOutputDataSpace = dataspace;
+}
+
+void GLES20RenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
+ mState.displayMaxLuminance = maxLuminance;
+}
+
+void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) {
+ GLuint target = texture.getTextureTarget();
+ glBindTexture(target, texture.getTextureName());
+ GLenum filter = GL_NEAREST;
+ if (texture.getFiltering()) {
+ filter = GL_LINEAR;
+ }
+ glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+
+ mState.texture = texture;
+ mState.textureEnabled = true;
+}
+
+void GLES20RenderEngine::setupLayerBlackedOut() {
+ glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
+ Texture texture(Texture::TEXTURE_2D, mProtectedTexName);
+ texture.setDimensions(1, 1); // FIXME: we should get that from somewhere
+ mState.texture = texture;
+ mState.textureEnabled = true;
+}
+
+void GLES20RenderEngine::setColorTransform(const mat4& colorTransform) {
+ mState.colorMatrix = colorTransform;
+}
+
+void GLES20RenderEngine::disableTexturing() {
+ mState.textureEnabled = false;
+}
+
+void GLES20RenderEngine::disableBlending() {
+ glDisable(GL_BLEND);
+}
+
+void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
+ mState.isPremultipliedAlpha = true;
+ mState.isOpaque = false;
+ mState.color = half4(r, g, b, a);
+ mState.textureEnabled = false;
+ glDisable(GL_BLEND);
+}
+
+void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
+ ATRACE_CALL();
+ if (mesh.getTexCoordsSize()) {
+ glEnableVertexAttribArray(Program::texCoords);
+ glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getTexCoords());
+ }
+
+ glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getPositions());
+
+ // By default, DISPLAY_P3 is the only supported wide color output. However,
+ // when HDR content is present, hardware composer may be able to handle
+ // BT2020 data space, in that case, the output data space is set to be
+ // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
+ // to respect this and convert non-HDR content to HDR format.
+ if (mUseColorManagement) {
+ Description managedState = mState;
+ Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
+ Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+ Dataspace outputStandard =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::STANDARD_MASK);
+ Dataspace outputTransfer =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+ bool needsXYZConversion = needsXYZTransformMatrix();
+
+ if (needsXYZConversion) {
+ // The supported input color spaces are standard RGB, Display P3 and BT2020.
+ switch (inputStandard) {
+ case Dataspace::STANDARD_DCI_P3:
+ managedState.inputTransformMatrix = mDisplayP3ToXyz;
+ break;
+ case Dataspace::STANDARD_BT2020:
+ managedState.inputTransformMatrix = mBt2020ToXyz;
+ break;
+ default:
+ managedState.inputTransformMatrix = mSrgbToXyz;
+ break;
+ }
+
+ // The supported output color spaces are BT2020, Display P3 and standard RGB.
+ switch (outputStandard) {
+ case Dataspace::STANDARD_BT2020:
+ managedState.outputTransformMatrix = mXyzToBt2020;
+ break;
+ case Dataspace::STANDARD_DCI_P3:
+ managedState.outputTransformMatrix = mXyzToDisplayP3;
+ break;
+ default:
+ managedState.outputTransformMatrix = mXyzToSrgb;
+ break;
+ }
+ } else if (inputStandard != outputStandard) {
+ // At this point, the input data space and output data space could be both
+ // HDR data spaces, but they match each other, we do nothing in this case.
+ // In addition to the case above, the input data space could be
+ // - scRGB linear
+ // - scRGB non-linear
+ // - sRGB
+ // - Display P3
+ // The output data spaces could be
+ // - sRGB
+ // - Display P3
+ if (outputStandard == Dataspace::STANDARD_BT709) {
+ managedState.outputTransformMatrix = mDisplayP3ToSrgb;
+ } else if (outputStandard == Dataspace::STANDARD_DCI_P3) {
+ managedState.outputTransformMatrix = mSrgbToDisplayP3;
+ }
+ }
+
+ // we need to convert the RGB value to linear space and convert it back when:
+ // - there is a color matrix that is not an identity matrix, or
+ // - there is an output transform matrix that is not an identity matrix, or
+ // - the input transfer function doesn't match the output transfer function.
+ if (managedState.hasColorMatrix() || managedState.hasOutputTransformMatrix() ||
+ inputTransfer != outputTransfer) {
+ managedState.inputTransferFunction =
+ Description::dataSpaceToTransferFunction(inputTransfer);
+ managedState.outputTransferFunction =
+ Description::dataSpaceToTransferFunction(outputTransfer);
+ }
+
+ ProgramCache::getInstance().useProgram(managedState);
+
+ glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+
+ if (outputDebugPPMs) {
+ static uint64_t managedColorFrameCount = 0;
+ std::ostringstream out;
+ out << "/data/texture_out" << managedColorFrameCount++;
+ writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+ }
+ } else {
+ ProgramCache::getInstance().useProgram(mState);
+
+ glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
+ }
+
+ if (mesh.getTexCoordsSize()) {
+ glDisableVertexAttribArray(Program::texCoords);
+ }
+}
+
+size_t GLES20RenderEngine::getMaxTextureSize() const {
+ return mMaxTextureSize;
+}
+
+size_t GLES20RenderEngine::getMaxViewportDims() const {
+ return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
+}
+
+void GLES20RenderEngine::dump(String8& result) {
+ const GLExtensions& extensions = GLExtensions::getInstance();
+
+ result.appendFormat("EGL implementation : %s\n", extensions.getEGLVersion());
+ result.appendFormat("%s\n", extensions.getEGLExtensions());
+
+ result.appendFormat("GLES: %s, %s, %s\n", extensions.getVendor(), extensions.getRenderer(),
+ extensions.getVersion());
+ result.appendFormat("%s\n", extensions.getExtensions());
+
+ result.appendFormat("RenderEngine program cache size: %zu\n",
+ ProgramCache::getInstance().getSize());
+
+ result.appendFormat("RenderEngine last dataspace conversion: (%s) to (%s)\n",
+ dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
+ dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
+}
+
+GLES20RenderEngine::GlesVersion GLES20RenderEngine::parseGlesVersion(const char* str) {
+ int major, minor;
+ if (sscanf(str, "OpenGL ES-CM %d.%d", &major, &minor) != 2) {
+ if (sscanf(str, "OpenGL ES %d.%d", &major, &minor) != 2) {
+ ALOGW("Unable to parse GL_VERSION string: \"%s\"", str);
+ return GLES_VERSION_1_0;
+ }
+ }
+
+ if (major == 1 && minor == 0) return GLES_VERSION_1_0;
+ if (major == 1 && minor >= 1) return GLES_VERSION_1_1;
+ if (major == 2 && minor >= 0) return GLES_VERSION_2_0;
+ if (major == 3 && minor >= 0) return GLES_VERSION_3_0;
+
+ ALOGW("Unrecognized OpenGL ES version: %d.%d", major, minor);
+ return GLES_VERSION_1_0;
+}
+
+bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
+ const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
+ const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
+ return standard == Dataspace::STANDARD_BT2020 &&
+ (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
+}
+
+// For convenience, we want to convert the input color space to XYZ color space first,
+// and then convert from XYZ color space to output color space when
+// - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
+// HDR content will be tone-mapped to SDR; Or,
+// - there are HDR PQ and HLG contents presented at the same time, where we want to convert
+// HLG content to PQ content.
+// In either case above, we need to operate the Y value in XYZ color space. Thus, when either
+// input data space or output data space is HDR data space, and the input transfer function
+// doesn't match the output transfer function, we would enable an intermediate transfrom to
+// XYZ color space.
+bool GLES20RenderEngine::needsXYZTransformMatrix() const {
+ const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
+ const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
+ const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
+ const Dataspace outputTransfer =
+ static_cast<Dataspace>(mOutputDataSpace & Dataspace::TRANSFER_MASK);
+
+ return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
+}
+
+void GLES20RenderEngine::setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt) {
+ mEGLDisplay = display;
+ mEGLConfig = config;
+ mEGLContext = ctxt;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLES20RenderEngine.h b/libs/renderengine/gl/GLES20RenderEngine.h
new file mode 100644
index 0000000..6ea8523
--- /dev/null
+++ b/libs/renderengine/gl/GLES20RenderEngine.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SF_GLES20RENDERENGINE_H_
+#define SF_GLES20RENDERENGINE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES2/gl2.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/private/Description.h>
+
+#define EGL_NO_CONFIG ((EGLConfig)0)
+
+namespace android {
+
+class String8;
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace gl {
+
+class GLImage;
+class GLSurface;
+
+class GLES20RenderEngine : public impl::RenderEngine {
+public:
+ static std::unique_ptr<GLES20RenderEngine> create(int hwcFormat, uint32_t featureFlags);
+ static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig);
+
+ GLES20RenderEngine(uint32_t featureFlags); // See RenderEngine::FeatureFlag
+ ~GLES20RenderEngine() override;
+
+ std::unique_ptr<Framebuffer> createFramebuffer() override;
+ std::unique_ptr<Surface> createSurface() override;
+ std::unique_ptr<Image> createImage() override;
+
+ void primeCache() const override;
+ bool isCurrent() const override;
+ bool setCurrentSurface(const Surface& surface) override;
+ void resetCurrentSurface() override;
+ base::unique_fd flush() override;
+ bool finish() override;
+ bool waitFence(base::unique_fd fenceFd) override;
+ void clearWithColor(float red, float green, float blue, float alpha) override;
+ void fillRegionWithColor(const Region& region, float red, float green, float blue,
+ float alpha) override;
+ void setScissor(const Rect& region) override;
+ void disableScissor() override;
+ void genTextures(size_t count, uint32_t* names) override;
+ void deleteTextures(size_t count, uint32_t const* names) override;
+ void bindExternalTextureImage(uint32_t texName, const Image& image) override;
+ status_t bindFrameBuffer(Framebuffer* framebuffer) override;
+ void unbindFrameBuffer(Framebuffer* framebuffer) override;
+ void checkErrors() const override;
+
+ status_t drawLayers(const DisplaySettings& settings, const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* const buffer,
+ base::unique_fd* displayFence) const override;
+
+ // internal to RenderEngine
+ EGLDisplay getEGLDisplay() const { return mEGLDisplay; }
+ EGLConfig getEGLConfig() const { return mEGLConfig; }
+
+protected:
+ void dump(String8& result) override;
+ void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+ ui::Transform::orientation_flags rotation) override;
+ void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color) override;
+ void setupLayerTexturing(const Texture& texture) override;
+ void setupLayerBlackedOut() override;
+ void setupFillWithColor(float r, float g, float b, float a) override;
+ void setColorTransform(const mat4& colorTransform) override;
+ void disableTexturing() override;
+ void disableBlending() override;
+
+ // HDR and color management related functions and state
+ void setSourceY410BT2020(bool enable) override;
+ void setSourceDataSpace(ui::Dataspace source) override;
+ void setOutputDataSpace(ui::Dataspace dataspace) override;
+ void setDisplayMaxLuminance(const float maxLuminance) override;
+
+ // drawing
+ void drawMesh(const Mesh& mesh) override;
+
+ size_t getMaxTextureSize() const override;
+ size_t getMaxViewportDims() const override;
+
+private:
+ enum GlesVersion {
+ GLES_VERSION_1_0 = 0x10000,
+ GLES_VERSION_1_1 = 0x10001,
+ GLES_VERSION_2_0 = 0x20000,
+ GLES_VERSION_3_0 = 0x30000,
+ };
+
+ static GlesVersion parseGlesVersion(const char* str);
+
+ // A data space is considered HDR data space if it has BT2020 color space
+ // with PQ or HLG transfer function.
+ bool isHdrDataSpace(const ui::Dataspace dataSpace) const;
+ bool needsXYZTransformMatrix() const;
+ void setEGLHandles(EGLDisplay display, EGLConfig config, EGLContext ctxt);
+
+ EGLDisplay mEGLDisplay;
+ EGLConfig mEGLConfig;
+ EGLContext mEGLContext;
+ GLuint mProtectedTexName;
+ GLint mMaxViewportDims[2];
+ GLint mMaxTextureSize;
+ GLuint mVpWidth;
+ GLuint mVpHeight;
+ Description mState;
+
+ mat4 mSrgbToDisplayP3;
+ mat4 mDisplayP3ToSrgb;
+ mat4 mSrgbToXyz;
+ mat4 mBt2020ToXyz;
+ mat4 mDisplayP3ToXyz;
+ mat4 mXyzToSrgb;
+ mat4 mXyzToDisplayP3;
+ mat4 mXyzToBt2020;
+
+ bool mRenderToFbo = false;
+ int32_t mSurfaceHeight = 0;
+ int32_t mFboHeight = 0;
+
+ // Current dataspace of layer being rendered
+ ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN;
+
+ // Current output dataspace of the render engine
+ ui::Dataspace mOutputDataSpace = ui::Dataspace::UNKNOWN;
+
+ // Whether device supports color management, currently color management
+ // supports sRGB, DisplayP3 color spaces.
+ const bool mUseColorManagement = false;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLES20RENDERENGINE_H_ */
diff --git a/libs/renderengine/gl/GLExtensions.cpp b/libs/renderengine/gl/GLExtensions.cpp
new file mode 100644
index 0000000..784693b
--- /dev/null
+++ b/libs/renderengine/gl/GLExtensions.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 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 "GLExtensions.h"
+
+#include <string>
+#include <unordered_set>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions)
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+namespace {
+
+class ExtensionSet {
+public:
+ ExtensionSet(const char* extensions) {
+ char const* curr = extensions;
+ char const* head = curr;
+ do {
+ head = strchr(curr, ' ');
+ size_t len = head ? head - curr : strlen(curr);
+ if (len > 0) {
+ mExtensions.emplace(curr, len);
+ }
+ curr = head + 1;
+ } while (head);
+ }
+
+ bool hasExtension(const char* extension) const { return mExtensions.count(extension) > 0; }
+
+private:
+ std::unordered_set<std::string> mExtensions;
+};
+
+} // anonymous namespace
+
+void GLExtensions::initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer,
+ GLubyte const* version, GLubyte const* extensions) {
+ mVendor = (char const*)vendor;
+ mRenderer = (char const*)renderer;
+ mVersion = (char const*)version;
+ mExtensions = (char const*)extensions;
+}
+
+char const* GLExtensions::getVendor() const {
+ return mVendor.string();
+}
+
+char const* GLExtensions::getRenderer() const {
+ return mRenderer.string();
+}
+
+char const* GLExtensions::getVersion() const {
+ return mVersion.string();
+}
+
+char const* GLExtensions::getExtensions() const {
+ return mExtensions.string();
+}
+
+void GLExtensions::initWithEGLStrings(char const* eglVersion, char const* eglExtensions) {
+ mEGLVersion = eglVersion;
+ mEGLExtensions = eglExtensions;
+
+ ExtensionSet extensionSet(eglExtensions);
+
+ // EGL_ANDROIDX_no_config_context is an experimental extension with no
+ // written specification. It will be replaced by something more formal.
+ // SurfaceFlinger is using it to allow a single EGLContext to render to
+ // both a 16-bit primary display framebuffer and a 32-bit virtual display
+ // framebuffer.
+ //
+ // EGL_KHR_no_config_context is official extension to allow creating a
+ // context that works with any surface of a display.
+ if (extensionSet.hasExtension("EGL_ANDROIDX_no_config_context") ||
+ extensionSet.hasExtension("EGL_KHR_no_config_context")) {
+ mHasNoConfigContext = true;
+ }
+
+ if (extensionSet.hasExtension("EGL_ANDROID_native_fence_sync")) {
+ mHasNativeFenceSync = true;
+ }
+ if (extensionSet.hasExtension("EGL_KHR_fence_sync")) {
+ mHasFenceSync = true;
+ }
+ if (extensionSet.hasExtension("EGL_KHR_wait_sync")) {
+ mHasWaitSync = true;
+ }
+ if (extensionSet.hasExtension("EGL_EXT_protected_content")) {
+ mHasProtectedContent = true;
+ }
+ if (extensionSet.hasExtension("EGL_IMG_context_priority")) {
+ mHasContextPriority = true;
+ }
+}
+
+char const* GLExtensions::getEGLVersion() const {
+ return mEGLVersion.string();
+}
+
+char const* GLExtensions::getEGLExtensions() const {
+ return mEGLExtensions.string();
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLExtensions.h b/libs/renderengine/gl/GLExtensions.h
new file mode 100644
index 0000000..382c23a
--- /dev/null
+++ b/libs/renderengine/gl/GLExtensions.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef ANDROID_SF_GLEXTENSION_H
+#define ANDROID_SF_GLEXTENSION_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <utils/Singleton.h>
+#include <utils/String8.h>
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLExtensions : public Singleton<GLExtensions> {
+ friend class Singleton<GLExtensions>;
+
+ bool mHasNoConfigContext = false;
+ bool mHasNativeFenceSync = false;
+ bool mHasFenceSync = false;
+ bool mHasWaitSync = false;
+ bool mHasProtectedContent = false;
+ bool mHasContextPriority = false;
+
+ String8 mVendor;
+ String8 mRenderer;
+ String8 mVersion;
+ String8 mExtensions;
+
+ String8 mEGLVersion;
+ String8 mEGLExtensions;
+
+ GLExtensions(const GLExtensions&);
+ GLExtensions& operator=(const GLExtensions&);
+
+protected:
+ GLExtensions() = default;
+
+public:
+ bool hasNoConfigContext() const { return mHasNoConfigContext; }
+ bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
+ bool hasFenceSync() const { return mHasFenceSync; }
+ bool hasWaitSync() const { return mHasWaitSync; }
+ bool hasProtectedContent() const { return mHasProtectedContent; }
+ bool hasContextPriority() const { return mHasContextPriority; }
+
+ void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
+ GLubyte const* extensions);
+ char const* getVendor() const;
+ char const* getRenderer() const;
+ char const* getVersion() const;
+ char const* getExtensions() const;
+
+ void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
+ char const* getEGLVersion() const;
+ char const* getEGLExtensions() const;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp
new file mode 100644
index 0000000..2bd4e7f
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 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 "GLFramebuffer.h"
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <nativebase/nativebase.h>
+#include "GLES20RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLFramebuffer::GLFramebuffer(const GLES20RenderEngine& engine)
+ : mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) {
+ glGenTextures(1, &mTextureName);
+ glGenFramebuffers(1, &mFramebufferName);
+}
+
+GLFramebuffer::~GLFramebuffer() {
+ glDeleteFramebuffers(1, &mFramebufferName);
+ glDeleteTextures(1, &mTextureName);
+ eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+}
+
+bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) {
+ if (mEGLImage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(mEGLDisplay, mEGLImage);
+ mEGLImage = EGL_NO_IMAGE_KHR;
+ mBufferWidth = 0;
+ mBufferHeight = 0;
+ }
+
+ if (nativeBuffer) {
+ mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ nativeBuffer, nullptr);
+ if (mEGLImage == EGL_NO_IMAGE_KHR) {
+ return false;
+ }
+ mBufferWidth = nativeBuffer->width;
+ mBufferHeight = nativeBuffer->height;
+ }
+ return true;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h
new file mode 100644
index 0000000..90c6f4a
--- /dev/null
+++ b/libs/renderengine/gl/GLFramebuffer.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2018 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 <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <renderengine/Framebuffer.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLES20RenderEngine;
+
+class GLFramebuffer : public renderengine::Framebuffer {
+public:
+ explicit GLFramebuffer(const GLES20RenderEngine& engine);
+ ~GLFramebuffer() override;
+
+ bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) override;
+ EGLImageKHR getEGLImage() const { return mEGLImage; }
+ uint32_t getTextureName() const { return mTextureName; }
+ uint32_t getFramebufferName() const { return mFramebufferName; }
+ int32_t getBufferHeight() const { return mBufferHeight; }
+ int32_t getBufferWidth() const { return mBufferWidth; }
+
+private:
+ EGLDisplay mEGLDisplay;
+ EGLImageKHR mEGLImage;
+ uint32_t mTextureName, mFramebufferName;
+
+ int32_t mBufferHeight = 0;
+ int32_t mBufferWidth = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLImage.cpp b/libs/renderengine/gl/GLImage.cpp
new file mode 100644
index 0000000..5a92093
--- /dev/null
+++ b/libs/renderengine/gl/GLImage.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2018 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 "GLImage.h"
+
+#include <vector>
+
+#include <log/log.h>
+#include "GLES20RenderEngine.h"
+#include "GLExtensions.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+static std::vector<EGLint> buildAttributeList(bool isProtected) {
+ std::vector<EGLint> attrs;
+ attrs.reserve(16);
+
+ attrs.push_back(EGL_IMAGE_PRESERVED_KHR);
+ attrs.push_back(EGL_TRUE);
+
+ if (isProtected && GLExtensions::getInstance().hasProtectedContent()) {
+ attrs.push_back(EGL_PROTECTED_CONTENT_EXT);
+ attrs.push_back(EGL_TRUE);
+ }
+
+ attrs.push_back(EGL_NONE);
+
+ return attrs;
+}
+
+GLImage::GLImage(const GLES20RenderEngine& engine) : mEGLDisplay(engine.getEGLDisplay()) {}
+
+GLImage::~GLImage() {
+ setNativeWindowBuffer(nullptr, false);
+}
+
+bool GLImage::setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) {
+ if (mEGLImage != EGL_NO_IMAGE_KHR) {
+ if (!eglDestroyImageKHR(mEGLDisplay, mEGLImage)) {
+ ALOGE("failed to destroy image: %#x", eglGetError());
+ }
+ mEGLImage = EGL_NO_IMAGE_KHR;
+ }
+
+ if (buffer) {
+ std::vector<EGLint> attrs = buildAttributeList(isProtected);
+ mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+ static_cast<EGLClientBuffer>(buffer), attrs.data());
+ if (mEGLImage == EGL_NO_IMAGE_KHR) {
+ ALOGE("failed to create EGLImage: %#x", eglGetError());
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLImage.h b/libs/renderengine/gl/GLImage.h
new file mode 100644
index 0000000..0e451f8
--- /dev/null
+++ b/libs/renderengine/gl/GLImage.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 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 <cstdint>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <android-base/macros.h>
+#include <renderengine/Image.h>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLES20RenderEngine;
+
+class GLImage : public renderengine::Image {
+public:
+ explicit GLImage(const GLES20RenderEngine& engine);
+ ~GLImage() override;
+
+ bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) override;
+
+ EGLImageKHR getEGLImage() const { return mEGLImage; }
+
+private:
+ EGLDisplay mEGLDisplay;
+ EGLImageKHR mEGLImage = EGL_NO_IMAGE_KHR;
+
+ DISALLOW_COPY_AND_ASSIGN(GLImage);
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSurface.cpp b/libs/renderengine/gl/GLSurface.cpp
new file mode 100644
index 0000000..2d694e9
--- /dev/null
+++ b/libs/renderengine/gl/GLSurface.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2018 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 "GLSurface.h"
+
+#include <android/native_window.h>
+#include <log/log.h>
+#include <ui/PixelFormat.h>
+#include "GLES20RenderEngine.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLSurface::GLSurface(const GLES20RenderEngine& engine)
+ : mEGLDisplay(engine.getEGLDisplay()), mEGLConfig(engine.getEGLConfig()) {
+ // RE does not assume any config when EGL_KHR_no_config_context is supported
+ if (mEGLConfig == EGL_NO_CONFIG_KHR) {
+ mEGLConfig =
+ GLES20RenderEngine::chooseEglConfig(mEGLDisplay, PIXEL_FORMAT_RGBA_8888, false);
+ }
+}
+
+GLSurface::~GLSurface() {
+ setNativeWindow(nullptr);
+}
+
+void GLSurface::setNativeWindow(ANativeWindow* window) {
+ if (mEGLSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEGLDisplay, mEGLSurface);
+ mEGLSurface = EGL_NO_SURFACE;
+ mSurfaceWidth = 0;
+ mSurfaceHeight = 0;
+ }
+
+ mWindow = window;
+ if (mWindow) {
+ mEGLSurface = eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mWindow, nullptr);
+ mSurfaceWidth = ANativeWindow_getWidth(window);
+ mSurfaceHeight = ANativeWindow_getHeight(window);
+ }
+}
+
+void GLSurface::swapBuffers() const {
+ if (!eglSwapBuffers(mEGLDisplay, mEGLSurface)) {
+ EGLint error = eglGetError();
+
+ const char format[] = "eglSwapBuffers(%p, %p) failed with 0x%08x";
+ if (mCritical || error == EGL_CONTEXT_LOST) {
+ LOG_ALWAYS_FATAL(format, mEGLDisplay, mEGLSurface, error);
+ } else {
+ ALOGE(format, mEGLDisplay, mEGLSurface, error);
+ }
+ }
+}
+
+EGLint GLSurface::queryConfig(EGLint attrib) const {
+ EGLint value;
+ if (!eglGetConfigAttrib(mEGLDisplay, mEGLConfig, attrib, &value)) {
+ value = 0;
+ }
+
+ return value;
+}
+
+int32_t GLSurface::queryRedSize() const {
+ return queryConfig(EGL_RED_SIZE);
+}
+
+int32_t GLSurface::queryGreenSize() const {
+ return queryConfig(EGL_GREEN_SIZE);
+}
+
+int32_t GLSurface::queryBlueSize() const {
+ return queryConfig(EGL_BLUE_SIZE);
+}
+
+int32_t GLSurface::queryAlphaSize() const {
+ return queryConfig(EGL_ALPHA_SIZE);
+}
+
+int32_t GLSurface::getWidth() const {
+ return mSurfaceWidth;
+}
+
+int32_t GLSurface::getHeight() const {
+ return mSurfaceHeight;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSurface.h b/libs/renderengine/gl/GLSurface.h
new file mode 100644
index 0000000..092d371
--- /dev/null
+++ b/libs/renderengine/gl/GLSurface.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2018 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 <cstdint>
+
+#include <EGL/egl.h>
+#include <android-base/macros.h>
+#include <renderengine/Surface.h>
+
+struct ANativeWindow;
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+class GLES20RenderEngine;
+
+class GLSurface final : public renderengine::Surface {
+public:
+ GLSurface(const GLES20RenderEngine& engine);
+ ~GLSurface() override;
+
+ // renderengine::Surface implementation
+ void setCritical(bool enable) override { mCritical = enable; }
+ void setAsync(bool enable) override { mAsync = enable; }
+
+ void setNativeWindow(ANativeWindow* window) override;
+ void swapBuffers() const override;
+
+ int32_t queryRedSize() const override;
+ int32_t queryGreenSize() const override;
+ int32_t queryBlueSize() const override;
+ int32_t queryAlphaSize() const override;
+
+ bool getAsync() const { return mAsync; }
+ EGLSurface getEGLSurface() const { return mEGLSurface; }
+
+ int32_t getWidth() const override;
+ int32_t getHeight() const override;
+
+private:
+ EGLint queryConfig(EGLint attrib) const;
+
+ EGLDisplay mEGLDisplay;
+ EGLConfig mEGLConfig;
+
+ bool mCritical = false;
+ bool mAsync = false;
+
+ int32_t mSurfaceWidth = 0;
+ int32_t mSurfaceHeight = 0;
+
+ ANativeWindow* mWindow = nullptr;
+ EGLSurface mEGLSurface = EGL_NO_SURFACE;
+
+ DISALLOW_COPY_AND_ASSIGN(GLSurface);
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
new file mode 100644
index 0000000..7d2ea90
--- /dev/null
+++ b/libs/renderengine/gl/Program.cpp
@@ -0,0 +1,144 @@
+/*Gluint
+ * Copyright 2013 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 "Program.h"
+
+#include <stdint.h>
+
+#include <log/log.h>
+#include <math/mat4.h>
+#include <utils/String8.h>
+#include "ProgramCache.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+Program::Program(const ProgramCache::Key& /*needs*/, const char* vertex, const char* fragment)
+ : mInitialized(false) {
+ GLuint vertexId = buildShader(vertex, GL_VERTEX_SHADER);
+ GLuint fragmentId = buildShader(fragment, GL_FRAGMENT_SHADER);
+ GLuint programId = glCreateProgram();
+ glAttachShader(programId, vertexId);
+ glAttachShader(programId, fragmentId);
+ glBindAttribLocation(programId, position, "position");
+ glBindAttribLocation(programId, texCoords, "texCoords");
+ glLinkProgram(programId);
+
+ GLint status;
+ glGetProgramiv(programId, GL_LINK_STATUS, &status);
+ if (status != GL_TRUE) {
+ ALOGE("Error while linking shaders:");
+ GLint infoLen = 0;
+ glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen > 1) {
+ GLchar log[infoLen];
+ glGetProgramInfoLog(programId, infoLen, 0, &log[0]);
+ ALOGE("%s", log);
+ }
+ glDetachShader(programId, vertexId);
+ glDetachShader(programId, fragmentId);
+ glDeleteShader(vertexId);
+ glDeleteShader(fragmentId);
+ glDeleteProgram(programId);
+ } else {
+ mProgram = programId;
+ mVertexShader = vertexId;
+ mFragmentShader = fragmentId;
+ mInitialized = true;
+ mProjectionMatrixLoc = glGetUniformLocation(programId, "projection");
+ mTextureMatrixLoc = glGetUniformLocation(programId, "texture");
+ mSamplerLoc = glGetUniformLocation(programId, "sampler");
+ mColorLoc = glGetUniformLocation(programId, "color");
+ mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
+ mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
+ mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
+
+ // set-up the default values for our uniforms
+ glUseProgram(programId);
+ glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, mat4().asArray());
+ glEnableVertexAttribArray(0);
+ }
+}
+
+bool Program::isValid() const {
+ return mInitialized;
+}
+
+void Program::use() {
+ glUseProgram(mProgram);
+}
+
+GLuint Program::getAttrib(const char* name) const {
+ // TODO: maybe use a local cache
+ return glGetAttribLocation(mProgram, name);
+}
+
+GLint Program::getUniform(const char* name) const {
+ // TODO: maybe use a local cache
+ return glGetUniformLocation(mProgram, name);
+}
+
+GLuint Program::buildShader(const char* source, GLenum type) {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &source, 0);
+ glCompileShader(shader);
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+ // use a fixed size instead
+ GLchar log[512];
+ glGetShaderInfoLog(shader, sizeof(log), 0, log);
+ ALOGE("Error while compiling shader: \n%s\n%s", source, log);
+ glDeleteShader(shader);
+ return 0;
+ }
+ return shader;
+}
+
+void Program::setUniforms(const Description& desc) {
+ // TODO: we should have a mechanism here to not always reset uniforms that
+ // didn't change for this program.
+
+ if (mSamplerLoc >= 0) {
+ glUniform1i(mSamplerLoc, 0);
+ glUniformMatrix4fv(mTextureMatrixLoc, 1, GL_FALSE, desc.texture.getMatrix().asArray());
+ }
+ if (mColorLoc >= 0) {
+ const float color[4] = {desc.color.r, desc.color.g, desc.color.b, desc.color.a};
+ glUniform4fv(mColorLoc, 1, color);
+ }
+ if (mInputTransformMatrixLoc >= 0) {
+ mat4 inputTransformMatrix = desc.inputTransformMatrix;
+ glUniformMatrix4fv(mInputTransformMatrixLoc, 1, GL_FALSE, inputTransformMatrix.asArray());
+ }
+ if (mOutputTransformMatrixLoc >= 0) {
+ // The output transform matrix and color matrix can be combined as one matrix
+ // that is applied right before applying OETF.
+ mat4 outputTransformMatrix = desc.colorMatrix * desc.outputTransformMatrix;
+ glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE, outputTransformMatrix.asArray());
+ }
+ if (mDisplayMaxLuminanceLoc >= 0) {
+ glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
+ }
+ // these uniforms are always present
+ glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.projectionMatrix.asArray());
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
new file mode 100644
index 0000000..99bf0f0
--- /dev/null
+++ b/libs/renderengine/gl/Program.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_PROGRAM_H
+#define SF_RENDER_ENGINE_PROGRAM_H
+
+#include <stdint.h>
+
+#include <GLES2/gl2.h>
+#include <renderengine/private/Description.h>
+#include "ProgramCache.h"
+
+namespace android {
+
+class String8;
+
+namespace renderengine {
+namespace gl {
+
+/*
+ * Abstracts a GLSL program comprising a vertex and fragment shader
+ */
+class Program {
+public:
+ // known locations for position and texture coordinates
+ enum { position = 0, texCoords = 1 };
+
+ Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
+ ~Program() = default;
+
+ /* whether this object is usable */
+ bool isValid() const;
+
+ /* Binds this program to the GLES context */
+ void use();
+
+ /* Returns the location of the specified attribute */
+ GLuint getAttrib(const char* name) const;
+
+ /* Returns the location of the specified uniform */
+ GLint getUniform(const char* name) const;
+
+ /* set-up uniforms from the description */
+ void setUniforms(const Description& desc);
+
+private:
+ GLuint buildShader(const char* source, GLenum type);
+
+ // whether the initialization succeeded
+ bool mInitialized;
+
+ // Name of the OpenGL program and shaders
+ GLuint mProgram;
+ GLuint mVertexShader;
+ GLuint mFragmentShader;
+
+ /* location of the projection matrix uniform */
+ GLint mProjectionMatrixLoc;
+
+ /* location of the texture matrix uniform */
+ GLint mTextureMatrixLoc;
+
+ /* location of the sampler uniform */
+ GLint mSamplerLoc;
+
+ /* location of the color uniform */
+ GLint mColorLoc;
+
+ /* location of display luminance uniform */
+ GLint mDisplayMaxLuminanceLoc;
+
+ /* location of transform matrix */
+ GLint mInputTransformMatrixLoc;
+ GLint mOutputTransformMatrixLoc;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_PROGRAM_H */
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
new file mode 100644
index 0000000..464fc15
--- /dev/null
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -0,0 +1,684 @@
+/*
+ * Copyright 2013 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 ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include "ProgramCache.h"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+#include <log/log.h>
+#include <renderengine/private/Description.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
+#include "Program.h"
+
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::ProgramCache)
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/*
+ * A simple formatter class to automatically add the endl and
+ * manage the indentation.
+ */
+
+class Formatter;
+static Formatter& indent(Formatter& f);
+static Formatter& dedent(Formatter& f);
+
+class Formatter {
+ String8 mString;
+ int mIndent;
+ typedef Formatter& (*FormaterManipFunc)(Formatter&);
+ friend Formatter& indent(Formatter& f);
+ friend Formatter& dedent(Formatter& f);
+
+public:
+ Formatter() : mIndent(0) {}
+
+ String8 getString() const { return mString; }
+
+ friend Formatter& operator<<(Formatter& out, const char* in) {
+ for (int i = 0; i < out.mIndent; i++) {
+ out.mString.append(" ");
+ }
+ out.mString.append(in);
+ out.mString.append("\n");
+ return out;
+ }
+ friend inline Formatter& operator<<(Formatter& out, const String8& in) {
+ return operator<<(out, in.string());
+ }
+ friend inline Formatter& operator<<(Formatter& to, FormaterManipFunc func) {
+ return (*func)(to);
+ }
+};
+Formatter& indent(Formatter& f) {
+ f.mIndent++;
+ return f;
+}
+Formatter& dedent(Formatter& f) {
+ f.mIndent--;
+ return f;
+}
+
+void ProgramCache::primeCache(bool useColorManagement) {
+ uint32_t shaderCount = 0;
+ uint32_t keyMask = Key::BLEND_MASK | Key::OPACITY_MASK | Key::ALPHA_MASK | Key::TEXTURE_MASK;
+ // Prime the cache for all combinations of the above masks,
+ // leaving off the experimental color matrix mask options.
+
+ nsecs_t timeBefore = systemTime();
+ for (uint32_t keyVal = 0; keyVal <= keyMask; keyVal++) {
+ Key shaderKey;
+ shaderKey.set(keyMask, keyVal);
+ uint32_t tex = shaderKey.getTextureTarget();
+ if (tex != Key::TEXTURE_OFF && tex != Key::TEXTURE_EXT && tex != Key::TEXTURE_2D) {
+ continue;
+ }
+ if (mCache.count(shaderKey) == 0) {
+ mCache.emplace(shaderKey, generateProgram(shaderKey));
+ shaderCount++;
+ }
+ }
+
+ // Prime for sRGB->P3 conversion
+ if (useColorManagement) {
+ Key shaderKey;
+ shaderKey.set(Key::BLEND_MASK | Key::TEXTURE_MASK | Key::OUTPUT_TRANSFORM_MATRIX_MASK |
+ Key::INPUT_TF_MASK | Key::OUTPUT_TF_MASK,
+ Key::BLEND_PREMULT | Key::TEXTURE_EXT | Key::OUTPUT_TRANSFORM_MATRIX_ON |
+ Key::INPUT_TF_SRGB | Key::OUTPUT_TF_SRGB);
+ for (int i = 0; i < 4; i++) {
+ shaderKey.set(Key::OPACITY_MASK,
+ (i & 1) ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT);
+ shaderKey.set(Key::ALPHA_MASK, (i & 2) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE);
+ if (mCache.count(shaderKey) == 0) {
+ mCache.emplace(shaderKey, generateProgram(shaderKey));
+ shaderCount++;
+ }
+ }
+ }
+
+ nsecs_t timeAfter = systemTime();
+ float compileTimeMs = static_cast<float>(timeAfter - timeBefore) / 1.0E6;
+ ALOGD("shader cache generated - %u shaders in %f ms\n", shaderCount, compileTimeMs);
+}
+
+ProgramCache::Key ProgramCache::computeKey(const Description& description) {
+ Key needs;
+ needs.set(Key::TEXTURE_MASK,
+ !description.textureEnabled
+ ? Key::TEXTURE_OFF
+ : description.texture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
+ ? Key::TEXTURE_EXT
+ : description.texture.getTextureTarget() == GL_TEXTURE_2D
+ ? Key::TEXTURE_2D
+ : Key::TEXTURE_OFF)
+ .set(Key::ALPHA_MASK, (description.color.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
+ .set(Key::BLEND_MASK,
+ description.isPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
+ .set(Key::OPACITY_MASK,
+ description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
+ .set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
+ description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
+ : Key::INPUT_TRANSFORM_MATRIX_OFF)
+ .set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
+ description.hasOutputTransformMatrix() || description.hasColorMatrix()
+ ? Key::OUTPUT_TRANSFORM_MATRIX_ON
+ : Key::OUTPUT_TRANSFORM_MATRIX_OFF);
+
+ needs.set(Key::Y410_BT2020_MASK,
+ description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ switch (description.inputTransferFunction) {
+ case Description::TransferFunction::LINEAR:
+ default:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_LINEAR);
+ break;
+ case Description::TransferFunction::SRGB:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_SRGB);
+ break;
+ case Description::TransferFunction::ST2084:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_ST2084);
+ break;
+ case Description::TransferFunction::HLG:
+ needs.set(Key::INPUT_TF_MASK, Key::INPUT_TF_HLG);
+ break;
+ }
+
+ switch (description.outputTransferFunction) {
+ case Description::TransferFunction::LINEAR:
+ default:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_LINEAR);
+ break;
+ case Description::TransferFunction::SRGB:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_SRGB);
+ break;
+ case Description::TransferFunction::ST2084:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_ST2084);
+ break;
+ case Description::TransferFunction::HLG:
+ needs.set(Key::OUTPUT_TF_MASK, Key::OUTPUT_TF_HLG);
+ break;
+ }
+ }
+
+ return needs;
+}
+
+// Generate EOTF that converts signal values to relative display light,
+// both normalized to [0, 1].
+void ProgramCache::generateEOTF(Formatter& fs, const Key& needs) {
+ switch (needs.getInputTF()) {
+ case Key::INPUT_TF_SRGB:
+ fs << R"__SHADER__(
+ float EOTF_sRGB(float srgb) {
+ return srgb <= 0.04045 ? srgb / 12.92 : pow((srgb + 0.055) / 1.055, 2.4);
+ }
+
+ vec3 EOTF_sRGB(const vec3 srgb) {
+ return vec3(EOTF_sRGB(srgb.r), EOTF_sRGB(srgb.g), EOTF_sRGB(srgb.b));
+ }
+
+ vec3 EOTF(const vec3 srgb) {
+ return sign(srgb.rgb) * EOTF_sRGB(abs(srgb.rgb));
+ }
+ )__SHADER__";
+ break;
+ case Key::INPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ vec3 EOTF(const highp vec3 color) {
+ const highp float m1 = (2610.0 / 4096.0) / 4.0;
+ const highp float m2 = (2523.0 / 4096.0) * 128.0;
+ const highp float c1 = (3424.0 / 4096.0);
+ const highp float c2 = (2413.0 / 4096.0) * 32.0;
+ const highp float c3 = (2392.0 / 4096.0) * 32.0;
+
+ highp vec3 tmp = pow(color, 1.0 / vec3(m2));
+ tmp = max(tmp - c1, 0.0) / (c2 - c3 * tmp);
+ return pow(tmp, 1.0 / vec3(m1));
+ }
+ )__SHADER__";
+ break;
+ case Key::INPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp float EOTF_channel(const highp float channel) {
+ const highp float a = 0.17883277;
+ const highp float b = 0.28466892;
+ const highp float c = 0.55991073;
+ return channel <= 0.5 ? channel * channel / 3.0 :
+ (exp((channel - c) / a) + b) / 12.0;
+ }
+
+ vec3 EOTF(const highp vec3 color) {
+ return vec3(EOTF_channel(color.r), EOTF_channel(color.g),
+ EOTF_channel(color.b));
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ vec3 EOTF(const vec3 linear) {
+ return linear;
+ }
+ )__SHADER__";
+ break;
+ }
+}
+
+void ProgramCache::generateToneMappingProcess(Formatter& fs, const Key& needs) {
+ // Convert relative light to absolute light.
+ switch (needs.getInputTF()) {
+ case Key::INPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ highp vec3 ScaleLuminance(highp vec3 color) {
+ return color * 10000.0;
+ }
+ )__SHADER__";
+ break;
+ case Key::INPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp vec3 ScaleLuminance(highp vec3 color) {
+ // The formula is:
+ // alpha * pow(Y, gamma - 1.0) * color + beta;
+ // where alpha is 1000.0, gamma is 1.2, beta is 0.0.
+ return color * 1000.0 * pow(color.y, 0.2);
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ highp vec3 ScaleLuminance(highp vec3 color) {
+ return color * displayMaxLuminance;
+ }
+ )__SHADER__";
+ break;
+ }
+
+ // Tone map absolute light to display luminance range.
+ switch (needs.getInputTF()) {
+ case Key::INPUT_TF_ST2084:
+ case Key::INPUT_TF_HLG:
+ switch (needs.getOutputTF()) {
+ case Key::OUTPUT_TF_HLG:
+ // Right now when mixed PQ and HLG contents are presented,
+ // HLG content will always be converted to PQ. However, for
+ // completeness, we simply clamp the value to [0.0, 1000.0].
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ return clamp(color, 0.0, 1000.0);
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ const float maxMasteringLumi = 1000.0;
+ const float maxContentLumi = 1000.0;
+ const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+ float maxOutLumi = displayMaxLuminance;
+
+ float nits = color.y;
+
+ // clamp to max input luminance
+ nits = clamp(nits, 0.0, maxInLumi);
+
+ // scale [0.0, maxInLumi] to [0.0, maxOutLumi]
+ if (maxInLumi <= maxOutLumi) {
+ return color * (maxOutLumi / maxInLumi);
+ } else {
+ // three control points
+ const float x0 = 10.0;
+ const float y0 = 17.0;
+ float x1 = maxOutLumi * 0.75;
+ float y1 = x1;
+ float x2 = x1 + (maxInLumi - x1) / 2.0;
+ float y2 = y1 + (maxOutLumi - y1) * 0.75;
+
+ // horizontal distances between the last three control points
+ float h12 = x2 - x1;
+ float h23 = maxInLumi - x2;
+ // tangents at the last three control points
+ float m1 = (y2 - y1) / h12;
+ float m3 = (maxOutLumi - y2) / h23;
+ float m2 = (m1 + m3) / 2.0;
+
+ if (nits < x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ float slope = y0 / x0;
+ return color * slope;
+ } else if (nits < x1) {
+ // scale [x0, x1] to [y0, y1] linearly
+ float slope = (y1 - y0) / (x1 - x0);
+ nits = y0 + (nits - x0) * slope;
+ } else if (nits < x2) {
+ // scale [x1, x2] to [y1, y2] using Hermite interp
+ float t = (nits - x1) / h12;
+ nits = (y1 * (1.0 + 2.0 * t) + h12 * m1 * t) * (1.0 - t) * (1.0 - t) +
+ (y2 * (3.0 - 2.0 * t) + h12 * m2 * (t - 1.0)) * t * t;
+ } else {
+ // scale [x2, maxInLumi] to [y2, maxOutLumi] using Hermite interp
+ float t = (nits - x2) / h23;
+ nits = (y2 * (1.0 + 2.0 * t) + h23 * m2 * t) * (1.0 - t) * (1.0 - t) +
+ (maxOutLumi * (3.0 - 2.0 * t) + h23 * m3 * (t - 1.0)) * t * t;
+ }
+ }
+
+ // color.y is greater than x0 and is thus non-zero
+ return color * (nits / color.y);
+ }
+ )__SHADER__";
+ break;
+ }
+ break;
+ default:
+ // inverse tone map; the output luminance can be up to maxOutLumi.
+ fs << R"__SHADER__(
+ highp vec3 ToneMap(highp vec3 color) {
+ const float maxOutLumi = 3000.0;
+
+ const float x0 = 5.0;
+ const float y0 = 2.5;
+ float x1 = displayMaxLuminance * 0.7;
+ float y1 = maxOutLumi * 0.15;
+ float x2 = displayMaxLuminance * 0.9;
+ float y2 = maxOutLumi * 0.45;
+ float x3 = displayMaxLuminance;
+ float y3 = maxOutLumi;
+
+ float c1 = y1 / 3.0;
+ float c2 = y2 / 2.0;
+ float c3 = y3 / 1.5;
+
+ float nits = color.y;
+
+ float scale;
+ if (nits <= x0) {
+ // scale [0.0, x0] to [0.0, y0] linearly
+ const float slope = y0 / x0;
+ return color * slope;
+ } else if (nits <= x1) {
+ // scale [x0, x1] to [y0, y1] using a curve
+ float t = (nits - x0) / (x1 - x0);
+ nits = (1.0 - t) * (1.0 - t) * y0 + 2.0 * (1.0 - t) * t * c1 + t * t * y1;
+ } else if (nits <= x2) {
+ // scale [x1, x2] to [y1, y2] using a curve
+ float t = (nits - x1) / (x2 - x1);
+ nits = (1.0 - t) * (1.0 - t) * y1 + 2.0 * (1.0 - t) * t * c2 + t * t * y2;
+ } else {
+ // scale [x2, x3] to [y2, y3] using a curve
+ float t = (nits - x2) / (x3 - x2);
+ nits = (1.0 - t) * (1.0 - t) * y2 + 2.0 * (1.0 - t) * t * c3 + t * t * y3;
+ }
+
+ // color.y is greater than x0 and is thus non-zero
+ return color * (nits / color.y);
+ }
+ )__SHADER__";
+ break;
+ }
+
+ // convert absolute light to relative light.
+ switch (needs.getOutputTF()) {
+ case Key::OUTPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ highp vec3 NormalizeLuminance(highp vec3 color) {
+ return color / 10000.0;
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp vec3 NormalizeLuminance(highp vec3 color) {
+ return color / 1000.0 * pow(color.y / 1000.0, -0.2 / 1.2);
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ highp vec3 NormalizeLuminance(highp vec3 color) {
+ return color / displayMaxLuminance;
+ }
+ )__SHADER__";
+ break;
+ }
+}
+
+// Generate OOTF that modifies the relative scence light to relative display light.
+void ProgramCache::generateOOTF(Formatter& fs, const ProgramCache::Key& needs) {
+ if (!needs.needsToneMapping()) {
+ fs << R"__SHADER__(
+ highp vec3 OOTF(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ } else {
+ generateToneMappingProcess(fs, needs);
+ fs << R"__SHADER__(
+ highp vec3 OOTF(const highp vec3 color) {
+ return NormalizeLuminance(ToneMap(ScaleLuminance(color)));
+ }
+ )__SHADER__";
+ }
+}
+
+// Generate OETF that converts relative display light to signal values,
+// both normalized to [0, 1]
+void ProgramCache::generateOETF(Formatter& fs, const Key& needs) {
+ switch (needs.getOutputTF()) {
+ case Key::OUTPUT_TF_SRGB:
+ fs << R"__SHADER__(
+ float OETF_sRGB(const float linear) {
+ return linear <= 0.0031308 ?
+ linear * 12.92 : (pow(linear, 1.0 / 2.4) * 1.055) - 0.055;
+ }
+
+ vec3 OETF_sRGB(const vec3 linear) {
+ return vec3(OETF_sRGB(linear.r), OETF_sRGB(linear.g), OETF_sRGB(linear.b));
+ }
+
+ vec3 OETF(const vec3 linear) {
+ return sign(linear.rgb) * OETF_sRGB(abs(linear.rgb));
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_ST2084:
+ fs << R"__SHADER__(
+ vec3 OETF(const vec3 linear) {
+ const highp float m1 = (2610.0 / 4096.0) / 4.0;
+ const highp float m2 = (2523.0 / 4096.0) * 128.0;
+ const highp float c1 = (3424.0 / 4096.0);
+ const highp float c2 = (2413.0 / 4096.0) * 32.0;
+ const highp float c3 = (2392.0 / 4096.0) * 32.0;
+
+ highp vec3 tmp = pow(linear, vec3(m1));
+ tmp = (c1 + c2 * tmp) / (1.0 + c3 * tmp);
+ return pow(tmp, vec3(m2));
+ }
+ )__SHADER__";
+ break;
+ case Key::OUTPUT_TF_HLG:
+ fs << R"__SHADER__(
+ highp float OETF_channel(const highp float channel) {
+ const highp float a = 0.17883277;
+ const highp float b = 0.28466892;
+ const highp float c = 0.55991073;
+ return channel <= 1.0 / 12.0 ? sqrt(3.0 * channel) :
+ a * log(12.0 * channel - b) + c;
+ }
+
+ vec3 OETF(const highp vec3 color) {
+ return vec3(OETF_channel(color.r), OETF_channel(color.g),
+ OETF_channel(color.b));
+ }
+ )__SHADER__";
+ break;
+ default:
+ fs << R"__SHADER__(
+ vec3 OETF(const vec3 linear) {
+ return linear;
+ }
+ )__SHADER__";
+ break;
+ }
+}
+
+String8 ProgramCache::generateVertexShader(const Key& needs) {
+ Formatter vs;
+ if (needs.isTexturing()) {
+ vs << "attribute vec4 texCoords;"
+ << "varying vec2 outTexCoords;";
+ }
+ vs << "attribute vec4 position;"
+ << "uniform mat4 projection;"
+ << "uniform mat4 texture;"
+ << "void main(void) {" << indent << "gl_Position = projection * position;";
+ if (needs.isTexturing()) {
+ vs << "outTexCoords = (texture * texCoords).st;";
+ }
+ vs << dedent << "}";
+ return vs.getString();
+}
+
+String8 ProgramCache::generateFragmentShader(const Key& needs) {
+ Formatter fs;
+ if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
+ fs << "#extension GL_OES_EGL_image_external : require";
+ }
+
+ // default precision is required-ish in fragment shaders
+ fs << "precision mediump float;";
+
+ if (needs.getTextureTarget() == Key::TEXTURE_EXT) {
+ fs << "uniform samplerExternalOES sampler;"
+ << "varying vec2 outTexCoords;";
+ } else if (needs.getTextureTarget() == Key::TEXTURE_2D) {
+ fs << "uniform sampler2D sampler;"
+ << "varying vec2 outTexCoords;";
+ }
+
+ if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
+ fs << "uniform vec4 color;";
+ }
+
+ if (needs.isY410BT2020()) {
+ fs << R"__SHADER__(
+ vec3 convertY410BT2020(const vec3 color) {
+ const vec3 offset = vec3(0.0625, 0.5, 0.5);
+ const mat3 transform = mat3(
+ vec3(1.1678, 1.1678, 1.1678),
+ vec3( 0.0, -0.1878, 2.1481),
+ vec3(1.6836, -0.6523, 0.0));
+ // Y is in G, U is in R, and V is in B
+ return clamp(transform * (color.grb - offset), 0.0, 1.0);
+ }
+ )__SHADER__";
+ }
+
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ // Currently, display maximum luminance is needed when doing tone mapping.
+ if (needs.needsToneMapping()) {
+ fs << "uniform float displayMaxLuminance;";
+ }
+
+ if (needs.hasInputTransformMatrix()) {
+ fs << "uniform mat4 inputTransformMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 InputTransform(const highp vec3 color) {
+ return clamp(vec3(inputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 InputTransform(const highp vec3 color) {
+ return color;
+ }
+ )__SHADER__";
+ }
+
+ // the transformation from a wider colorspace to a narrower one can
+ // result in >1.0 or <0.0 pixel values
+ if (needs.hasOutputTransformMatrix()) {
+ fs << "uniform mat4 outputTransformMatrix;";
+ fs << R"__SHADER__(
+ highp vec3 OutputTransform(const highp vec3 color) {
+ return clamp(vec3(outputTransformMatrix * vec4(color, 1.0)), 0.0, 1.0);
+ }
+ )__SHADER__";
+ } else {
+ fs << R"__SHADER__(
+ highp vec3 OutputTransform(const highp vec3 color) {
+ return clamp(color, 0.0, 1.0);
+ }
+ )__SHADER__";
+ }
+
+ generateEOTF(fs, needs);
+ generateOOTF(fs, needs);
+ generateOETF(fs, needs);
+ }
+
+ fs << "void main(void) {" << indent;
+ if (needs.isTexturing()) {
+ fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+ if (needs.isY410BT2020()) {
+ fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+ }
+ } else {
+ fs << "gl_FragColor.rgb = color.rgb;";
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.isOpaque()) {
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.hasAlpha()) {
+ // modulate the current alpha value with alpha set
+ if (needs.isPremultiplied()) {
+ // ... and the color too if we're premultiplied
+ fs << "gl_FragColor *= color.a;";
+ } else {
+ fs << "gl_FragColor.a *= color.a;";
+ }
+ }
+
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ if (!needs.isOpaque() && needs.isPremultiplied()) {
+ // un-premultiply if needed before linearization
+ // avoid divide by 0 by adding 0.5/256 to the alpha channel
+ fs << "gl_FragColor.rgb = gl_FragColor.rgb / (gl_FragColor.a + 0.0019);";
+ }
+ fs << "gl_FragColor.rgb = "
+ "OETF(OutputTransform(OOTF(InputTransform(EOTF(gl_FragColor.rgb)))));";
+ if (!needs.isOpaque() && needs.isPremultiplied()) {
+ // and re-premultiply if needed after gamma correction
+ fs << "gl_FragColor.rgb = gl_FragColor.rgb * (gl_FragColor.a + 0.0019);";
+ }
+ }
+
+ fs << dedent << "}";
+ return fs.getString();
+}
+
+std::unique_ptr<Program> ProgramCache::generateProgram(const Key& needs) {
+ ATRACE_CALL();
+
+ // vertex shader
+ String8 vs = generateVertexShader(needs);
+
+ // fragment shader
+ String8 fs = generateFragmentShader(needs);
+
+ return std::make_unique<Program>(needs, vs.string(), fs.string());
+}
+
+void ProgramCache::useProgram(const Description& description) {
+ // generate the key for the shader based on the description
+ Key needs(computeKey(description));
+
+ // look-up the program in the cache
+ auto it = mCache.find(needs);
+ if (it == mCache.end()) {
+ // we didn't find our program, so generate one...
+ nsecs_t time = systemTime();
+ it = mCache.emplace(needs, generateProgram(needs)).first;
+ time = systemTime() - time;
+
+ ALOGV(">>> generated new program: needs=%08X, time=%u ms (%zu programs)", needs.mKey,
+ uint32_t(ns2ms(time)), mCache.size());
+ }
+
+ // here we have a suitable program for this description
+ std::unique_ptr<Program>& program = it->second;
+ if (program->isValid()) {
+ program->use();
+ program->setUniforms(description);
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
new file mode 100644
index 0000000..d60fee6
--- /dev/null
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_PROGRAMCACHE_H
+#define SF_RENDER_ENGINE_PROGRAMCACHE_H
+
+#include <memory>
+#include <unordered_map>
+
+#include <GLES2/gl2.h>
+#include <renderengine/private/Description.h>
+#include <utils/Singleton.h>
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+class String8;
+
+namespace renderengine {
+
+struct Description;
+
+namespace gl {
+
+class Formatter;
+class Program;
+
+/*
+ * This class generates GLSL programs suitable to handle a given
+ * Description. It's responsible for figuring out what to
+ * generate from a Description.
+ * It also maintains a cache of these Programs.
+ */
+class ProgramCache : public Singleton<ProgramCache> {
+public:
+ /*
+ * Key is used to retrieve a Program in the cache.
+ * A Key is generated from a Description.
+ */
+ class Key {
+ friend class ProgramCache;
+ typedef uint32_t key_t;
+ key_t mKey;
+
+ public:
+ enum {
+ BLEND_SHIFT = 0,
+ BLEND_MASK = 1 << BLEND_SHIFT,
+ BLEND_PREMULT = 1 << BLEND_SHIFT,
+ BLEND_NORMAL = 0 << BLEND_SHIFT,
+
+ OPACITY_SHIFT = 1,
+ OPACITY_MASK = 1 << OPACITY_SHIFT,
+ OPACITY_OPAQUE = 1 << OPACITY_SHIFT,
+ OPACITY_TRANSLUCENT = 0 << OPACITY_SHIFT,
+
+ ALPHA_SHIFT = 2,
+ ALPHA_MASK = 1 << ALPHA_SHIFT,
+ ALPHA_LT_ONE = 1 << ALPHA_SHIFT,
+ ALPHA_EQ_ONE = 0 << ALPHA_SHIFT,
+
+ TEXTURE_SHIFT = 3,
+ TEXTURE_MASK = 3 << TEXTURE_SHIFT,
+ TEXTURE_OFF = 0 << TEXTURE_SHIFT,
+ TEXTURE_EXT = 1 << TEXTURE_SHIFT,
+ TEXTURE_2D = 2 << TEXTURE_SHIFT,
+
+ INPUT_TRANSFORM_MATRIX_SHIFT = 5,
+ INPUT_TRANSFORM_MATRIX_MASK = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
+ INPUT_TRANSFORM_MATRIX_OFF = 0 << INPUT_TRANSFORM_MATRIX_SHIFT,
+ INPUT_TRANSFORM_MATRIX_ON = 1 << INPUT_TRANSFORM_MATRIX_SHIFT,
+
+ OUTPUT_TRANSFORM_MATRIX_SHIFT = 6,
+ OUTPUT_TRANSFORM_MATRIX_MASK = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
+ OUTPUT_TRANSFORM_MATRIX_OFF = 0 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
+ OUTPUT_TRANSFORM_MATRIX_ON = 1 << OUTPUT_TRANSFORM_MATRIX_SHIFT,
+
+ INPUT_TF_SHIFT = 7,
+ INPUT_TF_MASK = 3 << INPUT_TF_SHIFT,
+ INPUT_TF_LINEAR = 0 << INPUT_TF_SHIFT,
+ INPUT_TF_SRGB = 1 << INPUT_TF_SHIFT,
+ INPUT_TF_ST2084 = 2 << INPUT_TF_SHIFT,
+ INPUT_TF_HLG = 3 << INPUT_TF_SHIFT,
+
+ OUTPUT_TF_SHIFT = 9,
+ OUTPUT_TF_MASK = 3 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_LINEAR = 0 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_SRGB = 1 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_ST2084 = 2 << OUTPUT_TF_SHIFT,
+ OUTPUT_TF_HLG = 3 << OUTPUT_TF_SHIFT,
+
+ Y410_BT2020_SHIFT = 11,
+ Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
+ Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
+ Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
+ };
+
+ inline Key() : mKey(0) {}
+ inline Key(const Key& rhs) : mKey(rhs.mKey) {}
+
+ inline Key& set(key_t mask, key_t value) {
+ mKey = (mKey & ~mask) | value;
+ return *this;
+ }
+
+ inline bool isTexturing() const { return (mKey & TEXTURE_MASK) != TEXTURE_OFF; }
+ inline int getTextureTarget() const { return (mKey & TEXTURE_MASK); }
+ inline bool isPremultiplied() const { return (mKey & BLEND_MASK) == BLEND_PREMULT; }
+ inline bool isOpaque() const { return (mKey & OPACITY_MASK) == OPACITY_OPAQUE; }
+ inline bool hasAlpha() const { return (mKey & ALPHA_MASK) == ALPHA_LT_ONE; }
+ inline bool hasInputTransformMatrix() const {
+ return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
+ }
+ inline bool hasOutputTransformMatrix() const {
+ return (mKey & OUTPUT_TRANSFORM_MATRIX_MASK) == OUTPUT_TRANSFORM_MATRIX_ON;
+ }
+ inline bool hasTransformMatrix() const {
+ return hasInputTransformMatrix() || hasOutputTransformMatrix();
+ }
+ inline int getInputTF() const { return (mKey & INPUT_TF_MASK); }
+ inline int getOutputTF() const { return (mKey & OUTPUT_TF_MASK); }
+
+ // When HDR and non-HDR contents are mixed, or different types of HDR contents are
+ // mixed, we will do a tone mapping process to tone map the input content to output
+ // content. Currently, the following conversions handled, they are:
+ // * SDR -> HLG
+ // * SDR -> PQ
+ // * HLG -> PQ
+ inline bool needsToneMapping() const {
+ int inputTF = getInputTF();
+ int outputTF = getOutputTF();
+
+ // Return false when converting from SDR to SDR.
+ if (inputTF == Key::INPUT_TF_SRGB && outputTF == Key::OUTPUT_TF_LINEAR) {
+ return false;
+ }
+ if (inputTF == Key::INPUT_TF_LINEAR && outputTF == Key::OUTPUT_TF_SRGB) {
+ return false;
+ }
+
+ inputTF >>= Key::INPUT_TF_SHIFT;
+ outputTF >>= Key::OUTPUT_TF_SHIFT;
+ return inputTF != outputTF;
+ }
+ inline bool isY410BT2020() const { return (mKey & Y410_BT2020_MASK) == Y410_BT2020_ON; }
+
+ // for use by std::unordered_map
+
+ bool operator==(const Key& other) const { return mKey == other.mKey; }
+
+ struct Hash {
+ size_t operator()(const Key& key) const { return static_cast<size_t>(key.mKey); }
+ };
+ };
+
+ ProgramCache() = default;
+ ~ProgramCache() = default;
+
+ // Generate shaders to populate the cache
+ void primeCache(bool useColorManagement);
+
+ size_t getSize() const { return mCache.size(); }
+
+ // useProgram lookup a suitable program in the cache or generates one
+ // if none can be found.
+ void useProgram(const Description& description);
+
+private:
+ // compute a cache Key from a Description
+ static Key computeKey(const Description& description);
+ // Generate EOTF based from Key.
+ static void generateEOTF(Formatter& fs, const Key& needs);
+ // Generate necessary tone mapping methods for OOTF.
+ static void generateToneMappingProcess(Formatter& fs, const Key& needs);
+ // Generate OOTF based from Key.
+ static void generateOOTF(Formatter& fs, const Key& needs);
+ // Generate OETF based from Key.
+ static void generateOETF(Formatter& fs, const Key& needs);
+ // generates a program from the Key
+ static std::unique_ptr<Program> generateProgram(const Key& needs);
+ // generates the vertex shader from the Key
+ static String8 generateVertexShader(const Key& needs);
+ // generates the fragment shader from the Key
+ static String8 generateFragmentShader(const Key& needs);
+
+ // Key/Value map used for caching Programs. Currently the cache
+ // is never shrunk (and the GL program objects are never deleted).
+ std::unordered_map<Key, std::unique_ptr<Program>, Key::Hash> mCache;
+};
+
+} // namespace gl
+} // namespace renderengine
+
+ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key)
+
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
new file mode 100644
index 0000000..5941cdf
--- /dev/null
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2018 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 <math/mat4.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+namespace android {
+namespace renderengine {
+
+// DisplaySettings contains the settings that are applicable when drawing all
+// layers for a given display.
+struct DisplaySettings {
+ // Rectangle describing the physical display. We will project from the
+ // logical clip onto this rectangle.
+ Rect physicalDisplay;
+
+ // Rectangle bounded by the x,y- clipping planes in the logical display, so
+ // that the orthographic projection matrix can be computed. When
+ // constructing this matrix, z-coordinate bound are assumed to be at z=0 and
+ // z=1.
+ Rect clip;
+
+ // Global transform to apply to all layers.
+ mat4 globalTransform;
+
+ // Maximum luminance pulled from the display's HDR capabilities.
+ float maxLuminence;
+
+ // Output dataspace that will be populated if wide color gamut is used, or
+ // DataSpace::UNKNOWN otherwise.
+ ui::Dataspace outputDataspace;
+
+ // Additional color transform to apply in linear space after transforming
+ // to the output dataspace.
+ mat4 colorTransform;
+
+ // Region that will be cleared to (0, 0, 0, 0) prior to rendering.
+ // clearRegion will first be transformed by globalTransform so that it will
+ // be in the same coordinate space as the rendered layers.
+ Region clearRegion;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/Framebuffer.h b/libs/renderengine/include/renderengine/Framebuffer.h
new file mode 100644
index 0000000..558b9c7
--- /dev/null
+++ b/libs/renderengine/include/renderengine/Framebuffer.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2018 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 <cstdint>
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+
+class Framebuffer {
+public:
+ virtual ~Framebuffer() = default;
+
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer) = 0;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/Image.h b/libs/renderengine/include/renderengine/Image.h
new file mode 100644
index 0000000..3bb4731
--- /dev/null
+++ b/libs/renderengine/include/renderengine/Image.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017 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
+
+struct ANativeWindowBuffer;
+
+namespace android {
+namespace renderengine {
+
+class Image {
+public:
+ virtual ~Image() = default;
+ virtual bool setNativeWindowBuffer(ANativeWindowBuffer* buffer, bool isProtected) = 0;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
new file mode 100644
index 0000000..facea21
--- /dev/null
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2018 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 <math/mat4.h>
+#include <math/vec3.h>
+#include <renderengine/Texture.h>
+#include <ui/FloatRect.h>
+#include <ui/GraphicBuffer.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+#include <ui/Transform.h>
+
+namespace android {
+namespace renderengine {
+
+// Metadata describing the input buffer to render from.
+struct Buffer {
+ // Buffer containing the image that we will render.
+ // If buffer == nullptr, then the rest of the fields in this struct will be
+ // ignored.
+ sp<GraphicBuffer> buffer;
+
+ // Texture identifier to bind the external texture to.
+ // TODO(alecmouri): This is GL-specific...make the type backend-agnostic.
+ uint32_t textureName;
+
+ // Whether to use filtering when rendering the texture.
+ bool useTextureFiltering;
+
+ // Transform matrix to apply to texture coordinates.
+ mat4 textureTransform;
+
+ // Wheteher to use pre-multiplied alpha
+ bool usePremultipliedAlpha;
+
+ // HDR color-space setting for Y410.
+ bool isY410BT2020;
+};
+
+// Metadata describing the layer geometry.
+struct Geometry {
+ // Boundaries of the layer.
+ FloatRect boundaries;
+
+ // Transform matrix to apply to mesh coordinates.
+ mat4 positionTransform;
+};
+
+// Descriptor of the source pixels for this layer.
+struct PixelSource {
+ // Source buffer
+ Buffer buffer;
+
+ // The solid color with which to fill the layer.
+ // This should only be populated if we don't render from an application
+ // buffer.
+ half3 solidColor;
+};
+
+// The settings that RenderEngine requires for correctly rendering a Layer.
+struct LayerSettings {
+ // Geometry information
+ Geometry geometry;
+
+ // Source pixels for this layer.
+ PixelSource source;
+
+ // Alpha option to apply to the source pixels
+ half alpha;
+
+ // Color space describing how the source pixels should be interpreted.
+ ui::Dataspace sourceDataspace;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
new file mode 100644
index 0000000..fe9022d
--- /dev/null
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_MESH_H
+#define SF_RENDER_ENGINE_MESH_H
+
+#include <vector>
+
+#include <stdint.h>
+
+namespace android {
+namespace renderengine {
+
+class Mesh {
+public:
+ enum Primitive {
+ TRIANGLES = 0x0004, // GL_TRIANGLES
+ TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
+ TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN
+ };
+
+ Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0);
+ ~Mesh() = default;
+
+ /*
+ * VertexArray handles the stride automatically.
+ */
+ template <typename TYPE>
+ class VertexArray {
+ friend class Mesh;
+ float* mData;
+ size_t mStride;
+ VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
+
+ public:
+ TYPE& operator[](size_t index) { return *reinterpret_cast<TYPE*>(&mData[index * mStride]); }
+ TYPE const& operator[](size_t index) const {
+ return *reinterpret_cast<TYPE const*>(&mData[index * mStride]);
+ }
+ };
+
+ template <typename TYPE>
+ VertexArray<TYPE> getPositionArray() {
+ return VertexArray<TYPE>(getPositions(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getTexCoordArray() {
+ return VertexArray<TYPE>(getTexCoords(), mStride);
+ }
+
+ Primitive getPrimitive() const;
+
+ // returns a pointer to the vertices positions
+ float const* getPositions() const;
+
+ // returns a pointer to the vertices texture coordinates
+ float const* getTexCoords() const;
+
+ // number of vertices in this mesh
+ size_t getVertexCount() const;
+
+ // dimension of vertices
+ size_t getVertexSize() const;
+
+ // dimension of texture coordinates
+ size_t getTexCoordsSize() const;
+
+ // return stride in bytes
+ size_t getByteStride() const;
+
+ // return stride in floats
+ size_t getStride() const;
+
+private:
+ Mesh(const Mesh&);
+ Mesh& operator=(const Mesh&);
+ Mesh const& operator=(const Mesh&) const;
+
+ float* getPositions();
+ float* getTexCoords();
+
+ std::vector<float> mVertices;
+ size_t mVertexCount;
+ size_t mVertexSize;
+ size_t mTexCoordsSize;
+ size_t mStride;
+ Primitive mPrimitive;
+};
+
+} // namespace renderengine
+} // namespace android
+#endif /* SF_RENDER_ENGINE_MESH_H */
diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h
new file mode 100644
index 0000000..becb3c3
--- /dev/null
+++ b/libs/renderengine/include/renderengine/RenderEngine.h
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SF_RENDERENGINE_H_
+#define SF_RENDERENGINE_H_
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <memory>
+
+#include <android-base/unique_fd.h>
+#include <math/mat4.h>
+#include <renderengine/DisplaySettings.h>
+#include <renderengine/Framebuffer.h>
+#include <renderengine/Image.h>
+#include <renderengine/LayerSettings.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Transform.h>
+
+/**
+ * Allows to set RenderEngine backend to GLES (default) or Vulkan (NOT yet supported).
+ */
+#define PROPERTY_DEBUG_RENDERENGINE_BACKEND "debug.renderengine.backend"
+
+struct ANativeWindowBuffer;
+
+namespace android {
+
+class String8;
+class Rect;
+class Region;
+
+namespace renderengine {
+
+class BindNativeBufferAsFramebuffer;
+class Image;
+class Mesh;
+class Surface;
+class Texture;
+
+namespace impl {
+class RenderEngine;
+}
+
+class RenderEngine {
+public:
+ enum FeatureFlag {
+ USE_COLOR_MANAGEMENT = 1 << 0, // Device manages color
+ USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context
+ };
+
+ static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags);
+
+ virtual ~RenderEngine() = 0;
+
+ // ----- BEGIN DEPRECATED INTERFACE -----
+ // This interface, while still in use until a suitable replacement is built,
+ // should be considered deprecated, minus some methods which still may be
+ // used to support legacy behavior.
+
+ virtual std::unique_ptr<Framebuffer> createFramebuffer() = 0;
+ virtual std::unique_ptr<Surface> createSurface() = 0;
+ virtual std::unique_ptr<Image> createImage() = 0;
+
+ virtual void primeCache() const = 0;
+
+ // dump the extension strings. always call the base class.
+ virtual void dump(String8& result) = 0;
+
+ virtual bool useNativeFenceSync() const = 0;
+ virtual bool useWaitSync() const = 0;
+
+ virtual bool isCurrent() const = 0;
+ virtual bool setCurrentSurface(const Surface& surface) = 0;
+ virtual void resetCurrentSurface() = 0;
+
+ // helpers
+ // flush submits RenderEngine command stream for execution and returns a
+ // native fence fd that is signaled when the execution has completed. It
+ // returns -1 on errors.
+ virtual base::unique_fd flush() = 0;
+ // finish waits until RenderEngine command stream has been executed. It
+ // returns false on errors.
+ virtual bool finish() = 0;
+ // waitFence inserts a wait on an external fence fd to RenderEngine
+ // command stream. It returns false on errors.
+ virtual bool waitFence(base::unique_fd fenceFd) = 0;
+
+ virtual void clearWithColor(float red, float green, float blue, float alpha) = 0;
+ virtual void fillRegionWithColor(const Region& region, float red, float green, float blue,
+ float alpha) = 0;
+
+ virtual void setScissor(const Rect& region) = 0;
+ virtual void disableScissor() = 0;
+ virtual void genTextures(size_t count, uint32_t* names) = 0;
+ virtual void deleteTextures(size_t count, uint32_t const* names) = 0;
+ virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0;
+ // When binding a native buffer, it must be done before setViewportAndProjection
+ // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation.
+ virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0;
+ virtual void unbindFrameBuffer(Framebuffer* framebuffer) = 0;
+
+ // set-up
+ virtual void checkErrors() const = 0;
+ virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+ ui::Transform::orientation_flags rotation) = 0;
+ virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color) = 0;
+ virtual void setupLayerTexturing(const Texture& texture) = 0;
+ virtual void setupLayerBlackedOut() = 0;
+ virtual void setupFillWithColor(float r, float g, float b, float a) = 0;
+
+ // Set a color transform matrix that is applied in linear space right before OETF.
+ virtual void setColorTransform(const mat4& /* colorTransform */) = 0;
+ virtual void disableTexturing() = 0;
+ virtual void disableBlending() = 0;
+
+ // HDR and color management support
+ virtual void setSourceY410BT2020(bool enable) = 0;
+ virtual void setSourceDataSpace(ui::Dataspace source) = 0;
+ virtual void setOutputDataSpace(ui::Dataspace dataspace) = 0;
+ virtual void setDisplayMaxLuminance(const float maxLuminance) = 0;
+
+ // drawing
+ virtual void drawMesh(const Mesh& mesh) = 0;
+
+ // queries
+ virtual size_t getMaxTextureSize() const = 0;
+ virtual size_t getMaxViewportDims() const = 0;
+
+ // ----- END DEPRECATED INTERFACE -----
+
+ // ----- BEGIN NEW INTERFACE -----
+
+ // Renders layers for a particular display via GPU composition. This method
+ // should be called for every display that needs to be rendered via the GPU.
+ // @param settings The display-wide settings that should be applied prior to
+ // drawing any layers.
+ // @param layers The layers to draw onto the display, in Z-order.
+ // @param buffer The buffer which will be drawn to. This buffer will be
+ // ready once displayFence fires.
+ // @param displayFence A pointer to a fence, which will fire when the buffer
+ // has been drawn to and is ready to be examined. The fence will be
+ // initialized by this method. The caller will be responsible for owning the
+ // fence.
+ // @return An error code indicating whether drawing was successful. For
+ // now, this always returns NO_ERROR.
+ // TODO(alecmouri): Consider making this a multi-display API, so that the
+ // caller deoes not need to handle multiple fences.
+ virtual status_t drawLayers(const DisplaySettings& settings,
+ const std::vector<LayerSettings>& layers,
+ ANativeWindowBuffer* const buffer,
+ base::unique_fd* displayFence) const = 0;
+
+ // TODO(alecmouri): Expose something like bindTexImage() so that devices
+ // that don't support native sync fences can get rid of code duplicated
+ // between BufferStateLayer and BufferQueueLayer for binding an external
+ // texture.
+
+ // TODO(alecmouri): Add API to help with managing a texture pool.
+};
+
+class BindNativeBufferAsFramebuffer {
+public:
+ BindNativeBufferAsFramebuffer(RenderEngine& engine, ANativeWindowBuffer* buffer)
+ : mEngine(engine), mFramebuffer(mEngine.createFramebuffer()), mStatus(NO_ERROR) {
+ mStatus = mFramebuffer->setNativeWindowBuffer(buffer)
+ ? mEngine.bindFrameBuffer(mFramebuffer.get())
+ : NO_MEMORY;
+ }
+ ~BindNativeBufferAsFramebuffer() {
+ mFramebuffer->setNativeWindowBuffer(nullptr);
+ mEngine.unbindFrameBuffer(mFramebuffer.get());
+ }
+ status_t getStatus() const { return mStatus; }
+
+private:
+ RenderEngine& mEngine;
+ std::unique_ptr<Framebuffer> mFramebuffer;
+ status_t mStatus;
+};
+
+namespace impl {
+
+// impl::RenderEngine contains common implementation that is graphics back-end agnostic.
+class RenderEngine : public renderengine::RenderEngine {
+public:
+ virtual ~RenderEngine() = 0;
+
+ bool useNativeFenceSync() const override;
+ bool useWaitSync() const override;
+
+protected:
+ RenderEngine(uint32_t featureFlags);
+ const uint32_t mFeatureFlags;
+};
+
+} // namespace impl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDERENGINE_H_ */
diff --git a/libs/renderengine/include/renderengine/Surface.h b/libs/renderengine/include/renderengine/Surface.h
new file mode 100644
index 0000000..ba7331d
--- /dev/null
+++ b/libs/renderengine/include/renderengine/Surface.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2017 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 <cstdint>
+
+struct ANativeWindow;
+
+namespace android {
+namespace renderengine {
+
+class Surface {
+public:
+ virtual ~Surface() = default;
+
+ virtual void setCritical(bool enable) = 0;
+ virtual void setAsync(bool enable) = 0;
+
+ virtual void setNativeWindow(ANativeWindow* window) = 0;
+ virtual void swapBuffers() const = 0;
+
+ virtual int32_t queryRedSize() const = 0;
+ virtual int32_t queryGreenSize() const = 0;
+ virtual int32_t queryBlueSize() const = 0;
+ virtual int32_t queryAlphaSize() const = 0;
+
+ virtual int32_t getWidth() const = 0;
+ virtual int32_t getHeight() const = 0;
+};
+
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/include/renderengine/Texture.h b/libs/renderengine/include/renderengine/Texture.h
new file mode 100644
index 0000000..c69ace0
--- /dev/null
+++ b/libs/renderengine/include/renderengine/Texture.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_TEXTURE_H
+#define SF_RENDER_ENGINE_TEXTURE_H
+
+#include <stdint.h>
+
+#include <math/mat4.h>
+
+namespace android {
+namespace renderengine {
+
+class Texture {
+public:
+ enum Target { TEXTURE_2D = 0x0DE1, TEXTURE_EXTERNAL = 0x8D65 };
+
+ Texture();
+ Texture(Target textureTarget, uint32_t textureName);
+ ~Texture();
+
+ void init(Target textureTarget, uint32_t textureName);
+
+ void setMatrix(float const* matrix);
+ void setFiltering(bool enabled);
+ void setDimensions(size_t width, size_t height);
+
+ uint32_t getTextureName() const;
+ uint32_t getTextureTarget() const;
+
+ const mat4& getMatrix() const;
+ bool getFiltering() const;
+ size_t getWidth() const;
+ size_t getHeight() const;
+
+private:
+ uint32_t mTextureName;
+ uint32_t mTextureTarget;
+ size_t mWidth;
+ size_t mHeight;
+ bool mFiltering;
+ mat4 mTextureMatrix;
+};
+
+} // namespace renderengine
+} // namespace android
+#endif /* SF_RENDER_ENGINE_TEXTURE_H */
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
new file mode 100644
index 0000000..eadd656
--- /dev/null
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2013 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.
+ */
+
+#ifndef SF_RENDER_ENGINE_DESCRIPTION_H_
+#define SF_RENDER_ENGINE_DESCRIPTION_H_
+
+#include <renderengine/Texture.h>
+#include <ui/GraphicTypes.h>
+
+namespace android {
+namespace renderengine {
+
+/*
+ * This is the structure that holds the state of the rendering engine.
+ * This class is used to generate a corresponding GLSL program and set the
+ * appropriate uniform.
+ */
+struct Description {
+ enum class TransferFunction : int {
+ LINEAR,
+ SRGB,
+ ST2084,
+ HLG, // Hybrid Log-Gamma for HDR.
+ };
+
+ static TransferFunction dataSpaceToTransferFunction(ui::Dataspace dataSpace);
+
+ Description() = default;
+ ~Description() = default;
+
+ bool hasInputTransformMatrix() const;
+ bool hasOutputTransformMatrix() const;
+ bool hasColorMatrix() const;
+
+ // whether textures are premultiplied
+ bool isPremultipliedAlpha = false;
+ // whether this layer is marked as opaque
+ bool isOpaque = true;
+
+ // Texture this layer uses
+ Texture texture;
+ bool textureEnabled = false;
+
+ // color used when texturing is disabled or when setting alpha.
+ half4 color;
+
+ // true if the sampled pixel values are in Y410/BT2020 rather than RGBA
+ bool isY410BT2020 = false;
+
+ // transfer functions for the input/output
+ TransferFunction inputTransferFunction = TransferFunction::LINEAR;
+ TransferFunction outputTransferFunction = TransferFunction::LINEAR;
+
+ float displayMaxLuminance;
+
+ // projection matrix
+ mat4 projectionMatrix;
+
+ // The color matrix will be applied in linear space right before OETF.
+ mat4 colorMatrix;
+ mat4 inputTransformMatrix;
+ mat4 outputTransformMatrix;
+};
+
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_DESCRIPTION_H_ */
diff --git a/libs/renderengine/tests/Android.bp b/libs/renderengine/tests/Android.bp
new file mode 100644
index 0000000..65b7c82
--- /dev/null
+++ b/libs/renderengine/tests/Android.bp
@@ -0,0 +1,36 @@
+// Copyright 2018 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_test {
+ name: "librenderengine_test",
+ defaults: ["surfaceflinger_defaults"],
+ test_suites: ["device-tests"],
+ srcs: [
+ "RenderEngineTest.cpp",
+ ],
+ static_libs: [
+ "libgmock",
+ "librenderengine",
+ ],
+ shared_libs: [
+ "libcutils",
+ "libEGL",
+ "libGLESv2",
+ "libgui",
+ "liblog",
+ "libnativewindow",
+ "libui",
+ "libutils",
+ ],
+}
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
new file mode 100644
index 0000000..345c7ea
--- /dev/null
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2018 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 <gtest/gtest.h>
+
+#include <renderengine/RenderEngine.h>
+#include <ui/PixelFormat.h>
+
+namespace android {
+
+class RenderEngineTest : public ::testing::Test {
+public:
+ RenderEngineTest() {
+ // Initialize with some sane defaults.
+ // TODO(alecmouri): This should probably be the same instance used by
+ // SurfaceFlinger eventually.
+ mRE = renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888),
+ 0);
+ }
+
+ status_t drawEmptyLayers() {
+ renderengine::DisplaySettings settings;
+ std::vector<renderengine::LayerSettings> layers;
+ // Meaningless buffer since we don't do any drawing
+ sp<GraphicBuffer> buffer = new GraphicBuffer();
+ base::unique_fd fence;
+ return mRE->drawLayers(settings, layers, buffer->getNativeBuffer(), &fence);
+ }
+
+private:
+ std::unique_ptr<renderengine::RenderEngine> mRE;
+};
+
+TEST_F(RenderEngineTest, drawLayers_noLayersToDraw_works) {
+ status_t result = drawEmptyLayers();
+ ASSERT_EQ(NO_ERROR, result);
+}
+
+} // namespace android