Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for external displays #93

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open

Conversation

krnlyng
Copy link
Contributor

@krnlyng krnlyng commented Dec 27, 2021

Based on @thp s and @elros34 s work. Rebased to latest master, added some workarounds for certain devices.

NotKit and others added 5 commits December 27, 2021 13:19
[v11] introduce no-active-config quirk. JB#56560

Can be used on devices where getActiveConfig is not functional in the v11 backend.

Co-authored-by: Franz-Josef Haider <[email protected]>
[v11] Support for external displays. JB#56560

Co-authored-by: elros34 <[email protected]>
Co-authored-by: Franz-Josef Haider <[email protected]>
[v11] multiscreen: don't set transform to anything but 0 for now. JB#56560
…frame since it's really slow on some devices.

[v11] multiscreen: avoid calling getDisplayAttributes on every frame since it's really slow on some devices. JB#56560
[v11] multiscreen: close releaseFenceFd of framebuffer layer. JB#56560
// glitches and warnings in logcat. By setting the planarAlpha to non-
// opaque, we attempt to force the HWC into using HWC_FRAMEBUFFER for this
// layer so the HWC_FRAMEBUFFER_TARGET layer actually gets used.
bool tryToForceGLES = !qgetenv("QPA_HWC_FORCE_GLES").isEmpty();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about static bool instead calling qgetenv on every frame.

Suggested change
bool tryToForceGLES = !qgetenv("QPA_HWC_FORCE_GLES").isEmpty();
static bool tryToForceGLES = qEnvironmentVariableIsSet("QPA_HWC_FORCE_GLES");

@elros34
Copy link

elros34 commented Dec 27, 2021

It was quite a long time since I last tried this code but I remember I had crash in hwcomposer when connecting hdmi without this: elros34@9556cd5 (and close fence elros34@a823e4f). hnd pointer is expected to be not NULL: https://github.com/mer-hybris/android_hardware_qcom_display-caf/blob/hybris-11.0-44S/libhwcomposer/hwc_copybit.cpp#L191. This should also prevents "layer handle is NULL" spam in logcat: https://github.com/mer-hybris/android_hardware_qcom_display-caf/blob/hybris-11.0-44S/libhwcomposer/hwc_mdpcomp.cpp#L299.

