1807 lines
59 KiB
C
1807 lines
59 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"
|
||
|
|
||
|
// FourCC formats are encoded in reverse because we're little endian:
|
||
|
|
||
|
#define FOURCC_YUY2 '2YUY'
|
||
|
|
||
|
// Worst-case possible number of FIFO entries we'll have to wait for in
|
||
|
// DdBlt for any operation:
|
||
|
|
||
|
#define DDBLT_FIFO_COUNT 7
|
||
|
|
||
|
// 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; \
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vYuvStretch
|
||
|
*
|
||
|
* Does an expanding stretch blt from a 16-bit YUV surface to video memory.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vYuvStretch(
|
||
|
PDEV* ppdev,
|
||
|
RECTL* prclDst,
|
||
|
VOID* pvSrc,
|
||
|
LONG lDeltaSrc,
|
||
|
RECTL* prclSrc)
|
||
|
{
|
||
|
BYTE* pjBase;
|
||
|
BYTE* pjDma;
|
||
|
LONG cxMemory;
|
||
|
LONG cxDst;
|
||
|
LONG cxSrc;
|
||
|
LONG cyDst;
|
||
|
LONG cySrc;
|
||
|
LONG cyWholeDuplicate;
|
||
|
LONG lPartialDuplicate;
|
||
|
LONG lError;
|
||
|
LONG lErrorLimit;
|
||
|
LONG xDstLeft;
|
||
|
LONG xDstRight;
|
||
|
LONG xDstRightFast;
|
||
|
LONG yDstTop;
|
||
|
LONG xSrcAlign;
|
||
|
ULONG cd;
|
||
|
BYTE* pjSrc;
|
||
|
ULONG ulCmd;
|
||
|
LONG lDstAddress;
|
||
|
ULONG* pulSrc;
|
||
|
ULONG* pulDma;
|
||
|
ULONG i;
|
||
|
LONG cyDuplicate;
|
||
|
LONG cyBreak;
|
||
|
LONG iBreak;
|
||
|
|
||
|
pjBase = ppdev->pjBase;
|
||
|
cxMemory = ppdev->cxMemory;
|
||
|
pjDma = pjBase + DMAWND;
|
||
|
ppdev->HopeFlags = SIGN_CACHE; // Only register that's zero when done
|
||
|
|
||
|
cxDst = prclDst->right - prclDst->left;
|
||
|
cxSrc = prclSrc->right - prclSrc->left;
|
||
|
|
||
|
cyDst = prclDst->bottom - prclDst->top;
|
||
|
cySrc = prclSrc->bottom - prclSrc->top;
|
||
|
|
||
|
ASSERTDD((cySrc <= cyDst) && (cxSrc <= cxDst),
|
||
|
"Expanding stretches only may be allowed here");
|
||
|
|
||
|
// We'll be doing the vertical stretching in software, so calculate the
|
||
|
// DDA terms here. We have the luxury of not worrying about overflow
|
||
|
// because DirectDraw limits our coordinate space to 15 bits:
|
||
|
|
||
|
cyWholeDuplicate = (cyDst / cySrc) - 1;
|
||
|
lPartialDuplicate = (cyDst % cySrc);
|
||
|
lErrorLimit = cySrc;
|
||
|
lError = cySrc >> 1;
|
||
|
|
||
|
xDstLeft = prclDst->left;
|
||
|
xDstRight = prclDst->right - 1; // Note this is inclusive
|
||
|
yDstTop = prclDst->top;
|
||
|
cyDst = prclDst->bottom - prclDst->top;
|
||
|
|
||
|
// Fast WRAM-WRAM blts have a funky requirement for 'FXRIGHT':
|
||
|
|
||
|
switch (ppdev->cjPelSize)
|
||
|
{
|
||
|
case 1: xDstRightFast = xDstRight | 0x40; break;
|
||
|
case 2: xDstRightFast = xDstRight | 0x20; break;
|
||
|
case 4: xDstRightFast = xDstRight | 0x10; break;
|
||
|
case 3: xDstRightFast = (((xDstRight * 3) + 2) | 0x40) / 3;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Figure out how many scans we can duplicate before we hit the first
|
||
|
// WRAM boundary:
|
||
|
|
||
|
cyBreak = 0xffff;
|
||
|
for (iBreak = 0; iBreak < ppdev->cyBreak; iBreak++)
|
||
|
{
|
||
|
if (ppdev->ayBreak[iBreak] >= yDstTop)
|
||
|
{
|
||
|
cyBreak = ppdev->ayBreak[iBreak] - yDstTop;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, 8);
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_YDST, yDstTop);
|
||
|
CP_WRITE(pjBase, DWG_CXBNDRY, (xDstRight << bfxright_SHIFT)
|
||
|
| (xDstLeft));
|
||
|
|
||
|
// Make sure we always read dword-aligned from the source:
|
||
|
|
||
|
xSrcAlign = prclSrc->left & 1;
|
||
|
if (xSrcAlign)
|
||
|
{
|
||
|
xDstLeft -= cxDst / cxSrc; // We guess that Millennium takes ceiling
|
||
|
}
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_FXBNDRY, (xDstRight << bfxright_SHIFT)
|
||
|
| (xDstLeft & bfxleft_MASK));
|
||
|
|
||
|
lDstAddress = (yDstTop - 1) * cxMemory + xDstLeft + ppdev->ulYDstOrg;
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_AR5, cxMemory);
|
||
|
CP_WRITE(pjBase, DWG_AR3, lDstAddress);
|
||
|
CP_WRITE(pjBase, DWG_AR0, lDstAddress + cxDst - 1);
|
||
|
|
||
|
cd = (cxSrc + xSrcAlign + 1) >> 1;
|
||
|
pjSrc = (BYTE*) pvSrc + (prclSrc->top * lDeltaSrc)
|
||
|
+ ((prclSrc->left - xSrcAlign) * 2);
|
||
|
|
||
|
ASSERTDD(((ULONG_PTR) pjSrc & 3) == 0, "Must dword align source");
|
||
|
|
||
|
if (cxDst >= 2 * cxSrc)
|
||
|
{
|
||
|
ulCmd = opcode_ILOAD_FILTER |
|
||
|
atype_RPL |
|
||
|
blockm_OFF |
|
||
|
bltmod_BUYUV |
|
||
|
pattern_OFF |
|
||
|
transc_BG_OPAQUE |
|
||
|
bop_SRCCOPY |
|
||
|
shftzero_ZERO |
|
||
|
sgnzero_ZERO;
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_AR2, 2 * cxSrc - 1);
|
||
|
CP_WRITE(pjBase, DWG_AR6, 2 * cxSrc - cxDst - 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ulCmd = opcode_ILOAD_SCALE |
|
||
|
atype_RPL |
|
||
|
blockm_OFF |
|
||
|
bltmod_BUYUV |
|
||
|
pattern_OFF |
|
||
|
transc_BG_OPAQUE |
|
||
|
bop_SRCCOPY |
|
||
|
shftzero_ZERO |
|
||
|
sgnzero_ZERO;
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_AR2, cxSrc);
|
||
|
CP_WRITE(pjBase, DWG_AR6, cxSrc - cxDst);
|
||
|
}
|
||
|
|
||
|
|
||
|
do {
|
||
|
CHECK_FIFO_SPACE(pjBase, 2);
|
||
|
CP_WRITE(pjBase, DWG_DWGCTL, ulCmd);
|
||
|
CP_START(pjBase, DWG_LEN, 1);
|
||
|
|
||
|
// Turn on pseudo-DMA so that we can use PCI burst mode:
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, FIFOSIZE);
|
||
|
BLT_WRITE_ON(ppdev, pjBase);
|
||
|
|
||
|
pulSrc = (ULONG*) pjSrc;
|
||
|
pulDma = (ULONG*) pjDma;
|
||
|
pjSrc += lDeltaSrc;
|
||
|
|
||
|
#if defined(_X86_)
|
||
|
|
||
|
__asm mov esi, pulSrc
|
||
|
__asm mov edi, pulDma
|
||
|
__asm mov ecx, cd
|
||
|
__asm rep movsd
|
||
|
|
||
|
#else
|
||
|
|
||
|
for (i = cd; i != 0; i--)
|
||
|
{
|
||
|
WRITE_REGISTER_ULONG(pulDma, *pulSrc);
|
||
|
pulSrc++;
|
||
|
pulDma++;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
BLT_WRITE_OFF(ppdev, pjBase);
|
||
|
|
||
|
// Do an iteration of the DDA to determine how many lines we'll
|
||
|
// be duplicating. This biases
|
||
|
|
||
|
cyDuplicate = cyWholeDuplicate;
|
||
|
lError += lPartialDuplicate;
|
||
|
if (lError >= lErrorLimit)
|
||
|
{
|
||
|
cyDuplicate++;
|
||
|
lError -= lErrorLimit;
|
||
|
}
|
||
|
|
||
|
// Account for the line we just stretched:
|
||
|
|
||
|
cyDst--;
|
||
|
cyBreak--;
|
||
|
|
||
|
// Remember not to duplicate past where we're supposed to end:
|
||
|
|
||
|
if (cyDuplicate > cyDst)
|
||
|
cyDuplicate = cyDst;
|
||
|
|
||
|
if (cyDuplicate != 0)
|
||
|
{
|
||
|
cyDst -= cyDuplicate;
|
||
|
cyBreak -= cyDuplicate;
|
||
|
|
||
|
if (cyBreak >= 0)
|
||
|
{
|
||
|
// We haven't crossed a WRAM boundary, so we can use a
|
||
|
// WRAM-WRAM blt to duplicate the scan:
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, 4);
|
||
|
CP_WRITE(pjBase, DWG_DWGCTL, opcode_FBITBLT |
|
||
|
atype_RPL |
|
||
|
blockm_OFF |
|
||
|
bltmod_BFCOL |
|
||
|
pattern_OFF |
|
||
|
transc_BG_OPAQUE |
|
||
|
bop_NOP |
|
||
|
shftzero_ZERO |
|
||
|
sgnzero_ZERO);
|
||
|
CP_WRITE(pjBase, DWG_FXRIGHT, xDstRightFast);
|
||
|
CP_START(pjBase, DWG_LEN, cyDuplicate);
|
||
|
CP_WRITE(pjBase, DWG_FXRIGHT, xDstRight);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We just crossed a WRAM boundary, so we have to use a
|
||
|
// regular blt to duplicate the scan:
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, 2);
|
||
|
CP_WRITE(pjBase, DWG_DWGCTL, opcode_BITBLT |
|
||
|
atype_RPL |
|
||
|
blockm_OFF |
|
||
|
bltmod_BFCOL |
|
||
|
pattern_OFF |
|
||
|
transc_BG_OPAQUE |
|
||
|
bop_SRCCOPY |
|
||
|
shftzero_ZERO |
|
||
|
sgnzero_ZERO);
|
||
|
CP_START(pjBase, DWG_LEN, cyDuplicate);
|
||
|
|
||
|
iBreak++;
|
||
|
if (iBreak >= ppdev->cyBreak)
|
||
|
{
|
||
|
// That was the last break we have to worry about:
|
||
|
|
||
|
cyBreak = 0xffff;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cyBreak += ppdev->ayBreak[iBreak]
|
||
|
- ppdev->ayBreak[iBreak - 1];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} while (cyDst != 0);
|
||
|
|
||
|
// Reset the clipping, and we're done!
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, 1);
|
||
|
CP_WRITE(pjBase, DWG_CXBNDRY, (cxMemory - 1) << bcxright_SHIFT);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vYuvBlt
|
||
|
*
|
||
|
* Does a non-stretching blt from a 16-bit YUV surface to video memory.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vYuvBlt(
|
||
|
PDEV* ppdev,
|
||
|
RECTL* prclDst,
|
||
|
VOID* pvSrc,
|
||
|
LONG lDeltaSrc,
|
||
|
POINTL* pptlSrc)
|
||
|
{
|
||
|
BYTE* pjBase;
|
||
|
BYTE* pjDma;
|
||
|
LONG cy;
|
||
|
LONG xLeft;
|
||
|
LONG xRight;
|
||
|
LONG xAlign;
|
||
|
ULONG cd;
|
||
|
BYTE* pjSrc;
|
||
|
ULONG* pulSrc;
|
||
|
ULONG* pulDma;
|
||
|
ULONG i;
|
||
|
|
||
|
pjBase = ppdev->pjBase;
|
||
|
pjDma = pjBase + DMAWND;
|
||
|
ppdev->HopeFlags = SIGN_CACHE; // Only register that's zero when done
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, 6);
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_DWGCTL, opcode_ILOAD
|
||
|
| atype_RPL
|
||
|
| blockm_OFF
|
||
|
| pattern_OFF
|
||
|
| transc_BG_OPAQUE
|
||
|
| bop_SRCCOPY
|
||
|
| shftzero_ZERO
|
||
|
| sgnzero_ZERO
|
||
|
| bltmod_BUYUV);
|
||
|
|
||
|
xLeft = prclDst->left;
|
||
|
xRight = prclDst->right;
|
||
|
cy = prclDst->bottom - prclDst->top;
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_AR3, 0);
|
||
|
CP_WRITE(pjBase, DWG_YDSTLEN, (prclDst->top << yval_SHIFT) | cy);
|
||
|
|
||
|
// Make sure we always read dword-aligned from the source:
|
||
|
|
||
|
xAlign = pptlSrc->x & 1;
|
||
|
if (xAlign)
|
||
|
{
|
||
|
CP_WRITE(pjBase, DWG_CXLEFT, xLeft);
|
||
|
xLeft--;
|
||
|
}
|
||
|
|
||
|
CP_WRITE(pjBase, DWG_FXBNDRY, ((xRight - 1) << bfxright_SHIFT) |
|
||
|
(xLeft & bfxleft_MASK));
|
||
|
CP_START(pjBase, DWG_AR0, xRight - xLeft - 1);
|
||
|
|
||
|
cd = (xRight - xLeft + 1) >> 1;
|
||
|
pjSrc = (BYTE*) pvSrc + (pptlSrc->y * lDeltaSrc)
|
||
|
+ ((pptlSrc->x - xAlign) * 2);
|
||
|
|
||
|
ASSERTDD(((ULONG_PTR) pjSrc & 3) == 0, "Must dword align source");
|
||
|
|
||
|
// Turn on pseudo-DMA so that we can use PCI burst mode:
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, FIFOSIZE);
|
||
|
BLT_WRITE_ON(ppdev, pjBase);
|
||
|
|
||
|
do {
|
||
|
pulSrc = (ULONG*) pjSrc;
|
||
|
pulDma = (ULONG*) pjDma;
|
||
|
pjSrc += lDeltaSrc;
|
||
|
|
||
|
#if defined(_X86_)
|
||
|
|
||
|
__asm mov esi, pulSrc
|
||
|
__asm mov edi, pulDma
|
||
|
__asm mov ecx, cd
|
||
|
__asm rep movsd
|
||
|
|
||
|
#else
|
||
|
|
||
|
for (i = cd; i != 0; i--)
|
||
|
{
|
||
|
WRITE_REGISTER_ULONG(pulDma, *pulSrc);
|
||
|
pulSrc++;
|
||
|
pulDma++;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
} while (--cy != 0);
|
||
|
|
||
|
// Reset the registers and leave:
|
||
|
|
||
|
BLT_WRITE_OFF(ppdev, pjBase);
|
||
|
if (xAlign)
|
||
|
{
|
||
|
CHECK_FIFO_SPACE(pjBase, 1);
|
||
|
CP_WRITE(pjBase, DWG_CXLEFT, 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/******************************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 8
|
||
|
|
||
|
VOID vGetDisplayDuration(PDEV* ppdev)
|
||
|
{
|
||
|
BYTE* pjBase = ppdev->pjBase;
|
||
|
LONG i;
|
||
|
LONG j;
|
||
|
LONGLONG li;
|
||
|
LONGLONG liMin;
|
||
|
LONGLONG aliMeasurement[NUM_MEASUREMENTS_TO_TAKE + 1];
|
||
|
|
||
|
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(pjBase))
|
||
|
;
|
||
|
while (!(VBLANK_IS_ACTIVE(pjBase)))
|
||
|
;
|
||
|
|
||
|
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(pjBase)))
|
||
|
;
|
||
|
|
||
|
for (j = 0; j < NUM_VBLANKS_TO_MEASURE; j++)
|
||
|
{
|
||
|
while (VBLANK_IS_ACTIVE(pjBase))
|
||
|
;
|
||
|
while (!(VBLANK_IS_ACTIVE(pjBase)))
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
EngQueryPerformanceCounter(&aliMeasurement[NUM_MEASUREMENTS_TO_TAKE]);
|
||
|
|
||
|
// Use the minimum:
|
||
|
|
||
|
liMin = aliMeasurement[1] - aliMeasurement[0];
|
||
|
|
||
|
DISPDBG((1, "Refresh count: %li - %li", 1, (ULONG) liMin));
|
||
|
|
||
|
for (i = 2; i <= NUM_MEASUREMENTS_TO_TAKE; i++)
|
||
|
{
|
||
|
li = aliMeasurement[i] - aliMeasurement[i - 1];
|
||
|
|
||
|
DISPDBG((1, " %li - %li", i, (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.bFlipFlag = FALSE;
|
||
|
ppdev->flipRecord.fpFlipFrom = 0;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* HRESULT ddrvalUpdateFlipStatus
|
||
|
*
|
||
|
* Checks to if the most recent flip has occurred.
|
||
|
*
|
||
|
* Takes advantage of the hardware's ability to get the current scan line
|
||
|
* to determine if a vertical retrace has occured since the flip command
|
||
|
* was given.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
HRESULT ddrvalUpdateFlipStatus(
|
||
|
PDEV* ppdev,
|
||
|
FLATPTR fpVidMem) // Surface for which we're requesting flip status;
|
||
|
// -1 indicates status of last flip, regardless of what
|
||
|
// surface it was.
|
||
|
{
|
||
|
BYTE* pjBase;
|
||
|
DWORD dwScanLine;
|
||
|
LONGLONG liTime;
|
||
|
|
||
|
pjBase = ppdev->pjBase;
|
||
|
|
||
|
if (ppdev->flipRecord.bFlipFlag)
|
||
|
{
|
||
|
dwScanLine = GET_SCANLINE(pjBase);
|
||
|
if (dwScanLine < ppdev->flipRecord.dwScanLine)
|
||
|
{
|
||
|
ppdev->flipRecord.bFlipFlag = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ppdev->flipRecord.dwScanLine = dwScanLine;
|
||
|
if ((fpVidMem == (FLATPTR) -1) ||
|
||
|
(fpVidMem == ppdev->flipRecord.fpFlipFrom))
|
||
|
{
|
||
|
// Sampling the current scan line at random times is not a
|
||
|
// fool-proof indicator that the flip has occured. As a
|
||
|
// backup, if the time elapsed since the flip command was
|
||
|
// given is more than the duration of one entire refresh of
|
||
|
// the display, then we know for sure it has happened:
|
||
|
|
||
|
EngQueryPerformanceCounter(&liTime);
|
||
|
|
||
|
if (liTime - ppdev->flipRecord.liFlipTime
|
||
|
<= ppdev->flipRecord.liFlipDuration)
|
||
|
{
|
||
|
return(DDERR_WASSTILLDRAWING);
|
||
|
}
|
||
|
|
||
|
ppdev->flipRecord.bFlipFlag = FALSE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(DD_OK);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdBlt
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdBlt(
|
||
|
PDD_BLTDATA lpBlt)
|
||
|
{
|
||
|
PDD_SURFACE_GLOBAL srcSurf;
|
||
|
PDD_SURFACE_LOCAL dstSurfx;
|
||
|
PDD_SURFACE_GLOBAL dstSurf;
|
||
|
PDEV* ppdev;
|
||
|
HRESULT ddRVal;
|
||
|
DWORD dstX;
|
||
|
DWORD dstY;
|
||
|
DWORD dwFlags;
|
||
|
DWORD srcX;
|
||
|
DWORD srcY;
|
||
|
LONG dstWidth;
|
||
|
LONG dstHeight;
|
||
|
LONG srcWidth;
|
||
|
LONG srcHeight;
|
||
|
ULONG ulBltCmd;
|
||
|
LONG lSrcStart;
|
||
|
LONG lSignedPitch;
|
||
|
RECTL rclSrc;
|
||
|
RECTL rclDest;
|
||
|
BYTE* pjBase;
|
||
|
|
||
|
ppdev = (PDEV*) lpBlt->lpDD->dhpdev;
|
||
|
pjBase = ppdev->pjBase;
|
||
|
|
||
|
dstSurfx = lpBlt->lpDDDestSurface;
|
||
|
dstSurf = dstSurfx->lpGbl;
|
||
|
|
||
|
ASSERTDD(dstSurf->ddpfSurface.dwSize == sizeof(DDPIXELFORMAT),
|
||
|
"NT is supposed to guarantee that ddpfSurface.dwSize is valid");
|
||
|
|
||
|
// We don't have to do any drawing to YUV surfaces. Note that unlike
|
||
|
// Windows 95, Windows NT always guarantees that there will be a valid
|
||
|
// 'ddpfSurface' structure, so we don't have to first check if
|
||
|
// 'dwSize == sizeof(DDPIXELFORMAT)':
|
||
|
|
||
|
if (dstSurf->ddpfSurface.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
// Is a flip in progress?
|
||
|
|
||
|
ddRVal = ddrvalUpdateFlipStatus(ppdev, dstSurf->fpVidMem);
|
||
|
if (ddRVal != DD_OK)
|
||
|
{
|
||
|
lpBlt->ddRVal = ddRVal;
|
||
|
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.
|
||
|
//
|
||
|
// The FIFO wait should account for the worst-case possible
|
||
|
// blt that we would do:
|
||
|
//
|
||
|
// We should check for enough entries that we're guaranteed
|
||
|
// to not have to wait later in this routine.
|
||
|
|
||
|
if (GET_FIFO_SPACE(pjBase) < DDBLT_FIFO_COUNT)
|
||
|
{
|
||
|
lpBlt->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Copy destination rectangle:
|
||
|
|
||
|
dstX = lpBlt->rDest.left;
|
||
|
dstY = lpBlt->rDest.top;
|
||
|
dstWidth = lpBlt->rDest.right - lpBlt->rDest.left;
|
||
|
dstHeight = lpBlt->rDest.bottom - lpBlt->rDest.top;
|
||
|
|
||
|
convertToGlobalCord(dstX, dstY, dstSurf);
|
||
|
|
||
|
if (dwFlags & DDBLT_COLORFILL)
|
||
|
{
|
||
|
ppdev->HopeFlags = (SIGN_CACHE | ARX_CACHE | PATTERN_CACHE);
|
||
|
|
||
|
if (ppdev->iBitmapFormat == BMF_24BPP)
|
||
|
{
|
||
|
// We can't use block mode.
|
||
|
|
||
|
ulBltCmd = (opcode_TRAP + blockm_OFF + atype_RPL + solid_SOLID +
|
||
|
arzero_ZERO + sgnzero_ZERO + shftzero_ZERO +
|
||
|
bop_SRCCOPY + pattern_OFF + transc_BG_OPAQUE);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ulBltCmd = (opcode_TRAP + blockm_ON + solid_SOLID +
|
||
|
arzero_ZERO + sgnzero_ZERO + shftzero_ZERO +
|
||
|
bop_SRCCOPY + pattern_OFF + transc_BG_OPAQUE);
|
||
|
}
|
||
|
|
||
|
CHECK_FIFO_SPACE(pjBase, 4);
|
||
|
CP_WRITE(pjBase, DWG_DWGCTL, ulBltCmd);
|
||
|
CP_WRITE(pjBase, DWG_FCOL, COLOR_REPLICATE(ppdev, lpBlt->bltFX.dwFillColor));
|
||
|
CP_WRITE(pjBase, DWG_FXBNDRY, (((dstX + dstWidth) << bfxright_SHIFT) | dstX));
|
||
|
CP_START(pjBase, DWG_YDSTLEN, (((dstY) << yval_SHIFT) | dstHeight));
|
||
|
|
||
|
lpBlt->ddRVal = DD_OK;
|
||
|
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 source rectangle dimensions:
|
||
|
|
||
|
srcSurf = lpBlt->lpDDSrcSurface->lpGbl;
|
||
|
srcX = lpBlt->rSrc.left;
|
||
|
srcY = lpBlt->rSrc.top;
|
||
|
srcWidth = lpBlt->rSrc.right - lpBlt->rSrc.left;
|
||
|
srcHeight = lpBlt->rSrc.bottom - lpBlt->rSrc.top;
|
||
|
|
||
|
|
||
|
rclDest.left = dstX;
|
||
|
rclDest.right = dstX + dstWidth;
|
||
|
rclDest.top = dstY;
|
||
|
rclDest.bottom = dstY + dstHeight;
|
||
|
|
||
|
if (srcSurf->ddpfSurface.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
rclSrc.left = srcX;
|
||
|
rclSrc.top = srcY;
|
||
|
rclSrc.right = srcX + srcWidth;
|
||
|
rclSrc.bottom = srcY + srcHeight;
|
||
|
|
||
|
if ((dstWidth == srcWidth) && (dstHeight == srcHeight))
|
||
|
{
|
||
|
vYuvBlt(ppdev,
|
||
|
&rclDest,
|
||
|
(VOID*) srcSurf->fpVidMem,
|
||
|
srcSurf->lPitch,
|
||
|
(POINTL*) &rclSrc);
|
||
|
}
|
||
|
|
||
|
// Note that we would fall over if we actually got a shrink here,
|
||
|
// even though we've set our caps to indicate we can only do
|
||
|
// expands. We're paranoid and don't want to ever fall over:
|
||
|
|
||
|
else if ((dstWidth >= srcWidth) && (dstHeight >= srcHeight))
|
||
|
{
|
||
|
vYuvStretch(ppdev,
|
||
|
&rclDest,
|
||
|
(VOID*) srcSurf->fpVidMem,
|
||
|
srcSurf->lPitch,
|
||
|
&rclSrc);
|
||
|
}
|
||
|
|
||
|
lpBlt->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// NT only ever gives us SRCCOPY rops, so don't even bother checking
|
||
|
// for anything else.
|
||
|
|
||
|
convertToGlobalCord(srcX, srcY, srcSurf);
|
||
|
|
||
|
rclSrc.left = srcX;
|
||
|
rclSrc.top = srcY;
|
||
|
rclSrc.right = srcX + srcWidth;
|
||
|
rclSrc.bottom = srcY + srcHeight;
|
||
|
|
||
|
// Must be set for our copy routines to operate properly:
|
||
|
|
||
|
ppdev->xOffset = 0;
|
||
|
ppdev->yOffset = 0;
|
||
|
|
||
|
if ((srcWidth == dstWidth) && (srcHeight == dstHeight))
|
||
|
{
|
||
|
// There's no stretching involved, so do a straight screen-to-
|
||
|
// screen copy. 'vMilCopyBlt' takes care of the overlapping
|
||
|
// cases, and
|
||
|
|
||
|
vMilCopyBlt(ppdev, 1, &rclDest, 0xcccc, (POINTL*) &rclSrc, &rclDest);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Ugh, we've been asked to stretch an off-screen surface. We'll
|
||
|
// just pass it to our hardware-assisted StretchBlt routine.
|
||
|
//
|
||
|
// Unfortunately, the source is in off-screen memory and so the
|
||
|
// performance will be terrible -- slower than if the surface had
|
||
|
// been created in system memory. We have to support stretched RGB
|
||
|
// surfaces in the first place because we set DDCAPS_BLTSTRETCH so
|
||
|
// that we could use the Millennium's YUV stretch capabilities --
|
||
|
// and DirectDraw has no concept of being able to say "we support
|
||
|
// hardware stretches with these types of off-screen surfaces, but
|
||
|
// not those with those other types of off-screen surfaces." Oh
|
||
|
// well. I expect that if applications will be doing stretches,
|
||
|
// they'll be doing it mostly from YUV surfaces (as will be the
|
||
|
// case with ActiveMovie), so this should be a win overall.
|
||
|
//
|
||
|
// Note: If you are modeling your driver on this code and don't have
|
||
|
// any hardware stretch capabilities, then simply don't set
|
||
|
// DDCAPS_BLTSTRETCH, and you'll never have to worry about
|
||
|
// this! We only do this weirdness here because the
|
||
|
// Millennium can hardware stretch YUV surfaces but not RGB
|
||
|
// surfaces. (Sort of.)
|
||
|
|
||
|
vStretchDIB(ppdev,
|
||
|
&rclDest,
|
||
|
ppdev->pjScreen + (ppdev->ulYDstOrg * ppdev->cjPelSize),
|
||
|
ppdev->lDelta,
|
||
|
&rclSrc,
|
||
|
&rclDest);
|
||
|
}
|
||
|
|
||
|
lpBlt->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdFlip
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdFlip(
|
||
|
PDD_FLIPDATA lpFlip)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjBase;
|
||
|
HRESULT ddRVal;
|
||
|
ULONG ulMemoryOffset;
|
||
|
ULONG ulLowOffset;
|
||
|
ULONG ulMiddleOffset;
|
||
|
ULONG ulHighOffset;
|
||
|
BYTE jReg;
|
||
|
|
||
|
ppdev = (PDEV*) lpFlip->lpDD->dhpdev;
|
||
|
pjBase = ppdev->pjBase;
|
||
|
|
||
|
// 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 = ddrvalUpdateFlipStatus(ppdev, (FLATPTR) -1);
|
||
|
if ((ddRVal != DD_OK) || (IS_BUSY(pjBase)))
|
||
|
{
|
||
|
lpFlip->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// Do the flip:
|
||
|
|
||
|
ulMemoryOffset = (ULONG)(lpFlip->lpSurfTarg->lpGbl->fpVidMem >> 2);
|
||
|
|
||
|
ulMemoryOffset >>= ((ppdev->flFeatures & INTERLEAVE_MODE) ? 1 : 0);
|
||
|
|
||
|
ulLowOffset = 0x0d | ((ulMemoryOffset & 0x0000ff) << 8);
|
||
|
ulMiddleOffset = 0x0c | ((ulMemoryOffset & 0x00ff00));
|
||
|
ulHighOffset = 0x00 | ((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(pjBase)))
|
||
|
;
|
||
|
|
||
|
CP_WRITE_REGISTER_BYTE(pjBase + VGA_CRTCEXT_INDEX, 0x00);
|
||
|
jReg = CP_READ_REGISTER_BYTE(pjBase + VGA_CRTCEXT_DATA);
|
||
|
jReg &= ~0x0f;
|
||
|
CP_WRITE_REGISTER_WORD(pjBase + VGA_CRTC_INDEX, ulLowOffset);
|
||
|
CP_WRITE_REGISTER_WORD(pjBase + VGA_CRTC_INDEX, ulMiddleOffset);
|
||
|
CP_WRITE_REGISTER_WORD(pjBase + VGA_CRTCEXT_INDEX, ((ulHighOffset) |
|
||
|
(jReg << 8)));
|
||
|
|
||
|
// Remember where and when we were when we did the flip:
|
||
|
|
||
|
EngQueryPerformanceCounter(&ppdev->flipRecord.liFlipTime);
|
||
|
|
||
|
ppdev->flipRecord.dwScanLine = GET_SCANLINE(pjBase);
|
||
|
ppdev->flipRecord.bFlipFlag = TRUE;
|
||
|
ppdev->flipRecord.fpFlipFrom = lpFlip->lpSurfCurr->lpGbl->fpVidMem;
|
||
|
|
||
|
lpFlip->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdLock
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdLock(
|
||
|
PDD_LOCKDATA lpLock)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjBase;
|
||
|
DD_SURFACE_GLOBAL* lpSurface;
|
||
|
HRESULT ddRVal;
|
||
|
|
||
|
ppdev = (PDEV*) lpLock->lpDD->dhpdev;
|
||
|
pjBase = ppdev->pjBase;
|
||
|
lpSurface = lpLock->lpDDSurface->lpGbl;
|
||
|
|
||
|
if (lpSurface->ddpfSurface.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
// We create all FourCC surfaces in system memory, so just return
|
||
|
// the user-mode address:
|
||
|
|
||
|
lpLock->lpSurfData = (VOID*) lpSurface->fpVidMem;
|
||
|
lpLock->ddRVal = DD_OK;
|
||
|
|
||
|
// When a driver returns DD_OK and DDHAL_DRIVER_HANDLED from DdLock,
|
||
|
// DirectDraw expects it to have adjusted the resulting pointer
|
||
|
// to point to the upper left corner of the specified rectangle, if
|
||
|
// any:
|
||
|
|
||
|
if (lpLock->bHasRect)
|
||
|
{
|
||
|
lpLock->lpSurfData = (VOID*) ((BYTE*) lpLock->lpSurfData
|
||
|
+ lpLock->rArea.top * lpSurface->lPitch
|
||
|
+ lpLock->rArea.left
|
||
|
* (lpSurface->ddpfSurface.dwYUVBitCount >> 3));
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// Check to see if any pending physical flip has occurred.
|
||
|
// Don't allow a lock if a blt is in progress:
|
||
|
|
||
|
ddRVal = ddrvalUpdateFlipStatus(ppdev, lpSurface->fpVidMem);
|
||
|
if (ddRVal != DD_OK)
|
||
|
{
|
||
|
lpLock->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// Here's one of the places where the Windows 95 and Windows NT DirectDraw
|
||
|
// implementations differ: on Windows NT, you should watch for
|
||
|
// DDLOCK_WAIT and loop in the driver while the accelerator is busy.
|
||
|
// On Windows 95, it doesn't really matter.
|
||
|
//
|
||
|
// (The reason is that Windows NT allows applications to draw directly
|
||
|
// to the frame buffer even while the accelerator is running, and does
|
||
|
// not synchronize everything on the Win16Lock. Note that on Windows NT,
|
||
|
// it is even possible for multiple threads to be holding different
|
||
|
// DirectDraw surface locks at the same time.)
|
||
|
|
||
|
if (lpLock->dwFlags & DDLOCK_WAIT)
|
||
|
{
|
||
|
WAIT_NOT_BUSY(pjBase)
|
||
|
}
|
||
|
else if (IS_BUSY(pjBase))
|
||
|
{
|
||
|
lpLock->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// Because we correctly set 'fpVidMem' to be the offset into our frame
|
||
|
// buffer when we created the surface, DirectDraw will automatically take
|
||
|
// care of adding in the user-mode frame buffer address if we return
|
||
|
// DDHAL_DRIVER_NOTHANDLED:
|
||
|
|
||
|
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;
|
||
|
HRESULT ddRVal;
|
||
|
BYTE* pjBase;
|
||
|
|
||
|
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 = ddrvalUpdateFlipStatus(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?
|
||
|
|
||
|
if (GET_FIFO_SPACE(pjBase) < DDBLT_FIFO_COUNT)
|
||
|
{
|
||
|
ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// DDGBS_ISBLTDONE case: is a blt in progress?
|
||
|
|
||
|
if (IS_BUSY(pjBase))
|
||
|
{
|
||
|
ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lpGetBltStatus->ddRVal = ddRVal;
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
lpMapMemory->fpProcess = (FLATPTR)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;
|
||
|
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;
|
||
|
|
||
|
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 = ddrvalUpdateFlipStatus(ppdev, (FLATPTR) -1);
|
||
|
|
||
|
// 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(pjBase)))
|
||
|
{
|
||
|
lpGetFlipStatus->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdWaitForVerticalBlank
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdWaitForVerticalBlank(
|
||
|
PDD_WAITFORVERTICALBLANKDATA lpWaitForVerticalBlank)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjBase;
|
||
|
|
||
|
ppdev = (PDEV*) lpWaitForVerticalBlank->lpDD->dhpdev;
|
||
|
pjBase = ppdev->pjBase;
|
||
|
|
||
|
switch (lpWaitForVerticalBlank->dwFlags)
|
||
|
{
|
||
|
case DDWAITVB_I_TESTVB:
|
||
|
|
||
|
// If TESTVB, it's just a request for the current vertical blank
|
||
|
// status:
|
||
|
|
||
|
if (VBLANK_IS_ACTIVE(pjBase))
|
||
|
{
|
||
|
lpWaitForVerticalBlank->bIsInVB = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lpWaitForVerticalBlank->bIsInVB = FALSE;
|
||
|
}
|
||
|
|
||
|
lpWaitForVerticalBlank->ddRVal = DD_OK;
|
||
|
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(pjBase))
|
||
|
;
|
||
|
while (!(VBLANK_IS_ACTIVE(pjBase)))
|
||
|
;
|
||
|
|
||
|
lpWaitForVerticalBlank->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
|
||
|
case DDWAITVB_BLOCKEND:
|
||
|
|
||
|
// If BLOCKEND is requested, we wait for the vblank interval to end:
|
||
|
|
||
|
while (!(VBLANK_IS_ACTIVE(pjBase)))
|
||
|
;
|
||
|
while (VBLANK_IS_ACTIVE(pjBase))
|
||
|
;
|
||
|
|
||
|
lpWaitForVerticalBlank->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdGetScanLine
|
||
|
*
|
||
|
* Reads the scan line currently being scanned by the CRT.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdGetScanLine(
|
||
|
PDD_GETSCANLINEDATA lpGetScanLine)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjBase;
|
||
|
|
||
|
ppdev = (PDEV*) lpGetScanLine->lpDD->dhpdev;
|
||
|
pjBase = ppdev->pjBase;
|
||
|
|
||
|
lpGetScanLine->dwScanLine = GET_SCANLINE(pjBase);
|
||
|
|
||
|
lpGetScanLine->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdCanCreateSurface
|
||
|
*
|
||
|
* Called by DirectDraw to determine if the driver can create a particular
|
||
|
* off-screen surface type.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdCanCreateSurface(
|
||
|
PDD_CANCREATESURFACEDATA lpCanCreateSurface)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
LPDDSURFACEDESC lpSurfaceDesc;
|
||
|
|
||
|
ppdev = (PDEV*) lpCanCreateSurface->lpDD->dhpdev;
|
||
|
lpSurfaceDesc = lpCanCreateSurface->lpDDSurfaceDesc;
|
||
|
|
||
|
// It's trivially easy to create surfaces that are the same type as
|
||
|
// the primary surface:
|
||
|
|
||
|
if (!lpCanCreateSurface->bIsDifferentPixelFormat)
|
||
|
{
|
||
|
lpCanCreateSurface->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// The only type of YUV mode that the Millennium supports is
|
||
|
// "YUY2". The Millennium also supports 24bpp and 32bpp surfaces,
|
||
|
// but we won't support them because they're not used very much
|
||
|
// and there isn't any good testing coverage for it.
|
||
|
//
|
||
|
// In addition, the Millennium supports YUV only when in RGB modes,
|
||
|
// and at 8bpp we're always running palettized.
|
||
|
|
||
|
if ((lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_FOURCC) &&
|
||
|
(lpSurfaceDesc->ddpfPixelFormat.dwFourCC == FOURCC_YUY2) &&
|
||
|
((ppdev->iBitmapFormat == BMF_16BPP) ||
|
||
|
(ppdev->iBitmapFormat == BMF_32BPP)))
|
||
|
{
|
||
|
// We have to fill-in the bit count:
|
||
|
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwYUVBitCount = 16;
|
||
|
|
||
|
lpCanCreateSurface->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
if (lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB)
|
||
|
{
|
||
|
DISPDBG((0, "Failed creation of %libpp RGB surface %lx %lx %lx",
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount,
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwRBitMask,
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwGBitMask,
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwBBitMask));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DISPDBG((0, "Failed creation of type 0x%lx YUV 0x%lx surface",
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwFlags,
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwFourCC));
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdCreateSurface
|
||
|
*
|
||
|
* Creates an off-screen surface.
|
||
|
*
|
||
|
* We use the Millennium's own off-screen heap manager instead of DirectDraw's
|
||
|
* so that the MCD and DirectDraw parts can coexist -- at the time of this
|
||
|
* writing NT has no support for call-backs from the driver to allocate memory,
|
||
|
* which we need to do to allocate the MCD's back buffer and Z-buffer. So
|
||
|
* we simply manage all of off-screen memory ourselves.
|
||
|
*
|
||
|
* In addition, on the Millennium, YUV surfaces must live in CPU memory.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdCreateSurface(
|
||
|
PDD_CREATESURFACEDATA lpCreateSurface)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
DD_SURFACE_GLOBAL* lpSurface;
|
||
|
LPDDSURFACEDESC lpSurfaceDesc;
|
||
|
LONG wWidth;
|
||
|
LONG wHeight;
|
||
|
LONG lPitch;
|
||
|
OH* poh;
|
||
|
FLATPTR fpVidMem;
|
||
|
|
||
|
ppdev = (PDEV*) lpCreateSurface->lpDD->dhpdev;
|
||
|
|
||
|
// On Windows NT, dwSCnt will always be 1, so there will only ever
|
||
|
// be one entry in the 'lplpSList' array:
|
||
|
|
||
|
lpSurface = lpCreateSurface->lplpSList[0]->lpGbl;
|
||
|
lpSurfaceDesc = lpCreateSurface->lpDDSurfaceDesc;
|
||
|
|
||
|
wWidth = lpSurface->wWidth;
|
||
|
wHeight = lpSurface->wHeight;
|
||
|
|
||
|
// We repeat the same checks we did in 'DdCanCreateSurface' because
|
||
|
// it's possible that an application doesn't call 'DdCanCreateSurface'
|
||
|
// before calling 'DdCreateSurface'.
|
||
|
|
||
|
ASSERTDD(lpSurface->ddpfSurface.dwSize == sizeof(DDPIXELFORMAT),
|
||
|
"NT is supposed to guarantee that ddpfSurface.dwSize is valid");
|
||
|
|
||
|
// Note that the Millennium cannot do YUV surfaces at 24bpp or at
|
||
|
// palettized 8bpp:
|
||
|
|
||
|
if ((lpSurface->ddpfSurface.dwFlags & DDPF_FOURCC) &&
|
||
|
(lpSurface->ddpfSurface.dwFourCC == FOURCC_YUY2) &&
|
||
|
((ppdev->iBitmapFormat == BMF_16BPP) ||
|
||
|
(ppdev->iBitmapFormat == BMF_32BPP)))
|
||
|
{
|
||
|
// Compute the stride of the surface, keeping in mind that it has
|
||
|
// to be dword aligned. Since the Millennium supports only 16bpp
|
||
|
// YUV surfaces, this is easy to do:
|
||
|
|
||
|
lPitch = (2 * wWidth + 3) & ~3;
|
||
|
|
||
|
// By setting 'fpVidMem' to 'DDHAL_PLEASEALLOC_USERMEM', we can have
|
||
|
// DirectDraw allocate a piece of user-mode memory of our requested
|
||
|
// size.
|
||
|
//
|
||
|
// Note that we could not simply call EngAllocMem, because that gives
|
||
|
// us a chunk of kernel-mode memory that is not visible from user-mode.
|
||
|
// We also cannot call EngAllocUserMem for obscure reasons dealing with
|
||
|
// the fact EngFreeUserMem must be called from the same process context
|
||
|
// in which the memory was allocated, and DirectDraw sometimes needs
|
||
|
// to call DestroySurface from the context of a different process.
|
||
|
|
||
|
lpSurface->fpVidMem = DDHAL_PLEASEALLOC_USERMEM;
|
||
|
lpSurface->dwUserMemSize = lPitch * wHeight;
|
||
|
lpSurface->lPitch = lPitch;
|
||
|
|
||
|
// DirectDraw expects us to fill in the following fields, too:
|
||
|
|
||
|
lpSurface->ddpfSurface.dwYUVBitCount = 16;
|
||
|
lpSurfaceDesc->lPitch = lPitch;
|
||
|
lpSurfaceDesc->dwFlags |= DDSD_PITCH;
|
||
|
|
||
|
DISPDBG((0, "Created YUV: %li x %li", wWidth, wHeight));
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Due to weirdness of the Matrox, we create non-flippable off-screen
|
||
|
// surfaces only if running at 8bpp. (The reason is that at 16bpp and
|
||
|
// 32bpp, we report DDCAPS_BLTSTRETCH so that applications can stretch
|
||
|
// YUV surfaces via the hardware -- but the hardware is increidbly
|
||
|
// slow at stretching off-screen RGB surfaces, we don't want off-screen
|
||
|
// RGB surfaces that are likely to be stretched.)
|
||
|
|
||
|
if ((ppdev->iBitmapFormat == BMF_8BPP) ||
|
||
|
((wWidth == ppdev->cxScreen) && (wHeight == ppdev->cyScreen)))
|
||
|
{
|
||
|
// Allocate a space in off-screen memory, using our own heap
|
||
|
// manager:
|
||
|
|
||
|
poh = pohAllocate(ppdev, NULL, wWidth, wHeight, FLOH_MAKE_PERMANENT);
|
||
|
if (poh != NULL)
|
||
|
{
|
||
|
fpVidMem = (poh->y * ppdev->lDelta)
|
||
|
+ (poh->x + ppdev->ulYDstOrg) * ppdev->cjPelSize;
|
||
|
|
||
|
// Flip surfaces, detected by surface requests that are
|
||
|
// the same size as the current display, have special
|
||
|
// considerations on the Millennium: they must live entirely
|
||
|
// in the first two megabytes of video memory:
|
||
|
|
||
|
if ((wWidth != ppdev->cxScreen) ||
|
||
|
(wHeight != ppdev->cyScreen) ||
|
||
|
((fpVidMem + (wHeight * ppdev->lDelta)) <= 0x200000))
|
||
|
{
|
||
|
lpSurface->dwReserved1 = (ULONG_PTR)poh;
|
||
|
lpSurface->xHint = poh->x;
|
||
|
lpSurface->yHint = poh->y;
|
||
|
lpSurface->fpVidMem = fpVidMem;
|
||
|
lpSurface->lPitch = ppdev->lDelta;
|
||
|
|
||
|
lpSurfaceDesc->lPitch = ppdev->lDelta;
|
||
|
lpSurfaceDesc->dwFlags |= DDSD_PITCH;
|
||
|
|
||
|
// We handled the creation entirely ourselves, so we have to
|
||
|
// set the return code and return DDHAL_DRIVER_HANDLED:
|
||
|
|
||
|
lpCreateSurface->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// Argh, it's a possible flip surface that we can't use:
|
||
|
|
||
|
pohFree(ppdev, poh);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Fail the call by not setting lpSurface->fpVidMem and returning
|
||
|
// DDHAL_DRIVER_NOTHANDLED:
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdDestroySurface
|
||
|
*
|
||
|
* Note that if DirectDraw did the allocation, DDHAL_DRIVER_NOTHANDLED
|
||
|
* should be returned.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdDestroySurface(
|
||
|
PDD_DESTROYSURFACEDATA lpDestroySurface)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
DD_SURFACE_GLOBAL* lpSurface;
|
||
|
LONG lPitch;
|
||
|
|
||
|
ppdev = (PDEV*) lpDestroySurface->lpDD->dhpdev;
|
||
|
lpSurface = lpDestroySurface->lpDDSurface->lpGbl;
|
||
|
|
||
|
if (!(lpSurface->ddpfSurface.dwFlags & DDPF_FOURCC))
|
||
|
{
|
||
|
pohFree(ppdev, (OH*) lpSurface->dwReserved1);
|
||
|
|
||
|
// Since we did the original allocation ourselves, we have to
|
||
|
// return DDHAL_DRIVER_HANDLED here:
|
||
|
|
||
|
lpDestroySurface->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* GetAvailDriverMemory
|
||
|
*
|
||
|
* DDraw 'miscellaneous' callback returning the amount of free memory in driver's
|
||
|
* 'private' heap
|
||
|
***************************************************************************************/
|
||
|
|
||
|
DWORD __stdcall GetAvailDriverMemory (PDD_GETAVAILDRIVERMEMORYDATA pDmd)
|
||
|
{
|
||
|
OH *poh;
|
||
|
OH *pohSentinel;
|
||
|
LONG lArea;
|
||
|
PDEV *ppdev;
|
||
|
ppdev = (PDEV*)(pDmd->lpDD->dhpdev);
|
||
|
ASSERTDD(ppdev != NULL,"Bad ppdev in GetAvailDriverMemory");
|
||
|
pohSentinel = &ppdev->heap.ohFree;
|
||
|
lArea = 0;
|
||
|
for (poh = pohSentinel->pohNext; poh != pohSentinel; poh = poh->pohNext)
|
||
|
{
|
||
|
ASSERTDD(poh->ohState != OH_PERMANENT,
|
||
|
"Permanent node in free or discardable list");
|
||
|
lArea += poh->cx * poh->cy;
|
||
|
}
|
||
|
pDmd->dwTotal = ppdev->ulTotalAvailVideoMemory;
|
||
|
pDmd->dwFree = lArea * ppdev->cjPelSize;
|
||
|
pDmd->ddRVal = DD_OK;
|
||
|
return DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD __stdcall DdGetDriverInfo
|
||
|
*
|
||
|
* DESCRIPTION: DirectDraw has had many compatability problems
|
||
|
* in the past, particularly from adding or modifying
|
||
|
* members of public structures. GetDriverInfo is an extension
|
||
|
* architecture that intends to allow DirectDraw to
|
||
|
* continue evolving, while maintaining backward compatability.
|
||
|
* This function is passed a GUID which represents some DirectDraw
|
||
|
* extension. If the driver recognises and supports this extension,
|
||
|
* it fills out the required data and returns.
|
||
|
*
|
||
|
* Callback that registers additional DDraw callbacks
|
||
|
* in this case used to register GetAvailDriverMemory callback
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD __stdcall DdGetDriverInfo(DD_GETDRIVERINFODATA *lpInput)
|
||
|
{
|
||
|
DWORD dwSize = 0;
|
||
|
lpInput->ddRVal = DDERR_CURRENTLYNOTAVAIL;
|
||
|
if ( IsEqualIID(&lpInput->guidInfo, &GUID_MiscellaneousCallbacks) )
|
||
|
{
|
||
|
DD_MISCELLANEOUSCALLBACKS MiscellaneousCallbacks;
|
||
|
memset(&MiscellaneousCallbacks, 0, sizeof(MiscellaneousCallbacks));
|
||
|
DISPDBG((0,"Get Miscelaneous Callbacks"));
|
||
|
dwSize = min(lpInput->dwExpectedSize, sizeof(DD_MISCELLANEOUSCALLBACKS));
|
||
|
MiscellaneousCallbacks.dwSize = dwSize;
|
||
|
MiscellaneousCallbacks.dwFlags = DDHAL_MISCCB32_GETAVAILDRIVERMEMORY | 0;
|
||
|
MiscellaneousCallbacks.GetAvailDriverMemory = GetAvailDriverMemory;
|
||
|
memcpy(lpInput->lpvData, &MiscellaneousCallbacks, dwSize);
|
||
|
lpInput->ddRVal = DD_OK;
|
||
|
}
|
||
|
return DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* BOOL DrvGetDirectDrawInfo
|
||
|
*
|
||
|
* Will be called twice 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;
|
||
|
|
||
|
ppdev = (PDEV*) dhpdev;
|
||
|
|
||
|
*pdwNumFourCC = 0;
|
||
|
*pdwNumHeaps = 0;
|
||
|
|
||
|
// We may not support DirectDraw on this card:
|
||
|
|
||
|
if (!(ppdev->flStatus & STAT_DIRECTDRAW))
|
||
|
return(FALSE);
|
||
|
|
||
|
pHalInfo->dwSize = sizeof(*pHalInfo);
|
||
|
|
||
|
// Current primary surface attributes:
|
||
|
|
||
|
pHalInfo->vmiData.pvPrimary = ppdev->pjScreen;
|
||
|
pHalInfo->vmiData.fpPrimary = ppdev->ulYDstOrg * ppdev->cjPelSize;
|
||
|
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;
|
||
|
pHalInfo->vmiData.ddpfDisplay.dwRGBBitCount = ppdev->cjHwPel * 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;
|
||
|
|
||
|
// Free up as much off-screen memory as possible:
|
||
|
|
||
|
bMoveAllDfbsFromOffscreenToDibs(ppdev);
|
||
|
|
||
|
// Capabilities supported:
|
||
|
|
||
|
pHalInfo->ddCaps.dwCaps = DDCAPS_BLT
|
||
|
| DDCAPS_BLTCOLORFILL
|
||
|
| DDCAPS_READSCANLINE;
|
||
|
|
||
|
pHalInfo->ddCaps.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN
|
||
|
| DDSCAPS_PRIMARYSURFACE
|
||
|
| DDSCAPS_FLIP;
|
||
|
|
||
|
// We have to tell DirectDraw our preferred off-screen alignment, even
|
||
|
// if we're doing our own off-screen memory management:
|
||
|
|
||
|
pHalInfo->vmiData.dwOffscreenAlign = 4;
|
||
|
|
||
|
// Since we do our own memory allocation, we have to set dwVidMemTotal
|
||
|
// ourselves. Note that this represents the amount of available off-
|
||
|
// screen memory, not all of video memory:
|
||
|
|
||
|
pHalInfo->ddCaps.dwVidMemTotal
|
||
|
= ppdev->heap.cxMax * ppdev->heap.cyMax * ppdev->cjPelSize;
|
||
|
|
||
|
// We can do YUV conversions and hardware accelerated stretches at
|
||
|
// all RGB modes except 24bpp.
|
||
|
|
||
|
if ((ppdev->iBitmapFormat != BMF_24BPP) &&
|
||
|
(ppdev->iBitmapFormat != BMF_8BPP))
|
||
|
{
|
||
|
pHalInfo->ddCaps.dwCaps |= DDCAPS_BLTSTRETCH
|
||
|
| DDCAPS_BLTFOURCC;
|
||
|
|
||
|
pHalInfo->ddCaps.dwFXCaps |= DDFXCAPS_BLTSTRETCHX
|
||
|
| DDFXCAPS_BLTSTRETCHY;
|
||
|
|
||
|
// The Millennium supports only one type of YUV format:
|
||
|
|
||
|
*pdwNumFourCC = 1;
|
||
|
if (pdwFourCC)
|
||
|
{
|
||
|
*pdwFourCC = FOURCC_YUY2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Tell DDraw that we support additional callbacks through DdGetDriverInfo
|
||
|
pHalInfo->GetDriverInfo = DdGetDriverInfo;
|
||
|
pHalInfo->dwFlags |= DDHALINFO_GETDRIVERINFOSET;
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
/**************************************************************************\
|
||
|
* ULONG TotalAvailVideoMemory
|
||
|
*
|
||
|
* Added for GetAvailVideoMemoty calback
|
||
|
* Calculate total amount of offscreen video memory without permanent
|
||
|
* driver allocations. We need to do it here since we won't be able
|
||
|
* to distinguish between driver's permanent allocation and ddraw's
|
||
|
* permanent allocation later.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
ULONG TotalAvailVideoMemory(PDEV *ppdev)
|
||
|
{
|
||
|
OH *poh;
|
||
|
OH *pohSentinel;
|
||
|
ULONG ulArea;
|
||
|
ULONG i;
|
||
|
|
||
|
ASSERTDD(ppdev != NULL,"Bad ppdev TotalAvailVideoMemory");
|
||
|
|
||
|
ulArea = 0;
|
||
|
pohSentinel = &ppdev->heap.ohFree;
|
||
|
|
||
|
for (i = 2; i != 0; i--)
|
||
|
{
|
||
|
for (poh = pohSentinel->pohNext; poh != pohSentinel; poh = poh->pohNext)
|
||
|
{
|
||
|
ASSERTDD(poh->ohState != OH_PERMANENT,
|
||
|
"Permanent node in free or discardable list");
|
||
|
ulArea += poh->cx * poh->cy;
|
||
|
}
|
||
|
// Second time through, loop through the list of discardable
|
||
|
// rectangles:
|
||
|
pohSentinel = &ppdev->heap.ohDiscardable;
|
||
|
}
|
||
|
return ulArea * ppdev->cjPelSize;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* BOOL DrvEnableDirectDraw
|
||
|
*
|
||
|
* This function is called by GDI to enable DirectDraw when a DirectDraw
|
||
|
* program is started and DirectDraw is not already active.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
BOOL DrvEnableDirectDraw(
|
||
|
DHPDEV dhpdev,
|
||
|
DD_CALLBACKS* pCallBacks,
|
||
|
DD_SURFACECALLBACKS* pSurfaceCallBacks,
|
||
|
DD_PALETTECALLBACKS* pPaletteCallBacks)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
|
||
|
ppdev = (PDEV*) dhpdev;
|
||
|
|
||
|
pCallBacks->WaitForVerticalBlank = DdWaitForVerticalBlank;
|
||
|
pCallBacks->MapMemory = DdMapMemory;
|
||
|
pCallBacks->CanCreateSurface = DdCanCreateSurface;
|
||
|
pCallBacks->CreateSurface = DdCreateSurface;
|
||
|
pCallBacks->GetScanLine = DdGetScanLine;
|
||
|
pCallBacks->dwFlags = DDHAL_CB32_WAITFORVERTICALBLANK
|
||
|
| DDHAL_CB32_MAPMEMORY
|
||
|
| DDHAL_CB32_CANCREATESURFACE
|
||
|
| DDHAL_CB32_CREATESURFACE
|
||
|
| DDHAL_CB32_GETSCANLINE;
|
||
|
|
||
|
pSurfaceCallBacks->Blt = DdBlt;
|
||
|
pSurfaceCallBacks->Flip = DdFlip;
|
||
|
pSurfaceCallBacks->Lock = DdLock;
|
||
|
pSurfaceCallBacks->GetBltStatus = DdGetBltStatus;
|
||
|
pSurfaceCallBacks->GetFlipStatus = DdGetFlipStatus;
|
||
|
pSurfaceCallBacks->DestroySurface = DdDestroySurface;
|
||
|
pSurfaceCallBacks->dwFlags = DDHAL_SURFCB32_BLT
|
||
|
| DDHAL_SURFCB32_FLIP
|
||
|
| DDHAL_SURFCB32_LOCK
|
||
|
| DDHAL_SURFCB32_GETBLTSTATUS
|
||
|
| DDHAL_SURFCB32_GETFLIPSTATUS
|
||
|
| DDHAL_SURFCB32_DESTROYSURFACE;
|
||
|
|
||
|
// 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.
|
||
|
|
||
|
|
||
|
// Added for GetAvailDriverMemory callback
|
||
|
ppdev->ulTotalAvailVideoMemory = TotalAvailVideoMemory(ppdev);
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID DrvDisableDirectDraw
|
||
|
*
|
||
|
* This function is called by GDI when the last active DirectDraw program
|
||
|
* is quit and DirectDraw will no longer be active.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID DrvDisableDirectDraw(
|
||
|
DHPDEV dhpdev)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vAssertModeDirectDraw
|
||
|
*
|
||
|
* This function is called by enable.c when entering or leaving the
|
||
|
* DOS full-screen character mode.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vAssertModeDirectDraw(
|
||
|
PDEV* ppdev,
|
||
|
BOOL bEnabled)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* BOOL bEnableDirectDraw
|
||
|
*
|
||
|
* This function is called by enable.c when the mode is first initialized,
|
||
|
* right after the miniport does the mode-set.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
BOOL bEnableDirectDraw(
|
||
|
PDEV* ppdev)
|
||
|
{
|
||
|
// We're not going to bother to support accelerated DirectDraw on
|
||
|
// the Impression or earlier, because they don't have linear frame
|
||
|
// buffers.
|
||
|
|
||
|
if (ppdev->ulBoardId == MGA_STORM)
|
||
|
{
|
||
|
// Accurately measure the refresh rate for later:
|
||
|
|
||
|
vGetDisplayDuration(ppdev);
|
||
|
|
||
|
// DirectDraw is all set to be used on this card:
|
||
|
|
||
|
ppdev->flStatus |= STAT_DIRECTDRAW;
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vDisableDirectDraw
|
||
|
*
|
||
|
* This function is called by enable.c when the driver is shutting down.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vDisableDirectDraw(
|
||
|
PDEV* ppdev)
|
||
|
{
|
||
|
}
|