Screenshot Problem (Missing HUD images)

Started by
17 comments, last by Evil Steve 16 years, 2 months ago
Hi Guys, Recently I incorporated a Screenshot function into my tech demo. The problem I am having is that when I take a screenshot everything is copied into the created .png image apart from any HUD images/textures I have set on the screen (these are also .png). It seems like it is only drawing the 3d scene into the screenshot and leaving out 2d stuff. I am taking a screenshot of the backbuffer, is this my problem? If so how can I solve it? Heres my code:

  // Save the screen shot
  if(!screenShotSaved && KEY_DOWN(VK_SNAPSHOT))
  {
     D3DDISPLAYMODE display;
     d3ddev->GetDisplayMode(0, &display);

     // Create the off screen surface with the same info as the back buffer
     d3ddev->CreateOffscreenPlainSurface(display.Width, display.Height, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, 
										 &ScreenShotSurface, NULL);

     // Save the back buffer to the surface
     d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &ScreenShotSurface);
     sprintf_s(screenshotdir,"Screenshots/Screenshot%d.png",i);
     D3DXSaveSurfaceToFile(screenshotdir, D3DXIFF_PNG, ScreenShotSurface, NULL, NULL);
     // Increase i for next screenshot (Only allow up to 10 screenshots max for memory safety)
    if(i>=10)
      i = 1;
    else
      i++;

    screenShotSaved = true;
  }


thanks.
Advertisement
First, that's a resource leak - GetBackBuffer() returns a pointer to a surface, so you newly created surface is lost. You're also not Release()ing the backbuffer pointer GetBackBuffer() gives you.

As for the actual problem, where are you calling that function? The correct place is after the last EndScene() call (and therefore after any ID3DXSprite::End() calls too), but before Present().

You should be using the Debug Runtimes, which will point out any obvious problems, and will show that memory leak I mentioned.
Hi EvilSteve,

thanks for taking out the time to help me.


I forgot to mention I am releasing it in my CleanD3D() function:

    if(ScreenShotSurface != NULL)    {         ScreenShotSurface->Release();         ScreenShotSurface = NULL;    }



and as for the screenshot function i am calling it:


AH DAMN!! THanks Steve, just realised when I was going to actually copy the code here that I am calling it before I draw the HUD in my render function.
I will change it to appear just before Present!

Another question I wanted to ask was about the FPS, I am calling my FPS function straight after:

d3ddev->Present(NULL, NULL, NULL, NULL);
// This will get the frames per second
GetFPS();

I will show my FPS function below, but what my problem is that I am getting around the region of 33 fps everytime? All I am doing in my program is calling a few .x meshes and rotating them. Is their a reason why my FPS is low?
Because I have seen similar tutorial demos, and in them I get FPS of about 220 or even one demo at 700fps.

I know this question is probably very awkward to answer as it depends on a lot of things but do you have any sujjesstions?

Here is my code:

// Gets and displays the Frames Per Secondvoid GetFPS(){   // Hold the number of frames per second in between seconds   static float FPS = 0.0f;   // Check if a second has passed   float nextSecond = 0.0f;   // Last second that occured   static float prevSecond = 0.0f;   // Add to the frames per second every time this function is called   FPS++;   // Get the second in millisecond then convert to seconds (by multiplying 0.001)   nextSecond = GetTickCount() * 0.001f;				   // If the time we have now substracted from the previous second is greater than   // or equal to 1 (i.e. if a second has passed) then we display the FPS number   if(nextSecond - prevSecond > 1.0f)      {         // Make the second we just got the previous second for next time the function is called	     prevSecond = nextSecond;		            // Display the FPS		 sprintf_s(frames,"FPS: %d",int(FPS));         // Reset the FPS counter         FPS = 0;      }}//End GetFPS


thanks alot for your help.
Quote:Original post by Nads
Hi EvilSteve,

thanks for taking out the time to help me.


I forgot to mention I am releasing it in my CleanD3D() function:

*** Source Snippet Removed ***

