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