| John Reck | 67b1e2b | 2020-08-26 13:17:24 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright 2020 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 |  | 
| John Reck | 67b1e2b | 2020-08-26 13:17:24 -0700 | [diff] [blame] | 17 | #undef LOG_TAG | 
|  | 18 | #define LOG_TAG "RenderEngine" | 
|  | 19 | #define ATRACE_TAG ATRACE_TAG_GRAPHICS | 
|  | 20 |  | 
| rnlee | c6a7364 | 2021-06-04 14:16:42 -0700 | [diff] [blame] | 21 | #include "SkiaRenderEngine.h" | 
|  | 22 |  | 
| Kevin Lubick | 208e11a | 2023-05-31 19:34:46 +0000 | [diff] [blame] | 23 | #include <include/gpu/ganesh/SkSurfaceGanesh.h> | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 24 | #include <GrBackendSemaphore.h> | 
|  | 25 | #include <GrContextOptions.h> | 
|  | 26 | #include <SkBlendMode.h> | 
|  | 27 | #include <SkCanvas.h> | 
|  | 28 | #include <SkColor.h> | 
|  | 29 | #include <SkColorFilter.h> | 
|  | 30 | #include <SkColorMatrix.h> | 
|  | 31 | #include <SkColorSpace.h> | 
|  | 32 | #include <SkData.h> | 
|  | 33 | #include <SkGraphics.h> | 
|  | 34 | #include <SkImage.h> | 
|  | 35 | #include <SkImageFilters.h> | 
|  | 36 | #include <SkImageInfo.h> | 
|  | 37 | #include <SkM44.h> | 
|  | 38 | #include <SkMatrix.h> | 
|  | 39 | #include <SkPaint.h> | 
|  | 40 | #include <SkPath.h> | 
|  | 41 | #include <SkPoint.h> | 
|  | 42 | #include <SkPoint3.h> | 
| Alec Mouri | 0e7d8fd | 2023-05-03 23:58:43 +0000 | [diff] [blame] | 43 | #include <SkRRect.h> | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 44 | #include <SkRect.h> | 
|  | 45 | #include <SkRefCnt.h> | 
|  | 46 | #include <SkRegion.h> | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 47 | #include <SkRuntimeEffect.h> | 
|  | 48 | #include <SkSamplingOptions.h> | 
|  | 49 | #include <SkScalar.h> | 
|  | 50 | #include <SkShader.h> | 
|  | 51 | #include <SkShadowUtils.h> | 
|  | 52 | #include <SkString.h> | 
|  | 53 | #include <SkSurface.h> | 
|  | 54 | #include <SkTileMode.h> | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 55 | #include <android-base/stringprintf.h> | 
| Alec Mouri | 0e7d8fd | 2023-05-03 23:58:43 +0000 | [diff] [blame] | 56 | #include <gui/FenceMonitor.h> | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 57 | #include <gui/TraceUtils.h> | 
| Alec Mouri | 0e7d8fd | 2023-05-03 23:58:43 +0000 | [diff] [blame] | 58 | #include <pthread.h> | 
|  | 59 | #include <src/core/SkTraceEventCommon.h> | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 60 | #include <sync/sync.h> | 
|  | 61 | #include <ui/BlurRegion.h> | 
|  | 62 | #include <ui/DataspaceUtils.h> | 
|  | 63 | #include <ui/DebugUtils.h> | 
|  | 64 | #include <ui/GraphicBuffer.h> | 
|  | 65 | #include <utils/Trace.h> | 
|  | 66 |  | 
|  | 67 | #include <cmath> | 
|  | 68 | #include <cstdint> | 
| Alec Mouri | 0e7d8fd | 2023-05-03 23:58:43 +0000 | [diff] [blame] | 69 | #include <deque> | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 70 | #include <memory> | 
|  | 71 | #include <numeric> | 
|  | 72 |  | 
|  | 73 | #include "Cache.h" | 
|  | 74 | #include "ColorSpaces.h" | 
|  | 75 | #include "filters/BlurFilter.h" | 
|  | 76 | #include "filters/GaussianBlurFilter.h" | 
|  | 77 | #include "filters/KawaseBlurFilter.h" | 
|  | 78 | #include "filters/LinearEffect.h" | 
|  | 79 | #include "log/log_main.h" | 
|  | 80 | #include "skia/debug/SkiaCapture.h" | 
|  | 81 | #include "skia/debug/SkiaMemoryReporter.h" | 
|  | 82 | #include "skia/filters/StretchShaderFactory.h" | 
|  | 83 | #include "system/graphics-base-v1.0.h" | 
|  | 84 |  | 
|  | 85 | namespace { | 
|  | 86 |  | 
|  | 87 | // Debugging settings | 
|  | 88 | static const bool kPrintLayerSettings = false; | 
|  | 89 | static const bool kFlushAfterEveryLayer = kPrintLayerSettings; | 
| John Reck | 5b02fc4 | 2023-06-14 14:41:10 -0400 | [diff] [blame] | 90 | static constexpr bool kEnableLayerBrightening = true; | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 91 |  | 
|  | 92 | } // namespace | 
|  | 93 |  | 
|  | 94 | // Utility functions related to SkRect | 
|  | 95 |  | 
|  | 96 | namespace { | 
|  | 97 |  | 
|  | 98 | static inline SkRect getSkRect(const android::FloatRect& rect) { | 
|  | 99 | return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | static inline SkRect getSkRect(const android::Rect& rect) { | 
|  | 103 | return SkRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom); | 
|  | 104 | } | 
|  | 105 |  | 
|  | 106 | /** | 
|  | 107 | *  Verifies that common, simple bounds + clip combinations can be converted into | 
|  | 108 | *  a single RRect draw call returning true if possible. If true the radii parameter | 
|  | 109 | *  will be filled with the correct radii values that combined with bounds param will | 
|  | 110 | *  produce the insected roundRect. If false, the returned state of the radii param is undefined. | 
|  | 111 | */ | 
|  | 112 | static bool intersectionIsRoundRect(const SkRect& bounds, const SkRect& crop, | 
|  | 113 | const SkRect& insetCrop, const android::vec2& cornerRadius, | 
|  | 114 | SkVector radii[4]) { | 
|  | 115 | const bool leftEqual = bounds.fLeft == crop.fLeft; | 
|  | 116 | const bool topEqual = bounds.fTop == crop.fTop; | 
|  | 117 | const bool rightEqual = bounds.fRight == crop.fRight; | 
|  | 118 | const bool bottomEqual = bounds.fBottom == crop.fBottom; | 
|  | 119 |  | 
|  | 120 | // In the event that the corners of the bounds only partially align with the crop we | 
|  | 121 | // need to ensure that the resulting shape can still be represented as a round rect. | 
|  | 122 | // In particular the round rect implementation will scale the value of all corner radii | 
|  | 123 | // if the sum of the radius along any edge is greater than the length of that edge. | 
|  | 124 | // See https://www.w3.org/TR/css-backgrounds-3/#corner-overlap | 
|  | 125 | const bool requiredWidth = bounds.width() > (cornerRadius.x * 2); | 
|  | 126 | const bool requiredHeight = bounds.height() > (cornerRadius.y * 2); | 
|  | 127 | if (!requiredWidth || !requiredHeight) { | 
|  | 128 | return false; | 
|  | 129 | } | 
|  | 130 |  | 
|  | 131 | // Check each cropped corner to ensure that it exactly matches the crop or its corner is | 
|  | 132 | // contained within the cropped shape and does not need rounded. | 
|  | 133 | // compute the UpperLeft corner radius | 
|  | 134 | if (leftEqual && topEqual) { | 
|  | 135 | radii[0].set(cornerRadius.x, cornerRadius.y); | 
|  | 136 | } else if ((leftEqual && bounds.fTop >= insetCrop.fTop) || | 
|  | 137 | (topEqual && bounds.fLeft >= insetCrop.fLeft)) { | 
|  | 138 | radii[0].set(0, 0); | 
|  | 139 | } else { | 
|  | 140 | return false; | 
|  | 141 | } | 
|  | 142 | // compute the UpperRight corner radius | 
|  | 143 | if (rightEqual && topEqual) { | 
|  | 144 | radii[1].set(cornerRadius.x, cornerRadius.y); | 
|  | 145 | } else if ((rightEqual && bounds.fTop >= insetCrop.fTop) || | 
|  | 146 | (topEqual && bounds.fRight <= insetCrop.fRight)) { | 
|  | 147 | radii[1].set(0, 0); | 
|  | 148 | } else { | 
|  | 149 | return false; | 
|  | 150 | } | 
|  | 151 | // compute the BottomRight corner radius | 
|  | 152 | if (rightEqual && bottomEqual) { | 
|  | 153 | radii[2].set(cornerRadius.x, cornerRadius.y); | 
|  | 154 | } else if ((rightEqual && bounds.fBottom <= insetCrop.fBottom) || | 
|  | 155 | (bottomEqual && bounds.fRight <= insetCrop.fRight)) { | 
|  | 156 | radii[2].set(0, 0); | 
|  | 157 | } else { | 
|  | 158 | return false; | 
|  | 159 | } | 
|  | 160 | // compute the BottomLeft corner radius | 
|  | 161 | if (leftEqual && bottomEqual) { | 
|  | 162 | radii[3].set(cornerRadius.x, cornerRadius.y); | 
|  | 163 | } else if ((leftEqual && bounds.fBottom <= insetCrop.fBottom) || | 
|  | 164 | (bottomEqual && bounds.fLeft >= insetCrop.fLeft)) { | 
|  | 165 | radii[3].set(0, 0); | 
|  | 166 | } else { | 
|  | 167 | return false; | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | return true; | 
|  | 171 | } | 
|  | 172 |  | 
|  | 173 | static inline std::pair<SkRRect, SkRRect> getBoundsAndClip(const android::FloatRect& boundsRect, | 
|  | 174 | const android::FloatRect& cropRect, | 
|  | 175 | const android::vec2& cornerRadius) { | 
|  | 176 | const SkRect bounds = getSkRect(boundsRect); | 
|  | 177 | const SkRect crop = getSkRect(cropRect); | 
|  | 178 |  | 
|  | 179 | SkRRect clip; | 
|  | 180 | if (cornerRadius.x > 0 && cornerRadius.y > 0) { | 
|  | 181 | // it the crop and the bounds are equivalent or there is no crop then we don't need a clip | 
|  | 182 | if (bounds == crop || crop.isEmpty()) { | 
|  | 183 | return {SkRRect::MakeRectXY(bounds, cornerRadius.x, cornerRadius.y), clip}; | 
|  | 184 | } | 
|  | 185 |  | 
|  | 186 | // This makes an effort to speed up common, simple bounds + clip combinations by | 
|  | 187 | // converting them to a single RRect draw. It is possible there are other cases | 
|  | 188 | // that can be converted. | 
|  | 189 | if (crop.contains(bounds)) { | 
|  | 190 | const auto insetCrop = crop.makeInset(cornerRadius.x, cornerRadius.y); | 
|  | 191 | if (insetCrop.contains(bounds)) { | 
|  | 192 | return {SkRRect::MakeRect(bounds), clip}; // clip is empty - no rounding required | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | SkVector radii[4]; | 
|  | 196 | if (intersectionIsRoundRect(bounds, crop, insetCrop, cornerRadius, radii)) { | 
|  | 197 | SkRRect intersectionBounds; | 
|  | 198 | intersectionBounds.setRectRadii(bounds, radii); | 
|  | 199 | return {intersectionBounds, clip}; | 
|  | 200 | } | 
|  | 201 | } | 
|  | 202 |  | 
|  | 203 | // we didn't hit any of our fast paths so set the clip to the cropRect | 
|  | 204 | clip.setRectXY(crop, cornerRadius.x, cornerRadius.y); | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 | // if we hit this point then we either don't have rounded corners or we are going to rely | 
|  | 208 | // on the clip to round the corners for us | 
|  | 209 | return {SkRRect::MakeRect(bounds), clip}; | 
|  | 210 | } | 
|  | 211 |  | 
|  | 212 | static inline bool layerHasBlur(const android::renderengine::LayerSettings& layer, | 
|  | 213 | bool colorTransformModifiesAlpha) { | 
|  | 214 | if (layer.backgroundBlurRadius > 0 || layer.blurRegions.size()) { | 
|  | 215 | // return false if the content is opaque and would therefore occlude the blur | 
|  | 216 | const bool opaqueContent = !layer.source.buffer.buffer || layer.source.buffer.isOpaque; | 
|  | 217 | const bool opaqueAlpha = layer.alpha == 1.0f && !colorTransformModifiesAlpha; | 
|  | 218 | return layer.skipContentDraw || !(opaqueContent && opaqueAlpha); | 
|  | 219 | } | 
|  | 220 | return false; | 
|  | 221 | } | 
|  | 222 |  | 
|  | 223 | static inline SkColor getSkColor(const android::vec4& color) { | 
|  | 224 | return SkColorSetARGB(color.a * 255, color.r * 255, color.g * 255, color.b * 255); | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | static inline SkM44 getSkM44(const android::mat4& matrix) { | 
|  | 228 | return SkM44(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], | 
|  | 229 | matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1], | 
|  | 230 | matrix[0][2], matrix[1][2], matrix[2][2], matrix[3][2], | 
|  | 231 | matrix[0][3], matrix[1][3], matrix[2][3], matrix[3][3]); | 
|  | 232 | } | 
|  | 233 |  | 
|  | 234 | static inline SkPoint3 getSkPoint3(const android::vec3& vector) { | 
|  | 235 | return SkPoint3::Make(vector.x, vector.y, vector.z); | 
|  | 236 | } | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 237 | } // namespace | 
| rnlee | c6a7364 | 2021-06-04 14:16:42 -0700 | [diff] [blame] | 238 |  | 
| John Reck | 67b1e2b | 2020-08-26 13:17:24 -0700 | [diff] [blame] | 239 | namespace android { | 
|  | 240 | namespace renderengine { | 
| rnlee | c6a7364 | 2021-06-04 14:16:42 -0700 | [diff] [blame] | 241 | namespace skia { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 242 |  | 
|  | 243 | using base::StringAppendF; | 
|  | 244 |  | 
|  | 245 | std::future<void> SkiaRenderEngine::primeCache() { | 
|  | 246 | Cache::primeShaderCache(this); | 
|  | 247 | return {}; | 
|  | 248 | } | 
|  | 249 |  | 
|  | 250 | sk_sp<SkData> SkiaRenderEngine::SkSLCacheMonitor::load(const SkData& key) { | 
|  | 251 | // This "cache" does not actually cache anything. It just allows us to | 
|  | 252 | // monitor Skia's internal cache. So this method always returns null. | 
|  | 253 | return nullptr; | 
|  | 254 | } | 
|  | 255 |  | 
|  | 256 | void SkiaRenderEngine::SkSLCacheMonitor::store(const SkData& key, const SkData& data, | 
|  | 257 | const SkString& description) { | 
|  | 258 | mShadersCachedSinceLastCall++; | 
|  | 259 | mTotalShadersCompiled++; | 
|  | 260 | ATRACE_FORMAT("SF cache: %i shaders", mTotalShadersCompiled); | 
|  | 261 | } | 
|  | 262 |  | 
|  | 263 | int SkiaRenderEngine::reportShadersCompiled() { | 
|  | 264 | return mSkSLCacheMonitor.totalShadersCompiled(); | 
|  | 265 | } | 
| Leon Scroggins III | a37ca99 | 2022-02-02 18:08:20 -0500 | [diff] [blame] | 266 |  | 
|  | 267 | void SkiaRenderEngine::setEnableTracing(bool tracingEnabled) { | 
|  | 268 | SkAndroidFrameworkTraceUtil::setEnableTracing(tracingEnabled); | 
| rnlee | c6a7364 | 2021-06-04 14:16:42 -0700 | [diff] [blame] | 269 | } | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 270 |  | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 271 | SkiaRenderEngine::SkiaRenderEngine(RenderEngineType type, PixelFormat pixelFormat, | 
|  | 272 | bool useColorManagement, bool supportsBackgroundBlur) | 
|  | 273 | : RenderEngine(type), | 
|  | 274 | mDefaultPixelFormat(pixelFormat), | 
|  | 275 | mUseColorManagement(useColorManagement) { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 276 | if (supportsBackgroundBlur) { | 
|  | 277 | ALOGD("Background Blurs Enabled"); | 
|  | 278 | mBlurFilter = new KawaseBlurFilter(); | 
|  | 279 | } | 
|  | 280 | mCapture = std::make_unique<SkiaCapture>(); | 
|  | 281 | } | 
|  | 282 |  | 
|  | 283 | SkiaRenderEngine::~SkiaRenderEngine() { } | 
|  | 284 |  | 
|  | 285 | // To be called from backend dtors. | 
|  | 286 | void SkiaRenderEngine::finishRenderingAndAbandonContext() { | 
|  | 287 | std::lock_guard<std::mutex> lock(mRenderingMutex); | 
|  | 288 |  | 
|  | 289 | if (mBlurFilter) { | 
|  | 290 | delete mBlurFilter; | 
|  | 291 | } | 
|  | 292 |  | 
|  | 293 | if (mGrContext) { | 
|  | 294 | mGrContext->flushAndSubmit(true); | 
|  | 295 | mGrContext->abandonContext(); | 
|  | 296 | } | 
|  | 297 |  | 
|  | 298 | if (mProtectedGrContext) { | 
|  | 299 | mProtectedGrContext->flushAndSubmit(true); | 
|  | 300 | mProtectedGrContext->abandonContext(); | 
|  | 301 | } | 
|  | 302 | } | 
|  | 303 |  | 
|  | 304 | void SkiaRenderEngine::useProtectedContext(bool useProtectedContext) { | 
|  | 305 | if (useProtectedContext == mInProtectedContext || | 
|  | 306 | (useProtectedContext && !supportsProtectedContent())) { | 
|  | 307 | return; | 
|  | 308 | } | 
|  | 309 |  | 
|  | 310 | // release any scratch resources before switching into a new mode | 
|  | 311 | if (getActiveGrContext()) { | 
|  | 312 | getActiveGrContext()->purgeUnlockedResources(true); | 
|  | 313 | } | 
|  | 314 |  | 
|  | 315 | // Backend-specific way to switch to protected context | 
|  | 316 | if (useProtectedContextImpl( | 
|  | 317 | useProtectedContext ? GrProtected::kYes : GrProtected::kNo)) { | 
|  | 318 | mInProtectedContext = useProtectedContext; | 
|  | 319 | // given that we are sharing the same thread between two GrContexts we need to | 
|  | 320 | // make sure that the thread state is reset when switching between the two. | 
|  | 321 | if (getActiveGrContext()) { | 
|  | 322 | getActiveGrContext()->resetContext(); | 
|  | 323 | } | 
|  | 324 | } | 
|  | 325 | } | 
|  | 326 |  | 
|  | 327 | GrDirectContext* SkiaRenderEngine::getActiveGrContext() { | 
|  | 328 | return mInProtectedContext ? mProtectedGrContext.get() : mGrContext.get(); | 
|  | 329 | } | 
|  | 330 |  | 
|  | 331 | static float toDegrees(uint32_t transform) { | 
|  | 332 | switch (transform) { | 
|  | 333 | case ui::Transform::ROT_90: | 
|  | 334 | return 90.0; | 
|  | 335 | case ui::Transform::ROT_180: | 
|  | 336 | return 180.0; | 
|  | 337 | case ui::Transform::ROT_270: | 
|  | 338 | return 270.0; | 
|  | 339 | default: | 
|  | 340 | return 0.0; | 
|  | 341 | } | 
|  | 342 | } | 
|  | 343 |  | 
|  | 344 | static SkColorMatrix toSkColorMatrix(const android::mat4& matrix) { | 
|  | 345 | return SkColorMatrix(matrix[0][0], matrix[1][0], matrix[2][0], matrix[3][0], 0, matrix[0][1], | 
|  | 346 | matrix[1][1], matrix[2][1], matrix[3][1], 0, matrix[0][2], matrix[1][2], | 
|  | 347 | matrix[2][2], matrix[3][2], 0, matrix[0][3], matrix[1][3], matrix[2][3], | 
|  | 348 | matrix[3][3], 0); | 
|  | 349 | } | 
|  | 350 |  | 
|  | 351 | static bool needsToneMapping(ui::Dataspace sourceDataspace, ui::Dataspace destinationDataspace) { | 
|  | 352 | int64_t sourceTransfer = sourceDataspace & HAL_DATASPACE_TRANSFER_MASK; | 
|  | 353 | int64_t destTransfer = destinationDataspace & HAL_DATASPACE_TRANSFER_MASK; | 
|  | 354 |  | 
|  | 355 | // Treat unsupported dataspaces as srgb | 
|  | 356 | if (destTransfer != HAL_DATASPACE_TRANSFER_LINEAR && | 
|  | 357 | destTransfer != HAL_DATASPACE_TRANSFER_HLG && | 
|  | 358 | destTransfer != HAL_DATASPACE_TRANSFER_ST2084) { | 
|  | 359 | destTransfer = HAL_DATASPACE_TRANSFER_SRGB; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | if (sourceTransfer != HAL_DATASPACE_TRANSFER_LINEAR && | 
|  | 363 | sourceTransfer != HAL_DATASPACE_TRANSFER_HLG && | 
|  | 364 | sourceTransfer != HAL_DATASPACE_TRANSFER_ST2084) { | 
|  | 365 | sourceTransfer = HAL_DATASPACE_TRANSFER_SRGB; | 
|  | 366 | } | 
|  | 367 |  | 
|  | 368 | const bool isSourceLinear = sourceTransfer == HAL_DATASPACE_TRANSFER_LINEAR; | 
|  | 369 | const bool isSourceSRGB = sourceTransfer == HAL_DATASPACE_TRANSFER_SRGB; | 
|  | 370 | const bool isDestLinear = destTransfer == HAL_DATASPACE_TRANSFER_LINEAR; | 
|  | 371 | const bool isDestSRGB = destTransfer == HAL_DATASPACE_TRANSFER_SRGB; | 
|  | 372 |  | 
|  | 373 | return !(isSourceLinear && isDestSRGB) && !(isSourceSRGB && isDestLinear) && | 
|  | 374 | sourceTransfer != destTransfer; | 
|  | 375 | } | 
|  | 376 |  | 
|  | 377 | void SkiaRenderEngine::ensureGrContextsCreated() { | 
|  | 378 | if (mGrContext) { | 
|  | 379 | return; | 
|  | 380 | } | 
|  | 381 |  | 
|  | 382 | GrContextOptions options; | 
|  | 383 | options.fDisableDriverCorrectnessWorkarounds = true; | 
|  | 384 | options.fDisableDistanceFieldPaths = true; | 
|  | 385 | options.fReducedShaderVariations = true; | 
|  | 386 | options.fPersistentCache = &mSkSLCacheMonitor; | 
|  | 387 | std::tie(mGrContext, mProtectedGrContext) = createDirectContexts(options); | 
|  | 388 | } | 
|  | 389 |  | 
|  | 390 | void SkiaRenderEngine::mapExternalTextureBuffer(const sp<GraphicBuffer>& buffer, | 
|  | 391 | bool isRenderable) { | 
| Ian Elliott | 1f0911e | 2022-09-09 16:31:47 -0600 | [diff] [blame] | 392 | // Only run this if RE is running on its own thread. This | 
|  | 393 | // way the access to GL operations is guaranteed to be happening on the | 
|  | 394 | // same thread. | 
|  | 395 | if (mRenderEngineType != RenderEngineType::SKIA_GL_THREADED && | 
|  | 396 | mRenderEngineType != RenderEngineType::SKIA_VK_THREADED) { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 397 | return; | 
|  | 398 | } | 
| Ian Elliott | 8506e36 | 2023-03-08 12:12:09 -0700 | [diff] [blame] | 399 | // We don't attempt to map a buffer if the buffer contains protected content. In GL this is | 
|  | 400 | // important because GPU resources for protected buffers are much more limited. (In Vk we | 
|  | 401 | // simply match the existing behavior for protected buffers.)  In Vk, we never cache any | 
|  | 402 | // buffers while in a protected context, since Vk cannot share across contexts, and protected | 
|  | 403 | // is less common. | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 404 | const bool isProtectedBuffer = buffer->getUsage() & GRALLOC_USAGE_PROTECTED; | 
| Ian Elliott | 8506e36 | 2023-03-08 12:12:09 -0700 | [diff] [blame] | 405 | if (isProtectedBuffer || | 
|  | 406 | (mRenderEngineType == RenderEngineType::SKIA_VK_THREADED && isProtected())) { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 407 | return; | 
|  | 408 | } | 
|  | 409 | ATRACE_CALL(); | 
|  | 410 |  | 
|  | 411 | // If we were to support caching protected buffers then we will need to switch the | 
|  | 412 | // currently bound context if we are not already using the protected context (and subsequently | 
|  | 413 | // switch back after the buffer is cached).  However, for non-protected content we can bind | 
|  | 414 | // the texture in either GL context because they are initialized with the same share_context | 
|  | 415 | // which allows the texture state to be shared between them. | 
|  | 416 | auto grContext = getActiveGrContext(); | 
|  | 417 | auto& cache = mTextureCache; | 
|  | 418 |  | 
|  | 419 | std::lock_guard<std::mutex> lock(mRenderingMutex); | 
|  | 420 | mGraphicBufferExternalRefs[buffer->getId()]++; | 
|  | 421 |  | 
|  | 422 | if (const auto& iter = cache.find(buffer->getId()); iter == cache.end()) { | 
|  | 423 | std::shared_ptr<AutoBackendTexture::LocalRef> imageTextureRef = | 
|  | 424 | std::make_shared<AutoBackendTexture::LocalRef>(grContext, | 
|  | 425 | buffer->toAHardwareBuffer(), | 
|  | 426 | isRenderable, mTextureCleanupMgr); | 
|  | 427 | cache.insert({buffer->getId(), imageTextureRef}); | 
|  | 428 | } | 
|  | 429 | } | 
|  | 430 |  | 
| Alec Mouri | 92f89fa | 2023-02-24 00:05:06 +0000 | [diff] [blame] | 431 | void SkiaRenderEngine::unmapExternalTextureBuffer(sp<GraphicBuffer>&& buffer) { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 432 | ATRACE_CALL(); | 
|  | 433 | std::lock_guard<std::mutex> lock(mRenderingMutex); | 
|  | 434 | if (const auto& iter = mGraphicBufferExternalRefs.find(buffer->getId()); | 
|  | 435 | iter != mGraphicBufferExternalRefs.end()) { | 
|  | 436 | if (iter->second == 0) { | 
|  | 437 | ALOGW("Attempted to unmap GraphicBuffer <id: %" PRId64 | 
|  | 438 | "> from RenderEngine texture, but the " | 
|  | 439 | "ref count was already zero!", | 
|  | 440 | buffer->getId()); | 
|  | 441 | mGraphicBufferExternalRefs.erase(buffer->getId()); | 
|  | 442 | return; | 
|  | 443 | } | 
|  | 444 |  | 
|  | 445 | iter->second--; | 
|  | 446 |  | 
|  | 447 | // Swap contexts if needed prior to deleting this buffer | 
|  | 448 | // See Issue 1 of | 
|  | 449 | // https://www.khronos.org/registry/EGL/extensions/EXT/EGL_EXT_protected_content.txt: even | 
|  | 450 | // when a protected context and an unprotected context are part of the same share group, | 
|  | 451 | // protected surfaces may not be accessed by an unprotected context, implying that protected | 
|  | 452 | // surfaces may only be freed when a protected context is active. | 
|  | 453 | const bool inProtected = mInProtectedContext; | 
|  | 454 | useProtectedContext(buffer->getUsage() & GRALLOC_USAGE_PROTECTED); | 
|  | 455 |  | 
|  | 456 | if (iter->second == 0) { | 
|  | 457 | mTextureCache.erase(buffer->getId()); | 
|  | 458 | mGraphicBufferExternalRefs.erase(buffer->getId()); | 
|  | 459 | } | 
|  | 460 |  | 
|  | 461 | // Swap back to the previous context so that cached values of isProtected in SurfaceFlinger | 
|  | 462 | // are up-to-date. | 
|  | 463 | if (inProtected != mInProtectedContext) { | 
|  | 464 | useProtectedContext(inProtected); | 
|  | 465 | } | 
|  | 466 | } | 
|  | 467 | } | 
|  | 468 |  | 
| Ian Elliott | 8506e36 | 2023-03-08 12:12:09 -0700 | [diff] [blame] | 469 | std::shared_ptr<AutoBackendTexture::LocalRef> SkiaRenderEngine::getOrCreateBackendTexture( | 
|  | 470 | const sp<GraphicBuffer>& buffer, bool isOutputBuffer) { | 
|  | 471 | // Do not lookup the buffer in the cache for protected contexts with the SkiaVk back-end | 
|  | 472 | if (mRenderEngineType == RenderEngineType::SKIA_GL_THREADED || | 
|  | 473 | (mRenderEngineType == RenderEngineType::SKIA_VK_THREADED && !isProtected())) { | 
|  | 474 | if (const auto& it = mTextureCache.find(buffer->getId()); it != mTextureCache.end()) { | 
|  | 475 | return it->second; | 
|  | 476 | } | 
|  | 477 | } | 
|  | 478 | return std::make_shared<AutoBackendTexture::LocalRef>(getActiveGrContext(), | 
|  | 479 | buffer->toAHardwareBuffer(), | 
|  | 480 | isOutputBuffer, mTextureCleanupMgr); | 
|  | 481 | } | 
|  | 482 |  | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 483 | bool SkiaRenderEngine::canSkipPostRenderCleanup() const { | 
|  | 484 | std::lock_guard<std::mutex> lock(mRenderingMutex); | 
|  | 485 | return mTextureCleanupMgr.isEmpty(); | 
|  | 486 | } | 
|  | 487 |  | 
|  | 488 | void SkiaRenderEngine::cleanupPostRender() { | 
|  | 489 | ATRACE_CALL(); | 
|  | 490 | std::lock_guard<std::mutex> lock(mRenderingMutex); | 
|  | 491 | mTextureCleanupMgr.cleanup(); | 
|  | 492 | } | 
|  | 493 |  | 
|  | 494 | sk_sp<SkShader> SkiaRenderEngine::createRuntimeEffectShader( | 
|  | 495 | const RuntimeEffectShaderParameters& parameters) { | 
|  | 496 | // The given surface will be stretched by HWUI via matrix transformation | 
|  | 497 | // which gets similar results for most surfaces | 
|  | 498 | // Determine later on if we need to leverage the stertch shader within | 
|  | 499 | // surface flinger | 
|  | 500 | const auto& stretchEffect = parameters.layer.stretchEffect; | 
|  | 501 | auto shader = parameters.shader; | 
|  | 502 | if (stretchEffect.hasEffect()) { | 
|  | 503 | const auto targetBuffer = parameters.layer.source.buffer.buffer; | 
|  | 504 | const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; | 
|  | 505 | if (graphicBuffer && parameters.shader) { | 
|  | 506 | shader = mStretchShaderFactory.createSkShader(shader, stretchEffect); | 
|  | 507 | } | 
|  | 508 | } | 
|  | 509 |  | 
|  | 510 | if (parameters.requiresLinearEffect) { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 511 | auto effect = | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 512 | shaders::LinearEffect{.inputDataspace = parameters.layer.sourceDataspace, | 
|  | 513 | .outputDataspace = parameters.outputDataSpace, | 
| Alec Mouri | e0bb6f4 | 2023-08-02 22:41:52 +0000 | [diff] [blame] | 514 | .undoPremultipliedAlpha = parameters.undoPremultipliedAlpha, | 
|  | 515 | .fakeOutputDataspace = parameters.fakeOutputDataspace}; | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 516 |  | 
|  | 517 | auto effectIter = mRuntimeEffects.find(effect); | 
|  | 518 | sk_sp<SkRuntimeEffect> runtimeEffect = nullptr; | 
|  | 519 | if (effectIter == mRuntimeEffects.end()) { | 
|  | 520 | runtimeEffect = buildRuntimeEffect(effect); | 
|  | 521 | mRuntimeEffects.insert({effect, runtimeEffect}); | 
|  | 522 | } else { | 
|  | 523 | runtimeEffect = effectIter->second; | 
|  | 524 | } | 
| Alec Mouri | 3e5965f | 2023-04-07 18:00:58 +0000 | [diff] [blame] | 525 |  | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 526 | mat4 colorTransform = parameters.layer.colorTransform; | 
|  | 527 |  | 
|  | 528 | colorTransform *= | 
|  | 529 | mat4::scale(vec4(parameters.layerDimmingRatio, parameters.layerDimmingRatio, | 
|  | 530 | parameters.layerDimmingRatio, 1.f)); | 
| Alec Mouri | 3e5965f | 2023-04-07 18:00:58 +0000 | [diff] [blame] | 531 |  | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 532 | const auto targetBuffer = parameters.layer.source.buffer.buffer; | 
|  | 533 | const auto graphicBuffer = targetBuffer ? targetBuffer->getBuffer() : nullptr; | 
|  | 534 | const auto hardwareBuffer = graphicBuffer ? graphicBuffer->toAHardwareBuffer() : nullptr; | 
| Alec Mouri | 3e5965f | 2023-04-07 18:00:58 +0000 | [diff] [blame] | 535 | return createLinearEffectShader(parameters.shader, effect, runtimeEffect, | 
|  | 536 | std::move(colorTransform), parameters.display.maxLuminance, | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 537 | parameters.display.currentLuminanceNits, | 
|  | 538 | parameters.layer.source.buffer.maxLuminanceNits, | 
|  | 539 | hardwareBuffer, parameters.display.renderIntent); | 
|  | 540 | } | 
|  | 541 | return parameters.shader; | 
|  | 542 | } | 
|  | 543 |  | 
|  | 544 | void SkiaRenderEngine::initCanvas(SkCanvas* canvas, const DisplaySettings& display) { | 
|  | 545 | if (CC_UNLIKELY(mCapture->isCaptureRunning())) { | 
|  | 546 | // Record display settings when capture is running. | 
|  | 547 | std::stringstream displaySettings; | 
|  | 548 | PrintTo(display, &displaySettings); | 
|  | 549 | // Store the DisplaySettings in additional information. | 
|  | 550 | canvas->drawAnnotation(SkRect::MakeEmpty(), "DisplaySettings", | 
|  | 551 | SkData::MakeWithCString(displaySettings.str().c_str())); | 
|  | 552 | } | 
|  | 553 |  | 
|  | 554 | // Before doing any drawing, let's make sure that we'll start at the origin of the display. | 
|  | 555 | // Some displays don't start at 0,0 for example when we're mirroring the screen. Also, virtual | 
|  | 556 | // displays might have different scaling when compared to the physical screen. | 
|  | 557 |  | 
|  | 558 | canvas->clipRect(getSkRect(display.physicalDisplay)); | 
|  | 559 | canvas->translate(display.physicalDisplay.left, display.physicalDisplay.top); | 
|  | 560 |  | 
|  | 561 | const auto clipWidth = display.clip.width(); | 
|  | 562 | const auto clipHeight = display.clip.height(); | 
|  | 563 | auto rotatedClipWidth = clipWidth; | 
|  | 564 | auto rotatedClipHeight = clipHeight; | 
|  | 565 | // Scale is contingent on the rotation result. | 
|  | 566 | if (display.orientation & ui::Transform::ROT_90) { | 
|  | 567 | std::swap(rotatedClipWidth, rotatedClipHeight); | 
|  | 568 | } | 
|  | 569 | const auto scaleX = static_cast<SkScalar>(display.physicalDisplay.width()) / | 
|  | 570 | static_cast<SkScalar>(rotatedClipWidth); | 
|  | 571 | const auto scaleY = static_cast<SkScalar>(display.physicalDisplay.height()) / | 
|  | 572 | static_cast<SkScalar>(rotatedClipHeight); | 
|  | 573 | canvas->scale(scaleX, scaleY); | 
|  | 574 |  | 
|  | 575 | // Canvas rotation is done by centering the clip window at the origin, rotating, translating | 
|  | 576 | // back so that the top left corner of the clip is at (0, 0). | 
|  | 577 | canvas->translate(rotatedClipWidth / 2, rotatedClipHeight / 2); | 
|  | 578 | canvas->rotate(toDegrees(display.orientation)); | 
|  | 579 | canvas->translate(-clipWidth / 2, -clipHeight / 2); | 
|  | 580 | canvas->translate(-display.clip.left, -display.clip.top); | 
|  | 581 | } | 
|  | 582 |  | 
|  | 583 | class AutoSaveRestore { | 
|  | 584 | public: | 
|  | 585 | AutoSaveRestore(SkCanvas* canvas) : mCanvas(canvas) { mSaveCount = canvas->save(); } | 
|  | 586 | ~AutoSaveRestore() { restore(); } | 
|  | 587 | void replace(SkCanvas* canvas) { | 
|  | 588 | mCanvas = canvas; | 
|  | 589 | mSaveCount = canvas->save(); | 
|  | 590 | } | 
|  | 591 | void restore() { | 
|  | 592 | if (mCanvas) { | 
|  | 593 | mCanvas->restoreToCount(mSaveCount); | 
|  | 594 | mCanvas = nullptr; | 
|  | 595 | } | 
|  | 596 | } | 
|  | 597 |  | 
|  | 598 | private: | 
|  | 599 | SkCanvas* mCanvas; | 
|  | 600 | int mSaveCount; | 
|  | 601 | }; | 
|  | 602 |  | 
|  | 603 | static SkRRect getBlurRRect(const BlurRegion& region) { | 
|  | 604 | const auto rect = SkRect::MakeLTRB(region.left, region.top, region.right, region.bottom); | 
|  | 605 | const SkVector radii[4] = {SkVector::Make(region.cornerRadiusTL, region.cornerRadiusTL), | 
|  | 606 | SkVector::Make(region.cornerRadiusTR, region.cornerRadiusTR), | 
|  | 607 | SkVector::Make(region.cornerRadiusBR, region.cornerRadiusBR), | 
|  | 608 | SkVector::Make(region.cornerRadiusBL, region.cornerRadiusBL)}; | 
|  | 609 | SkRRect roundedRect; | 
|  | 610 | roundedRect.setRectRadii(rect, radii); | 
|  | 611 | return roundedRect; | 
|  | 612 | } | 
|  | 613 |  | 
|  | 614 | // Arbitrary default margin which should be close enough to zero. | 
|  | 615 | constexpr float kDefaultMargin = 0.0001f; | 
|  | 616 | static bool equalsWithinMargin(float expected, float value, float margin = kDefaultMargin) { | 
|  | 617 | LOG_ALWAYS_FATAL_IF(margin < 0.f, "Margin is negative!"); | 
|  | 618 | return std::abs(expected - value) < margin; | 
|  | 619 | } | 
|  | 620 |  | 
|  | 621 | namespace { | 
|  | 622 | template <typename T> | 
|  | 623 | void logSettings(const T& t) { | 
|  | 624 | std::stringstream stream; | 
|  | 625 | PrintTo(t, &stream); | 
|  | 626 | auto string = stream.str(); | 
|  | 627 | size_t pos = 0; | 
|  | 628 | // Perfetto ignores \n, so split up manually into separate ALOGD statements. | 
|  | 629 | const size_t size = string.size(); | 
|  | 630 | while (pos < size) { | 
|  | 631 | const size_t end = std::min(string.find("\n", pos), size); | 
|  | 632 | ALOGD("%s", string.substr(pos, end - pos).c_str()); | 
|  | 633 | pos = end + 1; | 
|  | 634 | } | 
|  | 635 | } | 
|  | 636 | } // namespace | 
|  | 637 |  | 
|  | 638 | // Helper class intended to be used on the stack to ensure that texture cleanup | 
|  | 639 | // is deferred until after this class goes out of scope. | 
|  | 640 | class DeferTextureCleanup final { | 
|  | 641 | public: | 
|  | 642 | DeferTextureCleanup(AutoBackendTexture::CleanupManager& mgr) : mMgr(mgr) { | 
|  | 643 | mMgr.setDeferredStatus(true); | 
|  | 644 | } | 
|  | 645 | ~DeferTextureCleanup() { mMgr.setDeferredStatus(false); } | 
|  | 646 |  | 
|  | 647 | private: | 
|  | 648 | DISALLOW_COPY_AND_ASSIGN(DeferTextureCleanup); | 
|  | 649 | AutoBackendTexture::CleanupManager& mMgr; | 
|  | 650 | }; | 
|  | 651 |  | 
|  | 652 | void SkiaRenderEngine::drawLayersInternal( | 
| Patrick Williams | 2e9748f | 2022-08-09 22:48:18 +0000 | [diff] [blame] | 653 | const std::shared_ptr<std::promise<FenceResult>>&& resultPromise, | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 654 | const DisplaySettings& display, const std::vector<LayerSettings>& layers, | 
|  | 655 | const std::shared_ptr<ExternalTexture>& buffer, const bool /*useFramebufferCache*/, | 
|  | 656 | base::unique_fd&& bufferFence) { | 
| Leon Scroggins III | 5a655b8 | 2022-09-07 13:17:09 -0400 | [diff] [blame] | 657 | ATRACE_FORMAT("%s for %s", __func__, display.namePlusId.c_str()); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 658 |  | 
|  | 659 | std::lock_guard<std::mutex> lock(mRenderingMutex); | 
|  | 660 |  | 
|  | 661 | if (buffer == nullptr) { | 
|  | 662 | ALOGE("No output buffer provided. Aborting GPU composition."); | 
| Patrick Williams | 2e9748f | 2022-08-09 22:48:18 +0000 | [diff] [blame] | 663 | resultPromise->set_value(base::unexpected(BAD_VALUE)); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 664 | return; | 
|  | 665 | } | 
|  | 666 |  | 
|  | 667 | validateOutputBufferUsage(buffer->getBuffer()); | 
|  | 668 |  | 
|  | 669 | auto grContext = getActiveGrContext(); | 
| Leon Scroggins III | 41c00c5 | 2023-06-21 15:55:55 -0400 | [diff] [blame] | 670 | LOG_ALWAYS_FATAL_IF(grContext->abandoned(), "GrContext is abandoned/device lost at start of %s", | 
|  | 671 | __func__); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 672 |  | 
|  | 673 | // any AutoBackendTexture deletions will now be deferred until cleanupPostRender is called | 
|  | 674 | DeferTextureCleanup dtc(mTextureCleanupMgr); | 
|  | 675 |  | 
| Ian Elliott | 8506e36 | 2023-03-08 12:12:09 -0700 | [diff] [blame] | 676 | auto surfaceTextureRef = getOrCreateBackendTexture(buffer->getBuffer(), true); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 677 |  | 
|  | 678 | // wait on the buffer to be ready to use prior to using it | 
|  | 679 | waitFence(grContext, bufferFence); | 
|  | 680 |  | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 681 | sk_sp<SkSurface> dstSurface = | 
|  | 682 | surfaceTextureRef->getOrCreateSurface(display.outputDataspace, grContext); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 683 |  | 
|  | 684 | SkCanvas* dstCanvas = mCapture->tryCapture(dstSurface.get()); | 
|  | 685 | if (dstCanvas == nullptr) { | 
|  | 686 | ALOGE("Cannot acquire canvas from Skia."); | 
| Patrick Williams | 2e9748f | 2022-08-09 22:48:18 +0000 | [diff] [blame] | 687 | resultPromise->set_value(base::unexpected(BAD_VALUE)); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 688 | return; | 
|  | 689 | } | 
|  | 690 |  | 
|  | 691 | // setup color filter if necessary | 
|  | 692 | sk_sp<SkColorFilter> displayColorTransform; | 
|  | 693 | if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { | 
|  | 694 | displayColorTransform = SkColorFilters::Matrix(toSkColorMatrix(display.colorTransform)); | 
|  | 695 | } | 
|  | 696 | const bool ctModifiesAlpha = | 
|  | 697 | displayColorTransform && !displayColorTransform->isAlphaUnchanged(); | 
|  | 698 |  | 
|  | 699 | // Find the max layer white point to determine the max luminance of the scene... | 
|  | 700 | const float maxLayerWhitePoint = std::transform_reduce( | 
|  | 701 | layers.cbegin(), layers.cend(), 0.f, | 
|  | 702 | [](float left, float right) { return std::max(left, right); }, | 
|  | 703 | [&](const auto& l) { return l.whitePointNits; }); | 
|  | 704 |  | 
|  | 705 | // ...and compute the dimming ratio if dimming is requested | 
|  | 706 | const float displayDimmingRatio = display.targetLuminanceNits > 0.f && | 
| John Reck | 5b02fc4 | 2023-06-14 14:41:10 -0400 | [diff] [blame] | 707 | maxLayerWhitePoint > 0.f && | 
|  | 708 | (kEnableLayerBrightening || display.targetLuminanceNits > maxLayerWhitePoint) | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 709 | ? maxLayerWhitePoint / display.targetLuminanceNits | 
|  | 710 | : 1.f; | 
|  | 711 |  | 
|  | 712 | // Find if any layers have requested blur, we'll use that info to decide when to render to an | 
|  | 713 | // offscreen buffer and when to render to the native buffer. | 
|  | 714 | sk_sp<SkSurface> activeSurface(dstSurface); | 
|  | 715 | SkCanvas* canvas = dstCanvas; | 
|  | 716 | SkiaCapture::OffscreenState offscreenCaptureState; | 
|  | 717 | const LayerSettings* blurCompositionLayer = nullptr; | 
| Leon Scroggins III | 4623eeb | 2023-07-19 15:16:15 -0400 | [diff] [blame] | 718 |  | 
|  | 719 | // TODO (b/270314344): Enable blurs in protected context. | 
|  | 720 | if (mBlurFilter && !mInProtectedContext) { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 721 | bool requiresCompositionLayer = false; | 
|  | 722 | for (const auto& layer : layers) { | 
|  | 723 | // if the layer doesn't have blur or it is not visible then continue | 
|  | 724 | if (!layerHasBlur(layer, ctModifiesAlpha)) { | 
|  | 725 | continue; | 
|  | 726 | } | 
|  | 727 | if (layer.backgroundBlurRadius > 0 && | 
|  | 728 | layer.backgroundBlurRadius < mBlurFilter->getMaxCrossFadeRadius()) { | 
|  | 729 | requiresCompositionLayer = true; | 
|  | 730 | } | 
|  | 731 | for (auto region : layer.blurRegions) { | 
|  | 732 | if (region.blurRadius < mBlurFilter->getMaxCrossFadeRadius()) { | 
|  | 733 | requiresCompositionLayer = true; | 
|  | 734 | } | 
|  | 735 | } | 
|  | 736 | if (requiresCompositionLayer) { | 
|  | 737 | activeSurface = dstSurface->makeSurface(dstSurface->imageInfo()); | 
|  | 738 | canvas = mCapture->tryOffscreenCapture(activeSurface.get(), &offscreenCaptureState); | 
|  | 739 | blurCompositionLayer = &layer; | 
|  | 740 | break; | 
|  | 741 | } | 
|  | 742 | } | 
|  | 743 | } | 
|  | 744 |  | 
|  | 745 | AutoSaveRestore surfaceAutoSaveRestore(canvas); | 
|  | 746 | // Clear the entire canvas with a transparent black to prevent ghost images. | 
|  | 747 | canvas->clear(SK_ColorTRANSPARENT); | 
|  | 748 | initCanvas(canvas, display); | 
|  | 749 |  | 
|  | 750 | if (kPrintLayerSettings) { | 
|  | 751 | logSettings(display); | 
|  | 752 | } | 
|  | 753 | for (const auto& layer : layers) { | 
|  | 754 | ATRACE_FORMAT("DrawLayer: %s", layer.name.c_str()); | 
|  | 755 |  | 
|  | 756 | if (kPrintLayerSettings) { | 
|  | 757 | logSettings(layer); | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | sk_sp<SkImage> blurInput; | 
|  | 761 | if (blurCompositionLayer == &layer) { | 
|  | 762 | LOG_ALWAYS_FATAL_IF(activeSurface == dstSurface); | 
|  | 763 | LOG_ALWAYS_FATAL_IF(canvas == dstCanvas); | 
|  | 764 |  | 
|  | 765 | // save a snapshot of the activeSurface to use as input to the blur shaders | 
|  | 766 | blurInput = activeSurface->makeImageSnapshot(); | 
|  | 767 |  | 
|  | 768 | // blit the offscreen framebuffer into the destination AHB, but only | 
|  | 769 | // if there are blur regions. backgroundBlurRadius blurs the entire | 
|  | 770 | // image below, so it can skip this step. | 
|  | 771 | if (layer.blurRegions.size()) { | 
|  | 772 | SkPaint paint; | 
|  | 773 | paint.setBlendMode(SkBlendMode::kSrc); | 
|  | 774 | if (CC_UNLIKELY(mCapture->isCaptureRunning())) { | 
|  | 775 | uint64_t id = mCapture->endOffscreenCapture(&offscreenCaptureState); | 
|  | 776 | dstCanvas->drawAnnotation(SkRect::Make(dstCanvas->imageInfo().dimensions()), | 
|  | 777 | String8::format("SurfaceID|%" PRId64, id).c_str(), | 
|  | 778 | nullptr); | 
|  | 779 | dstCanvas->drawImage(blurInput, 0, 0, SkSamplingOptions(), &paint); | 
|  | 780 | } else { | 
|  | 781 | activeSurface->draw(dstCanvas, 0, 0, SkSamplingOptions(), &paint); | 
|  | 782 | } | 
|  | 783 | } | 
|  | 784 |  | 
|  | 785 | // assign dstCanvas to canvas and ensure that the canvas state is up to date | 
|  | 786 | canvas = dstCanvas; | 
|  | 787 | surfaceAutoSaveRestore.replace(canvas); | 
|  | 788 | initCanvas(canvas, display); | 
|  | 789 |  | 
|  | 790 | LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getSaveCount() != | 
|  | 791 | dstSurface->getCanvas()->getSaveCount()); | 
|  | 792 | LOG_ALWAYS_FATAL_IF(activeSurface->getCanvas()->getTotalMatrix() != | 
|  | 793 | dstSurface->getCanvas()->getTotalMatrix()); | 
|  | 794 |  | 
|  | 795 | // assign dstSurface to activeSurface | 
|  | 796 | activeSurface = dstSurface; | 
|  | 797 | } | 
|  | 798 |  | 
|  | 799 | SkAutoCanvasRestore layerAutoSaveRestore(canvas, true); | 
|  | 800 | if (CC_UNLIKELY(mCapture->isCaptureRunning())) { | 
|  | 801 | // Record the name of the layer if the capture is running. | 
|  | 802 | std::stringstream layerSettings; | 
|  | 803 | PrintTo(layer, &layerSettings); | 
|  | 804 | // Store the LayerSettings in additional information. | 
|  | 805 | canvas->drawAnnotation(SkRect::MakeEmpty(), layer.name.c_str(), | 
|  | 806 | SkData::MakeWithCString(layerSettings.str().c_str())); | 
|  | 807 | } | 
|  | 808 | // Layers have a local transform that should be applied to them | 
|  | 809 | canvas->concat(getSkM44(layer.geometry.positionTransform).asM33()); | 
|  | 810 |  | 
|  | 811 | const auto [bounds, roundRectClip] = | 
|  | 812 | getBoundsAndClip(layer.geometry.boundaries, layer.geometry.roundedCornersCrop, | 
|  | 813 | layer.geometry.roundedCornersRadius); | 
| Leon Scroggins III | 4623eeb | 2023-07-19 15:16:15 -0400 | [diff] [blame] | 814 | // TODO (b/270314344): Enable blurs in protected context. | 
|  | 815 | if (mBlurFilter && layerHasBlur(layer, ctModifiesAlpha) && !mInProtectedContext) { | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 816 | std::unordered_map<uint32_t, sk_sp<SkImage>> cachedBlurs; | 
|  | 817 |  | 
|  | 818 | // if multiple layers have blur, then we need to take a snapshot now because | 
|  | 819 | // only the lowest layer will have blurImage populated earlier | 
|  | 820 | if (!blurInput) { | 
|  | 821 | blurInput = activeSurface->makeImageSnapshot(); | 
|  | 822 | } | 
| Leon Scroggins III | a09cddc | 2023-07-25 09:34:11 -0400 | [diff] [blame] | 823 |  | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 824 | // rect to be blurred in the coordinate space of blurInput | 
| Leon Scroggins III | a09cddc | 2023-07-25 09:34:11 -0400 | [diff] [blame] | 825 | SkRect blurRect = canvas->getTotalMatrix().mapRect(bounds.rect()); | 
|  | 826 |  | 
|  | 827 | // Some layers may be much bigger than the screen. If we used | 
|  | 828 | // `blurRect` directly, this would allocate a large buffer with no | 
|  | 829 | // benefit. Apply the clip, which already takes the display size | 
|  | 830 | // into account. The clipped size will then be used to calculate the | 
|  | 831 | // size of the buffer we will create for blurring. | 
|  | 832 | if (!blurRect.intersect(SkRect::Make(canvas->getDeviceClipBounds()))) { | 
|  | 833 | // This should not happen, but if it did, we would use the full | 
|  | 834 | // sized layer, which should still be fine. | 
|  | 835 | ALOGW("blur bounds does not intersect display clip!"); | 
|  | 836 | } | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 837 |  | 
|  | 838 | // if the clip needs to be applied then apply it now and make sure | 
|  | 839 | // it is restored before we attempt to draw any shadows. | 
|  | 840 | SkAutoCanvasRestore acr(canvas, true); | 
|  | 841 | if (!roundRectClip.isEmpty()) { | 
|  | 842 | canvas->clipRRect(roundRectClip, true); | 
|  | 843 | } | 
|  | 844 |  | 
|  | 845 | // TODO(b/182216890): Filter out empty layers earlier | 
|  | 846 | if (blurRect.width() > 0 && blurRect.height() > 0) { | 
|  | 847 | if (layer.backgroundBlurRadius > 0) { | 
|  | 848 | ATRACE_NAME("BackgroundBlur"); | 
|  | 849 | auto blurredImage = mBlurFilter->generate(grContext, layer.backgroundBlurRadius, | 
|  | 850 | blurInput, blurRect); | 
|  | 851 |  | 
|  | 852 | cachedBlurs[layer.backgroundBlurRadius] = blurredImage; | 
|  | 853 |  | 
|  | 854 | mBlurFilter->drawBlurRegion(canvas, bounds, layer.backgroundBlurRadius, 1.0f, | 
|  | 855 | blurRect, blurredImage, blurInput); | 
|  | 856 | } | 
|  | 857 |  | 
|  | 858 | canvas->concat(getSkM44(layer.blurRegionTransform).asM33()); | 
|  | 859 | for (auto region : layer.blurRegions) { | 
|  | 860 | if (cachedBlurs[region.blurRadius] == nullptr) { | 
|  | 861 | ATRACE_NAME("BlurRegion"); | 
|  | 862 | cachedBlurs[region.blurRadius] = | 
|  | 863 | mBlurFilter->generate(grContext, region.blurRadius, blurInput, | 
|  | 864 | blurRect); | 
|  | 865 | } | 
|  | 866 |  | 
|  | 867 | mBlurFilter->drawBlurRegion(canvas, getBlurRRect(region), region.blurRadius, | 
|  | 868 | region.alpha, blurRect, | 
|  | 869 | cachedBlurs[region.blurRadius], blurInput); | 
|  | 870 | } | 
|  | 871 | } | 
|  | 872 | } | 
|  | 873 |  | 
|  | 874 | if (layer.shadow.length > 0) { | 
|  | 875 | // This would require a new parameter/flag to SkShadowUtils::DrawShadow | 
|  | 876 | LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with a shadow"); | 
|  | 877 |  | 
|  | 878 | SkRRect shadowBounds, shadowClip; | 
|  | 879 | if (layer.geometry.boundaries == layer.shadow.boundaries) { | 
|  | 880 | shadowBounds = bounds; | 
|  | 881 | shadowClip = roundRectClip; | 
|  | 882 | } else { | 
|  | 883 | std::tie(shadowBounds, shadowClip) = | 
|  | 884 | getBoundsAndClip(layer.shadow.boundaries, layer.geometry.roundedCornersCrop, | 
|  | 885 | layer.geometry.roundedCornersRadius); | 
|  | 886 | } | 
|  | 887 |  | 
|  | 888 | // Technically, if bounds is a rect and roundRectClip is not empty, | 
|  | 889 | // it means that the bounds and roundedCornersCrop were different | 
|  | 890 | // enough that we should intersect them to find the proper shadow. | 
|  | 891 | // In practice, this often happens when the two rectangles appear to | 
|  | 892 | // not match due to rounding errors. Draw the rounded version, which | 
|  | 893 | // looks more like the intent. | 
|  | 894 | const auto& rrect = | 
|  | 895 | shadowBounds.isRect() && !shadowClip.isEmpty() ? shadowClip : shadowBounds; | 
|  | 896 | drawShadow(canvas, rrect, layer.shadow); | 
|  | 897 | } | 
|  | 898 |  | 
|  | 899 | const float layerDimmingRatio = layer.whitePointNits <= 0.f | 
|  | 900 | ? displayDimmingRatio | 
|  | 901 | : (layer.whitePointNits / maxLayerWhitePoint) * displayDimmingRatio; | 
|  | 902 |  | 
|  | 903 | const bool dimInLinearSpace = display.dimmingStage != | 
|  | 904 | aidl::android::hardware::graphics::composer3::DimmingStage::GAMMA_OETF; | 
|  | 905 |  | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 906 | const bool isExtendedHdr = (layer.sourceDataspace & ui::Dataspace::RANGE_MASK) == | 
|  | 907 | static_cast<int32_t>(ui::Dataspace::RANGE_EXTENDED) && | 
|  | 908 | (display.outputDataspace & ui::Dataspace::TRANSFER_MASK) == | 
|  | 909 | static_cast<int32_t>(ui::Dataspace::TRANSFER_SRGB); | 
|  | 910 |  | 
| Alec Mouri | e0bb6f4 | 2023-08-02 22:41:52 +0000 | [diff] [blame] | 911 | const bool useFakeOutputDataspaceForRuntimeEffect = !dimInLinearSpace && isExtendedHdr; | 
|  | 912 |  | 
|  | 913 | const ui::Dataspace fakeDataspace = useFakeOutputDataspaceForRuntimeEffect | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 914 | ? static_cast<ui::Dataspace>( | 
|  | 915 | (display.outputDataspace & ui::Dataspace::STANDARD_MASK) | | 
|  | 916 | ui::Dataspace::TRANSFER_GAMMA2_2 | | 
|  | 917 | (display.outputDataspace & ui::Dataspace::RANGE_MASK)) | 
| Alec Mouri | e0bb6f4 | 2023-08-02 22:41:52 +0000 | [diff] [blame] | 918 | : ui::Dataspace::UNKNOWN; | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 919 |  | 
|  | 920 | // If the input dataspace is range extended, the output dataspace transfer is sRGB | 
|  | 921 | // and dimmingStage is GAMMA_OETF, dim in linear space instead, and | 
|  | 922 | // set the output dataspace's transfer to be GAMMA2_2. | 
|  | 923 | // This allows DPU side to use oetf_gamma_2p2 for extended HDR layer | 
|  | 924 | // to avoid tone shift. | 
|  | 925 | // The reason of tone shift here is because HDR layers manage white point | 
|  | 926 | // luminance in linear space, which color pipelines request GAMMA_OETF break | 
|  | 927 | // without a gamma 2.2 fixup. | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 928 | const bool requiresLinearEffect = layer.colorTransform != mat4() || | 
|  | 929 | (mUseColorManagement && | 
|  | 930 | needsToneMapping(layer.sourceDataspace, display.outputDataspace)) || | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 931 | (dimInLinearSpace && !equalsWithinMargin(1.f, layerDimmingRatio)) || | 
|  | 932 | (!dimInLinearSpace && isExtendedHdr); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 933 |  | 
|  | 934 | // quick abort from drawing the remaining portion of the layer | 
|  | 935 | if (layer.skipContentDraw || | 
|  | 936 | (layer.alpha == 0 && !requiresLinearEffect && !layer.disableBlending && | 
|  | 937 | (!displayColorTransform || displayColorTransform->isAlphaUnchanged()))) { | 
|  | 938 | continue; | 
|  | 939 | } | 
|  | 940 |  | 
| Alec Mouri | 8a18610 | 2023-04-25 00:34:30 +0000 | [diff] [blame] | 941 | // If color management is disabled, then mark the source image with the same colorspace as | 
|  | 942 | // the destination surface so that Skia's color management is a no-op. | 
|  | 943 | const ui::Dataspace layerDataspace = | 
|  | 944 | !mUseColorManagement ? display.outputDataspace : layer.sourceDataspace; | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 945 |  | 
|  | 946 | SkPaint paint; | 
|  | 947 | if (layer.source.buffer.buffer) { | 
|  | 948 | ATRACE_NAME("DrawImage"); | 
|  | 949 | validateInputBufferUsage(layer.source.buffer.buffer->getBuffer()); | 
|  | 950 | const auto& item = layer.source.buffer; | 
| Ian Elliott | 8506e36 | 2023-03-08 12:12:09 -0700 | [diff] [blame] | 951 | auto imageTextureRef = getOrCreateBackendTexture(item.buffer->getBuffer(), false); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 952 |  | 
|  | 953 | // if the layer's buffer has a fence, then we must must respect the fence prior to using | 
|  | 954 | // the buffer. | 
|  | 955 | if (layer.source.buffer.fence != nullptr) { | 
|  | 956 | waitFence(grContext, layer.source.buffer.fence->get()); | 
|  | 957 | } | 
|  | 958 |  | 
|  | 959 | // isOpaque means we need to ignore the alpha in the image, | 
|  | 960 | // replacing it with the alpha specified by the LayerSettings. See | 
|  | 961 | // https://developer.android.com/reference/android/view/SurfaceControl.Builder#setOpaque(boolean) | 
|  | 962 | // The proper way to do this is to use an SkColorType that ignores | 
|  | 963 | // alpha, like kRGB_888x_SkColorType, and that is used if the | 
|  | 964 | // incoming image is kRGBA_8888_SkColorType. However, the incoming | 
|  | 965 | // image may be kRGBA_F16_SkColorType, for which there is no RGBX | 
|  | 966 | // SkColorType, or kRGBA_1010102_SkColorType, for which we have | 
|  | 967 | // kRGB_101010x_SkColorType, but it is not yet supported as a source | 
|  | 968 | // on the GPU. (Adding both is tracked in skbug.com/12048.) In the | 
|  | 969 | // meantime, we'll use a workaround that works unless we need to do | 
|  | 970 | // any color conversion. The workaround requires that we pretend the | 
|  | 971 | // image is already premultiplied, so that we do not premultiply it | 
|  | 972 | // before applying SkBlendMode::kPlus. | 
|  | 973 | const bool useIsOpaqueWorkaround = item.isOpaque && | 
|  | 974 | (imageTextureRef->colorType() == kRGBA_1010102_SkColorType || | 
|  | 975 | imageTextureRef->colorType() == kRGBA_F16_SkColorType); | 
|  | 976 | const auto alphaType = useIsOpaqueWorkaround ? kPremul_SkAlphaType | 
|  | 977 | : item.isOpaque                      ? kOpaque_SkAlphaType | 
|  | 978 | : item.usePremultipliedAlpha         ? kPremul_SkAlphaType | 
|  | 979 | : kUnpremul_SkAlphaType; | 
|  | 980 | sk_sp<SkImage> image = imageTextureRef->makeImage(layerDataspace, alphaType, grContext); | 
|  | 981 |  | 
|  | 982 | auto texMatrix = getSkM44(item.textureTransform).asM33(); | 
|  | 983 | // textureTansform was intended to be passed directly into a shader, so when | 
|  | 984 | // building the total matrix with the textureTransform we need to first | 
|  | 985 | // normalize it, then apply the textureTransform, then scale back up. | 
|  | 986 | texMatrix.preScale(1.0f / bounds.width(), 1.0f / bounds.height()); | 
|  | 987 | texMatrix.postScale(image->width(), image->height()); | 
|  | 988 |  | 
|  | 989 | SkMatrix matrix; | 
|  | 990 | if (!texMatrix.invert(&matrix)) { | 
|  | 991 | matrix = texMatrix; | 
|  | 992 | } | 
|  | 993 | // The shader does not respect the translation, so we add it to the texture | 
|  | 994 | // transform for the SkImage. This will make sure that the correct layer contents | 
|  | 995 | // are drawn in the correct part of the screen. | 
|  | 996 | matrix.postTranslate(bounds.rect().fLeft, bounds.rect().fTop); | 
|  | 997 |  | 
|  | 998 | sk_sp<SkShader> shader; | 
|  | 999 |  | 
|  | 1000 | if (layer.source.buffer.useTextureFiltering) { | 
|  | 1001 | shader = image->makeShader(SkTileMode::kClamp, SkTileMode::kClamp, | 
|  | 1002 | SkSamplingOptions( | 
|  | 1003 | {SkFilterMode::kLinear, SkMipmapMode::kNone}), | 
|  | 1004 | &matrix); | 
|  | 1005 | } else { | 
|  | 1006 | shader = image->makeShader(SkSamplingOptions(), matrix); | 
|  | 1007 | } | 
|  | 1008 |  | 
|  | 1009 | if (useIsOpaqueWorkaround) { | 
|  | 1010 | shader = SkShaders::Blend(SkBlendMode::kPlus, shader, | 
|  | 1011 | SkShaders::Color(SkColors::kBlack, | 
|  | 1012 | toSkColorSpace(layerDataspace))); | 
|  | 1013 | } | 
|  | 1014 |  | 
|  | 1015 | paint.setShader(createRuntimeEffectShader( | 
|  | 1016 | RuntimeEffectShaderParameters{.shader = shader, | 
|  | 1017 | .layer = layer, | 
|  | 1018 | .display = display, | 
|  | 1019 | .undoPremultipliedAlpha = !item.isOpaque && | 
|  | 1020 | item.usePremultipliedAlpha, | 
|  | 1021 | .requiresLinearEffect = requiresLinearEffect, | 
|  | 1022 | .layerDimmingRatio = dimInLinearSpace | 
|  | 1023 | ? layerDimmingRatio | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 1024 | : 1.f, | 
| Alec Mouri | e0bb6f4 | 2023-08-02 22:41:52 +0000 | [diff] [blame] | 1025 | .outputDataSpace = display.outputDataspace, | 
|  | 1026 | .fakeOutputDataspace = fakeDataspace})); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 1027 |  | 
|  | 1028 | // Turn on dithering when dimming beyond this (arbitrary) threshold... | 
|  | 1029 | static constexpr float kDimmingThreshold = 0.2f; | 
|  | 1030 | // ...or we're rendering an HDR layer down to an 8-bit target | 
|  | 1031 | // Most HDR standards require at least 10-bits of color depth for source content, so we | 
|  | 1032 | // can just extract the transfer function rather than dig into precise gralloc layout. | 
|  | 1033 | // Furthermore, we can assume that the only 8-bit target we support is RGBA8888. | 
|  | 1034 | const bool requiresDownsample = isHdrDataspace(layer.sourceDataspace) && | 
|  | 1035 | buffer->getPixelFormat() == PIXEL_FORMAT_RGBA_8888; | 
|  | 1036 | if (layerDimmingRatio <= kDimmingThreshold || requiresDownsample) { | 
|  | 1037 | paint.setDither(true); | 
|  | 1038 | } | 
|  | 1039 | paint.setAlphaf(layer.alpha); | 
|  | 1040 |  | 
|  | 1041 | if (imageTextureRef->colorType() == kAlpha_8_SkColorType) { | 
|  | 1042 | LOG_ALWAYS_FATAL_IF(layer.disableBlending, "Cannot disableBlending with A8"); | 
|  | 1043 |  | 
|  | 1044 | // SysUI creates the alpha layer as a coverage layer, which is | 
|  | 1045 | // appropriate for the DPU. Use a color matrix to convert it to | 
|  | 1046 | // a mask. | 
|  | 1047 | // TODO (b/219525258): Handle input as a mask. | 
|  | 1048 | // | 
|  | 1049 | // The color matrix will convert A8 pixels with no alpha to | 
|  | 1050 | // black, as described by this vector. If the display handles | 
|  | 1051 | // the color transform, we need to invert it to find the color | 
|  | 1052 | // that will result in black after the DPU applies the transform. | 
|  | 1053 | SkV4 black{0.0f, 0.0f, 0.0f, 1.0f}; // r, g, b, a | 
|  | 1054 | if (display.colorTransform != mat4() && display.deviceHandlesColorTransform) { | 
|  | 1055 | SkM44 colorSpaceMatrix = getSkM44(display.colorTransform); | 
|  | 1056 | if (colorSpaceMatrix.invert(&colorSpaceMatrix)) { | 
|  | 1057 | black = colorSpaceMatrix * black; | 
|  | 1058 | } else { | 
|  | 1059 | // We'll just have to use 0,0,0 as black, which should | 
|  | 1060 | // be close to correct. | 
|  | 1061 | ALOGI("Could not invert colorTransform!"); | 
|  | 1062 | } | 
|  | 1063 | } | 
|  | 1064 | SkColorMatrix colorMatrix(0, 0, 0, 0, black[0], | 
|  | 1065 | 0, 0, 0, 0, black[1], | 
|  | 1066 | 0, 0, 0, 0, black[2], | 
|  | 1067 | 0, 0, 0, -1, 1); | 
|  | 1068 | if (display.colorTransform != mat4() && !display.deviceHandlesColorTransform) { | 
|  | 1069 | // On the other hand, if the device doesn't handle it, we | 
|  | 1070 | // have to apply it ourselves. | 
|  | 1071 | colorMatrix.postConcat(toSkColorMatrix(display.colorTransform)); | 
|  | 1072 | } | 
|  | 1073 | paint.setColorFilter(SkColorFilters::Matrix(colorMatrix)); | 
|  | 1074 | } | 
|  | 1075 | } else { | 
|  | 1076 | ATRACE_NAME("DrawColor"); | 
|  | 1077 | const auto color = layer.source.solidColor; | 
|  | 1078 | sk_sp<SkShader> shader = SkShaders::Color(SkColor4f{.fR = color.r, | 
|  | 1079 | .fG = color.g, | 
|  | 1080 | .fB = color.b, | 
|  | 1081 | .fA = layer.alpha}, | 
|  | 1082 | toSkColorSpace(layerDataspace)); | 
|  | 1083 | paint.setShader(createRuntimeEffectShader( | 
|  | 1084 | RuntimeEffectShaderParameters{.shader = shader, | 
|  | 1085 | .layer = layer, | 
|  | 1086 | .display = display, | 
|  | 1087 | .undoPremultipliedAlpha = false, | 
|  | 1088 | .requiresLinearEffect = requiresLinearEffect, | 
| Sally Qi | 628ef6e | 2023-03-30 14:49:03 -0700 | [diff] [blame] | 1089 | .layerDimmingRatio = layerDimmingRatio, | 
| Alec Mouri | e0bb6f4 | 2023-08-02 22:41:52 +0000 | [diff] [blame] | 1090 | .outputDataSpace = display.outputDataspace, | 
|  | 1091 | .fakeOutputDataspace = fakeDataspace})); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 1092 | } | 
|  | 1093 |  | 
|  | 1094 | if (layer.disableBlending) { | 
|  | 1095 | paint.setBlendMode(SkBlendMode::kSrc); | 
|  | 1096 | } | 
|  | 1097 |  | 
|  | 1098 | // An A8 buffer will already have the proper color filter attached to | 
|  | 1099 | // its paint, including the displayColorTransform as needed. | 
|  | 1100 | if (!paint.getColorFilter()) { | 
|  | 1101 | if (!dimInLinearSpace && !equalsWithinMargin(1.0, layerDimmingRatio)) { | 
|  | 1102 | // If we don't dim in linear space, then when we gamma correct the dimming ratio we | 
|  | 1103 | // can assume a gamma 2.2 transfer function. | 
|  | 1104 | static constexpr float kInverseGamma22 = 1.f / 2.2f; | 
|  | 1105 | const auto gammaCorrectedDimmingRatio = | 
|  | 1106 | std::pow(layerDimmingRatio, kInverseGamma22); | 
|  | 1107 | auto dimmingMatrix = | 
|  | 1108 | mat4::scale(vec4(gammaCorrectedDimmingRatio, gammaCorrectedDimmingRatio, | 
|  | 1109 | gammaCorrectedDimmingRatio, 1.f)); | 
|  | 1110 |  | 
|  | 1111 | const auto colorFilter = | 
|  | 1112 | SkColorFilters::Matrix(toSkColorMatrix(std::move(dimmingMatrix))); | 
|  | 1113 | paint.setColorFilter(displayColorTransform | 
|  | 1114 | ? displayColorTransform->makeComposed(colorFilter) | 
|  | 1115 | : colorFilter); | 
|  | 1116 | } else { | 
|  | 1117 | paint.setColorFilter(displayColorTransform); | 
|  | 1118 | } | 
|  | 1119 | } | 
|  | 1120 |  | 
|  | 1121 | if (!roundRectClip.isEmpty()) { | 
|  | 1122 | canvas->clipRRect(roundRectClip, true); | 
|  | 1123 | } | 
|  | 1124 |  | 
|  | 1125 | if (!bounds.isRect()) { | 
|  | 1126 | paint.setAntiAlias(true); | 
|  | 1127 | canvas->drawRRect(bounds, paint); | 
|  | 1128 | } else { | 
|  | 1129 | canvas->drawRect(bounds.rect(), paint); | 
|  | 1130 | } | 
|  | 1131 | if (kFlushAfterEveryLayer) { | 
|  | 1132 | ATRACE_NAME("flush surface"); | 
| Kevin Lubick | 208e11a | 2023-05-31 19:34:46 +0000 | [diff] [blame] | 1133 | skgpu::ganesh::Flush(activeSurface); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 1134 | } | 
|  | 1135 | } | 
|  | 1136 | for (const auto& borderRenderInfo : display.borderInfoList) { | 
|  | 1137 | SkPaint p; | 
|  | 1138 | p.setColor(SkColor4f{borderRenderInfo.color.r, borderRenderInfo.color.g, | 
|  | 1139 | borderRenderInfo.color.b, borderRenderInfo.color.a}); | 
|  | 1140 | p.setAntiAlias(true); | 
|  | 1141 | p.setStyle(SkPaint::kStroke_Style); | 
|  | 1142 | p.setStrokeWidth(borderRenderInfo.width); | 
|  | 1143 | SkRegion sk_region; | 
|  | 1144 | SkPath path; | 
|  | 1145 |  | 
|  | 1146 | // Construct a final SkRegion using Regions | 
|  | 1147 | for (const auto& r : borderRenderInfo.combinedRegion) { | 
|  | 1148 | sk_region.op({r.left, r.top, r.right, r.bottom}, SkRegion::kUnion_Op); | 
|  | 1149 | } | 
|  | 1150 |  | 
|  | 1151 | sk_region.getBoundaryPath(&path); | 
|  | 1152 | canvas->drawPath(path, p); | 
|  | 1153 | path.close(); | 
|  | 1154 | } | 
|  | 1155 |  | 
|  | 1156 | surfaceAutoSaveRestore.restore(); | 
|  | 1157 | mCapture->endCapture(); | 
|  | 1158 | { | 
|  | 1159 | ATRACE_NAME("flush surface"); | 
|  | 1160 | LOG_ALWAYS_FATAL_IF(activeSurface != dstSurface); | 
| Kevin Lubick | 208e11a | 2023-05-31 19:34:46 +0000 | [diff] [blame] | 1161 | skgpu::ganesh::Flush(activeSurface); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 1162 | } | 
|  | 1163 |  | 
| Alec Mouri | 0e7d8fd | 2023-05-03 23:58:43 +0000 | [diff] [blame] | 1164 | auto drawFence = sp<Fence>::make(flushAndSubmit(grContext)); | 
|  | 1165 |  | 
|  | 1166 | if (ATRACE_ENABLED()) { | 
|  | 1167 | static gui::FenceMonitor sMonitor("RE Completion"); | 
|  | 1168 | sMonitor.queueFence(drawFence); | 
|  | 1169 | } | 
|  | 1170 | resultPromise->set_value(std::move(drawFence)); | 
| Lingfeng Yang | 00c1ff6 | 2022-06-02 09:19:28 -0700 | [diff] [blame] | 1171 | } | 
|  | 1172 |  | 
|  | 1173 | size_t SkiaRenderEngine::getMaxTextureSize() const { | 
|  | 1174 | return mGrContext->maxTextureSize(); | 
|  | 1175 | } | 
|  | 1176 |  | 
|  | 1177 | size_t SkiaRenderEngine::getMaxViewportDims() const { | 
|  | 1178 | return mGrContext->maxRenderTargetSize(); | 
|  | 1179 | } | 
|  | 1180 |  | 
|  | 1181 | void SkiaRenderEngine::drawShadow(SkCanvas* canvas, | 
|  | 1182 | const SkRRect& casterRRect, | 
|  | 1183 | const ShadowSettings& settings) { | 
|  | 1184 | ATRACE_CALL(); | 
|  | 1185 | const float casterZ = settings.length / 2.0f; | 
|  | 1186 | const auto flags = | 
|  | 1187 | settings.casterIsTranslucent ? kTransparentOccluder_ShadowFlag : kNone_ShadowFlag; | 
|  | 1188 |  | 
|  | 1189 | SkShadowUtils::DrawShadow(canvas, SkPath::RRect(casterRRect), SkPoint3::Make(0, 0, casterZ), | 
|  | 1190 | getSkPoint3(settings.lightPos), settings.lightRadius, | 
|  | 1191 | getSkColor(settings.ambientColor), getSkColor(settings.spotColor), | 
|  | 1192 | flags); | 
|  | 1193 | } | 
|  | 1194 |  | 
|  | 1195 | void SkiaRenderEngine::onActiveDisplaySizeChanged(ui::Size size) { | 
|  | 1196 | // This cache multiplier was selected based on review of cache sizes relative | 
|  | 1197 | // to the screen resolution. Looking at the worst case memory needed by blur (~1.5x), | 
|  | 1198 | // shadows (~1x), and general data structures (e.g. vertex buffers) we selected this as a | 
|  | 1199 | // conservative default based on that analysis. | 
|  | 1200 | const float SURFACE_SIZE_MULTIPLIER = 3.5f * bytesPerPixel(mDefaultPixelFormat); | 
|  | 1201 | const int maxResourceBytes = size.width * size.height * SURFACE_SIZE_MULTIPLIER; | 
|  | 1202 |  | 
|  | 1203 | // start by resizing the current context | 
|  | 1204 | getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); | 
|  | 1205 |  | 
|  | 1206 | // if it is possible to switch contexts then we will resize the other context | 
|  | 1207 | const bool originalProtectedState = mInProtectedContext; | 
|  | 1208 | useProtectedContext(!mInProtectedContext); | 
|  | 1209 | if (mInProtectedContext != originalProtectedState) { | 
|  | 1210 | getActiveGrContext()->setResourceCacheLimit(maxResourceBytes); | 
|  | 1211 | // reset back to the initial context that was active when this method was called | 
|  | 1212 | useProtectedContext(originalProtectedState); | 
|  | 1213 | } | 
|  | 1214 | } | 
|  | 1215 |  | 
|  | 1216 | void SkiaRenderEngine::dump(std::string& result) { | 
|  | 1217 | // Dump for the specific backend (GLES or Vk) | 
|  | 1218 | appendBackendSpecificInfoToDump(result); | 
|  | 1219 |  | 
|  | 1220 | // Info about protected content | 
|  | 1221 | StringAppendF(&result, "RenderEngine supports protected context: %d\n", | 
|  | 1222 | supportsProtectedContent()); | 
|  | 1223 | StringAppendF(&result, "RenderEngine is in protected context: %d\n", mInProtectedContext); | 
|  | 1224 | StringAppendF(&result, "RenderEngine shaders cached since last dump/primeCache: %d\n", | 
|  | 1225 | mSkSLCacheMonitor.shadersCachedSinceLastCall()); | 
|  | 1226 |  | 
|  | 1227 | std::vector<ResourcePair> cpuResourceMap = { | 
|  | 1228 | {"skia/sk_resource_cache/bitmap_", "Bitmaps"}, | 
|  | 1229 | {"skia/sk_resource_cache/rrect-blur_", "Masks"}, | 
|  | 1230 | {"skia/sk_resource_cache/rects-blur_", "Masks"}, | 
|  | 1231 | {"skia/sk_resource_cache/tessellated", "Shadows"}, | 
|  | 1232 | {"skia", "Other"}, | 
|  | 1233 | }; | 
|  | 1234 | SkiaMemoryReporter cpuReporter(cpuResourceMap, false); | 
|  | 1235 | SkGraphics::DumpMemoryStatistics(&cpuReporter); | 
|  | 1236 | StringAppendF(&result, "Skia CPU Caches: "); | 
|  | 1237 | cpuReporter.logTotals(result); | 
|  | 1238 | cpuReporter.logOutput(result); | 
|  | 1239 |  | 
|  | 1240 | { | 
|  | 1241 | std::lock_guard<std::mutex> lock(mRenderingMutex); | 
|  | 1242 |  | 
|  | 1243 | std::vector<ResourcePair> gpuResourceMap = { | 
|  | 1244 | {"texture_renderbuffer", "Texture/RenderBuffer"}, | 
|  | 1245 | {"texture", "Texture"}, | 
|  | 1246 | {"gr_text_blob_cache", "Text"}, | 
|  | 1247 | {"skia", "Other"}, | 
|  | 1248 | }; | 
|  | 1249 | SkiaMemoryReporter gpuReporter(gpuResourceMap, true); | 
|  | 1250 | mGrContext->dumpMemoryStatistics(&gpuReporter); | 
|  | 1251 | StringAppendF(&result, "Skia's GPU Caches: "); | 
|  | 1252 | gpuReporter.logTotals(result); | 
|  | 1253 | gpuReporter.logOutput(result); | 
|  | 1254 | StringAppendF(&result, "Skia's Wrapped Objects:\n"); | 
|  | 1255 | gpuReporter.logOutput(result, true); | 
|  | 1256 |  | 
|  | 1257 | StringAppendF(&result, "RenderEngine tracked buffers: %zu\n", | 
|  | 1258 | mGraphicBufferExternalRefs.size()); | 
|  | 1259 | StringAppendF(&result, "Dumping buffer ids...\n"); | 
|  | 1260 | for (const auto& [id, refCounts] : mGraphicBufferExternalRefs) { | 
|  | 1261 | StringAppendF(&result, "- 0x%" PRIx64 " - %d refs \n", id, refCounts); | 
|  | 1262 | } | 
|  | 1263 | StringAppendF(&result, "RenderEngine AHB/BackendTexture cache size: %zu\n", | 
|  | 1264 | mTextureCache.size()); | 
|  | 1265 | StringAppendF(&result, "Dumping buffer ids...\n"); | 
|  | 1266 | // TODO(178539829): It would be nice to know which layer these are coming from and what | 
|  | 1267 | // the texture sizes are. | 
|  | 1268 | for (const auto& [id, unused] : mTextureCache) { | 
|  | 1269 | StringAppendF(&result, "- 0x%" PRIx64 "\n", id); | 
|  | 1270 | } | 
|  | 1271 | StringAppendF(&result, "\n"); | 
|  | 1272 |  | 
|  | 1273 | SkiaMemoryReporter gpuProtectedReporter(gpuResourceMap, true); | 
|  | 1274 | if (mProtectedGrContext) { | 
|  | 1275 | mProtectedGrContext->dumpMemoryStatistics(&gpuProtectedReporter); | 
|  | 1276 | } | 
|  | 1277 | StringAppendF(&result, "Skia's GPU Protected Caches: "); | 
|  | 1278 | gpuProtectedReporter.logTotals(result); | 
|  | 1279 | gpuProtectedReporter.logOutput(result); | 
|  | 1280 | StringAppendF(&result, "Skia's Protected Wrapped Objects:\n"); | 
|  | 1281 | gpuProtectedReporter.logOutput(result, true); | 
|  | 1282 |  | 
|  | 1283 | StringAppendF(&result, "\n"); | 
|  | 1284 | StringAppendF(&result, "RenderEngine runtime effects: %zu\n", mRuntimeEffects.size()); | 
|  | 1285 | for (const auto& [linearEffect, unused] : mRuntimeEffects) { | 
|  | 1286 | StringAppendF(&result, "- inputDataspace: %s\n", | 
|  | 1287 | dataspaceDetails( | 
|  | 1288 | static_cast<android_dataspace>(linearEffect.inputDataspace)) | 
|  | 1289 | .c_str()); | 
|  | 1290 | StringAppendF(&result, "- outputDataspace: %s\n", | 
|  | 1291 | dataspaceDetails( | 
|  | 1292 | static_cast<android_dataspace>(linearEffect.outputDataspace)) | 
|  | 1293 | .c_str()); | 
|  | 1294 | StringAppendF(&result, "undoPremultipliedAlpha: %s\n", | 
|  | 1295 | linearEffect.undoPremultipliedAlpha ? "true" : "false"); | 
|  | 1296 | } | 
|  | 1297 | } | 
|  | 1298 | StringAppendF(&result, "\n"); | 
|  | 1299 | } | 
|  | 1300 |  | 
| rnlee | c6a7364 | 2021-06-04 14:16:42 -0700 | [diff] [blame] | 1301 | } // namespace skia | 
| John Reck | 67b1e2b | 2020-08-26 13:17:24 -0700 | [diff] [blame] | 1302 | } // namespace renderengine | 
| rnlee | c6a7364 | 2021-06-04 14:16:42 -0700 | [diff] [blame] | 1303 | } // namespace android |