Skip to content

Commit 251e612

Browse files
author
Dominik Laskowski
committed
SF: Let DM resize framebuffer on resolution change
The goal of this change is to synchronize resolution switching across SF and DM. The lack of synchronization causes glitches, which are currently papered over in HWC by dropping frames whose size don't match the active resolution. A mode set involving a resolution switch was finalized by destroying the DisplayDevice and thus its HWC layers. Remove this special case in favor of letting DM call SurfaceControl.setDisplaySize right after requesting the mode set via setDesiredDisplayModeSpecs. Emit a mode change event to DM as soon as the DisplayModeRequest becomes the desired mode. In response, DM sends the transaction that resizes the display via setDisplaySize, so wait until that commit before modesetting to the new resolution, and resize the framebuffer in that same frame. Display projection depends on display size, so update the latter first when committing DisplayDeviceState. Fixes: 355427258 Flag: com.android.graphics.surfaceflinger.flags.synced_resolution_switch Test: Internal (caiman) and external displays, with different rotations. Test: Inner display (comet) with install orientation. Change-Id: Ifaf300f3b5f907f7cd10b8db2aa6165ad2106530
1 parent 23069b3 commit 251e612

9 files changed

Lines changed: 102 additions & 31 deletions

File tree

libs/gui/include/gui/LayerState.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -504,12 +504,18 @@ struct DisplayState {
504504
Rect layerStackSpaceRect = Rect::EMPTY_RECT;
505505
Rect orientedDisplaySpaceRect = Rect::EMPTY_RECT;
506506

507-
// Exclusive to virtual displays: The sink surface into which the virtual display is rendered,
508-
// and an optional resolution that overrides its default dimensions.
509-
sp<IGraphicBufferProducer> surface;
507+
// For physical displays, this is the resolution, which must match the active display mode. To
508+
// change the resolution, the client must first call SurfaceControl.setDesiredDisplayModeSpecs
509+
// with the new DesiredDisplayModeSpecs#defaultMode, then commit the matching width and height.
510+
//
511+
// For virtual displays, this is an optional resolution that overrides its default dimensions.
512+
//
510513
uint32_t width = 0;
511514
uint32_t height = 0;
512515

516+
// For virtual displays, this is the sink surface into which the virtual display is rendered.
517+
sp<IGraphicBufferProducer> surface;
518+
513519
status_t write(Parcel& output) const;
514520
status_t read(const Parcel& input);
515521
};

