1131 lines
34 KiB
C
1131 lines
34 KiB
C
/******************************Module*Header*******************************\
|
|
* Module Name: ddraw.c
|
|
*
|
|
* Implements all the DirectDraw components for the driver.
|
|
*
|
|
* Copyright (c) 1995-1996 Microsoft Corporation
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
|
|
// NT is kind enough to pre-calculate the 2-d surface offset as a 'hint' so
|
|
// that we don't have to do the following, which would be 6 DIVs per blt:
|
|
//
|
|
// y += (offset / pitch)
|
|
// x += (offset % pitch) / bytes_per_pixel
|
|
|
|
#define convertToGlobalCord(x, y, surf) \
|
|
{ \
|
|
y += surf->yHint; \
|
|
x += surf->xHint; \
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////
|
|
// DirectDraw stuff
|
|
|
|
#define VBLANK_IS_ACTIVE(pjPorts)\
|
|
((CP_IN_BYTE(pjPorts, STATUS_1) & 0x08) ? TRUE : FALSE) // !!! 0x3da
|
|
|
|
#define DISPLAY_IS_ACTIVE(pjPorts)\
|
|
((CP_IN_BYTE(pjPorts, STATUS_1) & 0x01) ? TRUE : FALSE)
|
|
|
|
|
|
#define ENTER(s) DISPDBG((10, "Entering "#s));
|
|
#define EXIT(s) DISPDBG((10, "Exiting "#s" line(%d)", __LINE__));
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vGetDisplayDuration
|
|
*
|
|
* Get the length, in EngQueryPerformanceCounter() ticks, of a refresh cycle.
|
|
*
|
|
* If we could trust the miniport to return back and accurate value for
|
|
* the refresh rate, we could use that. Unfortunately, our miniport doesn't
|
|
* ensure that it's an accurate value.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#define NUM_VBLANKS_TO_MEASURE 1
|
|
#define NUM_MEASUREMENTS_TO_TAKE 10
|
|
#define NUM_MEASUREMENTS_TO_DISCARD 3
|
|
|
|
#if (NUM_MEASUREMENTS_TO_TAKE - NUM_MEASUREMENTS_TO_DISCARD) < 2
|
|
#error ***************************************
|
|
#error *** You discarded too many measurements
|
|
#error ***************************************
|
|
#endif
|
|
|
|
VOID vGetDisplayDuration(PDEV* ppdev)
|
|
{
|
|
BYTE* pjBase;
|
|
BYTE* pjPorts;
|
|
LONG i;
|
|
LONG j;
|
|
LONGLONG li;
|
|
LONGLONG liMin;
|
|
LONGLONG aliMeasurement[NUM_MEASUREMENTS_TO_TAKE + 1];
|
|
|
|
pjBase = ppdev->pjBase;
|
|
pjPorts = ppdev->pjPorts;
|
|
|
|
memset(&ppdev->flipRecord, 0, sizeof(ppdev->flipRecord));
|
|
|
|
// Warm up EngQUeryPerformanceCounter to make sure it's in the working
|
|
// set:
|
|
|
|
EngQueryPerformanceCounter(&li);
|
|
|
|
// Unfortunately, since NT is a proper multitasking system, we can't
|
|
// just disable interrupts to take an accurate reading. We also can't
|
|
// do anything so goofy as dynamically change our thread's priority to
|
|
// real-time.
|
|
//
|
|
// So we just do a bunch of short measurements and take the minimum.
|
|
//
|
|
// It would be 'okay' if we got a result that's longer than the actual
|
|
// VBlank cycle time -- nothing bad would happen except that the app
|
|
// would run a little slower. We don't want to get a result that's
|
|
// shorter than the actual VBlank cycle time -- that could cause us
|
|
// to start drawing over a frame before the Flip has occured.
|
|
|
|
while (VBLANK_IS_ACTIVE(pjPorts))
|
|
;
|
|
while (!(VBLANK_IS_ACTIVE(pjPorts)))
|
|
;
|
|
|
|
for (i = 0; i < NUM_MEASUREMENTS_TO_TAKE; i++)
|
|
{
|
|
// We're at the start of the VBlank active cycle!
|
|
|
|
EngQueryPerformanceCounter(&aliMeasurement[i]);
|
|
|
|
// Okay, so life in a multi-tasking environment isn't all that
|
|
// simple. What if we had taken a context switch just before
|
|
// the above EngQueryPerformanceCounter call, and now were half
|
|
// way through the VBlank inactive cycle? Then we would measure
|
|
// only half a VBlank cycle, which is obviously bad. The worst
|
|
// thing we can do is get a time shorter than the actual VBlank
|
|
// cycle time.
|
|
//
|
|
// So we solve this by making sure we're in the VBlank active
|
|
// time before and after we query the time. If it's not, we'll
|
|
// sync up to the next VBlank (it's okay to measure this period --
|
|
// it will be guaranteed to be longer than the VBlank cycle and
|
|
// will likely be thrown out when we select the minimum sample).
|
|
// There's a chance that we'll take a context switch and return
|
|
// just before the end of the active VBlank time -- meaning that
|
|
// the actual measured time would be less than the true amount --
|
|
// but since the VBlank is active less than 1% of the time, this
|
|
// means that we would have a maximum of 1% error approximately
|
|
// 1% of the times we take a context switch. An acceptable risk.
|
|
//
|
|
// This next line will cause us wait if we're no longer in the
|
|
// VBlank active cycle as we should be at this point:
|
|
|
|
while (!(VBLANK_IS_ACTIVE(pjPorts)))
|
|
;
|
|
|
|
for (j = 0; j < NUM_VBLANKS_TO_MEASURE; j++)
|
|
{
|
|
while (VBLANK_IS_ACTIVE(pjPorts))
|
|
;
|
|
while (!(VBLANK_IS_ACTIVE(pjPorts)))
|
|
;
|
|
}
|
|
}
|
|
|
|
EngQueryPerformanceCounter(&aliMeasurement[NUM_MEASUREMENTS_TO_TAKE]);
|
|
|
|
// Use the minimum:
|
|
|
|
liMin = aliMeasurement[1+NUM_MEASUREMENTS_TO_DISCARD] -
|
|
aliMeasurement[0+NUM_MEASUREMENTS_TO_DISCARD];
|
|
|
|
DISPDBG((1, "Refresh count: %li - %li", 1, (ULONG) liMin));
|
|
|
|
for (i = 2+NUM_MEASUREMENTS_TO_DISCARD; i <= NUM_MEASUREMENTS_TO_TAKE; i++)
|
|
{
|
|
li = aliMeasurement[i] - aliMeasurement[i - 1];
|
|
|
|
DISPDBG((1, " %li - %li", i - NUM_MEASUREMENTS_TO_DISCARD, (ULONG) li));
|
|
|
|
if (li < liMin)
|
|
liMin = li;
|
|
}
|
|
|
|
// Round the result:
|
|
|
|
ppdev->flipRecord.liFlipDuration
|
|
= (DWORD) (liMin + (NUM_VBLANKS_TO_MEASURE / 2)) / NUM_VBLANKS_TO_MEASURE;
|
|
|
|
DISPDBG((1, "Frequency %li.%03li Hz",
|
|
(ULONG) (EngQueryPerformanceFrequency(&li),
|
|
li / ppdev->flipRecord.liFlipDuration),
|
|
(ULONG) (EngQueryPerformanceFrequency(&li),
|
|
((li * 1000) / ppdev->flipRecord.liFlipDuration) % 1000)));
|
|
|
|
ppdev->flipRecord.liFlipTime = aliMeasurement[NUM_MEASUREMENTS_TO_TAKE];
|
|
ppdev->flipRecord.bFlipFlag = FALSE;
|
|
ppdev->flipRecord.fpFlipFrom = 0;
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* HRESULT vUpdateFlipStatus
|
|
*
|
|
* Checks and sees if the most recent flip has occurred.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
HRESULT vUpdateFlipStatus(
|
|
PDEV* ppdev,
|
|
FLATPTR fpVidMem)
|
|
{
|
|
BYTE* pjBase;
|
|
BYTE* pjPorts;
|
|
LONGLONG liTime;
|
|
|
|
ENTER(vUpdateFlipStatus);
|
|
|
|
pjBase = ppdev->pjBase;
|
|
pjPorts = ppdev->pjPorts;
|
|
|
|
if ((ppdev->flipRecord.bFlipFlag) &&
|
|
((fpVidMem == 0) || (fpVidMem == ppdev->flipRecord.fpFlipFrom)))
|
|
{
|
|
if (VBLANK_IS_ACTIVE(pjPorts))
|
|
{
|
|
if (ppdev->flipRecord.bWasEverInDisplay)
|
|
{
|
|
ppdev->flipRecord.bHaveEverCrossedVBlank = TRUE;
|
|
}
|
|
}
|
|
else if (DISPLAY_IS_ACTIVE(pjPorts))
|
|
{
|
|
if( ppdev->flipRecord.bHaveEverCrossedVBlank )
|
|
{
|
|
ppdev->flipRecord.bFlipFlag = FALSE;
|
|
|
|
EXIT(vUpdateFlipStatus);
|
|
return(DD_OK);
|
|
}
|
|
ppdev->flipRecord.bWasEverInDisplay = TRUE;
|
|
}
|
|
|
|
EngQueryPerformanceCounter(&liTime);
|
|
|
|
if (liTime - ppdev->flipRecord.liFlipTime
|
|
<= ppdev->flipRecord.liFlipDuration)
|
|
{
|
|
EXIT(vUpdateFlipStatus);
|
|
return(DDERR_WASSTILLDRAWING);
|
|
}
|
|
|
|
ppdev->flipRecord.bFlipFlag = FALSE;
|
|
}
|
|
|
|
EXIT(vUpdateFlipStatus);
|
|
|
|
return(DD_OK);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdBlt
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdBlt(
|
|
PDD_BLTDATA lpBlt)
|
|
{
|
|
PDD_SURFACE_GLOBAL srcSurf;
|
|
PDD_SURFACE_LOCAL destSurfx;
|
|
PDD_SURFACE_GLOBAL destSurf;
|
|
PDEV* ppdev;
|
|
BYTE* pjBase;
|
|
HRESULT ddrval;
|
|
FLATPTR destOffset;
|
|
DWORD destPitch;
|
|
DWORD destX;
|
|
DWORD destY;
|
|
DWORD direction;
|
|
DWORD dwFlags;
|
|
DWORD height;
|
|
BYTE rop;
|
|
FLATPTR sourceOffset;
|
|
DWORD srcPitch;
|
|
DWORD srcX;
|
|
DWORD srcY;
|
|
DWORD width;
|
|
LONG lDelta;
|
|
LONG cBpp;
|
|
|
|
ULONG ulBltAdjust = 0;
|
|
|
|
ENTER(DdBlt);
|
|
|
|
ppdev = (PDEV*) lpBlt->lpDD->dhpdev;
|
|
pjBase = ppdev->pjBase;
|
|
|
|
lDelta = ppdev->lDelta;
|
|
cBpp = ppdev->cBpp;
|
|
|
|
destSurfx = lpBlt->lpDDDestSurface;
|
|
destSurf = destSurfx->lpGbl;
|
|
|
|
// Is a flip in progress?
|
|
|
|
ddrval = vUpdateFlipStatus(ppdev, destSurf->fpVidMem);
|
|
if (ddrval != DD_OK)
|
|
{
|
|
lpBlt->ddRVal = ddrval;
|
|
EXIT(DdBlt);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
dwFlags = lpBlt->dwFlags;
|
|
|
|
if (dwFlags & DDBLT_ASYNC)
|
|
{
|
|
// If async, then only work if we won't have to wait on the
|
|
// accelerator to start the command.
|
|
|
|
// !!! is this next line correct?
|
|
|
|
if (IS_BUSY(ppdev, pjBase))
|
|
{
|
|
lpBlt->ddRVal = DDERR_WASSTILLDRAWING;
|
|
EXIT(DdBlt);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
}
|
|
|
|
// Copy src/dest rects:
|
|
|
|
destX = lpBlt->rDest.left;
|
|
destY = lpBlt->rDest.top;
|
|
width = lpBlt->rDest.right - lpBlt->rDest.left;
|
|
height = lpBlt->rDest.bottom - lpBlt->rDest.top;
|
|
destPitch = destSurf->lPitch;
|
|
destOffset = destSurf->fpVidMem;
|
|
|
|
if (dwFlags & DDBLT_COLORFILL)
|
|
{
|
|
lpBlt->ddRVal = DD_OK;
|
|
|
|
convertToGlobalCord(destX, destY, destSurf);
|
|
|
|
// Solid fill here
|
|
|
|
{
|
|
WAIT_FOR_EMPTY_ACL_QUEUE(ppdev, pjBase);
|
|
CP_FG_ROP(ppdev, pjBase, R3_PATCOPY);
|
|
CP_DST_Y_OFFSET(ppdev, pjBase, (lDelta - 1));
|
|
CP_PAT_ADDR(ppdev, pjBase, ppdev->ulSolidColorOffset);
|
|
CP_XCNT(ppdev, pjBase, (width * cBpp - 1));
|
|
CP_YCNT(ppdev, pjBase, (height - 1));
|
|
WAIT_FOR_IDLE_ACL(ppdev, pjBase);
|
|
*(PULONG)(ppdev->pjScreen + ppdev->ulSolidColorOffset) =
|
|
COLOR_REPLICATE(ppdev, lpBlt->bltFX.dwFillColor);
|
|
|
|
if (cBpp == 3)
|
|
{
|
|
CP_PEL_DEPTH(ppdev, pjBase, HW_PEL_DEPTH_24BPP);
|
|
CP_PAT_WRAP(ppdev, pjBase, SOLID_COLOR_PATTERN_WRAP_24BPP);
|
|
CP_PAT_Y_OFFSET(ppdev, pjBase, (SOLID_COLOR_PATTERN_OFFSET_24BPP - 1));
|
|
CP_DST_ADDR(ppdev, pjBase, ((destY * lDelta) + (cBpp * destX)));
|
|
WAIT_FOR_EMPTY_ACL_QUEUE(ppdev, pjBase);
|
|
CP_PEL_DEPTH(ppdev, pjBase, HW_PEL_DEPTH_8BPP);
|
|
}
|
|
else
|
|
{
|
|
CP_PAT_WRAP(ppdev, pjBase, SOLID_COLOR_PATTERN_WRAP);
|
|
CP_PAT_Y_OFFSET(ppdev, pjBase, (SOLID_COLOR_PATTERN_OFFSET - 1));
|
|
CP_DST_ADDR(ppdev, pjBase, ((destY * lDelta) + (cBpp * destX)));
|
|
}
|
|
}
|
|
|
|
EXIT(DdBlt);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
// We specified with Our ddCaps.dwCaps that we handle a limited number
|
|
// of commands, and by this point in our routine we've handled everything
|
|
// except DDBLT_ROP. DirectDraw and GDI shouldn't pass us anything
|
|
// else; we'll assert on debug builds to prove this:
|
|
|
|
ASSERTDD((dwFlags & DDBLT_ROP) && (lpBlt->lpDDSrcSurface),
|
|
"Expected dwFlags commands of only DDBLT_ASYNC and DDBLT_COLORFILL");
|
|
|
|
// Get offset, width, and height for source:
|
|
|
|
srcSurf = lpBlt->lpDDSrcSurface->lpGbl;
|
|
srcX = lpBlt->rSrc.left;
|
|
srcY = lpBlt->rSrc.top;
|
|
srcPitch = srcSurf->lPitch;
|
|
sourceOffset = srcSurf->fpVidMem;
|
|
|
|
// Assume we can do the blt top-to-bottom, left-to-right:
|
|
|
|
if ((destSurf == srcSurf) && (srcX + width > destX) &&
|
|
(srcY + height > destY) && (destX + width > srcX) &&
|
|
(destY + height > srcY) &&
|
|
(((srcY == destY) && (destX > srcX) )
|
|
|| ((srcY != destY) && (destY > srcY))))
|
|
{
|
|
// Okay, we have to do the blt bottom-to-top, right-to-left:
|
|
|
|
ulBltAdjust = 1;
|
|
|
|
srcX = lpBlt->rSrc.right;
|
|
srcY = lpBlt->rSrc.bottom - 1;
|
|
destX = lpBlt->rDest.right;
|
|
destY = lpBlt->rDest.bottom - 1;
|
|
}
|
|
|
|
// NT only ever gives us SRCCOPY rops, so don't even both checking
|
|
// for anything else.
|
|
|
|
convertToGlobalCord(srcX, srcY, srcSurf);
|
|
convertToGlobalCord(destX, destY, destSurf);
|
|
|
|
// Bitmap Blt
|
|
|
|
{
|
|
WAIT_FOR_EMPTY_ACL_QUEUE(ppdev, pjBase);
|
|
|
|
if (ulBltAdjust) {
|
|
CP_XY_DIR(ppdev, pjBase, (BOTTOM_TO_TOP | RIGHT_TO_LEFT));
|
|
}
|
|
|
|
if (dwFlags & DDBLT_KEYSRCOVERRIDE)
|
|
{
|
|
// Color keyed Transparency
|
|
|
|
CP_FG_ROP(ppdev, pjBase, R3_SRCCOPY);
|
|
CP_SRC_WRAP(ppdev, pjBase, NO_PATTERN_WRAP);
|
|
CP_SRC_Y_OFFSET(ppdev, pjBase, (lDelta - 1));
|
|
CP_DST_Y_OFFSET(ppdev, pjBase, (lDelta - 1));
|
|
if (ulBltAdjust) {
|
|
CP_PAT_ADDR(ppdev, pjBase, ppdev->ulSolidColorOffset + cBpp - 1);
|
|
} else {
|
|
CP_PAT_ADDR(ppdev, pjBase, ppdev->ulSolidColorOffset);
|
|
}
|
|
CP_ROUTING_CTRL(ppdev, pjBase, 0x13); // Generate CompareMap
|
|
CP_XCNT(ppdev, pjBase, ((cBpp * width) - 1));
|
|
CP_YCNT(ppdev, pjBase, (height - 1));
|
|
CP_SRC_ADDR(ppdev, pjBase, ((srcY * lDelta) + (cBpp * srcX) - ulBltAdjust));
|
|
WAIT_FOR_IDLE_ACL(ppdev, pjBase);
|
|
*(PULONG)(ppdev->pjScreen + ppdev->ulSolidColorOffset) =
|
|
COLOR_REPLICATE(ppdev, lpBlt->bltFX.ddckSrcColorkey.dwColorSpaceLowValue);
|
|
|
|
if (cBpp == 3)
|
|
{
|
|
CP_PEL_DEPTH(ppdev, pjBase, HW_PEL_DEPTH_24BPP);
|
|
CP_PAT_WRAP(ppdev, pjBase, SOLID_COLOR_PATTERN_WRAP_24BPP);
|
|
CP_PAT_Y_OFFSET(ppdev, pjBase, (SOLID_COLOR_PATTERN_OFFSET_24BPP - 1));
|
|
CP_DST_ADDR(ppdev, pjBase, ((destY * lDelta) + (cBpp * destX) - ulBltAdjust));
|
|
}
|
|
else if (cBpp == 2)
|
|
{
|
|
CP_PEL_DEPTH(ppdev, pjBase, HW_PEL_DEPTH_16BPP);
|
|
CP_PAT_WRAP(ppdev, pjBase, SOLID_COLOR_PATTERN_WRAP);
|
|
CP_PAT_Y_OFFSET(ppdev, pjBase, (SOLID_COLOR_PATTERN_OFFSET - 1));
|
|
CP_DST_ADDR(ppdev, pjBase, ((destY * lDelta) + (cBpp * destX) - ulBltAdjust));
|
|
}
|
|
else
|
|
{
|
|
CP_PAT_WRAP(ppdev, pjBase, SOLID_COLOR_PATTERN_WRAP);
|
|
CP_PAT_Y_OFFSET(ppdev, pjBase, (SOLID_COLOR_PATTERN_OFFSET - 1));
|
|
CP_DST_ADDR(ppdev, pjBase, ((destY * lDelta) + (cBpp * destX) - ulBltAdjust));
|
|
}
|
|
|
|
WAIT_FOR_EMPTY_ACL_QUEUE(ppdev, pjBase);
|
|
CP_PEL_DEPTH(ppdev, pjBase, HW_PEL_DEPTH_8BPP);
|
|
CP_ROUTING_CTRL(ppdev, pjBase, 0x33);
|
|
}
|
|
else
|
|
{
|
|
// Opaque
|
|
|
|
CP_FG_ROP(ppdev, pjBase, R3_SRCCOPY);
|
|
CP_SRC_WRAP(ppdev, pjBase, NO_PATTERN_WRAP);
|
|
CP_SRC_Y_OFFSET(ppdev, pjBase, (lDelta - 1));
|
|
CP_DST_Y_OFFSET(ppdev, pjBase, (lDelta - 1));
|
|
CP_XCNT(ppdev, pjBase, ((cBpp * width) - 1));
|
|
CP_YCNT(ppdev, pjBase, (height - 1));
|
|
CP_SRC_ADDR(ppdev, pjBase, ((srcY * lDelta) + (cBpp * srcX) - ulBltAdjust));
|
|
|
|
CP_DST_ADDR(ppdev, pjBase, ((destY * lDelta) + (cBpp * destX) - ulBltAdjust));
|
|
}
|
|
|
|
if (ulBltAdjust) {
|
|
WAIT_FOR_EMPTY_ACL_QUEUE(ppdev, pjBase);
|
|
CP_XY_DIR(ppdev, pjBase, 0);
|
|
}
|
|
}
|
|
|
|
|
|
lpBlt->ddRVal = DD_OK;
|
|
|
|
EXIT(DdBlt);
|
|
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdFlip
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdFlip(
|
|
PDD_FLIPDATA lpFlip)
|
|
{
|
|
PDEV* ppdev;
|
|
BYTE* pjBase;
|
|
BYTE* pjPorts;
|
|
HRESULT ddrval;
|
|
ULONG ulMemoryOffset;
|
|
ULONG ulLowOffset;
|
|
ULONG ulMiddleOffset;
|
|
ULONG ulHighOffset;
|
|
|
|
ENTER(DdFLip);
|
|
|
|
ppdev = (PDEV*) lpFlip->lpDD->dhpdev;
|
|
pjBase = ppdev->pjBase;
|
|
pjPorts = ppdev->pjPorts;
|
|
|
|
// Is the current flip still in progress?
|
|
//
|
|
// Don't want a flip to work until after the last flip is done,
|
|
// so we ask for the general flip status and ignore the vmem.
|
|
|
|
ddrval = vUpdateFlipStatus(ppdev, 0);
|
|
if ((ddrval != DD_OK) || (IS_BUSY(ppdev, pjBase)))
|
|
{
|
|
lpFlip->ddRVal = DDERR_WASSTILLDRAWING;
|
|
EXIT(DdFLip);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
// Do the flip:
|
|
|
|
ulMemoryOffset = (ULONG)(lpFlip->lpSurfTarg->lpGbl->fpVidMem) >> 2;
|
|
|
|
ulLowOffset = 0x0d | ((ulMemoryOffset & 0x0000ff) << 8);
|
|
ulMiddleOffset = 0x0c | ((ulMemoryOffset & 0x00ff00));
|
|
ulHighOffset = 0x33 | ((ulMemoryOffset & 0x0f0000) >> 8);
|
|
|
|
// Make sure that the border/blanking period isn't active; wait if
|
|
// it is. We could return DDERR_WASSTILLDRAWING in this case, but
|
|
// that will increase the odds that we can't flip the next time:
|
|
|
|
while (!(DISPLAY_IS_ACTIVE(pjPorts)))
|
|
;
|
|
|
|
CP_OUT_WORD(pjPorts, CRTC_INDEX, ulLowOffset);
|
|
CP_OUT_WORD(pjPorts, CRTC_INDEX, ulMiddleOffset);
|
|
CP_OUT_WORD(pjPorts, CRTC_INDEX, ulHighOffset);
|
|
|
|
// Remember where and when we were when we did the flip:
|
|
|
|
EngQueryPerformanceCounter(&ppdev->flipRecord.liFlipTime);
|
|
|
|
ppdev->flipRecord.bFlipFlag = TRUE;
|
|
ppdev->flipRecord.bHaveEverCrossedVBlank = FALSE;
|
|
ppdev->flipRecord.bWasEverInDisplay = FALSE;
|
|
|
|
ppdev->flipRecord.fpFlipFrom = lpFlip->lpSurfCurr->lpGbl->fpVidMem;
|
|
|
|
lpFlip->ddRVal = DD_OK;
|
|
|
|
EXIT(DdFLip);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdLock
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdLock(
|
|
PDD_LOCKDATA lpLock)
|
|
{
|
|
PDEV* ppdev;
|
|
BYTE* pjBase;
|
|
HRESULT ddrval;
|
|
|
|
ENTER(DdLock);
|
|
|
|
ppdev = (PDEV*) lpLock->lpDD->dhpdev;
|
|
pjBase = ppdev->pjBase;
|
|
|
|
// Check to see if any pending physical flip has occurred.
|
|
// Don't allow a lock if a blt is in progress:
|
|
|
|
ddrval = vUpdateFlipStatus(ppdev, lpLock->lpDDSurface->lpGbl->fpVidMem);
|
|
if (ddrval != DD_OK)
|
|
{
|
|
lpLock->ddRVal = DDERR_WASSTILLDRAWING;
|
|
EXIT(DdLock);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
if ((ppdev->dwLinearCnt == 0) && (IS_BUSY(ppdev, pjBase)))
|
|
{
|
|
lpLock->ddRVal = DDERR_WASSTILLDRAWING;
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
// Reference count it, just for the heck of it:
|
|
|
|
ppdev->dwLinearCnt++;
|
|
|
|
EXIT(DdLock);
|
|
return(DDHAL_DRIVER_NOTHANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdUnlock
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdUnlock(
|
|
PDD_UNLOCKDATA lpUnlock)
|
|
{
|
|
PDEV* ppdev;
|
|
|
|
ENTER(DdUnlock);
|
|
|
|
ppdev = (PDEV*) lpUnlock->lpDD->dhpdev;
|
|
|
|
ppdev->dwLinearCnt--;
|
|
|
|
EXIT(DdUnlock);
|
|
return(DDHAL_DRIVER_NOTHANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdGetBltStatus
|
|
*
|
|
* Doesn't currently really care what surface is specified, just checks
|
|
* and goes.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdGetBltStatus(
|
|
PDD_GETBLTSTATUSDATA lpGetBltStatus)
|
|
{
|
|
PDEV* ppdev;
|
|
BYTE* pjBase;
|
|
HRESULT ddRVal;
|
|
|
|
ENTER(DdGetBltStatus);
|
|
|
|
ppdev = (PDEV*) lpGetBltStatus->lpDD->dhpdev;
|
|
pjBase = ppdev->pjBase;
|
|
|
|
ddRVal = DD_OK;
|
|
if (lpGetBltStatus->dwFlags == DDGBS_CANBLT)
|
|
{
|
|
// DDGBS_CANBLT case: can we add a blt?
|
|
|
|
ddRVal = vUpdateFlipStatus(ppdev,
|
|
lpGetBltStatus->lpDDSurface->lpGbl->fpVidMem);
|
|
|
|
if (ddRVal == DD_OK)
|
|
{
|
|
// There was no flip going on, so is there room in the FIFO
|
|
// to add a blt?
|
|
|
|
// !!! is this next line correct?
|
|
|
|
if (IS_BUSY(ppdev, pjBase))
|
|
{
|
|
ddRVal = DDERR_WASSTILLDRAWING;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// DDGBS_ISBLTDONE case: is a blt in progress?
|
|
|
|
if (IS_BUSY(ppdev, pjBase))
|
|
{
|
|
ddRVal = DDERR_WASSTILLDRAWING;
|
|
}
|
|
}
|
|
|
|
lpGetBltStatus->ddRVal = ddRVal;
|
|
|
|
EXIT(DdGetBltStatus);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdMapMemory
|
|
*
|
|
* This is a new DDI call specific to Windows NT that is used to map
|
|
* or unmap all the application modifiable portions of the frame buffer
|
|
* into the specified process's address space.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdMapMemory(
|
|
PDD_MAPMEMORYDATA lpMapMemory)
|
|
{
|
|
PDEV* ppdev;
|
|
VIDEO_SHARE_MEMORY ShareMemory;
|
|
VIDEO_SHARE_MEMORY_INFORMATION ShareMemoryInformation;
|
|
DWORD ReturnedDataLength;
|
|
|
|
ENTER(DdMapMemory);
|
|
|
|
ppdev = (PDEV*) lpMapMemory->lpDD->dhpdev;
|
|
|
|
if (lpMapMemory->bMap)
|
|
{
|
|
ShareMemory.ProcessHandle = lpMapMemory->hProcess;
|
|
|
|
// 'RequestedVirtualAddress' isn't actually used for the SHARE IOCTL:
|
|
|
|
ShareMemory.RequestedVirtualAddress = 0;
|
|
|
|
// We map in starting at the top of the frame buffer:
|
|
|
|
ShareMemory.ViewOffset = 0;
|
|
|
|
// We map down to the end of the frame buffer.
|
|
//
|
|
// Note: There is a 64k granularity on the mapping (meaning that
|
|
// we have to round up to 64k).
|
|
//
|
|
// Note: If there is any portion of the frame buffer that must
|
|
// not be modified by an application, that portion of memory
|
|
// MUST NOT be mapped in by this call. This would include
|
|
// any data that, if modified by a malicious application,
|
|
// would cause the driver to crash. This could include, for
|
|
// example, any DSP code that is kept in off-screen memory.
|
|
|
|
ShareMemory.ViewSize
|
|
= ROUND_UP_TO_64K(ppdev->cyMemory * ppdev->lDelta);
|
|
|
|
if (EngDeviceIoControl(ppdev->hDriver,
|
|
IOCTL_VIDEO_SHARE_VIDEO_MEMORY,
|
|
&ShareMemory,
|
|
sizeof(VIDEO_SHARE_MEMORY),
|
|
&ShareMemoryInformation,
|
|
sizeof(VIDEO_SHARE_MEMORY_INFORMATION),
|
|
&ReturnedDataLength))
|
|
{
|
|
DISPDBG((0, "Failed IOCTL_VIDEO_SHARE_MEMORY"));
|
|
|
|
lpMapMemory->ddRVal = DDERR_GENERIC;
|
|
EXIT(DdMapMemory);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
lpMapMemory->fpProcess = (ULONG_PTR) ShareMemoryInformation.VirtualAddress;
|
|
}
|
|
else
|
|
{
|
|
ShareMemory.ProcessHandle = lpMapMemory->hProcess;
|
|
ShareMemory.ViewOffset = 0;
|
|
ShareMemory.ViewSize = 0;
|
|
ShareMemory.RequestedVirtualAddress = (VOID*) lpMapMemory->fpProcess;
|
|
|
|
if (EngDeviceIoControl(ppdev->hDriver,
|
|
IOCTL_VIDEO_UNSHARE_VIDEO_MEMORY,
|
|
&ShareMemory,
|
|
sizeof(VIDEO_SHARE_MEMORY),
|
|
NULL,
|
|
0,
|
|
&ReturnedDataLength))
|
|
{
|
|
RIP("Failed IOCTL_VIDEO_UNSHARE_MEMORY");
|
|
}
|
|
}
|
|
|
|
lpMapMemory->ddRVal = DD_OK;
|
|
EXIT(DdMapMemory);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdGetFlipStatus
|
|
*
|
|
* If the display has gone through one refresh cycle since the flip
|
|
* occurred, we return DD_OK. If it has not gone through one refresh
|
|
* cycle we return DDERR_WASSTILLDRAWING to indicate that this surface
|
|
* is still busy "drawing" the flipped page. We also return
|
|
* DDERR_WASSTILLDRAWING if the bltter is busy and the caller wanted
|
|
* to know if they could flip yet.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdGetFlipStatus(
|
|
PDD_GETFLIPSTATUSDATA lpGetFlipStatus)
|
|
{
|
|
PDEV* ppdev;
|
|
BYTE* pjBase;
|
|
|
|
ENTER(DdGetFlipStatus);
|
|
|
|
ppdev = (PDEV*) lpGetFlipStatus->lpDD->dhpdev;
|
|
pjBase = ppdev->pjBase;
|
|
|
|
// We don't want a flip to work until after the last flip is done,
|
|
// so we ask for the general flip status and ignore the vmem:
|
|
|
|
lpGetFlipStatus->ddRVal = vUpdateFlipStatus(ppdev, 0);
|
|
|
|
// Check if the bltter is busy if someone wants to know if they can
|
|
// flip:
|
|
|
|
if (lpGetFlipStatus->dwFlags == DDGFS_CANFLIP)
|
|
{
|
|
if ((lpGetFlipStatus->ddRVal == DD_OK) && (IS_BUSY(ppdev, pjBase)))
|
|
{
|
|
lpGetFlipStatus->ddRVal = DDERR_WASSTILLDRAWING;
|
|
}
|
|
}
|
|
|
|
EXIT(DdGetFlipStatus);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* DWORD DdWaitForVerticalBlank
|
|
*
|
|
\**************************************************************************/
|
|
|
|
DWORD DdWaitForVerticalBlank(
|
|
PDD_WAITFORVERTICALBLANKDATA lpWaitForVerticalBlank)
|
|
{
|
|
PDEV* ppdev;
|
|
BYTE* pjBase;
|
|
BYTE* pjPorts;
|
|
|
|
ENTER(DdWaitForVerticalBlank);
|
|
|
|
ppdev = (PDEV*) lpWaitForVerticalBlank->lpDD->dhpdev;
|
|
pjBase = ppdev->pjBase;
|
|
pjPorts = ppdev->pjPorts;
|
|
|
|
lpWaitForVerticalBlank->ddRVal = DD_OK;
|
|
|
|
switch (lpWaitForVerticalBlank->dwFlags)
|
|
{
|
|
case DDWAITVB_I_TESTVB:
|
|
|
|
// If TESTVB, it's just a request for the current vertical blank
|
|
// status:
|
|
|
|
if (VBLANK_IS_ACTIVE(pjPorts))
|
|
lpWaitForVerticalBlank->bIsInVB = TRUE;
|
|
else
|
|
lpWaitForVerticalBlank->bIsInVB = FALSE;
|
|
|
|
EXIT(DdWaitForVerticalBlank);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
|
|
case DDWAITVB_BLOCKBEGIN:
|
|
|
|
// If BLOCKBEGIN is requested, we wait until the vertical blank
|
|
// is over, and then wait for the display period to end:
|
|
|
|
while (VBLANK_IS_ACTIVE(pjPorts))
|
|
;
|
|
while (!(VBLANK_IS_ACTIVE(pjPorts)))
|
|
;
|
|
|
|
EXIT(DdWaitForVerticalBlank);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
|
|
case DDWAITVB_BLOCKEND:
|
|
|
|
// If BLOCKEND is requested, we wait for the vblank interval to end:
|
|
|
|
while (!(VBLANK_IS_ACTIVE(pjPorts)))
|
|
;
|
|
while (VBLANK_IS_ACTIVE(pjPorts))
|
|
;
|
|
|
|
EXIT(DdWaitForVerticalBlank);
|
|
return(DDHAL_DRIVER_HANDLED);
|
|
}
|
|
|
|
EXIT(DdWaitForVerticalBlank);
|
|
return(DDHAL_DRIVER_NOTHANDLED);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL DrvGetDirectDrawInfo
|
|
*
|
|
* Will be called before DrvEnableDirectDraw is called.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL DrvGetDirectDrawInfo(
|
|
DHPDEV dhpdev,
|
|
DD_HALINFO* pHalInfo,
|
|
DWORD* pdwNumHeaps,
|
|
VIDEOMEMORY* pvmList, // Will be NULL on first call
|
|
DWORD* pdwNumFourCC,
|
|
DWORD* pdwFourCC) // Will be NULL on first call
|
|
{
|
|
BOOL bCanFlip;
|
|
PDEV* ppdev;
|
|
LONGLONG li;
|
|
OH* poh;
|
|
|
|
ENTER(DrvGetDirectDrawInfo);
|
|
|
|
ppdev = (PDEV*) dhpdev;
|
|
|
|
// We may not support DirectDraw on this card:
|
|
|
|
if (!(ppdev->flStatus & STAT_DIRECTDRAW))
|
|
{
|
|
EXIT(DrvGetDirectDrawInfo);
|
|
return(FALSE);
|
|
}
|
|
|
|
pHalInfo->dwSize = sizeof(*pHalInfo);
|
|
|
|
// Current primary surface attributes. Since HalInfo is zero-initialized
|
|
// by GDI, we only have to fill in the fields which should be non-zero:
|
|
|
|
pHalInfo->vmiData.pvPrimary = ppdev->pjScreen;
|
|
pHalInfo->vmiData.dwDisplayWidth = ppdev->cxScreen;
|
|
pHalInfo->vmiData.dwDisplayHeight = ppdev->cyScreen;
|
|
pHalInfo->vmiData.lDisplayPitch = ppdev->lDelta;
|
|
|
|
pHalInfo->vmiData.ddpfDisplay.dwSize = sizeof(DDPIXELFORMAT);
|
|
pHalInfo->vmiData.ddpfDisplay.dwFlags = DDPF_RGB;
|
|
|
|
// !!! What about 15 vs. 16 Bpp below?
|
|
|
|
pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount = ppdev->cBpp * 8;
|
|
|
|
if (ppdev->iBitmapFormat == BMF_8BPP)
|
|
{
|
|
pHalInfo->vmiData.ddpfDisplay.dwFlags |= DDPF_PALETTEINDEXED8;
|
|
}
|
|
|
|
// These masks will be zero at 8bpp:
|
|
|
|
pHalInfo->vmiData.ddpfDisplay.dwRBitMask = ppdev->flRed;
|
|
pHalInfo->vmiData.ddpfDisplay.dwGBitMask = ppdev->flGreen;
|
|
pHalInfo->vmiData.ddpfDisplay.dwBBitMask = ppdev->flBlue;
|
|
|
|
if (ppdev->iBitmapFormat == BMF_32BPP)
|
|
{
|
|
pHalInfo->vmiData.ddpfDisplay.dwRGBAlphaBitMask
|
|
= ~(ppdev->flRed | ppdev->flGreen | ppdev->flBlue);
|
|
}
|
|
else
|
|
{
|
|
pHalInfo->vmiData.ddpfDisplay.dwRGBAlphaBitMask = 0;
|
|
}
|
|
|
|
// Set up the pointer to the first available video memory after
|
|
// the primary surface:
|
|
|
|
bCanFlip = FALSE;
|
|
*pdwNumHeaps = 0;
|
|
|
|
// Free up as much off-screen memory as possible:
|
|
|
|
bMoveAllDfbsFromOffscreenToDibs(ppdev);
|
|
|
|
// Now simply reserve the biggest chunk for use by DirectDraw:
|
|
|
|
poh = ppdev->pohDirectDraw;
|
|
if (poh == NULL)
|
|
{
|
|
poh = pohAllocate(ppdev,
|
|
NULL,
|
|
ppdev->heap.cxMax,
|
|
ppdev->heap.cyMax,
|
|
FLOH_MAKE_PERMANENT);
|
|
|
|
ppdev->pohDirectDraw = poh;
|
|
}
|
|
|
|
if (poh != NULL)
|
|
{
|
|
*pdwNumHeaps = 1;
|
|
|
|
// Fill in the list of off-screen rectangles if we've been asked
|
|
// to do so:
|
|
|
|
if (pvmList != NULL)
|
|
{
|
|
DISPDBG((1, "DirectDraw gets %li x %li surface at (%li, %li)",
|
|
poh->cx, poh->cy, poh->x, poh->y));
|
|
|
|
pvmList->dwFlags = VIDMEM_ISRECTANGULAR;
|
|
pvmList->fpStart = (poh->y * ppdev->lDelta)
|
|
+ (poh->x * ppdev->cBpp);
|
|
pvmList->dwWidth = poh->cx * ppdev->cBpp;
|
|
pvmList->dwHeight = poh->cy;
|
|
pvmList->ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
|
|
if ((DWORD) ppdev->cyScreen <= pvmList->dwHeight)
|
|
{
|
|
bCanFlip = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Capabilities supported:
|
|
|
|
pHalInfo->ddCaps.dwFXCaps = 0;
|
|
pHalInfo->ddCaps.dwCaps = DDCAPS_BLT
|
|
| DDCAPS_BLTCOLORFILL
|
|
| DDCAPS_COLORKEY;
|
|
|
|
pHalInfo->ddCaps.dwCKeyCaps = DDCKEYCAPS_SRCBLT;
|
|
pHalInfo->ddCaps.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN
|
|
| DDSCAPS_PRIMARYSURFACE;
|
|
if (bCanFlip)
|
|
{
|
|
pHalInfo->ddCaps.ddsCaps.dwCaps |= DDSCAPS_FLIP;
|
|
}
|
|
|
|
// Required alignments of the scan lines for each kind of memory:
|
|
|
|
pHalInfo->vmiData.dwOffscreenAlign = 8 * ppdev->cBpp;
|
|
|
|
// FourCCs supported:
|
|
|
|
*pdwNumFourCC = 0;
|
|
|
|
EXIT(DrvGetDirectDrawInfo);
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL DrvEnableDirectDraw
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL DrvEnableDirectDraw(
|
|
DHPDEV dhpdev,
|
|
DD_CALLBACKS* pCallBacks,
|
|
DD_SURFACECALLBACKS* pSurfaceCallBacks,
|
|
DD_PALETTECALLBACKS* pPaletteCallBacks)
|
|
{
|
|
PDEV* ppdev;
|
|
|
|
ENTER(DrvEnableDirectDraw);
|
|
|
|
ppdev = (PDEV*) dhpdev;
|
|
|
|
pCallBacks->WaitForVerticalBlank = DdWaitForVerticalBlank;
|
|
pCallBacks->MapMemory = DdMapMemory;
|
|
pCallBacks->dwFlags = DDHAL_CB32_WAITFORVERTICALBLANK
|
|
| DDHAL_CB32_MAPMEMORY;
|
|
|
|
pSurfaceCallBacks->Blt = DdBlt;
|
|
pSurfaceCallBacks->Flip = DdFlip;
|
|
pSurfaceCallBacks->Lock = DdLock;
|
|
pSurfaceCallBacks->Unlock = DdUnlock;
|
|
pSurfaceCallBacks->GetBltStatus = DdGetBltStatus;
|
|
pSurfaceCallBacks->GetFlipStatus = DdGetFlipStatus;
|
|
pSurfaceCallBacks->dwFlags = DDHAL_SURFCB32_BLT
|
|
| DDHAL_SURFCB32_FLIP
|
|
| DDHAL_SURFCB32_LOCK
|
|
| DDHAL_SURFCB32_UNLOCK
|
|
| DDHAL_SURFCB32_GETBLTSTATUS
|
|
| DDHAL_SURFCB32_GETFLIPSTATUS;
|
|
|
|
// Note that we don't call 'vGetDisplayDuration' here, for a couple of
|
|
// reasons:
|
|
//
|
|
// o Because the system is already running, it would be disconcerting
|
|
// to pause the graphics for a good portion of a second just to read
|
|
// the refresh rate;
|
|
// o More importantly, we may not be in graphics mode right now.
|
|
//
|
|
// For both reasons, we always measure the refresh rate when we switch
|
|
// to a new mode.
|
|
|
|
EXIT(DrvEnableDirectDraw);
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID DrvDisableDirectDraw
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID DrvDisableDirectDraw(
|
|
DHPDEV dhpdev)
|
|
{
|
|
PDEV* ppdev;
|
|
|
|
ENTER(DrvDisableDirectDraw);
|
|
|
|
ppdev = (PDEV*) dhpdev;
|
|
|
|
// DirectDraw is done with the display, so we can go back to using
|
|
// all of off-screen memory ourselves:
|
|
|
|
pohFree(ppdev, ppdev->pohDirectDraw);
|
|
ppdev->pohDirectDraw = NULL;
|
|
|
|
EXIT(DrvDisableDirectDraw);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bAssertModeDirectDraw
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vAssertModeDirectDraw(
|
|
PDEV* ppdev,
|
|
BOOL bEnabled)
|
|
{
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* BOOL bEnableDirectDraw
|
|
*
|
|
* This function is called when the mode is first initialized, right after
|
|
* the miniport does the mode-set.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bEnableDirectDraw(
|
|
PDEV* ppdev)
|
|
{
|
|
ENTER(bEnableDirectDraw);
|
|
|
|
// We're not going to bother to support accelerated DirectDraw on
|
|
// the pre-ET6000 chips, because they don't have linear frame
|
|
// buffers.
|
|
|
|
if (ppdev->ulChipID == ET6000)
|
|
{
|
|
// Accurately measure the refresh rate for later:
|
|
|
|
vGetDisplayDuration(ppdev);
|
|
|
|
// DirectDraw is all set to be used on this card:
|
|
|
|
ppdev->flStatus |= STAT_DIRECTDRAW;
|
|
}
|
|
|
|
EXIT(bEnableDirectDraw);
|
|
return(TRUE);
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
* VOID vDisableDirectDraw
|
|
*
|
|
\**************************************************************************/
|
|
|
|
VOID vDisableDirectDraw(
|
|
PDEV* ppdev)
|
|
{
|
|
}
|