From 872872f3d3f0d89010d3a196b70e8445a08d0784 Mon Sep 17 00:00:00 2001 From: Jani Hautakangas Date: Fri, 23 Sep 2011 07:47:24 +0300 Subject: Fix to QtOpenGL crash Exiting the native video recorder on Symbian and going back to Qt app which runs on opengl graphics system crashed on some devices because there was not enough GPU memory and also because low GPU mem device environment detection was flawed. This patch fixes 32MB GPU memory detection and adds wait/retrial to EGL surface creation if it fails. Task-number: QTBUG-21499 Reviewed-by: Laszlo Agocs --- src/gui/egl/qegl.cpp | 32 ++++++++ src/gui/painting/qgraphicssystemex_symbian.cpp | 105 +++++++++++++++++++++---- 2 files changed, 123 insertions(+), 14 deletions(-) diff --git a/src/gui/egl/qegl.cpp b/src/gui/egl/qegl.cpp index 4db4a6a..02adef8 100644 --- a/src/gui/egl/qegl.cpp +++ b/src/gui/egl/qegl.cpp @@ -684,6 +684,37 @@ EGLSurface QEgl::createSurface(QPaintDevice *device, EGLConfig cfg, const QEglPr else props = 0; EGLSurface surf; +#ifdef Q_OS_SYMBIAN + // On Symbian there might be situations (especially on 32MB GPU devices) + // where Qt is trying to create EGL surface while some other application + // is still holding all available GPU memory but is about to release it + // soon. For an example when exiting native video recorder and going back to + // Qt application behind it. Video stack tear down takes some time and Qt + // app might be too quick in reserving its EGL surface and thus running out + // of GPU memory right away. So if EGL surface creation fails due to bad + // alloc, let's try recreating it four times within ~1 second if needed. + // This strategy gives some time for video recorder to tear down its stack + // and a chance to Qt for creating a valid surface. + int tries = 4; + while(tries--) { + if (devType == QInternal::Widget) + surf = eglCreateWindowSurface(QEgl::display(), cfg, windowDrawable, props); + else + surf = eglCreatePixmapSurface(QEgl::display(), cfg, pixmapDrawable, props); + if (surf == EGL_NO_SURFACE) { + EGLint error = eglGetError(); + if (error == EGL_BAD_ALLOC) { + if (tries) { + User::After(1000 * 250); // 250ms + continue; + } + } + qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", error); + } else { + break; + } + } +#else if (devType == QInternal::Widget) surf = eglCreateWindowSurface(QEgl::display(), cfg, windowDrawable, props); else @@ -691,6 +722,7 @@ EGLSurface QEgl::createSurface(QPaintDevice *device, EGLConfig cfg, const QEglPr if (surf == EGL_NO_SURFACE) { qWarning("QEglContext::createSurface(): Unable to create EGL surface, error = 0x%x", eglGetError()); } +#endif return surf; } #endif diff --git a/src/gui/painting/qgraphicssystemex_symbian.cpp b/src/gui/painting/qgraphicssystemex_symbian.cpp index 4469704..46731d8 100644 --- a/src/gui/painting/qgraphicssystemex_symbian.cpp +++ b/src/gui/painting/qgraphicssystemex_symbian.cpp @@ -46,31 +46,106 @@ #include +#ifdef Q_SYMBIAN_SUPPORTS_SURFACES +#include "private/qegl_p.h" +#endif + QT_BEGIN_NAMESPACE static bool bcm2727Initialized = false; static bool bcm2727 = false; +typedef EGLBoolean (*NOK_resource_profiling)(EGLDisplay, EGLint, EGLint*, EGLint, EGLint*); +#define EGL_PROF_TOTAL_MEMORY_NOK 0x3070 + +// Detect if Qt is running on BCM2727 chip. +// BCM2727 is a special case on Symbian because +// it has only 32MB GPU memory which exposes +// significant limitations to hw accelerated UI. bool QSymbianGraphicsSystemEx::hasBCM2727() { if (bcm2727Initialized) return bcm2727; - const TUid KIvePropertyCat = {0x2726beef}; - enum TIvePropertyChipType { - EVCBCM2727B1 = 0x00000000, - EVCBCM2763A0 = 0x04000100, - EVCBCM2763B0 = 0x04000102, - EVCBCM2763C0 = 0x04000103, - EVCBCM2763C1 = 0x04000104, - EVCBCMUnknown = 0x7fffffff - }; - - TInt chipType = EVCBCMUnknown; - if (RProperty::Get(KIvePropertyCat, 0, chipType) == KErrNone) { - if (chipType == EVCBCM2727B1) +#ifdef Q_SYMBIAN_SUPPORTS_SURFACES + EGLDisplay display = QEgl::display(); +#if 1 + // Hacky but fast ~0ms. + const char* vendor = eglQueryString(display, EGL_VENDOR); + if (strstr(vendor, "Broadcom")) { + const TUid KIvePropertyCat = {0x2726beef}; + enum TIvePropertyChipType { + EVCBCM2727B1 = 0x00000000, + EVCBCM2763A0 = 0x04000100, + EVCBCM2763B0 = 0x04000102, + EVCBCM2763C0 = 0x04000103, + EVCBCM2763C1 = 0x04000104, + EVCBCMUnknown = 0x7fffffff + }; + + // Broadcom driver publishes KIvePropertyCat PS key on + // devices which are running on BCM2727 chip and post Anna Symbian. + TInt chipType = EVCBCMUnknown; + if (RProperty::Get(KIvePropertyCat, 0, chipType) == KErrNone) { + if (chipType == EVCBCM2727B1) + bcm2727 = true; + } else if (QSysInfo::symbianVersion() <= QSysInfo::SV_SF_3) { + // Device is running on Symbian Anna or older Symbian^3 in which + // KIvePropertyCat is not published. These ones are always 32MB devices. + bcm2727 = true; + } else { + // We have some other Broadcom chip on post Anna Symbian. + // Should have > 32MB GPU memory. + } + } +#else + // Fool proof but takes 15-20ms and we don't want this delay on app startup... + + // All devices with <= 32MB GPU memory should be + // dealed in similar manner to BCM2727 + // So let's query max GPU memory amount. + NOK_resource_profiling eglQueryProfilingData = (NOK_resource_profiling)eglGetProcAddress("eglQueryProfilingDataNOK"); + if (eglQueryProfilingData) { + EGLint dataCount; + eglQueryProfilingData(display, + EGL_PROF_QUERY_GLOBAL_BIT_NOK | + EGL_PROF_QUERY_MEMORY_USAGE_BIT_NOK, + NULL, + 0, + (EGLint*)&dataCount); + + // Allocate room for the profiling data + EGLint* profData = (EGLint*)malloc(dataCount * sizeof(EGLint)); + memset(profData,0,dataCount * sizeof(EGLint)); + + // Retrieve the profiling data + eglQueryProfilingData(display, + EGL_PROF_QUERY_GLOBAL_BIT_NOK | + EGL_PROF_QUERY_MEMORY_USAGE_BIT_NOK, + profData, + dataCount, + (EGLint*)&dataCount); + + int totalMemory; + EGLint i = 0; + while (profData && i < dataCount) { + switch (profData[i++]) { + case EGL_PROF_TOTAL_MEMORY_NOK: + totalMemory = profData[i++]; + break; + default: + i++; + } + } + + // ok, hasBCM2727() naming is a bit misleading but Qt must + // behave the same on all chips like BCM2727 (<= 32MB GPU memory) + // and our code (and others) are already using this function. + if (totalMemory <= 33554432) bcm2727 = true; } +#endif +#endif // Q_SYMBIAN_SUPPORTS_SURFACES bcm2727Initialized = true; @@ -80,7 +155,9 @@ bool QSymbianGraphicsSystemEx::hasBCM2727() void QSymbianGraphicsSystemEx::releaseCachedGpuResources() { // Do nothing here - // This is implemented in graphics system specific plugin + + // This virtual function should be implemented in graphics system specific + // plugin } void QSymbianGraphicsSystemEx::releaseAllGpuResources() -- cgit v0.12 From e90cffc2c5f313cc813d0a21a1d9d0afe8d0ea7a Mon Sep 17 00:00:00 2001 From: Sami Merila Date: Tue, 27 Sep 2011 08:17:25 +0300 Subject: Crash in QDeclarativeCompiler::indexOfProperty In QDeclarativePropertyCache, plain integer bitfield overrideIndex is initialized with -1 in class constructor. Unfortunately, ARM compiler treats bitfields as unsigned, unless explicitly defined as signed [1]. Therefore, overrideIndex actually gets initial value of 2147483647, which causes array operations done with the index to fail. As a fix, define overrideIndex as signed int bitfield. [1] http://www.keil.com/support/man/docs/armccref/armccref_Babjddhe.htm Under bitfields/Note: "A plain bitfield, declared without either signed or unsigned qualifiers, is treated as unsigned" Task-number: QT-5285 Reviewed-by: Aaron Kennedy --- src/declarative/qml/qdeclarativepropertycache_p.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/declarative/qml/qdeclarativepropertycache_p.h b/src/declarative/qml/qdeclarativepropertycache_p.h index 581f519..c648d25 100644 --- a/src/declarative/qml/qdeclarativepropertycache_p.h +++ b/src/declarative/qml/qdeclarativepropertycache_p.h @@ -111,7 +111,7 @@ public: int relatedIndex; // When IsFunction }; uint overrideIndexIsProperty : 1; - int overrideIndex : 31; + signed int overrideIndex : 31; int revision; int metaObjectOffset; -- cgit v0.12 From 24855ef3983fa5d87b3e3a06fe891a769e670ae5 Mon Sep 17 00:00:00 2001 From: Gareth Stockwell Date: Wed, 31 Aug 2011 15:52:25 +0100 Subject: Always recreate backing store when TLW transparency changes When using either the opengl or openvg graphics system on Symbian, if a TLW is made transparent, the widget switches to using raster rendering, because EGL surface transparency is currently not supported by the platform. This patch enables the reverse to occur: when the widget is subsequently made opaque, rendering switches back to using GL/VG. Task-number: QTBUG-21211 Reviewed-by: Jani Hautakangas Reviewed-by: Laszlo Agocs Reviewed-by: Sami Merila --- src/gui/kernel/qwidget.cpp | 24 ++++++++++++---- src/gui/kernel/qwidget_p.h | 1 + src/gui/kernel/qwidget_s60.cpp | 58 ++++++++++++++++++++++++-------------- tests/auto/qwidget/tst_qwidget.cpp | 45 ++++++++++++++++++++++++++++- 4 files changed, 101 insertions(+), 27 deletions(-) diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index 8e8266c..bda0885 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -2238,10 +2238,16 @@ void QWidgetPrivate::updateIsOpaque() #endif #ifdef Q_WS_S60 - if (q->windowType() == Qt::Dialog && q->testAttribute(Qt::WA_TranslucentBackground) - && S60->avkonComponentsSupportTransparency) { - setOpaque(false); - return; + if (q->testAttribute(Qt::WA_TranslucentBackground)) { + if (q->windowType() & Qt::Dialog || q->windowType() & Qt::Popup) { + if (S60->avkonComponentsSupportTransparency) { + setOpaque(false); + return; + } + } else { + setOpaque(false); + return; + } } #endif @@ -2261,11 +2267,16 @@ void QWidgetPrivate::updateIsOpaque() } if (q->isWindow() && !q->testAttribute(Qt::WA_NoSystemBackground)) { +#ifdef Q_WS_S60 + setOpaque(true); + return; +#else const QBrush &windowBrush = q->palette().brush(QPalette::Window); if (windowBrush.style() != Qt::NoBrush && windowBrush.isOpaque()) { setOpaque(true); return; } +#endif } setOpaque(false); } @@ -10845,11 +10856,14 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) } break; case Qt::WA_TranslucentBackground: +#if defined(Q_OS_SYMBIAN) + setAttribute(Qt::WA_NoSystemBackground, on); +#else if (on) { setAttribute(Qt::WA_NoSystemBackground); d->updateIsTranslucent(); } - +#endif break; case Qt::WA_AcceptTouchEvents: #if defined(Q_WS_WIN) || defined(Q_WS_MAC) || defined(Q_OS_SYMBIAN) diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index e30497c..caa6454 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -228,6 +228,7 @@ struct QTLWExtra { uint inExpose : 1; // Prevents drawing recursion uint nativeWindowTransparencyEnabled : 1; // Tracks native window transparency uint forcedToRaster : 1; + uint noSystemRotationDisabled : 1; #endif }; diff --git a/src/gui/kernel/qwidget_s60.cpp b/src/gui/kernel/qwidget_s60.cpp index e80eced..e12dfa7 100644 --- a/src/gui/kernel/qwidget_s60.cpp +++ b/src/gui/kernel/qwidget_s60.cpp @@ -820,19 +820,12 @@ void QWidgetPrivate::setConstraints_sys() void QWidgetPrivate::s60UpdateIsOpaque() { Q_Q(QWidget); - if (!q->testAttribute(Qt::WA_WState_Created)) return; - const bool writeAlpha = extraData()->nativePaintMode == QWExtra::BlitWriteAlpha; - if (!q->testAttribute(Qt::WA_TranslucentBackground) && !writeAlpha) - return; const bool requireAlphaChannel = !isOpaque || writeAlpha; - createTLExtra(); - RWindow *const window = static_cast(q->effectiveWinId()->DrawableWindow()); - #ifdef Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE if (QApplicationPrivate::instance()->useTranslucentEGLSurfaces && !extra->topextra->forcedToRaster) { @@ -841,25 +834,47 @@ void QWidgetPrivate::s60UpdateIsOpaque() return; } #endif + const bool recreateBackingStore = extra->topextra->backingStore.data() && ( + QApplicationPrivate::graphics_system_name == QLatin1String("openvg") || + QApplicationPrivate::graphics_system_name == QLatin1String("opengl") + ); if (requireAlphaChannel) { - const TDisplayMode displayMode = static_cast(window->SetRequiredDisplayMode(EColor16MA)); - if (window->SetTransparencyAlphaChannel() == KErrNone) { + window->SetRequiredDisplayMode(EColor16MA); + if (window->SetTransparencyAlphaChannel() == KErrNone) window->SetBackgroundColor(TRgb(255, 255, 255, 0)); - extra->topextra->nativeWindowTransparencyEnabled = 1; - if (extra->topextra->backingStore.data() && ( - QApplicationPrivate::graphics_system_name == QLatin1String("openvg") - || QApplicationPrivate::graphics_system_name == QLatin1String("opengl"))) { - // Semi-transparent EGL surfaces aren't supported. We need to - // recreate backing store to get translucent surface (raster surface). - extra->topextra->backingStore.create(q); - extra->topextra->backingStore.registerWidget(q); - // FixNativeOrientation() will not work without an EGL surface. + } else { + if (recreateBackingStore) { + // Clear the UI surface to ensure that the EGL surface content is visible + CWsScreenDevice *screenDevice = S60->screenDevice(q); + QScopedPointer gc(new CWindowGc(screenDevice)); + const int err = gc->Construct(); + if (!err) { + gc->Activate(*window); + window->BeginRedraw(); + gc->SetDrawMode(CWindowGc::EDrawModeWriteAlpha); + gc->SetBrushColor(TRgb(0, 0, 0, 0)); + gc->Clear(TRect(0, 0, q->width(), q->height())); + window->EndRedraw(); + } + } + if (extra->topextra->nativeWindowTransparencyEnabled) + window->SetTransparentRegion(TRegionFix<1>()); + } + extra->topextra->nativeWindowTransparencyEnabled = requireAlphaChannel; + if (recreateBackingStore) { + extra->topextra->backingStore.create(q); + extra->topextra->backingStore.registerWidget(q); + bool noSystemRotationDisabled = false; + if (requireAlphaChannel) { + if (q->testAttribute(Qt::WA_SymbianNoSystemRotation)) { + // FixNativeOrientation() will not work without an EGL surface q->setAttribute(Qt::WA_SymbianNoSystemRotation, false); + noSystemRotationDisabled = true; } + } else { + q->setAttribute(Qt::WA_SymbianNoSystemRotation, extra->topextra->noSystemRotationDisabled); } - } else if (extra->topextra->nativeWindowTransparencyEnabled) { - window->SetTransparentRegion(TRegionFix<1>()); - extra->topextra->nativeWindowTransparencyEnabled = 0; + extra->topextra->noSystemRotationDisabled = noSystemRotationDisabled; } } @@ -1020,6 +1035,7 @@ void QWidgetPrivate::createTLSysExtra() extra->topextra->inExpose = 0; extra->topextra->nativeWindowTransparencyEnabled = 0; extra->topextra->forcedToRaster = 0; + extra->topextra->noSystemRotationDisabled = 0; } void QWidgetPrivate::deleteTLSysExtra() diff --git a/tests/auto/qwidget/tst_qwidget.cpp b/tests/auto/qwidget/tst_qwidget.cpp index 43dd077..2161f99 100644 --- a/tests/auto/qwidget/tst_qwidget.cpp +++ b/tests/auto/qwidget/tst_qwidget.cpp @@ -397,6 +397,9 @@ private slots: void minimizedWindowModeTransitions(); void normalWindowModeTransitions(); void focusSwitchClosesPopupMenu(); +#if !defined(Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE) + void opacityChangeCausesBackingStoreRecreation(); +#endif #endif void focusProxyAndInputMethods(); @@ -10336,7 +10339,47 @@ void tst_QWidget::focusSwitchClosesPopupMenu() mainWindow.activateWindow(); QVERIFY(!CEikonEnv::Static()->AppUiFactory()->MenuBar()->IsDisplayed()); } -#endif + +#if !defined(Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE) +class OpacityChangeWidget : public QWidget +{ +public: + OpacityChangeWidget() : m_paintEngineType(QPaintEngine::MaxUser) { } + void paintEvent(QPaintEvent *) + { + QPainter painter(this); + m_paintEngineType = painter.paintEngine()->type(); + } + QPaintEngine::Type paintEngineType() const { return m_paintEngineType; } +private: + QPaintEngine::Type m_paintEngineType; +}; + +void tst_QWidget::opacityChangeCausesBackingStoreRecreation() +{ + OpacityChangeWidget w; + w.show(); + QTest::qWaitForWindowShown(&w); + const QPaintEngine::Type type = w.paintEngineType(); + if (QPaintEngine::OpenGL != type && QPaintEngine::OpenVG != type) { + QSKIP("Test case is only valid when using opengl or openvg graphics system", SkipAll); + } else { + if (QApplicationPrivate::instance()->useTranslucentEGLSurfaces) { + QSKIP("Test case is only valid when EGL surface transparency is not supported", SkipAll); + } else { + // Making window transparent should force switch to raster graphics system + w.setAttribute(Qt::WA_TranslucentBackground, true); + w.repaint(); + QCOMPARE(w.paintEngineType(), QPaintEngine::Raster); + // Making window opaque should cause switch back to previous graphics system + w.setAttribute(Qt::WA_TranslucentBackground, false); + w.repaint(); + QCOMPARE(w.paintEngineType(), type); + } + } +} +#endif // !Q_SYMBIAN_SEMITRANSPARENT_BG_SURFACE +#endif // Q_OS_SYMBIAN class InputContextTester : public QInputContext { -- cgit v0.12 From 74d548094307f3f3609890d839d5742296e611da Mon Sep 17 00:00:00 2001 From: Jani Hautakangas Date: Tue, 27 Sep 2011 23:55:42 +0300 Subject: A patch for 'Fix to QtOpenGL crash' EGL API was used also on non-EGL Symbian platforms which generated compile errors when compiling Qt on those platforms. This patch #ifdefs EGL usage correctly for EGL enabled platforms only. Reviewed-by: TRUSTME --- src/gui/painting/qgraphicssystemex_symbian.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/painting/qgraphicssystemex_symbian.cpp b/src/gui/painting/qgraphicssystemex_symbian.cpp index 46731d8..32e040f 100644 --- a/src/gui/painting/qgraphicssystemex_symbian.cpp +++ b/src/gui/painting/qgraphicssystemex_symbian.cpp @@ -55,8 +55,10 @@ QT_BEGIN_NAMESPACE static bool bcm2727Initialized = false; static bool bcm2727 = false; +#ifdef Q_SYMBIAN_SUPPORTS_SURFACES typedef EGLBoolean (*NOK_resource_profiling)(EGLDisplay, EGLint, EGLint*, EGLint, EGLint*); #define EGL_PROF_TOTAL_MEMORY_NOK 0x3070 +#endif // Detect if Qt is running on BCM2727 chip. // BCM2727 is a special case on Symbian because -- cgit v0.12