1 /* 2 * Copyright (C) 2016 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 #include "OpenGLReadback.h" 18 19 #include "Caches.h" 20 #include "GlLayer.h" 21 #include "GlopBuilder.h" 22 #include "Image.h" 23 #include "renderstate/RenderState.h" 24 #include "renderthread/EglManager.h" 25 #include "utils/GLUtils.h" 26 27 #include <GLES2/gl2.h> 28 #include <gui/Surface.h> 29 #include <ui/Fence.h> 30 #include <ui/GraphicBuffer.h> 31 32 namespace android { 33 namespace uirenderer { 34 35 CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect, 36 SkBitmap* bitmap) { 37 ATRACE_CALL(); 38 // Setup the source 39 sp<GraphicBuffer> sourceBuffer; 40 sp<Fence> sourceFence; 41 Matrix4 texTransform; 42 status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data); 43 texTransform.invalidateType(); 44 if (err != NO_ERROR) { 45 ALOGW("Failed to get last queued buffer, error = %d", err); 46 return CopyResult::UnknownError; 47 } 48 if (!sourceBuffer.get()) { 49 ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); 50 return CopyResult::SourceEmpty; 51 } 52 if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { 53 ALOGW("Surface is protected, unable to copy from it"); 54 return CopyResult::SourceInvalid; 55 } 56 err = sourceFence->wait(500 /* ms */); 57 if (err != NO_ERROR) { 58 ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); 59 return CopyResult::Timeout; 60 } 61 62 return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap); 63 } 64 65 CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, 66 Matrix4& texTransform, const Rect& srcRect, 67 SkBitmap* bitmap) { 68 mRenderThread.eglManager().initialize(); 69 // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via 70 // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES 71 // to be able to properly sample from the buffer. 72 73 // Create the EGLImage object that maps the GraphicBuffer 74 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 75 EGLClientBuffer clientBuffer = (EGLClientBuffer)graphicBuffer->getNativeBuffer(); 76 EGLint attrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; 77 78 EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, 79 clientBuffer, attrs); 80 81 if (sourceImage == EGL_NO_IMAGE_KHR) { 82 ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); 83 return CopyResult::UnknownError; 84 } 85 86 uint32_t width = graphicBuffer->getWidth(); 87 uint32_t height = graphicBuffer->getHeight(); 88 CopyResult copyResult = 89 copyImageInto(sourceImage, texTransform, width, height, srcRect, bitmap); 90 91 // All we're flushing & finishing is the deletion of the texture since 92 // copyImageInto already did a major flush & finish as an implicit 93 // part of glReadPixels, so this shouldn't pose any major stalls. 94 glFinish(); 95 eglDestroyImageKHR(display, sourceImage); 96 return copyResult; 97 } 98 99 CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) { 100 Rect srcRect; 101 Matrix4 transform; 102 transform.loadScale(1, -1, 1); 103 transform.translate(0, -1); 104 return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap); 105 } 106 107 static float sFlipVInit[16] = { 108 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 109 }; 110 111 static const Matrix4 sFlipV(sFlipVInit); 112 113 //////////////////////////////////////////////////////////////////////////////// 114 //////////////////////////////////////////////////////////////////////////////// 115 116 inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, Texture& sourceTexture, 117 const Matrix4& texTransform, const Rect& srcRect, 118 SkBitmap* bitmap) { 119 int destWidth = bitmap->width(); 120 int destHeight = bitmap->height(); 121 if (destWidth > caches.maxTextureSize || destHeight > caches.maxTextureSize) { 122 ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d", destWidth, 123 destHeight, caches.maxTextureSize); 124 return CopyResult::DestinationInvalid; 125 } 126 127 if (bitmap->colorType() == kRGBA_F16_SkColorType && 128 !caches.extensions().hasRenderableFloatTextures()) { 129 ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported"); 130 return CopyResult::DestinationInvalid; 131 } 132 133 GLuint fbo = renderState.createFramebuffer(); 134 if (!fbo) { 135 ALOGW("Could not obtain an FBO"); 136 return CopyResult::UnknownError; 137 } 138 139 GLuint texture; 140 141 GLenum format; 142 GLenum internalFormat; 143 GLenum type; 144 145 switch (bitmap->colorType()) { 146 case kAlpha_8_SkColorType: 147 format = GL_ALPHA; 148 internalFormat = GL_ALPHA; 149 type = GL_UNSIGNED_BYTE; 150 break; 151 case kRGB_565_SkColorType: 152 format = GL_RGB; 153 internalFormat = GL_RGB; 154 type = GL_UNSIGNED_SHORT_5_6_5; 155 break; 156 case kARGB_4444_SkColorType: 157 format = GL_RGBA; 158 internalFormat = GL_RGBA; 159 type = GL_UNSIGNED_SHORT_4_4_4_4; 160 break; 161 case kRGBA_F16_SkColorType: 162 format = GL_RGBA; 163 internalFormat = GL_RGBA16F; 164 type = GL_HALF_FLOAT; 165 break; 166 case kN32_SkColorType: 167 default: 168 format = GL_RGBA; 169 internalFormat = GL_RGBA; 170 type = GL_UNSIGNED_BYTE; 171 break; 172 } 173 174 renderState.bindFramebuffer(fbo); 175 176 // TODO: Use layerPool or something to get this maybe? But since we 177 // need explicit format control we can't currently. 178 179 // Setup the rendertarget 180 glGenTextures(1, &texture); 181 caches.textureState().activateTexture(0); 182 caches.textureState().bindTexture(texture); 183 glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel()); 184 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 185 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 186 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 188 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight, 0, format, type, nullptr); 189 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); 190 191 { 192 bool requiresFilter; 193 // Draw & readback 194 renderState.setViewport(destWidth, destHeight); 195 renderState.scissor().setEnabled(false); 196 renderState.blend().syncEnabled(); 197 renderState.stencil().disable(); 198 199 Matrix4 croppedTexTransform(texTransform); 200 if (!srcRect.isEmpty()) { 201 // We flipV to convert to 0,0 top-left for the srcRect 202 // coordinates then flip back to 0,0 bottom-left for 203 // GLES coordinates. 204 croppedTexTransform.multiply(sFlipV); 205 croppedTexTransform.translate(srcRect.left / sourceTexture.width(), 206 srcRect.top / sourceTexture.height(), 0); 207 croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(), 208 srcRect.getHeight() / sourceTexture.height(), 1); 209 croppedTexTransform.multiply(sFlipV); 210 requiresFilter = srcRect.getWidth() != (float)destWidth || 211 srcRect.getHeight() != (float)destHeight; 212 } else { 213 requiresFilter = sourceTexture.width() != (uint32_t)destWidth || 214 sourceTexture.height() != (uint32_t)destHeight; 215 } 216 Glop glop; 217 GlopBuilder(renderState, caches, &glop) 218 .setRoundRectClipState(nullptr) 219 .setMeshTexturedUnitQuad(nullptr) 220 .setFillExternalTexture(sourceTexture, croppedTexTransform, requiresFilter) 221 .setTransform(Matrix4::identity(), TransformFlags::None) 222 .setModelViewMapUnitToRect(Rect(destWidth, destHeight)) 223 .build(); 224 Matrix4 ortho; 225 ortho.loadOrtho(destWidth, destHeight); 226 renderState.render(glop, ortho, false); 227 228 // TODO: We should convert to linear space when the target is RGBA16F 229 glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels()); 230 bitmap->notifyPixelsChanged(); 231 } 232 233 // Cleanup 234 caches.textureState().deleteTexture(texture); 235 renderState.deleteFramebuffer(fbo); 236 237 GL_CHECKPOINT(MODERATE); 238 239 return CopyResult::Success; 240 } 241 242 CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, const Matrix4& imgTransform, 243 int imgWidth, int imgHeight, const Rect& srcRect, 244 SkBitmap* bitmap) { 245 // If this is a 90 or 270 degree rotation we need to swap width/height 246 // This is a fuzzy way of checking that. 247 if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) { 248 std::swap(imgWidth, imgHeight); 249 } 250 251 Caches& caches = Caches::getInstance(); 252 GLuint sourceTexId; 253 // Create a 2D texture to sample from the EGLImage 254 glGenTextures(1, &sourceTexId); 255 caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); 256 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); 257 258 GLenum status = GL_NO_ERROR; 259 while ((status = glGetError()) != GL_NO_ERROR) { 260 ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status); 261 return CopyResult::UnknownError; 262 } 263 264 Texture sourceTexture(caches); 265 sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */, 266 GL_TEXTURE_EXTERNAL_OES); 267 268 CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), sourceTexture, 269 imgTransform, srcRect, bitmap); 270 sourceTexture.deleteTexture(); 271 return copyResult; 272 } 273 274 bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, GlLayer& layer, 275 SkBitmap* bitmap) { 276 if (!layer.isRenderable()) { 277 // layer has never been updated by DeferredLayerUpdater, abort copy 278 return false; 279 } 280 281 return CopyResult::Success == copyTextureInto(Caches::getInstance(), renderThread.renderState(), 282 layer.getTexture(), layer.getTexTransform(), 283 Rect(), bitmap); 284 } 285 286 } // namespace uirenderer 287 } // namespace android 288