OpenGLReadback.cpp revision 8cd3edfa15cc9cdbffa935d19ab894426b08d174
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 // Draw & readback 195 renderState.setViewport(destWidth, destHeight); 196 renderState.scissor().setEnabled(false); 197 renderState.blend().syncEnabled(); 198 renderState.stencil().disable(); 199 200 Matrix4 croppedTexTransform(texTransform); 201 if (!srcRect.isEmpty()) { 202 // We flipV to convert to 0,0 top-left for the srcRect 203 // coordinates then flip back to 0,0 bottom-left for 204 // GLES coordinates. 205 croppedTexTransform.multiply(sFlipV); 206 croppedTexTransform.translate(srcRect.left / sourceTexture.width(), 207 srcRect.top / sourceTexture.height(), 0); 208 croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(), 209 srcRect.getHeight() / sourceTexture.height(), 1); 210 croppedTexTransform.multiply(sFlipV); 211 } 212 Glop glop; 213 GlopBuilder(renderState, caches, &glop) 214 .setRoundRectClipState(nullptr) 215 .setMeshTexturedUnitQuad(nullptr) 216 .setFillExternalTexture(sourceTexture, croppedTexTransform) 217 .setTransform(Matrix4::identity(), TransformFlags::None) 218 .setModelViewMapUnitToRect(Rect(destWidth, destHeight)) 219 .build(); 220 Matrix4 ortho; 221 ortho.loadOrtho(destWidth, destHeight); 222 renderState.render(glop, ortho); 223 224 glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, 225 type, bitmap->getPixels()); 226 } 227 228 // Cleanup 229 caches.textureState().deleteTexture(texture); 230 renderState.deleteFramebuffer(fbo); 231 232 GL_CHECKPOINT(MODERATE); 233 234 return CopyResult::Success; 235} 236 237CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage, 238 const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect, 239 SkBitmap* bitmap) { 240 241 Caches& caches = Caches::getInstance(); 242 GLuint sourceTexId; 243 // Create a 2D texture to sample from the EGLImage 244 glGenTextures(1, &sourceTexId); 245 caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); 246 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage); 247 248 GLenum status = GL_NO_ERROR; 249 while ((status = glGetError()) != GL_NO_ERROR) { 250 ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status); 251 return CopyResult::UnknownError; 252 } 253 254 Texture sourceTexture(caches); 255 sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */, 256 GL_TEXTURE_EXTERNAL_OES); 257 258 CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(), 259 sourceTexture, imgTransform, srcRect, bitmap); 260 sourceTexture.deleteTexture(); 261 return copyResult; 262} 263 264bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread, 265 GlLayer& layer, SkBitmap* bitmap) { 266 return CopyResult::Success == copyTextureInto(Caches::getInstance(), 267 renderThread.renderState(), layer.getTexture(), layer.getTexTransform(), 268 Rect(), bitmap); 269} 270 271 272} // namespace uirenderer 273} // namespace android 274