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