blob: 5c4263e03abc46b84684b63bc02073a82112abf0 [file] [log] [blame]
John Reck10dd0582016-03-31 16:36:16 -07001/*
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
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050017#include "OpenGLReadback.h"
John Reck10dd0582016-03-31 16:36:16 -070018
19#include "Caches.h"
20#include "Image.h"
21#include "GlopBuilder.h"
Greg Daniel8cd3edf2017-01-09 14:15:41 -050022#include "GlLayer.h"
John Reck10dd0582016-03-31 16:36:16 -070023#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>
Mathias Agopian3d7d5f92017-05-01 15:13:20 -070030#include <gui/Surface.h>
John Reck10dd0582016-03-31 16:36:16 -070031
32namespace android {
33namespace uirenderer {
34
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050035CopyResult OpenGLReadback::copySurfaceInto(Surface& surface, const Rect& srcRect,
36 SkBitmap* bitmap) {
37 ATRACE_CALL();
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050038 // Setup the source
39 sp<GraphicBuffer> sourceBuffer;
40 sp<Fence> sourceFence;
41 Matrix4 texTransform;
42 status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence,
43 texTransform.data);
44 texTransform.invalidateType();
45 if (err != NO_ERROR) {
46 ALOGW("Failed to get last queued buffer, error = %d", err);
47 return CopyResult::UnknownError;
48 }
49 if (!sourceBuffer.get()) {
50 ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
51 return CopyResult::SourceEmpty;
52 }
53 if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
54 ALOGW("Surface is protected, unable to copy from it");
55 return CopyResult::SourceInvalid;
56 }
57 err = sourceFence->wait(500 /* ms */);
58 if (err != NO_ERROR) {
59 ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
60 return CopyResult::Timeout;
61 }
62
sergeyv59eecb522016-11-17 17:54:57 -080063 return copyGraphicBufferInto(sourceBuffer.get(), texTransform, srcRect, bitmap);
64}
65
66CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer,
67 Matrix4& texTransform, const Rect& srcRect, SkBitmap* bitmap) {
68 mRenderThread.eglManager().initialize();
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050069 // TODO: Can't use Image helper since it forces GL_TEXTURE_2D usage via
70 // GL_OES_EGL_image, which doesn't work since we need samplerExternalOES
71 // to be able to properly sample from the buffer.
72
73 // Create the EGLImage object that maps the GraphicBuffer
74 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
sergeyv59eecb522016-11-17 17:54:57 -080075 EGLClientBuffer clientBuffer = (EGLClientBuffer) graphicBuffer->getNativeBuffer();
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050076 EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
77
78 EGLImageKHR sourceImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
79 EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
80
81 if (sourceImage == EGL_NO_IMAGE_KHR) {
82 ALOGW("eglCreateImageKHR failed (%#x)", eglGetError());
83 return CopyResult::UnknownError;
84 }
85
John Reck912bebe2016-12-07 16:36:15 -080086 uint32_t width = graphicBuffer->getWidth();
87 uint32_t height = graphicBuffer->getHeight();
John Reck912bebe2016-12-07 16:36:15 -080088 CopyResult copyResult = copyImageInto(sourceImage, texTransform, width, height,
89 srcRect, bitmap);
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -050090
91 // All we're flushing & finishing is the deletion of the texture since
92 // copyImageInto already did a major flush & finish as an implicit
93 // part of glReadPixels, so this shouldn't pose any major stalls.
94 glFinish();
95 eglDestroyImageKHR(display, sourceImage);
96 return copyResult;
97}
98
sergeyv59eecb522016-11-17 17:54:57 -080099CopyResult OpenGLReadback::copyGraphicBufferInto(GraphicBuffer* graphicBuffer, SkBitmap* bitmap) {
100 Rect srcRect;
101 Matrix4 transform;
102 transform.loadScale(1, -1, 1);
103 transform.translate(0, -1);
104 return copyGraphicBufferInto(graphicBuffer, transform, srcRect, bitmap);
105}
106
John Reckeb418eda2016-12-15 10:26:33 -0800107static float sFlipVInit[16] = {
108 1, 0, 0, 0,
109 0, -1, 0, 0,
110 0, 0, 1, 0,
111 0, 1, 0, 1,
112};
113
114static const Matrix4 sFlipV(sFlipVInit);
115
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500116////////////////////////////////////////////////////////////////////////////////
117////////////////////////////////////////////////////////////////////////////////
118
119inline CopyResult copyTextureInto(Caches& caches, RenderState& renderState,
120 Texture& sourceTexture, const Matrix4& texTransform, const Rect& srcRect,
John Reck95801462016-09-01 09:44:09 -0700121 SkBitmap* bitmap) {
John Reck10dd0582016-03-31 16:36:16 -0700122 int destWidth = bitmap->width();
123 int destHeight = bitmap->height();
124 if (destWidth > caches.maxTextureSize
125 || destHeight > caches.maxTextureSize) {
126 ALOGW("Can't copy surface into bitmap, %dx%d exceeds max texture size %d",
127 destWidth, destHeight, caches.maxTextureSize);
John Recke94cbc72016-04-25 13:03:44 -0700128 return CopyResult::DestinationInvalid;
John Reck10dd0582016-03-31 16:36:16 -0700129 }
Romain Guy9505a652016-12-14 09:43:50 -0800130
Romain Guy88e060f2017-06-16 18:07:54 -0700131 if (bitmap->colorType() == kRGBA_F16_SkColorType && !caches.extensions().hasFloatTextures()) {
Romain Guy9505a652016-12-14 09:43:50 -0800132 ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
133 return CopyResult::DestinationInvalid;
134 }
135
John Reck10dd0582016-03-31 16:36:16 -0700136 GLuint fbo = renderState.createFramebuffer();
137 if (!fbo) {
138 ALOGW("Could not obtain an FBO");
John Recke94cbc72016-04-25 13:03:44 -0700139 return CopyResult::UnknownError;
John Reck10dd0582016-03-31 16:36:16 -0700140 }
141
John Reck10dd0582016-03-31 16:36:16 -0700142 GLuint texture;
143
144 GLenum format;
Romain Guy88e060f2017-06-16 18:07:54 -0700145 GLenum internalFormat;
John Reck10dd0582016-03-31 16:36:16 -0700146 GLenum type;
147
148 switch (bitmap->colorType()) {
149 case kAlpha_8_SkColorType:
150 format = GL_ALPHA;
Romain Guy88e060f2017-06-16 18:07:54 -0700151 internalFormat = GL_ALPHA;
John Reck10dd0582016-03-31 16:36:16 -0700152 type = GL_UNSIGNED_BYTE;
153 break;
154 case kRGB_565_SkColorType:
155 format = GL_RGB;
Romain Guy88e060f2017-06-16 18:07:54 -0700156 internalFormat = GL_RGB;
John Reck10dd0582016-03-31 16:36:16 -0700157 type = GL_UNSIGNED_SHORT_5_6_5;
158 break;
159 case kARGB_4444_SkColorType:
160 format = GL_RGBA;
Romain Guy88e060f2017-06-16 18:07:54 -0700161 internalFormat = GL_RGBA;
John Reck10dd0582016-03-31 16:36:16 -0700162 type = GL_UNSIGNED_SHORT_4_4_4_4;
163 break;
Romain Guy88e060f2017-06-16 18:07:54 -0700164 case kRGBA_F16_SkColorType:
165 format = GL_RGBA;
166 internalFormat = GL_RGBA16F;
167 type = GL_HALF_FLOAT;
168 break;
John Reck10dd0582016-03-31 16:36:16 -0700169 case kN32_SkColorType:
170 default:
171 format = GL_RGBA;
Romain Guy88e060f2017-06-16 18:07:54 -0700172 internalFormat = GL_RGBA;
John Reck10dd0582016-03-31 16:36:16 -0700173 type = GL_UNSIGNED_BYTE;
174 break;
175 }
176
177 renderState.bindFramebuffer(fbo);
178
179 // TODO: Use layerPool or something to get this maybe? But since we
180 // need explicit format control we can't currently.
181
182 // Setup the rendertarget
183 glGenTextures(1, &texture);
184 caches.textureState().activateTexture(0);
185 caches.textureState().bindTexture(texture);
186 glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
187 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
188 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
189 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
190 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Romain Guy88e060f2017-06-16 18:07:54 -0700191 glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, destWidth, destHeight,
John Reck10dd0582016-03-31 16:36:16 -0700192 0, format, type, nullptr);
193 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
194 GL_TEXTURE_2D, texture, 0);
195
Chris Craik764045d2016-07-06 17:14:05 -0700196 {
John Reck7bf96a02017-06-28 11:08:07 -0700197 bool requiresFilter;
Chris Craik764045d2016-07-06 17:14:05 -0700198 // Draw & readback
199 renderState.setViewport(destWidth, destHeight);
200 renderState.scissor().setEnabled(false);
201 renderState.blend().syncEnabled();
202 renderState.stencil().disable();
203
John Reck95801462016-09-01 09:44:09 -0700204 Matrix4 croppedTexTransform(texTransform);
205 if (!srcRect.isEmpty()) {
John Reckeb418eda2016-12-15 10:26:33 -0800206 // We flipV to convert to 0,0 top-left for the srcRect
207 // coordinates then flip back to 0,0 bottom-left for
208 // GLES coordinates.
209 croppedTexTransform.multiply(sFlipV);
210 croppedTexTransform.translate(srcRect.left / sourceTexture.width(),
John Reck95801462016-09-01 09:44:09 -0700211 srcRect.top / sourceTexture.height(), 0);
212 croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(),
213 srcRect.getHeight() / sourceTexture.height(), 1);
John Reckeb418eda2016-12-15 10:26:33 -0800214 croppedTexTransform.multiply(sFlipV);
John Reck7bf96a02017-06-28 11:08:07 -0700215 requiresFilter = srcRect.getWidth() != (float) destWidth
216 || srcRect.getHeight() != (float) destHeight;
217 } else {
218 requiresFilter = sourceTexture.width() != (uint32_t) destWidth
219 || sourceTexture.height() != (uint32_t) destHeight;
John Reck95801462016-09-01 09:44:09 -0700220 }
Chris Craik764045d2016-07-06 17:14:05 -0700221 Glop glop;
222 GlopBuilder(renderState, caches, &glop)
223 .setRoundRectClipState(nullptr)
224 .setMeshTexturedUnitQuad(nullptr)
John Reck7bf96a02017-06-28 11:08:07 -0700225 .setFillExternalTexture(sourceTexture, croppedTexTransform, requiresFilter)
Chris Craik764045d2016-07-06 17:14:05 -0700226 .setTransform(Matrix4::identity(), TransformFlags::None)
227 .setModelViewMapUnitToRect(Rect(destWidth, destHeight))
228 .build();
229 Matrix4 ortho;
230 ortho.loadOrtho(destWidth, destHeight);
231 renderState.render(glop, ortho);
232
Romain Guy88e060f2017-06-16 18:07:54 -0700233 // TODO: We should convert to linear space when the target is RGBA16F
Chris Craik764045d2016-07-06 17:14:05 -0700234 glReadPixels(0, 0, bitmap->width(), bitmap->height(), format,
235 type, bitmap->getPixels());
John Reckabbedfc2017-07-06 15:27:23 -0700236 bitmap->notifyPixelsChanged();
Chris Craik764045d2016-07-06 17:14:05 -0700237 }
238
239 // Cleanup
240 caches.textureState().deleteTexture(texture);
241 renderState.deleteFramebuffer(fbo);
242
243 GL_CHECKPOINT(MODERATE);
244
245 return CopyResult::Success;
246}
247
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500248CopyResult OpenGLReadbackImpl::copyImageInto(EGLImageKHR eglImage,
249 const Matrix4& imgTransform, int imgWidth, int imgHeight, const Rect& srcRect,
250 SkBitmap* bitmap) {
Chris Craik764045d2016-07-06 17:14:05 -0700251
Stan Ilievdf6520e2017-07-17 18:50:16 -0400252 // If this is a 90 or 270 degree rotation we need to swap width/height
253 // This is a fuzzy way of checking that.
254 if (imgTransform[Matrix4::kSkewX] >= 0.5f || imgTransform[Matrix4::kSkewX] <= -0.5f) {
255 std::swap(imgWidth, imgHeight);
256 }
257
Chris Craik764045d2016-07-06 17:14:05 -0700258 Caches& caches = Caches::getInstance();
John Reck2f783272016-04-19 07:51:13 -0700259 GLuint sourceTexId;
260 // Create a 2D texture to sample from the EGLImage
261 glGenTextures(1, &sourceTexId);
Chris Craik764045d2016-07-06 17:14:05 -0700262 caches.textureState().bindTexture(GL_TEXTURE_EXTERNAL_OES, sourceTexId);
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500263 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, eglImage);
John Reck2f783272016-04-19 07:51:13 -0700264
265 GLenum status = GL_NO_ERROR;
266 while ((status = glGetError()) != GL_NO_ERROR) {
John Reck8a29c0e2016-09-01 13:04:00 -0700267 ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status);
John Recke94cbc72016-04-25 13:03:44 -0700268 return CopyResult::UnknownError;
John Reck2f783272016-04-19 07:51:13 -0700269 }
270
John Reck10dd0582016-03-31 16:36:16 -0700271 Texture sourceTexture(caches);
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500272 sourceTexture.wrap(sourceTexId, imgWidth, imgHeight, 0, 0 /* total lie */,
273 GL_TEXTURE_EXTERNAL_OES);
John Reck10dd0582016-03-31 16:36:16 -0700274
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500275 CopyResult copyResult = copyTextureInto(caches, mRenderThread.renderState(),
276 sourceTexture, imgTransform, srcRect, bitmap);
John Reck8a29c0e2016-09-01 13:04:00 -0700277 sourceTexture.deleteTexture();
John Reck8a29c0e2016-09-01 13:04:00 -0700278 return copyResult;
Chris Craik764045d2016-07-06 17:14:05 -0700279}
John Reck10dd0582016-03-31 16:36:16 -0700280
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500281bool OpenGLReadbackImpl::copyLayerInto(renderthread::RenderThread& renderThread,
Greg Daniel8cd3edf2017-01-09 14:15:41 -0500282 GlLayer& layer, SkBitmap* bitmap) {
Chris Craik89cd62c2017-09-22 09:31:05 -0700283 if (!layer.isRenderable()) {
284 // layer has never been updated by DeferredLayerUpdater, abort copy
285 return false;
286 }
287
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500288 return CopyResult::Success == copyTextureInto(Caches::getInstance(),
289 renderThread.renderState(), layer.getTexture(), layer.getTexTransform(),
290 Rect(), bitmap);
John Reck10dd0582016-03-31 16:36:16 -0700291}
292
Derek Sollenbergerc4fbada2016-11-07 16:05:41 -0500293
John Reck10dd0582016-03-31 16:36:16 -0700294} // namespace uirenderer
295} // namespace android