Why? Since you're not using it after you take the screenshot, you may as well release it as soon as you're done with it. This will also help prevent any mistakes; for example if you forget to call CleanD3D() every time you take a screenshot.
NextWar: The Quest for Earth available now for Windows Phone 7.
GetTickCount() is extremely inaccurate, it's only got a granularity of about 25ms. So if a frame takes under 25ms, it'll report a time of 0ms.

You should use QueryPerformanceCounter instead, or timeGetTime (QPC has it's own issues).

Here's my timer class (Which may have bugs of its own [smile]):

Header:
//============================================================================// PTimer.h - Timer class//============================================================================#ifndef __PTIMER_H__#define __PTIMER_H__#include <windows.h>class PTimer{public:	PTimer();	~PTimer();	// Get the time taken for the last frame in ms	float GetFrameTime() const { return m_fFrameTime; }	//========================================================================	void BeginFrame();	void EndFrame();protected:	float m_fFrameTime;	DWORD m_dwStartTime;	size_t m_nIndex;	bool m_bFirstLoop;	LARGE_INTEGER m_liFreq;	LARGE_INTEGER m_liStart;	static const size_t ms_nFramesAverage = 16;	float m_fFrameTimes[ms_nFramesAverage];};#endif // __PTIMER_H__


Source code:
//============================================================================// PTimer.cpp - Timer class//============================================================================#include "PTimer.h"#include <mmsystem.h>#pragma comment(lib, "winmm.lib")// Max time one frame can take (Clamped to this max value)static const DWORD s_dwMaxFrameTime = 1000;// Frame time threshold (in ms) under which QPC result is usedstatic const DWORD s_dwQPCThreshold = 5;//============================================================================PTimer::PTimer() :	m_dwStartTime(0),	m_nIndex(0),	m_bFirstLoop(true){	timeBeginPeriod(1);	QueryPerformanceFrequency(&m_liFreq);	m_liStart.QuadPart = 0;	for(size_t i=0; i<ms_nFramesAverage; ++i)		m_fFrameTimes = 0.0f;}PTimer::~PTimer(){	timeEndPeriod(1);}//============================================================================void PTimer::BeginFrame(){	m_dwStartTime = timeGetTime();	QueryPerformanceCounter(&m_liStart);}void PTimer::EndFrame(){	// Get frame time (via QPC and timeGetTime())	LARGE_INTEGER liFrameTime;	QueryPerformanceCounter(&liFrameTime);	liFrameTime.QuadPart -= m_liStart.QuadPart;	DWORD dwFrameTime = timeGetTime() - m_dwStartTime;	bool bUseTimeGetTime;	float fTime = 0.0f;	// If this was a fast frame, use QPC, and check results with timeGetTime	if(dwFrameTime < s_dwQPCThreshold)	{		fTime = ((float)(liFrameTime.QuadPart*1000) / (float)m_liFreq.QuadPart);		// Validate nonsense answers		if(fTime > (float)s_dwQPCThreshold)			bUseTimeGetTime = true;		else			bUseTimeGetTime = false;	}	else		bUseTimeGetTime = true;	// This was a slow frame, don't care what QPC says, use timeGetTime	if(bUseTimeGetTime)	{		if(dwFrameTime > s_dwMaxFrameTime)			dwFrameTime = s_dwMaxFrameTime;		fTime = (float)dwFrameTime;	}	// Record frame	m_fFrameTimes[m_nIndex++] = fTime;	if(m_nIndex >= ms_nFramesAverage)	{		m_bFirstLoop = false;		m_nIndex = 0;	}	// Calculate total	float fTotal = m_fFrameTimes[0];	for(size_t i=1; i<ms_nFramesAverage; ++i)		fTotal += m_fFrameTimes;	// Average	if(m_bFirstLoop)		m_fFrameTime = fTotal / (float)m_nIndex;	else		m_fFrameTime = fTotal / (float)ms_nFramesAverage;}//============================================================================


It's a bit complicated, but it uses QPC for quick frames (Since it's more accurate), and timeGetTime for slower ones. If QPC returns rubbish (Which it can do on dual-core CPUs with no CPU driver, and laptops), it falls back to timeGetTime(). And it also averages the FPS over 16 frames to stop it jumping around wildly.
Quote:Original post by Sc4Freak
Quote:Original post by Nads
Hi EvilSteve,

thanks for taking out the time to help me.


