why unreal engine wait the gpu to finish rendering after every Present?

Started by
4 comments, last by MJP 9 years, 11 months ago

i fount unreal engine code here:

http://www.cabalchronicle.com/download/InfinityBlade_UE3_WIN&IOS_SRC_By_MeGaMaX/InfinityBlade/Development/Src/D3D9Drv/Src/D3D9Viewport.cpp


void FD3D9DynamicRHI::EndDrawingViewport(FViewportRHIParamRef ViewportRHI,UBOOL bPresent,UBOOL bLockToVsync)
{
	DYNAMIC_CAST_D3D9RESOURCE(Viewport,Viewport);

	SCOPE_CYCLE_COUNTER(STAT_D3D9PresentTime);

	GPUFrameTiming.EndTiming();

	extern DWORD GGPUFrameTime;
	if ( GPUFrameTiming.IsSupported() )
	{
		QWORD GPUTiming = GPUFrameTiming.GetTiming();
		QWORD GPUFreq = GPUFrameTiming.GetTimingFrequency();
		GGPUFrameTime = appTrunc( DOUBLE(GPUTiming) / DOUBLE(GPUFreq) / GSecondsPerCycle );
	}
	else
	{
		GGPUFrameTime = 0;
	}

	check(DrawingViewport.GetReference() == Viewport);
	DrawingViewport = NULL;

	// Clear references the device might have to resources.
	Direct3DDevice->SetRenderTarget(0,*BackBuffer);
	Direct3DDevice->SetDepthStencilSurface(NULL);

	UnsetPSTextures();
	UnsetVSTextures();

	Direct3DDevice->SetVertexShader(NULL);

	ResetVertexStreams();

	Direct3DDevice->SetIndices(NULL);
	Direct3DDevice->SetPixelShader(NULL);

	#if WITH_PANORAMA
		extern void appPanoramaRenderHookRender(void);
		// Allow G4WLive to render the Live Guide as needed (or toasts)
		appPanoramaRenderHookRender();
	#endif

	// Tell D3D we're done rendering.
	Direct3DDevice->EndScene();

	if(bPresent)
	{
		// Present the back buffer to the viewport window.
		HRESULT Result = S_OK;
		if(Viewport->IsFullscreen())
		{
			Result = Direct3DDevice->Present(NULL,NULL,NULL,NULL);
		}
		else
		{
			RECT DestRect;
			if(GetClientRect((HWND)Viewport->GetWindowHandle(),&DestRect))
			{		
				RECT SourceRect;
				SourceRect.left		= SourceRect.top = 0;
				SourceRect.right	= Viewport->GetSizeX();
				SourceRect.bottom	= Viewport->GetSizeY();

				// Only present to the viewport if its client area isn't zero-sized.
				if(DestRect.right > 0 && DestRect.bottom > 0)
				{
					Result = Direct3DDevice->Present(&SourceRect,NULL,(HWND)Viewport->GetWindowHandle(),NULL);
				}
			}
		}

		// Detect a lost device.
		if(Result == D3DERR_DEVICELOST || Result == E_FAIL)
		{
			// This variable is checked periodically by the main thread.
			bDeviceLost = TRUE;
		}
		else
		{
			VERIFYD3D9RESULT(Result);
		}
	}

	// Wait for the GPU to finish rendering the previous frame before finishing this frame.
	FrameSyncEvent.WaitForCompletion();
	FrameSyncEvent.IssueEvent();

	// If the input latency timer has been triggered, block until the GPU is completely
	// finished displaying this frame and calculate the delta time.
	if ( GInputLatencyTimer.RenderThreadTrigger )
	{
		FrameSyncEvent.WaitForCompletion();
		DWORD EndTime = appCycles();
		GInputLatencyTimer.DeltaTime = EndTime - GInputLatencyTimer.StartTime;
		GInputLatencyTimer.RenderThreadTrigger = FALSE;
	}
}

what the effect of this code:


FrameSyncEvent.WaitForCompletion();
	FrameSyncEvent.IssueEvent();

if i comment them, the engine works well too.

so i want to know why they do this ?

Advertisement
It seems to be waiting for an evens that occurred at the beginning of the previous frame.

Whenever you send commands to the GPU, they are actually just being written into a command buffer. The GPU may be executing D3D/GL calls 1,2,3+ frames after the CPU makes those calls.

I would assume that this is there as a hack to ensure that the GPU is only ever 1 frame behind the CPU, and no more than that. It basically seems like a hack to disable tripple buffering or any other kind of excessive buffering hat a driver might automatically be performing. I guess this might help reduce input-to-screen latency on some systems?

Yeah, the multi-frame queuing and the delay it causes can be seen in some FPS games when you enable vsync and move the mouse, and notice the camera movement to be clearly lagging behind. But in case the CPU has a lot of work to do anyway, this effect may not happen (as it never has time to queue multiple frames)

It seems to be waiting for an evens that occurred at the beginning of the previous frame.

Whenever you send commands to the GPU, they are actually just being written into a command buffer. The GPU may be executing D3D/GL calls 1,2,3+ frames after the CPU makes those calls.

I would assume that this is there as a hack to ensure that the GPU is only ever 1 frame behind the CPU, and no more than that. It basically seems like a hack to disable tripple buffering or any other kind of excessive buffering hat a driver might automatically be performing. I guess this might help reduce input-to-screen latency on some systems?

I remember that UT3 (one of the first UE3 games) has an in-game option for that.

"Recursion is the first step towards madness." - "Skegg?ld, Skálm?ld, Skildir ro Klofnir!"
Direct3D 12 quick reference: https://github.com/alessiot89/D3D12QuickRef/

It seems to be waiting for an evens that occurred at the beginning of the previous frame.

Whenever you send commands to the GPU, they are actually just being written into a command buffer. The GPU may be executing D3D/GL calls 1,2,3+ frames after the CPU makes those calls.

I would assume that this is there as a hack to ensure that the GPU is only ever 1 frame behind the CPU, and no more than that. It basically seems like a hack to disable tripple buffering or any other kind of excessive buffering hat a driver might automatically be performing. I guess this might help reduce input-to-screen latency on some systems?


void FD3D9EventQuery::IssueEvent()
{
	if( Query )
	{
		Query->Issue(D3DISSUE_END);
	}
}

void FD3D9EventQuery::WaitForCompletion()
{
	if( Query )
	{
		UBOOL bRenderingIsFinished = FALSE;
		while ( D3DRHI->GetQueryData(Query,&bRenderingIsFinished,sizeof(bRenderingIsFinished),TRUE) && !bRenderingIsFinished )
		{
		}
	}
}

Yeah that's a common trick used by D3D games to keep the CPU from getting too far ahead of the GPU. Basically you just sync on a query from the previous frame, and it forces the driver to wait for the GPU to catch up.

This topic is closed to new replies.

Advertisement