From: blackborn on
I'm developing a mirror display driver, that punts all ddi calls
(maked to the associated device surface) to the gdi-managed surface,
created with the EngCreateBitmap. All works fine, except DrvAlphaBlend-
>EngAlphaBlend. EngAlphaBlend crashes in the win32k.sys (xp sp2) when
called for the gdi-managed surface, but for the device-managed surface
it works fine (breaks down blend to lower blt operations). When i use
the gdi-managed bitmap as associated surface, it is also works fine.
All parameters for punted call to EngAlphaBlend are correct (i only
replace device-managed surfaces (src or\and dest) to gdi-maneged).

Maybe, i missed some important concept or there is more correct and
simple way to punt all mirrored calls to the gdi-managed surface (i
need also track all calls\changes)? Thanks!

From: blackborn on
Ivan, thanks for answer!

> You can call EngXXX only with Eng-Managed bitmaps.

But DDK says:
A bit-block transfer with alpha blending is supported between the
following surfaces:

>From one device-managed surface to another device-managed surface.
>From one GDI-managed standard format bitmap to another GDI-managed
standard format bitmap.
>From one device-managed surface to a GDI-managed surface, and vice
versa.
==

And in practice this works (expands to lower calls - textout, blt,
copy etc) - except AlphaBlend with DIB-DIB. But strange - when DIB
associated with device (aka primary surface) - all works fine. But for
Eng-Managed bitmap it fails..
only AlphaBlend, other EngXxx works.

> Maybe you are bing called with a device-owned *SOURCE*
> and an Eng-Managed *DESTINATION*, and, you are missing this case
> when you craft an Eng-Manager SURFOBJ before calling EngXXX

No. I surely handle such case:

if (psoDst)
{
if (psoDst->dhsurf)
{
1. // bReturn = EngAlphaBlend(pDevice->m_pBackSurf, ((psoSrc) &&
(psoSrc->dhsurf)) ? pDevice->m_pBackSurf : psoSrc, pco, pxlo,
prclDest, prclSrc, pBlendObj);
2. // bReturn = EngAlphaBlend(psoDst, psoSrc, pco, pxlo, prclDest,
prclSrc, pBlendObj);

1. - fails
2. - works (expands to lower blt calls).

all others cases silently returns true without putning. I think we
should not copy from a device to eng managed surfaces in mirror
drivers? or we must punt all calls? Ultravnc punts only if dest is
device managed, but it uses DIB as primary surface (strange, that it
calls EngXxx ever - just can return FALSE from DrvXxx for auto punt?).
As i mentioned before, it works and in my case. But i have a device-
managed surface as primary (i create eng managed mirror surface only
when it is really needed)...

--
Andrew Solodovnikov

From: blackborn on
> I guess the DDK is speaking from the caller point of view,
> while I was thinking of the code that detects a punted call
> and breaks that down to DrvCopyBits/EngXXX/DrvCopyBits.

Hmm. But why others punted EngXxx works as expected? And also i find
your messages about it:
http://groups.google.com/group/comp.os.ms-windows.programmer.nt.kernel-mode/browse_frm/thread/405afc6193af138c/e5bb277f48f93a6c

As i understand, you had spoke definitely about same things - the
primare device managed surface and the "mirror" eng-managed:
==
This way win32k.sys will call the Driver for the callbacks
specified in the EngCreateDeviceSurface, and, in each of the
implemented
DrvXXX functions, you can punt-to-Eng using the STYPE_BITMAP SURFOBJ.
==

I done the same thing - create the primary surface with the
EngCreateDeviceSurface, EngAssociateSurface and then in the ExtEscape
create the mirror surface with the EngCreateBitmap.

> The code below works under a few assumptions:
> - the driver does not have device-compatible surfaces
> - the primary surface is an Eng-Managed surface
> Maybe UltraVNC satisfies those assumptions.

But if the primary surface is Eng-managed and we not hook _all_ DrvXxx
(that can draw on surface), then gdi may draw on it without
notifications? This unacceptable (for example, coz it will not work on
a newer ddi engines, and i wonder about vnc mirror driver - it's
surely not correct, coz it hooks not all of the appropriate DrvXxx).

>
> Can you post the stack of the crash that
> you can get from a kernel-debugger session ?

Yes, surely:

