Fix filtering for screenshotting custom displays.
Previously, filtering was computed for screenshots by checking if
filtering was required for the DisplayDevice itself. That's correct for
rendering custom display sizes since we need to upsample to the
physical panel, but it's wrong for screenshotting to an output buffer
where the logical transform does not involve a scale. Instead, we want
to compare the source crop with the destination buffer size when
determining whether we need to apply linear filtering.
As part of this, some of the rendering math is now simpler. Rather than
setting DisplaySettings::physicalDisplay and DisplaySettings::clip to be
the same and explicitly computing the transform matrix in RenderEngine,
we instead set DisplaySettings::clip to be the logical viewport. Then
the global transform is implicitly applied as part of mapping the output
in glViewport. This is because if the logical display is smaller than
the screen size, with the previous behavior applying the global transform
will scale up the layer stack to the physical display size. But if we
want pixel-accurate screenshots we will need to downsample back down to
the logical display size, which may cause errors. The math simplification
will avoid that scenario entirely.
This also means that screenshot code needs to be adjusted - some callers
pass in a cropping rectangle that assumes the display screenshot is
captured in portrait orientation. Other callers pass in a cropping
rectangle relative to a particular layer's coordinate space. For each of
those paths, the cropping rectangle must be transformed to the logical
display space.
This has also discovered a bug in setting the background fill of the
screenshot, which has now been fixed.
Bug: 129101431
Test: adb shell screepcap
Test: Modify wm size and density, and run
SurfaceViewTests#testMovingWhiteSurfaceView
Test: Capture screenshots through vysor
Test: Capture screenshots on Pixel 3XL with notch hide
Test: Physically capture screenshots
Test: Display rotations
Test: SurfaceFlinger_test
Test: librenderengine_test
Test: libsurfaceflinger_unittest
Test: libcompositionengine_test
Change-Id: I7f4e98d4c5aa53a995fd7da70078a2e5ea43b14f
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e22bc61..aa0005d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -5357,7 +5357,6 @@
DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace,
renderAreaRotation, captureSecureLayers);
-
auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display,
std::placeholders::_1);
return captureScreenCommon(renderArea, traverseLayers, outBuffer, reqPixelFormat,
@@ -5485,10 +5484,7 @@
mFlinger(flinger),
mChildrenOnly(childrenOnly) {}
const ui::Transform& getTransform() const override { return mTransform; }
- Rect getBounds() const override {
- const Layer::State& layerState(mLayer->getDrawingState());
- return mLayer->getBufferSize(layerState);
- }
+ Rect getBounds() const override { return mLayer->getBufferSize(mLayer->getDrawingState()); }
int getHeight() const override {
return mLayer->getBufferSize(mLayer->getDrawingState()).getHeight();
}
@@ -5531,9 +5527,8 @@
mTransform = mLayer->getTransform().inverse();
drawLayers();
} else {
- Rect bounds = getBounds();
- uint32_t w = static_cast<uint32_t>(bounds.getWidth());
- uint32_t h = static_cast<uint32_t>(bounds.getHeight());
+ uint32_t w = static_cast<uint32_t>(getWidth());
+ uint32_t h = static_cast<uint32_t>(getHeight());
// In the "childrenOnly" case we reparent the children to a screenshot
// layer which has no properties set and which does not draw.
sp<ContainerLayer> screenshotParentLayer =
@@ -5748,9 +5743,9 @@
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
- const auto rotation = renderArea.getRotationFlags();
- const auto transform = renderArea.getTransform();
const auto sourceCrop = renderArea.getSourceCrop();
+ const auto transform = renderArea.getTransform();
+ const auto rotation = renderArea.getRotationFlags();
const auto& displayViewport = renderArea.getDisplayViewport();
renderengine::DisplaySettings clientCompositionDisplay;
@@ -5760,55 +5755,8 @@
// buffer bounds.
clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight);
clientCompositionDisplay.clip = sourceCrop;
- clientCompositionDisplay.globalTransform = transform.asMatrix4();
-
- // Now take into account the rotation flag. We append a transform that
- // rotates the layer stack about the origin, then translate by buffer
- // boundaries to be in the right quadrant.
- mat4 rotMatrix;
- int displacementX = 0;
- int displacementY = 0;
- float rot90InRadians = 2.0f * static_cast<float>(M_PI) / 4.0f;
- switch (rotation) {
- case ui::Transform::ROT_90:
- rotMatrix = mat4::rotate(rot90InRadians, vec3(0, 0, 1));
- displacementX = renderArea.getBounds().getHeight();
- break;
- case ui::Transform::ROT_180:
- rotMatrix = mat4::rotate(rot90InRadians * 2.0f, vec3(0, 0, 1));
- displacementY = renderArea.getBounds().getWidth();
- displacementX = renderArea.getBounds().getHeight();
- break;
- case ui::Transform::ROT_270:
- rotMatrix = mat4::rotate(rot90InRadians * 3.0f, vec3(0, 0, 1));
- displacementY = renderArea.getBounds().getWidth();
- break;
- default:
- break;
- }
-
- // We need to transform the clipping window into the right spot.
- // First, rotate the clipping rectangle by the rotation hint to get the
- // right orientation
- const vec4 clipTL = vec4(sourceCrop.left, sourceCrop.top, 0, 1);
- const vec4 clipBR = vec4(sourceCrop.right, sourceCrop.bottom, 0, 1);
- const vec4 rotClipTL = rotMatrix * clipTL;
- const vec4 rotClipBR = rotMatrix * clipBR;
- const int newClipLeft = std::min(rotClipTL[0], rotClipBR[0]);
- const int newClipTop = std::min(rotClipTL[1], rotClipBR[1]);
- const int newClipRight = std::max(rotClipTL[0], rotClipBR[0]);
- const int newClipBottom = std::max(rotClipTL[1], rotClipBR[1]);
-
- // Now reposition the clipping rectangle with the displacement vector
- // computed above.
- const mat4 displacementMat = mat4::translate(vec4(displacementX, displacementY, 0, 1));
- clientCompositionDisplay.clip =
- Rect(newClipLeft + displacementX, newClipTop + displacementY,
- newClipRight + displacementX, newClipBottom + displacementY);
-
- mat4 clipTransform = displacementMat * rotMatrix;
- clientCompositionDisplay.globalTransform =
- clipTransform * clientCompositionDisplay.globalTransform;
+ clientCompositionDisplay.globalTransform = mat4();
+ clientCompositionDisplay.orientation = rotation;
clientCompositionDisplay.outputDataspace = renderArea.getReqDataSpace();
clientCompositionDisplay.maxLuminance = DisplayDevice::sDefaultMaxLumiance;
@@ -5818,7 +5766,8 @@
compositionengine::LayerFE::LayerSettings fillLayer;
fillLayer.source.buffer.buffer = nullptr;
fillLayer.source.solidColor = half3(0.0, 0.0, 0.0);
- fillLayer.geometry.boundaries = FloatRect(0.0, 0.0, 1.0, 1.0);
+ fillLayer.geometry.boundaries =
+ FloatRect(sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom);
fillLayer.alpha = half(alpha);
clientCompositionLayers.push_back(fillLayer);
@@ -5830,7 +5779,8 @@
compositionengine::LayerFE::ClientCompositionTargetSettings targetSettings{
clip,
useIdentityTransform,
- layer->needsFiltering(renderArea.getDisplayDevice()) || renderArea.needsFiltering(),
+ layer->needsFilteringForScreenshots(renderArea.getDisplayDevice(), transform) ||
+ renderArea.needsFiltering(),
renderArea.isSecure(),
supportProtectedContent,
clearRegion,
@@ -5842,6 +5792,10 @@
std::vector<compositionengine::LayerFE::LayerSettings> results =
layer->prepareClientCompositionList(targetSettings);
if (results.size() > 0) {
+ for (auto& settings : results) {
+ settings.geometry.positionTransform =
+ transform.asMatrix4() * settings.geometry.positionTransform;
+ }
clientCompositionLayers.insert(clientCompositionLayers.end(),
std::make_move_iterator(results.begin()),
std::make_move_iterator(results.end()));