This likely means that either the Scintilla updates are not being
clipped properly (as the CView classes all share the same DC, being
part of the main frame DC), or that the update bitblts are being
delayed until the next frame flyback, by which time the view I am
shaking about has moved.
This does not happen if I shake a modeless CDialog about over the
Scintilla view, but then this has a separate DC and in Aero there
would be no way the two DCs could interact.
This happens in both the 3.0.2 pre-release and in 3.0.1.
> This does not happen if I shake a modeless CDialog about over the
> Scintilla view, but then this has a separate DC and in Aero there
> would be no way the two DCs could interact.
OK, I am unfamiliar with CView but it sounds like these do not have
their own window. If you are sharing drawing between Direct2D and GDI
in a single WM_PAINT then I expect there has to be some layering
discipline like only drawing with GDI over fully rendered Direct2D.
Neil
All the windows in question are child windows of the MDI application.
Scite has a tabbed document interface, so you only see one document at
a time, but if imagine Scite as an MDI app with each document in its
own window, that is the arrangement. I have no control at all over
what is drawn... By moving a small window rapidly over the Scintilla
window I am generating invalid regions that need painting. The paint
operations in the Scintilla operation are overwriting the moving
window... but only with Aero on.
When Aero is on, all top-level windows are independent, being drawn
off-screen, then being blitted on to the screen. However, sibling
windows share the same underlying DC (with clipping handling not
drawing on each other). But, in this case, when the update happens,
the clip rectangle does not appear to protect the moving sibling
window. There can be two logical explanations: 1) the clip region is
wrong (unlikely, as it works when windows do not move rapidly), 2) the
clip region was right, but the windows have subsequently moved.
It does seem that the D2D only does screen updates once per frame...
Is it possible that the Scintilla code is not waiting for the
underlying update to occur before returning in the WM_PAINT? This
would not cause a problem unless the clip regions were changing fast
enough that by the time the screen update happened, they had already
changed.
> When Aero is on, all top-level windows are independent, being drawn
> off-screen, then being blitted on to the screen. However, sibling
> windows share the same underlying DC (with clipping handling not
> drawing on each other). But, in this case, when the update happens,
> the clip rectangle does not appear to protect the moving sibling
> window. There can be two logical explanations: 1) the clip region is
> wrong (unlikely, as it works when windows do not move rapidly), 2) the
> clip region was right, but the windows have subsequently moved.
Does moving a window only overdraw the edge or does it overdraw the
whole window? If it is the whole window then it is unlikely to be a
lagging clip region.
> It does seem that the D2D only does screen updates once per frame...
> Is it possible that the Scintilla code is not waiting for the
> underlying update to occur before returning in the WM_PAINT?
Scintilla calls EndDraw on the render target before returning from
WM_PAINT. EndDraw is supposed to be the end of its responsibilities.
The render target could be released but that is something that is
avoided in Direct2D examples and may mean that no drawing is done.
Its possible that the freeing of the update region in the call to
BeginPaint is leading to Direct2D not picking up the update region -
some MS Direct2D examples do not call BeginPaint / EndPaint. But some
examples do call BeginPaint. You could try disabling BeginPaint and
EndPaint.
Drawing could be forced to the screen with DwmFlush
http://msdn.microsoft.com/en-us/library/windows/desktop/dd389405(v=VS.85).aspx
Neil
> As I understand it, BeginPaint picks up the invalid region, creates a suitable
> clip region and cleans out the invalid region... What does this function for
> DirectWrite?
ID2D1HwndRenderTarget::BeginDraw could be doing this but I don't really know.
> I'll see if I can find the time to try out the flush call... where
> would you put it?
For testing, at the end of painting, perhaps just after ::EndPaint.
Since it doesn't appear needed by SciTE, for example, there'd have to
be a control over whether it was called if in a release version.
It may be an idea to ask about this in a Direct2D forum. I don't
know much about the DirectX world or Aero composition.
Neil
Removing BeginPaint and EndPaint causes the entire scintilla window
including the H Scrollbar to be black, but the overwriting still
happens.
Moving the BeginPaint to just before the EndPaint leaves a black
window, but the H Scrollbar is back.
Debugging is tricky as putting in a break causes focus changes, which
repaints windows, so I added a 1 second delay in various places in
WndPaint, then dragged the window quickly diagonally up to the left.
1) Putting a delay at the start, before anything happens leaves
everything OK.
2) Putting a delay at the end, after the EndPaint causes a horrible
mess.
The critical position for the delay is putting it before pRenderTarget-
>EndDraw() is OK, after causes the problem.
The damage happens in the bit blit that updates the moving window. The
first delay pause (when the dragged window typically moves 1 pixel
left and up) is either OK or 1 pixel round the frame is damaged.
However, the mouse has moved further during the delay, queuing up
another window move which is quite a few pixels in x and y and which
makes it clear what is happening. There are two processes: 1) a bit
blit of the moving window, 2) update of the invalid regions of the
Scintilla window revealed by the move. The problem is that these are
happening in the wrong order. The Scintilla window gets updated
(damaging the bitmap area where the moving window is going), then the
copy happens, corrupting the image of the moving window.
This seems to suggest that the bitmap copy for the move with Aero
enabled is happening on a different thread, possibly waiting for the
once per frame event. If we put in a 17 millisecond delay before the
EndDraw(), then all is well. Anything less and there is mess after
some moves. I have no idea how to fix this.
This also has a bitmap of the screen after the damage is done - the
frame on the window is intact as it got repainted, but I'm sure you
can image what it looked like before this happened. My screen update
rate is 60 Hz (in case you are wandering where the 17 ms comes from).
> I have reported this to Microsoft as a possible bug - maybe you cannot
> mix GDI(+) and D2D:
GDI and Direct2D are certainly supposed to be usable together.
There is substantial Microsoft documentation on this:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd756743(v=VS.85).aspx
While the Scintilla Direct2D code was being developed, there was a
mix of Direct2D+DirectWrite and GDI calls and the GDI calls were
converted item by item. The biggest problem with that was performance
as there were many switches between the two APIs. There had to be
EndDraw or Flush calls at some switchover points.
This was all in the context of drawing to a simple HWND/HDC and
also to some pixmaps so your scenario is more complex.
Neil
--- Ferda
http://msdn.microsoft.com/en-us/library/windows/desktop/dd368122(v=vs.85).aspx
http://www.gamedev.net/topic/565428-why-is-direct2d-so-slow/ (end of the thread)
--- Ferda
> USE_D2D is 1 by default when compiled on VS2010 maybe someone else will be
> affected too.
That just enables the Direct2D code. The application still chooses
GDI or Direct2D with SCI_SETTECHNOLOGY.
Neil
In any case, with Aero enabled, there may be no difference as the
target bitmap is an off-screen bitmap (so no Vertical Blank), which I
guess is copied (if dirty) to the screen(s) in the vertical Blank
interrupt for each screen. At this point, I should mention that I am
using two monitors, so the concept of WaitForVerticalBlank() may be
moot as there is the question of which monitor we are talking about.
And before you ask, yes, I have tried this with only one monitor,
there is no difference.
You do not need to run MFC to see the effect. As far as I can deduce,
it will happen in any situation where there is an instance of
Scintilla in a window and there is a sibling or child window that can
move over it and Aero is enabled. Aero makes off-screen bitmaps for
all top-level windows, but all windows within each top-level window
share the same off-screen bitmap and then rely on clipping to prevent
trampling on each other. I'm sure you could see this effect in Scite
if you created another Child window at the same level as the current
Scintilla windows, then dragged it over the Scintilla window.
Now contains a short VS2005 project that builds an app to illustrate
the problem. You guys should be able to download it from there... if
not I can email it anyone who is interested in looking at this problem.
When the horizontal scroll bar is turned off, the vertical scroll
bar is often overdrawn. Sometimes it has to be clicked on to go into
this mode. A small demonstration program which illustrates this is at
http://www.scintilla.org/kruptd2d.zip . It has been simplified to not
require Scintilla.
http://www.scintilla.org/BadVScroll.png
Similar drawing problems to Greg's can be shown by uncommenting the
calls to BeginPaint/EndPaint in the demonstration app.
Neil
My text display view contains a window above the Scintilla window that
is used for buttons and status messages. When I first display the view
holding Scintilla, this additional area is now blank. It appears if I
resize the window (which will force a draw). My theory is that
Scintilla gets to draw first and is marking the entire window area as
valid when it should just be the scintilla rectangle.
If I shake the moving widow over the scintilla area, it seems less
prone to be damaged, and sometimes survives a (short) while, but it
does get damaged.
The test Scintilla application I posted on the MS system is more
resilient; maybe to do with the lack of an extra window area, which
may cause co-ordinate offsets. Anyhow, this also does show up damage.
The easiest way to show it (if you can compile it) is to open up two
windows (you can leave them empty of text), then minimise one and move
it about over the other. There seems to be no fixed pattern to the
corruption, but I can always make it happen.
I have expanded the MS report I made to include your simplified
example (please accept my apologies in advance if you object to
this...) as it seems to show at the very least that there is a
documentation problem, and quite possibly a real problem with Aero and
D2D. Although the best I have ever get from them is to acknowledge the
problem and say they will fix it in a future release, if you don't
tell them they do nothing.
> My text display view contains a window above the Scintilla window that
> is used for buttons and status messages. When I first display the view
> holding Scintilla, this additional area is now blank. It appears if I
> resize the window (which will force a draw).
I have seen some similar effects with one window being treated
specially. Even with the kruptd2d simplified example, the background
window has its top right corner drawn wrong initially. Its only after
it has received focus that it draws correctly.
> The test Scintilla application I posted on the MS system is more
> resilient; maybe to do with the lack of an extra window area, which
> may cause co-ordinate offsets. Anyhow, this also does show up damage.
> The easiest way to show it (if you can compile it) is to open up two
> windows (you can leave them empty of text), then minimise one and move
> it about over the other. There seems to be no fixed pattern to the
> corruption, but I can always make it happen.
Corruption mostly occurs in the non-client area. While this could
be just because the edges are more exposed to damage and the edges are
in the non-client area, the correlation appears much stronger than
this to me. The non-client area is likely being drawn by GDI rather
than Direct2D so there could be a sequencing issue there. Changing the
non-client area from its original state (such as turning off a scroll
bar) also sounds like a good candidate for a cause as Direct2D could
be storing information about the window when the render target is
created and this could go stale.
Neil
Neil
> The patch that avoids BeginPaint/EndPaint and GetUpdateRgn with
> Direct2D has now been committed.
While this appeared better in some tests, it has caused problems
drawing the tab bar and output pane in SciTE so will have to be
reverted.
Neil
> While this appeared better in some tests, it has caused problems
> drawing the tab bar and output pane in SciTE so will have to be
> reverted.
Reverted in Hg.
Neil
> "Thanks for the report. We have investigated the issue here and we
> believe that the problem is caused by “concurrent” D2D calls.
The quotes around 'concurrent' here may indicate that this is not
threading concurrency but instead refers to reentrance or simply
drawing on a single surface from two referring objects (such as two
render targets).
> implemented it in CWnd, each D2D rendering was wrapped by
> LockRenderTarget/UnlockRenderTarget calls (see CWnd::DoD2DPaint,
> CWnd::LockRenderTarget and CWnd::UnlockRenderTarget() methods).
'Lock' may be referring to threading mutexes but it could also be
referring to memory or graphics state management. A surface allocated
on the graphics card may need to be pinned while being accessed from
the CPU and there may also have to be cache flushing when switching to
drawing from another window.
Searching for 'LockRenderTarget' and similar terms doesn't produce
any exact matches but does show some Direct3D matches. It may be
necessary to access Direct3D resources behind the Direct2D render
target to call a Lock method. Methods like IDirect3DSurface9::LockRect
could be what is meant here although this is well beyond my
experience.
> I am very happy to implement this and test it... but do you have
> suggestions for where I should apply the locks?
Without seeing the MFC code I can only guess at what is being suggested.
Neil
> I thought CViews are CWnds (and so HWNDs) and thus have separate DCs.
Neil
MS say that they use LockRenderTarget to prevent this problem - but I am struggling to find this code. I will keep looking.
First the good news.
Maybe this is the problem? You seem to use a ID2D1DCRenderTarget for the ListBox, but nowhere else.
However, making this type of change feels a bit all pervasive... I don't feel I understand enough about what is going on to take on this sort of hack. As I understand things, you should use ID2D1DCRenderTarget when you need to interoperate with GDI... If you are concerned with speed we may need a build option to choose which to use so that folks going totally Direct2D don't pay the GDI penalty.
I hope this is clear. I can spare a little more time if you can give me more guidance about what to change to use a ID2D1DCRenderTarget.
Your application should create render targets once and hold onto them for the life of the application or until the render target's EndDraw method returns the D2DERR_RECREATE_TARGET error. When you receive this error, you need to recreate the render target (and any resources it created).
The Windows documentation for ID1D2DCRenderTarget interface says:Your application should create render targets once and hold onto them for the life of the application or until the render target's EndDraw method returns the D2DERR_RECREATE_TARGET error. When you receive this error, you need to recreate the render target (and any resources it created).
Just a note to say that I have had no further problems since implementing the TECHNOLOGY = 3, so it would be nice to see this officially implemented... possibly as the only D2D implementation as both the others fail (in my situation). I suspect that other users are not going to test this until it is part of the distribution.
I would look up the bugs 1602/1603 (DXGI warnings on app exit) with a view to investigating but do not know how to find them.
Sorry Neil, you misunderstood. A couple of messages back you referred me to bugs 1602/1603 in Scintilla which are something to do with fixes for the DXGI problem but caused crashes. This is what I meant about not being able to find the bugs.