DirectWrite bug in 3.0.2 pre-release

612 views
Skip to first unread message

Greg

unread,
Dec 9, 2011, 10:40:41 AM12/9/11
to scintilla-interest
In my MFC application I have multiple CView classes, some of which a
Scintilla based, some are not. If I grab (click the mouse button in
the title bar of) another CView derived window (of any type) and shake
it around (vigorously) on top of a Sincilla-based view with
DirectWrite in use, the edges of the shaken view (which should not
change) get overwritten by the Scintilla view that is underneath. I am
running in Windows 7-64 with Aero enabled. If I disable Aero or
DirectWrite in Scintilla, this does not happen. (We have seen this
here on two machines with different video hardware).

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.

Neil Hodgson

unread,
Dec 9, 2011, 7:11:47 PM12/9/11
to scintilla...@googlegroups.com
Greg:

> 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

Greg

unread,
Dec 10, 2011, 1:26:57 PM12/10/11
to scintilla-interest
I guess I am not being very clear.

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.

Neil Hodgson

unread,
Dec 10, 2011, 5:26:11 PM12/10/11
to scintilla...@googlegroups.com
Greg:

> 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

Greg

unread,
Dec 11, 2011, 11:53:15 AM12/11/11
to scintilla-interest
>    Does moving a window only overdraw the edge or does it overdraw the> whole window?It overdraws from the edge (but not only the frame).
>    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.Will have to wait for weekdays as I don't have Aero-capable hardware at home. I somehow doubt that this would make any difference... 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?
>    Drawing could be forced to the screen with DwmFlush...This seems more likely. As this only occurs with Aero ON, i.e. when we are likely rendering to an off-screen buffer that is being blitted to the screen, we need an explanation for what is happening that explains what is different about rendering to an off-screen bitmap. In my situation, the Aero off-screen bitmap gets blitted to the screen as the application bitmap (if I understand what is going on), and our scintilla window is part of this. In non-aero mode, the bitmap is the actual screen.
When a window is moved over the scintilla window, this causes a bitmap
copy of the moving window, plus invalidation of the uncovered areas of
the Scintilla window. In non-aero mode, this is always OK, which
suggests that something must be serialising access to the screen
bitmap between the D2D rendering and the bitmap copies...
In Aero mode, I suspect that the actual render to the off-screen
bitmap is being delayed and that another window move get in ahead of
it. If I "shake" the window from side to side, this move may be back
in the opposite direction to the last one, resulting in the moving
window now getting rendered over by the D2D render, whenever it
happens.
I'll see if I can find the time to try out the flush call... where
would you put it?

Neil Hodgson

unread,
Dec 11, 2011, 5:54:25 PM12/11/11
to scintilla...@googlegroups.com
Greg:

> 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

Greg

unread,
Dec 12, 2011, 7:25:32 AM12/12/11
to scintilla-interest
Adding the flush call made no difference at all.

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.

Greg

unread,
Dec 12, 2011, 10:13:27 AM12/12/11
to scintilla-interest
I have reported this to Microsoft as a possible bug - maybe you cannot
mix GDI(+) and D2D:

https://connect.microsoft.com/VisualStudio/feedback/details/713420/direct2d-aero-interaction-causes-image-damage

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).

Neil Hodgson

unread,
Dec 12, 2011, 5:20:17 PM12/12/11
to scintilla...@googlegroups.com
Greg:

> 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

Ferdinand Prantl

unread,
Dec 13, 2011, 3:33:38 AM12/13/11
to scintilla...@googlegroups.com
Seeing those 17ms reminded me about synchronizing the frame rendering
frequency with the vsync. By forcing the rendering to occur just once
per page refresh you got rid of the artefacts. "Wait for vsync" is
used to get rid of image "tearing" in (usually full-screen) games - if
you have decent hardware and high refresh rate (otherwise
triple-buffering does better). However, wait for vsymc is turned on by
default in Windows Vista windowing mode. It was not in Windows XP and
I think Windows 7 with Aero would be like Vista. Power saving could
turn off the vsync interrupt but I doubt your CPU felt like saving
power when you were vigorously shaking your application :-) While I
find the resemblance intriguing I can't see a direct vsync affinity in
the GDI/D2D window rendering code.

--- Ferda

Greg

unread,
Dec 13, 2011, 6:03:55 AM12/13/11
to scintilla-interest
A quick scan of the Microsoft doc you pointed me at suggests that the
D2D rendering is to a separate (from GDI) surface that is then copied
to the GDI surface.
With Aero on, the GDI surface is an off-screen bitmap. This off-screen
bitmap is then copied to the screen during the frame blanking period.
With Aero off, the GDI surface is the screen... it is not clear if we
wait for blanking or not.
To get the problem, the moving window and the background window must
be sibling windows... if they descend from different top level
windows, with Aero on they will have different off-screen bitmaps and
will not interact. You do not see this in Scite because you use tabbed
sibling windows for the documents. Any dialogs you create are most
likely top level windows, so you will not see this effect.
I fear that I must time out on this problem as not using D2D is a
workaround, at least for now. My theory on what is happening is that
when Aero is on, the copy to the GDI surface from the D2D rendering
surface does not wait for frame blanking (which it would do with Aero
off). The window move operation which caused the invalidate is either
being deferred to the frame flyback, or is happening in parallel (I
doubt this), either way, this gets the GDI surface updated with text
before (or during) the window move, resulting in the problem.

Ferdinand Prantl

unread,
Dec 13, 2011, 2:38:16 PM12/13/11
to scintilla...@googlegroups.com
If you have not grown tired yet ;-) you can try to play with vsync
on/off in the D2D rendering target creation: setting presentOptions to
D2D1_PRESENT_OPTIONS_NONE or to D2D1_PRESENT_OPTIONS_IMMEDIATELY and
checking out what it changes.

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

Greg

unread,
Dec 14, 2011, 6:58:59 AM12/14/11
to scintilla-interest
On Dec 13, 7:38 pm, Ferdinand Prantl <pran...@gmail.com> wrote:
> If you have not grown tired yet ;-) you can try to play with vsync
> on/off in the D2D rendering target creation: setting presentOptions to
> D2D1_PRESENT_OPTIONS_NONE or to D2D1_PRESENT_OPTIONS_IMMEDIATELY and
> checking out what it changes.
Ferda: It seems that the surface we use is created by
CreateCompatibleRenderTarget(), which doesn't have the option of
changing the presentation. If you can suggest what changes to make to
the Scintilla code to try these ideas out I can do it, but I have a
lot of other things that I should be doing and I am not a D2D expert.
In the long term, getting this to work would be good, but in the short
term I can just not use D2D.
I do think that this is a serious bug; just because Scite doesn't hit
it doesn't mean it won't occur in other situations - basically any MDI
app with a Scintilla window will have this problem in Aero mode.

Ferdinand Prantl

unread,
Dec 15, 2011, 3:19:59 AM12/15/11
to scintilla...@googlegroups.com
I'm afraid I'm no D2D expert either. Neither have I an MFC application to test. What I thought about was replacing the call:
 D2D1::HwndRenderTargetProperties(hw, size)
at CreateHwndRenderTarget(...) in ScintillaWin::EnsureRenderTarget() with:
 D2D1::HwndRenderTargetProperties(hw, size, D2D1_PRESENT_OPTIONS_NONE)
and then with:
  D2D1::HwndRenderTargetProperties(hw, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY)
