Open CASCADE 3D view on Qt 4.5rc1

Cross-post from the discussion board of our Open CASCADE group at LinkedIn.

Hi everyone,
Yesterday I migrated to Qt 4.5 release candidate 1 and encountered ugly behavior of a 3D view (Windows XP Sp2, VS2005 SP1): - it does not draw upon view creation (i.e. contents of a former widget on view's place remain) - when dragging (resizing) contents get black and/or some artifacts/garbage appear - after switching between windows (e.g. Alt-Tab) the view is black - dragging rectangular selection ribbon creates a black area, also with ribbon traces. It's likely due to Widget attributes changed behavior (that QtSoftware introduced in 4.5 - they claim about some performance improvement).

If someone has successful experience with migration to 4.5, I would appreciate sharing it before my own diving into Qt code.
Here are the flags I use (worth mentioning that in Qt 4.4 they gave ugly flickering which I did not yet come up to investigation).
Thanks ! Roman

(Edited after posting: LinkedIn condenses the code below. Here is a screenshot of the code below - http://myphoto.nnov.ru/img/c52fecc42b0c02204fc1f4fba6c08969.gif )

QOOcc_View3d::QOOcc_View3d(...)
{
...
setAttribute (Qt::WA_PaintOnScreen);
setAttribute (Qt::WA_OpaquePaintEvent);
setAttribute (Qt::WA_NoSystemBackground);

//attempt to minimize flicking (it still remains however)
//setAutoFillBackground (false);
}

/*! Called by showEvent() only once.
*/
bool QOOcc_View3d::Init()
{
...

//sets the window handles of this widget to view
int windowHandle = (int)winId();
short hi, lo;
lo = (short) windowHandle;
hi = (short) (windowHandle >> 16);

#ifdef WNT
Handle(WNT_Window) aWnd = new WNT_Window(Handle(Graphic3d_WNTGraphicDevice)::
DownCast(myView->Viewer()->Device()), hi, lo);
//aWnd->SetDoubleBuffer (Standard_True);//does not remove flickerring
aWnd->SetFlags( WDF_NOERASEBKGRND );

#else
...
}

Francois Lauzon's picture

Hello Roman,
I have the same settings as you (XP sp3 and VS2005 sp1) and it's working fine with qt 4.5rc1.

Here are my settings, similar to yours:

setAutoFillBackground( false );
setAttribute( Qt::WA_NoSystemBackground );
setAttribute( Qt::WA_OpaquePaintEvent );
setAttribute( Qt::WA_WState_Visible, false );

I give the following QGLFormat to the QGLWidget:
QGLFormat(QGL::DoubleBuffer|QGL::DirectRendering|QGL::Rgba|QGL::NoStencilBuffer)

and here is how I create the OCC graphic device and WNT_Window:

// create the 3d device
myDevice=new Graphic3d_WNTGraphicDevice();

// create the NT window
myWindow=new WNT_Window(myDevice,winId(),Quantity_NOC_BLACK);
myWindow->SetDoubleBuffer(Standard_True);
myWindow->SetFlags( WDF_NOERASEBKGRND );

For the ribbon selection, I use a Visual3d_Layer to do the job (before, in Qt3 I was using Qt Painter, but I had flicker problem and ribbon traces).

Good Luck,
Francois.

Roman Lygin's picture

Hello Francois,

Nope, no luck :-(. I heard that ATI Radeon cards has a bad reputation for 3D but on the other hand Qt4.4 was just fine (except flicker). So, tracing Qt sources will likely be unavoidable.

I'm curious why do you need QGLWidget ? Do you use its some specific API ?

Thanks for the hint with the ribbon selection via Visual3d_Layer, never thought in that direction. Currently I'm using both layers (under- and over-) so will have to think how to merge this functionality as well.

Thanks again.
Roman
---
opencascade.blogspot.com - blog on Open CASCADE

Francois Lauzon's picture

Hello Roman,
for QGLWidget, it just part of the same framework we have been using since 1999, starting with Qt 2.X (I think) and Cas.Cade 2.X (I think), Cascade and Qt api haven't change much in that area.

Regarding the graphic cards, we mainly use NVIDIA Quadro and some ATI FireGL but no Radeon...

Francois.

Roman Lygin's picture

Bingo !!!

Here is the trick - must add a redefined method paintEngine() that returns 0.
The new code in QWidget::paintEngine() in Qt 4.5 resets painting on screen if there are no 3DShow or 3DDirect enabled, while in Qt4.4 this was not the case. Thus, the flag Qt::WA_PaintOnScreen did not have any effect. In your case, Francois, with QGLWidget attached, paintEngine() was somehow redefined and PaintOnScreen was working.

Once I have added a redefined virtual paintEngine() everything got back to work (I believe this addition should be back-portable).

Flicking (in resizing at least) is still very ugly (my feeling is that performance got only worse with move to 4.5). Do you have a flicker as well ?

Thanks.
Roman

---
opencascade.blogspot.com - blog on Open CASCADE

P Dolbey's picture

Missed this post while writing the one below. Yet I discovered that fix a while ago as well -

/*!
\brief Returns a NULL QPaintEngine
This should result in a minor performance benefit.
*/
QPaintEngine* Qocc3dWidget::paintEngine() const
{
return NULL;
}

Pete

Francois Lauzon's picture

Hello Roman,
I don't have any flicker, I have merge several classes into one (I haven't test it), to show what we do here to create a Qt class derived from QGLWidget (release 4.5) that work well with OCC (release 6.3):

Qt_WindowOpenGL::Qt_WindowOpenGL(QWidget * parent,
const char * name,
const QGLWidget * shareWidget,
Qt::WFlags f) :
QGLWidget(QGLFormat(QGL::DoubleBuffer|QGL::DirectRendering|QGL::Rgba|QGL::NoStencilBuffer),parent,name,shareWidget,f)
{
setMouseTracking(true);

// Avoid Qt background clears to improve resizing speed,
// along with a couple of other attributes
setAutoFillBackground( false );
setAttribute( Qt::WA_NoSystemBackground );
setAttribute( Qt::WA_OpaquePaintEvent );
setAttribute( Qt::WA_WState_Visible, false );

// needed so that this widget will process keyboard event
setFocusPolicy(Qt::StrongFocus);

// set default keyboard focus on this widget
setFocus();

try {
// create the 3d device
myDevice=new Graphic3d_WNTGraphicDevice();

// create the NT window
myWindow=new WNT_Window(myDevice,winId(),Quantity_NOC_BLACK);
myWindow->SetDoubleBuffer(Standard_True);
myWindow->SetFlags( WDF_NOERASEBKGRND );

// create a viewer
TCollection_ExtendedString Name("Viewer");
TCollection_AsciiString Domain("DBMReflex");
myViewer=new V3d_Viewer(myDevice,Name.ToExtString(),Domain.ToCString(),1000.0,V3d_XposYnegZpos,
Quantity_NOC_BLACK,V3d_ZBUFFER,V3d_GOURAUD,V3d_WAIT,Standard_True,
Standard_True,V3d_TEX_ALL);
myViewer->SetDefaultLights();
myViewer->SetLightOn();
Handle_V3d_View myView=new V3d_OrthographicView(myViewer);
myView->SetWindow(myWindow);
myView->ZBufferTriedronSetup();
myView->TriedronDisplay(Aspect_TOTP_RIGHT_UPPER,Quantity_NOC_YELLOW,0.08);
myView->SetTransparency(Standard_True);
myView->SetSurfaceDetail(V3d_TEX_ALL);
myView->SetComputedMode (Standard_False);

}

// catch any error
catch (Standard_Failure) {
}

setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);

}

void Qt_WindowOpenGL::resizeEvent(QResizeEvent *anEvent)
{
// myView is a Handle_V3d_View
if (!myView.IsNull()) {
myView->Window()->DoResize();
myView->MustBeResized();
}

}

void Qt_WindowOpenGL::paintEvent(QPaintEvent *anEvent)
{
if (!myView.IsNull()) {
// view as just been map
// this appened when we first create the widget
if (myIsMap==Standard_False) {
// we force a resize of the view
myView->MustBeResized();
myIsMap=myView->Window()->IsMapped();
}

myView->Redraw();
}
}

Hope it help someone.
Francois.

Roman Lygin's picture

Hi Francois,

Thanks for sharing your code. I noticed that you do DoResize() twice in resizeEvent() as it's called from myView->MustBeResized(). Did you try not to do it ?
My code is slightly different and I create WNT_Window and map it to a handle only once in redefined showEvent(). This allows to avoid extra checks that you have to do in paintEvent().

Regarding the flicker. It can be worsen by my 3D card but mainly because I have a gradient background (as described here http://opencascade.blogspot.com/2008/12/sexy-background.html. So it gets additional flicks when the background is filled with a solid color (during resize or selection). This is almost unnoticeable when the background already has such solid color.

What is an advantage of inheriting QGLWidget and not just QWidget ?

Thanks again.
Roman

---
opencascade.blogspot.com - blog on Open CASCADE

Francois Lauzon's picture

Hello Roman,
like I said in an earlier post, this class (in fact it's three different classes that work together in a flexible framework, but for the purpose of sharing the essential of the code I regroup them under one class) has been working since 1999, at that time we had to inherit from QGLWidget.

As for the DoResize() and MustBeResized(), I don't recal exactly with which version, but in some case the OCC View didn't get resize properly if this wasn't done (it think it was with the IRIX version of CasCade and Qt).

And for the mapping, you're right. But in our case we wanted to keep our Qt_WindowOpenGL unaware of Cascade, it's part of the framework we used (the example I gave is everything merge together).

Francois.

P Dolbey's picture

I had a similar problem when the Qt 4.4 rc (or tp) came out. I spent a lot of time minimizing flicker on win32. I haven't tried 4.5rc yet - I'll compile it up tonight. But here's some of my experiences from memory

First I assume you the "compulsory" GreenBlatt patch applied to OCC as per
http://www.opencascade.org/org/forum/thread_8291/ - significant reduction in flicker. You don't need step 1 as its covered by the setAutoFillBackground( false ); setting in Qt.

Here's my set of "settings". I needed the setAttribute( Qt::WA_NativeWindow ); as by Qt started using a backing store at 4.4 which didn't work well with the tp version - but seemed better after 4.4.0 came out.
-----------------------------------------------------------------

// Avoid Qt background clears to improve resizing speed,
// along with a couple of other attributes
setAutoFillBackground( false );
setAttribute( Qt::WA_NoSystemBackground );
#if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0))
setAttribute( Qt::WA_NativeWindow );
#endif

// This next attribute seems to be the secret of allowing OCC on Win32
// to "own" the window, even though its only supposed to work on X11.
setAttribute( Qt::WA_PaintOnScreen );
-----------------------------------------------------------------

Another trick for improving refresh during resize. Don't redraw in the resize event code. This will cause 2 occ display events to happen for each resize. What I did was to flip-flop a flag in the resize and paint events. Setting the flag in resize just caused MustBeResized on the next paint, other wise just redraw.

And you're right about ATI - I wasted a lot of time with an X300 card. It failed to observe the standard wgl constaint on win32 that you can't set more than 1 rendering contexts on a HWND - therby giving the impression that you can render in a QGLWidget derived class. Eventually had to give in and inherit from QWidget. However I'd like to revisit this issue sometime soon - if you take a look at http://labs.trolltech.com/blogs/2008/12/02/widgets-enter-the-third-dimen... and http://labs.trolltech.com/blogs/2008/06/27/accelerate-your-widgets-with-... you'll see how you can embed QWidgets/dialogs boxes directly into an OpenGL scene - imagine how this could look with active widgets embedded into a OCC model. To accomplish this we need to divorce the opengl rendering commands from the window management, delegating this to a QGLWidget based class. My thoughts are to branch off a new TKQtOpenGl lib - but I'll have to see if there's demand for it!

I'll let you know how I get on with 4.5.

Pete

Roman Lygin's picture

Hi Peter,

Thanks much for shedding more light on this !
Regarding NativeWindow flag - it worked for me with/without it without any difference, though Qt doc says it must be set.

Nice trick about flag in resize and paint events. I thought if indeed one could be avoided but did not experiment yet, so now will do.

Regarding the patch. I once noticed it but it went away from my radar and I forgot about it. Perhaps step#3 will do the trick, as step #2 had no difference in my earlier experiments. Step 1 seems MFC-specific.

Regarding paintEngine(), you could now extend the comment to mention that it's a must have for Qt 4.5. Qt::WA_PaintOnScreen is indeed documented as X11-specific in Qt doc but it's clearly cross-platform.

Roman

P.S. Hope to see OCC folks reading this and improving Qt-based samples in future release so that other users could avoid inventing the same wheels ;-).

---
opencascade.blogspot.com - blog on Open CASCADE

Roman Lygin's picture

The flicker has gone after applying the patch in OpenGl_telem_util.c mentioned by Pete (the url above). So, thanks much !
One more hint to avoid another possible flicker (appearing during redrawing part of the window – e.g. during rectangular selection) is to redraw an entire view, not just an area.

void QOOcc_View3d::paintEvent(QPaintEvent* theEvent)
{
//do not use area - redraw entire view instead
//when area is >1/2 window area Open CASCADE will enforce entire view
//redraw anyway, otherwise it draws in the front buffer causing a flicker
//const QRect& aR = theEvent->rect();
myView->Redraw (/*aR.x(), aR.y(), aR.width(), aR.height()*/);
}

The comment says it all. The code that causes a flicker is in call_togl_redraw_area() in OpenGl_togl_redraw.c
I initially thought that redrawing an area should be faster but diving deeper into the code I think it redraws the entire buffer (in scissors mode) and causes an annoying flicker.

---
opencascade.blogspot.com - blog on Open CASCADE

P Dolbey's picture

Compiled up Qt 4.5 rc1 with my latest qtgeom code last night - negligible flicker with the standard bottle, grid and my mechanism for gradient backgrounds.

Pete