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 
30 namespace android {
31 namespace uirenderer {
32 
copySurfaceInto(renderthread::RenderThread & renderThread,Surface & surface,SkBitmap * bitmap)33 CopyResult 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