and testing how it behaves. The former should be default but vsync settings depend on graphic settings and graphic hardware. I used the latter to make a D2D demo running more fluently on my hardware. It is common that games have a flag to control waiting for vsymc to tune the behavior on various hardware. But all this stuff that it could be a vsync issue is my speculation. If it helped, the solution would be cheap; if not I'm afraid that I don't know.

You found that a cunning sleep in the drawing routine helped. Actually, the EndDraw() should "sleep" if vsync was forced on and the rendering is "too fast" for the vertical refresh. The waiting could be done also by an explicit call to WaitForVerticalBlank() from DirectDraw but I saw some code using other means to compute the right timespan to wait and waiting by traditional timer because that method spins and thus increases CPU load.

USE_D2D is 1 by default when compiled on VS2010 maybe someone else will be affected too.

   --- Ferda

Neil Hodgson

unread,
Dec 15, 2011, 3:25:39 AM12/15/11
to scintilla...@googlegroups.com
Ferdinand Prantl:

> 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

Greg

unread,
Dec 15, 2011, 5:52:12 AM12/15/11
to scintilla-interest
Ferda:
I tried what you suggested, but it made no difference to the problem.
D2D1_PRESENT_OPTIONS_NONE is the default, so was what is currently
set. Setting D2D1_PRESENT_OPTIONS_IMMEDIATELY yielded identical
results as far as I could see.

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.

Greg

unread,
Dec 16, 2011, 6:06:36 AM12/16/11
to scintilla-interest
https://connect.microsoft.com/VisualStudio/feedback/details/713420/direct2d-aero-interaction-causes-image-damage#tabs

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.

Neil Hodgson

unread,
Dec 17, 2011, 10:36:50 PM12/17/11
to scintilla...@googlegroups.com
Managed to reproduce the problem but looks like 2 problems now. The
first is that calling BeginPaint/EndPaint and GetUpdateRgn did appear
to be causing problems so here is a patch that only uses these with
GDI drawing and avoids them for Direct2D drawing. This clears up a lot
of the image overwrite except when the horizontal scroll bar is turned
off as it is for line wrapping.

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

d2dchange.patch

Greg

unread,
Dec 19, 2011, 6:15:17 AM12/19/11
to scintilla-interest
I have applied your patches and the problem is not as bad, but is
still present for me. I now have an additional problem:

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.

Neil Hodgson

unread,
Dec 19, 2011, 5:50:31 PM12/19/11
to scintilla...@googlegroups.com
Greg:

> 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

Greg

unread,
Dec 21, 2011, 5:50:05 AM12/21/11
to scintilla-interest
Neil:
As ever, thanks for the work you do... I don't feel I can contribute
much more to this at the moment. I'll let you know if MS make any
response.

Neil Hodgson

unread,
Dec 21, 2011, 7:12:59 PM12/21/11
to scintilla...@googlegroups.com
The patch that avoids BeginPaint/EndPaint and GetUpdateRgn with
Direct2D has now been committed.

Neil

Neil Hodgson

unread,
Dec 29, 2011, 5:34:56 AM12/29/11
to scintilla...@googlegroups.com
Me:

>   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

Neil Hodgson

unread,
Dec 29, 2011, 5:04:47 PM12/29/11
to scintilla...@googlegroups.com
Me:

>   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

Greg

unread,
Feb 1, 2012, 5:05:39 AM2/1/12
to scintilla-interest
I have had a response from Pat Brenner at Microsoft:

"Thanks for the report. We have investigated the issue here and we
believe that the problem is caused by “concurrent” D2D calls. If you
take a look at the MFC D2D support, you will find that when we
implemented it in CWnd, each D2D rendering was wrapped by
LockRenderTarget/UnlockRenderTarget calls (see CWnd::DoD2DPaint,
CWnd::LockRenderTarget and CWnd::UnlockRenderTarget() methods).

You have commented, "If you put a Delay(17) (17 ms is my refresh rate)
before the EndDraw(), the problem goes away (at the cost of wasted
time and CPU cycles)." We believe this indicates that the problem is
that you need to synchronize your D2D calls. We suggest that you use
LockRenderTarget/UnlockRenderTarget (or something similar) to do so."

I am very happy to implement this and test it... but do you have
suggestions for where I should apply the locks?

Greg

unread,
Feb 1, 2012, 6:09:43 AM2/1/12
to scintilla-interest
I can find the CWnd:DoD2DPaint() but in the VS2010 MFC that I have,
there are no LockRenderTarget() or UnlockRenderTarget() routines to
look at, so I suspect that this is in the next MFC release. However, I
think what they mean is that we must use a CriticalSection object to
wrap rendering. This in turn means that we need to create and kill one
off in suitable places when D2D is in use. If Neil can suggest where I
might do this I can have a shot at adding this.

One might question how we can get into such a mess when there is only
one thread in Scintilla, unless they mean that the D2D rendering is
runing in a separate thread... but if it is, how will putting a
critical section in our code stop their thread from running out of
order? If they are just deferring updates to frame flyback I still
don't see how a critical section would help.

Neil Hodgson

unread,
Feb 1, 2012, 6:13:35 PM2/1/12
to scintilla...@googlegroups.com
Greg:

> "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

Greg

unread,
Feb 2, 2012, 5:20:42 AM2/2/12
to scintilla-interest
I posted a reponse to MS yesterday asking for clarification... I'll
let you know if they respond.

Greg

unread,
Mar 9, 2012, 8:13:55 AM3/9/12
to scintilla-interest
MS have just closed the report and ignored my request for an
explanation of LockRenderTarget() - I suspect it is in the VS-Next
rather than in the current version.

Just to say that this is still an issue in 3.0.4 (i.e. the various
changes to the D2D rendering have made no difference).

Greg

Greg

unread,
May 23, 2013, 7:53:17 AM5/23/13
to scintilla...@googlegroups.com
As you have made a change to D2D support in 3.3.2 I retested the problem with overwriting of a window moving over a Scintilla window and can report that it still fails.

What is odd (to me) is that the corruption of the moving window (which is an MFC CView derived item moving over the CView derived window that holds the Scintilla-based control) happens on the bottom edge of the moving window when it moved upwards... Now it is a bit hard to understand how this can happen if the corruption happens when a Scintilla update is deferred to the flyback time as one presumes that the moving window has moved up the screen... so how can it get painted over... it feels like it is something to do with view clipping (the two MFC CViews ultimately share the same bitmap/DC) being messed up by deferred updates. The moving window is just being bit-blitted as it is dragged... somehow the clipping out of the moving window from the Scintilla window is getting out of sync... or getting interleaved with the bit-blit of the moving window... perhaps this is what the LockRenderTarget stuff is meant to prevent?

Neil Hodgson

unread,
May 23, 2013, 8:27:00 PM5/23/13
to scintilla...@googlegroups.com
Greg:

> What is odd (to me) is that the corruption of the moving window (which is an MFC CView derived item moving over the CView derived window that holds the Scintilla-based control) happens on the bottom edge of the moving window when it moved upwards... Now it is a bit hard to understand how this can happen if the corruption happens when a Scintilla update is deferred to the flyback time as one presumes that the moving window has moved up the screen... so how can it get painted over... it feels like it is something to do with view clipping (the two MFC CViews ultimately share the same bitmap/DC)

I thought CViews are CWnds (and so HWNDs) and thus have separate DCs.