f82783d8 bf9e8c7e e1acba48 e1a954d0 e1494d58 win32k!EngAlphaBlend
+0x1a9
WARNING: Stack unwind information not available. Following frames may
be wrong.
f8278450 bf84d747 e10c46c8 e1a601b0 e1494d58 Rcphook+0xc7e
f82784a0 bf95b074 bf9e8c0e e10cce70 e10c46c8 win32k!OffAlphaBlend+0x91
f82785f0 bf83a18e e10c4920 e1a601b0 f8278620 win32k!MulAlphaBlend
+0x382
f82787ec 804df06b 01010054 00000000 e10a9e88 win32k!NtGdiAlphaBlend
+0xb55
f82787ec 7c90eb94 01010054 00000000 e10a9e88 nt!KiFastCallEntry+0xf8
00e8f44c 00000000 00000000 00000000 00000000 0x7c90eb94
f8278ad8 80566730 f8278b94 f8278b98 f8278b68 nt!KiCallUserMode+0x4
f8278b34 bf813d09 00000002 f8278b78 00000018 nt!KeUserModeCallback
+0x87
f8278bb8 bf813ea0 bc636c90 00000128 00010001 win32k!SfnDWORD+0xa8
f8278c00 bf814092 02636c90 00000128 00010001 win32k!
xxxSendMessageToClient+0x176
f8278c4c bf80f470 bc636c90 00000128 00010001 win32k!
xxxSendMessageTimeout+0x1a6
f8278c70 bf809b52 bc636c90 00000128 00010001 win32k!xxxSendMessage
+0x1b
f8278cd4 bf80f436 bc636ba8 00000128 00010001 win32k!
xxxRealDefWindowProc+0xe3a
f8278cec bf81bca1 bc636ba8 00000128 00010001 win32k!
xxxWrapRealDefWindowProc+0x16
f8278d08 bf80f67d bc636ba8 00000128 00010001 win32k!NtUserfnDWORD+0x27
f8278d40 804df06b 0003003c 00000128 00010001 win32k!NtUserMessageCall
+0xae
f8278d40 7c90eb94 0003003c 00000128 00010001 nt!KiFastCallEntry+0xf8
00e8fb14 00000000 00000000 00000000 00000000 0x7c90eb94


f8278450 bf84d747 Rcphook+0xc7e - exactly after the EngAlphaBlend in
the previously posted code.

Code from the point of the crash:

bf9037d0 5e pop esi
bf9037d1 5b pop ebx
bf9037d2 c9 leave
bf9037d3 c21c00 ret 0x1c
bf9037d6 8b431c mov eax,[ebx+0x1c]
>>bf9037d9 8b8008030000 mov eax,[eax+0x308] ds:0023:00000308=????????
bf9037df 894518 mov [ebp+0x18],eax
bf9037e2 e967feffff jmp win32k!EngAlphaBlend+0x1b2
(bf90364e)
bf9037e7 8b4508 mov eax,[ebp+0x8]
bf9037ea f6404904 test byte ptr [eax+0x49],0x4
bf9037ee 8955d4 mov [ebp-0x2c],edx
bf9037f1 8955d8 mov [ebp-0x28],edx
bf9037f4 742a jz win32k!EngAlphaBlend+0x35b
(bf903820)
bf9037f6 8b481c mov ecx,[eax+0x1c]
bf9037f9 8b89e4050000 mov ecx,[ecx+0x5e4]

Thanks!

==
Andrew Solodovnikov

From: blackborn on
> My points about punting are the following:
> -1- If you know you are going to punt a call,
> do not report the capability in the Hook-Flags bassed to EngCreateSurface.

No, i need to be notified about ddi calls. So i must hook all calls -
in highest possible level, or expanded to the lower (DrvCopyBits, for
example). As i understand, there is only way to achieve this - by
creating a device managed surface and associate it as primary, but
punting calls to an another, eng-managed surface.

> -2- If you really want to punt, please translate the SURFOBJ to something
> that Eng can scribble on.

And i done it - all punts to the surface, created with
EngCreateBitmap.

> -3- If you punt without translating the SURFOBJ, then, Eng will make
> a best effor to detect a puntend call, and,
> it will implement again the CopyBits/EngXXX/CopyBits stuff.
>

This works fine for EngAlphaBlend, as i mentioned before...

> Eng MUST be able to scribble the pixel (for read and write, dipending
> on the Operation, the ROP, etc, etc).
> For an opaque surface, the only method to read and write the pixels
> is to copy them somewhere.
> If you don't want a copy, create a STYPE_BITMAP surfobj with a
> driver supplied frame buffer.

> > But if the primary surface is Eng-managed and we not hook _all_ DrvXxx
> > (that can draw on surface), then gdi may draw on it without
> > notifications?
>
> The only notification you will have in this case are two DrvCopyBits calls.
> In fact, if you set the Acceleration level to the lowest, that is what will
> happen anyway.

It is not clear for me. For example, my primary surface is
STYPE_BITMAP. I want to hook CopyBts, BitBlt, TextOut and set
appropriate flags in call to EngAssociateSurface.
What will happens, when my driver receive, for example, AlphaBlend? As
i know, in a callback table, that gdi builds for driver, it place
EngAlphaBlend in INDEX_DrvAlphaBlend entry. So it just calls
EngAlphaBlend and modifies surface silently without notifying my
driver. Is this correct?


>
> For your crash,
>
> From the disassembly, it looks like the SURFOBJ::hdev is NULL for the
> source,
> and, there is no XLATEOBJ

