blob: 80f2b5714659f10c6838e196a6d5ba08c5badfc7 [file] [log] [blame]
Stan Iliev1a025a72018-09-05 16:35:11 -04001/*
2 * Copyright (C) 2018 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 "pipeline/skia/LayerDrawable.h"
20#include "renderthread/EglManager.h"
21#include "renderthread/VulkanManager.h"
22
23#include <SkToSRGBColorFilter.h>
24#include <gui/Surface.h>
25#include <ui/Fence.h>
26#include <ui/GraphicBuffer.h>
27#include "DeferredLayerUpdater.h"
28#include "Properties.h"
29#include "hwui/Bitmap.h"
30#include "utils/Color.h"
31#include "utils/MathUtils.h"
32
33using namespace android::uirenderer::renderthread;
34
35namespace android {
36namespace uirenderer {
37
38CopyResult Readback::copySurfaceInto(Surface& surface, const Rect& srcRect, SkBitmap* bitmap) {
39 ATRACE_CALL();
40 // Setup the source
41 sp<GraphicBuffer> sourceBuffer;
42 sp<Fence> sourceFence;
43 Matrix4 texTransform;
44 status_t err = surface.getLastQueuedBuffer(&sourceBuffer, &sourceFence, texTransform.data);
45 texTransform.invalidateType();
46 if (err != NO_ERROR) {
47 ALOGW("Failed to get last queued buffer, error = %d", err);
48 return CopyResult::UnknownError;
49 }
50 if (!sourceBuffer.get()) {
51 ALOGW("Surface doesn't have any previously queued frames, nothing to readback from");
52 return CopyResult::SourceEmpty;
53 }
54 if (sourceBuffer->getUsage() & GRALLOC_USAGE_PROTECTED) {
55 ALOGW("Surface is protected, unable to copy from it");
56 return CopyResult::SourceInvalid;
57 }
58 err = sourceFence->wait(500 /* ms */);
59 if (err != NO_ERROR) {
60 ALOGE("Timeout (500ms) exceeded waiting for buffer fence, abandoning readback attempt");
61 return CopyResult::Timeout;
62 }
63 if (!sourceBuffer.get()) {
64 return CopyResult::UnknownError;
65 }
66
67 sk_sp<SkColorSpace> colorSpace =
68 DataSpaceToColorSpace(static_cast<android_dataspace>(surface.getBuffersDataSpace()));
69 sk_sp<SkColorFilter> colorSpaceFilter;
70 if (colorSpace && !colorSpace->isSRGB()) {
71 colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
72 }
73 sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
74 reinterpret_cast<AHardwareBuffer*>(sourceBuffer.get()), kPremul_SkAlphaType);
75 return copyImageInto(image, colorSpaceFilter, texTransform, srcRect, bitmap);
76}
77
78CopyResult Readback::copyHWBitmapInto(Bitmap* hwBitmap, SkBitmap* bitmap) {
79 LOG_ALWAYS_FATAL_IF(!hwBitmap->isHardware());
80
81 Rect srcRect;
82 Matrix4 transform;
83 transform.loadScale(1, -1, 1);
84 transform.translate(0, -1);
85
86 // TODO: Try to take and reuse the image inside HW bitmap with "hwBitmap->makeImage".
87 // TODO: When this was attempted, it resulted in instability.
88 sk_sp<SkColorFilter> colorSpaceFilter;
89 sk_sp<SkColorSpace> colorSpace = hwBitmap->info().refColorSpace();
90 if (colorSpace && !colorSpace->isSRGB()) {
91 colorSpaceFilter = SkToSRGBColorFilter::Make(colorSpace);
92 }
93 sk_sp<SkImage> image = SkImage::MakeFromAHardwareBuffer(
94 reinterpret_cast<AHardwareBuffer*>(hwBitmap->graphicBuffer()), kPremul_SkAlphaType);
95
96 // HW Bitmap currently can only attach to a GraphicBuffer with PIXEL_FORMAT_RGBA_8888 format
97 // and SRGB color space. ImageDecoder can create a new HW Bitmap with non-SRGB color space: for
98 // example see android.graphics.cts.BitmapColorSpaceTest#testEncodeP3hardware test.
99 return copyImageInto(image, colorSpaceFilter, transform, srcRect, bitmap);
100}
101
102CopyResult Readback::copyLayerInto(DeferredLayerUpdater* deferredLayer, SkBitmap* bitmap) {
103 if (!mRenderThread.getGrContext()) {
104 return CopyResult::UnknownError;
105 }
106
107 // acquire most recent buffer for drawing
108 deferredLayer->updateTexImage();
109 deferredLayer->apply();
110 const SkRect dstRect = SkRect::MakeIWH(bitmap->width(), bitmap->height());
111 CopyResult copyResult = CopyResult::UnknownError;
112 Layer* layer = deferredLayer->backingLayer();
113 if (layer) {
114 if (copyLayerInto(layer, nullptr, &dstRect, bitmap)) {
115 copyResult = CopyResult::Success;
116 }
117 }
118 return copyResult;
119}
120
121CopyResult Readback::copyImageInto(const sk_sp<SkImage>& image,
122 sk_sp<SkColorFilter>& colorSpaceFilter, Matrix4& texTransform,
123 const Rect& srcRect, SkBitmap* bitmap) {
124 if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaGL) {
125 mRenderThread.requireGlContext();
126 } else {
127 mRenderThread.vulkanManager().initialize();
128 }
129 if (!image.get()) {
130 return CopyResult::UnknownError;
131 }
132 int imgWidth = image->width();
133 int imgHeight = image->height();
134 sk_sp<GrContext> grContext = sk_ref_sp(mRenderThread.getGrContext());
135
136 if (bitmap->colorType() == kRGBA_F16_SkColorType &&
137 !grContext->colorTypeSupportedAsSurface(bitmap->colorType())) {
138 ALOGW("Can't copy surface into bitmap, RGBA_F16 config is not supported");
139 return CopyResult::DestinationInvalid;
140 }
141
142 CopyResult copyResult = CopyResult::UnknownError;
143
144 int displayedWidth = imgWidth, displayedHeight = imgHeight;
145 // If this is a 90 or 270 degree rotation we need to swap width/height to get the device
146 // size.
147 if (texTransform[Matrix4::kSkewX] >= 0.5f || texTransform[Matrix4::kSkewX] <= -0.5f) {
148 std::swap(displayedWidth, displayedHeight);
149 }
150 SkRect skiaDestRect = SkRect::MakeWH(bitmap->width(), bitmap->height());
151 SkRect skiaSrcRect = srcRect.toSkRect();
152 if (skiaSrcRect.isEmpty()) {
153 skiaSrcRect = SkRect::MakeIWH(displayedWidth, displayedHeight);
154 }
155 bool srcNotEmpty = skiaSrcRect.intersect(SkRect::MakeIWH(displayedWidth, displayedHeight));
156 if (!srcNotEmpty) {
157 return copyResult;
158 }
159
160 // See Readback::copyLayerInto for an overview of color space conversion.
161 // HW Bitmap are allowed to be in a non-SRGB color space (for example coming from ImageDecoder).
162 // For Surface and HW Bitmap readback flows we pass colorSpaceFilter, which does the conversion.
163 // TextureView readback is using Layer::setDataSpace, which creates a SkColorFilter internally.
164 Layer layer(mRenderThread.renderState(), colorSpaceFilter, 255, SkBlendMode::kSrc);
165 bool disableFilter = MathUtils::areEqual(skiaSrcRect.width(), skiaDestRect.width()) &&
166 MathUtils::areEqual(skiaSrcRect.height(), skiaDestRect.height());
167 layer.setForceFilter(!disableFilter);
168 layer.setSize(displayedWidth, displayedHeight);
169 texTransform.copyTo(layer.getTexTransform());
170 layer.setImage(image);
171 if (copyLayerInto(&layer, &skiaSrcRect, &skiaDestRect, bitmap)) {
172 copyResult = CopyResult::Success;
173 }
174
175 return copyResult;
176}
177
178bool Readback::copyLayerInto(Layer* layer, const SkRect* srcRect, const SkRect* dstRect,
179 SkBitmap* bitmap) {
180 /*
181 * In the past only TextureView readback was setting the temporary surface color space to null.
182 * Now all 3 readback flows are drawing into a SkSurface with null color space.
183 * At readback there are 3 options to convert the source image color space to the destination
184 * color space requested in "bitmap->info().colorSpace()":
185 * 1. Set color space for temporary surface render target to null (disables color management),
186 * colorspace tag from source SkImage is ignored by Skia,
187 * convert SkImage to SRGB at draw time with SkColorFilter/SkToSRGBColorFilter,
188 * do a readback from temporary SkSurface to a temporary SRGB SkBitmap "bitmap2",
189 * read back from SRGB "bitmap2" into non-SRGB "bitmap" which will do a CPU color conversion.
190 *
191 * 2. Set color space for temporary surface render target to SRGB (not nullptr),
192 * colorspace tag on the source SkImage is used by Skia to enable conversion,
193 * convert SkImage to SRGB at draw time with drawImage (no filters),
194 * do a readback from temporary SkSurface, which will do a color conversion from SRGB to
195 * bitmap->info().colorSpace() on the CPU.
196 *
197 * 3. Set color space for temporary surface render target to bitmap->info().colorSpace(),
198 * colorspace tag on the source SkImage is used by Skia to enable conversion,
199 * convert SkImage to bitmap->info().colorSpace() at draw time with drawImage (no filters),
200 * do a readback from SkSurface, which will not do any color conversion, because
201 * surface was created with the same color space as the "bitmap".
202 *
203 * Option 1 is used for all readback flows.
204 * Options 2 and 3 are new, because skia added support for non-SRGB render targets without
205 * linear blending.
206 * TODO: evaluate if options 2 or 3 for color space conversion are better.
207 */
208
209 // drop the colorSpace from the temporary surface.
210 SkImageInfo surfaceInfo = bitmap->info().makeColorSpace(nullptr);
211
212 /* This intermediate surface is present to work around a bug in SwiftShader that
213 * prevents us from reading the contents of the layer's texture directly. The
214 * workaround involves first rendering that texture into an intermediate buffer and
215 * then reading from the intermediate buffer into the bitmap.
216 * Another reason to render in an offscreen buffer is to scale and to avoid an issue b/62262733
217 * with reading incorrect data from EGLImage backed SkImage (likely a driver bug).
218 */
219 sk_sp<SkSurface> tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(),
220 SkBudgeted::kYes, surfaceInfo);
221
222 if (!tmpSurface.get()) {
223 surfaceInfo = surfaceInfo.makeColorType(SkColorType::kN32_SkColorType);
224 tmpSurface = SkSurface::MakeRenderTarget(mRenderThread.getGrContext(), SkBudgeted::kYes,
225 surfaceInfo);
226 if (!tmpSurface.get()) {
227 ALOGW("Unable to readback GPU contents into the provided bitmap");
228 return false;
229 }
230 }
231
232 if (skiapipeline::LayerDrawable::DrawLayer(mRenderThread.getGrContext(),
233 tmpSurface->getCanvas(), layer, srcRect, dstRect,
234 false)) {
235 // If bitmap->info().colorSpace() is non-SRGB, convert the data from SRGB to non-SRGB on
236 // CPU. We can't just pass bitmap->info() to SkSurface::readPixels, because "tmpSurface" has
237 // disabled color conversion.
238 SkColorSpace* destColorSpace = bitmap->info().colorSpace();
239 SkBitmap tempSRGBBitmap;
240 SkBitmap tmpN32Bitmap;
241 SkBitmap* bitmapInSRGB;
242 if (destColorSpace && !destColorSpace->isSRGB()) {
243 tempSRGBBitmap.allocPixels(bitmap->info().makeColorSpace(SkColorSpace::MakeSRGB()));
244 bitmapInSRGB = &tempSRGBBitmap; // Need to convert latter from SRGB to non-SRGB.
245 } else {
246 bitmapInSRGB = bitmap; // No need for color conversion - write directly into output.
247 }
248 bool success = false;
249
250 // TODO: does any of the readbacks below clamp F16 exSRGB?
251 // Readback into a SRGB SkBitmap.
252 if (tmpSurface->readPixels(bitmapInSRGB->info(), bitmapInSRGB->getPixels(),
253 bitmapInSRGB->rowBytes(), 0, 0)) {
254 success = true;
255 } else {
256 // if we fail to readback from the GPU directly (e.g. 565) then we attempt to read into
257 // 8888 and then convert that into the destination format before giving up.
258 SkImageInfo bitmapInfo =
259 SkImageInfo::MakeN32(bitmap->width(), bitmap->height(), bitmap->alphaType(),
260 SkColorSpace::MakeSRGB());
261 if (tmpN32Bitmap.tryAllocPixels(bitmapInfo) &&
262 tmpSurface->readPixels(bitmapInfo, tmpN32Bitmap.getPixels(),
263 tmpN32Bitmap.rowBytes(), 0, 0)) {
264 success = true;
265 bitmapInSRGB = &tmpN32Bitmap;
266 }
267 }
268
269 if (success) {
270 if (bitmapInSRGB != bitmap) {
271 // Convert from SRGB to non-SRGB color space if needed. Convert from N32 to
272 // destination bitmap color format if needed.
273 if (!bitmapInSRGB->readPixels(bitmap->info(), bitmap->getPixels(),
274 bitmap->rowBytes(), 0, 0)) {
275 return false;
276 }
277 }
278 bitmap->notifyPixelsChanged();
279 return true;
280 }
281 }
282
283 return false;
284}
285
286} /* namespace uirenderer */
287} /* namespace android */