[Bug 3902] Unnormal slowness in Heroes 4 (X11DRV_BitBlt?)

Wine Bugs wine-bugs at winehq.org
Mon Feb 6 04:27:40 CST 2006


http://bugs.winehq.org/show_bug.cgi?id=3902





------- Additional Comments From giulian2003 at hotmail.com  2006-06-02 04:27 -------
Hi Marty, 
I haven't made any patch because even though i obtained good results, i still 
had some problems (some bitmap areas didn't 

update right). 
Anyway, its easy to make the necesary changes, it will take you about half un 
hour, i will try to guide trough it:

First of all, I've wrote an email to Robert Shearman asking for informations 
about DIBs - X Server sync and this is what he 

wrote me:

Question:

Hello, 

Could anyone give me some more info on  X11DRV_CoerceDIBSection function and 
the logic behind using 'DIB_Status_GdiMod' and 

'DIB_Status_AppMod' stats? I am trying to repair a bug (3902) that has to do 
with application waiting exccessivly on some 

locks and i am kind of lost ;( Any help would be greatly appreciated! 
 
Answer:

That code is designed to keep DIBs sync'ed between applications and the X 
server. When the application does a Win32 GDI call 

then the state of the DIB gets set to DIB_Status_GdiMod and the memory that 
backs the DIB is set to no-access so that a page 

fault occurs if the application tries to read it. A handler detects this and 
downloads the DIB from the X server and sets the 

state to DIB_Status_None. In this state the DIB memory is set to read-only, so 
that a page fault occurs if the application 

tries to write it. The same handler also detects this and consequentially sets 
the DIB state to DIB_Status_AppMod and allows 

full access to the DIB memory. 


Now, the problem is this:

If you look in the function X11DRV_DIB_Coerce() in 'wine-
0.9.x\dlls\x11drv\dib.c', you will notice that in some cases (InSync 

requested in status GdiMod, etc.) the function X11DRV_DIB_DoUpdateDIBSection() 
is called which syncronises ALL the bitmap. 

even though the previous function modified only a small area of the bitmap:

static void X11DRV_DIB_DoUpdateDIBSection(X_PHYSBITMAP *physBitmap, BOOL toDIB)
{
    BITMAP bitmap;
    GetObjectW( physBitmap->hbitmap, sizeof(bitmap), &bitmap );
    X11DRV_DIB_DoCopyDIBSection(physBitmap, toDIB,
                                physBitmap->colorMap, physBitmap->nColorMap,
                                physBitmap->pixmap, 0, 0, 0, 0,
                                bitmap.bmWidth, bitmap.bmHeight);
}

My ideea was this:
In some cases is possible to know what area of the bitmap will get modified so 
a GDI function could memorise the rectangle to 

be modified! This way, the X11DRV_DIB_DoUpdateDIBSection() function could 
syncronise only the modified area of the bitmap 

instead of all the bitmap.

In order to implement this, i made the following changes:

- added a member to X_PHYSBITMAP structure in 'wine-0.9.3\dlls\x11drv\x11drv.h':
	//it has the purpose to memorise the current modifing rectangle for a 
certain bitmap
	XRectangle modified_rect;

- added 2 functions in 'wine-0.9.x\dlls\x11drv\dib.c' designed for reading and 
setting the modifing rectangle:

/***********************************************************************
 *           X11DRV_SetBoundRect
 */
void X11DRV_SetBoundRect(X_PHYSBITMAP *physBitmap, INT x, INT y, INT width, INT 
height)
{
	XRectangle *t;
	INT xMin,yMin,xMax,yMax;

	if (!physBitmap) return;

	t = &(physBitmap->modified_rect);
	
	if ( t->width && t->height && /*already a valid rect inside*/
             width && height ) /*not trying to invalidate rect*/
	{
		//calculate the union of the two rectangles
		xMin = ((t->x < x)?t->x:x);
		yMin = ((t->y < y)?t->y:y);
		xMax = ((t->x + t->width > x + width)?(t->x + t->width):(x + 
width));
		yMax = ((t->y + t->height > y + height)?(t->y + t->height):(y + 
height));

		t->x = xMin;
		t->y = yMin;
		t->width = xMax - xMin;
		t->height = yMax - yMin;

	}
	else
	{
		t->x = x;
		t->y = y;
		t->width = width;
		t->height = height;
	}

	TRACE("(%p,%d,%d,%d,%d)\n", physBitmap->hbitmap, t->x, t->y, t->width, 
t->height);

}