Hmm. I surely punt it to the EngAlphaBlend. I think, the SURFOBJ with
null hdev is the pDevice->m_pBackSurf, coz it is not associated with
the driver.

Lets ressemle the simplified order of operations:
1. DrvEnableSurface
1.1. prim_surf = EngCreateDeviceSurface;
1.2. mirr_surf = EngCreateBitmap;
1.3. EngAssociateSurface(prim_surf);
2. DrvAlphaBlend
2.1. replace prim_surf with mirr_surf, when needed;
2.2. punt call, when needed.


>
> 0:000> u bf8be95d
> win32k!EngAlphaBlend+0x196 [d:\nt\windows\core\ntgdi\gre\alphablt.cxx @
> 1470]:
> bf8be95d 8b411c mov eax,dword ptr [ecx+1Ch] // SURFOBJ::hdev
> bf8be960 8b8008030000 mov eax,dword ptr [eax+308h]
> bf8be966 89450c mov dword ptr [ebp+0Ch],eax
>
> Can you `dt SURFOBJ <address_surfobj_src>` ?
>
> I'd be curious to see where that soruce SURFOBJ comes from,
> but, without your code, it's not easy.

Maybe... Can i modify the sample mirror driver from the DDK to
reproduce this crash and post\send a result code to you?

From: blackborn on
> > What will happens, when my driver receive, for example, AlphaBlend? As
> > i know, in a callback table, that gdi builds for driver, it place
> > EngAlphaBlend in INDEX_DrvAlphaBlend entry. So it just calls
> > EngAlphaBlend and modifies surface silently without notifying my
> > driver. Is this correct?
>
> Not completely.
> the PDEV for your display driver will contain empty
> entries for the non implemented DrvXXX calls.
> If there is no entry, the system will call EngXXX.

In the Feng Yuan's book there is a sample with internal "final" DDI
function table for display driver. Commented, that in place of
callbacks, not supported by driver, gdi will insert EngXxx function to
that table, so calls goes directly to that function. From the "user"
side it looks exactly as you said.

> EngXXX will take care from this point on.
> It will call DrvCopyBits to read the surface
> (if the operation requires a read), or,
> it will implement the operation
> and the it will call CopyBits with the final result.

>
> Something like:
> EngXXX(pDestination, pSource)
> {
> SURFOBJ pDibSource;
> if (!eng_managed(pSource)) {
> pDibSource = CreateDIBforSource();
> CopyBits(pDibSource,pSource);
> } else {
> pDibSource = pSource;
> }
> SURFOBJ pDibDest;
> if (!eng_managed(pDest)) {
> pDibDest = CreateDIBforSource();
> CopyBits(pDibDest,pDest);
> } else {
> pDibDest = pDestination;
> }
>
> internalXXX(pDibDest,pDibSource);
>
> if (!eng_managed(pDest)) {
> CopyBits(pDest,pDibDest);
> }
>
> }
>
> Given that, not supporting an operation or punting will have the same
> result: your driver will get a CopyBits with the final result.

Hmm. Remember, that we talks about an eng-managed (STYPE_BITMAP)
primary surface. From this code i see that a CopyBits will not be
called at all. So if we not provide a DrvXxx operation (and have eng-
managed primary surface), ddi will not inform us about that operation
in any form - it just will silently draw directly to surface. Is this
right? (It is not my case - this is how ultra vnc driver works. I just
point, that we surely cannot use eng-managed primary surface, if we
want to be informed about all operation on it. Right?)

>
> If you can repro on the DDK mirror driver, I will try to take a look.

Thanks! I will post link to code tomorrow.

>
> BTW, supporting AlphaBlend is not a very interesting operation.
> In the case of layered, redirected or composed windows,
> it's better to have the final BitBlt from the pSoScreen kept by
> the GDI sprite manager, instead of doing a chain or alphablend
> for all the overlay and underaly of each layer in the Z-order.
> At the end, you will receive what gets on the primary surface.

Yes, but for windows XP i see some AlphaBlend calls without layered or
composited windows. And i can effectively merge intersected calls - so
it is not a big problem.

>
> One more thing, do you really have a primary surface that is STYPE_BITMAP ?


I just talk about "ultra vnc style" mirror driver, that have
STYPE_BITMAP primary surface :) I wonder about, why it works at all -
it surely should loose some updates. There is one interesting thing -
before associate eng-managed surface with a driver, they set hsurf
member to non-null value (Lock\Set\Unlock). It is look like a some
sort of "hack" to distinct the primary surface from other eng-managed
surfaces...

> That's an oximoron. You should have an STYPE_DEVICE that, possibly,
> wraps a STYPE_BITMAP.

I don't understand, really. How a STYPE_DEVICE can "wrap" a
STYPE_BITMAP? Can you explain? If you talks about EngModifySurface -
it can't be called with associated surfaces (after return from
EngEnableSurface), as i know.
Thanks!

--
Andrew Solodovnikov