Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

Threaded screenshots (no d3d calls)

Joined
Jan 9, 2009
Messages
614
Reaction score
152
Got tired of screenshots using d3d and causing jitter, so i removed all d3d calls and detached a thread to make the screenshots smooth (ive tested and it seems stable, but I can't guarantee that the thread will execute before the if statement at the end of the screenshot function, thus prompting th euser the screenshot failed when it didn't).Anyways, onto the code:

Find ZGameInterface::SaveScreenShot()

replace the entire function with this:
Code:
void ZGameInterface::SaveScreenShot()
{
	static unsigned long int st_nLastTime = 0;
	unsigned long int nNowTime = timeGetTime();
#define SCREENSHOT_DELAY		2000


	if ((nNowTime-st_nLastTime) < SCREENSHOT_DELAY)	return;
	st_nLastTime = nNowTime;


	char szPath[_MAX_PATH];
	char szFilename[_MAX_PATH];
	char szFilenameSafe[_MAX_PATH];

	TCHAR szMyDocPath[MAX_PATH];
	if(GetMyDocumentsPath(szMyDocPath)) {
		strcpy(szPath,szMyDocPath);
		strcat(szPath,GUNZ_FOLDER);
		CreatePath( szPath );
		strcat(szPath,SCREENSHOT_FOLDER);
		CreatePath( szPath );
		strcat(szPath, "/");
	}


	SYSTEMTIME t;
	GetLocalTime( &t );
	char szCharName[MATCHOBJECT_NAME_LENGTH];
	ValidateFilename(szCharName, ZGetMyInfo()->GetCharName(), '_');

	sprintf(szFilename,"%s_%4d%02d%02d_%02d%02d%02d",  
		szCharName, t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);

	sprintf(szFilenameSafe,"nocharname_%4d%02d%02d_%02d%02d%02d",  
		t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);

	RECT rect;
        ///Note: if you want the entire window, use GetWindowRect instead.
	::GetClientRect(g_hWnd, &rect);
	char szFullPath[_MAX_PATH];	
	char szFullPathToNotify[_MAX_PATH];
	sprintf(szFullPath,"%s%s", szPath, szFilename);
	sprintf(szFullPathToNotify,"/Gunz/Screenshots/%s.jpg", szFilename);
	bool bSuccess = false;

	std::thread th(RScreenShot, std::ref(bSuccess), g_hWnd, rect.right - rect.left, rect.bottom - rect.top, szFullPath);
	th.detach();

	if (bSuccess = false)
		ZChatOutput(MCOLOR(ZCOLOR_CHAT_SYSTEM), ZMsg(MSG_SCREENSHOT_CANT_SAVE));
	else {
		char szOutput[256];
		ZTransMsg(szOutput, MSG_SCREENSHOT_SAVED, 1, szFullPathToNotify);
		ZChatOutput(MCOLOR(ZCOLOR_CHAT_SYSTEM), szOutput);
	}
}

Next, open realspace2.h

find bool rscreenshot and replace it with this:

Code:
void RScreenShot(bool& result,HWND wnd,int x,int y,const char *szFilename);

Now open realspace2.cpp and replace rscreenshot there with this:
Code:
void RScreenShot(bool& result,HWND wnd,int x,int y,const char *szFilename)
{
	char szFullFileName[_MAX_DIR];

	#ifdef _USE_GDIPLUS
		sprintf(szFullFileName, "%s.jpg", szFilename);
		RSaveAsJpeg(result,wnd,x, y, szFullFileName);
	#else
		sprintf(szFullFileName, "%s.bmp", szFilename);
		return RSaveAsBmp(x, y, data, szFullFileName);
	#endif
}


