/* * Copyright 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "Cache.h" #include "AutoBackendTexture.h" #include "SkiaRenderEngine.h" #include "android-base/unique_fd.h" #include "cutils/properties.h" #include "renderengine/DisplaySettings.h" #include "renderengine/LayerSettings.h" #include "renderengine/impl/ExternalTexture.h" #include "ui/GraphicBuffer.h" #include "ui/GraphicTypes.h" #include "ui/PixelFormat.h" #include "ui/Rect.h" #include "utils/Timers.h" #include namespace android::renderengine::skia { namespace { // clang-format off // Any non-identity matrix will do. const auto kScaleAndTranslate = mat4(0.7f, 0.f, 0.f, 0.f, 0.f, 0.7f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 67.3f, 52.2f, 0.f, 1.f); const auto kScaleAsymmetric = mat4(0.8f, 0.f, 0.f, 0.f, 0.f, 1.1f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f); const auto kFlip = mat4(1.1f, -0.1f, 0.f, 0.f, 0.1f, 1.1f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 2.f, 2.f, 0.f, 1.f); // clang-format on // When setting layer.sourceDataspace, whether it matches the destination or not determines whether // a color correction effect is added to the shader. constexpr auto kDestDataSpace = ui::Dataspace::SRGB; constexpr auto kOtherDataSpace = ui::Dataspace::DISPLAY_P3; constexpr auto kBT2020DataSpace = ui::Dataspace::BT2020_ITU_PQ; constexpr auto kExtendedHdrDataSpce = static_cast(ui::Dataspace::RANGE_EXTENDED | ui::Dataspace::TRANSFER_SRGB | ui::Dataspace::STANDARD_DCI_P3); // Dimming is needed to trigger linear effects for some dataspace pairs const std::array kLayerWhitePoints = { 1000.0f, 500.0f, 100.0f, // trigger dithering by dimming below 20% }; } // namespace static void drawShadowLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture) { // Somewhat arbitrary dimensions, but on screen and slightly shorter, based // on actual use. const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); FloatRect smallerRect(20, 20, displayRect.width()-20, displayRect.height()-20); LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, .roundedCornersRadius = {50.f, 50.f}, .roundedCornersCrop = rect, }, .alpha = 1, // setting this is mandatory for shadows and blurs .skipContentDraw = true, // drawShadow ignores alpha .shadow = ShadowSettings{ .boundaries = rect, .ambientColor = vec4(0, 0, 0, 0.00935997f), .spotColor = vec4(0, 0, 0, 0.0455841f), .lightPos = vec3(500.f, -1500.f, 1500.f), .lightRadius = 2500.0f, .length = 15.f, }, }; LayerSettings caster{ .geometry = Geometry{ .boundaries = smallerRect, .roundedCornersRadius = {50.f, 50.f}, .roundedCornersCrop = rect, }, .source = PixelSource{ .solidColor = half3(0.f, 0.f, 0.f), }, .alpha = 1, }; // Four combinations of settings are used (two transforms here, and drawShadowLayers is // called with two different destination data spaces) They're all rounded rect. // Three of these are cache misses that generate new shaders. // The first combination generates a short and simple shadow shader. // The second combination, flip transform, generates two shaders. The first appears to involve // gaussian_fp. The second is a long and general purpose shadow shader with a device space // transformation stage. // The third combination is a cache hit, nothing new. // The fourth combination, flip transform with a non-SRGB destination dataspace, is new. // It is unique in that nearly everything is done in the vertex shader, and that vertex shader // requires color correction. This is triggered differently from every other instance of color // correction. All other instances are triggered when src and dst dataspaces differ, while // this one is triggered by the destination being non-srgb. Apparently since the third // combination is a cache hit, this color correction is only added when the vertex shader is // doing something non-trivial. for (auto transform : {mat4(), kFlip}) { layer.geometry.positionTransform = transform; caster.geometry.positionTransform = transform; auto layers = std::vector{layer, caster}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } static void drawImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, .maxLuminanceNits = 1000.f, }}, }; for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) { layer.sourceDataspace = dataspace; // Cache shaders for both rects and round rects. // In reduced shader mode, all non-zero round rect radii get the same code path. for (float roundedCornersRadius : {0.0f, 50.0f}) { // roundedCornersCrop is always set, but the radius triggers the behavior layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius}; for (bool isOpaque : {true, false}) { layer.source.buffer.isOpaque = isOpaque; for (auto alpha : {half(.2f), half(1.0f)}) { layer.alpha = alpha; auto layers = std::vector{layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } } } static void drawSolidLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, }, .source = PixelSource{ .solidColor = half3(0.1f, 0.2f, 0.3f), }, .alpha = 0.5, }; for (auto transform : {mat4(), kScaleAndTranslate}) { layer.geometry.positionTransform = transform; for (float roundedCornersRadius : {0.0f, 50.f}) { layer.geometry.roundedCornersRadius = {roundedCornersRadius, roundedCornersRadius}; auto layers = std::vector{layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } static void drawBlurLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, }, .alpha = 1, // setting this is mandatory for shadows and blurs .skipContentDraw = true, }; // Different blur code is invoked for radii less and greater than 30 pixels for (int radius : {9, 60}) { layer.backgroundBlurRadius = radius; auto layers = std::vector{layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } // The unique feature of these layers is that the boundary is slightly smaller than the rounded // rect crop, so the rounded edges intersect that boundary and require a different clipping method. // For buffers, this is done with a stage that computes coverage and it will differ for round and // elliptical corners. static void drawClippedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height() - 20); // boundary is smaller PixelSource bufferSource{.buffer = Buffer{ .buffer = srcTexture, .isOpaque = 0, .maxLuminanceNits = 1000.f, }}; PixelSource bufferOpaque{.buffer = Buffer{ .buffer = srcTexture, .isOpaque = 1, .maxLuminanceNits = 1000.f, }}; PixelSource colorSource{.solidColor = half3(0.1f, 0.2f, 0.3f)}; LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, .roundedCornersRadius = {27.f, 27.f}, .roundedCornersCrop = FloatRect(0, 0, displayRect.width(), displayRect.height()), }, }; for (auto pixelSource : {bufferSource, bufferOpaque, colorSource}) { layer.source = pixelSource; for (auto dataspace : {kDestDataSpace, kOtherDataSpace}) { layer.sourceDataspace = dataspace; // Produce a CircularRRect clip and an EllipticalRRect clip. for (auto transform : {kScaleAndTranslate, kScaleAsymmetric}) { layer.geometry.positionTransform = transform; for (float alpha : {0.5f, 1.f}) { layer.alpha = alpha; auto layers = std::vector{layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } } } static void drawPIPImageLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, // Note that this flip matrix only makes a difference when clipping, // which happens in this layer because the roundrect crop is just a bit // larger than the layer bounds. .positionTransform = kFlip, .roundedCornersRadius = {94.2551f, 94.2551f}, .roundedCornersCrop = FloatRect(-93.75, 0, displayRect.width() + 93.75, displayRect.height()), }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, .usePremultipliedAlpha = 1, .isOpaque = 0, .maxLuminanceNits = 1000.f, }}, .alpha = 1, .sourceDataspace = kOtherDataSpace, }; auto layers = std::vector{layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } static void drawHolePunchLayer(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); FloatRect small(0, 0, displayRect.width()-20, displayRect.height()+20); LayerSettings layer{ .geometry = Geometry{ // the boundaries have to be smaller than the rounded crop so that // clipRRect is used instead of drawRRect .boundaries = small, .positionTransform = kScaleAndTranslate, .roundedCornersRadius = {50.f, 50.f}, .roundedCornersCrop = rect, }, .source = PixelSource{ .solidColor = half3(0.f, 0.f, 0.f), }, .alpha = 0, .sourceDataspace = kDestDataSpace, .disableBlending = true, }; auto layers = std::vector{layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } static void drawImageDimmedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), .boundaries = rect, .roundedCornersCrop = rect, .roundedCornersRadius = {0.f, 0.f}, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = true}}, .alpha = 1.f, .sourceDataspace = kDestDataSpace, }; std::vector layers; for (auto layerWhitePoint : kLayerWhitePoints) { layer.whitePointNits = layerWhitePoint; layers.push_back(layer); } renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } static void drawTransparentImageDimmedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ .positionTransform = mat4(), .boundaries = rect, .roundedCornersCrop = rect, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = false, }}, .sourceDataspace = kDestDataSpace, }; for (auto roundedCornerRadius : {0.f, 50.f}) { layer.geometry.roundedCornersRadius = {roundedCornerRadius, roundedCornerRadius}; for (auto alpha : {0.5f, 1.0f}) { layer.alpha = alpha; for (auto isOpaque : {true, false}) { if (roundedCornerRadius == 0.f && isOpaque) { // already covered in drawImageDimmedLayers continue; } layer.source.buffer.isOpaque = isOpaque; std::vector layers; for (auto layerWhitePoint : kLayerWhitePoints) { layer.whitePointNits = layerWhitePoint; layers.push_back(layer); } renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } } static void drawClippedDimmedImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; // If rect and boundary is too small compared to roundedCornersRadius, Skia will switch to // blending instead of EllipticalRRect, so enlarge them a bit. FloatRect rect(0, 0, displayRect.width(), displayRect.height()); FloatRect boundary(0, 0, displayRect.width(), displayRect.height() - 20); // boundary is smaller LayerSettings layer{ .geometry = Geometry{ .positionTransform = mat4(), .boundaries = boundary, .roundedCornersCrop = rect, .roundedCornersRadius = {27.f, 27.f}, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = false, }}, .alpha = 1.f, .sourceDataspace = kDestDataSpace, }; std::array transforms = {kScaleAndTranslate, kScaleAsymmetric}; constexpr float radius = 27.f; for (size_t i = 0; i < transforms.size(); i++) { layer.geometry.positionTransform = transforms[i]; layer.geometry.roundedCornersRadius = {radius, radius}; std::vector layers; for (auto layerWhitePoint : kLayerWhitePoints) { layer.whitePointNits = layerWhitePoint; layers.push_back(layer); } renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } static void drawSolidDimmedLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ .boundaries = rect, .roundedCornersCrop = rect, }, .source = PixelSource{ .solidColor = half3(0.1f, 0.2f, 0.3f), }, .alpha = 1.f, }; std::vector layers; for (auto layerWhitePoint : kLayerWhitePoints) { layer.whitePointNits = layerWhitePoint; layers.push_back(layer); } renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } static void drawBT2020ImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), .boundaries = rect, .roundedCornersCrop = rect, .roundedCornersRadius = {0.f, 0.f}, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = true}}, .alpha = 1.f, .sourceDataspace = kBT2020DataSpace, }; for (auto alpha : {0.5f, 1.f}) { layer.alpha = alpha; std::vector layers; layer.whitePointNits = -1.f; layers.push_back(layer); renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } static void drawBT2020ClippedImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; // If rect and boundary is too small compared to roundedCornersRadius, Skia will switch to // blending instead of EllipticalRRect, so enlarge them a bit. FloatRect rect(0, 0, displayRect.width(), displayRect.height()); FloatRect boundary(0, 0, displayRect.width(), displayRect.height() - 10); // boundary is smaller LayerSettings layer{ .geometry = Geometry{ .positionTransform = kScaleAsymmetric, .boundaries = boundary, .roundedCornersCrop = rect, .roundedCornersRadius = {64.1f, 64.1f}, }, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = true, }}, .alpha = 0.5f, .sourceDataspace = kBT2020DataSpace, }; std::vector layers = {layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } static void drawExtendedHDRImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), .boundaries = rect, .roundedCornersCrop = rect, .roundedCornersRadius = {50.f, 50.f}, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = true}}, .alpha = 0.5f, .sourceDataspace = kExtendedHdrDataSpce, }; for (auto roundedCornerRadius : {0.f, 50.f}) { layer.geometry.roundedCornersRadius = {roundedCornerRadius, roundedCornerRadius}; for (auto alpha : {0.5f, 1.f}) { layer.alpha = alpha; std::vector layers; for (auto layerWhitePoint : kLayerWhitePoints) { layer.whitePointNits = layerWhitePoint; layers.push_back(layer); } renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } } static void drawP3ImageLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; FloatRect rect(0, 0, displayRect.width(), displayRect.height()); LayerSettings layer{ .geometry = Geometry{ // The position transform doesn't matter when the reduced shader mode // in in effect. A matrix transform stage is always included. .positionTransform = mat4(), .boundaries = rect, .roundedCornersCrop = rect, .roundedCornersRadius = {50.f, 50.f}, }, .source = PixelSource{.buffer = Buffer{.buffer = srcTexture, .maxLuminanceNits = 1000.f, .usePremultipliedAlpha = true, .isOpaque = false}}, .alpha = 0.5f, .sourceDataspace = kOtherDataSpace, }; for (auto alpha : {0.5f, 1.f}) { layer.alpha = alpha; std::vector layers; for (auto layerWhitePoint : kLayerWhitePoints) { layer.whitePointNits = layerWhitePoint; layers.push_back(layer); } renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } static void drawEdgeExtensionLayers(SkiaRenderEngine* renderengine, const DisplaySettings& display, const std::shared_ptr& dstTexture, const std::shared_ptr& srcTexture) { const Rect& displayRect = display.physicalDisplay; // Make the layer LayerSettings layer{ // Make the layer bigger than the texture .geometry = Geometry{.boundaries = FloatRect(0, 0, displayRect.width(), displayRect.height())}, .source = PixelSource{.buffer = Buffer{ .buffer = srcTexture, .isOpaque = 1, }}, // The type of effect does not affect the shader's uniforms, but the layer must have a // valid EdgeExtensionEffect to apply the shader .edgeExtensionEffect = EdgeExtensionEffect(true /* left */, false, false, true /* bottom */), }; for (float alpha : {0.5, 0.0, 1.0}) { layer.alpha = alpha; auto layers = std::vector{layer}; renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()); } } // // The collection of shaders cached here were found by using perfetto to record shader compiles // during actions that involve RenderEngine, logging the layer settings, and the shader code // and reproducing those settings here. // // It is helpful when debugging this to turn on // in SkGLRenderEngine.cpp: // kPrintLayerSettings = true // kFlushAfterEveryLayer = true // in external/skia/src/gpu/gl/builders/GrGLShaderStringBuilder.cpp // gPrintSKSL = true void Cache::primeShaderCache(SkiaRenderEngine* renderengine, PrimeCacheConfig config) { const int previousCount = renderengine->reportShadersCompiled(); if (previousCount) { ALOGD("%d Shaders already compiled before Cache::primeShaderCache ran\n", previousCount); } // The loop is beneficial for debugging and should otherwise be optimized out by the compiler. // Adding additional bounds to the loop is useful for verifying that the size of the dst buffer // does not impact the shader compilation counts by triggering different behaviors in RE/Skia. for (SkSize bounds : {SkSize::Make(128, 128), /*SkSize::Make(1080, 2340)*/}) { const nsecs_t timeBefore = systemTime(); // The dimensions should not matter, so long as we draw inside them. const Rect displayRect(0, 0, bounds.fWidth, bounds.fHeight); DisplaySettings display{ .physicalDisplay = displayRect, .clip = displayRect, .maxLuminance = 500, .outputDataspace = kDestDataSpace, }; DisplaySettings p3Display{ .physicalDisplay = displayRect, .clip = displayRect, .maxLuminance = 500, .outputDataspace = kOtherDataSpace, }; DisplaySettings p3DisplayEnhance{.physicalDisplay = displayRect, .clip = displayRect, .maxLuminance = 500, .outputDataspace = kOtherDataSpace, .dimmingStage = aidl::android::hardware::graphics:: composer3::DimmingStage::GAMMA_OETF, .renderIntent = aidl::android::hardware::graphics:: composer3::RenderIntent::ENHANCE}; DisplaySettings bt2020Display{.physicalDisplay = displayRect, .clip = displayRect, .maxLuminance = 500, .outputDataspace = ui::Dataspace::BT2020, .deviceHandlesColorTransform = true, .dimmingStage = aidl::android::hardware::graphics::composer3:: DimmingStage::GAMMA_OETF, .renderIntent = aidl::android::hardware::graphics::composer3:: RenderIntent::TONE_MAP_ENHANCE}; const int64_t usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE; sp dstBuffer = sp::make(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, usage, "primeShaderCache_dst"); const auto dstTexture = std::make_shared(dstBuffer, *renderengine, impl::ExternalTexture::Usage::WRITEABLE); // This buffer will be the source for the call to drawImageLayers. Draw // something to it as a placeholder for what an app draws. We should draw // something, but the details are not important. Make use of the shadow layer drawing step // to populate it. sp srcBuffer = sp::make(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, usage, "drawImageLayer_src"); const auto srcTexture = std::make_shared< impl::ExternalTexture>(srcBuffer, *renderengine, impl::ExternalTexture::Usage::READABLE | impl::ExternalTexture::Usage::WRITEABLE); if (config.cacheHolePunchLayer) { drawHolePunchLayer(renderengine, display, dstTexture); } if (config.cacheSolidLayers) { drawSolidLayers(renderengine, display, dstTexture); drawSolidLayers(renderengine, p3Display, dstTexture); } if (config.cacheSolidDimmedLayers) { drawSolidDimmedLayers(renderengine, display, dstTexture); } if (config.cacheShadowLayers) { drawShadowLayers(renderengine, display, srcTexture); drawShadowLayers(renderengine, p3Display, srcTexture); } if (renderengine->supportsBackgroundBlur()) { drawBlurLayers(renderengine, display, dstTexture); } // The majority of skia shaders needed by RenderEngine are related to sampling images. // These need to be generated with various source textures. // Make a list of applicable sources. // GRALLOC_USAGE_HW_TEXTURE should be the same as AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE. const int64_t usageExternal = GRALLOC_USAGE_HW_TEXTURE; sp externalBuffer = sp::make(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_8888, 1, usageExternal, "primeShaderCache_external"); const auto externalTexture = std::make_shared(externalBuffer, *renderengine, impl::ExternalTexture::Usage::READABLE); std::vector> textures = {srcTexture, externalTexture}; // Another external texture with a different pixel format triggers useIsOpaqueWorkaround. // It doesn't have to be f16, but it can't be the usual 8888. sp f16ExternalBuffer = sp::make(displayRect.width(), displayRect.height(), PIXEL_FORMAT_RGBA_FP16, 1, usageExternal, "primeShaderCache_external_f16"); // The F16 texture may not be usable on all devices, so check first that it was created. status_t error = f16ExternalBuffer->initCheck(); if (!error) { const auto f16ExternalTexture = std::make_shared(f16ExternalBuffer, *renderengine, impl::ExternalTexture::Usage::READABLE); textures.push_back(f16ExternalTexture); } for (auto texture : textures) { if (config.cacheImageLayers) { drawImageLayers(renderengine, display, dstTexture, texture); } if (config.cacheImageDimmedLayers) { drawImageDimmedLayers(renderengine, display, dstTexture, texture); drawImageDimmedLayers(renderengine, p3Display, dstTexture, texture); drawImageDimmedLayers(renderengine, bt2020Display, dstTexture, texture); } if (config.cacheClippedLayers) { // Draw layers for b/185569240. drawClippedLayers(renderengine, display, dstTexture, texture); } if (com::android::graphics::libgui::flags::edge_extension_shader() && config.cacheEdgeExtension) { drawEdgeExtensionLayers(renderengine, display, dstTexture, texture); drawEdgeExtensionLayers(renderengine, p3Display, dstTexture, texture); } } if (config.cachePIPImageLayers) { drawPIPImageLayer(renderengine, display, dstTexture, externalTexture); } if (config.cacheTransparentImageDimmedLayers) { drawTransparentImageDimmedLayers(renderengine, bt2020Display, dstTexture, externalTexture); drawTransparentImageDimmedLayers(renderengine, display, dstTexture, externalTexture); drawTransparentImageDimmedLayers(renderengine, p3Display, dstTexture, externalTexture); drawTransparentImageDimmedLayers(renderengine, p3DisplayEnhance, dstTexture, externalTexture); } if (config.cacheClippedDimmedImageLayers) { drawClippedDimmedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); } if (config.cacheUltraHDR) { drawBT2020ClippedImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); drawBT2020ImageLayers(renderengine, bt2020Display, dstTexture, externalTexture); drawBT2020ImageLayers(renderengine, p3Display, dstTexture, externalTexture); drawExtendedHDRImageLayers(renderengine, display, dstTexture, externalTexture); drawExtendedHDRImageLayers(renderengine, p3Display, dstTexture, externalTexture); drawExtendedHDRImageLayers(renderengine, p3DisplayEnhance, dstTexture, externalTexture); drawP3ImageLayers(renderengine, p3DisplayEnhance, dstTexture, externalTexture); } // draw one final layer synchronously to force GL submit LayerSettings layer{ .source = PixelSource{.solidColor = half3(0.f, 0.f, 0.f)}, }; auto layers = std::vector{layer}; // call get() to make it synchronous renderengine->drawLayers(display, layers, dstTexture, base::unique_fd()).get(); const nsecs_t timeAfter = systemTime(); const float compileTimeMs = static_cast(timeAfter - timeBefore) / 1.0E6; const int shadersCompiled = renderengine->reportShadersCompiled() - previousCount; ALOGD("Shader cache generated %d shaders in %f ms\n", shadersCompiled, compileTimeMs); } } } // namespace android::renderengine::skia