If you are using Direct2D for drawing content surrounding the Scintilla window then its possible your view creates a surface over a rectangle that includes Scintilla. It saves the Scintilla pixels, does some drawing, and then blitting that whole surface onto the screen is scheduled. Scintilla's drawing is also scheduled but they overlap and the final composition occurs in an unfortunate sequence. Its also possible that the surface areas are larger than asked for, perhaps scaled up to an 8x8 pixel grid or similar, leading to unexpected overlap.

Neil

Greg

unread,
May 24, 2013, 5:31:48 AM5/24/13
to scintilla...@googlegroups.com, nyama...@me.com

> I thought CViews are CWnds (and so HWNDs) and thus have separate DCs.
An HWnd identifies a window. A top level window (in Aero at least) has an off-screen bitmap that is shared by all the children of the window... I suspect that when a child gets a DC it is given a shifted and clipped (or not clipped - ClipSiblings flags etc) version of the parent DC. In the case of an MFC application, the main application window frame is a top level window, all the CView (representing documents) as children of it and thus share the bitmap.


>If you are using Direct2D for drawing content surrounding the Scintilla window then its possible your view creates a surface over a rectangle that includes Scintilla. It saves the Scintilla pixels, does some drawing, and then blitting that whole surface onto the screen is scheduled. Scintilla's drawing is also scheduled but they overlap and the final composition occurs in an unfortunate sequence. Its also possible that the surface areas are larger than asked for, perhaps scaled up to an 8x8 pixel grid or similar, leading to unexpected overlap.
The problem happens regardless of the technology used to draw the moving window; it happens if the moving window is minimised so it is just the title bar. I am pretty sure it is related to the sequence of bit blits that update the image - but I have no idea how to control this.

MS say that they use LockRenderTarget to prevent this problem - but I am struggling to find this code. I will keep looking.
 

   Neil

Greg

unread,
May 24, 2013, 5:40:28 AM5/24/13
to scintilla...@googlegroups.com, nyama...@me.com
Just in case anyone else thinks of this, I have tried using:
D2D1_RENDER_TARGET_USAGE_GDI_COMPATIBLE
in place of
D2D1_RENDER_TARGET_USAGE_NONE as the rest of my application is probably using GDI, but it makes no difference.

Neil Hodgson

unread,
May 24, 2013, 8:42:05 AM5/24/13
to scintilla...@googlegroups.com
Greg:

MS say that they use LockRenderTarget to prevent this problem - but I am struggling to find this code. I will keep looking.
 
    Do you have the version of MFC from Visual Studio 2012? I couldn't find the documentation for this version online. 

    Neil

Greg

unread,
May 28, 2013, 8:20:39 AM5/28/13
to scintilla...@googlegroups.com
I do have VS2012 and have done a bit more digging. It looks like to make this work in an MFC app (or in any app that is using GDI) there is more syncing work to be done. In MFC, it seems that you must call CWinApp::EnableD2DSupport() and also call CWnd::EnableD2DSupport() for the window itself. Then they add:

    afx_msg LRESULT OnDraw2D(WPARAM wParam, LPARAM lParam); // in the header


BEGIN_MESSAGE_MAP(CSciCtrl, CWnd)
    ON_REGISTERED_MESSAGE(AFX_WM_DRAW2D, &CSciCtrl::OnDraw2D)
END_MESSAGE_MAP()

afx_msg LRESULT CSciCtrl::OnDraw2D(WPARAM wParam, LPARAM lParam)
{
    CHwndRenderTarget* pRenderTarget = (CHwndRenderTarget*)lParam;
    ASSERT_VALID(pRenderTarget);
    ...your code in here to render...
    return TRUE;
}

I guess that this is tried before the CWnd::OnPaint()...

This seems to be saying that to make this work, MFC must handle the render target so that it can arbitrate between the various items that are attempting to update it. Unless you can suggest a simple method to link this into the underlying Scintilla D2D rendering I think I will just have to live with the GDI rendering (at least until I have a lot more time to look at this).

The code suggested here is available on the Web, but there seems very little help for people trying to do this.

Greg

unread,
May 28, 2013, 9:45:34 AM5/28/13
to scintilla...@googlegroups.com
I realise that to make any sense of the previous post, I need to explain that a CSciCtrl is a CWnd descendent that wraps up a SciDirect pointer. The ScriCtrl::Create(...) holds:

BOOL CSciCtrl::Create(DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID,
                                          DWORD dwExStyle, LPVOID lpParam)
{
    if ((m_hModScintilla == NULL) &&         // if first user...
        !InitScintilla())                                   // ...load the Scintilla DLL
        return FALSE;
    BOOL bOK = CreateEx(dwExStyle, _T("scintilla"), NULL, dwStyle, rect, pParentWnd, nID, lpParam);
    if (bOK)            // if all was OK we can now send messages to the window
    {
        ASSERT(::IsWindow(m_hWnd));                         // moan if we failed
        m_pSciDirect = (SciFnDirect)SendMessage(SCI_GETDIRECTFUNCTION);  // get address to use
        m_pSciData = SendMessage(SCI_GETDIRECTPOINTER);     // get our instance data pointer
 #ifdef SCI_USED2D
        EnableD2DSupport();
        if (IsD2DSupportEnabled())
            SetTechnology(SC_TECHNOLOGY_DIRECTWRITE);       // attempt to set direct write
        if (GetTechnology() == SC_TECHNOLOGY_DIRECTWRITE)   // if using DirectWrite
        {
            SetBufferedDraw(false);                         // probably right
            SetTwoPhaseDraw(false);
        }
#endif

        SetFontQuality(SC_EFF_QUALITY_LCD_OPTIMIZED);       // and best quality font
    }
    return bOK;
}

The rest of the class is there to turn the scintilla messages into member functions and convenience functions. To blend the MFC code with the scintilla control we need to get scintilla to use the CHwndRenderTarget we get from the AFX_WM_DRAW2D registered message... then there is a chance that the GDI and D2D painting will be proprely sequenced.

Neil Hodgson

unread,
May 28, 2013, 9:58:31 AM5/28/13
to scintilla...@googlegroups.com
Greg:

> The rest of the class is there to turn the scintilla messages into member functions and convenience functions. To blend the MFC code with the scintilla control we need to get scintilla to use the CHwndRenderTarget we get from the AFX_WM_DRAW2D registered message... then there is a chance that the GDI and D2D painting will be proprely sequenced.

As a proof of concept, you can just munge the Scintilla code heavily. Remove the current window handling in ScintillaWin and splice it into an MFC shell. It should be possible to only implement a few methods to get drawing working.

Then try to refactor so that minimal changes are needed to ScintillaWin - maybe some way to pass the render target into WndPaint.

Neil

Greg Smith

unread,
Apr 22, 2014, 3:26:27 PM4/22/14
to scintilla...@googlegroups.com
I have tried again with the latest Scintilla (just in case the various recent D2D fixes made any difference). I'm afraid that the problem is still present.

I've had a look at ScintillaWin.cxx... I don't really see any simple way through to calling the existing code. The CHwndRenderTarget* you get from the lParam of the OnDraw2D call does contain pointers to both a ID2D1RenderTarget and a ID2D1HwndRenderTarget, so it is likely doing a lot of the same things as your Surface is doing. However, it also contains other state information: list of CD2DResource objects and a default CD2DTextFormat object.

To reiterate the problem: in WIndows 7 with Aero on, if I move a window around on top of a scintilla window, what happens is that the moving windows is corrupted by Scintilla text. The Scintilla window itself is never corrupted. If I move the top window to the right, it is the left edge of the window that is corrupted with Scintilla output. The moving window and Scintilla are sharing the same off-screen bitmap, and then this gets copied to the screen bitmap (with Aero on). With Aero off, the problem does not occur.

This suggests the following to me for the case when I drag the moving (non-scintilla) window to the right:

1) The window is dragged to the right. This books two Windows operations in the off-screen bitmap: Bit-blit the moving rectangle right and repaint the uncovered area.
2) The bit-blit of the moving window to the right gets handed off to the GDI and the Scintilla update of the uncovered area is requested via D2D
3) A screen refresh comes up (requiring the off-screen bitmap to be copied to the screen)
4) The bit-blit of the moving window gets suspended because of the screen update, but the Scintilla update goes ahead
5) The off-screen bit-blit now happens, and copies data that has already been written by Scintilla

This sequence exactly explains what I see on the screen. Of course, this may well not be what actually happens, but to explain what I see on screen, the off-screen bit-blit and the Scintilla D2D paint order must be reversed. I have never seen a corruption (when moving right) that has other than a clean vertical edge, so it is not likely to be simultaneous execution of the D2D update and the bit-blit as I would expect to occasionally see an incomplete text update, and it is an all or nothing effect.

Any thoughts?

Neil Hodgson

unread,
Apr 23, 2014, 10:04:33 PM4/23/14
to scintilla...@googlegroups.com
Greg Smith:

> To reiterate the problem: in WIndows 7 with Aero on, if I move a window around on top of a scintilla window, what happens is that the moving windows is corrupted by Scintilla text. The Scintilla window itself is never corrupted. If I move the top window to the right, it is the left edge of the window that is corrupted with Scintilla output. The moving window and Scintilla are sharing the same off-screen bitmap, and then this gets copied to the screen bitmap (with Aero on). With Aero off, the problem does not occur.

Its possible that having a child window displayed over a Direct2D-drawn window just isn’t directly supported by Direct2D on Windows 7 with Aero. There have been problems in the past with combining DirectX with other drawing techniques. MFC could be working around this by performing clipping and draw flushing for its own windows so they can be combined like this but that doesn’t extend to non-MFC windows.

It should be possible to use Direct2D drawing with some additional redirection to prevent clashes with other Windows if the clashes are due to sharing the underlying window bitmap. One approach would be to respond to the paint request by creating an offscreen Direct2D bitmap, asking Scintilla to draw into that, then using GDI to copy the bitmap contents onto the paint HDC. Pretty sure this will isolate the Direct2D drawing and so work but will cost a little memory and performance.

Another technique may be to use GDI interop and using Direct2D to draw into the paint HDC. Less likely to be isolated but could be faster.
http://msdn.microsoft.com/en-us/library/windows/desktop/dd370971(v=vs.85).aspx

One of these could then be packaged into another technology choice (SC_TECHNOLOGY_DW_INDIRECT) that could be used by applications that want to use child windows along with the benefits of Direct2D/DirectWrite drawing.

These seem to be reasonably simple to try but its not really something I’m interested in working on myself since the current Direct2D code works fine for SciTE.

There is a debug tool that may be worthwhile trying to see if it says anything interesting with the current code:
http://msdn.microsoft.com/en-us/library/windows/desktop/ee794278(v=vs.85).aspx
Even without the debug layer, there are two DXGI warnings when shutting down SciTE but that is because the factory objects aren’t released at shutdown.

Neil

Greg Smith

unread,
Sep 10, 2014, 8:15:03 AM9/10/14
to scintilla...@googlegroups.com
I have just got around to trying out the SC_TECHNOLOGY_DIRECTWRITERETAIN to see if it makes any difference to the corruption problem (expecting the answer no) and sadly it does not. It appears that in the MFC situation, MS are providing us with the AFX_WM_DRAW2D registered message because we need it to synchronise GDI calls and D2D calls.

At the moment, resizing a D2D-drawn window (apart from this corruption problem) is unpleasant when you drag the left hand size of the window left as you get black/blank areas appearing on the right before the window gets copied in (I guess in fly-back time). I am pretty sure that this is caused by the deferred screen update (in Aero mode) that is also responsible for the corruption problem.

I have looked at the current rendering code, and to be honest, I am at a loss to know where to start with it. What we end up with from MS is a call:

afx_msg LRESULT CSciCtrl::OnDraw2D(WPARAM wParam, LPARAM lParam)
{
    CHwndRenderTarget* pRenderTarget = reinterpret_cast<CHwndRenderTarget*>(lParam);
    ASSERT_VALID(pRenderTarget);

   // We need to render in here

    return TRUE;
}

To make any progress I'll need some clues on how to connect the pRenderTarget to Scintilla.

Neil Hodgson

unread,
Sep 10, 2014, 8:21:36 PM9/10/14
to scintilla...@googlegroups.com
Greg Smith:

> I have just got around to trying out the SC_TECHNOLOGY_DIRECTWRITERETAIN to see if it makes any difference to the corruption problem (expecting the answer no) and sadly it does not.

I had some hope that this was going to be the fix.

> I have looked at the current rendering code, and to be honest, I am at a loss to know where to start with it. What we end up with from MS is a call:
>
> afx_msg LRESULT CSciCtrl::OnDraw2D(WPARAM wParam, LPARAM lParam)
> {
> CHwndRenderTarget* pRenderTarget = reinterpret_cast<CHwndRenderTarget*>(lParam);
> ASSERT_VALID(pRenderTarget);
>
> // We need to render in here
>
> return TRUE;
> }
>
> To make any progress I'll need some clues on how to connect the pRenderTarget to Scintilla.

Duplicate the ScintillaWin::WndPaint method in win32/ScintillaWin.cxx and rename it to, say, ScintillaWin::RenderTargetPaint taking an ID2D1RenderTarget* parameter. Remove anything in RenderTargetPaint related to *Ocx* or SC_TECHNOLOGY_DEFAULT. Dummy the update area fields (rcPaint, hRgnUpdate, paintingAllText) to reflect redrawing the whole area. Then find some way to expose this to your code: may be simplest (for now) to make a new SCI_RENDERTARGET API that takes the ID2D1RenderTarget* as its lParam.

Neil

Greg Smith

unread,
Sep 11, 2014, 2:11:20 PM9/11/14
to scintilla...@googlegroups.com
I have done as you suggested and have got my code calling RenderTargetPaint(ID2D1RenderTarget* pRenderTarget) so I have the MS CHwndRenderTarget* that I was given by them cast to an ID2D1RenderTarget* (which I hope is OK). MSDN says that ID2D1HwndRenderTarget inhertis from ID2D2RenderTarget, so I trus it is OK to treat it as the same thing.

I have not munged the rcClient or rcPaint or hRgnUpdate,,, the two rectangles look OK. Both are {0,0,860,672} or whatever. PaintingAllText get set to true.

EnsureRenderTarget() gets called, which makes another render target, and calls DropGraphics().
        AutoSurface surfaceWindow(pRenderTarget, this);
gets constructed, using the render target I passed in, not the one created in EnsureRenderTarget() - is this what you intended?
if (surfaceWindow) is true, so we call pRenderTarget->BeginDraw();
Which generates an access violation.
pRenderTarget (the one passed in) is an HwndRenderTarget.

Inspection of the MS example seems to show that we must not call BeginDraw() or EndDraw()... MS do all this for us. All we have to do is render onto the already created window.

I removed the BeginDraw() and EndDraw() calls.

Now I get a crash in EditView::RefreshPixMaps() within the pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid); call.

Do you have any suggestions? The crash seems to be on the first call into the pRenderTarget.

It looks to me that in an MFC app, MS are already doing almost all the heavy lifting needed in the AFX_WM_DRAW2D message. They seem to create the render target for you and take care of BeginDraw) and EndDraw() and synchronise it with the GDI.


Neil Hodgson

unread,
Sep 12, 2014, 4:05:37 AM9/12/14
to scintilla...@googlegroups.com
Greg Smith:

> I have done as you suggested and have got my code calling RenderTargetPaint(ID2D1RenderTarget* pRenderTarget) so I have the MS CHwndRenderTarget* that I was given by them cast to an ID2D1RenderTarget* (which I hope is OK).

Its doubtful you can can directly cast the CHwndRenderTarget* to an ID2D1RenderTarget* if that’s what you mean. You want to cast the result of GetHwndRenderTarget() (an ID2D1HwndRenderTarget*) to an ID2D1RenderTarget* since ID2D1HwndRenderTarget inherits from ID2D1RenderTarget.

> Now I get a crash in EditView::RefreshPixMaps() within the pixmapIndentGuide->InitPixMap(1, vsDraw.lineHeight + 1, surfaceWindow, wid); call.
>
> Do you have any suggestions? The crash seems to be on the first call into the pRenderTarget.

Trace in and check that the passed in surface really is a SurfaceD2D since an unexpected SurfaceGDI will cause trouble. Check that psurfOther->pRenderTarget is the render target you are providing. Then try calling GetPixelFormat (which is the first call to the render target in InitPixMap) on your pRenderTarget to see if it crashes and what it returns.

Neil

Greg Smith

unread,
Sep 12, 2014, 6:23:36 AM9/12/14
to scintilla...@googlegroups.com

First the good news.

Passing the result of GetHwndRenderTarget() into the new rendering code works; stupid of me, I was being misled by MSDN saying that it inherited from the other interface. I can also confirm that the old WndPaint is never being called, only the new AFX_WM_DRAW2D message is happening. To first order, everything seems to be working exactly as it did before.

Now the bad.
This makes NO difference to the corruption of a view that is dragged across the Scintilla version.

I think that for you to see the effect you need a MDI Windows application running in Aero mode with multiple views. Make one view smallish and drag it over a Scintilla view and "shake" it. You do not get the problem when you drag a top level window in the same application (a modeless dialog, for example). What baffles me about this is that if the moving window moves down, the top edge is corrupted. If it moves up, the bottom edge suffers. If I move right it is the left edge, if I move left, it is the right edge.

I have just managed to show that if I "jerk" the window down a short distance, the text that corrupts the top of the moving window is the Scintilla content that was under the moved window. This suggests that there is a race between the window updates... Scintilla knows that there is an invalid region and somehow manages to update it before the moving window has grabbed the bitmap... this all seems far fetched. However, in Aero mode there are off-screen bitmaps that the application actually draws to and then that is bit-blitted to the screen, so there is room for all sorts of problems...

I know this could be a MS problem, but surely it would show up in other applications if it were. I am open to suggestions as to what to try.


Neil Hodgson

unread,
Sep 12, 2014, 7:31:49 PM9/12/14
to scintilla...@googlegroups.com
Greg Smith:

> This suggests that there is a race between the window updates... Scintilla knows that there is an invalid region and somehow manages to update it before the moving window has grabbed the bitmap... this all seems far fetched. However, in Aero mode there are off-screen bitmaps that the application actually draws to and then that is bit-blitted to the screen, so there is room for all sorts of problems…

I don’t have much insight here. Something I may look into is disabling the system caret due to one of the replies on
https://sourceforge.net/p/scintilla/bugs/1643/

You could try changing WM_ERASEBKGND to call DefWindowProc but that should effect the inside of Scintilla’s area, not the surrounds.

The only route that appears likely to work is to use Direct2D to draw into a bitmap but to copy this to screen using GDI in a normal WM_PAINT handler. This was mentioned in the “d2d and Scintilla” thread. That way there would be no clever display synchronisation or flipping being done by Direct2D but the results would look like Direct2D.

Neil

Greg Smith

unread,
Sep 15, 2014, 9:33:59 AM9/15/14
to scintilla...@googlegroups.com
I think Scintilla is off the hook for this one. I followed the MS instructions to create a D2D MFC app in their walkthrough (search for OnDraw2D) just go to http://msdn.microsoft.com/en-us/library/gg482848.aspx. I then edited the MainFrame code EnableMDITabbedGroups call to set the first argument FALSE, so the MDI windows are floating, not docked.

Then create two windows, and set one small and shake it across the larger one and the same bug appears. I will report this to Microsoft and see if I can get them to take it seriously this time.

Greg

Greg Smith

unread,
Sep 30, 2014, 9:43:51 AM9/30/14
to scintilla...@googlegroups.com
MS have identified a bug in MFC with D2D support, and fixing (well, more accurately, working around it) makes the example that I sent them work (which used only MS code, no Scintilla), but does not fix Scintilla. For interest, the MS code was doing a spurious ValidateRect(NULL) call in the WM_PAINT handler after the D2D update had happened - it is not clear (to me) how this caused the problem.

The MS code goes to great lengths to lock access to the D2D surface which is used by all the CView windows (as they all have a common underlying bitmap). However, even when I use the surface that they provide and pass it into Scintilla, I get exactly the same problem (even after fixing the MS problem). I am coming around to the view that this really is a Scintilla bug.

Consider the situation where two windows share the same underlying bitmap and we drag one over the other. The dragged window is moved, purely by bitblt and the underlying window gets updated for regions that are uncovered. You must be relying on a clip region to prevent incorrect updates. I note that Scintilla does get hold of the update region, but does very little with it. It is not clear to me what happens if the update region is complex, for example it has a hole in the middle. This seems to be possible if you shake a small window over a larger one in Aero mode. I don't know enough about D2D drawing to know how it copes with clip regions.

Is it possible that Scintilla updates part of the bitmap that is in the client area, but that another window is using? It may be that with Aero enabled, the order of operations is different, for example if the bitblt of the moving window without Aero was done by taking a copy of the moving window, fixing the underneath window, then bitblt the old (so overwrites do not appear), but with Aero, the bitblt was done immediately and it relied on clipping to handle updates of the underlying window.

Neil Hodgson

unread,
Sep 30, 2014, 7:56:40 PM9/30/14
to scintilla...@googlegroups.com
Greg Smith:

> I note that Scintilla does get hold of the update region, but does very little with it. It is not clear to me what happens if the update region is complex, for example it has a hole in the middle.

You could try to set the render target to clip to the update region. For simple rectangles there is PushAxisAlignedClip. Its possible that complex regions could be broken down into a sequence of rectangles and layers used.

http://msdn.microsoft.com/en-us/library/windows/desktop/dd316860(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/dd756654(v=vs.85).aspx#clipping_an_arbitrary_shape

And from StackOverflow
http://stackoverflow.com/questions/11309768/direct2d-leave-a-region-of-a-window-render-target-untouched

Neil

Greg Smith

unread,
Oct 1, 2014, 8:11:27 AM10/1/14
to scintilla...@googlegroups.com, nyama...@me.com
AFAICT the updates you do are correct and are correctly clipped, even for complex regions (which do occur). I have just logged more information on the MS site: Search for:

AFX_WM_DRAW2D in one MDI CView can damage GDI portion of displayed bitmap in other CView in Aero mode in Windows 7

to see where this has got to. Basically, I am certain that this is a race between the bitblt that moves the non-changing window over the Scintilla window and the Scintilla drawing. If I put a break in the Scintilla drawing anywhere before the EndDraw(), the moved window is moved, then when I step over the EndDraw(), the screen updates correctly. This means that all the clipping is working correctly.

If I put a break just after the EndDraw(), what I see is exactly what you would expect if the drawing happened first, then the bitblt. My suggestion is that the bitblt is being deferred until the next flyback and the drawing is getting in first. This only happens in Aero mode, and only when you have windows sharing the same bitmap, which is likely why it doesn't show up much. I fear the only way to get MS to pay attention will be to construct the same situation without using Scintilla.

Greg Smith

unread,
Oct 1, 2014, 11:51:32 AM10/1/14
to scintilla...@googlegroups.com, nyama...@me.com
I have built a MS-only bit of code that draws has a separate window that draws using D2D with a window class of STATIC and I cannot get it to fail, so I am now looking very closely at how the Scintilla window is set up. Nothing to report yet, however, I have a question for Neil:
Is there a reason to have the class styles CS_HREDRAW and CSVREDRAW? These are usually applied when you have a window that scales the contents to the window, not one where we just uncover more or less window. Or am I missing something here?
I guess the next thing to do is to replace all the scintilla drawing with just cleaning out the surface and see if that causes the same effect.

Greg Smith

unread,
Oct 1, 2014, 1:17:31 PM10/1/14
to scintilla...@googlegroups.com, nyama...@me.com
It seems that calling BeginPaint() and EndPaint() is not necessary, and may be damaging, when dealing with D2D. Certainly, the MS code does not do it. The logical sequence of the MS code when seeing a WM_PAINT is:

1) Get a pRenderTarget by calling LockRenderTarget(). This gets the window render target with a lock. The target is created when you call CWnd::EnableD2DSupport() for the window.
2) If NULL, ulock and bail.
3) If not valid, Create() it for this window
4) Call BeginDraw()
5) Send the AFX_WM_DRAW2D message - which is just render to the pRenderTarget
6) ICall EndDraw(). If got D2ERR_RECREATE_TARGET recreate the target and send AFX_WM_RECREATED2DRESOURCES message
7) Unlock the render target and return TRUE if the drawing thought it was OK, FALSE if not

There is no call to BeginPaint/EndPaint. I suspect that the EndPaint is acting rather like the ValidateRect(NULL) that caused the problem in the MS code.

I have now removed the Begin/EndPaint from the Rendering code. I have the problem that something, not part of the rendering, is setting the entire window to black, causing flicker and also corrupting the moving window with blackness... the scintilla text is no longer corrupting it, which may mean I am close to a solution.


Greg Smith

unread,
Oct 2, 2014, 7:37:33 AM10/2/14
to scintilla...@googlegroups.com
I have managed to reproduce the same problem in the MS-only code, so the ball is back in their court. I am pretty sure that it is a sequencing problem between windows that share the same underlying bitmap and that it is likely a fault in Aero. As Aero is no longer flavour of the month, there may be no solution. I'm stopping working on this now as I have taken it as far as I feel is worthwhile. If you follow the MS interaction, you will see that they now suggest doing exactly what you so anyway (using a CDCRenderTarget, which is a wrapper for a ID2D1DCRenderTarget).

I think that the Begin/EndPaint() speculation is a red herring... but there is no MS doc that I can find on how these calls interact with the render targets.

Greg Smith

unread,
Oct 2, 2014, 2:35:53 PM10/2/14
to scintilla...@googlegroups.com
I now have code from Microsoft that works correctly with a separate window and does not trample over windows that share the same bitmap. There is still a problem with their MFC support for Direct2D, but this is their problem and not Scintilla's. In the MS-only code, I have a CWnd derived class based on a "STATIC" (rather than a "Scintilla" window class (CWnd is a MFC wrapper for a hWnd).. There is a member variable defined for the class:

CDCRenderTarget m_dcRenderTarget; // This is a wrapper for an ID2D1DCRenderTarget.

Then in the class constructor I have:
D2D1_RENDER_TARGET_PROPERTIES props;

    props.type = D2D1_RENDER_TARGET_TYPE_DEFAULT;
    props.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM;
    props.pixelFormat.alphaMode = D2D1_ALPHA_MODE_IGNORE;
    props.dpiX = props.dpiY = 0;
    props.usage = D2D1_RENDER_TARGET_USAGE_NONE;
    props.minLevel = D2D1_FEATURE_LEVEL_DEFAULT;

    m_dcRenderTarget.Create(props);

Then my OnPaint - handler for WM_PAINT, I have:

void CD2DCtrl::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    CRect rect;
    GetClientRect(rect);
    m_dcRenderTarget.BindDC(dc, rect);
    m_dcRenderTarget.BeginDraw();
    m_dcRenderTarget.FillRectangle(rect, m_pLinearGradientBrush);
    m_dcRenderTarget.DrawText(_T("Hello, World!"), rect, m_pBlackBrush, m_pTextFormat);
    m_dcRenderTarget.EndDraw();
}

The CPaintDC does the BeginPaint() and EndPaint() in its destructor. This all seems to work and integrate properly with the existng DC.

AFAICT, this is pretty much what you do, except they use a ID2D1DCRenderTarget and also have the BindDC call, which becomes m_pDCRenderTarget->BindDC, which is essentially calling ID2D1DCRenderTarget::BindDC().
I don't see this happening in Scintilla.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.

Neil Hodgson

unread,
Oct 3, 2014, 7:24:15 AM10/3/14
to scintilla...@googlegroups.com
Greg Smith:

Maybe this is the problem? You seem to use a ID2D1DCRenderTarget for the ListBox, but nowhere else.

   The ListBox is a different case since the ListBox control is handling the WM_PAINT and then handing a DC to Scintilla to draw each line. There is no way to replace the ListBox drawing code with a render target.


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.

   It can probably be a runtime option like SC_TECHNOLOGY_DIRECTWRITERETAIN.

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.

   Here’s a patch to use a DC render target. Set technology to 3 to activate this mode.

   There was an unexpected problem with buffered drawing turned on. The call to Flush when copying the bitmap buffers would fail with D2DERR_WRONG_RESOURCE_DOMAIN. Maybe each freshly created DC render target is a new domain, although I would have thought they’d all be similar enough to use bitmaps compatible with previous calls. Was fixed up  by calling DropGraphics before drawing so that the buffers would be recreated for this DC/RenderTarget. Its possible this problem is the cause of previously reported drawing bugs with the HwndRenderTarget code and adding in DropGraphics may help in those cases.

   Neil

DCRT.patch

Greg Smith

unread,
Oct 3, 2014, 9:55:43 AM10/3/14
to scintilla...@googlegroups.com, nyama...@me.com
I have applied your patches (by hand as I couldn't get TortoiseHg to do it... I guess I need to match the right place in the tree...).

Setting Technology to 3 fixes the problems.

I cannot see how calling DropGraphics() would help with my problem in the HwndRendertarget case, but do you want me to try this out; if so exactly what needs to be tried?

Many thanks for your help with this. I think this will fix all the reports of screen corruption with Scintilla and D2D over the past few years.

Neil Hodgson

unread,
Oct 3, 2014, 8:01:44 PM10/3/14
to Scintilla mailing list
Greg Smith:

> I have applied your patches (by hand as I couldn't get TortoiseHg to do it... I guess I need to match the right place in the tree...).
>
> Setting Technology to 3 fixes the problems.

Good.

For others experiencing Direct2D problems, it would help to try out this patch and report results, both positive and negative.

> I cannot see how calling DropGraphics() would help with my problem in the HwndRendertarget case, but do you want me to try this out; if so exactly what needs to be tried?

It probably won’t help you. It was meant for issues like bug #1643 which could be bad caching where an initial draw works but is then disabled until a change clears the cache.
https://sourceforge.net/p/scintilla/bugs/1643/

To try dropping graphics, move the two added DropGraphics(false) calls up before the test for (technology == 3) so that it occurs for all Direct2D painting. Change to DropGraphics(true) for a stronger cache clear.

Neil

Greg Smith

unread,
Oct 7, 2014, 8:00:39 AM10/7/14
to scintilla...@googlegroups.com, nyama...@me.com
Although the change fixes the corruption problem, it has caused another.

It appears that the screen is not getting updated until the Scintilla windows gets focus!
If you are typing, this manifests itself as a delay of about 0.5 seconds before a typed character appears. If I use my internal scripting language to write to the Scintilla window when it doesn't have focus, nothing appears until I click on the window containing the Scintilla control, at which point it updates. If I change back to SC_TECHNOLOGY_DIRECTWRITE or DEFAULT there is no delay.

The text window with this effect uses a lexer (of our own devising). I mention this because in the same program, there is another Scintilla window that I use as a log view. This one has no styling or lexer. This window is also set to draw using SetTechnology(3) and this one does not suffer from the delay.

Do you have any suggestions? There seems to be use of the WM_FOCUS message to blink the caret (which causes the entire line holding the caret to repaint - even when there is no caret line highlight), and I guess when you make a change, the next caret blink is deferred, hence the delay).


Greg Smith

unread,
Oct 8, 2014, 5:53:00 AM10/8/14
to scintilla...@googlegroups.com, nyama...@me.com
I retract the 0.5 second delay... today I cannot reproduce it.
 I can only assume that I had got my machine into a weird state. So, all is well again with TECHNOLOGY_DCDIRECTWRITE or whatever it will be called. Reading a bit more about this, it appears that the rendering is to a separate (possibly hardware) bitmap which is then copied into the GDI context at EndDraw(). So it will be a bit slower than TECHNOLOGY_DIRECTWRITE, but at least it works!

I will keep checking and will let you know if anything else weird happens.

Greg Smith

unread,
Oct 8, 2014, 10:34:12 AM10/8/14
to scintilla...@googlegroups.com, nyama...@me.com
I'm afraid it is back again. It seems to come and go. My application is MDI and I can have two identical windows, one showing this effect and one not showing the effect.

It could be explained by an invalidate or a paint being lost. If this were the case, adding text to a line would get a refresh at the next caret update as this invalidates the entire line. Yes; after a bit of experimentation that does seem to be what is going on. For example, I have a right-click command that toggles comments. If I select a block of text and toggle the comments, then nothing changes except on the line where the caret is, which updates correctly on the next caret toggle.

I will investigate further.

Greg Smith

unread,
Oct 8, 2014, 10:49:05 AM10/8/14
to scintilla...@googlegroups.com, nyama...@me.com
It seems that the BindDC call is failing. I will investigate.

Greg Smith

unread,
Oct 8, 2014, 10:57:27 AM10/8/14
to scintilla...@googlegroups.com, nyama...@me.com
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).

Scintilla seems to be recreating the target every time around, which is likely the cause of the problem. This would fit in with what I see, which is that it works for a while, then starts to fail. Can you modify your patches to do this?

Neil Hodgson

unread,
Oct 8, 2014, 6:10:22 PM10/8/14
to Scintilla mailing list
Greg Smith:

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).

   That is because of the time cost of recreating resources. Creating a new render target for each paint should be the safer but slower option. The render target is released at the end of the paint, so should not affect the next time.

   Neil

Neil Hodgson

unread,
Oct 9, 2014, 8:38:28 AM10/9/14
to scintilla...@googlegroups.com
Greg Smith:

> It seems that the BindDC call is failing. I will investigate.

Its an idea to find the symbolic name of the failing HRESULT and then perform a web search for that. Also print out the rectangle in case its degenerate.

Neil

Greg Smith

unread,
Oct 9, 2014, 10:15:30 AM10/9/14
to scintilla...@googlegroups.com
The first reason it is failing is that when ScintillaWin::FullPaint()
gets called, it passes 0 as the hdc. I have modified it so that it uses
the same code as non-D2D in the technology == 3 case. However, this does
not explain the problem when typing individual characters as this does
not use FullPaint().

I'll keep trying to reproduce the other problem.
Greg

Neil Hodgson

unread,
Oct 9, 2014, 5:59:38 PM10/9/14
to scintilla...@googlegroups.com
Greg Smith:

> The first reason it is failing is that when ScintillaWin::FullPaint() gets called, it passes 0 as the hdc.

It shouldn’t. That’s the point of the comparison to 3 in this hunk which lumps technology 3 with GDI:

@@ -2541,7 +2590,7 @@
* This paint will not be abandoned.
*/
void ScintillaWin::FullPaint() {
- if (technology == SC_TECHNOLOGY_DEFAULT) {
+ if ((technology == SC_TECHNOLOGY_DEFAULT) || (technology == 3)) {
HDC hdc = ::GetDC(MainHWND());
FullPaintDC(hdc);
::ReleaseDC(MainHWND(), hdc);

Neil

Greg Smith

unread,
Oct 10, 2014, 5:04:00 AM10/10/14
to scintilla...@googlegroups.com, nyama...@me.com
Apologies. As I said before, the patch  was applied by hand and I must have missed this crucial change. I have not had any problems since fixing this, but have not been using it very much due to working on other things). I am trying to imagine how having this error left in could trigger other failures when not using FullPaint().

Greg Smith

unread,
Oct 29, 2014, 12:42:28 PM10/29/14
to scintilla...@googlegroups.com
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.

Greg Smith

unread,
Oct 29, 2014, 12:50:08 PM10/29/14
to scintilla...@googlegroups.com
I intended to mention the:

DXGI WARNING: Process is terminating. Using simple reporting. Please call ReportLiveObjects() at runtime for standard reporting. [ STATE_CREATION WARNING #0: ]
DXGI WARNING: Live Producer at 0x00424B88, Refcount: 4. [ STATE_CREATION WARNING #0: ]
DXGI WARNING: Live Object at 0x0540ED18, Refcount: 3. [ STATE_CREATION WARNING #0: ]
DXGI WARNING: Live                         Object :      1 [ STATE_CREATION WARNING #0: ]

I get after closing down my application. This seems to cause no actual problems, but maybe suggests that we are not closing something down properly. The message has the same number of RefCount values regardless of the number of Scintilla windows I have had open in my application.

Neil Hodgson

unread,
Oct 31, 2014, 2:24:46 AM10/31/14
to scintilla...@googlegroups.com
Greg Smith:
> DXGI WARNING: Process is terminating. Using simple reporting. Please call ReportLiveObjects() at runtime for standard reporting. [ STATE_CREATION WARNING #0: ]…

This was 'fixed' back in May but caused crashes and hangs so was then reverted and an extra release made. See bugs 1602 and 1603.

Neil

Neil Hodgson

unread,
Oct 31, 2014, 2:30:16 AM10/31/14
to scintilla...@googlegroups.com
Greg Smith:

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.

    The code can probably be much simplified by making it more similar to the main path.

   Neil

Neil Hodgson

unread,
Nov 16, 2014, 1:45:09 AM11/16/14
to scintilla...@googlegroups.com
Here is a patch that implements a SC_TECHNOLOGY_DIRECTWRITEDC but is merged better with the other SC_TECHNOLOGY_DIRECTWRITE* cases.

Neil
DCRT.patch

Greg Smith

unread,
Nov 18, 2014, 8:56:45 AM11/18/14
to scintilla...@googlegroups.com, nyama...@me.com
I'd be happy to try this, but it is unclear (to me) what your patch is against.

I have tried applying your patch to the latest Scintilla repository, but MercurialHg just refuses to apply it. The easiest thing (for me) would be for you to commit these changes to Mercurial so I can just grab it. You may recall that last time I had to hand patch your changes, and I have had no problems since (apart from the resource warnings)  that you already know about when the program closes.

I tried to apply you patch by hand, but I get into awful confusion between what you changed last time and this time, so had to abandon it beyond the trivial changes to the .h and .iface files!

Neil Hodgson

unread,
Nov 18, 2014, 5:34:48 PM11/18/14
to Scintilla mailing list
Greg Smith:

> I'd be happy to try this, but it is unclear (to me) what your patch is against.

Its against current Hg (Rev 5322) although I was just about to push an unrelated change to ScintilaWin.cxx.

> I have tried applying your patch to the latest Scintilla repository, but MercurialHg just refuses to apply it.

Patches are a hassle. I just applied it on a different machine using SourceTree and it applied clean. Make sure the line ends in the patch (CRLF) match your source files as that is a common reason for patch problems.

> I tried to apply you patch by hand, but I get into awful confusion between what you changed last time and this time, so had to abandon it beyond the trivial changes to the .h and .iface files!

This is a quite different patch. You shouldn’t need to look at what I changed last time - you are applying this to a clean version without my previous CreateDCRenderTarget changes?

A patched version of ScintillaWin.cxx is attached. Posting from a Mac so may have LF line ends depending on what the mailers do.

Neil
ScintillaWin.cxx

Greg Smith

unread,
Nov 19, 2014, 9:57:03 AM11/19/14
to scintilla...@googlegroups.com
>A patched version of ScintillaWin.cxx is attached. Posting from a Mac
so may have LF line ends depending on what the mailers do. Neil

Hi Neil,

Your patch (thanks for the source) works just as well as the previous
one and passes all my tests. The DXGI warnings are still present (as you
would expect).

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.

Greg

Neil Hodgson

unread,
Nov 19, 2014, 4:57:49 PM11/19/14
to scintilla...@googlegroups.com
Greg Smith:

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.

   Error numbers should be huge and are normally written in hex like 0x88990011.


   Neil

Greg Smith

unread,
Nov 19, 2014, 6:37:24 PM11/19/14
to scintilla...@googlegroups.com
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.

If there are suggestions for fixing this I can have a look at them.

Greg

Neil Hodgson

unread,
Nov 19, 2014, 8:30:39 PM11/19/14
to scintilla...@googlegroups.com
Greg Smith:

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.

Greg Smith

unread,
Nov 20, 2014, 10:32:38 AM11/20/14
to scintilla...@googlegroups.com
So, due to race conditions, you are not allowed to kill off the last D2D
resource from DllMain. Does this mean that we need a new call to say
that we are done with Scintilla, which we call before we unload the
Scintilla DLL?

If this is the case, I can try this out as I already have code to unload
scintilla on the way out... it would be easy to add a call to a
finalising routine just before unloading. Can you tell me what needs to
be released?

I must admit to being a little hazy about why releasing stuff before I
unload a dll is different from releasing in the unload code, unless this
relates to the case where the dll is loaded automatically rather than
dynamically, so the unload order is unspecified.

Greg Smith

Neil Hodgson

unread,
Nov 20, 2014, 5:15:02 PM11/20/14
to scintilla...@googlegroups.com
Greg Smith:

> If this is the case, I can try this out as I already have code to unload scintilla on the way out... it would be easy to add a call to a finalising routine just before unloading. Can you tell me what needs to be released?

See Scintilla_ReleaseResources which is currently used for static builds such as the Sc1 version of SciTE.

This can only be called after all the windows have been freed so there can not be a SCI_RELEASERESOURCES message even through the direct function mechanism.

> I must admit to being a little hazy about why releasing stuff before I unload a dll is different from releasing in the unload code, unless this relates to the case where the dll is loaded automatically rather than dynamically, so the unload order is unspecified.

Yes, its due to things that can fail during process exit with DLL unloading. Raymond Chen’s blog is good for these sorts of issues.
http://blogs.msdn.com/b/oldnewthing/

Neil

Greg Smith

unread,
Nov 21, 2014, 5:44:23 AM11/21/14
to scintilla...@googlegroups.com
On 20/11/2014 22:14, Neil Hodgson wrote:
> See Scintilla_ReleaseResources which is currently used for static
> builds such as the Sc1 version of SciTE. This can only be called after
> all the windows have been freed so there can not be a
> SCI_RELEASERESOURCES message even through the direct function mechanism.
"all the windows"?
At this point the only Scintilla-related window left is the Scintilla
command target - or do I misunderstand what you mean.

Are you saying that we need a direct function call that tears down the
Scintilla command target, then we call Scintilla_ReleaseResources via
GetProcAddress, then we unload the Scintilla dll?

If so, how about a direct SCI_SCINTILLASHUTDOWN call that cleans up
remaining window, sets up Scintilla so that the dll unload does not
repeat any work already done and returns us the
Scintilla_ReleaseResources address so that we can call this to tidy up
the D2D stuff... or is this too simplistic?

Greg

Neil Hodgson

unread,
Nov 21, 2014, 4:18:36 PM11/21/14
to scintilla...@googlegroups.com
Greg Smith:

> "all the windows"?
> At this point the only Scintilla-related window left is the Scintilla command target - or do I misunderstand what you mean.

Yes, have the application close all its windows in an order that works while all features are still available to handle failure.

> Are you saying that we need a direct function call that tears down the Scintilla command target, then we call Scintilla_ReleaseResources via GetProcAddress, then we unload the Scintilla dll?

No, use the Windows API to close every Scintilla window. Once all the UI has been turned off and the message loop has been exited, then ask for Scintilla to release resources.

> If so, how about a direct SCI_SCINTILLASHUTDOWN call that cleans up remaining window, sets up Scintilla so that the dll unload does not repeat any work already done and returns us the Scintilla_ReleaseResources address so that we can call this to tidy up the D2D stuff... or is this too simplistic?

This will surely fail in new and interesting ways. There would be much less chance of problems (and subsequent support incidents) to simply ignore the warnings instead of going down this path.

Neil

Neil Hodgson

unread,
Nov 24, 2014, 2:07:03 AM11/24/14
to scintilla...@googlegroups.com
Reply all
Reply to author
Forward
0 new messages