/*==========================================================================; * * Copyright (C) 1999-2000 Microsoft Corporation. All Rights Reserved. * * File: surface.cpp * Content: Implementation of the CSurface class. * * ***************************************************************************/ #include "ddrawpr.h" #include "surface.hpp" #include "pixel.hpp" #include "swapchan.hpp" #undef DPF_MODNAME #define DPF_MODNAME "CSurface::Create" // Static class function for creating a RenderTarget/ZStencil object. // (Because it is static; it doesn't have a this pointer.) HRESULT CSurface::Create(CBaseDevice *pDevice, DWORD Width, DWORD Height, DWORD Usage, D3DFORMAT UserFormat, D3DMULTISAMPLE_TYPE MultiSampleType, REF_TYPE refType, IDirect3DSurface8 **ppSurface) { HRESULT hr; // Do parameter checking here if (!VALID_PTR_PTR(ppSurface)) { DPF_ERR("Bad parameter passed for ppSurface for creating a surface. CreateRenderTarget/CreateDepthStencil failed"); return D3DERR_INVALIDCALL; } // Zero-out return parameter *ppSurface = NULL; // Size may need to be 4x4 if (CPixel::Requires4X4(UserFormat)) { if ((Width & 3) || (Height & 3)) { DPF_ERR("DXT Formats require width/height to be a multiple of 4. CreateRenderTarget/CreateDepthStencil failed."); return D3DERR_INVALIDCALL; } } // Validate against zero width/height if (Width == 0 || Height == 0) { DPF_ERR("Width/Height must be non-zero. CreateRenderTarget/CreateDepthStencil failed"); return D3DERR_INVALIDCALL; } // Now verify that the device can support the specified format hr = pDevice->CheckDeviceFormat( Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL), D3DRTYPE_SURFACE, UserFormat); if (FAILED(hr)) { DPF_ERR("The format is not supported by this device. CreateRenderTarget/CreateDepthStencil failed"); return D3DERR_INVALIDCALL; } // Infer lockability for DepthStencil from format if (Usage & D3DUSAGE_DEPTHSTENCIL) { if (!CPixel::IsNonLockableZ(UserFormat)) { Usage |= D3DUSAGE_LOCK; } } // Validate lockability if ((MultiSampleType != D3DMULTISAMPLE_NONE) && (Usage & D3DUSAGE_LOCK)) { // RT have explicit lockability if (Usage & D3DUSAGE_RENDERTARGET) { DPF_ERR("Multi-Sampled render-targets are not lockable. CreateRenderTarget failed"); return D3DERR_INVALIDCALL; } else { DPF_ERR("Multi-Sampled Depth Stencil buffers are not lockable. " "Use D3DFMT_D16 instead of D3DFMT_D16_LOCKABLE. CreateDepthStencil failed"); return D3DERR_INVALIDCALL; } } // Map depth/stencil format D3DFORMAT RealFormat = pDevice->MapDepthStencilFormat(UserFormat); // Create the surface CSurface *pSurface; pSurface = new CDriverSurface(pDevice, Width, Height, Usage, UserFormat, RealFormat, MultiSampleType, 0, // hKernelHandle refType, &hr); if (pSurface == NULL) { DPF_ERR("Out of Memory creating surface. CreateRenderTarget/CreateDepthStencil failed"); return E_OUTOFMEMORY; } if (FAILED(hr)) { DPF_ERR("Error during initialization of surface. CreateRenderTarget/CreateDepthStencil failed"); if (refType == REF_EXTERNAL) { // External objects get released pSurface->Release(); } else { // Internal and intrinsic objects get decremented DXGASSERT(refType == REF_INTERNAL || refType == REF_INTRINSIC); pSurface->DecrementUseCount(); } return hr; } // We're done; just return the object *ppSurface = pSurface; return hr; } // static Create for ZBuffers and RenderTargets #undef DPF_MODNAME #define DPF_MODNAME "CSurface::CreateImageSurface" // Function for creating sys-mem stand-alone surfaces // that can be used with CopyRect and SetCursorSurface and // ReadBuffer HRESULT CSurface::CreateImageSurface(CBaseDevice *pDevice, DWORD Width, DWORD Height, D3DFORMAT Format, REF_TYPE refType, IDirect3DSurface8 **ppSurface) { HRESULT hr; // Do parameter checking here if (!VALID_PTR_PTR(ppSurface)) { DPF_ERR("Bad parameter passed for ppSurface for creating a surface. CreateImageSurface failed."); return D3DERR_INVALIDCALL; } // Zero-out return parameter *ppSurface = NULL; // Has to be supported format if (!CPixel::IsSupported(D3DRTYPE_SURFACE, Format)) { DPF_ERR("This format is not supported for CreateImageSurface"); return D3DERR_INVALIDCALL; } if (CPixel::IsNonLockableZ(Format)) { DPF_ERR("This Z format is not supported for CreateImageSurface"); return D3DERR_INVALIDCALL; } // Size may need to be 4x4 if (CPixel::Requires4X4(Format)) { if ((Width & 3) || (Height & 3)) { DPF_ERR("DXT Formats require width/height to be a multiple of 4. CreateImageSurface failed."); return D3DERR_INVALIDCALL; } } // Validate against zero width/height if (Width == 0 || Height == 0) { DPF_ERR("Width/Height must be non-zero. CreateImageSurface failed."); return D3DERR_INVALIDCALL; } // Usage is explictly just Usage_LOCK DWORD Usage = D3DUSAGE_LOCK; CSurface *pSurface = new CSysMemSurface(pDevice, Width, Height, Usage, Format, refType, &hr); if (pSurface == NULL) { DPF_ERR("Out of Memory creating surface. CreateImageSurface failed."); return E_OUTOFMEMORY; } if (FAILED(hr)) { DPF_ERR("Error during initialization of surface. CreateImageSurface failed."); if (refType == REF_EXTERNAL) { // External objects get released pSurface->Release(); } else { // Internal and intrinsic objects get decremented DXGASSERT(refType == REF_INTERNAL || refType == REF_INTRINSIC); pSurface->DecrementUseCount(); } return hr; } // We're done; just return the object *ppSurface = pSurface; return S_OK; } // static CreateImageSurface #undef DPF_MODNAME #define DPF_MODNAME "CSurface::CSurface" // Constructor the surface class; this is the // base class for render targets/zbuffers/and backbuffers CSurface::CSurface(CBaseDevice *pDevice, DWORD Width, DWORD Height, DWORD Usage, D3DFORMAT Format, REF_TYPE refType, HRESULT *phr ) : CBaseObject(pDevice, refType), m_qwBatchCount(0) { // Sanity check DXGASSERT(phr); // Initialize basic structures m_desc.Format = Format; m_desc.Pool = D3DPOOL_DEFAULT; m_desc.Usage = Usage; m_desc.Type = D3DRTYPE_SURFACE; m_desc.Width = Width; m_desc.Height = Height; m_formatUser = Format; m_poolUser = D3DPOOL_DEFAULT; // Return success *phr = S_OK; } // CSurface::CSurface #undef DPF_MODNAME #define DPF_MODNAME "CSurface::~CSurface" // Destructor CSurface::~CSurface() { // The destructor has to handle partially // created objects. // Check to make sure that we aren't deleting // an object that is referenced in the current (unflushed) // command stream buffer. DXGASSERT(m_qwBatchCount <= static_cast(Device())->CurrentBatch()); } // CSurface::~CSurface // IUnknown methods #undef DPF_MODNAME #define DPF_MODNAME "CSurface::QueryInterface" STDMETHODIMP CSurface::QueryInterface(REFIID riid, LPVOID FAR *ppvObj) { API_ENTER(Device()); if (!VALID_PTR_PTR(ppvObj)) { DPF_ERR("Invalid ppvObj parameter passed to CSurface::QueryInterface"); return D3DERR_INVALIDCALL; } if (!VALID_PTR(&riid, sizeof(GUID))) { DPF_ERR("Invalid guid memory address to CSurface::QueryInterface"); return D3DERR_INVALIDCALL; } if (riid == IID_IDirect3DSurface8 || riid == IID_IUnknown) { *ppvObj = static_cast(static_cast(this)); AddRef(); return S_OK; } DPF_ERR("Unsupported Interface identifier passed to CSurface::QueryInterface"); // Null out param *ppvObj = NULL; return E_NOINTERFACE; } // QueryInterface #undef DPF_MODNAME #define DPF_MODNAME "CSurface::AddRef" STDMETHODIMP_(ULONG) CSurface::AddRef() { API_ENTER_NO_LOCK(Device()); return AddRefImpl(); } // AddRef #undef DPF_MODNAME #define DPF_MODNAME "CSurface::Release" STDMETHODIMP_(ULONG) CSurface::Release() { API_ENTER_SUBOBJECT_RELEASE(Device()); return ReleaseImpl(); } // Release // IDirect3DBuffer methods #undef DPF_MODNAME #define DPF_MODNAME "CSurface::GetDevice" STDMETHODIMP CSurface::GetDevice(IDirect3DDevice8 ** ppObj) { API_ENTER(Device()); return GetDeviceImpl(ppObj); } // GetDevice #undef DPF_MODNAME #define DPF_MODNAME "CSurface::SetPrivateData" STDMETHODIMP CSurface::SetPrivateData(REFGUID riid, CONST VOID* pvData, DWORD cbData, DWORD dwFlags) { API_ENTER(Device()); // We use level zero for our data return SetPrivateDataImpl(riid, pvData, cbData, dwFlags, 0); } // SetPrivateData #undef DPF_MODNAME #define DPF_MODNAME "CSurface::GetPrivateData" STDMETHODIMP CSurface::GetPrivateData(REFGUID riid, LPVOID pvData, LPDWORD pcbData) { API_ENTER(Device()); // We use level zero for our data return GetPrivateDataImpl(riid, pvData, pcbData, 0); } // GetPrivateData #undef DPF_MODNAME #define DPF_MODNAME "CSurface::FreePrivateData" STDMETHODIMP CSurface::FreePrivateData(REFGUID riid) { API_ENTER(Device()); // We use level zero for our data return FreePrivateDataImpl(riid, 0); } // FreePrivateData #undef DPF_MODNAME #define DPF_MODNAME "CSurface::GetContainer" STDMETHODIMP CSurface::GetContainer(REFIID riid, void **ppContainer) { API_ENTER(Device()); // Our 'container' is just the device since // we are a standalone surface object return Device()->QueryInterface( riid, ppContainer); } // OpenContainer #undef DPF_MODNAME #define DPF_MODNAME "CSurface::GetDesc" STDMETHODIMP CSurface::GetDesc(D3DSURFACE_DESC *pDesc) { API_ENTER(Device()); // If parameters are bad, then we should fail some stuff if (!VALID_WRITEPTR(pDesc, sizeof(*pDesc))) { DPF_ERR("bad pointer for pDesc passed to CSurface::GetDesc"); return D3DERR_INVALIDCALL; } *pDesc = m_desc; pDesc->Format = m_formatUser; pDesc->Pool = m_poolUser; pDesc->Usage &= D3DUSAGE_EXTERNAL; return S_OK; } // GetDesc #undef DPF_MODNAME #define DPF_MODNAME "CSurface::InternalGetDesc" D3DSURFACE_DESC CSurface::InternalGetDesc() const { return m_desc; } // InternalGetDesc #ifdef DEBUG #undef DPF_MODNAME #define DPF_MODNAME "CSurface::ReportWhyLockFailed" // DPF why Lock failed as clearly as possible void CSurface::ReportWhyLockFailed(void) const { // If there are multiple reasons that lock failed; we report // them all to minimize user confusion if (InternalGetDesc().MultiSampleType != D3DMULTISAMPLE_NONE) { DPF_ERR("Lock is not supported for surfaces that have multi-sampling enabled."); } if (InternalGetDesc().Usage & D3DUSAGE_DEPTHSTENCIL) { DPF_ERR("Lock is not supported for depth formats other than D3DFMT_D16_LOCKABLE"); } // If this is not a non-lockable Z format, and // we are not multisampled; then the user must // have explicitly chosen to create us in an non-lockable way if (InternalGetDesc().Usage & D3DUSAGE_BACKBUFFER) { DPF_ERR("Backbuffers are not lockable unless application specifies " "D3DPRESENTFLAG_LOCKABLE_BACKBUFFER at CreateDevice and Reset. " "Lockable backbuffers incur a performance cost on some " "graphics hardware."); } else if (InternalGetDesc().Usage & D3DUSAGE_RENDERTARGET) { DPF_ERR("RenderTargets are not lockable unless application specifies " "TRUE for the Lockable parameter for CreateRenderTarget. Lockable " "render targets incur a performance cost on some graphics hardware."); } // If we got here; then USAGE_LOCK should not have been set DXGASSERT(!(InternalGetDesc().Usage & D3DUSAGE_LOCK)); return; } // CSurface::ReportWhyLockFailed #endif // DEBUG //============================================= // Methods for the CSysMemSurface class //============================================= #undef DPF_MODNAME #define DPF_MODNAME "CSysMemSurface::CSysMemSurface" CSysMemSurface::CSysMemSurface(CBaseDevice *pDevice, DWORD Width, DWORD Height, DWORD Usage, D3DFORMAT Format, REF_TYPE refType, HRESULT *phr ) : CSurface(pDevice, Width, Height, Usage, Format, refType, phr), m_rgbPixels(NULL) { if (FAILED(*phr)) return; // Compute how much memory we need m_desc.Size = CPixel::ComputeSurfaceSize(Width, Height, Format); // Specify system memory m_desc.Pool = D3DPOOL_SYSTEMMEM; m_poolUser = D3DPOOL_SYSTEMMEM; // Specify no multisampling m_desc.MultiSampleType = D3DMULTISAMPLE_NONE; // Allocate the memory m_rgbPixels = new BYTE[m_desc.Size]; if (m_rgbPixels == NULL) { DPF_ERR("Out of memory allocating surface."); *phr = E_OUTOFMEMORY; return; } // Figure out our pitch D3DLOCKED_RECT lock; CPixel::ComputeSurfaceOffset(&m_desc, m_rgbPixels, NULL, // pRect &lock); // Create a DDSURFACE and CreateSurfaceData object DDSURFACEINFO SurfInfo; ZeroMemory(&SurfInfo, sizeof(SurfInfo)); // If we are not passed a handle, then we need to get one from // the DDI D3D8_CREATESURFACEDATA CreateSurfaceData; ZeroMemory(&CreateSurfaceData, sizeof(CreateSurfaceData)); // Set up the basic information CreateSurfaceData.hDD = pDevice->GetHandle(); CreateSurfaceData.pSList = &SurfInfo; CreateSurfaceData.dwSCnt = 1; // ImageSurface is an internal type so that the thunk layer // knows that it is not really a texture CreateSurfaceData.Type = D3DRTYPE_IMAGESURFACE; CreateSurfaceData.Pool = m_desc.Pool; CreateSurfaceData.dwUsage = m_desc.Usage; CreateSurfaceData.MultiSampleType = D3DMULTISAMPLE_NONE; CreateSurfaceData.Format = Format; // Specify the surface data SurfInfo.cpWidth = Width; SurfInfo.cpHeight = Height; SurfInfo.pbPixels = (BYTE*)lock.pBits; SurfInfo.iPitch = lock.Pitch; *phr = pDevice->GetHalCallbacks()->CreateSurface(&CreateSurfaceData); if (FAILED(*phr)) { DPF_ERR("Failed to create sys-mem surface"); return; } DXGASSERT(CreateSurfaceData.Pool == D3DPOOL_SYSTEMMEM); DXGASSERT(m_desc.Pool == D3DPOOL_SYSTEMMEM); DXGASSERT(m_poolUser == D3DPOOL_SYSTEMMEM); SetKernelHandle(SurfInfo.hKernelHandle); return; } // CSysMemSurface::CSysMemSurface #undef DPF_MODNAME #define DPF_MODNAME "CSysMemSurface::~CSysMemSurface" CSysMemSurface::~CSysMemSurface() { if (KernelHandle() != 0) { D3D8_DESTROYSURFACEDATA DestroyData; ZeroMemory(&DestroyData, sizeof DestroyData); DestroyData.hDD = Device()->GetHandle(); DestroyData.hSurface = KernelHandle(); Device()->GetHalCallbacks()->DestroySurface(&DestroyData); } // Free the memory we've allocated for the surface delete [] m_rgbPixels; return; } // CSysMemSurface::CSysMemSurface #undef DPF_MODNAME #define DPF_MODNAME "CSysMemSurface::LockRect" STDMETHODIMP CSysMemSurface::LockRect(D3DLOCKED_RECT *pLockedRectData, CONST RECT *pRect, DWORD dwFlags) { API_ENTER(Device()); // If parameters are bad, then we should fail some stuff if (!VALID_WRITEPTR(pLockedRectData, sizeof(D3DLOCKED_RECT))) { DPF_ERR("bad pointer for m_pLockedRectData passed to LockRect for an ImageSurface."); return D3DERR_INVALIDCALL; } // Zero out returned data ZeroMemory(pLockedRectData, sizeof(D3DLOCKED_RECT)); // Validate Rect if (pRect != NULL) { if (!CPixel::IsValidRect(m_desc.Format, m_desc.Width, m_desc.Height, pRect)) { DPF_ERR("LockRect for a Surface failed"); return D3DERR_INVALIDCALL; } } if (dwFlags & ~D3DLOCK_SURF_VALID) { DPF_ERR("Invalid dwFlags parameter passed to LockRect for an ImageSurface"); DPF_EXPLAIN_BAD_LOCK_FLAGS(0, dwFlags & ~D3DLOCK_SURF_VALID); return D3DERR_INVALIDCALL; } // Can't lock surfaces that are not lockable if (!IsLockable()) { ReportWhyLockFailed(); return D3DERR_INVALIDCALL; } return InternalLockRect(pLockedRectData, pRect, dwFlags); } // LockRect #undef DPF_MODNAME #define DPF_MODNAME "CSysMemSurface::InternalLockRect" HRESULT CSysMemSurface::InternalLockRect(D3DLOCKED_RECT *pLockedRectData, CONST RECT *pRect, DWORD dwFlags) { // Only one lock outstanding at a time is supported // (even internally) if (m_isLocked) { DPF_ERR("LockRect failed on a surface; surface was already locked for an ImageSurface"); return D3DERR_INVALIDCALL; } CPixel::ComputeSurfaceOffset(&m_desc, m_rgbPixels, pRect, pLockedRectData); // Mark ourselves as locked m_isLocked = TRUE; // Done return S_OK; } // InternalLockRect #undef DPF_MODNAME #define DPF_MODNAME "CSysMemSurface::UnlockRect" STDMETHODIMP CSysMemSurface::UnlockRect() { API_ENTER(Device()); // If we aren't locked; then something is wrong if (!m_isLocked) { DPF_ERR("UnlockRect failed on a mip level; surface wasn't locked for an ImageSurface"); return D3DERR_INVALIDCALL; } DXGASSERT(IsLockable()); return InternalUnlockRect(); } // UnlockRect #undef DPF_MODNAME #define DPF_MODNAME "CSysMemSurface::InternalUnlockRect" HRESULT CSysMemSurface::InternalUnlockRect() { DXGASSERT(m_isLocked); // Clear our locked state m_isLocked = FALSE; // Done return S_OK; } // InternalUnlockRect //============================================= // Methods for the CDriverSurface class //============================================= #undef DPF_MODNAME #define DPF_MODNAME "CDriverSurface::CDriverSurface" CDriverSurface::CDriverSurface(CBaseDevice *pDevice, DWORD Width, DWORD Height, DWORD Usage, D3DFORMAT UserFormat, D3DFORMAT RealFormat, D3DMULTISAMPLE_TYPE MultiSampleType, HANDLE hKernelHandle, REF_TYPE refType, HRESULT *phr ) : CSurface(pDevice, Width, Height, Usage, RealFormat, refType, phr) { // Even in failure paths, we need to remember // the passed in kernel handle so we can uniformly // free it if (hKernelHandle) SetKernelHandle(hKernelHandle); // On failure; just return here if (FAILED(*phr)) { return; } // Remember User Format m_formatUser = UserFormat; // Remember multi-sample type m_desc.MultiSampleType = MultiSampleType; // Parameter check MS types; (since swapchan bypasses // the static Create; we need to parameter check here.) if (MultiSampleType != D3DMULTISAMPLE_NONE) { *phr = pDevice->CheckDeviceMultiSampleType(RealFormat, pDevice->SwapChain()->Windowed(), MultiSampleType); if (FAILED(*phr)) { DPF_ERR("Unsupported multisample type requested. CreateRenderTarget/CreateDepthStencil failed."); return; } } // Back buffers are actually, for now, created just like other device // surfaces. // Otherwise, we need to call the driver // and get ourselves a handle. // Create a DDSURFACE and CreateSurfaceData object DDSURFACEINFO SurfInfo; ZeroMemory(&SurfInfo, sizeof(SurfInfo)); if ((hKernelHandle == NULL) && (!(pDevice->Enum()->NoDDrawSupport(pDevice->AdapterIndex())) || !(D3DUSAGE_PRIMARYSURFACE & Usage)) ) { // If we are not passed a handle, then we need to get one from // the DDI D3D8_CREATESURFACEDATA CreateSurfaceData; ZeroMemory(&CreateSurfaceData, sizeof(CreateSurfaceData)); // Set up the basic information CreateSurfaceData.hDD = pDevice->GetHandle(); CreateSurfaceData.pSList = &SurfInfo; CreateSurfaceData.dwSCnt = 1; CreateSurfaceData.Type = D3DRTYPE_SURFACE; CreateSurfaceData.Pool = m_desc.Pool; CreateSurfaceData.dwUsage = m_desc.Usage; CreateSurfaceData.Format = RealFormat; CreateSurfaceData.MultiSampleType = MultiSampleType; // Specify the surface data SurfInfo.cpWidth = Width; SurfInfo.cpHeight = Height; *phr = pDevice->GetHalCallbacks()->CreateSurface(&CreateSurfaceData); if (FAILED(*phr)) { DPF_ERR("Failed to create driver surface"); return; } // Remember the kernel handle SetKernelHandle(SurfInfo.hKernelHandle); // Remember the actual pool m_desc.Pool = CreateSurfaceData.Pool; } else { // If the caller has already allocated this // then we assume that the pool is LocalVidMem SurfInfo.hKernelHandle = hKernelHandle; m_desc.Pool = D3DPOOL_LOCALVIDMEM; } m_desc.Size = SurfInfo.iPitch * Height; if (m_desc.MultiSampleType != D3DMULTISAMPLE_NONE) m_desc.Size *= (UINT)m_desc.MultiSampleType; return; } // CDriverSurface::CDriverSurface #undef DPF_MODNAME #define DPF_MODNAME "CDriverSurface::~CDriverSurface" CDriverSurface::~CDriverSurface() { if (KernelHandle() != 0) { D3D8_DESTROYSURFACEDATA DestroyData; ZeroMemory(&DestroyData, sizeof DestroyData); DestroyData.hDD = Device()->GetHandle(); DestroyData.hSurface = KernelHandle(); Device()->GetHalCallbacks()->DestroySurface(&DestroyData); } return; } // CDriverSurface::CDriverSurface #undef DPF_MODNAME #define DPF_MODNAME "CDriverSurface::LockRect" STDMETHODIMP CDriverSurface::LockRect(D3DLOCKED_RECT *pLockedRectData, CONST RECT *pRect, DWORD dwFlags) { API_ENTER(Device()); // If parameters are bad, then we should fail some stuff if (!VALID_WRITEPTR(pLockedRectData, sizeof(D3DLOCKED_RECT))) { DPF_ERR("bad pointer for m_pLockedRectData passed to LockRect"); return D3DERR_INVALIDCALL; } // Zero out returned data ZeroMemory(pLockedRectData, sizeof(D3DLOCKED_RECT)); // Validate Rect if (pRect != NULL) { if (!CPixel::IsValidRect(m_desc.Format, m_desc.Width, m_desc.Height, pRect)) { DPF_ERR("LockRect for a driver-allocated Surface failed"); return D3DERR_INVALIDCALL; } } if (dwFlags & ~D3DLOCK_SURF_VALID) { DPF_ERR("Invalid dwFlags parameter passed to LockRect"); DPF_EXPLAIN_BAD_LOCK_FLAGS(0, dwFlags & ~D3DLOCK_SURF_VALID); return D3DERR_INVALIDCALL; } // Can't lock surfaces that are not lockable if (!IsLockable()) { ReportWhyLockFailed(); return D3DERR_INVALIDCALL; } return InternalLockRect(pLockedRectData, pRect, dwFlags); } #undef DPF_MODNAME #define DPF_MODNAME "CDriverSurface::InternalLockRect" HRESULT CDriverSurface::InternalLockRect(D3DLOCKED_RECT *pLockedRectData, CONST RECT *pRect, DWORD dwFlags) { // Only one lock outstanding at a time is supported // (even internally) if (m_isLocked) { DPF_ERR("LockRect failed on a surface; surface was already locked."); return D3DERR_INVALIDCALL; } D3D8_LOCKDATA lockData; ZeroMemory(&lockData, sizeof lockData); lockData.hDD = Device()->GetHandle(); lockData.hSurface = KernelHandle(); lockData.dwFlags = dwFlags; if (pRect != NULL) { lockData.bHasRect = TRUE; lockData.rArea = *((RECTL *) pRect); } else { DXGASSERT(lockData.bHasRect == FALSE); } // Sync before allowing read or write access Sync(); HRESULT hr = Device()->GetHalCallbacks()->Lock(&lockData); if (FAILED(hr)) { DPF_ERR("Error trying to lock driver surface"); return hr; } // Fill in the Locked_Rect fields if (CPixel::IsDXT(m_desc.Format)) { // Pitch is the number of bytes for // one row's worth of blocks for linear formats // Start with our width UINT Width = m_desc.Width; // Convert to blocks Width = Width / 4; // At least one block if (Width == 0) Width = 1; if (m_desc.Format == D3DFMT_DXT1) { // 8 bytes per block for DXT1 pLockedRectData->Pitch = Width * 8; } else { // 16 bytes per block for DXT2-5 pLockedRectData->Pitch = Width * 16; } } else { pLockedRectData->Pitch = lockData.lPitch; } pLockedRectData->pBits = lockData.lpSurfData; // Mark ourselves as locked m_isLocked = TRUE; // Done return hr; } // LockRect #undef DPF_MODNAME #define DPF_MODNAME "CDriverSurface::UnlockRect" STDMETHODIMP CDriverSurface::UnlockRect() { API_ENTER(Device()); // If we aren't locked; then something is wrong if (!m_isLocked) { DPF_ERR("UnlockRect failed; surface wasn't locked."); return D3DERR_INVALIDCALL; } return InternalUnlockRect(); } #undef DPF_MODNAME #define DPF_MODNAME "CDriverSurface::InternalUnlockRect" HRESULT CDriverSurface::InternalUnlockRect() { DXGASSERT(m_isLocked); D3D8_UNLOCKDATA unlockData = { Device()->GetHandle(), KernelHandle() }; HRESULT hr = Device()->GetHalCallbacks()->Unlock(&unlockData); if (SUCCEEDED(hr)) { // Clear our locked state m_isLocked = FALSE; } // Done return hr; } // UnlockRect // End of file : surface.cpp