OpenGLReadback.cpp revision 912bebeb7488f498954282a1eb82a4b641e6418e
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 "Layer.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 111//////////////////////////////////////////////////////////////////////////////// 112//////////////////////////////////////////////////////////////////////////////// 113 114inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState, 115 Texture& sourceTexture, const Matrix4& texTransform, const Rect& srcRect, 116 SkBitmap* bitmap) { 117 int destWidth = bitmap->width(); 118 int destHeight = bitmap->height(); 119 if (destWidth > caches.maxTextureSize 120 || destHeight > caches.maxTextureSize) { 121 ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d", 122 destWidth, destHeight, caches.maxTextureSize); 123 return CopyResult::DestinationInvalid; 124 } 125 GLuint fbo = renderState.createFramebuffer(); 126 if (!fbo) { 127 ALOGW("Could not obtain an FBO"); 128 return CopyResult::UnknownError; 129 } 130 131 SkAutoLockPixels alp(*bitmap); 132 133 GLuint texture; 134 135 GLenum format; 136 GLenum type; 137 138 switch (bitmap->colorType()) { 139 case kAlpha_8_SkColorType: 140 format = GL_ALPHA; 141 type = GL_UNSIGNED_BYTE; 142 break; 143 case kRGB_565_SkColorType: 144 format = GL_RGB; 145 type = GL_UNSIGNED_SHORT_5_6_5; 146 break; 147 case kARGB_4444_SkColorType: 148 format = GL_RGBA; 149 type = GL_UNSIGNED_SHORT_4_4_4_4; 150 break; 151 case kN32_SkColorType: 152 default: 153 format = GL_RGBA; 154 type = GL_UNSIGNED_BYTE; 155 break; 156 } 157 158 renderState.bindFramebuffer(fbo); 159 160 // TODO: Use layerPool or something to get this maybe? But since we 161 // need explicit format control we can't currently. 162 163 // Setup the rendertarget 164 glGenTextures(1, &texture); 165 caches.textureState().activateTexture(0); 166 caches.textureState().bindTexture(texture); 167 glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel()); 168 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 169 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 171 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 172 glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight, 173 0, format, type, nullptr); 174 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 175 GL_TEXTURE_2D, texture, 0); 176 177 { 178 // Draw & readback 179 renderState.setViewport(destWidth, destHeight); 180 renderState.scissor().setEnabled(false); 181 renderState.blend().syncEnabled(); 182 renderState.stencil().disable(); 183 184 Matrix4 croppedTexTransform(texTransform); 185 if (!srcRect.isEmpty()) { 186 croppedTexTransform.loadTranslate(srcRect.left / sourceTexture.width(), 187 srcRect.top / sourceTexture.height(), 0); 188 croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(), 189 srcRect.getHeight() / sourceTexture.height(), 1); 190 croppedTexTransform.multiply(texTransform); 191 } 192 Glop glop; 193 GlopBuilder(renderState, caches, &glop) 194 .setRoundRectClipState(nullptr) 195 .setMeshTexturedUnitQuad(nullptr) 196 .setFillExternalTexture(sourceTexture, croppedTexTransform) 197 .setTransform(Matrix4::identity(), TransformFlags::None) 198 .setModelViewMapUnitToRect(Rect(destWidth, destHeight)) 199 .build(); 200 Matrix4 ortho; 201 ortho.loadOrtho(destWidth, destHeight); 202 renderState.render(glop, ortho); 203 204 glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, 205 type, bitmap->getPixels()); 206 } 207 208 // Cleanup 209 caches.textureState().deleteTexture(texture); 210 renderState.deleteFramebuffer(fbo); 211 212 GL_CHECKPOINT(MODERATE); 213 214 return CopyResult::Success; 215} 216 217CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, 218 const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect, 219 SkBitmap* bitmap) { 220 221 Caches& caches = Caches::getInstance(); 222 GLuint sourceTexId; 223 // Create a 2D texture to sample from the EGLImage 224 glGenTextures(1, &sourceTexId); 225 caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); 226 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); 227 228 GLenum status = GL_NO_ERROR; 229 while ((status = glGetError()) != GL_NO_ERROR) { 230 ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status); 231 return CopyResult::UnknownError; 232 } 233 234 Texture sourceTexture(caches); 235 sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */, 236 GL_TEXTURE_EXTERNAL_OES); 237 238 CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), 239 sourceTexture, imgTransform, srcRect, bitmap); 240 sourceTexture.deleteTexture(); 241 return copyResult; 242} 243 244bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, 245 Layer& layer, SkBitmap* bitmap) { 246 return CopyResult::Success == copyTextureInto(Caches::getInstance(), 247 renderThread.renderState(), layer.getTexture(), layer.getTexTransform(), 248 Rect(), bitmap); 249} 250 251 252} // namespace uirenderer 253} // namespace android 254