OpenGLReadback.cpp revision 764045da2ce980f1eee78171de5b4f09dfb601a7
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 "Readback.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 34static CopyResult copyTextureInto(Caches& caches, RenderState& renderState, 35 Texture& sourceTexture, Matrix4& texTransform, SkBitmap* bitmap) { 36 int destWidth = bitmap->width(); 37 int destHeight = bitmap->height(); 38 if (destWidth > caches.maxTextureSize 39 || destHeight > caches.maxTextureSize) { 40 ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d", 41 destWidth, destHeight, caches.maxTextureSize); 42 return CopyResult::DestinationInvalid; 43 } 44 GLuint fbo = renderState.createFramebuffer(); 45 if (!fbo) { 46 ALOGW("Could not obtain an FBO"); 47 return CopyResult::UnknownError; 48 } 49 50 SkAutoLockPixels alp(*bitmap); 51 52 GLuint texture; 53 54 GLenum format; 55 GLenum type; 56 57 switch (bitmap->colorType()) { 58 case kAlpha_8_SkColorType: 59 format = GL_ALPHA; 60 type = GL_UNSIGNED_BYTE; 61 break; 62 case kRGB_565_SkColorType: 63 format = GL_RGB; 64 type = GL_UNSIGNED_SHORT_5_6_5; 65 break; 66 case kARGB_4444_SkColorType: 67 format = GL_RGBA; 68 type = GL_UNSIGNED_SHORT_4_4_4_4; 69 break; 70 case kN32_SkColorType: 71 default: 72 format = GL_RGBA; 73 type = GL_UNSIGNED_BYTE; 74 break; 75 } 76 77 renderState.bindFramebuffer(fbo); 78 79 // TODO: Use layerPool or something to get this maybe? But since we 80 // need explicit format control we can't currently. 81 82 // Setup the rendertarget 83 glGenTextures(1, &texture); 84 caches.textureState().activateTexture(0); 85 caches.textureState().bindTexture(texture); 86 glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel()); 87 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 88 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 89 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 90 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 91 glTexImage2D(GL_TEXTURE_2D, 0, format, destWidth, destHeight, 92 0, format, type, nullptr); 93 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 94 GL_TEXTURE_2D, texture, 0); 95 96 { 97 // Draw & readback 98 renderState.setViewport(destWidth, destHeight); 99 renderState.scissor().setEnabled(false); 100 renderState.blend().syncEnabled(); 101 renderState.stencil().disable(); 102 103 Glop glop; 104 GlopBuilder(renderState, caches, &glop) 105 .setRoundRectClipState(nullptr) 106 .setMeshTexturedUnitQuad(nullptr) 107 .setFillExternalTexture(sourceTexture, texTransform) 108 .setTransform(Matrix4::identity(), TransformFlags::None) 109 .setModelViewMapUnitToRect(Rect(destWidth, destHeight)) 110 .build(); 111 Matrix4 ortho; 112 ortho.loadOrtho(destWidth, destHeight); 113 renderState.render(glop, ortho); 114 115 glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, 116 type, bitmap->getPixels()); 117 } 118 119 // Cleanup 120 caches.textureState().deleteTexture(texture); 121 renderState.deleteFramebuffer(fbo); 122 123 GL_CHECKPOINT(MODERATE); 124 125 return CopyResult::Success; 126} 127 128CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, 129 Surface& surface, SkBitmap* bitmap) { 130 renderThread.eglManager().initialize(); 131 132 Caches& caches = Caches::getInstance(); 133 134 // Setup the source 135 sp<GraphicBuffer> sourceBuffer; 136 sp<Fence> sourceFence; 137 Matrix4 texTransform; 138 status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, 139 texTransform.data); 140 texTransform.invalidateType(); 141 if (err != NO_ERROR) { 142 ALOGW("Failed to get last queued buffer, error = %d", err); 143 return CopyResult::UnknownError; 144 } 145 if (!sourceBuffer.get()) { 146 ALOGW("Surface doesn't have any previously queued frames, nothing to readback from"); 147 return CopyResult::SourceEmpty; 148 } 149 if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) { 150 ALOGW("Surface is protected, unable to copy from it"); 151 return CopyResult::SourceInvalid; 152 } 153 err = sourceFence->wait(500 /* ms */); 154 if (err != NO_ERROR) { 155 ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt"); 156 return CopyResult::Timeout; 157 } 158 159 // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via 160 // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES 161 // to be able to properly sample from the buffer. 162 163 // Create the EGLImage object that maps the GraphicBuffer 164 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 165 EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer(); 166 EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; 167 168 EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, 169 EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); 170 171 if (sourceImage == EGL_NO_IMAGE_KHR) { 172 ALOGW("Error creating image (%#x)", eglGetError()); 173 return CopyResult::UnknownError; 174 } 175 GLuint sourceTexId; 176 // Create a 2D texture to sample from the EGLImage 177 glGenTextures(1, &sourceTexId); 178 caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId); 179 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage); 180 181 GLenum status = GL_NO_ERROR; 182 while ((status = glGetError()) != GL_NO_ERROR) { 183 ALOGW("Error creating image (%#x)", status); 184 return CopyResult::UnknownError; 185 } 186 187 Texture sourceTexture(caches); 188 sourceTexture.wrap(sourceTexId, 189 sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */); 190 191 return copyTextureInto(caches, renderThread.renderState(), sourceTexture, texTransform, bitmap); 192} 193 194CopyResult Readback::copyTextureLayerInto(renderthread::RenderThread& renderThread, 195 Layer& layer, SkBitmap* bitmap) { 196 return copyTextureInto(Caches::getInstance(), renderThread.renderState(), 197 layer.getTexture(), layer.getTexTransform(), bitmap); 198} 199 200} // namespace uirenderer 201} // namespace android 202