services/surfaceflinger/Display/DisplayModeRequest.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ namespace android::display {
2626
struct DisplayModeRequest {
2727
scheduler::FrameRateMode mode;
2828

29-
// Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE.
29+
// Whether to emit DisplayEventReceiver::DISPLAY_EVENT_MODE_CHANGE for a change in refresh rate
30+
// or render rate. Ignored for resolution changes, which always emit the event.
3031
bool emitEvent = false;
3132

3233
// Whether to force the request to be applied, even if the mode is unchanged.

services/surfaceflinger/DisplayDevice.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,9 +223,7 @@ void DisplayDevice::setFlags(uint32_t flags) {
223223
mFlags = flags;
224224
}
225225

226-
void DisplayDevice::setDisplaySize(int width, int height) {
227-
LOG_FATAL_IF(!isVirtual(), "Changing the display size is supported only for virtual displays.");
228-
const auto size = ui::Size(width, height);
226+
void DisplayDevice::setDisplaySize(ui::Size size) {
229227
mCompositionDisplay->setDisplaySize(size);
230228
if (mRefreshRateOverlay) {
231229
mRefreshRateOverlay->setViewport(size);

services/surfaceflinger/DisplayDevice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class DisplayDevice : public RefBase {
9898
ui::Size getSize() const { return {getWidth(), getHeight()}; }
9999

100100
void setLayerFilter(ui::LayerFilter);
101-
void setDisplaySize(int width, int height);
101+
void setDisplaySize(ui::Size);
102102
void setProjection(ui::Rotation orientation, Rect viewport, Rect frame);
103103
void stageBrightness(float brightness) REQUIRES(kMainThreadContext);
104104
void persistBrightness(bool needsComposite) REQUIRES(kMainThreadContext);

services/surfaceflinger/Scheduler/include/scheduler/FrameRateMode.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ struct FrameRateMode {
3333
}
3434

3535
bool operator!=(const FrameRateMode& other) const { return !(*this == other); }
36+
37+
bool matchesResolution(const FrameRateMode& other) const {
38+
return modePtr->getResolution() == other.modePtr->getResolution();
39+
}
3640
};
3741

3842
inline std::string to_string(const FrameRateMode& mode) {

services/surfaceflinger/SurfaceFlinger.cpp

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1366,7 +1366,8 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) {
13661366
const auto selectorPtr = mDisplayModeController.selectorPtrFor(displayId);
13671367
if (!selectorPtr) break;
13681368

1369-
const Fps renderRate = selectorPtr->getActiveMode().fps;
1369+
const auto activeMode = selectorPtr->getActiveMode();
1370+
const Fps renderRate = activeMode.fps;
13701371

13711372
// DisplayModeController::setDesiredMode updated the render rate, so inform Scheduler.
13721373
mScheduler->setRenderRate(displayId, renderRate, true /* applyImmediately */);
@@ -1385,6 +1386,15 @@ void SurfaceFlinger::setDesiredMode(display::DisplayModeRequest&& desiredMode) {
13851386

13861387
mScheduler->updatePhaseConfiguration(displayId, mode.fps);
13871388
mScheduler->setModeChangePending(true);
1389+
1390+
// The mode set to switch resolution is not initiated until the display transaction that
1391+
// resizes the display. DM sends this transaction in response to a mode change event, so
1392+
// emit the event now, not when finalizing the mode change as for a refresh rate switch.
1393+
if (FlagManager::getInstance().synced_resolution_switch() &&
1394+
!mode.matchesResolution(activeMode)) {
1395+
mScheduler->onDisplayModeChanged(displayId, mode,
1396+
/*clearContentRequirements*/ true);
1397+
}
13881398
break;
13891399
}
13901400
case DesiredModeAction::InitiateRenderRateSwitch:
@@ -1463,27 +1473,34 @@ bool SurfaceFlinger::finalizeDisplayModeChange(PhysicalDisplayId displayId) {
14631473
}
14641474

14651475
const auto& activeMode = pendingModeOpt->mode;
1466-
1467-
if (const auto oldResolution =
1468-
mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
1469-
oldResolution != activeMode.modePtr->getResolution()) {
1470-
auto& state = mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
1471-
// We need to generate new sequenceId in order to recreate the display (and this
1472-
// way the framebuffer).
1473-
state.sequenceId = DisplayDeviceState{}.sequenceId;
1474-
state.physical->activeMode = activeMode.modePtr.get();
1475-
processDisplayChangesLocked();
1476-
1477-
// The DisplayDevice has been destroyed, so abort the commit for the now dead FrameTargeter.
1478-
return false;
1476+
const bool resolutionMatch = !FlagManager::getInstance().synced_resolution_switch() ||
1477+
activeMode.matchesResolution(mDisplayModeController.getActiveMode(displayId));
1478+
1479+
if (!FlagManager::getInstance().synced_resolution_switch()) {
1480+
if (const auto oldResolution =
1481+
mDisplayModeController.getActiveMode(displayId).modePtr->getResolution();
1482+
oldResolution != activeMode.modePtr->getResolution()) {
1483+
auto& state =
1484+
mCurrentState.displays.editValueFor(getPhysicalDisplayTokenLocked(displayId));
1485+
// We need to generate new sequenceId in order to recreate the display (and this
1486+
// way the framebuffer).
1487+
state.sequenceId = DisplayDeviceState{}.sequenceId;
1488+
state.physical->activeMode = activeMode.modePtr.get();
1489+
processDisplayChangesLocked();
1490+
1491+
// The DisplayDevice has been destroyed, so abort the commit for the now dead
1492+
// FrameTargeter.
1493+
return false;
1494+
}
14791495
}
14801496

14811497
mDisplayModeController.finalizeModeChange(displayId, activeMode.modePtr->getId(),
14821498
activeMode.modePtr->getVsyncRate(), activeMode.fps);
14831499

14841500
mScheduler->updatePhaseConfiguration(displayId, activeMode.fps);
14851501

1486-
if (pendingModeOpt->emitEvent) {
1502+
// Skip for resolution changes, since the event was already emitted on setting the desired mode.
1503+
if (resolutionMatch && pendingModeOpt->emitEvent) {
14871504
mScheduler->onDisplayModeChanged(displayId, activeMode, /*clearContentRequirements*/ true);
14881505
}
14891506

@@ -1535,8 +1552,9 @@ void SurfaceFlinger::initiateDisplayModeChanges() {
15351552
to_string(displayModePtrOpt->get()->getVsyncRate()).c_str(),
15361553
to_string(displayId).c_str());
15371554

1538-
if ((!FlagManager::getInstance().connected_display() || !desiredModeOpt->force) &&
1539-
mDisplayModeController.getActiveMode(displayId) == desiredModeOpt->mode) {
1555+
const auto activeMode = mDisplayModeController.getActiveMode(displayId);
1556+
1557+
if (!desiredModeOpt->force && desiredModeOpt->mode == activeMode) {
15401558
applyActiveMode(displayId);
15411559
continue;
15421560
}
@@ -1557,6 +1575,15 @@ void SurfaceFlinger::initiateDisplayModeChanges() {
15571575
constraints.seamlessRequired = false;
15581576
hal::VsyncPeriodChangeTimeline outTimeline;
15591577

1578+
// When initiating a resolution change, wait until the commit that resizes the display.
1579+
if (FlagManager::getInstance().synced_resolution_switch() &&
1580+
!activeMode.matchesResolution(desiredModeOpt->mode)) {
1581+
const auto display = getDisplayDeviceLocked(displayId);
1582+
if (display->getSize() != desiredModeOpt->mode.modePtr->getResolution()) {
1583+
continue;
1584+
}
1585+
}
1586+
15601587
const auto error =
15611588
mDisplayModeController.initiateModeChange(displayId, std::move(*desiredModeOpt),
15621589
constraints, outTimeline);
@@ -4096,6 +4123,35 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
40964123
if (currentState.flags != drawingState.flags) {
40974124
display->setFlags(currentState.flags);
40984125
}
4126+
4127+
const auto updateDisplaySize = [&]() {
4128+
if (currentState.width != drawingState.width ||
4129+
currentState.height != drawingState.height) {
4130+
const ui::Size resolution = ui::Size(currentState.width, currentState.height);
4131+
4132+
// Resize the framebuffer. For a virtual display, always do so. For a physical
4133+
// display, only do so if it has a pending modeset for the matching resolution.
4134+
if (!currentState.physical ||
4135+
(FlagManager::getInstance().synced_resolution_switch() &&
4136+
mDisplayModeController.getDesiredMode(display->getPhysicalId())
4137+
.transform([resolution](const auto& request) {
4138+
return resolution == request.mode.modePtr->getResolution();
4139+
})
4140+
.value_or(false))) {
4141+
display->setDisplaySize(resolution);
4142+
}
4143+
4144+
if (display->getId() == mActiveDisplayId) {
4145+
onActiveDisplaySizeChanged(*display);
4146+
}
4147+
}
4148+
};
4149+
4150+
if (FlagManager::getInstance().synced_resolution_switch()) {
4151+
// Update display size first, as display projection below depends on it.
4152+
updateDisplaySize();
4153+
}
4154+
40994155
if ((currentState.orientation != drawingState.orientation) ||
41004156
(currentState.layerStackSpaceRect != drawingState.layerStackSpaceRect) ||
41014157
(currentState.orientedDisplaySpaceRect != drawingState.orientedDisplaySpaceRect)) {
@@ -4107,13 +4163,9 @@ void SurfaceFlinger::processDisplayChanged(const wp<IBinder>& displayToken,
41074163
ui::Transform::toRotationFlags(display->getOrientation());
41084164
}
41094165
}
4110-
if (currentState.width != drawingState.width ||
4111-
currentState.height != drawingState.height) {
4112-
display->setDisplaySize(currentState.width, currentState.height);
41134166

4114-
if (display->getId() == mActiveDisplayId) {
4115-
onActiveDisplaySizeChanged(*display);
4116-
}
4167+
if (!FlagManager::getInstance().synced_resolution_switch()) {
4168+
updateDisplaySize();
41174169
}
41184170
}
41194171
}

services/surfaceflinger/common/FlagManager.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ void FlagManager::dump(std::string& result) const {
171171
DUMP_ACONFIG_FLAG(restore_blur_step);
172172
DUMP_ACONFIG_FLAG(skip_invisible_windows_in_input);
173173
DUMP_ACONFIG_FLAG(stable_edid_ids);
174+
DUMP_ACONFIG_FLAG(synced_resolution_switch);
174175
DUMP_ACONFIG_FLAG(trace_frame_rate_override);
175176
DUMP_ACONFIG_FLAG(true_hdr_screenshots);
176177
DUMP_ACONFIG_FLAG(use_known_refresh_rate_for_fps_consistency);
@@ -295,6 +296,7 @@ FLAG_MANAGER_ACONFIG_FLAG(skip_invisible_windows_in_input, "");
295296
FLAG_MANAGER_ACONFIG_FLAG(begone_bright_hlg, "debug.sf.begone_bright_hlg");
296297
FLAG_MANAGER_ACONFIG_FLAG(window_blur_kawase2, "");
297298
FLAG_MANAGER_ACONFIG_FLAG(reject_dupe_layerstacks, "");
299+
FLAG_MANAGER_ACONFIG_FLAG(synced_resolution_switch, "");
298300

299301
/// Trunk stable server (R/W) flags ///
300302
FLAG_MANAGER_ACONFIG_FLAG(refresh_rate_overlay_on_external_display, "")

services/surfaceflinger/common/include/common/FlagManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class FlagManager {
107107
bool restore_blur_step() const;
108108
bool skip_invisible_windows_in_input() const;
109109
bool stable_edid_ids() const;
110+
bool synced_resolution_switch() const;
110111
bool trace_frame_rate_override() const;
111112
bool true_hdr_screenshots() const;
112113
bool use_known_refresh_rate_for_fps_consistency() const;

services/surfaceflinger/surfaceflinger_flags_new.aconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,13 @@ flag {
264264
is_fixed_read_only: true
265265
} # stable_edid_ids
266266

267+
flag {
268+
name: "synced_resolution_switch"
269+
namespace: "core_graphics"
270+
description: "Synchronize resolution modeset with framebuffer resizing"
271+
bug: "355427258"
272+
} # synced_resolution_switch
273+
267274
flag {
268275
name: "true_hdr_screenshots"
269276
namespace: "core_graphics"

0 commit comments

Comments
 (0)