#include "pch.hpp"
//
//===========================================================================//
//                                                                           //
// Contents: Dirty Rectangle routines										 //
//                                                                           //
// Copyright (C) 1999 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
//
#include "DXRasterizer.hpp"
#include "DirectX.hpp"

extern HGOSHEAP DefaultHeap;



//
// Create copies of the target and maybe Z buffer
//
void CreateCopyBuffers()
{
	if( 0==Environment.DirtyRectangle )
		return;
//
// Get the current back buffer surface description
//
	DDSURFACEDESC2 ddsd;
	memset(&ddsd,0,sizeof(ddsd));
	ddsd.dwSize = sizeof(ddsd);
	wGetSurfaceDesc( BackBufferSurface,&ddsd );
//
// Try creating in video memory, unless back buffer in system or user has asked for system
//
	CopyTarget=0;
	if( Environment.Renderer!=3 && 0==(Environment.DirtyRectangle&4) && 0==(ddsd.ddsCaps.dwCaps&DDSCAPS_SYSTEMMEMORY) )
	{
		ddsd.dwFlags=DDSD_CAPS | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_WIDTH;
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
		ddsd.ddsCaps.dwCaps2 = 0;
		AllowFail=1;
		wCreateSurface( CurrentDDobject, &ddsd, &CopyTarget, NULL );
		AllowFail=0;
	}
//
// If video memory failed, or user requested, create in system memory - this can never fail
//
	if( 0==CopyTarget )
	{
		ddsd.dwFlags=DDSD_CAPS | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_WIDTH;
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
		ddsd.ddsCaps.dwCaps2 = 0;
		wCreateSurface( CurrentDDobject, &ddsd, &CopyTarget, NULL );
	}
//
// Should I save the Z buffer too?
//
	if( Environment.DirtyRectangle&2 )
	{
//
// Use same size as back buffer (cannot create system memory Z buffers when HAL is in use...)
//
//		memset(&ddsd,0,sizeof(ddsd));
//		ddsd.dwSize = sizeof(ddsd);
//		wGetSurfaceDesc( ZBufferSurface, &ddsd );

		CopyZBuffer=0;
		if( Environment.Renderer!=3 && 0==(Environment.DirtyRectangle&4) && 0==(ddsd.ddsCaps.dwCaps&DDSCAPS_SYSTEMMEMORY) )
		{
			ddsd.dwFlags=DDSD_CAPS | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_WIDTH;
			ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;
			ddsd.ddsCaps.dwCaps2 = 0;
			AllowFail=1;
			wCreateSurface( CurrentDDobject, &ddsd, &CopyZBuffer, NULL );
			AllowFail=0;
		}
//
// If video memory failed, or user requested, create in system memory - this can never fail
//
		if( 0==CopyZBuffer )
		{
			ddsd.dwFlags=DDSD_CAPS | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_WIDTH;
			ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
			ddsd.ddsCaps.dwCaps2 = 0;
			wCreateSurface( CurrentDDobject, &ddsd, &CopyZBuffer, NULL );
		}
	}
}






//
// The application should call this API at the start of UpdateRenderers.
//  If this returns TRUE it should re-render the whole scene and save the back buffer again.
//  This will happen if the saved surfaces were in video memory and the mode has been changed.
//  If it returns false the application can assume all the dirty rectangles have been copied over
//  and it is free to continue rendering. On some hardware, where it is not possible to save/restore
//  the back buffer this may return true every frame.
//
bool __stdcall gosDirtyRectangeLostTarget()
{
	gosASSERT( Environment.DirtyRectangle && CopyTarget );
	SPEW(( GROUP_DIRECTDRAW, "gosDirtyRectangeLostTarget()" ));

	if( ModeChanged )
		return true;
	else
		return false;
}




//
// When this is executed the WHOLE back buffer will be saved to the copy surface
//  (if you requested the Z buffer to be saved, it will also be copied).
//  You should call this when you have drawn the initial background scene.

//
void __stdcall gosDirtyRectangleSaveTarget()
{
	gosASSERT( InsideBeginScene && Environment.DirtyRectangle && CopyTarget );
	SPEW(( GROUP_DIRECTDRAW, "gosDirtyRectangleSaveTarget()" ));
//
// End the scene to flush rendering
//
	SafeFPU();
	wEndScene( d3dDevice7 );
	GameOSFPU();
//
// Save the back buffer to the copy
//
	wBlt( CopyTarget, NULL, BackBufferSurface, NULL, DDBLT_WAIT, 0 );
	if( Environment.DirtyRectangle&2 )
		wBlt( CopyZBuffer, NULL, ZBufferSurface, NULL, DDBLT_WAIT, 0 );
//
// Restart rendering now save is complete
//
	SafeFPU();
	wBeginScene( d3dDevice7 );
	GameOSFPU();
}



//
// Variables used to save list of dirty rectangles
//
typedef struct _DirtyAreas
{
	_DirtyAreas*	pNext;
	DWORD			Left;
	DWORD			Top;
	DWORD			Right;
	DWORD			Bottom;
} DirtyAreas;

DirtyAreas*	pDirtyAreas=0;




//
// Specifies a region that has been rendered to on the back buffer.
//  Before the next BeginScene, GameOS will copy all these regions specified in the previous
//  frame from the copy buffer to the back buffer. In this way the whole scene does not have
//  to be re-rendered, only the areas that actually are animating or moving. If you open the
//  debugger (Control Break) or use frame graphs the whole screen will be invalidated and it
//  will be completly restored from the copy of the back buffer. This may be slower if the
//  copy is in system memory, but at least all the debugger functions should still operate as normal.
//
void __stdcall gosDirtyRectangeRestoreArea( DWORD Left, DWORD Top, DWORD Right, DWORD Bottom )
{
	gosASSERT( InsideBeginScene && Environment.DirtyRectangle && CopyTarget );
	SPEW(( GROUP_DIRECTDRAW, "gosDirtyRectangeRestoreArea(%d,%d to %d,%d)",Left,Top,Right,Bottom ));
//
// Add area to a list of areas to be cleared
//
	DirtyAreas* pThis=(DirtyAreas*)gos_Malloc( sizeof(DirtyAreas),DefaultHeap );
	pThis->pNext=pDirtyAreas;
	pThis->Left=Left;
	pThis->Right=Right;
	pThis->Top=Top;
	pThis->Bottom=Bottom;
	pDirtyAreas=pThis;
}



//
// Called at the start of a frame to restore all the areas of the screen.
//
void RestoreAreas()
{
	if( 0==Environment.DirtyRectangle )
		return;

	SPEW(( GROUP_DIRECTDRAW, "RestoringDirtyRectangles()" ));
//
// Go through list of areas and restore them (unless mode has changed, then just ignore them)
//
	while( pDirtyAreas )
	{
		DirtyAreas* pThis=pDirtyAreas;
		pDirtyAreas=pDirtyAreas->pNext;

		if( !ModeChanged && !DebuggerUsed )
		{
			SPEW(( GROUP_DIRECTDRAW, "Restore %d,%d to %d,%d",pThis->Left,pThis->Top,pThis->Right,pThis->Bottom ));

			RECT temp;
			temp.left=pThis->Left;
			temp.bottom=pThis->Bottom;
			temp.right=pThis->Right;
			temp.top=pThis->Top;
			
			wBlt( BackBufferSurface, &temp, CopyTarget, &temp, DDBLT_WAIT, 0 );

			if( Environment.DirtyRectangle&2 )
				wBlt( ZBufferSurface, &temp, CopyZBuffer, &temp, DDBLT_WAIT, 0 );
		}
		gos_Free( pThis );
	}
	pDirtyAreas=0;
//
// If the debugger was used, restore WHOLE back buffer - ignore regions
//
	if( !ModeChanged && DebuggerUsed )
	{
		wBlt( BackBufferSurface, NULL, CopyTarget, NULL, DDBLT_WAIT, 0 );

		if( Environment.DirtyRectangle&2 )
			wBlt( ZBufferSurface, NULL, CopyZBuffer, NULL, DDBLT_WAIT, 0 );

	}
}






//
// Shut down dirty rectangles
//
void DestroyDirtyRectangles()
{
//
// Remove any regions
//
	while( pDirtyAreas )
	{
		DirtyAreas* pThis=pDirtyAreas;
		pDirtyAreas=pDirtyAreas->pNext;
		gos_Free( pThis );
	}
	pDirtyAreas=0;
//
// Remove copy buffers
//
	if( CopyTarget )
	{
		wRelease( CopyTarget );
		CopyTarget=0;
	}
	if( CopyZBuffer )
	{
		wRelease( CopyZBuffer );
		CopyZBuffer=0;
	}
}


