[RenderEngine] Organize RenderEngine directory.
This patch:
1. adds proper namespace renderengine to all RenderEngine code,
and namespace gl to all GLES related code
2. creates gl/ directory for GLES backend code
3. Reorder include header files, remove unused header files.
BUG: 112585051
Test: Build, flash, run display validation
Change-Id: I81f0b8831213607cde08562958f7c38ddaf4c9e6
diff --git a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
new file mode 100644
index 0000000..e0f1850
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.cpp
@@ -0,0 +1,468 @@
+/*
+ * 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 <utils/String8.h>
+#include <utils/Trace.h>
+#include "Program.h"
+#include "ProgramCache.h"
+
+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;
+
+GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags)
+ : RenderEngine(featureFlags),
+ 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 = srgb.getRGBtoXYZ();
+ mDisplayP3ToXyz = displayP3.getRGBtoXYZ();
+ mBt2020ToXyz = bt2020.getRGBtoXYZ();
+ mXyzToSrgb = mat4(srgb.getXYZtoRGB());
+ mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
+ mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
+ }
+}
+
+GLES20RenderEngine::~GLES20RenderEngine() {}
+
+size_t GLES20RenderEngine::getMaxTextureSize() const {
+ return mMaxTextureSize;
+}
+
+size_t GLES20RenderEngine::getMaxViewportDims() const {
+ return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
+}
+
+void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
+ size_t hwh, bool yswap,
+ ui::Transform::orientation_flags rotation) {
+ int32_t l = sourceCrop.left;
+ int32_t r = sourceCrop.right;
+
+ // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
+ int32_t t = hwh - sourceCrop.top;
+ int32_t b = hwh - sourceCrop.bottom;
+
+ mat4 m;
+ if (yswap) {
+ m = mat4::ortho(l, r, t, b, 0, 1);
+ } else {
+ 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.setProjectionMatrix(m);
+ mVpWidth = vpw;
+ mVpHeight = vph;
+}
+
+void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque,
+ bool disableTexture, const half4& color) {
+ mState.setPremultipliedAlpha(premultipliedAlpha);
+ mState.setOpaque(opaque);
+ mState.setColor(color);
+
+ if (disableTexture) {
+ mState.disableTexture();
+ }
+
+ 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.setY410BT2020(enable);
+}
+
+void GLES20RenderEngine::setSourceDataSpace(Dataspace source) {
+ mDataSpace = source;
+}
+
+void GLES20RenderEngine::setOutputDataSpace(Dataspace dataspace) {
+ mOutputDataSpace = dataspace;
+}
+
+void GLES20RenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
+ mState.setDisplayMaxLuminance(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.setTexture(texture);
+}
+
+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.setTexture(texture);
+}
+
+void GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) {
+ mState.setColorMatrix(colorTransform);
+}
+
+void GLES20RenderEngine::disableTexturing() {
+ mState.disableTexture();
+}
+
+void GLES20RenderEngine::disableBlending() {
+ glDisable(GL_BLEND);
+}
+
+void GLES20RenderEngine::bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName,
+ uint32_t* fbName, uint32_t* status) {
+ GLuint tname, name;
+ // turn our EGLImage into a texture
+ glGenTextures(1, &tname);
+ glBindTexture(GL_TEXTURE_2D, tname);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
+
+ // create a Framebuffer Object to render into
+ glGenFramebuffers(1, &name);
+ glBindFramebuffer(GL_FRAMEBUFFER, name);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tname, 0);
+
+ *status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ *texName = tname;
+ *fbName = name;
+}
+
+void GLES20RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ glDeleteFramebuffers(1, &fbName);
+ glDeleteTextures(1, &texName);
+}
+
+void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
+ mState.setPremultipliedAlpha(true);
+ mState.setOpaque(false);
+ mState.setColor(half4(r, g, b, a));
+ mState.disableTexture();
+ 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.setInputTransformMatrix(mDisplayP3ToXyz);
+ break;
+ case Dataspace::STANDARD_BT2020:
+ managedState.setInputTransformMatrix(mBt2020ToXyz);
+ break;
+ default:
+ managedState.setInputTransformMatrix(mSrgbToXyz);
+ break;
+ }
+
+ // The supported output color spaces are BT2020, Display P3 and standard RGB.
+ switch (outputStandard) {
+ case Dataspace::STANDARD_BT2020:
+ managedState.setOutputTransformMatrix(mXyzToBt2020);
+ break;
+ case Dataspace::STANDARD_DCI_P3:
+ managedState.setOutputTransformMatrix(mXyzToDisplayP3);
+ break;
+ default:
+ managedState.setOutputTransformMatrix(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.setOutputTransformMatrix(mDisplayP3ToSrgb);
+ } else if (outputStandard == Dataspace::STANDARD_DCI_P3) {
+ managedState.setOutputTransformMatrix(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) {
+ switch (inputTransfer) {
+ case Dataspace::TRANSFER_ST2084:
+ managedState.setInputTransferFunction(Description::TransferFunction::ST2084);
+ break;
+ case Dataspace::TRANSFER_HLG:
+ managedState.setInputTransferFunction(Description::TransferFunction::HLG);
+ break;
+ case Dataspace::TRANSFER_LINEAR:
+ managedState.setInputTransferFunction(Description::TransferFunction::LINEAR);
+ break;
+ default:
+ managedState.setInputTransferFunction(Description::TransferFunction::SRGB);
+ break;
+ }
+
+ switch (outputTransfer) {
+ case Dataspace::TRANSFER_ST2084:
+ managedState.setOutputTransferFunction(Description::TransferFunction::ST2084);
+ break;
+ case Dataspace::TRANSFER_HLG:
+ managedState.setOutputTransferFunction(Description::TransferFunction::HLG);
+ break;
+ default:
+ managedState.setOutputTransferFunction(Description::TransferFunction::SRGB);
+ break;
+ }
+ }
+
+ 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);
+ }
+}
+
+void GLES20RenderEngine::dump(String8& result) {
+ RenderEngine::dump(result);
+ 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());
+}
+
+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;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#if defined(__gl_h_)
+#error "don't include gl/gl.h in this file"
+#endif
diff --git a/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
new file mode 100644
index 0000000..c830184
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/GLES20RenderEngine.h
@@ -0,0 +1,117 @@
+/*
+ * 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 <GLES2/gl2.h>
+#include <renderengine/RenderEngine.h>
+#include <renderengine/private/Description.h>
+
+namespace android {
+
+class String8;
+
+namespace renderengine {
+
+class Mesh;
+class Texture;
+
+namespace gl {
+
+class GLES20RenderEngine : public impl::RenderEngine {
+ GLuint mProtectedTexName;
+ GLint mMaxViewportDims[2];
+ GLint mMaxTextureSize;
+ GLuint mVpWidth;
+ GLuint mVpHeight;
+
+ struct Group {
+ GLuint texture;
+ GLuint fbo;
+ GLuint width;
+ GLuint height;
+ mat4 colorTransform;
+ };
+
+ Description mState;
+
+ virtual void bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName, uint32_t* fbName,
+ uint32_t* status);
+ virtual void unbindFramebuffer(uint32_t texName, uint32_t fbName);
+
+public:
+ GLES20RenderEngine(uint32_t featureFlags); // See RenderEngine::FeatureFlag
+ virtual ~GLES20RenderEngine();
+
+protected:
+ virtual void dump(String8& result);
+ virtual void setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop, size_t hwh,
+ bool yswap, ui::Transform::orientation_flags rotation);
+ virtual void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
+ const half4& color) override;
+
+ // 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;
+
+ virtual void setupLayerTexturing(const Texture& texture);
+ virtual void setupLayerBlackedOut();
+ virtual void setupFillWithColor(float r, float g, float b, float a);
+ virtual void setupColorTransform(const mat4& colorTransform);
+ virtual void disableTexturing();
+ virtual void disableBlending();
+
+ virtual void drawMesh(const Mesh& mesh);
+
+ virtual size_t getMaxTextureSize() const;
+ virtual size_t getMaxViewportDims() const;
+
+ // 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;
+ mat4 mSrgbToDisplayP3;
+ mat4 mDisplayP3ToSrgb;
+ mat3 mSrgbToXyz;
+ mat3 mBt2020ToXyz;
+ mat3 mDisplayP3ToXyz;
+ mat4 mXyzToSrgb;
+ mat4 mXyzToDisplayP3;
+ mat4 mXyzToBt2020;
+
+private:
+ // 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;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif /* SF_GLES20RENDERENGINE_H_ */
diff --git a/services/surfaceflinger/RenderEngine/gl/GLExtensions.cpp b/services/surfaceflinger/RenderEngine/gl/GLExtensions.cpp
new file mode 100644
index 0000000..6f50ea7
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/GLExtensions.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+ANDROID_SINGLETON_STATIC_INSTANCE(android::renderengine::gl::GLExtensions)
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+SortedVector<String8> GLExtensions::parseExtensionString(char const* extensions) {
+ SortedVector<String8> list;
+
+ char const* curr = extensions;
+ char const* head = curr;
+ do {
+ head = strchr(curr, ' ');
+ String8 s(curr, head ? head - curr : strlen(curr));
+ if (s.length()) {
+ list.add(s);
+ }
+ curr = head + 1;
+ } while (head);
+
+ return list;
+}
+
+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;
+ mExtensionList = parseExtensionString(mExtensions);
+}
+
+bool GLExtensions::hasExtension(char const* extension) const {
+ const String8 s(extension);
+ return mExtensionList.indexOf(s) >= 0;
+}
+
+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;
+ mEGLExtensionList = parseExtensionString(mEGLExtensions);
+
+ // 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 (hasEGLExtension("EGL_ANDROIDX_no_config_context") ||
+ hasEGLExtension("EGL_KHR_no_config_context")) {
+ mHasNoConfigContext = true;
+ }
+
+ if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+ mHasNativeFenceSync = true;
+ }
+ if (hasEGLExtension("EGL_KHR_fence_sync")) {
+ mHasFenceSync = true;
+ }
+ if (hasEGLExtension("EGL_KHR_wait_sync")) {
+ mHasWaitSync = true;
+ }
+ if (hasEGLExtension("EGL_EXT_protected_content")) {
+ mHasProtectedContent = true;
+ }
+ if (hasEGLExtension("EGL_IMG_context_priority")) {
+ mHasContextPriority = true;
+ }
+}
+
+char const* GLExtensions::getEGLVersion() const {
+ return mEGLVersion.string();
+}
+
+char const* GLExtensions::getEGLExtensions() const {
+ return mEGLExtensions.string();
+}
+
+bool GLExtensions::hasEGLExtension(char const* extension) const {
+ const String8 s(extension);
+ return mEGLExtensionList.indexOf(s) >= 0;
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/gl/GLExtensions.h b/services/surfaceflinger/RenderEngine/gl/GLExtensions.h
new file mode 100644
index 0000000..efdd8b7
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/GLExtensions.h
@@ -0,0 +1,89 @@
+/*
+ * 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/SortedVector.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;
+ SortedVector<String8> mExtensionList;
+
+ String8 mEGLVersion;
+ String8 mEGLExtensions;
+ SortedVector<String8> mEGLExtensionList;
+
+ static SortedVector<String8> parseExtensionString(char const* extensions);
+
+ 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;
+ bool hasExtension(char const* extension) const;
+
+ void initWithEGLStrings(char const* eglVersion, char const* eglExtensions);
+ char const* getEGLVersion() const;
+ char const* getEGLExtensions() const;
+ bool hasEGLExtension(char const* extension) const;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
+
+#endif // ANDROID_SF_GLEXTENSION_H
diff --git a/services/surfaceflinger/RenderEngine/gl/Program.cpp b/services/surfaceflinger/RenderEngine/gl/Program.cpp
new file mode 100644
index 0000000..4d6839b
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/Program.cpp
@@ -0,0 +1,159 @@
+/*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 <renderengine/private/Description.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);
+ }
+}
+
+Program::~Program() {}
+
+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;
+}
+
+String8& Program::dumpShader(String8& result, GLenum /*type*/) {
+ GLuint shader = GL_FRAGMENT_SHADER ? mFragmentShader : mVertexShader;
+ GLint l;
+ glGetShaderiv(shader, GL_SHADER_SOURCE_LENGTH, &l);
+ char* src = new char[l];
+ glGetShaderSource(shader, l, nullptr, src);
+ result.append(src);
+ delete[] src;
+ return result;
+}
+
+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.mTexture.getMatrix().asArray());
+ }
+ if (mColorLoc >= 0) {
+ const float color[4] = {desc.mColor.r, desc.mColor.g, desc.mColor.b, desc.mColor.a};
+ glUniform4fv(mColorLoc, 1, color);
+ }
+ if (mInputTransformMatrixLoc >= 0) {
+ mat4 inputTransformMatrix = mat4(desc.mInputTransformMatrix);
+ 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.mColorMatrix * desc.mOutputTransformMatrix;
+ glUniformMatrix4fv(mOutputTransformMatrixLoc, 1, GL_FALSE,
+ outputTransformMatrix.asArray());
+ }
+ if (mDisplayMaxLuminanceLoc >= 0) {
+ glUniform1f(mDisplayMaxLuminanceLoc, desc.mDisplayMaxLuminance);
+ }
+ // these uniforms are always present
+ glUniformMatrix4fv(mProjectionMatrixLoc, 1, GL_FALSE, desc.mProjectionMatrix.asArray());
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/gl/Program.h b/services/surfaceflinger/RenderEngine/gl/Program.h
new file mode 100644
index 0000000..bb429ef
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/Program.h
@@ -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.
+ */
+
+#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();
+
+ /* 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);
+ String8& dumpShader(String8& result, 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/services/surfaceflinger/RenderEngine/gl/ProgramCache.cpp b/services/surfaceflinger/RenderEngine/gl/ProgramCache.cpp
new file mode 100644
index 0000000..a19c1f1
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/ProgramCache.cpp
@@ -0,0 +1,689 @@
+/*
+ * 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 <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;
+}
+
+ProgramCache::ProgramCache() {}
+
+ProgramCache::~ProgramCache() {}
+
+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;
+ }
+ Program* program = mCache.valueFor(shaderKey);
+ if (program == nullptr) {
+ program = generateProgram(shaderKey);
+ mCache.add(shaderKey, program);
+ 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);
+ Program* program = mCache.valueFor(shaderKey);
+ if (program == nullptr) {
+ program = generateProgram(shaderKey);
+ mCache.add(shaderKey, program);
+ 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.mTextureEnabled
+ ? Key::TEXTURE_OFF
+ : description.mTexture.getTextureTarget() == GL_TEXTURE_EXTERNAL_OES
+ ? Key::TEXTURE_EXT
+ : description.mTexture.getTextureTarget() == GL_TEXTURE_2D
+ ? Key::TEXTURE_2D
+ : Key::TEXTURE_OFF)
+ .set(Key::ALPHA_MASK,
+ (description.mColor.a < 1) ? Key::ALPHA_LT_ONE : Key::ALPHA_EQ_ONE)
+ .set(Key::BLEND_MASK,
+ description.mPremultipliedAlpha ? Key::BLEND_PREMULT : Key::BLEND_NORMAL)
+ .set(Key::OPACITY_MASK,
+ description.mOpaque ? 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.mY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
+
+ if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
+ switch (description.mInputTransferFunction) {
+ 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.mOutputTransferFunction) {
+ 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) {
+ nits *= 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;
+ nits *= 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;
+ }
+ }
+
+ return color * (nits / max(1e-6, 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;
+ nits *= 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;
+ }
+
+ return color * (nits / max(1e-6, 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 vec3(inputTransformMatrix * vec4(color, 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();
+}
+
+Program* ProgramCache::generateProgram(const Key& needs) {
+ ATRACE_CALL();
+
+ // vertex shader
+ String8 vs = generateVertexShader(needs);
+
+ // fragment shader
+ String8 fs = generateFragmentShader(needs);
+
+ Program* program = new Program(needs, vs.string(), fs.string());
+ return program;
+}
+
+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
+ Program* program = mCache.valueFor(needs);
+ if (program == nullptr) {
+ // we didn't find our program, so generate one...
+ nsecs_t time = -systemTime();
+ program = generateProgram(needs);
+ mCache.add(needs, program);
+ time += systemTime();
+
+ 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
+ if (program->isValid()) {
+ program->use();
+ program->setUniforms(description);
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/services/surfaceflinger/RenderEngine/gl/ProgramCache.h b/services/surfaceflinger/RenderEngine/gl/ProgramCache.h
new file mode 100644
index 0000000..ea77a2d
--- /dev/null
+++ b/services/surfaceflinger/RenderEngine/gl/ProgramCache.h
@@ -0,0 +1,204 @@
+/*
+ * 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 <GLES2/gl2.h>
+#include <renderengine/private/Description.h>
+#include <utils/KeyedVector.h>
+#include <utils/Singleton.h>
+#include <utils/TypeHelpers.h>
+
+namespace android {
+
+class String8;
+
+namespace renderengine {
+
+class 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; }
+
+ // this is the definition of a friend function -- not a method of class Needs
+ friend inline int strictly_order_type(const Key& lhs, const Key& rhs) {
+ return (lhs.mKey < rhs.mKey) ? 1 : 0;
+ }
+ };
+
+ ProgramCache();
+ ~ProgramCache();
+
+ // Generate shaders to populate the cache
+ void primeCache(bool useColorManagement);
+
+ // 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 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.
+ DefaultKeyedVector<Key, Program*> mCache;
+};
+
+} // namespace gl
+} // namespace renderengine
+
+ANDROID_BASIC_TYPES_TRAITS(renderengine::gl::ProgramCache::Key)
+
+} // namespace android
+
+#endif /* SF_RENDER_ENGINE_PROGRAMCACHE_H */