/***********************************************************************
 *           X11DRV_GetBoundRect
 */
UINT X11DRV_GetBoundRect(X_PHYSBITMAP *physBitmap, INT *x, INT *y, INT *width, 
INT *height)
{

	DIBSECTION dib;
	XRectangle *t;
 

	if (!physBitmap || GetObjectW( physBitmap->hbitmap, sizeof(dib), 
&dib ) != sizeof(dib))
	{
		ERR("called for non-DIBSection!?\n");
		return 1;
	}

	t = &(physBitmap->modified_rect);

	/* we should check for oversize values */
	if ( (t->width > 0) && (t->height > 0) &&
	     (t->x < dib.dsBm.bmWidth) && (t->y < dib.dsBm.bmHeight) ) 
	{
		*x = t->x;
		*y = t->y;

		if (t->x + t->width > dib.dsBm.bmWidth)
			*width = dib.dsBm.bmWidth - t->x;
		else
			*width = t->width;

		if (t->y + t->height > dib.dsBm.bmHeight)
			*height = dib.dsBm.bmHeight - t->y;
		else
			*height = t->height;
	}
	else
	{
		*x = 0;
		*y = 0;
		*width = dib.dsBm.bmWidth;
		*height = dib.dsBm.bmHeight;
	}

	TRACE("(%p,%d,%d,%d,%d)\n", physBitmap->hbitmap, *x, *y, *width, 
*height);

	return 0;
}

And finally, i modified the X11DRV_DIB_DoUpdateDIBSection() function in 'wine-
0.9.x\dlls\x11drv\dib.c' to take in 

consideration the modifing rectangle!

static void X11DRV_DIB_DoUpdateDIBSection(X_PHYSBITMAP *physBitmap, BOOL toDIB)
{
    INT x,y,width,height;

    if ( X11DRV_GetBoundRect(physBitmap,&x,&y,&width,&height) ) return;

    /*invalidate bound rect*/
    X11DRV_SetBoundRect(physBitmap,0,0,0,0);

    X11DRV_DIB_DoCopyDIBSection(physBitmap, toDIB,
                                physBitmap->colorMap, physBitmap->nColorMap,
                                physBitmap->pixmap, x, y, x, y,
                                width, height);

}


Now, a GDI function (for example BitBlt()) should be called like this:

    X11DRV_SetBoundRect(physDevDst->bitmap,xDst,yDst,width,height);
    X11DRV_CoerceDIBSection( physDevDst, DIB_Status_GdiMod, FALSE );
    if (physDevDst != physDevSrc)
    {
      X11DRV_SetBoundRect(physDevSrc->bitmap,xSrc,ySrc,width,height);
      X11DRV_CoerceDIBSection( physDevSrc, DIB_Status_GdiMod, FALSE );
    }

    result = BITBLT_InternalStretchBlt( physDevDst, xDst, yDst, width, height,
                                        physDevSrc, xSrc, ySrc, width, height, 
					rop );


    if (physDevDst != physDevSrc)
      X11DRV_UnlockDIBSection( physDevSrc, FALSE );
    X11DRV_UnlockDIBSection( physDevDst, TRUE );



As i said, i obtained good results, but the bitmaps don't update right! ;(
I think one of the problems is i didn't took into consideration the origines in 
X11DRV_PDEVICE struct when i call 

X11DRV_SetBoundRect(), moustly because i don't have a clear understanding about 
how things work! 

The members of the X11DRV_PDEVICE struct that i belive should be taken into 
consideration are:

    POINT         org;          /* DC origin relative to drawable */
    POINT         drawable_org; /* Origin of drawable relative to screen */
    HRGN          region;       /* Device region (visible region & clip region) 
*

Not sure if all of them or maybe only 'org'?


If you have any more questions, please don't esitate to ask them...

Iulian


-- 
Configure bugmail: http://bugs.winehq.org/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
You are on the CC list for the bug, or are watching someone who is.



More information about the wine-bugs mailing list