Readback.cpp revision 2f78327cfcd4c7b23aae9bb0262e64050d093a64
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
33bool 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 false;
48    }
49    GLuint fbo = renderState.createFramebuffer();
50    if (!fbo) {
51        ALOGW("Could not obtain an FBO");
52        return false;
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    status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence);
105    if (err != NO_ERROR) {
106        ALOGW("Failed to get last queued buffer, error = %d", err);
107        return false;
108    }
109    if (!sourceBuffer.get()) {
110        ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
111        return false;
112    }
113    err = sourceFence->wait(500 /* ms */);
114    if (err != NO_ERROR) {
115        ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
116        return false;
117    }
118
119    // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
120    // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
121    // to be able to properly sample from the buffer.
122
123    // Create the EGLImage object that maps the GraphicBuffer
124    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
125    EGLClientBuffer clientBuffer = (EGLClientBuffer) sourceBuffer->getNativeBuffer();
126    EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
127
128    EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
129            EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
130
131    if (sourceImage == EGL_NO_IMAGE_KHR) {
132        ALOGW("Error creating image (%#x)", eglGetError());
133        return false;
134    }
135    GLuint sourceTexId;
136    // Create a 2D texture to sample from the EGLImage
137    glGenTextures(1, &sourceTexId);
138    Caches::getInstance().textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
139    glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, sourceImage);
140
141    GLenum status = GL_NO_ERROR;
142    while ((status = glGetError()) != GL_NO_ERROR) {
143        ALOGW("Error creating image (%#x)", status);
144        return false;
145    }
146
147    Texture sourceTexture(caches);
148    sourceTexture.wrap(sourceTexId,
149            sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
150
151    {
152        // Draw & readback
153        renderState.setViewport(destWidth, destHeight);
154        renderState.scissor().setEnabled(false);
155        renderState.blend().syncEnabled();
156        renderState.stencil().disable();
157
158        Rect destRect(destWidth, destHeight);
159        Glop glop;
160        GlopBuilder(renderState, caches, &glop)
161                .setRoundRectClipState(nullptr)
162                .setMeshTexturedUvQuad(nullptr, Rect(0, 1, 1, 0)) // TODO: simplify with VBO
163                .setFillExternalTexture(sourceTexture)
164                .setTransform(Matrix4::identity(), TransformFlags::None)
165                .setModelViewMapUnitToRect(destRect)
166                .build();
167        Matrix4 ortho;
168        ortho.loadOrtho(destWidth, destHeight);
169        renderState.render(glop, ortho);
170
171        glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
172                type, bitmap->getPixels());
173    }
174
175    // Cleanup
176    caches.textureState().deleteTexture(texture);
177    renderState.deleteFramebuffer(fbo);
178
179    GL_CHECKPOINT(MODERATE);
180
181    return true;
182}
183
184} // namespace uirenderer
185} // namespace android
186