I forgot to mention I am releasing it in my CleanD3D() function:

*** Source Snippet Removed ***

Why? Since you're not using it after you take the screenshot, you may as well release it as soon as you're done with it. This will also help prevent any mistakes; for example if you forget to call CleanD3D() every time you take a screenshot.



thanks, I will change that.
Quote:Original post by Evil Steve
GetTickCount() is extremely inaccurate, it's only got a granularity of about 25ms. So if a frame takes under 25ms, it'll report a time of 0ms.

You should use QueryPerformanceCounter instead, or timeGetTime (QPC has it's own issues).

Here's my timer class (Which may have bugs of its own [smile]):

Header:
*** Source Snippet Removed ***

Source code:
*** Source Snippet Removed ***

It's a bit complicated, but it uses QPC for quick frames (Since it's more accurate), and timeGetTime for slower ones. If QPC returns rubbish (Which it can do on dual-core CPUs with no CPU driver, and laptops), it falls back to timeGetTime(). And it also averages the FPS over 16 frames to stop it jumping around wildly.


Thanks Steve.

I did try using timeGetTime aswell last night, but it gave me the same result!

Your timer class looks highly complicated, but when I get home tonight (currently at work) I will try it out and see if I can understand and incorporate it into my demo.

Are you actually calling your begin and end functions in the render function, like how I called mine after ->Present()?

Do you use this timer class just for the FPS, or is it used for other things too?

Also in your header file you have:

PTimer();
~PTimer();

its probably a very beginner question but whats the ~ stand for?

Thanks.
Quote:Original post by Nads
Thanks Steve.

I did try using timeGetTime aswell last night, but it gave me the same result!
Did you call timeBeginPeriod(1); ? That increases the accuracy of timeGetTime() (Called from the constructor in my class).

Quote:Original post by Nads
Your timer class looks highly complicated, but when I get home tonight (currently at work) I will try it out and see if I can understand and incorporate it into my demo.

Are you actually calling your begin and end functions in the render function, like how I called mine after ->Present()?

Do you use this timer class just for the FPS, or is it used for other things too?
Yeah, my main loop is:
void PApp::MainLoop(){	do	{		m_timer.BeginFrame();		Loop(true);		m_timer.EndFrame();	} while(!ShouldExit());}

Where Loop() does all the game logic and rendering. I only use the timer for keeping track of FPS currently, but there's no reason it couldn't be used for anything else - although you'd probably want to remove the frame time averaging code.


Quote:Original post by Nads
Also in your header file you have:

PTimer();
~PTimer();

its probably a very beginner question but whats the ~ stand for?
The first function is the constructor, which is called when the object is created (And used to initialise any member variables and state). The second one (~PTimer) is the destructor, which is called when the object is destroyed (When it goes out of scope or it's deleted), and is used to clean up after the object.
For example, a rendering class might call IDirect3DDevice9::Release() from the destructor to make sure that the device was always cleaned up.


Thanks again Steve.

Quote:Did you call timeBeginPeriod(1); ? That increases the accuracy of timeGetTime() (Called from the constructor in my class).


No I didnt try that but will do tonight. But even then how much can it increase it by?? As I mentioned earlier I was getting about 33FPS, where I am expecting atleast over 100FPS. To me it just feels like I was doing the function wrong although i did take tips from many tutorials. Hence I dont think timeBeginPeriod(1) could fix it by that much (although I could be wrong and therefore will try it!)

Thanks for explaining about the deconstructor, it isnt something I had come across before but will also read up on it now that i know what it is.

Quote:Original post by Nads
No I didnt try that but will do tonight. But even then how much can it increase it by?? As I mentioned earlier I was getting about 33FPS, where I am expecting atleast over 100FPS. To me it just feels like I was doing the function wrong although i did take tips from many tutorials. Hence I dont think timeBeginPeriod(1) could fix it by that much (although I could be wrong and therefore will try it!)
From the timeGetTime docs: "Windows NT/2000: The default precision of the timeGetTime function can be five milliseconds or more, depending on the machine." 5ms would give you a max FPS of 200FPS though, so I would have thought the default should be fine.

This topic is closed to new replies.

Advertisement