Lastly, find rsaveasjpeg, and replace the whole function with this one:
Code:
	void RSaveAsJpeg(bool& result,HWND wnd,int x,int y,const char *szFilename)
	{
		HDC scrdc, memdc;
		HBITMAP membit;
		scrdc = GetDC(wnd);
		int Height = y;
		int Width = x;
		memdc = CreateCompatibleDC(scrdc);
		membit = CreateCompatibleBitmap(scrdc, Width, Height);
		HBITMAP hOldBitmap = (HBITMAP)SelectObject(memdc, membit);
		BitBlt(memdc, 0, 0, Width, Height, scrdc, 0, 0, SRCCOPY);

		Gdiplus::Bitmap bitmap(membit, NULL);
		CLSID clsid;
		EncoderParameters encoderParameters;
		ULONG             quality = 100;
		encoderParameters.Count = 1;

		encoderParameters.Parameter[0].Guid = EncoderQuality;
		encoderParameters.Parameter[0].Type = EncoderParameterValueTypeLong;
		encoderParameters.Parameter[0].NumberOfValues = 1;
		encoderParameters.Parameter[0].Value = &quality;

		// Make WFileName
		WCHAR wstrName[256];
		int nNameLen = strlen(szFilename) + 1;
		MultiByteToWideChar(CP_ACP, 0, szFilename, -1, wstrName, nNameLen - 1);
		wstrName[nNameLen - 1] = 0;

		// Save Bitmap
		CLSID  Clsid;
		int ret = GetCodecClsid(L"image/jpeg", &Clsid);

		if (bitmap.Save(wstrName, &Clsid, &encoderParameters) == Ok) {

			SelectObject(memdc, hOldBitmap);

			DeleteObject(memdc);

			DeleteObject(membit);

			ReleaseDC(wnd, scrdc);

			result = true;
		}
		else {
			result = false;
		}

You should now have very smooth screenshots (if the screenshot size is too large for you, reduce the quality variable in rsaveasjpeg to 50 instead of 100.
 
Experienced Elementalist
Joined
Oct 14, 2015
Messages
290
Reaction score
83
Thanks for the release.
 
Junior Spellweaver
Joined
Jun 14, 2015
Messages
123
Reaction score
20
That looks like it's not going to work at all in fullscreen
 
Junior Spellweaver
Joined
Jun 14, 2015
Messages
123
Reaction score
20
Well, that's sort of an inherent problem with the non-D3D approach as far as I can see. I don't think there's really *anything* in userland that can acquire the frame buffer in fullscreen other than D3D. The entire point of "fullscreen" is that D3D has a direct line to the graphics driver, completely disconnected from the normal way Windows deals with drawing windows.

This is also why e.g. puush's screen capture or f.lux's color manipulation don't work in fullscreen games. Certain applications like Fraps can touch fullscreen games, but they do that AFAIK by injecting into the game process and interacting with the graphics API.

You say "using D3D and causing jitter" in the OP as if it's D3D itself's fault, which I don't think it is.

Saving a screenshot seems like it'd only require you to:

1. Move the frame buffer memory back into your own RAM buffer
2. Encode it to the file format you want
3. Save the result to disk

It seems to me like step 1 is the only step involving D3D at all, and that should take much less time than the other two steps.

If your approach in the last thread about this (where I think you used D3DXSaveTextureToFile or w/e it's called, off-thread, with that threaded device flag) was still causing lag, that sounds like it's a problem with that specific D3DX function locking the device for longer than it needs.

And if step 1 is somehow *really* that slow, you could just split the work up into several frames.
 
Joined
Jan 9, 2009
Messages
614
Reaction score
152
Well, that's sort of an inherent problem with the non-D3D approach as far as I can see. I don't think there's really *anything* in userland that can acquire the frame buffer in fullscreen other than D3D. The entire point of "fullscreen" is that D3D has a direct line to the graphics driver, completely disconnected from the normal way Windows deals with drawing windows.

This is also why e.g. puush's screen capture or f.lux's color manipulation don't work in fullscreen games. Certain applications like Fraps can touch fullscreen games, but they do that AFAIK by injecting into the game process and interacting with the graphics API.

You say "using D3D and causing jitter" in the OP as if it's D3D itself's fault, which I don't think it is.

Saving a screenshot seems like it'd only require you to:

1. Move the frame buffer memory back into your own RAM buffer
2. Encode it to the file format you want
3. Save the result to disk

It seems to me like step 1 is the only step involving D3D at all, and that should take much less time than the other two steps.

If your approach in the last thread about this (where I think you used D3DXSaveTextureToFile or w/e it's called, off-thread, with that threaded device flag) was still causing lag, that sounds like it's a problem with that specific D3DX function locking the device for longer than it needs.

And if step 1 is somehow *really* that slow, you could just split the work up into several frames.

Yep, all i had to do was take 5 minutes to read where gdiplus can't do it, request mods close this thread. still works fine if you check for being in widnwoed mode, so i'll probably keep it as an optional way to screenshot. From what im reading, the issue seems to be getfrontbufferdata being dreadfully slow, which is what maiet used. trying to get it from the backbuffer just gives ya a blank screen. Hoped to avoid having to use the multithread flag, but it seems everyone either has to do that or deal with frontbufferdata taking almost a second to handle its business.



Requests mods close this thread, sorry guys. I'll release something good =very soon to make up for a poop release.
 
Last edited:
Back
Top