/*==========================================================================; * * Copyright (C) 1995 Microsoft Corporation. All Rights Reserved. * * File: vwport.c * Content: Direct3D viewport functions * ***************************************************************************/ #include "pch.cpp" #pragma hdrstop /* * Create an api for the Direct3DViewport object */ #include "d3dfei.h" #include "drawprim.hpp" //--------------------------------------------------------------------- // Update pre-computed constants related to viewport // // This functions should be called every time the viewport parameters are // changed // // Notes: // 1. scaleY and offsetY are computed to flip Y axes from up to down. // 2. Mclip matrix is computed multiplied by Mshift matrix // const D3DVALUE SMALL_NUMBER = 0.000001f; // Maximum number of clear rectangles considered legal. // This limit is set by NT kernel for Clear2 callback const DWORD MAX_CLEAR_RECTS = 0x1000; void UpdateViewportCache(LPDIRECT3DDEVICEI device, D3DVIEWPORT7 *data) { #if DBG // Bail if we are going to cause any divide by zero exceptions. // The likely reason is that we have a bogus viewport set by // TLVertex execute buffer app. if (data->dwWidth == 0 || data->dwHeight == 0) { D3D_ERR("Viewport width or height is zero"); throw DDERR_INVALIDPARAMS; } if (data->dvMaxZ < 0 || data->dvMinZ < 0 || data->dvMaxZ > 1 || data->dvMinZ > 1) { D3D_ERR("dvMaxZ and dvMinZ should be between 0 and 1"); throw DDERR_INVALIDPARAMS; } if (data->dvMaxZ < data->dvMinZ) { D3D_ERR("dvMaxZ should not be smaller than dvMinZ"); throw DDERR_INVALIDPARAMS; } #endif // DBG const D3DVALUE eps = 0.001f; if (data->dvMaxZ - data->dvMinZ < eps) { // When we clip, we transform vertices from the screen space to the // clipping space. With the above condition it is impossible. So we do // a little hack here by setting dvMinZ and dvMaxZ to different values if (data->dvMaxZ >= 0.5f) data->dvMinZ = data->dvMaxZ - eps; else data->dvMaxZ = data->dvMinZ + eps; } D3DFE_VIEWPORTCACHE *cache = &device->vcache; cache->dvX = D3DVAL(data->dwX); cache->dvY = D3DVAL(data->dwY); cache->dvWidth = D3DVAL(data->dwWidth); cache->dvHeight = D3DVAL(data->dwHeight); cache->scaleX = cache->dvWidth; cache->scaleY = - cache->dvHeight; cache->scaleZ = D3DVAL(data->dvMaxZ - data->dvMinZ); cache->offsetX = cache->dvX; cache->offsetY = cache->dvY + cache->dvHeight; cache->offsetZ = D3DVAL(data->dvMinZ); // Small offset is added to prevent generation of negative screen // coordinates (this could happen because of precision errors). cache->offsetX += SMALL_NUMBER; cache->offsetY += SMALL_NUMBER; cache->scaleXi = D3DVAL(1) / cache->scaleX; cache->scaleYi = D3DVAL(1) / cache->scaleY; cache->scaleZi = D3DVAL(1) / cache->scaleZ; cache->minX = cache->dvX; cache->maxX = cache->dvX + cache->dvWidth; cache->minY = cache->dvY; cache->maxY = cache->dvY + cache->dvHeight; cache->minXi = FTOI(cache->minX); cache->maxXi = FTOI(cache->maxX); cache->minYi = FTOI(cache->minY); cache->maxYi = FTOI(cache->maxY); if (device->dwDeviceFlags & D3DDEV_GUARDBAND) { LPD3DHAL_D3DEXTENDEDCAPS lpCaps = device->lpD3DExtendedCaps; // Because we clip by guard band window we have to use its extents cache->minXgb = lpCaps->dvGuardBandLeft; cache->maxXgb = lpCaps->dvGuardBandRight; cache->minYgb = lpCaps->dvGuardBandTop; cache->maxYgb = lpCaps->dvGuardBandBottom; D3DVALUE w = 2.0f / cache->dvWidth; D3DVALUE h = 2.0f / cache->dvHeight; D3DVALUE ax1 = -(lpCaps->dvGuardBandLeft - cache->dvX) * w + 1.0f; D3DVALUE ax2 = (lpCaps->dvGuardBandRight - cache->dvX) * w - 1.0f; D3DVALUE ay1 = (lpCaps->dvGuardBandBottom - cache->dvY) * h - 1.0f; D3DVALUE ay2 = -(lpCaps->dvGuardBandTop - cache->dvY) * h + 1.0f; cache->gb11 = 2.0f / (ax1 + ax2); cache->gb41 = cache->gb11 * (ax1 - 1.0f) * 0.5f; cache->gb22 = 2.0f / (ay1 + ay2); cache->gb42 = cache->gb22 * (ay1 - 1.0f) * 0.5f; cache->Kgbx1 = 0.5f * (1.0f - ax1); cache->Kgbx2 = 0.5f * (1.0f + ax2); cache->Kgby1 = 0.5f * (1.0f - ay1); cache->Kgby2 = 0.5f * (1.0f + ay2); } else { cache->minXgb = cache->minX; cache->maxXgb = cache->maxX; cache->minYgb = cache->minY; cache->maxYgb = cache->maxY; } } //--------------------------------------------------------------------- DWORD ProcessRects(LPDIRECT3DDEVICEI pDevI, DWORD dwCount, LPD3DRECT rects) { RECT vwport; DWORD i,j; /* * Rip through the rects and validate that they * are within the viewport. */ if(dwCount == 0 && rects == NULL) { dwCount = 1; } #if DBG else if(rects == NULL) { D3D_ERR("invalid clear rectangle parameter rects == NULL"); throw DDERR_INVALIDPARAMS; } #endif if (dwCount > pDevI->clrCount) { if (D3DRealloc((void**)&pDevI->clrRects, dwCount * sizeof(D3DRECT)) != DD_OK) { pDevI->clrCount = 0; pDevI->clrRects = NULL; D3D_ERR("failed to allocate space for rects"); throw DDERR_OUTOFMEMORY; } } pDevI->clrCount = dwCount; // If nothing is specified, assume the viewport needs to be cleared if (!rects) { pDevI->clrRects[0].x1 = pDevI->m_Viewport.dwX; pDevI->clrRects[0].y1 = pDevI->m_Viewport.dwY; pDevI->clrRects[0].x2 = pDevI->m_Viewport.dwX + pDevI->m_Viewport.dwWidth; pDevI->clrRects[0].y2 = pDevI->m_Viewport.dwY + pDevI->m_Viewport.dwHeight; return 1; } else { vwport.left = pDevI->m_Viewport.dwX; vwport.top = pDevI->m_Viewport.dwY; vwport.right = pDevI->m_Viewport.dwX + pDevI->m_Viewport.dwWidth; vwport.bottom = pDevI->m_Viewport.dwY + pDevI->m_Viewport.dwHeight; j=0; for (i = 0; i < dwCount; i++) { if (IntersectRect((LPRECT)(pDevI->clrRects + j), &vwport, (LPRECT)(rects + i))) j++; } return j; } } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::SetViewportI" void DIRECT3DDEVICEI::SetViewportI(LPD3DVIEWPORT7 lpData) { // We have to check parameters here, because viewport could be changed // after creating a state set DWORD uSurfWidth,uSurfHeight; LPDIRECTDRAWSURFACE lpDDS = this->lpDDSTarget; uSurfWidth= ((LPDDRAWI_DDRAWSURFACE_INT) lpDDS)->lpLcl->lpGbl->wWidth; uSurfHeight= ((LPDDRAWI_DDRAWSURFACE_INT) lpDDS)->lpLcl->lpGbl->wHeight; if (lpData->dwX > uSurfWidth || lpData->dwY > uSurfHeight || lpData->dwX + lpData->dwWidth > uSurfWidth || lpData->dwY + lpData->dwHeight > uSurfHeight) { D3D_ERR("Viewport outside the render target surface"); throw DDERR_INVALIDPARAMS; } this->m_Viewport = *lpData; // Update front-end data UpdateViewportCache(this, &this->m_Viewport); if (!(this->dwFEFlags & D3DFE_EXECUTESTATEMODE)) { // Download viewport data this->UpdateDrvViewInfo(&this->m_Viewport); } } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::SetViewport" HRESULT D3DAPI DIRECT3DDEVICEI::SetViewport(LPD3DVIEWPORT7 lpData) { if (!VALID_D3DVIEWPORT_PTR(lpData)) { D3D_ERR( "Invalid D3DVIEWPORT7 pointer" ); return DDERR_INVALIDPARAMS; } try { CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock. if (this->dwFEFlags & D3DFE_RECORDSTATEMODE) m_pStateSets->InsertViewport(lpData); else SetViewportI(lpData); return D3D_OK; } catch(HRESULT ret) { return ret; } } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::GetViewport" HRESULT D3DAPI DIRECT3DDEVICEI::GetViewport(LPD3DVIEWPORT7 lpData) { CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock. if (!VALID_D3DVIEWPORT_PTR(lpData)) { D3D_ERR( "Invalid D3DVIEWPORT2 pointer" ); return DDERR_INVALIDPARAMS; } *lpData = this->m_Viewport; return (D3D_OK); } //--------------------------------------------------------------------- #undef DPF_MODNAME #define DPF_MODNAME "DIRECT3DDEVICEI::Clear" extern void BltFillRects(LPDIRECT3DDEVICEI, DWORD, LPD3DRECT, D3DCOLOR); extern void BltFillZRects(LPDIRECT3DDEVICEI, unsigned long,DWORD, LPD3DRECT, DWORD); #define bDoRGBClear ((dwFlags & D3DCLEAR_TARGET)!=0) #define bDoZClear ((dwFlags & D3DCLEAR_ZBUFFER)!=0) #define bDoStencilClear ((dwFlags & D3DCLEAR_STENCIL)!=0) HRESULT D3DAPI DIRECT3DDEVICEI::Clear(DWORD dwCount, LPD3DRECT rects, DWORD dwFlags, D3DCOLOR dwColor, D3DVALUE dvZ, DWORD dwStencil) { #if DBG if (IsBadWritePtr(rects, dwCount * sizeof(D3DRECT))) { D3D_ERR( "Invalid rects pointer" ); return DDERR_INVALIDPARAMS; } #endif try { HRESULT err; LPDDPIXELFORMAT pZPixFmt=NULL; CLockD3D lockObject(DPF_MODNAME, REMIND("")); // Takes D3D lock. if (dwCount > MAX_CLEAR_RECTS) { D3D_ERR("Cannot support more than 64K rectangles"); return DDERR_INVALIDPARAMS; } if (!(lpD3DHALGlobalDriverData->hwCaps.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR)) { if (bDoStencilClear||bDoZClear) { if(lpDDSZBuffer==NULL) { // unlike Clear(), specifying a Zbuffer-clearing flag without a zbuffer will // be considered an error #if DBG if(bDoZClear) { D3D_ERR("Invalid flag D3DCLEAR_ZBUFFER: no zbuffer is associated with device"); } if(bDoStencilClear) { D3D_ERR("Invalid flag D3DCLEAR_STENCIL: no zbuffer is associated with device"); } #endif return D3DERR_ZBUFFER_NOTPRESENT; } pZPixFmt=&((LPDDRAWI_DDRAWSURFACE_INT) lpDDSZBuffer)->lpLcl->lpGbl->ddpfSurface; if(bDoStencilClear) { if(!(pZPixFmt->dwFlags & DDPF_STENCILBUFFER)) { D3D_ERR("Invalid flag D3DCLEAR_STENCIL; current zbuffer's pixel format doesnt support stencil bits"); return D3DERR_STENCILBUFFER_NOTPRESENT; } } } } if (!(dwFlags & (D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL))) { D3D_ERR("No valid flags passed to Clear"); return DDERR_INVALIDPARAMS; } // bad clear values just cause wacky results but no crashes, so OK to allow in retail bld DDASSERT(!bDoZClear || ((dvZ>=0.0) && (dvZ<=1.0))); DDASSERT(!bDoStencilClear || !pZPixFmt || (dwStencil <= (DWORD)((1<dwStencilBitDepth)-1))); dwCount = ProcessRects(this, dwCount, rects); // Call DDI specific Clear routine ClearI(dwFlags, dwCount, dwColor, dvZ, dwStencil); return D3D_OK; } catch(HRESULT ret) { return ret; } } void DIRECT3DDEVICEI::ClearI(DWORD dwFlags, DWORD clrCount, D3DCOLOR dwColor, D3DVALUE dvZ, DWORD dwStencil) { HRESULT err; // Flush any outstanding geometry to put framebuffer/Zbuffer in a known state for Clears that // don't use tris (i.e. HAL Clears and Blts). Note this doesn't work for tiled architectures // outside of Begin/EndScene, this will be fixed later if ((err = FlushStates()) != D3D_OK) { D3D_ERR("Error trying to render batched commands in D3DFE_Clear2"); throw err; } if (lpD3DHALCallbacks3->Clear2) { // Clear2 HAL Callback exists D3DHAL_CLEAR2DATA Clear2Data; Clear2Data.dwhContext = dwhContext; Clear2Data.dwFlags = dwFlags; // Here I will follow the ClearData.dwFillColor convention that // color word is raw 32bit ARGB, unadjusted for surface bit depth Clear2Data.dwFillColor = dwColor; // depth/stencil values both passed straight from user args Clear2Data.dvFillDepth = dvZ; Clear2Data.dwFillStencil= dwStencil; Clear2Data.lpRects = clrRects; Clear2Data.dwNumRects = clrCount; Clear2Data.ddrval = D3D_OK; #ifndef WIN95 if((err = CheckContextSurface(this)) != D3D_OK) { throw err; } #endif CALL_HAL3ONLY(err, this, Clear2, &Clear2Data); if (err != DDHAL_DRIVER_HANDLED) { throw DDERR_UNSUPPORTED; } else if (Clear2Data.ddrval != DD_OK) { throw Clear2Data.ddrval; } else return; } if (lpD3DHALGlobalDriverData->hwCaps.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR) { if (bDoStencilClear) { D3D_ERR("Invalid flag D3DCLEAR_STENCIL: this ZBUFFERLESSHSR device doesn't support Clear2()"); throw D3DERR_ZBUFFER_NOTPRESENT; } if (bDoZClear) { if (!(lpD3DHALCallbacks2->Clear) || (dvZ!=1.0)) { D3D_WARN(3,"Ignoring D3DCLEAR_ZBUFFER since this ZBUFFERLESSHSR device doesn't even support Clear() or Z!=1"); dwFlags &= ~(D3DCLEAR_ZBUFFER); } } } LPDDPIXELFORMAT pZPixFmt; if (NULL != lpDDSZBuffer) { pZPixFmt = &((LPDDRAWI_DDRAWSURFACE_INT) lpDDSZBuffer)->lpLcl->lpGbl->ddpfSurface; } else { pZPixFmt = NULL; } if (lpD3DHALCallbacks2->Clear) { if(bDoZClear || bDoStencilClear) { if((pZPixFmt!=NULL) && //PowerVR need no Zbuffer (DDPF_STENCILBUFFER & pZPixFmt->dwFlags) ) { // if surface has stencil bits, must verify either Clear2 callback exists or // we're using SW rasterizers (which require the special WriteMask DDHEL blt) // This case should not be hit since we check right at the // driver initialization time if the driver doesnt report Clear2 // yet it supports stencils if(((LPDDRAWI_DDRAWSURFACE_INT)lpDDSZBuffer)->lpLcl->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) { goto Emulateclear; } else { D3D_ERR("Driver HAL doesn't provide Clear2 callback, cannot use Clear2 with HW stencil surfaces"); throw DDERR_INVALIDPIXELFORMAT; } } // if Clear2 callback doesnt exist and it's a z-only surface and not doing zclear to // non-max value then Clear2 is attempting to do no more than Clear could do, so it's // safe to call Clear() instead of Clear2(), which will take advantage of older // drivers that implement Clear but not Clear2 dwFlags &= ~D3DCLEAR_STENCIL; // Device cannot do stencil } D3DHAL_CLEARDATA ClearData; if (bDoZClear && dvZ != 1.0) { ClearData.dwFlags = dwFlags & ~D3DCLEAR_ZBUFFER; dwFlags = D3DCLEAR_ZBUFFER; } else { ClearData.dwFlags = dwFlags; dwFlags = 0; } if (ClearData.dwFlags) { ClearData.dwhContext = dwhContext; // Here I will follow the ClearData.dwFillColor convention that // color word is raw 32bit ARGB, unadjusted for surface bit depth ClearData.dwFillColor = dwColor; // must clear to 0xffffffff because legacy drivers expect this ClearData.dwFillDepth = 0xffffffff; ClearData.lpRects = clrRects; ClearData.dwNumRects = clrCount; ClearData.ddrval = D3D_OK; #ifndef WIN95 if((err = CheckContextSurface(this)) != D3D_OK) { throw err; } #endif CALL_HAL2ONLY(err, this, Clear, &ClearData); if (err != DDHAL_DRIVER_HANDLED) { throw DDERR_UNSUPPORTED; } } } Emulateclear: // Fall back to Emulation using Blt if(bDoRGBClear) { BltFillRects(this, clrCount, clrRects, dwColor); //ok to not return possible errors from Blt? } if ((bDoZClear || bDoStencilClear) && NULL != pZPixFmt) { DWORD dwZbufferClearValue=0; DWORD dwZbufferClearMask=0; DDASSERT(pZPixFmt->dwZBufferBitDepth<=32); DDASSERT(pZPixFmt->dwStencilBitDepth<32); DDASSERT(pZPixFmt->dwZBitMask!=0x0); DDASSERT((0xFFFFFFFF == (pZPixFmt->dwZBitMask | pZPixFmt->dwStencilBitMask)) | ((DWORD)((1<dwZBufferBitDepth)-1) == (pZPixFmt->dwZBitMask | pZPixFmt->dwStencilBitMask))); DDASSERT(0==(pZPixFmt->dwZBitMask & pZPixFmt->dwStencilBitMask)); if(bDoZClear) { dwZbufferClearMask = pZPixFmt->dwZBitMask; // special case the common cases if(dvZ==1.0) { dwZbufferClearValue=pZPixFmt->dwZBitMask; } else if(dvZ > 0.0) { dwZbufferClearValue=((DWORD)((dvZ*(pZPixFmt->dwZBitMask >> zmask_shift))+0.5)) << zmask_shift; } } if(bDoStencilClear) { DDASSERT(pZPixFmt->dwStencilBitMask!=0x0); DDASSERT(pZPixFmt->dwFlags & DDPF_STENCILBUFFER); dwZbufferClearMask |= pZPixFmt->dwStencilBitMask; // special case the common case if(dwStencil!=0) { dwZbufferClearValue |=(dwStencil << stencilmask_shift) & pZPixFmt->dwStencilBitMask; } } if (dwZbufferClearMask == (pZPixFmt->dwStencilBitMask | pZPixFmt->dwZBitMask)) { // do Stencil & Z Blt together, using regular DepthFill blt which will be faster // than the writemask blt because its write-only, instead of read-modify-write dwZbufferClearMask = 0; } BltFillZRects(this, dwZbufferClearValue, clrCount, clrRects, dwZbufferClearMask); } }