2267 lines
75 KiB
C
2267 lines
75 KiB
C
|
/******************************Module*Header*******************************\
|
||
|
*
|
||
|
* **************************
|
||
|
* * DirectDraw SAMPLE CODE *
|
||
|
* **************************
|
||
|
*
|
||
|
* Module Name: ddraw.c
|
||
|
*
|
||
|
* Implements all the DirectDraw components for the driver.
|
||
|
*
|
||
|
* Copyright (c) 1995-1998 Microsoft Corporation
|
||
|
\**************************************************************************/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
// Defines we'll use in the surface's 'dwReserved1' field:
|
||
|
|
||
|
#define DD_RESERVED_DIFFERENTPIXELFORMAT 0x0001
|
||
|
|
||
|
// Worst-case possible number of FIFO entries we'll have to wait for in
|
||
|
// DdBlt for any operation:
|
||
|
|
||
|
#define DDBLT_FIFO_COUNT 9
|
||
|
|
||
|
// 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 vFixMissingPixels
|
||
|
*
|
||
|
* Trio64V+ work-around.
|
||
|
*
|
||
|
* On 1024x768x8 and 800x600x8 modes, switching from K2 to stream processor
|
||
|
* results in 1 character clock pixels on the right handed side of the screen
|
||
|
* missing. This problem can be worked-around by adjusting CR2 register.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vFixMissingPixels(
|
||
|
PDEV* ppdev)
|
||
|
{
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE jVerticalRetraceEnd;
|
||
|
|
||
|
ASSERTDD(ppdev->flCaps & CAPS_STREAMS_CAPABLE, "Must be streams capable");
|
||
|
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
// Unlock CRTC control registers:
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x11);
|
||
|
jVerticalRetraceEnd = INP(pjIoBase, CRTC_DATA);
|
||
|
OUTP(pjIoBase, CRTC_DATA, jVerticalRetraceEnd & 0x7f);
|
||
|
|
||
|
// Add one character clock:
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x2);
|
||
|
ppdev->jSavedCR2 = INP(pjIoBase, CRTC_DATA);
|
||
|
OUTP(pjIoBase, CRTC_DATA, ppdev->jSavedCR2 + 1);
|
||
|
|
||
|
// Lock CRTC control registers again:
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x11);
|
||
|
OUTP(pjIoBase, CRTC_DATA, jVerticalRetraceEnd | 0x80);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vUnfixMissingPixels
|
||
|
*
|
||
|
* Trio64V+ work-around.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vUnfixMissingPixels(
|
||
|
PDEV* ppdev)
|
||
|
{
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE jVerticalRetraceEnd;
|
||
|
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
// Unlock CRTC control registers:
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x11);
|
||
|
jVerticalRetraceEnd = INP(pjIoBase, CRTC_DATA);
|
||
|
OUTP(pjIoBase, CRTC_DATA, jVerticalRetraceEnd & 0x7f);
|
||
|
|
||
|
// Restore original register value:
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x2);
|
||
|
OUTP(pjIoBase, CRTC_DATA, ppdev->jSavedCR2);
|
||
|
|
||
|
// Lock CRTC control registers again:
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x11);
|
||
|
OUTP(pjIoBase, CRTC_DATA, jVerticalRetraceEnd | 0x80);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vStreamsDelay()
|
||
|
*
|
||
|
* This tries to work around a hardware timing bug. Supposedly, consecutive
|
||
|
* writes to the streams processor in fast CPUs such as P120 and P133's
|
||
|
* have problems. I haven't seen this problem, but this work-around exists
|
||
|
* in the Windows 95 driver, and at this point don't want to chance not
|
||
|
* having it. Note that writes to the streams processor are not performance
|
||
|
* critical, so this is not a performance hit.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vStreamsDelay()
|
||
|
{
|
||
|
volatile LONG i;
|
||
|
|
||
|
for (i = 32; i != 0; i--)
|
||
|
;
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vTurnOnStreamsProcessorMode
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vTurnOnStreamsProcessorMode(
|
||
|
PDEV* ppdev)
|
||
|
{
|
||
|
BYTE* pjMmBase;
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE jStreamsProcessorModeSelect;
|
||
|
DWORD dwPFormat;
|
||
|
|
||
|
ASSERTDD(ppdev->flCaps & CAPS_STREAMS_CAPABLE, "Must be streams capable");
|
||
|
|
||
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
NW_GP_WAIT(ppdev, pjMmBase);
|
||
|
|
||
|
while (!(VBLANK_IS_ACTIVE(pjIoBase)))
|
||
|
;
|
||
|
|
||
|
// Full streams processor operation:
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x67);
|
||
|
jStreamsProcessorModeSelect = INP(pjIoBase, CRTC_DATA);
|
||
|
OUTP(pjIoBase, CRTC_DATA, jStreamsProcessorModeSelect | 0x0c);
|
||
|
|
||
|
if (ppdev->iBitmapFormat == BMF_8BPP)
|
||
|
{
|
||
|
vFixMissingPixels(ppdev);
|
||
|
}
|
||
|
|
||
|
switch(ppdev->iBitmapFormat)
|
||
|
{
|
||
|
case BMF_8BPP:
|
||
|
dwPFormat = P_RGB8;
|
||
|
break;
|
||
|
|
||
|
case BMF_16BPP:
|
||
|
if (IS_RGB15_R(ppdev->flRed))
|
||
|
dwPFormat = P_RGB15;
|
||
|
else
|
||
|
dwPFormat = P_RGB16;
|
||
|
break;
|
||
|
|
||
|
case BMF_32BPP:
|
||
|
dwPFormat = P_RGB32;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
RIP("Unexpected bitmap format");
|
||
|
}
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, P_CONTROL, dwPFormat );
|
||
|
WRITE_STREAM_D(pjMmBase, FIFO_CONTROL, ((0xcL << FifoAlloc_Shift)|
|
||
|
(4L << P_FifoThresh_Shift) |
|
||
|
(4L << S_FifoThresh_Shift)));
|
||
|
WRITE_STREAM_D(pjMmBase, P_0, 0);
|
||
|
WRITE_STREAM_D(pjMmBase, P_STRIDE, ppdev->lDelta);
|
||
|
WRITE_STREAM_D(pjMmBase, P_XY, 0x010001L);
|
||
|
WRITE_STREAM_D(pjMmBase, P_WH, WH(ppdev->cxScreen, ppdev->cyScreen));
|
||
|
WRITE_STREAM_D(pjMmBase, S_WH, WH(10, 2));
|
||
|
WRITE_STREAM_D(pjMmBase, CKEY_LOW, ppdev->ulColorKey |
|
||
|
CompareBits0t7 |
|
||
|
KeyFromCompare);
|
||
|
WRITE_STREAM_D(pjMmBase, CKEY_HI, ppdev->ulColorKey);
|
||
|
WRITE_STREAM_D(pjMmBase, BLEND_CONTROL, POnS);
|
||
|
WRITE_STREAM_D(pjMmBase, OPAQUE_CONTROL, 0);
|
||
|
WRITE_STREAM_D(pjMmBase, FIFO_CONTROL, ppdev->ulFifoValue);
|
||
|
|
||
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vTurnOffStreamsProcessorMode
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vTurnOffStreamsProcessorMode(
|
||
|
PDEV* ppdev)
|
||
|
{
|
||
|
BYTE* pjMmBase;
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE jStreamsProcessorModeSelect;
|
||
|
|
||
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
NW_GP_WAIT(ppdev, pjMmBase);
|
||
|
|
||
|
while (!(VBLANK_IS_ACTIVE(pjIoBase)))
|
||
|
;
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, FIFO_CONTROL, 0x3000L);
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x67);
|
||
|
jStreamsProcessorModeSelect = INP(pjIoBase, CRTC_DATA);
|
||
|
OUTP(pjIoBase, CRTC_DATA, jStreamsProcessorModeSelect & ~0x0C);
|
||
|
|
||
|
if (ppdev->iBitmapFormat == BMF_8BPP)
|
||
|
{
|
||
|
vUnfixMissingPixels(ppdev);
|
||
|
}
|
||
|
|
||
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD dwGetPaletteEntry
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD dwGetPaletteEntry(
|
||
|
PDEV* ppdev,
|
||
|
DWORD iIndex)
|
||
|
{
|
||
|
BYTE* pjIoBase;
|
||
|
DWORD dwRed;
|
||
|
DWORD dwGreen;
|
||
|
DWORD dwBlue;
|
||
|
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
OUTP(pjIoBase, 0x3c7, iIndex);
|
||
|
|
||
|
dwRed = INP(pjIoBase, 0x3c9) << 2;
|
||
|
dwGreen = INP(pjIoBase, 0x3c9) << 2;
|
||
|
dwBlue = INP(pjIoBase, 0x3c9) << 2;
|
||
|
|
||
|
return((dwRed << 16) | (dwGreen << 8) | (dwBlue));
|
||
|
}
|
||
|
|
||
|
/******************************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* pjIoBase;
|
||
|
LONG i;
|
||
|
LONG j;
|
||
|
LONGLONG li;
|
||
|
LONGLONG liFrequency;
|
||
|
LONGLONG liMin;
|
||
|
LONGLONG aliMeasurement[NUM_MEASUREMENTS_TO_TAKE + 1];
|
||
|
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
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.
|
||
|
//
|
||
|
// Skip a couple of vertical blanks to allow the hardware to settle
|
||
|
// down after the mode change, to make our readings accurate:
|
||
|
|
||
|
for (i = 2; i != 0; i--)
|
||
|
{
|
||
|
while (VBLANK_IS_ACTIVE(pjIoBase))
|
||
|
;
|
||
|
while (!(VBLANK_IS_ACTIVE(pjIoBase)))
|
||
|
;
|
||
|
}
|
||
|
|
||
|
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(pjIoBase)))
|
||
|
;
|
||
|
|
||
|
for (j = 0; j < NUM_VBLANKS_TO_MEASURE; j++)
|
||
|
{
|
||
|
while (VBLANK_IS_ACTIVE(pjIoBase))
|
||
|
;
|
||
|
while (!(VBLANK_IS_ACTIVE(pjIoBase)))
|
||
|
;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
ppdev->flipRecord.bFlipFlag = FALSE;
|
||
|
ppdev->flipRecord.fpFlipFrom = 0;
|
||
|
|
||
|
// We need the refresh rate in Hz to query the S3 miniport about the
|
||
|
// streams parameters:
|
||
|
|
||
|
EngQueryPerformanceFrequency(&liFrequency);
|
||
|
|
||
|
ppdev->ulRefreshRate
|
||
|
= (ULONG) ((liFrequency + (ppdev->flipRecord.liFlipDuration / 2))
|
||
|
/ ppdev->flipRecord.liFlipDuration);
|
||
|
|
||
|
DISPDBG((1, "Frequency: %li Hz", ppdev->ulRefreshRate));
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* HRESULT ddrvalUpdateFlipStatus
|
||
|
*
|
||
|
* Checks and sees if the most recent flip has occurred.
|
||
|
*
|
||
|
* Unfortunately, the hardware has no ability to tell us whether a vertical
|
||
|
* retrace has occured since the flip command was given other than by
|
||
|
* sampling the vertical-blank-active and display-active status bits.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
HRESULT ddrvalUpdateFlipStatus(
|
||
|
PDEV* ppdev,
|
||
|
FLATPTR fpVidMem)
|
||
|
{
|
||
|
BYTE* pjIoBase;
|
||
|
LONGLONG liTime;
|
||
|
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
if ((ppdev->flipRecord.bFlipFlag) &&
|
||
|
((fpVidMem == (FLATPTR) -1) ||
|
||
|
(fpVidMem == ppdev->flipRecord.fpFlipFrom)))
|
||
|
{
|
||
|
if (VBLANK_IS_ACTIVE(pjIoBase))
|
||
|
{
|
||
|
if (ppdev->flipRecord.bWasEverInDisplay)
|
||
|
{
|
||
|
ppdev->flipRecord.bHaveEverCrossedVBlank = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else if (DISPLAY_IS_ACTIVE(pjIoBase))
|
||
|
{
|
||
|
if (ppdev->flipRecord.bHaveEverCrossedVBlank)
|
||
|
{
|
||
|
ppdev->flipRecord.bFlipFlag = FALSE;
|
||
|
return(DD_OK);
|
||
|
}
|
||
|
ppdev->flipRecord.bWasEverInDisplay = TRUE;
|
||
|
}
|
||
|
|
||
|
// It's pretty unlikely that we'll happen to sample the vertical-
|
||
|
// blank-active at the first vertical blank after the flip command
|
||
|
// has been given. So to provide better results, we also check the
|
||
|
// time elapsed since the flip. If it's 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;
|
||
|
BYTE* pjMmBase;
|
||
|
HRESULT ddrval;
|
||
|
DWORD dstX;
|
||
|
DWORD dstY;
|
||
|
DWORD dwFlags;
|
||
|
DWORD dstWidth;
|
||
|
DWORD dstHeight;
|
||
|
DWORD srcWidth;
|
||
|
DWORD srcHeight;
|
||
|
DWORD dwError;
|
||
|
LONG dstPitch;
|
||
|
LONG srcPitch;
|
||
|
DWORD srcX;
|
||
|
DWORD srcY;
|
||
|
ULONG ulBltCmd;
|
||
|
DWORD dwVEctrl;
|
||
|
DWORD dwVEdda;
|
||
|
DWORD dwVEcrop;
|
||
|
DWORD dwVEdstAddr;
|
||
|
DWORD dwVEsrcAddr;
|
||
|
DWORD dwDstByteCount;
|
||
|
DWORD dwSrcByteCount;
|
||
|
DWORD dwSrcBytes;
|
||
|
DWORD dwCropSkip;
|
||
|
LONG i;
|
||
|
FLATPTR fp;
|
||
|
|
||
|
ppdev = (PDEV*) lpBlt->lpDD->dhpdev;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
|
||
|
dstSurfx = lpBlt->lpDDDestSurface;
|
||
|
dstSurf = dstSurfx->lpGbl;
|
||
|
|
||
|
// 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:
|
||
|
|
||
|
if (MM_FIFO_BUSY(ppdev, pjMmBase, DDBLT_FIFO_COUNT))
|
||
|
{
|
||
|
lpBlt->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Copy src/dst rects:
|
||
|
|
||
|
dstX = lpBlt->rDest.left;
|
||
|
dstY = lpBlt->rDest.top;
|
||
|
dstWidth = lpBlt->rDest.right - lpBlt->rDest.left;
|
||
|
dstHeight = lpBlt->rDest.bottom - lpBlt->rDest.top;
|
||
|
|
||
|
if (dwFlags & DDBLT_COLORFILL)
|
||
|
{
|
||
|
// The S3 can't easily do colour fills for off-screen surfaces that
|
||
|
// are a different pixel format than that of the primary display:
|
||
|
|
||
|
if (dstSurf->dwReserved1 & DD_RESERVED_DIFFERENTPIXELFORMAT)
|
||
|
{
|
||
|
DISPDBG((0, "Can't do colorfill to odd pixel format"));
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
convertToGlobalCord(dstX, dstY, dstSurf);
|
||
|
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 6);
|
||
|
|
||
|
NW_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
|
||
|
NW_ALT_MIX(ppdev, pjMmBase, FOREGROUND_COLOR | OVERPAINT, 0);
|
||
|
NW_FRGD_COLOR(ppdev, pjMmBase, lpBlt->bltFX.dwFillColor);
|
||
|
NW_ABS_CURXY_FAST(ppdev, pjMmBase, dstX, dstY);
|
||
|
NW_ALT_PCNT(ppdev, pjMmBase, dstWidth - 1, dstHeight - 1);
|
||
|
NW_ALT_CMD(ppdev, pjMmBase, RECTANGLE_FILL | DRAWING_DIR_TBLRXM |
|
||
|
DRAW | DIR_TYPE_XY |
|
||
|
LAST_PIXEL_ON | MULTIPLE_PIXELS |
|
||
|
WRITE);
|
||
|
|
||
|
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 offset, dstWidth, and dstHeight for source:
|
||
|
|
||
|
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;
|
||
|
|
||
|
// If a stretch or a funky pixel format blt are involved, we'll have to
|
||
|
// defer to the overlay or pixel formatter routines:
|
||
|
|
||
|
if ((srcWidth == dstWidth) &&
|
||
|
(srcHeight == dstHeight) &&
|
||
|
!(srcSurf->dwReserved1 & DD_RESERVED_DIFFERENTPIXELFORMAT) &&
|
||
|
!(dstSurf->dwReserved1 & DD_RESERVED_DIFFERENTPIXELFORMAT))
|
||
|
{
|
||
|
// Assume we can do the blt top-to-bottom, left-to-right:
|
||
|
|
||
|
ulBltCmd = BITBLT | DRAW | DIR_TYPE_XY | WRITE | DRAWING_DIR_TBLRXM;
|
||
|
|
||
|
if ((dstSurf == srcSurf) && (srcX + dstWidth > dstX) &&
|
||
|
(srcY + dstHeight > dstY) && (dstX + dstWidth > srcX) &&
|
||
|
(dstY + dstHeight > srcY) &&
|
||
|
(((srcY == dstY) && (dstX > srcX) )
|
||
|
|| ((srcY != dstY) && (dstY > srcY))))
|
||
|
{
|
||
|
// Okay, we have to do the blt bottom-to-top, right-to-left:
|
||
|
|
||
|
ulBltCmd = BITBLT | DRAW | DIR_TYPE_XY | WRITE | DRAWING_DIR_BTRLXM;
|
||
|
srcX = lpBlt->rSrc.right - 1;
|
||
|
srcY = lpBlt->rSrc.bottom - 1;
|
||
|
dstX = lpBlt->rDest.right - 1;
|
||
|
dstY = 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(dstX, dstY, dstSurf);
|
||
|
|
||
|
if (dwFlags & DDBLT_KEYSRCOVERRIDE)
|
||
|
{
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 9);
|
||
|
|
||
|
NW_MULT_MISC_READ_SEL(ppdev, pjMmBase, ppdev->ulMiscState
|
||
|
| MULT_MISC_COLOR_COMPARE, 0);
|
||
|
NW_COLOR_CMP(ppdev, pjMmBase,
|
||
|
lpBlt->bltFX.ddckSrcColorkey.dwColorSpaceLowValue);
|
||
|
NW_ALT_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | OVERPAINT, 0);
|
||
|
NW_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
|
||
|
NW_ABS_CURXY_FAST(ppdev, pjMmBase, srcX, srcY);
|
||
|
NW_ABS_DESTXY_FAST(ppdev, pjMmBase, dstX, dstY);
|
||
|
NW_ALT_PCNT(ppdev, pjMmBase, dstWidth - 1, dstHeight - 1);
|
||
|
NW_ALT_CMD(ppdev, pjMmBase, ulBltCmd);
|
||
|
NW_MULT_MISC_READ_SEL(ppdev, pjMmBase, ppdev->ulMiscState, 0);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 6);
|
||
|
|
||
|
NW_ALT_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | OVERPAINT, 0);
|
||
|
NW_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
|
||
|
NW_ABS_CURXY_FAST(ppdev, pjMmBase, srcX, srcY);
|
||
|
NW_ABS_DESTXY_FAST(ppdev, pjMmBase, dstX, dstY);
|
||
|
NW_ALT_PCNT(ppdev, pjMmBase, dstWidth - 1, dstHeight - 1);
|
||
|
NW_ALT_CMD(ppdev, pjMmBase, ulBltCmd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// Pixel Formatter Blts
|
||
|
//
|
||
|
// We can do stretches or funky pixel format blts only if a pixel
|
||
|
// formatter is present. Plus, we set our 'ddCaps' such that we
|
||
|
// shouldn't have to handle any shrinks.
|
||
|
//
|
||
|
// (We check to make sure we weren't asked to do a shrink, because we
|
||
|
// would probably hang if the application ignored what we told them
|
||
|
// and asked for a shrink):
|
||
|
|
||
|
else if ((ppdev->flCaps & CAPS_PIXEL_FORMATTER) &&
|
||
|
(srcWidth <= dstWidth) &&
|
||
|
(srcHeight <= dstHeight))
|
||
|
{
|
||
|
if ((dwFlags & DDBLT_KEYSRCOVERRIDE) ||
|
||
|
(dstWidth >= 4 * srcWidth))
|
||
|
{
|
||
|
// Contrary to what we're indicating in our capabilities, we
|
||
|
// can't colour key on stretches or pixel format conversions.
|
||
|
// The S3 hardware also can't do stretches of four times or
|
||
|
// more.
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
dwVEctrl = ~dstWidth & 0x00000FFF; // Initial accumulator
|
||
|
|
||
|
dwVEdda = 0x10000000 // Some reserved bit?
|
||
|
| (STRETCH | SCREEN) // Scale from video memory
|
||
|
| (srcWidth << 16) // K1
|
||
|
| ((srcWidth - dstWidth) & 0x7FF); // K2
|
||
|
|
||
|
// 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.
|
||
|
//
|
||
|
// Note that dwRGBBitCount is overloaded with dwYUVBitCount:
|
||
|
|
||
|
dwSrcByteCount = srcSurf->ddpfSurface.dwRGBBitCount >> 3;
|
||
|
if (srcSurf->ddpfSurface.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
dwVEctrl |= INPUT_YCrCb422 | CSCENABLE; // Not INPUT_YUV422!
|
||
|
}
|
||
|
else if (srcSurf->ddpfSurface.dwFlags & DDPF_RGB)
|
||
|
{
|
||
|
switch (dwSrcByteCount)
|
||
|
{
|
||
|
case 1:
|
||
|
dwVEctrl |= INPUT_RGB8;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
if (IS_RGB15_R(srcSurf->ddpfSurface.dwRBitMask))
|
||
|
dwVEctrl |= INPUT_RGB15;
|
||
|
else
|
||
|
dwVEctrl |= INPUT_RGB16;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
dwVEctrl |= INPUT_RGB32;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dwDstByteCount = dstSurf->ddpfSurface.dwRGBBitCount >> 3;
|
||
|
switch (dwDstByteCount)
|
||
|
{
|
||
|
case 1:
|
||
|
dwVEctrl |= OUTPUT_RGB8;
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
if (IS_RGB15_R(dstSurf->ddpfSurface.dwRBitMask))
|
||
|
dwVEctrl |= OUTPUT_RGB15;
|
||
|
else
|
||
|
dwVEctrl |= OUTPUT_RGB16;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
dwVEctrl |=OUTPUT_RGB32;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (dwDstByteCount > 1)
|
||
|
{
|
||
|
dwVEctrl |= FILTERENABLE;
|
||
|
|
||
|
if (dstWidth > 2 * srcWidth)
|
||
|
dwVEdda |= LINEAR12221; // linear, 1-2-2-2-1, >2X stretch
|
||
|
|
||
|
else if (dstWidth > srcWidth)
|
||
|
dwVEdda |= LINEAR02420; // linear, 0-2-4-2-0, 1-2X stretch
|
||
|
|
||
|
else
|
||
|
dwVEdda |= BILINEAR; // bi-linear, <1X stretch
|
||
|
}
|
||
|
|
||
|
dwVEsrcAddr = (DWORD)(srcSurf->fpVidMem + (srcY * srcSurf->lPitch)
|
||
|
+ (srcX * dwSrcByteCount));
|
||
|
dwVEdstAddr = (DWORD)(dstSurf->fpVidMem + (dstY * dstSurf->lPitch)
|
||
|
+ (dstX * dwDstByteCount));
|
||
|
|
||
|
srcPitch = srcSurf->lPitch;
|
||
|
dstPitch = dstSurf->lPitch;
|
||
|
|
||
|
// The S3's source alignment within the dword must be done using the
|
||
|
// crop register:
|
||
|
|
||
|
dwVEcrop = dstWidth;
|
||
|
|
||
|
if (dwVEsrcAddr & 3)
|
||
|
{
|
||
|
dwSrcBytes = (srcWidth * dwSrcByteCount);
|
||
|
|
||
|
// Transform the number of source pixels to the number of
|
||
|
// corresponding destination pixels, and round the result:
|
||
|
|
||
|
dwCropSkip = ((dwVEsrcAddr & 3) * dstWidth + (dwSrcBytes >> 1))
|
||
|
/ dwSrcBytes;
|
||
|
|
||
|
dwVEcrop += (dwCropSkip << 16);
|
||
|
|
||
|
dwVEsrcAddr &= ~3;
|
||
|
}
|
||
|
|
||
|
// We have to run the vertical DDA ourselves:
|
||
|
|
||
|
dwError = srcHeight >> 1;
|
||
|
i = dstHeight;
|
||
|
|
||
|
// Watch out for a hardware bug the destination will be 32 pixels
|
||
|
// or less:
|
||
|
//
|
||
|
// We'll use 40 as our minimum width to guarantee we shouldn't
|
||
|
// crash.
|
||
|
|
||
|
if (dstWidth >= 40)
|
||
|
{
|
||
|
// The S3 will sometimes hang when using the video engine with
|
||
|
// certain end-byte alignments. We'll simply lengthen the blt in
|
||
|
// this case and hope that no-one notices:
|
||
|
|
||
|
if (((dwVEdstAddr + (dstWidth * dwDstByteCount)) & 7) == 4)
|
||
|
{
|
||
|
dwVEcrop++;
|
||
|
}
|
||
|
|
||
|
// We have to execute a graphics engine NOP before using the
|
||
|
// pixel formatter video engine:
|
||
|
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 1);
|
||
|
NW_ALT_CMD(ppdev, pjMmBase, 0);
|
||
|
NW_GP_WAIT(ppdev, pjMmBase);
|
||
|
|
||
|
// Set up some non-variant registers:
|
||
|
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 4);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_CONTROL, dwVEctrl);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_DDA, dwVEdda);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_STEP, ppdev->dwVEstep);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_CROP, dwVEcrop);
|
||
|
|
||
|
do {
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 3);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_SRCADDR, dwVEsrcAddr);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_DSTADDR, dwVEdstAddr);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_NOP, 0);
|
||
|
NW_FORMATTER_WAIT(ppdev, pjMmBase);
|
||
|
|
||
|
dwVEdstAddr += dstPitch;
|
||
|
dwError += srcHeight;
|
||
|
if (dwError >= dstHeight)
|
||
|
{
|
||
|
dwError -= dstHeight;
|
||
|
dwVEsrcAddr += srcPitch;
|
||
|
}
|
||
|
} while (--i != 0);
|
||
|
}
|
||
|
else if (dwDstByteCount != (DWORD) ppdev->cjPelSize)
|
||
|
{
|
||
|
// Because for narrow video engine blts we have to copy the
|
||
|
// result using the normal graphics accelerator on a pixel
|
||
|
// basis, we can't handle funky destination colour depths.
|
||
|
// I expect zero applications to ask for narrow blts that
|
||
|
// hit this case, so we will simply fail the call should it
|
||
|
// ever actually occur:
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// The S3 will hang if we blt less than 32 pixels via the
|
||
|
// pixel formatter. Unfortunately, we can't simply return
|
||
|
// DDHAL_DRIVER_NOTHANDLED for this case. We said we'd do
|
||
|
// hardware stretches, so we have to handle all hardware
|
||
|
// stretches.
|
||
|
//
|
||
|
// We work around the problem by doing a 32 pixel stretch to
|
||
|
// a piece of off-screen memory, then blting the appropriate
|
||
|
// subset to the correct position on the screen.
|
||
|
//
|
||
|
// 32 isn't big enough. We still hang. Lets make it 40.
|
||
|
|
||
|
dwVEcrop = 32 + 8;
|
||
|
|
||
|
convertToGlobalCord(dstX, dstY, dstSurf);
|
||
|
srcX = ppdev->pdsurfVideoEngineScratch->x;
|
||
|
srcY = ppdev->pdsurfVideoEngineScratch->y;
|
||
|
dwVEdstAddr = (srcY * ppdev->lDelta) + (srcX * ppdev->cjPelSize);
|
||
|
|
||
|
ASSERTDD(((dwVEdstAddr + (dwVEcrop * dwDstByteCount)) & 7) != 4,
|
||
|
"Must account for S3 end-alignment bug");
|
||
|
|
||
|
do {
|
||
|
// Use the pixel formatter to blt to our scratch area:
|
||
|
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 1);
|
||
|
NW_ALT_CMD(ppdev, pjMmBase, 0);
|
||
|
NW_GP_WAIT(ppdev, pjMmBase);
|
||
|
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 7);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_CONTROL, dwVEctrl);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_DDA, dwVEdda);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_STEP, ppdev->dwVEstep);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_CROP, dwVEcrop);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_SRCADDR, dwVEsrcAddr);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_DSTADDR, dwVEdstAddr);
|
||
|
WRITE_FORMATTER_D(pjMmBase, PF_NOP, 0);
|
||
|
NW_FORMATTER_WAIT(ppdev, pjMmBase);
|
||
|
|
||
|
dwError += srcHeight;
|
||
|
if (dwError >= dstHeight)
|
||
|
{
|
||
|
dwError -= dstHeight;
|
||
|
dwVEsrcAddr += srcPitch;
|
||
|
}
|
||
|
|
||
|
// Now copy from the scratch area to the final destination:
|
||
|
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 6);
|
||
|
NW_ALT_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | OVERPAINT, 0);
|
||
|
NW_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
|
||
|
NW_ABS_CURXY_FAST(ppdev, pjMmBase, srcX, srcY);
|
||
|
NW_ABS_DESTXY_FAST(ppdev, pjMmBase, dstX, dstY);
|
||
|
NW_ALT_PCNT(ppdev, pjMmBase, dstWidth - 1, 0);
|
||
|
NW_ALT_CMD(ppdev, pjMmBase, BITBLT | DRAW | DIR_TYPE_XY |
|
||
|
WRITE | DRAWING_DIR_TBLRXM);
|
||
|
|
||
|
dstY++;
|
||
|
|
||
|
} while (--i != 0);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//////////////////////////////////////////////////////////////////////
|
||
|
// Overlay Blts
|
||
|
//
|
||
|
// Here we have to take care of cases where the destination is a
|
||
|
// funky pixel format.
|
||
|
|
||
|
// In order to make ActiveMovie and DirectVideo work, we have
|
||
|
// to support blting between funky pixel format surfaces of the
|
||
|
// same type. This is used to copy the current frame to the
|
||
|
// next overlay surface in line.
|
||
|
//
|
||
|
// Unfortunately, it's not easy to switch the S3 graphics
|
||
|
// processor out of its current pixel depth, so we'll only support
|
||
|
// the minimal functionality required:
|
||
|
|
||
|
if (!(dwFlags & DDBLT_ROP) ||
|
||
|
(srcX != 0) ||
|
||
|
(srcY != 0) ||
|
||
|
(dstX != 0) ||
|
||
|
(dstY != 0) ||
|
||
|
(dstWidth != dstSurf->wWidth) ||
|
||
|
(dstHeight != dstSurf->wHeight) ||
|
||
|
(dstSurf->lPitch != srcSurf->lPitch) ||
|
||
|
(dstSurf->ddpfSurface.dwRGBBitCount
|
||
|
!= srcSurf->ddpfSurface.dwRGBBitCount))
|
||
|
{
|
||
|
DISPDBG((0, "Sorry, we do only full-surface blts between same-type"));
|
||
|
DISPDBG((0, "surfaces that have a funky pixel format."));
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Convert the dimensions to the current pixel format. This
|
||
|
// is pretty easy because we created the bitmap linearly, so
|
||
|
// it takes the entire width of the screen:
|
||
|
|
||
|
dstWidth = ppdev->cxMemory;
|
||
|
dstHeight = dstSurf->dwBlockSizeY;
|
||
|
|
||
|
convertToGlobalCord(dstX, dstY, dstSurf);
|
||
|
convertToGlobalCord(srcX, srcY, srcSurf);
|
||
|
|
||
|
NW_FIFO_WAIT(ppdev, pjMmBase, 6);
|
||
|
NW_ALT_MIX(ppdev, pjMmBase, SRC_DISPLAY_MEMORY | OVERPAINT, 0);
|
||
|
NW_PIX_CNTL(ppdev, pjMmBase, ALL_ONES);
|
||
|
NW_ABS_CURXY_FAST(ppdev, pjMmBase, srcX, srcY);
|
||
|
NW_ABS_DESTXY_FAST(ppdev, pjMmBase, dstX, dstY);
|
||
|
NW_ALT_PCNT(ppdev, pjMmBase, dstWidth - 1, dstHeight - 1);
|
||
|
NW_ALT_CMD(ppdev, pjMmBase, BITBLT | DRAW | DIR_TYPE_XY |
|
||
|
WRITE | DRAWING_DIR_TBLRXM);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lpBlt->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdFlip
|
||
|
*
|
||
|
* Note that lpSurfCurr may not necessarily be valid.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdFlip(
|
||
|
PDD_FLIPDATA lpFlip)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE* pjMmBase;
|
||
|
HRESULT ddrval;
|
||
|
ULONG ulMemoryOffset;
|
||
|
ULONG ulLowOffset;
|
||
|
ULONG ulMiddleOffset;
|
||
|
ULONG ulHighOffset;
|
||
|
|
||
|
ppdev = (PDEV*) lpFlip->lpDD->dhpdev;
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
|
||
|
// 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) || (NW_GP_BUSY(ppdev, pjMmBase)))
|
||
|
{
|
||
|
lpFlip->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
ulMemoryOffset = (ULONG)(lpFlip->lpSurfTarg->lpGbl->fpVidMem);
|
||
|
|
||
|
// 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(pjIoBase)))
|
||
|
;
|
||
|
|
||
|
if (ppdev->flStatus & STAT_STREAMS_ENABLED)
|
||
|
{
|
||
|
// When using the streams processor, we have to do the flip via the
|
||
|
// streams registers:
|
||
|
|
||
|
if (lpFlip->lpSurfCurr->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
|
||
|
{
|
||
|
WRITE_STREAM_D(pjMmBase, P_0, ulMemoryOffset);
|
||
|
}
|
||
|
else if (lpFlip->lpSurfCurr->ddsCaps.dwCaps & DDSCAPS_OVERLAY)
|
||
|
{
|
||
|
// Make sure that the overlay surface we're flipping from is
|
||
|
// currently visible. If you don't do this check, you'll get
|
||
|
// really weird results when someone starts up two ActiveMovie
|
||
|
// or DirectVideo movies simultaneously!
|
||
|
|
||
|
if (lpFlip->lpSurfCurr->lpGbl->fpVidMem == ppdev->fpVisibleOverlay)
|
||
|
{
|
||
|
ppdev->fpVisibleOverlay = ulMemoryOffset;
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, S_0, ulMemoryOffset +
|
||
|
ppdev->dwOverlayFlipOffset);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Do the old way, via the CRTC registers:
|
||
|
|
||
|
ulMemoryOffset >>= 2;
|
||
|
|
||
|
ulLowOffset = 0x0d | ((ulMemoryOffset & 0x0000ff) << 8);
|
||
|
ulMiddleOffset = 0x0c | ((ulMemoryOffset & 0x00ff00));
|
||
|
ulHighOffset = 0x69 | ((ulMemoryOffset & 0x1f0000) >> 8)
|
||
|
| ppdev->ulExtendedSystemControl3Register_69;
|
||
|
|
||
|
// Don't let the cursor thread touch the CRT registers while we're
|
||
|
// using them:
|
||
|
|
||
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
|
||
|
// Too bad that the S3's flip can't be done in a single atomic register
|
||
|
// write; as it is, we stand a small chance of being context-switched
|
||
|
// out and exactly hitting the vertical blank in the middle of doing
|
||
|
// these outs, possibly causing the screen to momentarily jump.
|
||
|
//
|
||
|
// There are some hoops we could jump through to minimize the chances
|
||
|
// of this happening; we could try to align the flip buffer such that
|
||
|
// the minor registers are ensured to be identical for either flip
|
||
|
// position, and so that only the high address need be written, an
|
||
|
// obviously atomic operation.
|
||
|
//
|
||
|
// However, I'm simply not going to worry about it.
|
||
|
|
||
|
OUTPW(pjIoBase, CRTC_INDEX, ulLowOffset);
|
||
|
OUTPW(pjIoBase, CRTC_INDEX, ulMiddleOffset);
|
||
|
OUTPW(pjIoBase, CRTC_INDEX, ulHighOffset);
|
||
|
|
||
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdLock
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdLock(
|
||
|
PDD_LOCKDATA lpLock)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjMmBase;
|
||
|
HRESULT ddrval;
|
||
|
|
||
|
ppdev = (PDEV*) lpLock->lpDD->dhpdev;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
|
||
|
// Check to see if any pending physical flip has occurred. Don't allow
|
||
|
// a lock if a blt is in progress:
|
||
|
|
||
|
ddrval = ddrvalUpdateFlipStatus(ppdev, lpLock->lpDDSurface->lpGbl->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)
|
||
|
{
|
||
|
NW_GP_WAIT(ppdev, pjMmBase);
|
||
|
}
|
||
|
else if (NW_GP_BUSY(ppdev, pjMmBase))
|
||
|
{
|
||
|
lpLock->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
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* pjMmBase;
|
||
|
HRESULT ddRVal;
|
||
|
|
||
|
ppdev = (PDEV*) lpGetBltStatus->lpDD->dhpdev;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
|
||
|
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 (MM_FIFO_BUSY(ppdev, pjMmBase, DDBLT_FIFO_COUNT))
|
||
|
{
|
||
|
ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// DDGBS_ISBLTDONE case: is a blt in progress?
|
||
|
|
||
|
if (NW_GP_BUSY(ppdev, pjMmBase))
|
||
|
{
|
||
|
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* pjMmBase;
|
||
|
|
||
|
ppdev = (PDEV*) lpGetFlipStatus->lpDD->dhpdev;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
|
||
|
// 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) && (NW_GP_BUSY(ppdev, pjMmBase)))
|
||
|
{
|
||
|
lpGetFlipStatus->ddRVal = DDERR_WASSTILLDRAWING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdWaitForVerticalBlank
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdWaitForVerticalBlank(
|
||
|
PDD_WAITFORVERTICALBLANKDATA lpWaitForVerticalBlank)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjIoBase;
|
||
|
|
||
|
ppdev = (PDEV*) lpWaitForVerticalBlank->lpDD->dhpdev;
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
switch (lpWaitForVerticalBlank->dwFlags)
|
||
|
{
|
||
|
case DDWAITVB_I_TESTVB:
|
||
|
|
||
|
// If TESTVB, it's just a request for the current vertical blank
|
||
|
// status:
|
||
|
|
||
|
if (VBLANK_IS_ACTIVE(pjIoBase))
|
||
|
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(pjIoBase))
|
||
|
;
|
||
|
while (!(VBLANK_IS_ACTIVE(pjIoBase)))
|
||
|
;
|
||
|
|
||
|
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(pjIoBase)))
|
||
|
;
|
||
|
while (VBLANK_IS_ACTIVE(pjIoBase))
|
||
|
;
|
||
|
|
||
|
lpWaitForVerticalBlank->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdCanCreateSurface
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdCanCreateSurface(
|
||
|
PDD_CANCREATESURFACEDATA lpCanCreateSurface)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
DWORD dwRet;
|
||
|
LPDDSURFACEDESC lpSurfaceDesc;
|
||
|
|
||
|
ppdev = (PDEV*) lpCanCreateSurface->lpDD->dhpdev;
|
||
|
lpSurfaceDesc = lpCanCreateSurface->lpDDSurfaceDesc;
|
||
|
|
||
|
dwRet = DDHAL_DRIVER_NOTHANDLED;
|
||
|
|
||
|
if (!lpCanCreateSurface->bIsDifferentPixelFormat)
|
||
|
{
|
||
|
// It's trivially easy to create plain surfaces that are the same
|
||
|
// type as the primary surface:
|
||
|
|
||
|
dwRet = DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
|
||
|
// If the streams processor is capable, we can handle overlays:
|
||
|
|
||
|
else if (ppdev->flCaps & CAPS_STREAMS_CAPABLE)
|
||
|
{
|
||
|
// When using the Streams processor, we handle only overlays of
|
||
|
// different pixel formats -- not any off-screen memory:
|
||
|
|
||
|
if (lpSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_OVERLAY)
|
||
|
{
|
||
|
// We handle two types of YUV overlay surfaces:
|
||
|
|
||
|
if (lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
// Check first for a supported YUV type:
|
||
|
|
||
|
if (lpSurfaceDesc->ddpfPixelFormat.dwFourCC == FOURCC_YUY2)
|
||
|
{
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwYUVBitCount = 16;
|
||
|
dwRet = DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We handle 16bpp and 32bpp RGB overlay surfaces:
|
||
|
|
||
|
else if ((lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB) &&
|
||
|
!(lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8))
|
||
|
{
|
||
|
if (lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount == 16)
|
||
|
{
|
||
|
if (IS_RGB15(&lpSurfaceDesc->ddpfPixelFormat) ||
|
||
|
IS_RGB16(&lpSurfaceDesc->ddpfPixelFormat))
|
||
|
{
|
||
|
dwRet = DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We don't handle 24bpp overlay surfaces because they are
|
||
|
// undocumented and don't seem to work on the Trio64V+.
|
||
|
//
|
||
|
// We don't handle 32bpp overlay surfaces because our streams
|
||
|
// minimum-stretch-ratio tables were obviously created for
|
||
|
// 16bpp overlay surfaces; 32bpp overlay surfaces create a lot
|
||
|
// of noise when close to the minimum stretch ratio.
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If the pixel formatter is enabled, we can handle funky format off-
|
||
|
// screen surfaces, but not at 8bpp because of palette issues:
|
||
|
|
||
|
else if ((ppdev->flCaps & CAPS_PIXEL_FORMATTER) &&
|
||
|
(ppdev->iBitmapFormat > BMF_8BPP))
|
||
|
{
|
||
|
if (lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
if (lpSurfaceDesc->ddpfPixelFormat.dwFourCC == FOURCC_YUY2)
|
||
|
{
|
||
|
lpSurfaceDesc->ddpfPixelFormat.dwYUVBitCount = 16;
|
||
|
dwRet = DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We handle 16bpp and 32bpp RGB off-screen surfaces:
|
||
|
|
||
|
else if ((lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_RGB) &&
|
||
|
!(lpSurfaceDesc->ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8))
|
||
|
{
|
||
|
if (lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount == 16)
|
||
|
{
|
||
|
if (IS_RGB15(&lpSurfaceDesc->ddpfPixelFormat) ||
|
||
|
IS_RGB16(&lpSurfaceDesc->ddpfPixelFormat))
|
||
|
{
|
||
|
dwRet = DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
}
|
||
|
else if (lpSurfaceDesc->ddpfPixelFormat.dwRGBBitCount == 32)
|
||
|
{
|
||
|
if (IS_RGB32(&lpSurfaceDesc->ddpfPixelFormat))
|
||
|
{
|
||
|
dwRet = DDHAL_DRIVER_HANDLED;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Print some spew if this was a surface we refused to create:
|
||
|
|
||
|
if (dwRet == DDHAL_DRIVER_NOTHANDLED)
|
||
|
{
|
||
|
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));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lpCanCreateSurface->ddRVal = DD_OK;
|
||
|
return(dwRet);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdCreateSurface
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdCreateSurface(
|
||
|
PDD_CREATESURFACEDATA lpCreateSurface)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
DD_SURFACE_LOCAL* lpSurfaceLocal;
|
||
|
DD_SURFACE_GLOBAL* lpSurfaceGlobal;
|
||
|
LPDDSURFACEDESC lpSurfaceDesc;
|
||
|
DWORD dwByteCount;
|
||
|
LONG lLinearPitch;
|
||
|
DWORD dwHeight;
|
||
|
|
||
|
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:
|
||
|
|
||
|
lpSurfaceLocal = lpCreateSurface->lplpSList[0];
|
||
|
lpSurfaceGlobal = lpSurfaceLocal->lpGbl;
|
||
|
lpSurfaceDesc = lpCreateSurface->lpDDSurfaceDesc;
|
||
|
|
||
|
// We repeat the same checks we did in 'DdCanCreateSurface' because
|
||
|
// it's possible that an application doesn't call 'DdCanCreateSurface'
|
||
|
// before calling 'DdCreateSurface'.
|
||
|
|
||
|
ASSERTDD(lpSurfaceGlobal->ddpfSurface.dwSize == sizeof(DDPIXELFORMAT),
|
||
|
"NT is supposed to guarantee that ddpfSurface.dwSize is valid");
|
||
|
|
||
|
// DdCanCreateSurface already validated whether the hardware supports
|
||
|
// the surface, so we don't need to do any validation here. We'll
|
||
|
// just go ahead and allocate it.
|
||
|
//
|
||
|
// Note that we don't do anything special for RGB surfaces that are
|
||
|
// the same pixel format as the display -- by returning DDHAL_DRIVER_
|
||
|
// NOTHANDLED, DirectDraw will automatically handle the allocation
|
||
|
// for us.
|
||
|
//
|
||
|
// Also, since we'll be making linear surfaces, make sure the width
|
||
|
// isn't unreasonably large.
|
||
|
//
|
||
|
// Note that on NT, an overlay can be created only if the driver
|
||
|
// okay's it here in this routine. Under Win95, the overlay will be
|
||
|
// created automatically if it's the same pixel format as the primary
|
||
|
// display.
|
||
|
|
||
|
if ((lpSurfaceLocal->ddsCaps.dwCaps & DDSCAPS_OVERLAY) ||
|
||
|
(lpSurfaceGlobal->ddpfSurface.dwFlags & DDPF_FOURCC) ||
|
||
|
(lpSurfaceGlobal->ddpfSurface.dwYUVBitCount
|
||
|
!= (DWORD) 8 * ppdev->cjPelSize) ||
|
||
|
(lpSurfaceGlobal->ddpfSurface.dwRBitMask != ppdev->flRed))
|
||
|
{
|
||
|
if (lpSurfaceGlobal->wWidth <= (DWORD) ppdev->cxMemory)
|
||
|
{
|
||
|
// The S3 cannot easily draw to YUV surfaces or surfaces that are
|
||
|
// a different RGB format than the display. So we'll make them
|
||
|
// linear surfaces to save some space:
|
||
|
|
||
|
if (lpSurfaceGlobal->ddpfSurface.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
ASSERTDD((lpSurfaceGlobal->ddpfSurface.dwFourCC == FOURCC_YUY2),
|
||
|
"Expected our DdCanCreateSurface to allow only YUY2 or Y211");
|
||
|
|
||
|
dwByteCount = (lpSurfaceGlobal->ddpfSurface.dwFourCC == FOURCC_YUY2)
|
||
|
? 2 : 1;
|
||
|
|
||
|
// We have to fill in the bit-count for FourCC surfaces:
|
||
|
|
||
|
lpSurfaceGlobal->ddpfSurface.dwYUVBitCount = 8 * dwByteCount;
|
||
|
|
||
|
DISPDBG((0, "Created YUV: %li x %li",
|
||
|
lpSurfaceGlobal->wWidth, lpSurfaceGlobal->wHeight));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwByteCount = lpSurfaceGlobal->ddpfSurface.dwRGBBitCount >> 3;
|
||
|
|
||
|
DISPDBG((0, "Created RGB %libpp: %li x %li Red: %lx",
|
||
|
8 * dwByteCount, lpSurfaceGlobal->wWidth, lpSurfaceGlobal->wHeight,
|
||
|
lpSurfaceGlobal->ddpfSurface.dwRBitMask));
|
||
|
|
||
|
// The S3 can't handle palettized or 32bpp overlays. Note that
|
||
|
// we sometimes don't get a chance to say no to these surfaces
|
||
|
// in CanCreateSurface, because DirectDraw won't call
|
||
|
// CanCreateSurface if the surface to be created is the same
|
||
|
// pixel format as the primary display:
|
||
|
|
||
|
if ((dwByteCount != 2) &&
|
||
|
(lpSurfaceLocal->ddsCaps.dwCaps & DDSCAPS_OVERLAY))
|
||
|
{
|
||
|
lpCreateSurface->ddRVal = DDERR_INVALIDPIXELFORMAT;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// We want to allocate a linear surface to store the FourCC
|
||
|
// surface, but DirectDraw is using a 2-D heap-manager because
|
||
|
// the rest of our surfaces have to be 2-D. So here we have to
|
||
|
// convert the linear size to a 2-D size.
|
||
|
//
|
||
|
// The stride has to be a dword multiple:
|
||
|
|
||
|
lLinearPitch = (lpSurfaceGlobal->wWidth * dwByteCount + 3) & ~3;
|
||
|
dwHeight = (lpSurfaceGlobal->wHeight * lLinearPitch
|
||
|
+ ppdev->lDelta - 1) / ppdev->lDelta;
|
||
|
|
||
|
// Now fill in enough stuff to have the DirectDraw heap-manager
|
||
|
// do the allocation for us:
|
||
|
|
||
|
lpSurfaceGlobal->fpVidMem = DDHAL_PLEASEALLOC_BLOCKSIZE;
|
||
|
lpSurfaceGlobal->dwBlockSizeX = ppdev->lDelta; // Specified in bytes
|
||
|
lpSurfaceGlobal->dwBlockSizeY = dwHeight;
|
||
|
lpSurfaceGlobal->lPitch = lLinearPitch;
|
||
|
lpSurfaceGlobal->dwReserved1 = DD_RESERVED_DIFFERENTPIXELFORMAT;
|
||
|
|
||
|
lpSurfaceDesc->lPitch = lLinearPitch;
|
||
|
lpSurfaceDesc->dwFlags |= DDSD_PITCH;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DISPDBG((0, "Refused to create surface with large width"));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdFreeDriverMemory
|
||
|
*
|
||
|
* This function called by DirectDraw when it's running low on memory in
|
||
|
* our heap. You only need to implement this function if you use the
|
||
|
* DirectDraw 'HeapVidMemAllocAligned' function in your driver, and you
|
||
|
* can boot those allocations out of memory to make room for DirectDraw.
|
||
|
*
|
||
|
* We implement this function in the S3 driver because we have DirectDraw
|
||
|
* entirely manage our off-screen heap, and we use HeapVidMemAllocAligned
|
||
|
* to put GDI device-bitmaps in off-screen memory. DirectDraw applications
|
||
|
* have a higher priority for getting stuff into video memory, though, and
|
||
|
* so this function is used to boot those GDI surfaces out of memory in
|
||
|
* order to make room for DirectDraw.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdFreeDriverMemory(
|
||
|
PDD_FREEDRIVERMEMORYDATA lpFreeDriverMemory)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
|
||
|
ppdev = (PDEV*) lpFreeDriverMemory->lpDD->dhpdev;
|
||
|
|
||
|
lpFreeDriverMemory->ddRVal = DDERR_OUTOFMEMORY;
|
||
|
|
||
|
// If we successfully freed up some memory, set the return value to
|
||
|
// 'DD_OK'. DirectDraw will try again to do its allocation, and
|
||
|
// will call us again if there's still not enough room. (It will
|
||
|
// call us until either there's enough room for its alocation to
|
||
|
// succeed, or until we return something other than DD_OK.)
|
||
|
|
||
|
if (bMoveOldestOffscreenDfbToDib(ppdev))
|
||
|
{
|
||
|
lpFreeDriverMemory->ddRVal = DD_OK;
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdSetColorKey
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdSetColorKey(
|
||
|
PDD_SETCOLORKEYDATA lpSetColorKey)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE* pjMmBase;
|
||
|
DD_SURFACE_GLOBAL* lpSurface;
|
||
|
DWORD dwKeyLow;
|
||
|
DWORD dwKeyHigh;
|
||
|
|
||
|
ppdev = (PDEV*) lpSetColorKey->lpDD->dhpdev;
|
||
|
|
||
|
ASSERTDD(ppdev->flCaps & CAPS_STREAMS_CAPABLE, "Shouldn't have hooked call");
|
||
|
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
lpSurface = lpSetColorKey->lpDDSurface->lpGbl;
|
||
|
|
||
|
// We don't have to do anything for normal blt source colour keys:
|
||
|
|
||
|
if (lpSetColorKey->dwFlags & DDCKEY_SRCBLT)
|
||
|
{
|
||
|
lpSetColorKey->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
else if (lpSetColorKey->dwFlags & DDCKEY_DESTOVERLAY)
|
||
|
{
|
||
|
dwKeyLow = lpSetColorKey->ckNew.dwColorSpaceLowValue;
|
||
|
|
||
|
if (lpSurface->ddpfSurface.dwFlags & DDPF_PALETTEINDEXED8)
|
||
|
{
|
||
|
dwKeyLow = dwGetPaletteEntry(ppdev, dwKeyLow);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERTDD(lpSurface->ddpfSurface.dwFlags & DDPF_RGB,
|
||
|
"Expected only RGB cases here");
|
||
|
|
||
|
// We have to transform the colour key from its native format
|
||
|
// to 8-8-8:
|
||
|
|
||
|
if (lpSurface->ddpfSurface.dwRGBBitCount == 16)
|
||
|
{
|
||
|
if (IS_RGB15_R(lpSurface->ddpfSurface.dwRBitMask))
|
||
|
dwKeyLow = RGB15to32(dwKeyLow);
|
||
|
else
|
||
|
dwKeyLow = RGB16to32(dwKeyLow);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERTDD((lpSurface->ddpfSurface.dwRGBBitCount == 32),
|
||
|
"Expected the primary surface to be either 8, 16, or 32bpp");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dwKeyHigh = dwKeyLow;
|
||
|
dwKeyLow |= CompareBits0t7 | KeyFromCompare;
|
||
|
|
||
|
// Check for stream processor enabled before setting registers
|
||
|
if(ppdev->flStatus & STAT_STREAMS_ENABLED)
|
||
|
{
|
||
|
WAIT_FOR_VBLANK(pjIoBase);
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, CKEY_LOW, dwKeyLow);
|
||
|
WRITE_STREAM_D(pjMmBase, CKEY_HI, dwKeyHigh);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Save away the color key to be set when streams
|
||
|
// processor is turned on.
|
||
|
ppdev->ulColorKey = dwKeyHigh;
|
||
|
}
|
||
|
|
||
|
lpSetColorKey->ddRVal = DD_OK;
|
||
|
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
DISPDBG((0, "DdSetColorKey: Invalid command"));
|
||
|
return(DDHAL_DRIVER_NOTHANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdUpdateOverlay
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdUpdateOverlay(
|
||
|
PDD_UPDATEOVERLAYDATA lpUpdateOverlay)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE* pjMmBase;
|
||
|
DD_SURFACE_GLOBAL* lpSource;
|
||
|
DD_SURFACE_GLOBAL* lpDestination;
|
||
|
DWORD dwStride;
|
||
|
LONG srcWidth;
|
||
|
LONG srcHeight;
|
||
|
LONG dstWidth;
|
||
|
LONG dstHeight;
|
||
|
DWORD dwBitCount;
|
||
|
DWORD dwStart;
|
||
|
DWORD dwTmp;
|
||
|
BOOL bColorKey;
|
||
|
DWORD dwKeyLow;
|
||
|
DWORD dwKeyHigh;
|
||
|
DWORD dwBytesPerPixel;
|
||
|
|
||
|
DWORD dwSecCtrl;
|
||
|
DWORD dwBlendCtrl;
|
||
|
|
||
|
ppdev = (PDEV*) lpUpdateOverlay->lpDD->dhpdev;
|
||
|
|
||
|
ASSERTDD(ppdev->flCaps & CAPS_STREAMS_CAPABLE, "Shouldn't have hooked call");
|
||
|
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
|
||
|
// 'Source' is the overlay surface, 'destination' is the surface to
|
||
|
// be overlayed:
|
||
|
|
||
|
lpSource = lpUpdateOverlay->lpDDSrcSurface->lpGbl;
|
||
|
|
||
|
if (lpUpdateOverlay->dwFlags & DDOVER_HIDE)
|
||
|
{
|
||
|
if (lpSource->fpVidMem == ppdev->fpVisibleOverlay)
|
||
|
{
|
||
|
WAIT_FOR_VBLANK(pjIoBase);
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, BLEND_CONTROL, POnS);
|
||
|
WRITE_STREAM_D(pjMmBase, S_WH, WH(10, 2)); // Set to 10x2 rectangle
|
||
|
WRITE_STREAM_D(pjMmBase, OPAQUE_CONTROL, 0);// Disable opaque control
|
||
|
|
||
|
ppdev->fpVisibleOverlay = 0;
|
||
|
|
||
|
ASSERTDD(ppdev->flStatus & STAT_STREAMS_ENABLED,
|
||
|
"Expected streams to be enabled");
|
||
|
|
||
|
ppdev->flStatus &= ~STAT_STREAMS_ENABLED;
|
||
|
vTurnOffStreamsProcessorMode(ppdev);
|
||
|
}
|
||
|
|
||
|
lpUpdateOverlay->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
// Dereference 'lpDDDestSurface' only after checking for the DDOVER_HIDE
|
||
|
// case:
|
||
|
|
||
|
lpDestination = lpUpdateOverlay->lpDDDestSurface->lpGbl;
|
||
|
|
||
|
if (lpSource->fpVidMem != ppdev->fpVisibleOverlay)
|
||
|
{
|
||
|
if (lpUpdateOverlay->dwFlags & DDOVER_SHOW)
|
||
|
{
|
||
|
if (ppdev->fpVisibleOverlay != 0)
|
||
|
{
|
||
|
// Some other overlay is already visible:
|
||
|
|
||
|
DISPDBG((0, "DdUpdateOverlay: An overlay is already visible"));
|
||
|
|
||
|
lpUpdateOverlay->ddRVal = DDERR_OUTOFCAPS;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// We're going to make the overlay visible, so mark it as
|
||
|
// such:
|
||
|
|
||
|
ppdev->fpVisibleOverlay = lpSource->fpVidMem;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// The overlay isn't visible, and we haven't been asked to make
|
||
|
// it visible, so this call is trivially easy:
|
||
|
|
||
|
lpUpdateOverlay->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(ppdev->flStatus & STAT_STREAMS_ENABLED))
|
||
|
{
|
||
|
ppdev->flStatus |= STAT_STREAMS_ENABLED;
|
||
|
vTurnOnStreamsProcessorMode(ppdev);
|
||
|
}
|
||
|
|
||
|
dwStride = lpSource->lPitch;
|
||
|
srcWidth = lpUpdateOverlay->rSrc.right - lpUpdateOverlay->rSrc.left;
|
||
|
srcHeight = lpUpdateOverlay->rSrc.bottom - lpUpdateOverlay->rSrc.top;
|
||
|
dstWidth = lpUpdateOverlay->rDest.right - lpUpdateOverlay->rDest.left;
|
||
|
dstHeight = lpUpdateOverlay->rDest.bottom - lpUpdateOverlay->rDest.top;
|
||
|
|
||
|
// Calculate DDA horizonal accumulator initial value:
|
||
|
|
||
|
dwSecCtrl = HDDA(srcWidth, dstWidth);
|
||
|
|
||
|
// Overlay input data format:
|
||
|
|
||
|
if (lpSource->ddpfSurface.dwFlags & DDPF_FOURCC)
|
||
|
{
|
||
|
dwBitCount = lpSource->ddpfSurface.dwYUVBitCount;
|
||
|
|
||
|
switch (lpSource->ddpfSurface.dwFourCC)
|
||
|
{
|
||
|
case FOURCC_YUY2:
|
||
|
dwSecCtrl |= S_YCrCb422; // Not S_YUV422! Dunno why...
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
RIP("Unexpected FourCC");
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERTDD(lpSource->ddpfSurface.dwFlags & DDPF_RGB,
|
||
|
"Expected us to have created only RGB or YUV overlays");
|
||
|
|
||
|
// The overlay surface is in RGB format:
|
||
|
|
||
|
dwBitCount = lpSource->ddpfSurface.dwRGBBitCount;
|
||
|
|
||
|
ASSERTDD(dwBitCount == 16,
|
||
|
"Expected us to have created 16bpp RGB surfaces only");
|
||
|
|
||
|
if (IS_RGB15_R(lpSource->ddpfSurface.dwRBitMask))
|
||
|
dwSecCtrl |= S_RGB15;
|
||
|
else
|
||
|
dwSecCtrl |= S_RGB16;
|
||
|
}
|
||
|
|
||
|
// Calculate start of video memory in QWORD boundary
|
||
|
|
||
|
dwBytesPerPixel = dwBitCount >> 3;
|
||
|
|
||
|
dwStart = (lpUpdateOverlay->rSrc.top * dwStride)
|
||
|
+ (lpUpdateOverlay->rSrc.left * dwBytesPerPixel);
|
||
|
|
||
|
// Note that since we're shifting the source's edge to the left, we
|
||
|
// should really increase the source width to compensate. However,
|
||
|
// doing so when running at 1 to 1 would cause us to request a
|
||
|
// shrinking overlay -- something the S3 can't do.
|
||
|
|
||
|
dwStart = dwStart - (dwStart & 0x7);
|
||
|
|
||
|
ppdev->dwOverlayFlipOffset = dwStart; // Save for flip
|
||
|
dwStart += (DWORD)lpSource->fpVidMem;
|
||
|
|
||
|
// Set overlay filter characteristics:
|
||
|
|
||
|
if ((dstWidth != srcWidth) || (dstHeight != srcHeight))
|
||
|
{
|
||
|
if (dstWidth >= (srcWidth << 2))
|
||
|
{
|
||
|
dwSecCtrl |= S_Beyond4x; // Linear, 1-2-2-2-1, for >4X stretch
|
||
|
}
|
||
|
else if (dstWidth >= (srcWidth << 1))
|
||
|
{
|
||
|
dwSecCtrl |= S_2xTo4x; // Bi-linear, for 2X to 4X stretch
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwSecCtrl |= S_Upto2x; // Linear, 0-2-4-2-0, for X stretch
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Extract colour key:
|
||
|
|
||
|
bColorKey = FALSE;
|
||
|
dwBlendCtrl = 0;
|
||
|
|
||
|
if (lpUpdateOverlay->dwFlags & DDOVER_KEYDEST)
|
||
|
{
|
||
|
bColorKey = TRUE;
|
||
|
dwKeyLow = lpUpdateOverlay->lpDDDestSurface->ddckCKDestOverlay.dwColorSpaceLowValue;
|
||
|
dwBlendCtrl |= KeyOnP;
|
||
|
}
|
||
|
else if (lpUpdateOverlay->dwFlags & DDOVER_KEYDESTOVERRIDE)
|
||
|
{
|
||
|
bColorKey = TRUE;
|
||
|
dwKeyLow = lpUpdateOverlay->overlayFX.dckDestColorkey.dwColorSpaceLowValue;
|
||
|
dwBlendCtrl |= KeyOnP;
|
||
|
}
|
||
|
|
||
|
if (bColorKey)
|
||
|
{
|
||
|
// We support only destination colour keys:
|
||
|
|
||
|
if (lpDestination->ddpfSurface.dwFlags & DDPF_PALETTEINDEXED8)
|
||
|
{
|
||
|
dwKeyLow = dwGetPaletteEntry(ppdev, dwKeyLow);
|
||
|
}
|
||
|
else if (lpDestination->ddpfSurface.dwFlags & DDPF_RGB)
|
||
|
{
|
||
|
ASSERTDD(lpDestination->ddpfSurface.dwFlags & DDPF_RGB,
|
||
|
"Expected only RGB cases here");
|
||
|
|
||
|
// We have to transform the colour key from its native format
|
||
|
// to 8-8-8:
|
||
|
|
||
|
if (lpDestination->ddpfSurface.dwRGBBitCount == 16)
|
||
|
{
|
||
|
if (IS_RGB15_R(lpDestination->ddpfSurface.dwRBitMask))
|
||
|
dwKeyLow = RGB15to32(dwKeyLow);
|
||
|
else
|
||
|
dwKeyLow = RGB16to32(dwKeyLow);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERTDD((lpDestination->ddpfSurface.dwRGBBitCount == 32),
|
||
|
"Expected the primary surface to be either 8, 16, or 32bpp");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dwKeyHigh = dwKeyLow;
|
||
|
dwKeyLow |= CompareBits0t7 | KeyFromCompare;
|
||
|
}
|
||
|
|
||
|
// Update and show:
|
||
|
|
||
|
NW_GP_WAIT(ppdev, pjMmBase);
|
||
|
|
||
|
WAIT_FOR_VBLANK(pjIoBase);
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, S_0, dwStart);
|
||
|
WRITE_STREAM_D(pjMmBase, S_XY, XY(lpUpdateOverlay->rDest.left,
|
||
|
lpUpdateOverlay->rDest.top));
|
||
|
WRITE_STREAM_D(pjMmBase, S_WH, WH(dstWidth, dstHeight));
|
||
|
WRITE_STREAM_D(pjMmBase, S_STRIDE, dwStride);
|
||
|
WRITE_STREAM_D(pjMmBase, S_CONTROL, dwSecCtrl);
|
||
|
WRITE_STREAM_D(pjMmBase, S_HK1K2, HK1K2(srcWidth, dstWidth));
|
||
|
WRITE_STREAM_D(pjMmBase, S_VK1, VK1(srcHeight));
|
||
|
WRITE_STREAM_D(pjMmBase, S_VK2, VK2(srcHeight, dstHeight));
|
||
|
WRITE_STREAM_D(pjMmBase, S_VDDA, VDDA(dstHeight));
|
||
|
|
||
|
if (bColorKey)
|
||
|
{
|
||
|
WRITE_STREAM_D(pjMmBase, CKEY_LOW, dwKeyLow);
|
||
|
WRITE_STREAM_D(pjMmBase, CKEY_HI, dwKeyHigh);
|
||
|
}
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, BLEND_CONTROL, dwBlendCtrl);
|
||
|
WRITE_STREAM_D(pjMmBase, FIFO_CONTROL, ppdev->ulFifoValue);
|
||
|
|
||
|
lpUpdateOverlay->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdSetOverlayPosition
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdSetOverlayPosition(
|
||
|
PDD_SETOVERLAYPOSITIONDATA lpSetOverlayPosition)
|
||
|
{
|
||
|
PDEV* ppdev;
|
||
|
BYTE* pjIoBase;
|
||
|
BYTE* pjMmBase;
|
||
|
|
||
|
ppdev = (PDEV*) lpSetOverlayPosition->lpDD->dhpdev;
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
pjMmBase = ppdev->pjMmBase;
|
||
|
|
||
|
ASSERTDD(ppdev->flCaps & CAPS_STREAMS_CAPABLE, "Shouldn't have hooked call");
|
||
|
|
||
|
// Check that streams processor is enabled before settting registers
|
||
|
if(ppdev->flStatus & STAT_STREAMS_ENABLED)
|
||
|
{
|
||
|
WAIT_FOR_VBLANK(pjIoBase);
|
||
|
|
||
|
WRITE_STREAM_D(pjMmBase, S_XY, XY(lpSetOverlayPosition->lXPos,
|
||
|
lpSetOverlayPosition->lYPos));
|
||
|
}
|
||
|
|
||
|
lpSetOverlayPosition->ddRVal = DD_OK;
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* DWORD DdGetDriverInfo
|
||
|
*
|
||
|
* This function is an extensible method for returning DirectDraw
|
||
|
* capabilities and methods.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
DWORD DdGetDriverInfo(
|
||
|
PDD_GETDRIVERINFODATA lpGetDriverInfo)
|
||
|
{
|
||
|
DWORD dwSize;
|
||
|
|
||
|
lpGetDriverInfo->ddRVal = DDERR_CURRENTLYNOTAVAIL;
|
||
|
|
||
|
if (IsEqualIID(&lpGetDriverInfo->guidInfo, &GUID_NTCallbacks))
|
||
|
{
|
||
|
DD_NTCALLBACKS NtCallbacks;
|
||
|
|
||
|
memset(&NtCallbacks, 0, sizeof(NtCallbacks));
|
||
|
|
||
|
dwSize = min(lpGetDriverInfo->dwExpectedSize, sizeof(DD_NTCALLBACKS));
|
||
|
|
||
|
NtCallbacks.dwSize = dwSize;
|
||
|
NtCallbacks.dwFlags = DDHAL_NTCB32_FREEDRIVERMEMORY;
|
||
|
NtCallbacks.FreeDriverMemory = DdFreeDriverMemory;
|
||
|
|
||
|
memcpy(lpGetDriverInfo->lpvData, &NtCallbacks, dwSize);
|
||
|
|
||
|
lpGetDriverInfo->ddRVal = DD_OK;
|
||
|
}
|
||
|
|
||
|
return(DDHAL_DRIVER_HANDLED);
|
||
|
}
|
||
|
|
||
|
/******************************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 bEnable)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/******************************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)
|
||
|
{
|
||
|
BYTE* pjIoBase;
|
||
|
VIDEO_QUERY_STREAMS_MODE VideoQueryStreamsMode;
|
||
|
VIDEO_QUERY_STREAMS_PARAMETERS VideoQueryStreamsParameters;
|
||
|
DWORD ReturnedDataLength;
|
||
|
BOOL bDDrawEnabled=TRUE;
|
||
|
|
||
|
// We're not going to bother to support accelerated DirectDraw on
|
||
|
// those S3s that can't support memory-mapped I/O, simply because
|
||
|
// they're old cards and it's not worth the effort. We also
|
||
|
// require DIRECT_ACCESS to the frame buffer.
|
||
|
//
|
||
|
// We also don't support 864/964 cards because writing to the frame
|
||
|
// buffer can hang the entire system if an accelerated operation is
|
||
|
// going on at the same time.
|
||
|
//
|
||
|
// The 765 (Trio64V+) has a bug such that writing to the frame
|
||
|
// buffer during an accelerator operation may cause a hang if
|
||
|
// you do the write soon enough after starting the blt. (There is
|
||
|
// a small window of opportunity.) On UP machines, the context
|
||
|
// switch time seems to be enough to avoid the problem. However,
|
||
|
// on MP machines, we'll have to disable direct draw.
|
||
|
//
|
||
|
// NOTE: We can identify the 765 since it is the only chip with
|
||
|
// the CAPS_STREAMS_CAPABLE flag.
|
||
|
|
||
|
if (ppdev->flCaps & CAPS_STREAMS_CAPABLE)
|
||
|
{
|
||
|
DWORD numProcessors;
|
||
|
|
||
|
if (EngQuerySystemAttribute(EngNumberOfProcessors, &numProcessors))
|
||
|
{
|
||
|
if (numProcessors != 1)
|
||
|
{
|
||
|
DISPDBG((1, "Disabling DDraw for MP 765 box.\n"));
|
||
|
bDDrawEnabled = FALSE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DISPDBG((1, "Can't determine number of processors, so play it "
|
||
|
"safe and disable DDraw for 765.\n"));
|
||
|
|
||
|
bDDrawEnabled = FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// The stretch and YUV bltter capabilities of the S3 868 and 968 were
|
||
|
// disabled to account for bug 135541.
|
||
|
|
||
|
ppdev->flCaps &= ~CAPS_PIXEL_FORMATTER;
|
||
|
|
||
|
if ((ppdev->flCaps & CAPS_NEW_MMIO) &&
|
||
|
!(ppdev->flCaps & CAPS_NO_DIRECT_ACCESS) &&
|
||
|
(bDDrawEnabled))
|
||
|
{
|
||
|
pjIoBase = ppdev->pjIoBase;
|
||
|
|
||
|
// We have to preserve the contents of register 0x69 on the S3's page
|
||
|
// flip:
|
||
|
|
||
|
ACQUIRE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
|
||
|
OUTP(pjIoBase, CRTC_INDEX, 0x69);
|
||
|
ppdev->ulExtendedSystemControl3Register_69
|
||
|
= (INP(pjIoBase, CRTC_DATA) & 0xe0) << 8;
|
||
|
|
||
|
RELEASE_CRTC_CRITICAL_SECTION(ppdev);
|
||
|
|
||
|
// Accurately measure the refresh rate for later:
|
||
|
|
||
|
vGetDisplayDuration(ppdev);
|
||
|
|
||
|
if (ppdev->flCaps & CAPS_STREAMS_CAPABLE)
|
||
|
{
|
||
|
// Query the miniport to get the correct streams parameters
|
||
|
// for this mode:
|
||
|
|
||
|
VideoQueryStreamsMode.ScreenWidth = ppdev->cxScreen;
|
||
|
VideoQueryStreamsMode.BitsPerPel = ppdev->cBitsPerPel;
|
||
|
VideoQueryStreamsMode.RefreshRate = ppdev->ulRefreshRate;
|
||
|
|
||
|
if (EngDeviceIoControl(ppdev->hDriver,
|
||
|
IOCTL_VIDEO_S3_QUERY_STREAMS_PARAMETERS,
|
||
|
&VideoQueryStreamsMode,
|
||
|
sizeof(VideoQueryStreamsMode),
|
||
|
&VideoQueryStreamsParameters,
|
||
|
sizeof(VideoQueryStreamsParameters),
|
||
|
&ReturnedDataLength))
|
||
|
{
|
||
|
DISPDBG((0, "Miniport reported no streams parameters"));
|
||
|
|
||
|
ppdev->flCaps &= ~CAPS_STREAMS_CAPABLE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ppdev->ulMinOverlayStretch
|
||
|
= VideoQueryStreamsParameters.MinOverlayStretch;
|
||
|
ppdev->ulFifoValue
|
||
|
= VideoQueryStreamsParameters.FifoValue;
|
||
|
|
||
|
DISPDBG((0, "Refresh rate: %li Minimum overlay stretch: %li.%03li Fifo value: %lx",
|
||
|
ppdev->ulRefreshRate,
|
||
|
ppdev->ulMinOverlayStretch / 1000,
|
||
|
ppdev->ulMinOverlayStretch % 1000,
|
||
|
ppdev->ulFifoValue));
|
||
|
}
|
||
|
}
|
||
|
else if (ppdev->flCaps & CAPS_PIXEL_FORMATTER)
|
||
|
{
|
||
|
// The pixel formatter doesn't work at 24bpp:
|
||
|
|
||
|
if (ppdev->iBitmapFormat != BMF_24BPP)
|
||
|
{
|
||
|
// We'll need a pixel-high scratch area to work around a
|
||
|
// hardware bug for thin stretches:
|
||
|
|
||
|
ppdev->pdsurfVideoEngineScratch = pVidMemAllocate(ppdev,
|
||
|
ppdev->cxMemory,
|
||
|
1);
|
||
|
if (ppdev->pdsurfVideoEngineScratch)
|
||
|
{
|
||
|
if (ppdev->cyMemory * ppdev->lDelta <= 0x100000)
|
||
|
ppdev->dwVEstep = 0x00040004; // If 1MB, 4 bytes/write
|
||
|
else
|
||
|
ppdev->dwVEstep = 0x00080008; // If 2MB, 8 bytes/write
|
||
|
|
||
|
ppdev->flCaps |= CAPS_PIXEL_FORMATTER;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return(TRUE);
|
||
|
}
|
||
|
|
||
|
/******************************Public*Routine******************************\
|
||
|
* VOID vDisableDirectDraw
|
||
|
*
|
||
|
* This function is called by enable.c when the driver is shutting down.
|
||
|
*
|
||
|
\**************************************************************************/
|
||
|
|
||
|
VOID vDisableDirectDraw(
|
||
|
PDEV* ppdev)
|
||
|
{
|
||
|
if (ppdev->pdsurfVideoEngineScratch)
|
||
|
{
|
||
|
vVidMemFree(ppdev->pdsurfVideoEngineScratch);
|
||
|
}
|
||
|
}
|