{
free(contents);

for (auto &screen: screens) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

auto & -> auto? Unless you were writing back to the position in the vector I can't think why a reference to a pointer would be favorable to just a pointer.

~RetireFencePool()
{
for (auto fd: m_fds) {
fprintf(stderr, "Waiting and closing retire fence fd: %d\n", fd);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a macro with a environment variable or something for the debug output? A lot of the others are infrequent enough to not really matter but this is going to print every frame.


// Destination rectangle on the actual screen
const hwc_rect_t dest_rect = {
0, 0, m_screen_width, m_screen_height

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens when the screens have different aspect ratios? Is the content just stretched to fit on the external screens?

Copy link

@elros34 elros34 Dec 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can only tell that from my old test based on: https://github.com/elros34/qt5-qpa-hwcomposer-plugin/tree/a31 I have only managed to get this code working correctly on native landscape display tablet (screen width > screen height). The content on tv had same aspect ratio as device use. I had to resize content in tv options.
On regular device (moto photon q) which has screen width < screen height content is totally mess up. I have manged to get it working once in portrait mode while messing with width/height parameters in relayout but never managed to rotate content to landscape mode, that is why I dropped it. In nemo bug report (https://bugs.nemomobile.org/show_bug.cgi?id=765: Nemo bug 765 in Hybris-ing "[hammerhead][others] Enable external (Slimport/MHL) screen Mirroring/Extending on Wayland" [Task,New]) there were photos of some nexus devices which worked fine in portrait mode but link is no longer valid and contains some different bug.

};

// Don't call getDisplayAttributes too often here since it can cause big slowdowns.
if (m_screen_width <= 0 || m_screen_height <= 0) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be still executed because not connected displays (external and virtual) will have m_screen_width/height = 0 until first connection. I wonder whether HwComposerScreen_v11 should be created on demand for external and virtual display. For now relayout is called for 3 displays even without external display connection.
Is it really needed to call relayout() on every frame? From my tests calling it after unblank display was enough but I never manged to get whole code working correctly on native portrait display device so take it with grain of salt.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it have to be after unblank or is it enough if it's called on hotplug?

Copy link

@elros34 elros34 Jan 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added relayout(HwComposerBackend_v11->widht/height) in hotplug and in createWindow() after width/height are set and indeed both builtin and external display works on A31 based tablet I use for testing.

[v11] don't call relayout on every frame. JB#56560
[v11] update screen sizes on hotplug. JB#56560
…hotplug

[v11] update screen sizes not just for the external display on hotplug. JB#56560
[v11] remove unused variable. JB#56560
[v11] set initial screen sizes. JB#56560
[v11] fix QPA_HWC_FORCE_GLES. JB#56560
Copy link

@jpetrell jpetrell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The external display support has so far seen limited testing (landscape homescreen exported to external landscape display) so this is probably not fully working yet, but a good first step. Added few comments, but overall looking good. 🙂

}

((struct HwcProcs_v11*)procs)->backend->screenPlugged();
((struct HwcProcs_v11*)procs)->content->update_screen_sizes(((struct HwcProcs_v11*)procs)->backend);
}

HwComposerBackend_v11::~HwComposerBackend_v11()
{
hwc_device->eventControl(hwc_device, 0, HWC_EVENT_VSYNC, 0);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be hwc_device->eventControl(hwc_device, 1, HWC_EVENT_VSYNC, 0) also, i.e. for the external display?

HWC_PLUGIN_EXPECT_ZERO(status);
g_unblanked_displays[display] = (status == blank);

// on certian devices this is needed
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo certain

void
HwComposerBackend_v11::sleepDisplay(bool sleep)
{
// XXX: For debugging only
//dump_attributes(hwc_device, num_displays);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally such debug traces would be put behind Qt logging sub-categories for enabling them without recompilation

@@ -393,7 +687,7 @@ HwComposerBackend_v11::sleepDisplay(bool sleep)
}
}

int HwComposerBackend_v11::getSingleAttribute(uint32_t attribute)
int HwComposerBackend_v11::getSingleAttribute(uint32_t attribute, int dpy)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I see function like "getSingleAttribute(uint32_t attribute, int dpy)" it wass not automatically obvious to me that dpy refered to display's index number. In Qt style abbreviations are avoided (expect in fully local, obvious cases like for (int i; i < count; i++)... Could align with blankDisplay(int display, ..)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also would be good to consistently have display index as the first or second attribute, now both are used

blankDisplay(0, true);
}
fprintf(stderr, "Unblanking external display\n");
blankDisplay(1, false);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since display 0 is assumed to be always internal display and display 1 the external display would normally avoid hard-coded magical numbers and instead introduce enumerations like InternalDisplay, ExternalDisplay passed to the functions. Now debug prints above are used to document 0=internal, 1=external

// on certian devices this is needed
screenPlugged();
}

void
HwComposerBackend_v11::sleepDisplay(bool sleep)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds like sleepDisplay() is nowadays sleepDisplays()

HWC_PLUGIN_EXPECT_ZERO(hwc_device->blank(hwc_device, 0, 1));
for (int i=0; i<num_displays; i++) {
if (g_unblanked_displays[i]) {
blankDisplay(i, true);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As terms go personally I like blanking more than sleeping, but ideally whould stick to one term and at least use it consistently within the scope of internal variables. Now display on/off -> sleepDisplay true/false -> blankDisplay true/false -> device setPowerMode on/off.


static int g_external_connected = 0;
static int g_external_connected_next = 0;
static int g_unblanked_displays[HWC_NUM_DISPLAY_TYPES] = { 0 };
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-CamelCase function names get_screen_size and using of fprintf instead of qDebug irks me slightly, but these platform plugin codes are bit in the middle of two words so maybe mixing C-style with C++-style is fine

#if 0
for (int i=0; i<num_displays; i++) {
blankDisplay(i, false);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if other routines could also have been written to handle n displays instead of hardcoding to 2 (internal, external). Even if initially only two were supported. Maybe not worth the effort at this point

@@ -63,6 +99,7 @@ class HwComposerBackend_v11 : public QObject, public HwComposerBackend {
virtual EGLNativeWindowType createWindow(int width, int height);
virtual void destroyWindow(EGLNativeWindowType window);
virtual void swap(EGLNativeDisplayType display, EGLSurface surface);
virtual void blankDisplay(int display, bool blank);
virtual void sleepDisplay(bool sleep);
virtual float refreshRate();
virtual bool getScreenSizes(int *width, int *height, float *physical_width, float *physical_height);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getScreenSize calls getSingleAttribute without display/screen id, which causes getSingleAttribute to default to display 0. If the device has no internal display but only an external one the screen info exposed by the Qt platform plugin reports false values?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants