Direct2D Black Screen

455 views
Skip to first unread message

Florian Balmer

unread,
Apr 23, 2013, 2:11:44 PM4/23/13
to scintilla...@googlegroups.com
Dear Scintilla Community

The issue described below has been reproduced with Scintilla 3.2.3 through 3.3.1.

There have been reports about the latest beta versions of Notepad2 showing a black rectangle instead of the normal text, in certain siutations. The problem only appears on Windows Vista and above with Direct2D enabled (can be disabled in the Notepad2.ini file). The most reliable way to reproduce the issue is to lock Windows (Windows Key+L) while Notepad2 is in the foreground, then the black rectangle appears right after logging on. It can also be triggered through an UAC prompt, sometimes.

I've tracked down the issue to ScintillaWin::WndPaint(), where Scintilla discards the allocated Direct2D objects in response to the D2DERR_RECREATE_TARGET error. If FullPaint() is called after this error, the black rectangle won't appear.

I'm not familiar with Direct2D at all, but somehow this makes sense to me. The MSDN tutorial [1] does not explicitly mention that (see section "The Direct2D Render Loop"), but maybe implicitly in step 3 by saying "Repeat step 2 whenever you need to update or redraw the scene". The question is: is there a need to redraw if the render target has been lost, or not?


The problem does not appear with SciTE, but I suspect this is due to some housekeeping (such as showing/hiding the selection upon a focus change) causing a full repaint if the SciTE window is reactivated after logon.

Also, with Direct2D, the caret does not appear when setting the focus to Scintilla, if there's no text (empty document), unless some keyboard or mouse input has arrived to Scintilla. Forcing the full repaint as outlined above partially fixes this issue (but not for the initial display of the caret in a newly opened Notepad2 window with an empty document), that's why I think this may be a somehow related issue. Again, no problem here in SciTE, but maybe some action being taken on WM_ACTIVATE or WM_ACTIVATEAPP causes a full repaint, and reinitialization of the caret.

Has anybody experienced similar problems with Direct2D turning Scintilla into a black rectangle? Is my suggested fix reasonable, or did I miss the point about Direct2D completely? Has anybody else noted the behaviour with the caret, and found out if it's also linked to Direct2D?

--Florian

Sven Strickroth

unread,
Apr 23, 2013, 3:18:43 PM4/23/13
to scintilla...@googlegroups.com
Hi,

Am 23.04.2013 20:11 schrieb Florian Balmer:
> Has anybody experienced similar problems with Direct2D turning Scintilla
> into a black rectangle? Is my suggested fix reasonable, or did I miss
> the point about Direct2D completely? Has anybody else noted the
> behaviour with the caret, and found out if it's also linked to Direct2D?

we use Scintilla in TortoiseGit. In some previous versions we had
enabled Direct2D and experienced several issues:

* Background blinks/flashes black/white
* Redraw issues in mstsc Remote Desktop session
* Text cursor is often invisible
* Random crashes

See: http://sourceforge.net/p/scintilla/bugs/1398/

--
Best regards,
Sven Strickroth
PGP key id F5A9D4C4 @ any key-server

Neil Hodgson

unread,
Apr 23, 2013, 8:02:10 PM4/23/13
to scintilla...@googlegroups.com
Florian Balmer:

> I've tracked down the issue to ScintillaWin::WndPaint(), where Scintilla discards the allocated Direct2D objects in response to the D2DERR_RECREATE_TARGET error. If FullPaint() is called after this error, the black rectangle won't appear.
>
> I'm not familiar with Direct2D at all, but somehow this makes sense to me. The MSDN tutorial [1] does not explicitly mention that (see section "The Direct2D Render Loop"), but maybe implicitly in step 3 by saying "Repeat step 2 whenever you need to update or redraw the scene". The question is: is there a need to redraw if the render target has been lost, or not?

I'd prefer a loop around the drawing code for a second attempt when receiving D2DERR_RECREATE_TARGET. FullPaint is for other situations and lumping this one in may make it harder to maintain.

> Also, with Direct2D, the caret does not appear when setting the focus to Scintilla, if there's no text (empty document), unless some keyboard or mouse input has arrived to Scintilla.

Are you sure Scintilla is receiving focus? Its in the handling of WM_SETFOCUS that ShowCaretAtCurrentPosition is called and caret ticking is turned on. Can't see any reason for a blank document to be treated differently to one with text here.

To see if it has focus try typing and try using the tab key to see if the focus highlight is anywhere on screen.

Neil

Neil Hodgson

unread,
Apr 24, 2013, 8:34:07 PM4/24/13
to scintilla...@googlegroups.com
A modification has been pushed that tries to paint another time after receiving D2DERR_RECREATE_TARGET.
http://sourceforge.net/p/scintilla/code/ci/fabcea1716dcbcc935eea86c93a472c27d8039d2/
a previous clean up change should be applied first
http://sourceforge.net/p/scintilla/code/ci/a6b4a7c067473ed837176ac57338e320e0b311ed/

Neil

Florian Balmer

unread,
Apr 25, 2013, 3:40:31 PM4/25/13
to scintilla...@googlegroups.com, nyama...@me.com
Sven:

> we use Scintilla in TortoiseGit. In some previous versions we had
> enabled Direct2D and experienced several issues:
>
> * Background blinks/flashes black/white

Maybe this is the same sort of problem as discussed here, as the black
screen was occasionally reported to be white, and the temporary
visibility of the cached window contents after logon can make it look
like flashing.

> * Redraw issues in mstsc Remote Desktop session

Only problems with the "TeamViewer" remote control software have been
reported for Notepad2, by now.

> * Text cursor is often invisible

Something like this has also been reported for Notepad2, but only on
Windows Vista. The caret disappeared on certain positions when using the
arrow keys to move it right or left. The problem could be fixed by
setting a caret size (width) of at least 2 pixels or by enabling the
block caret style. That's why I suppose this is some kind of sub-pixel
rounding bug in the Windows Vista Direct2D engine. The problem has not
been reported on Windows 7 or Windows 8, up to now.

> * Random crashes

Fortunately, this didn't happen, so far.

Neil:

> I'd prefer a loop around the drawing code for a second attempt when
> receiving D2DERR_RECREATE_TARGET. FullPaint is for other situations
> and lumping this one in may make it harder to maintain.

> A modification has been pushed that tries to paint another time after
> receiving D2DERR_RECREATE_TARGET.

I was just making up the code in my mind today, but was not sure where
to put the loop, whether it was safe to reuse the Scintilla GDI objects,
and so on ... and here's the solution, already!

The issue is solved for all the situations I can test myself. If I get
feedback about the error persisting in some (weird, not used by me)
situations, I will post a follow up. Thank you very much for your help!

> Are you sure Scintilla is receiving focus? Its in the handling of
> WM_SETFOCUS that ShowCaretAtCurrentPosition is called and caret
> ticking is turned on. Can't see any reason for a blank document to be
> treated differently to one with text here.
>
> To see if it has focus try typing and try using the tab key to see if
> the focus highlight is anywhere on screen.

Yes, Scintilla has the focus, and everything works as expected with
Direct2D disabled. Even if I move the SetFocus()'ing of Scintilla to
WM_ACTIVATE(APP) as in SciTE, the problem remains the same.

I can fix this by post-messaging SCI_CHARLEFT on the initial activation
of Scintilla, if there's an emypt document. This is no longer necessary
once the caret has been displayed, i.e. for subsequent activations,
and also not required if the initially loaded document is not empty.

--Florian

Florian Balmer

unread,
Apr 28, 2013, 6:13:22 AM4/28/13
to scintilla...@googlegroups.com, nyama...@me.com
I haven't been able to solve the caret issue. The caret timers are
running, the caret drawing functions are called properly, and the
pRenderTarget objects seem to be valid, as all drawing goes through
WM_PAINT, which now handles D2DERR_RECREATE_TARGET.

Another work around besides posting SCI_CHARLEFT to fix this problem is
to set the Scintilla control text to a space, and remove it again, when
the control is created. It seems like Scintilla is using different
approaches to draw the caret, whether or not there's some text, and the
no-text variant will only initialize properly after the text variant has
already been used.

Another observation: defining SCI_DISABLE_PROVISIONAL raises a bunch of
build errors, so it's not possible to exclude the provisional features,
at the moment.

--Florian

Neil Hodgson

unread,
Apr 28, 2013, 6:23:14 AM4/28/13
to scintilla...@googlegroups.com
Florian Balmer:

> Another work around besides posting SCI_CHARLEFT to fix this problem is
> to set the Scintilla control text to a space, and remove it again, when
> the control is created. It seems like Scintilla is using different
> approaches to draw the caret, whether or not there's some text, and the
> no-text variant will only initialize properly after the text variant has
> already been used.

About the only interesting difference I know of between having text and not having text is that there are no calls to lexers/folders for an empty document. Its possible that something is tripped downstream by a notification caused by lexing/folding.

You could try checking that drawing is occurring by changing something such as turning on the caret line. Also try tracing the invalidation calls to see what areas are being invalidated.

> Another observation: defining SCI_DISABLE_PROVISIONAL raises a bunch of
> build errors, so it's not possible to exclude the provisional features,
> at the moment.

Scintilla is not meant to be built with SCI_DISABLE_PROVISIONAL - it should only be defined by client code that wants to avoid using provisional features.

Neil

Florian Balmer

unread,
Apr 29, 2013, 1:54:45 PM4/29/13
to scintilla...@googlegroups.com, nyama...@me.com
> You could try checking that drawing is occurring by changing something
> such as turning on the caret line. Also try tracing the invalidation
> calls to see what areas are being invalidated.

Thanks for the hint, tracking the areas being invalidated helped! I'm
using the following sequence to initialize Scintilla:

SCI_CLEARALL
SCI_SETSCROLLWIDTH(2048)
SCI_SETXOFFSET(0)
if (length)
SCI_ADDTEXT(length,text)
SCI_GOTOPOS(0)
SCI_CHOOSECARETX

This results in xOffset being set to 30 for length==0, but to 0 for
length>0, which I didn't foresee! Moving the SCI_SETXOFFSET call to the
end of the sequence solves my problem, the caret is displayed correctly!
I'm sorry for the false alarm, and thanks again for the help!

> Scintilla is not meant to be built with SCI_DISABLE_PROVISIONAL - it
> should only be defined by client code that wants to avoid using
> provisional features.

Ok, I see, thanks for the clarification.

--Florian

Florian Balmer

unread,
Jul 13, 2013, 12:49:52 PM7/13/13
to scintilla...@googlegroups.com, nyama...@me.com
A follow up to the issue:

With the fix that evolved from the above discussion, most people who reported the error to me in the first place, and who gave me a follow up, were not experiencing the problem any more. There's one system left I know of, which still suffers from the problem, a "Windows 7 64-bit SP1 Macbook Air/Thunderbolt Display" machine.

On my 32-bit systems, the problem disappeared with this weeks Windows Updates and Graphics Driver software updates, even for unpatched versions of Notepad2! On my 64-bit systems, the problem persisted for unpatched versions (both 32-bit and 64-bit builds), but the fixed version worked fine.

Moreover, a problem in Windows that caused the first UAC prompt in a session to cause a looooong time to appear (up to 10-20 seconds) has also been fixed with the latest Graphics Driver software (when doing some research about this issue, I've come across a statement from Microsoft that the origin of this is the Graphics Driver software). I consider this a somehow related problem (but I may be wrong), as switching to and away from the secure UAC desktop causes some troubles, either with initialization, or cleanup.

So the issue in general may always have been related to Graphics Driver software, and not to Scintilla. Anyway, the fix is a good solution one some systems, and does not seem to be harmful.

--Florian

Florian Balmer

unread,
Nov 9, 2013, 11:27:48 AM11/9/13
to scintilla...@googlegroups.com, nyama...@me.com
There has been one more report about this issue, this time on Windows 8, so I started a new round of investigation, and I believe I have found a definitive fix, now!

More systematic testing revealed that each time the Windows desktop is switched (i.e. if a lock screen, or an UAC prompt appears), the NORMAL behavior of ID2D1RenderTarget::EndDraw() is to return D2DERR_RECREATE_TARGET, which is what happens here [1].


The HALLELUJAH for me was to notice that just a few yoctoseconds later, there's a rescue mechanism [2].


So, as I have already suspected earlier, Scintilla-based editors not exhibiting the "black screen bug" always seem to run into the "paintState == paintAbandoned" situation, probably due to some internal housekeeping upon focus/activation changes or similar, and thus always get a nice, fresh, full redraw!

My proposed solution to fix this problem (once and for all, hopefully) is to drop the "for (int attempt=0; ...)" loop and it's corresponding break statement, and just set "paintState = paintAbandoned", right after calling DropRenderTarget(), in case ID2D1RenderTarget::EndDraw() delivers D2DERR_RECREATE_TARGET.

According to the MSDN samples, I'd also suggest to change the error checking to "if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET)".

This solution fixes the issue for anybody who has reported it to me, so I'd be happy if this could slip into Scintilla.

--Florian

Florian Balmer

unread,
Nov 10, 2013, 2:36:42 AM11/10/13
to scintilla...@googlegroups.com, nyama...@me.com
I forgot to mention that the D2DERR_RECREATE_TARGET error always requires a full repaint, that's why the "for (int attempt=0; ...)" loop is not sufficient to fix the problem. The loop works fine for the region that needs to be updated, and i.e. repaints the current line, but the rest of the window remains black. The Scintilla-based editors not showing the bug satisfy the need for a full repaint after hitting D2DERR_RECREATE_TARGET, because they have the "paintState == paintAbandoned" situation, which triggers a full paint.

Neil Hodgson

unread,
Nov 10, 2013, 3:44:03 AM11/10/13
to scintilla...@googlegroups.com
Florian Balmer:

> More systematic testing revealed that each time the Windows desktop is switched (i.e. if a lock screen, or an UAC prompt appears), the NORMAL behavior of ID2D1RenderTarget::EndDraw() is to return D2DERR_RECREATE_TARGET, which is what happens here [1].
>
> [1] http://sourceforge.net/p/scintilla/code/ci/45d12cc53b5ae3f2fab2ba79c6eddb093b46d774/tree/win32/ScintillaWin.cxx#l610
>
> The HALLELUJAH for me was to notice that just a few yoctoseconds later, there's a rescue mechanism [2].

Its an expensive rescue which you don't want to perform unless necessary.

> My proposed solution to fix this problem (once and for all, hopefully) is to drop the "for (int attempt=0; ...)" loop and it's corresponding break statement, and just set "paintState = paintAbandoned", right after calling DropRenderTarget(), in case ID2D1RenderTarget::EndDraw() delivers D2DERR_RECREATE_TARGET.

OK.

> According to the MSDN samples, I'd also suggest to change the error checking to "if (FAILED(hr) || hr == D2DERR_RECREATE_TARGET)".

That looks repetitive as D2DERR_RECREATE_TARGET should also trigger FAILED(). Unless there are other reported failure codes here, I'd prefer to leave it handling just D2DERR_RECREATE_TARGET.

Neil

Florian Balmer

unread,
Nov 10, 2013, 3:54:01 PM11/10/13
to scintilla...@googlegroups.com, nyama...@me.com
Neil Hodgson:

> Its an expensive rescue which you don't want to perform unless necessary.

I agree, but I would vote for this to be affordable for an uncommon corner case like desktop switching, when anything is likely to be flickering anyway, on Windows. By the way, the bug can also be triggered using the Sysinternals Desktops tool, which allows more comfortable testing (by setting hotkeys) than with the Windows secure desktops.

> That looks repetitive as D2DERR_RECREATE_TARGET should also trigger FAILED(). Unless there are other reported failure codes here, I'd prefer to leave it handling just D2DERR_RECREATE_TARGET.

I haven't seen or heard about other error codes in conjunction with the black screen problem, so sticking with D2DERR_RECREATE_TARGET seems fine.

--Florian

Neil Hodgson

unread,
Nov 10, 2013, 6:38:16 PM11/10/13
to Apple Inc.
Florian Balmer:

> I haven't seen or heard about other error codes in conjunction with the black screen problem, so sticking with D2DERR_RECREATE_TARGET seems fine.

Committed a change:
https://sourceforge.net/p/scintilla/code/ci/6888f930b5e1935d68f1a9beb9b5b26a384bf068/

There is also the case of call tip drawing which doesn’t do anything when EndDraw fails. Haven’t managed to show any problems with this as the call tip is cancelled when Scintilla loses focus. If anyone can provoke a black call tip then that code needs to look at the return from EndDraw and perform a redraw.

Neil

Reply all
Reply to author
Forward
0 new messages