• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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("eglCreateImageKHR failed (%#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("glEGLImageTargetTexture2DOES failed (%#x)", status);
151         eglDestroyImageKHR(display, sourceImage);
152         return CopyResult::UnknownError;
153     }
154 
155     Texture sourceTexture(caches);
156     sourceTexture.wrap(sourceTexId,
157             sourceBuffer->getWidth(), sourceBuffer->getHeight(), 0 /* total lie */);
158 
159     {
160         // Draw & readback
161         renderState.setViewport(destWidth, destHeight);
162         renderState.scissor().setEnabled(false);
163         renderState.blend().syncEnabled();
164         renderState.stencil().disable();
165 
166         Rect destRect(destWidth, destHeight);
167         Glop glop;
168         GlopBuilder(renderState, caches, &glop)
169                 .setRoundRectClipState(nullptr)
170                 .setMeshTexturedUnitQuad(nullptr)
171                 .setFillExternalTexture(sourceTexture, texTransform)
172                 .setTransform(Matrix4::identity(), TransformFlags::None)
173                 .setModelViewMapUnitToRect(destRect)
174                 .build();
175         Matrix4 ortho;
176         ortho.loadOrtho(destWidth, destHeight);
177         renderState.render(glop, ortho);
178 
179         glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
180                 type, bitmap->getPixels());
181     }
182 
183     // Cleanup
184     caches.textureState().deleteTexture(texture);
185     renderState.deleteFramebuffer(fbo);
186 
187     sourceTexture.deleteTexture();
188     // All we're flushing & finishing is the deletion of the texture since
189     // copyTextureInto already did a major flush & finish as an implicit
190     // part of glReadPixels, so this shouldn't pose any major stalls.
191     glFinish();
192     eglDestroyImageKHR(display, sourceImage);
193 
194     GL_CHECKPOINT(MODERATE);
195 
196     return CopyResult::Success;
197 }
198 
199 } // namespace uirenderer
200 } // namespace android
201