/******************************Module*Header*******************************\ * Module Name: d3d.cxx * * Contains all of GDI's private Direct3D APIs. * * Created: 04-Jun-1996 * Author: Drew Bliss [drewb] * * Copyright (c) 1995-1999 Microsoft Corporation * \**************************************************************************/ #include "precomp.hxx" // Minimum size of DrawPrimitive buffer associated with a context. #define MIN_PRIM_BUFFER_SIZE (1 << 14) // Maximum #define MAX_PRIM_BUFFER_SIZE (1 << 20) // Default #define DEF_PRIM_BUFFER_SIZE (1 << 16) // Alignment requirement for the DrawPrim buffer. Must be a power of two. #define DP_BUFFER_ALIGN 32 // Maximum number of vertices considered legal. #define MAX_VERTEX_COUNT 0x10000 // Maximum number of indices considered legal. #define MAX_INDEX_COUNT 0x80000 // Maximum number of clear rectangles considered legal. #define MAX_CLEAR_RECTS 0x1000 // Maximum number of state changes per RenderState call #define MAX_STATE_CHANGE (D3DRENDERSTATE_STIPPLEPATTERN31+1) #ifdef D3D_ENTRIES #define D3D_ENTRY(s) WARNING(s) #else #define D3D_ENTRY(s) #endif // Simple structure for managing DD surfaces struct D3D_SURFACE { HANDLE h; BOOL bOptional; EDD_SURFACE* peSurf; PDD_SURFACE_LOCAL pLcl; }; #define INIT_D3DSURFACE(SurfaceArray, Count) \ for (int i = 0 ; i < (Count) ; i++) { \ (SurfaceArray)[i].peSurf = NULL; \ } #define CLEANUP_D3DSURFACE(SurfaceArray, Count) \ for (int i = 0 ; i < (Count) ; i++) { \ if ((SurfaceArray)[i].peSurf) { \ DEC_EXCLUSIVE_REF_CNT((SurfaceArray)[i].peSurf); \ } \ } // Convenience macro for parameter validation. ProbeForWrite does // all the checks that ProbeForRead does in addition to write testing // so it serves as a read/write check. // Assumes DWORD alignment. #define CAPTURE_RW_STRUCT(ptr, type) \ (ProbeForWrite(ptr, sizeof(type), sizeof(DWORD)), *(ptr)) #define CAPTURE_RD_STRUCT(ptr, type) \ (ProbeForRead(ptr, sizeof(type), sizeof(DWORD)), *(ptr)) /******************************Public*Routine******************************\ * * D3dLockSurfaces * * Walks the array of surfaces, locking any with non-zero handles * * 1. This routine must be called with the HmgrSemaphore acquired. * 2. INIT_D3DSURFACE must be called for pSurf parameter. * * History: * Fri Jun 14 14:26:06 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL D3dLockSurfaces(int n, D3D_SURFACE *pSurf) { while (n-- > 0) { if (pSurf->h != NULL) { pSurf->peSurf = (EDD_SURFACE *)DdHmgLock((HDD_OBJ)pSurf->h, DD_SURFACE_TYPE, TRUE); if (pSurf->peSurf == NULL) { WARNING("D3dLockSurfaces unable to lock buffer"); return FALSE; } if (pSurf->peSurf->bLost) { WARNING("D3dLockSurfaces unable to lock buffer Surface is Lost"); return FALSE; } pSurf->pLcl = pSurf->peSurf; } else if (!pSurf->bOptional) { WARNING("D3dLockSurfaces: NULL for mandatory surface"); return FALSE; } else { pSurf->pLcl = NULL; } pSurf++; } return TRUE; } /******************************Public*Routine******************************\ * * D3dSetup * * Prepares the system for a call to a D3D driver * * History: * Tue Jun 04 17:09:23 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ BOOL D3dSetup(EDD_DIRECTDRAW_GLOBAL* peDdGlobal, KFLOATING_SAVE* pfsState) { ASSERTGDI(peDdGlobal != NULL, "D3dSetup on NULL global\n"); if (!NT_SUCCESS(KeSaveFloatingPointState(pfsState))) { WARNING("D3dSetup: Unable to save FP state\n"); return FALSE; } DxEngLockHdev(peDdGlobal->hdev); return TRUE; } /******************************Public*Routine******************************\ * * D3dCleanup * * Cleans up after a D3D driver calls * * History: * Tue Jun 04 17:10:21 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ void D3dCleanup(EDD_DIRECTDRAW_GLOBAL* peDdGlobal, KFLOATING_SAVE* pfsState) { DxEngUnlockHdev(peDdGlobal->hdev); KeRestoreFloatingPointState(pfsState); } /******************************Public*Routine******************************\ * * D3dLockContext * * Prepares the system for a call to a D3D driver with a driver context * * This routine must be called with the HmgrSemaphore acquired * * History: * Tue Jun 04 17:09:23 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ D3DNTHAL_CONTEXT *D3dLockContext(KFLOATING_SAVE *pfsState, ULONG_PTR *pdwhContext) { D3DNTHAL_CONTEXT *pdhctx; pdhctx = (D3DNTHAL_CONTEXT *)DdHmgLock((HDD_OBJ)*pdwhContext, D3D_HANDLE_TYPE, TRUE); // release handle manager lock. DdHmgReleaseHmgrSemaphore(); if (pdhctx == NULL) { WARNING("D3dLockContext unable to lock context"); return NULL; } // Before we access inside D3DCONTEXT.peDdGlobal, hold shared devlock, // since peDdGlobal can be changed during video mode change. DxEngLockShareSem(); if (pdhctx->dwType != DNHO_CONTEXT) { WARNING("D3dLockContext: Valid handle not a context"); goto Error_LockContext; } if (pdhctx->peDdGlobal == NULL) { WARNING("D3dLockContext: Call on disabled object"); goto Error_LockContext; } ASSERTGDI(pdhctx->peDdGlobal->Miscellaneous2CallBacks.CreateSurfaceEx, "D3dLockContext: No CreateSurfaceEx callback"); if (D3dSetup(pdhctx->peDdGlobal, pfsState)) { *pdwhContext = pdhctx->dwDriver; // keep holding share lock, will be released at D3dUnlockContext. return pdhctx; } Error_LockContext: DEC_EXCLUSIVE_REF_CNT(pdhctx); DxEngUnlockShareSem(); return NULL; } /******************************Public*Routine******************************\ * * D3dUnlockContext * * Cleans up after a D3D driver calls with D3D context * * History: * Tue Apr 10 16:15:00 2001 -by- Hideyuki Nagase [hideyukn] * Created * \**************************************************************************/ VOID D3dUnlockContext(D3DNTHAL_CONTEXT *pdhctx, KFLOATING_SAVE *pfsState) { // // Release devlock, and restore floating point state. // D3dCleanup(pdhctx->peDdGlobal, pfsState); // // Release share devlock. // DxEngUnlockShareSem(); // // Unlock context // DEC_EXCLUSIVE_REF_CNT(pdhctx); } /******************************Public*Routine******************************\ * * D3dDeleteHandle * * Cleans up D3D driver context wrapper objects * * History: * Tue Jun 11 17:42:25 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ DWORD D3dDeleteHandle(HANDLE hD3dHandle, ULONG_PTR dwContext, BOOL *pbRemoved, HRESULT *phr) { D3DNTHAL_OBJECT *pdhobj; D3DNTHAL_CONTEXT *pdhctx; D3DNTHAL_CONTEXTDESTROYDATA dcdd; D3DNTHAL_TEXTUREDESTROYDATA dtdd; KFLOATING_SAVE fsState; DWORD dwRet = DDHAL_DRIVER_HANDLED; // Lock handle first. pdhobj = (D3DNTHAL_OBJECT *) DdHmgLock((HDD_OBJ)hD3dHandle, D3D_HANDLE_TYPE, FALSE); if (pdhobj == NULL) { if (pbRemoved != NULL) { *pbRemoved = FALSE; } *phr = DDERR_INVALIDOBJECT; return (dwRet); } // Before we access inside D3DCONTEXT.peDdGlobal, hold shared devlock, // since peDdGlobal can be changed during video mode change. DxEngLockShareSem(); // Sundowndx: Needs to pass in cjBuffer as SIZE_T inorder to compiler in 64bit SIZE_T cjBuffer = 0; if (!D3dSetup(pdhobj->peDdGlobal, &fsState)) { DxEngUnlockShareSem(); DEC_EXCLUSIVE_REF_CNT(pdhobj); *phr = DDERR_OUTOFMEMORY; return (dwRet); } switch(pdhobj->dwType) { case DNHO_CONTEXT: // Clean up DrawPrimitive buffer. pdhctx = (D3DNTHAL_CONTEXT *)pdhobj; MmUnsecureVirtualMemory(pdhctx->hBufSecure); ZwFreeVirtualMemory(NtCurrentProcess(), &pdhctx->pvBufferAlloc, &cjBuffer, MEM_RELEASE); // Sundowndx: need to look at the d3d structure, truncate it for now pdhctx->cjBuffer = (ULONG)cjBuffer; // Call driver. dcdd.dwhContext = pdhobj->dwDriver; dwRet = pdhobj->peDdGlobal->D3dCallBacks.ContextDestroy(&dcdd); *phr = dcdd.ddrval; break; case DNHO_TEXTURE: dtdd.dwhContext = dwContext; dtdd.dwHandle = pdhobj->dwDriver; dwRet = pdhobj->peDdGlobal->D3dCallBacks.TextureDestroy(&dtdd); *phr = dtdd.ddrval; break; } // Now Remove handle from handle manager. PVOID pv = DdHmgRemoveObject((HDD_OBJ)hD3dHandle, 1, 0, TRUE, D3D_HANDLE_TYPE); // This shouldn't fail, because above DdHmgLock is succeeded. ASSERTGDI(pv,"DdHmgRemoveObject failed D3dDeleteHandle"); // Release devlock and restore floating point state. D3dCleanup(pdhobj->peDdGlobal, &fsState); // Release share lock DxEngUnlockShareSem(); // If this is last reference to DdGlobal (= GDI's PDEV), this // will release it, so we can't call it while devlock is hold (which // means between D3dSetup ~ D3dCleanup). vDdDecrementReferenceCount(pdhobj->peDdGlobal); // Delete the object. DdFreeObject(pdhobj, D3D_HANDLE_TYPE); if (pbRemoved != NULL) { *pbRemoved = TRUE; } return dwRet; } /******************************Public*Routine******************************\ * * D3D_SIMPLE_COPY_WITH_CONTEXT * * Macro which creates a thunk function for D3D entry points which * have only simple copy arguments in, a context to dereference and * only a simple copy out. * * D3D_SIMPLE_DECL can be defined to add declarations. * By default it's empty. * D3D_SIMPLE_SETUP can be defined to add more setup code. * By default it's empty. * D3D_SIMPLE_WRITEBACK can be defined to add more writeback code. * By default it's empty. * D3D_SIMPLE_CALLBACKS is defined to the callbacks structure to look in. * By default it's D3dCallBacks. * * History: * Fri Jun 14 14:12:54 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ #define D3D_SIMPLE_DECL #define D3D_SIMPLE_SETUP #define D3D_SIMPLE_WRITEBACK #define D3D_SIMPLE_CALLBACKS D3dCallBacks #define D3D_SIMPLE_COPY_WITH_CONTEXT(Name, Type) \ DWORD APIENTRY \ DxD3d##Name(LPD3DNTHAL_##Type##DATA pdata) \ { \ D3DNTHAL_##Type##DATA data; \ KFLOATING_SAVE fsState; \ DWORD dwRet; \ D3DNTHAL_CONTEXT* pdhctx; \ D3D_SIMPLE_DECL \ \ D3D_ENTRY("DxD3d" #Name); \ \ dwRet = DDHAL_DRIVER_NOTHANDLED; \ \ __try \ { \ data = CAPTURE_RW_STRUCT(pdata, D3DNTHAL_##Type##DATA); \ } \ __except(EXCEPTION_EXECUTE_HANDLER) \ { \ WARNING("DxD3d" #Name " unable to access argument"); \ return dwRet; \ } \ \ DdHmgAcquireHmgrSemaphore(); /* will be released inside D3dLockContext */ \ \ pdhctx = D3dLockContext(&fsState, &data.dwhContext); \ if (pdhctx == NULL) \ { \ return dwRet; \ } \ \ D3D_SIMPLE_SETUP \ \ if (pdhctx->peDdGlobal->bSuspended) \ { \ data.ddrval = DDERR_SURFACELOST; \ } \ else \ { \ if (pdhctx->peDdGlobal->D3D_SIMPLE_CALLBACKS.##Name) \ { \ dwRet = pdhctx->peDdGlobal->D3D_SIMPLE_CALLBACKS.##Name(&data);\ } \ else \ { \ WARNING("DxD3d" #Name " call not present!"); \ } \ } \ \ D3dUnlockContext(pdhctx, &fsState); \ \ __try \ { \ pdata->ddrval = data.ddrval; \ D3D_SIMPLE_WRITEBACK \ } \ __except(EXCEPTION_EXECUTE_HANDLER) \ { \ WARNING("DxD3d" #Name " unable to write back arguments"); \ } \ \ return dwRet; \ } /******************************Public*Routine******************************\ * * D3D_DELETE_HANDLE * * Macro for routines which destroy a handle-managed object. * * History: * Wed Oct 23 19:32:11 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ #define D3D_DELETE_HANDLE(Name, Type, Field) \ DWORD APIENTRY \ DxD3d##Name##Destroy(LPD3DNTHAL_##Type##DESTROYDATA pdata) \ { \ DWORD dwRet; \ HANDLE hD3dHandle; \ HRESULT hr; \ \ D3D_ENTRY("DxD3d" #Name "Destroy"); \ \ __try \ { \ ProbeForWrite(pdata, sizeof(D3DNTHAL_##Type##DESTROYDATA), \ sizeof(DWORD)); \ hD3dHandle = (HANDLE)pdata->Field; \ } \ __except(EXCEPTION_EXECUTE_HANDLER) \ { \ WARNING("DxD3d" #Name "Destroy unable to access argument"); \ return DDHAL_DRIVER_NOTHANDLED; \ } \ \ dwRet = D3dDeleteHandle(hD3dHandle, 0, (BOOL *)NULL, &hr); \ \ __try \ { \ pdata->ddrval = hr; \ } \ __except(EXCEPTION_EXECUTE_HANDLER) \ { \ WARNING("DxD3d" #Name "Destroy unable to write back arguments"); \ } \ \ return dwRet; \ } /******************************Public*Routine******************************\ * * DxD3dContextCreate * * History: * Tue Jun 04 13:05:18 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ DWORD APIENTRY DxD3dContextCreate(HANDLE hDirectDrawLocal, HANDLE hSurfColor, HANDLE hSurfZ, D3DNTHAL_CONTEXTCREATEI *pdcci) { LPD3DNTHAL_CONTEXTCREATEDATA pdccd; D3DNTHAL_CONTEXTCREATEDATA dccd; KFLOATING_SAVE fsState; DWORD dwRet; HANDLE hCleanup = 0; EDD_DIRECTDRAW_LOCAL* peDdLocal; EDD_LOCK_DIRECTDRAW eLockDd; EDD_DIRECTDRAW_GLOBAL* peDdGlobal; D3D_SURFACE dsurf[2]; NTSTATUS nts; SIZE_T cjBuffer; D3DNTHAL_CONTEXT* pdhctx = NULL; PVOID pvBuffer = NULL; PVOID pvBufRet = NULL; HANDLE hBufSecure = NULL; ULONG_PTR Interface; D3D_ENTRY("DxD3dContextCreate"); ASSERTGDI(FIELD_OFFSET(D3DNTHAL_CONTEXTCREATEI, pvBuffer) == sizeof(D3DNTHAL_CONTEXTCREATEDATA), "D3DNTHAL_CONTEXTCREATEI out of sync\n"); pdccd = (LPD3DNTHAL_CONTEXTCREATEDATA)pdcci; dwRet = DDHAL_DRIVER_NOTHANDLED; __try { ProbeForWrite(pdcci, sizeof(D3DNTHAL_CONTEXTCREATEI), sizeof(DWORD)); dccd.dwPID = pdccd->dwPID; dccd.dwhContext = pdccd->dwhContext; dccd.ddrval = pdccd->ddrval; cjBuffer = pdcci->cjBuffer; Interface = pdccd->dwhContext; } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxD3dContextCreate unable to access argument"); return dwRet; } if (cjBuffer == 0) { cjBuffer = DEF_PRIM_BUFFER_SIZE; } else if (cjBuffer < MIN_PRIM_BUFFER_SIZE || cjBuffer > MAX_PRIM_BUFFER_SIZE) { WARNING("DxD3dContextCreate: illegal prim buffer size"); return dwRet; } peDdLocal = eLockDd.peLock(hDirectDrawLocal); if (peDdLocal == NULL) { WARNING("DxD3dContextCreate unable to lock hDdLocal"); return dwRet; } peDdGlobal = peDdLocal->peDirectDrawGlobal; INIT_D3DSURFACE(dsurf, 2); dsurf[0].h = hSurfColor; dsurf[0].bOptional = FALSE; dsurf[1].h = hSurfZ; dsurf[1].bOptional = TRUE; DdHmgAcquireHmgrSemaphore(); if (!D3dLockSurfaces(2, dsurf)) { DdHmgReleaseHmgrSemaphore(); goto Exit_CreateContext; } DdHmgReleaseHmgrSemaphore(); pdhctx = (D3DNTHAL_CONTEXT *)DdHmgAlloc(sizeof(D3DNTHAL_CONTEXT), D3D_HANDLE_TYPE, HMGR_ALLOC_LOCK); if (pdhctx == NULL) { WARNING("DxD3dContextCreate unable to alloc handle"); goto Exit_CreateContext; } hCleanup = pdhctx->hHmgr; // Allocate and lock DrawPrimitive buffer space. We want the // buffer to be aligned on a large boundary so we alter the // pointer to return to be aligned. Note that we do not overallocate // since we don't want the extra alignment space to push the allocation // into another page. We're returning a size anyway so we can // get away with this transparently. nts = ZwAllocateVirtualMemory(NtCurrentProcess(), &pvBuffer, 0, &cjBuffer, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (!NT_SUCCESS(nts)) { goto Exit_CreateContext; } hBufSecure = MmSecureVirtualMemory(pvBuffer, cjBuffer, PAGE_READWRITE); if (hBufSecure == NULL) { goto Exit_CreateContext; } if (!D3dSetup(peDdGlobal, &fsState)) { goto Exit_CreateContext; } // CreateSurfaceEx callback must exist for DX7 or greater drivers if (!peDdGlobal->Miscellaneous2CallBacks.CreateSurfaceEx) { D3dCleanup(peDdGlobal, &fsState); goto Exit_CreateContext; } dccd.lpDDLcl = peDdLocal; dccd.lpDDS = dsurf[0].pLcl; dccd.lpDDSZ = dsurf[1].pLcl; if (peDdGlobal->D3dCallBacks.ContextCreate) { dwRet = peDdGlobal->D3dCallBacks.ContextCreate(&dccd); } else { WARNING("DxD3dContextCreate: ContextCreate callback not found"); } if (dwRet == DDHAL_DRIVER_HANDLED && dccd.ddrval == DD_OK) { // Create a wrapper for the handle and stash the DD global in it pdhctx->dwType = DNHO_CONTEXT; pdhctx->dwDriver = dccd.dwhContext; pdhctx->peDdGlobal = peDdGlobal; pdhctx->peDdLocal = peDdLocal; pdhctx->hSurfColor = hSurfColor; pdhctx->hSurfZ = hSurfZ; pdhctx->hBufSecure = hBufSecure; // Save the real pointer for freeing and return an aligned pointer // rather than the raw pointer. pdhctx->pvBufferAlloc = pvBuffer; pvBufRet = (PVOID)(((ULONG_PTR)pvBuffer+DP_BUFFER_ALIGN-1) & ~(DP_BUFFER_ALIGN-1)); pdhctx->pvBufferAligned = pvBufRet; pdhctx->cjBuffer = (ULONG)cjBuffer; // Subtract off any space used for alignment. cjBuffer -= (DWORD)((ULONG_PTR)pvBufRet-(ULONG_PTR)pvBuffer); // Save interface number pdhctx->Interface = Interface; // Null these values to deactivate cleanup code. hBufSecure = NULL; pvBuffer = NULL; hCleanup = NULL; dccd.dwhContext = (ULONG_PTR)pdhctx->hHmgr; /* Add reference to DirectDraw driver instance so that it won't go away during dynamic mode changes, until the object is deleted. */ vDdIncrementReferenceCount(pdhctx->peDdGlobal); } // We're done with pdhctx so unlock it DEC_EXCLUSIVE_REF_CNT(pdhctx); D3dCleanup(peDdGlobal, &fsState); __try { pdccd->dwhContext = dccd.dwhContext; pdccd->ddrval = dccd.ddrval; pdcci->pvBuffer = pvBufRet; pdcci->cjBuffer = (ULONG)cjBuffer; } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxD3dContextCreate unable to write back arguments"); /* This is an unlikely thing to occur so we don't bother cleaning up the handle here. It will be cleaned up by the process termination handle cleanup. */ dwRet = DDHAL_DRIVER_NOTHANDLED; } Exit_CreateContext: CLEANUP_D3DSURFACE(dsurf, 2); if (hBufSecure != NULL) { MmUnsecureVirtualMemory(hBufSecure); } if (pvBuffer != NULL) { // cjBuffer has to be zero to free memory. cjBuffer = 0; ZwFreeVirtualMemory(NtCurrentProcess(), &pvBuffer, &cjBuffer, MEM_RELEASE); } if (hCleanup != NULL) { DdHmgFree((HDD_OBJ)hCleanup); } return dwRet; } /******************************Public*Routine******************************\ * * DxD3dContextDestroy * * History: * Tue Jun 04 13:08:43 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ D3D_DELETE_HANDLE(Context, CONTEXT, dwhContext) /******************************Public*Routine******************************\ * * DxD3dContextDestroyAll * * History: * Tue Jun 04 13:09:04 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ DWORD APIENTRY DxD3dContextDestroyAll(LPD3DNTHAL_CONTEXTDESTROYALLDATA pdcdad) { DWORD dwRet; D3D_ENTRY("DxD3dContextDestroyAll"); __try { ProbeForWrite(pdcdad, sizeof(D3DNTHAL_CONTEXTDESTROYALLDATA), sizeof(DWORD)); } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxD3dContextDestroyAll unable to access argument"); return DDHAL_DRIVER_NOTHANDLED; } // Having the context wrappers be handle-based should // provide automatic process cleanup of driver contexts // If this function does need to be passed to the driver then // some way to get the proper DD global will need to be found // I don't think it's necessary, though dwRet = DDHAL_DRIVER_NOTHANDLED; __try { pdcdad->ddrval = DDERR_GENERIC; } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxD3dContextDestroyAll unable to write back arguments"); } return dwRet; } /******************************Public*Routine******************************\ * * DxD3dDrawPrimitives2 * * History: * Fri Jun 14 11:56:53 1996 -by- Drew Bliss [drewb] * Created * \**************************************************************************/ DWORD APIENTRY DxD3dDrawPrimitives2(HANDLE hCmdBuf, HANDLE hVBuf, LPD3DNTHAL_DRAWPRIMITIVES2DATA pded, FLATPTR* pfpVidMemCmd, DWORD* pdwSizeCmd, FLATPTR* pfpVidMemVtx, DWORD* pdwSizeVtx) { D3DNTHAL_DRAWPRIMITIVES2DATA ded; KFLOATING_SAVE fsState; DWORD dwRet; D3D_SURFACE dsurf[4]; HANDLE hSecure = NULL; D3DNTHAL_CONTEXT* pdhctx = NULL; D3D_ENTRY("DxD3dDrawPrimitives2"); dwRet = DDHAL_DRIVER_NOTHANDLED; __try { // // ProbeForRead and ProbeForWrite are fairly expensive. Moved the write probe // to the bottom but we could also do a ProbeForWriteStructure here if the // error checking needs to be the exact same semantics. // ded = ProbeAndReadStructure(pded, D3DNTHAL_DRAWPRIMITIVES2DATA); } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxD3dDrawPrimitives2 unable to access argument"); return dwRet; } // Initialize surface array. INIT_D3DSURFACE(dsurf, 4); DWORD Length = ded.dwVertexLength*ded.dwVertexSize; // Validate and secure user-mode memory if it is a user // allocated buffer instead of a ddraw surface if (ded.dwFlags & D3DNTHALDP2_USERMEMVERTICES) { // !!Assert here that hVBuf is NULL ASSERTGDI(hVBuf == NULL, "User allocated memory, hVBuf should be NULL\n"); if ((Length > 0) && (ded.lpVertices != NULL)) { LPVOID Address = (LPVOID) ((LPBYTE)ded.lpVertices + ded.dwVertexOffset); // Secure user-mode data. __try { ProbeForRead(Address, Length, sizeof(BYTE)); } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxD3dDrawPrimives2 fail to secure the vertex buffer"); goto Exit_DP2; } hSecure = MmSecureVirtualMemory(Address, Length, PAGE_READONLY); if (hSecure == NULL) { goto Exit_DP2; } dsurf[0].h = hCmdBuf; dsurf[0].bOptional = FALSE; DdHmgAcquireHmgrSemaphore(); // will be released at D3dLockContext(); if (!D3dLockSurfaces(1, dsurf)) { DdHmgReleaseHmgrSemaphore(); goto Exit_DP2; } ded.lpDDCommands = dsurf[0].pLcl; } else { goto Exit_DP2; } } else { dsurf[0].h = hCmdBuf; dsurf[0].bOptional = FALSE; dsurf[1].h = hVBuf; dsurf[1].bOptional = FALSE; DdHmgAcquireHmgrSemaphore(); // will be released at D3dLockContext(); if (!D3dLockSurfaces(2, dsurf)) { DdHmgReleaseHmgrSemaphore(); goto Exit_DP2; } ded.lpDDCommands = dsurf[0].pLcl; ded.lpDDVertex = dsurf[1].pLcl; // Make sure given buffer size is smaller than the actual surface size. if (dsurf[1].peSurf && ded.dwVertexSize) { DWORD dwSurfaceSize = dsurf[1].peSurf->lPitch; if (Length > dwSurfaceSize) { WARNING("DxD3dDrawPrimitive2 d3d.dwVertexLength is bigger than surface, trim it!!"); ded.dwVertexLength = dwSurfaceSize / ded.dwVertexSize; } } } pdhctx = D3dLockContext(&fsState, &ded.dwhContext); if (pdhctx == NULL) { goto Exit_DP2; } // Validate that the lpdwRStates pointer is the same as the one allocated // by the kernel during context create. The D3d runtime uses this pointer // to receive state information from the driver. if (ded.lpdwRStates != pdhctx->pvBufferAligned) { goto Exit_DP2; } if ((pdhctx->peDdGlobal->bSuspended) || ((pdhctx->peDdLocal->cSurface != pdhctx->peDdLocal->cActiveSurface) && // We wish we could do this for all interfaces, but due to legacy app compat reasons, we can't. // We have noticed that apps such as Dungeon Keeper II and Final Fantasy lose surfaces and // never bother to restore them. pdhctx->Interface >= 4)) { #if DBG if (pdhctx->peDdGlobal->bSuspended) { // WARNING("D3dDrawPrimitives2" Caller uses disabled device"); } else if ((pdhctx->peDdLocal->cSurface != pdhctx->peDdLocal->cActiveSurface)) { WARNING("D3dDrawPrimitives2: Cannot call driver when lost surfaces exist in the context"); } #endif dwRet = DDHAL_DRIVER_HANDLED; ded.ddrval = DDERR_SURFACELOST; } else { if (pdhctx->peDdGlobal->D3dCallBacks3.DrawPrimitives2) { dwRet = pdhctx->peDdGlobal->D3dCallBacks3.DrawPrimitives2(&ded); } else { WARNING("DxD3dDrawPrimitives2: DrawPrimitives2 callback absent!"); } } __try { if ((dwRet == DDHAL_DRIVER_HANDLED) && (ded.ddrval == DD_OK)) { if (ded.dwFlags & D3DNTHALDP2_SWAPCOMMANDBUFFER) { ProbeAndWriteStructure( pfpVidMemCmd, ded.lpDDCommands->lpGbl->fpVidMem, FLATPTR); ProbeAndWriteStructure( pdwSizeCmd, ded.lpDDCommands->lpGbl->dwLinearSize, DWORD); } if ((ded.dwFlags & D3DNTHALDP2_SWAPVERTEXBUFFER) && !(ded.dwFlags & D3DNTHALDP2_USERMEMVERTICES)) { ProbeAndWriteStructure( pfpVidMemVtx, ded.lpDDVertex->lpGbl->fpVidMem, FLATPTR); ProbeAndWriteStructure( pdwSizeVtx, ded.lpDDVertex->lpGbl->dwLinearSize, DWORD); } } ProbeAndWriteUlong(&pded->dwErrorOffset, ded.dwErrorOffset); // // Should be cleaned up as HRESULT might not allways be a ULONG // ProbeAndWriteLong(&pded->ddrval, ded.ddrval); } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxD3dDrawPrimitives2 unable to write back arguments"); dwRet = DDHAL_DRIVER_NOTHANDLED; } Exit_DP2: if (pdhctx) { D3dUnlockContext(pdhctx, &fsState); } CLEANUP_D3DSURFACE(dsurf, 4); if (hSecure) { MmUnsecureVirtualMemory(hSecure); } return dwRet; } /******************************Public*Routine******************************\ * * DxD3dValidateTextureStageState * \**************************************************************************/ #undef D3D_SIMPLE_WRITEBACK #define D3D_SIMPLE_WRITEBACK pdata->dwNumPasses = data.dwNumPasses; #undef D3D_SIMPLE_CALLBACKS #define D3D_SIMPLE_CALLBACKS D3dCallBacks3 D3D_SIMPLE_COPY_WITH_CONTEXT(ValidateTextureStageState, VALIDATETEXTURESTAGESTATE) #undef D3D_SIMPLE_WRITEBACK #define D3D_SIMPLE_WRITEBACK #undef D3D_SIMPLE_CALLBACKS #define D3D_SIMPLE_CALLBACKS D3dCallBacks /******************************Public*Routine******************************\ * * D3DParseUnknownCommand * \**************************************************************************/ HRESULT CALLBACK D3DParseUnknownCommand (LPVOID lpvCommands, LPVOID *lplpvReturnedCommand) { LPD3DINSTRUCTION lpInstr = (LPD3DINSTRUCTION) lpvCommands; LPD3DPROCESSVERTICES data; int i; // Initialize the return address to the command's address *lplpvReturnedCommand = lpvCommands; switch (lpInstr->bOpcode) { case D3DOP_SPAN: *lplpvReturnedCommand = (LPVOID) ((LPBYTE)lpInstr + sizeof (D3DINSTRUCTION) + lpInstr->wCount * lpInstr->bSize); return DD_OK; case D3DNTDP2OP_VIEWPORTINFO: *lplpvReturnedCommand = (LPVOID) ((LPBYTE)lpInstr + sizeof(D3DINSTRUCTION) + lpInstr->wCount * sizeof(D3DNTHAL_DP2VIEWPORTINFO)); return DD_OK; case D3DNTDP2OP_WINFO: *lplpvReturnedCommand = (LPVOID) ((LPBYTE)lpInstr + sizeof(D3DINSTRUCTION) + lpInstr->wCount * sizeof(D3DNTHAL_DP2WINFO)); return DD_OK; case D3DOP_PROCESSVERTICES: case D3DOP_MATRIXLOAD: case D3DOP_MATRIXMULTIPLY: case D3DOP_STATETRANSFORM: case D3DOP_STATELIGHT: case D3DOP_TEXTURELOAD: case D3DOP_BRANCHFORWARD: case D3DOP_SETSTATUS: case D3DOP_EXIT: return D3DNTERR_COMMAND_UNPARSED; default: return DDERR_GENERIC; } } /******************************Public*Routine******************************\ * * DxDdGetDriverState * \**************************************************************************/ DWORD APIENTRY DxDdGetDriverState(PDD_GETDRIVERSTATEDATA pdata) { DD_GETDRIVERSTATEDATA data; KFLOATING_SAVE fsState; DWORD dwRet; D3DNTHAL_CONTEXT* pdhctx; HANDLE hSecure; D3D_ENTRY("DxDdGetDriverState"); dwRet = DDHAL_DRIVER_NOTHANDLED; __try { data = CAPTURE_RW_STRUCT(pdata, DD_GETDRIVERSTATEDATA); } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxDdGetDriverState unable to access argument"); return dwRet; } if (data.lpdwStates == NULL) { WARNING("DxDdGetDriverState passed null lpdwStates"); return dwRet; } // Secure the usermode memory passed down to collect state data hSecure = MmSecureVirtualMemory((LPVOID)data.lpdwStates, data.dwLength, PAGE_READONLY); if (hSecure == 0) { return dwRet; } DdHmgAcquireHmgrSemaphore(); // will be released inside D3dLockContext() pdhctx = D3dLockContext(&fsState, &data.dwhContext); if (pdhctx == NULL) { goto Exit_GDS; } // No additional validation is needed // Assuming that GetDriverState exists in all DX7+ drivers ASSERTGDI(pdhctx->peDdGlobal->Miscellaneous2CallBacks.GetDriverState != NULL, "DxDdGetDriverState is not present. It is not an optional callback\n"); if (pdhctx->peDdGlobal->bSuspended) { data.ddRVal = DDERR_GENERIC; } else { if (pdhctx->peDdGlobal->Miscellaneous2CallBacks.GetDriverState) { dwRet = pdhctx->peDdGlobal->Miscellaneous2CallBacks.GetDriverState(&data); } else { WARNING("DxD3dDrawPrimitives2: GetDriverState callback absent!"); } } D3dUnlockContext(pdhctx, &fsState); __try { pdata->ddRVal = data.ddRVal; } __except(EXCEPTION_EXECUTE_HANDLER) { WARNING("DxDdGetDriverState unable to write back arguments"); } Exit_GDS: MmUnsecureVirtualMemory(hSecure); return dwRet; }