1 /*
2  * Copyright 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //#define LOG_NDEBUG 0
18 #undef LOG_TAG
19 #define LOG_TAG "RenderEngine"
20 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
21 
22 #include <GLES2/gl2.h>
23 #include <GLES2/gl2ext.h>
24 
25 #include <ui/ColorSpace.h>
26 #include <ui/DebugUtils.h>
27 #include <ui/Rect.h>
28 
29 #include <utils/String8.h>
30 #include <utils/Trace.h>
31 
32 #include <cutils/compiler.h>
33 #include <gui/ISurfaceComposer.h>
34 #include <math.h>
35 
36 #include "Description.h"
37 #include "GLES20RenderEngine.h"
38 #include "Mesh.h"
39 #include "Program.h"
40 #include "ProgramCache.h"
41 #include "Texture.h"
42 
43 #include <fstream>
44 #include <sstream>
45 
46 // ---------------------------------------------------------------------------
checkGlError(const char * op,int lineNumber)47 bool checkGlError(const char* op, int lineNumber) {
48     bool errorFound = false;
49     GLint error = glGetError();
50     while (error != GL_NO_ERROR) {
51         errorFound = true;
52         error = glGetError();
53         ALOGV("after %s() (line # %d) glError (0x%x)\n", op, lineNumber, error);
54     }
55     return errorFound;
56 }
57 
58 static constexpr bool outputDebugPPMs = false;
59 
writePPM(const char * basename,GLuint width,GLuint height)60 void writePPM(const char* basename, GLuint width, GLuint height) {
61     ALOGV("writePPM #%s: %d x %d", basename, width, height);
62 
63     std::vector<GLubyte> pixels(width * height * 4);
64     std::vector<GLubyte> outBuffer(width * height * 3);
65 
66     // TODO(courtneygo): We can now have float formats, need
67     // to remove this code or update to support.
68     // Make returned pixels fit in uint32_t, one byte per component
69     glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
70     if (checkGlError(__FUNCTION__, __LINE__)) {
71         return;
72     }
73 
74     std::string filename(basename);
75     filename.append(".ppm");
76     std::ofstream file(filename.c_str(), std::ios::binary);
77     if (!file.is_open()) {
78         ALOGE("Unable to open file: %s", filename.c_str());
79         ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
80               "surfaceflinger to write debug images");
81         return;
82     }
83 
84     file << "P6\n";
85     file << width << "\n";
86     file << height << "\n";
87     file << 255 << "\n";
88 
89     auto ptr = reinterpret_cast<char*>(pixels.data());
90     auto outPtr = reinterpret_cast<char*>(outBuffer.data());
91     for (int y = height - 1; y >= 0; y--) {
92         char* data = ptr + y * width * sizeof(uint32_t);
93 
94         for (GLuint x = 0; x < width; x++) {
95             // Only copy R, G and B components
96             outPtr[0] = data[0];
97             outPtr[1] = data[1];
98             outPtr[2] = data[2];
99             data += sizeof(uint32_t);
100             outPtr += 3;
101         }
102     }
103     file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
104 }
105 
106 // ---------------------------------------------------------------------------
107 namespace android {
108 namespace RE {
109 namespace impl {
110 // ---------------------------------------------------------------------------
111 
112 using ui::Dataspace;
113 
GLES20RenderEngine(uint32_t featureFlags)114 GLES20RenderEngine::GLES20RenderEngine(uint32_t featureFlags)
115       : RenderEngine(featureFlags),
116         mVpWidth(0),
117         mVpHeight(0),
118         mPlatformHasWideColor((featureFlags & WIDE_COLOR_SUPPORT) != 0) {
119     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
120     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
121 
122     glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
123     glPixelStorei(GL_PACK_ALIGNMENT, 4);
124 
125     const uint16_t protTexData[] = {0};
126     glGenTextures(1, &mProtectedTexName);
127     glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
128     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
129     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
130     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
131     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
132     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
133 
134     // mColorBlindnessCorrection = M;
135 
136     if (mPlatformHasWideColor) {
137         ColorSpace srgb(ColorSpace::sRGB());
138         ColorSpace displayP3(ColorSpace::DisplayP3());
139         ColorSpace bt2020(ColorSpace::BT2020());
140 
141         // Compute sRGB to Display P3 transform matrix.
142         // NOTE: For now, we are limiting output wide color space support to
143         // Display-P3 only.
144         mSrgbToDisplayP3 = mat4(ColorSpaceConnector(srgb, displayP3).getTransform());
145 
146         // Compute Display P3 to sRGB transform matrix.
147         mDisplayP3ToSrgb = mat4(ColorSpaceConnector(displayP3, srgb).getTransform());
148 
149         // no chromatic adaptation needed since all color spaces use D65 for their white points.
150         mSrgbToXyz = srgb.getRGBtoXYZ();
151         mDisplayP3ToXyz = displayP3.getRGBtoXYZ();
152         mBt2020ToXyz = bt2020.getRGBtoXYZ();
153         mXyzToSrgb = mat4(srgb.getXYZtoRGB());
154         mXyzToDisplayP3 = mat4(displayP3.getXYZtoRGB());
155         mXyzToBt2020 = mat4(bt2020.getXYZtoRGB());
156     }
157 }
158 
~GLES20RenderEngine()159 GLES20RenderEngine::~GLES20RenderEngine() {}
160 
getMaxTextureSize() const161 size_t GLES20RenderEngine::getMaxTextureSize() const {
162     return mMaxTextureSize;
163 }
164 
getMaxViewportDims() const165 size_t GLES20RenderEngine::getMaxViewportDims() const {
166     return mMaxViewportDims[0] < mMaxViewportDims[1] ? mMaxViewportDims[0] : mMaxViewportDims[1];
167 }
168 
setViewportAndProjection(size_t vpw,size_t vph,Rect sourceCrop,size_t hwh,bool yswap,Transform::orientation_flags rotation)169 void GLES20RenderEngine::setViewportAndProjection(size_t vpw, size_t vph, Rect sourceCrop,
170                                                   size_t hwh, bool yswap,
171                                                   Transform::orientation_flags rotation) {
172     int32_t l = sourceCrop.left;
173     int32_t r = sourceCrop.right;
174 
175     // In GL, (0, 0) is the bottom-left corner, so flip y coordinates
176     int32_t t = hwh - sourceCrop.top;
177     int32_t b = hwh - sourceCrop.bottom;
178 
179     mat4 m;
180     if (yswap) {
181         m = mat4::ortho(l, r, t, b, 0, 1);
182     } else {
183         m = mat4::ortho(l, r, b, t, 0, 1);
184     }
185 
186     // Apply custom rotation to the projection.
187     float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
188     switch (rotation) {
189         case Transform::ROT_0:
190             break;
191         case Transform::ROT_90:
192             m = mat4::rotate(rot90InRadians, vec3(0, 0, 1)) * m;
193             break;
194         case Transform::ROT_180:
195             m = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1)) * m;
196             break;
197         case Transform::ROT_270:
198             m = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1)) * m;
199             break;
200         default:
201             break;
202     }
203 
204     glViewport(0, 0, vpw, vph);
205     mState.setProjectionMatrix(m);
206     mVpWidth = vpw;
207     mVpHeight = vph;
208 }
209 
setupLayerBlending(bool premultipliedAlpha,bool opaque,bool disableTexture,const half4 & color)210 void GLES20RenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque,
211                                             bool disableTexture, const half4& color) {
212     mState.setPremultipliedAlpha(premultipliedAlpha);
213     mState.setOpaque(opaque);
214     mState.setColor(color);
215 
216     if (disableTexture) {
217         mState.disableTexture();
218     }
219 
220     if (color.a < 1.0f || !opaque) {
221         glEnable(GL_BLEND);
222         glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
223     } else {
224         glDisable(GL_BLEND);
225     }
226 }
227 
setSourceY410BT2020(bool enable)228 void GLES20RenderEngine::setSourceY410BT2020(bool enable) {
229     mState.setY410BT2020(enable);
230 }
231 
setSourceDataSpace(Dataspace source)232 void GLES20RenderEngine::setSourceDataSpace(Dataspace source) {
233     mDataSpace = source;
234 }
235 
setOutputDataSpace(Dataspace dataspace)236 void GLES20RenderEngine::setOutputDataSpace(Dataspace dataspace) {
237     mOutputDataSpace = dataspace;
238 }
239 
setDisplayMaxLuminance(const float maxLuminance)240 void GLES20RenderEngine::setDisplayMaxLuminance(const float maxLuminance) {
241     mState.setDisplayMaxLuminance(maxLuminance);
242 }
243 
setupLayerTexturing(const Texture & texture)244 void GLES20RenderEngine::setupLayerTexturing(const Texture& texture) {
245     GLuint target = texture.getTextureTarget();
246     glBindTexture(target, texture.getTextureName());
247     GLenum filter = GL_NEAREST;
248     if (texture.getFiltering()) {
249         filter = GL_LINEAR;
250     }
251     glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
252     glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
253     glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
254     glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
255 
256     mState.setTexture(texture);
257 }
258 
setupLayerBlackedOut()259 void GLES20RenderEngine::setupLayerBlackedOut() {
260     glBindTexture(GL_TEXTURE_2D, mProtectedTexName);
261     Texture texture(Texture::TEXTURE_2D, mProtectedTexName);
262     texture.setDimensions(1, 1); // FIXME: we should get that from somewhere
263     mState.setTexture(texture);
264 }
265 
setupColorTransform(const mat4 & colorTransform)266 void GLES20RenderEngine::setupColorTransform(const mat4& colorTransform) {
267     mState.setColorMatrix(colorTransform);
268 }
269 
setSaturationMatrix(const mat4 & saturationMatrix)270 void GLES20RenderEngine::setSaturationMatrix(const mat4& saturationMatrix) {
271     mState.setSaturationMatrix(saturationMatrix);
272 }
273 
disableTexturing()274 void GLES20RenderEngine::disableTexturing() {
275     mState.disableTexture();
276 }
277 
disableBlending()278 void GLES20RenderEngine::disableBlending() {
279     glDisable(GL_BLEND);
280 }
281 
bindImageAsFramebuffer(EGLImageKHR image,uint32_t * texName,uint32_t * fbName,uint32_t * status)282 void GLES20RenderEngine::bindImageAsFramebuffer(EGLImageKHR image, uint32_t* texName,
283                                                 uint32_t* fbName, uint32_t* status) {
284     GLuint tname, name;
285     // turn our EGLImage into a texture
286     glGenTextures(1, &tname);
287     glBindTexture(GL_TEXTURE_2D, tname);
288     glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image);
289 
290     // create a Framebuffer Object to render into
291     glGenFramebuffers(1, &name);
292     glBindFramebuffer(GL_FRAMEBUFFER, name);
293     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tname, 0);
294 
295     *status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
296     *texName = tname;
297     *fbName = name;
298 }
299 
unbindFramebuffer(uint32_t texName,uint32_t fbName)300 void GLES20RenderEngine::unbindFramebuffer(uint32_t texName, uint32_t fbName) {
301     glBindFramebuffer(GL_FRAMEBUFFER, 0);
302     glDeleteFramebuffers(1, &fbName);
303     glDeleteTextures(1, &texName);
304 }
305 
setupFillWithColor(float r,float g,float b,float a)306 void GLES20RenderEngine::setupFillWithColor(float r, float g, float b, float a) {
307     mState.setPremultipliedAlpha(true);
308     mState.setOpaque(false);
309     mState.setColor(half4(r, g, b, a));
310     mState.disableTexture();
311     glDisable(GL_BLEND);
312 }
313 
drawMesh(const Mesh & mesh)314 void GLES20RenderEngine::drawMesh(const Mesh& mesh) {
315     ATRACE_CALL();
316     if (mesh.getTexCoordsSize()) {
317         glEnableVertexAttribArray(Program::texCoords);
318         glVertexAttribPointer(Program::texCoords, mesh.getTexCoordsSize(), GL_FLOAT, GL_FALSE,
319                               mesh.getByteStride(), mesh.getTexCoords());
320     }
321 
322     glVertexAttribPointer(Program::position, mesh.getVertexSize(), GL_FLOAT, GL_FALSE,
323                           mesh.getByteStride(), mesh.getPositions());
324 
325     // By default, DISPLAY_P3 is the only supported wide color output. However,
326     // when HDR content is present, hardware composer may be able to handle
327     // BT2020 data space, in that case, the output data space is set to be
328     // BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
329     // to respect this and convert non-HDR content to HDR format.
330     if (mPlatformHasWideColor) {
331         Description wideColorState = mState;
332         Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
333         Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
334         Dataspace outputStandard = static_cast<Dataspace>(mOutputDataSpace &
335                                                           Dataspace::STANDARD_MASK);
336         Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace &
337                                                           Dataspace::TRANSFER_MASK);
338         bool needsXYZConversion = needsXYZTransformMatrix();
339 
340         if (needsXYZConversion) {
341             // The supported input color spaces are standard RGB, Display P3 and BT2020.
342             switch (inputStandard) {
343                 case Dataspace::STANDARD_DCI_P3:
344                     wideColorState.setInputTransformMatrix(mDisplayP3ToXyz);
345                     break;
346                 case Dataspace::STANDARD_BT2020:
347                     wideColorState.setInputTransformMatrix(mBt2020ToXyz);
348                     break;
349                 default:
350                     wideColorState.setInputTransformMatrix(mSrgbToXyz);
351                     break;
352             }
353 
354             // The supported output color spaces are BT2020, Display P3 and standard RGB.
355             switch (outputStandard) {
356                 case Dataspace::STANDARD_BT2020:
357                     wideColorState.setOutputTransformMatrix(mXyzToBt2020);
358                     break;
359                 case Dataspace::STANDARD_DCI_P3:
360                     wideColorState.setOutputTransformMatrix(mXyzToDisplayP3);
361                     break;
362                 default:
363                     wideColorState.setOutputTransformMatrix(mXyzToSrgb);
364                     break;
365             }
366         } else if (inputStandard != outputStandard) {
367             // At this point, the input data space and output data space could be both
368             // HDR data spaces, but they match each other, we do nothing in this case.
369             // In addition to the case above, the input data space could be
370             // - scRGB linear
371             // - scRGB non-linear
372             // - sRGB
373             // - Display P3
374             // The output data spaces could be
375             // - sRGB
376             // - Display P3
377             if (outputStandard == Dataspace::STANDARD_BT709) {
378                 wideColorState.setOutputTransformMatrix(mDisplayP3ToSrgb);
379             } else if (outputStandard == Dataspace::STANDARD_DCI_P3) {
380                 wideColorState.setOutputTransformMatrix(mSrgbToDisplayP3);
381             }
382         }
383 
384         // we need to convert the RGB value to linear space and convert it back when:
385         // - there is a color matrix that is not an identity matrix, or
386         // - there is a saturation matrix that is not an identity matrix, or
387         // - there is an output transform matrix that is not an identity matrix, or
388         // - the input transfer function doesn't match the output transfer function.
389         if (wideColorState.hasColorMatrix() || wideColorState.hasSaturationMatrix() ||
390             wideColorState.hasOutputTransformMatrix() || inputTransfer != outputTransfer) {
391             switch (inputTransfer) {
392                 case Dataspace::TRANSFER_ST2084:
393                     wideColorState.setInputTransferFunction(Description::TransferFunction::ST2084);
394                     break;
395                 case Dataspace::TRANSFER_HLG:
396                     wideColorState.setInputTransferFunction(Description::TransferFunction::HLG);
397                     break;
398                 case Dataspace::TRANSFER_LINEAR:
399                     wideColorState.setInputTransferFunction(Description::TransferFunction::LINEAR);
400                     break;
401                 default:
402                     wideColorState.setInputTransferFunction(Description::TransferFunction::SRGB);
403                     break;
404             }
405 
406             switch (outputTransfer) {
407                 case Dataspace::TRANSFER_ST2084:
408                     wideColorState.setOutputTransferFunction(Description::TransferFunction::ST2084);
409                     break;
410                 case Dataspace::TRANSFER_HLG:
411                     wideColorState.setOutputTransferFunction(Description::TransferFunction::HLG);
412                     break;
413                 default:
414                     wideColorState.setOutputTransferFunction(Description::TransferFunction::SRGB);
415                     break;
416             }
417         }
418 
419         ProgramCache::getInstance().useProgram(wideColorState);
420 
421         glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
422 
423         if (outputDebugPPMs) {
424             static uint64_t wideColorFrameCount = 0;
425             std::ostringstream out;
426             out << "/data/texture_out" << wideColorFrameCount++;
427             writePPM(out.str().c_str(), mVpWidth, mVpHeight);
428         }
429     } else {
430         ProgramCache::getInstance().useProgram(mState);
431 
432         glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
433     }
434 
435     if (mesh.getTexCoordsSize()) {
436         glDisableVertexAttribArray(Program::texCoords);
437     }
438 }
439 
dump(String8 & result)440 void GLES20RenderEngine::dump(String8& result) {
441     RenderEngine::dump(result);
442     result.appendFormat("RenderEngine last dataspace conversion: (%s) to (%s)\n",
443                         dataspaceDetails(static_cast<android_dataspace>(mDataSpace)).c_str(),
444                         dataspaceDetails(static_cast<android_dataspace>(mOutputDataSpace)).c_str());
445 }
446 
isHdrDataSpace(const Dataspace dataSpace) const447 bool GLES20RenderEngine::isHdrDataSpace(const Dataspace dataSpace) const {
448     const Dataspace standard = static_cast<Dataspace>(dataSpace & Dataspace::STANDARD_MASK);
449     const Dataspace transfer = static_cast<Dataspace>(dataSpace & Dataspace::TRANSFER_MASK);
450     return standard == Dataspace::STANDARD_BT2020 &&
451         (transfer == Dataspace::TRANSFER_ST2084 || transfer == Dataspace::TRANSFER_HLG);
452 }
453 
454 // For convenience, we want to convert the input color space to XYZ color space first,
455 // and then convert from XYZ color space to output color space when
456 // - SDR and HDR contents are mixed, either SDR content will be converted to HDR or
457 //   HDR content will be tone-mapped to SDR; Or,
458 // - there are HDR PQ and HLG contents presented at the same time, where we want to convert
459 //   HLG content to PQ content.
460 // In either case above, we need to operate the Y value in XYZ color space. Thus, when either
461 // input data space or output data space is HDR data space, and the input transfer function
462 // doesn't match the output transfer function, we would enable an intermediate transfrom to
463 // XYZ color space.
needsXYZTransformMatrix() const464 bool GLES20RenderEngine::needsXYZTransformMatrix() const {
465     const bool isInputHdrDataSpace = isHdrDataSpace(mDataSpace);
466     const bool isOutputHdrDataSpace = isHdrDataSpace(mOutputDataSpace);
467     const Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
468     const Dataspace outputTransfer = static_cast<Dataspace>(mOutputDataSpace &
469                                                             Dataspace::TRANSFER_MASK);
470 
471     return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer;
472 }
473 
474 // ---------------------------------------------------------------------------
475 } // namespace impl
476 } // namespace RE
477 } // namespace android
478 // ---------------------------------------------------------------------------
479 
480 #if defined(__gl_h_)
481 #error "don't include gl/gl.h in this file"
482 #endif
483