Handling "Lost Device", Changing res. and AA at ru

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
belfegor
Posts: 383
Joined: Mon Sep 18, 2006 7:22 pm
Location: Serbia

Handling "Lost Device", Changing res. and AA at ru

Post by belfegor »

This is the code for changing screen resolution and AntiAliasing at run-time.
It is for Windows/DirectX9/HAL-device type/FullScreen only.
With example that i have provided at the end of the post,
press 'R' to switch resolutions, press 'alt-tab' to jump out of app
and again to return to test device "recovery".
Some of code is copied from irrlicht "RT" as it is specific to
"alt-tabbing", and "room" uses shader from "shaders" example.
From DX help, something like "as of DX9 shaders doesn't need to be
released propertly before calling IDirect3DDevice9::Reset()"
so i put it as a test also.

This isn't tested thruoutly, so it might contain terrible bugs
and some things might be done better but as i am a noob i am
open for suggestions if you have any.

This is tested on WinXPSP2, MSVC++2005 Express,
DX SDK (June 2006) and Irrlicht 1.3.1 (not SVN).

Following things i have change for this in irrlicht source code,
open ../include/IrrCompileConfig.h and comment following
(some of them are already commented but i'll list them,
note: might work with some of them but i didnt need them):

Code: Select all

//#define _IRR_COMPILE_WITH_DIRECT3D_8_
//#define _IRR_COMPILE_WITH_OPENGL_
//#define _IRR_COMPILE_WITH_BURNINGSVIDEO_
//#define _IRR_COMPILE_WITH_X11_
//#define _IRR_LINUX_X11_RANDR_
//#define _IRR_COMPILE_WITH_ZLIB_
//#define _IRR_USE_NON_SYSTEM_ZLIB_
//#define _IRR_COMPILE_WITH_LIBPNG_
//#define _IRR_USE_NON_SYSTEM_LIB_PNG_
//#define BURNINGVIDEO_RENDERER_BEAUTIFUL
//#define BURNINGVIDEO_RENDERER_FAST
//#define BURNINGVIDEO_RENDERER_ULTRA_FAST
//#define IRRLICHT_FAST_MATH
now open ../include/IrrlichtDevice.h and add:

Code: Select all

virtual void SetResolutionAndFSAA(
			void (*OnLostDeviceCallback)(), 
			void (*OnResetDeviceCallback)(),
			core::dimension2d<s32> newRes, u16 newFSAA = 0) = 0;
then open ../include/IVideoDriver.h and add:

Code: Select all

virtual void ResetDevice(
			void (*OnLostDeviceCallback)(), 
			void (*OnResetDeviceCallback)(),
			core::dimension2d<s32> newResolution, u16 newFSAA) = 0;
then open ../source/CNullDriver.h and add decl.:

Code: Select all

virtual void ResetDevice(
			void (*OnLostDeviceCallback)(), 
			void (*OnResetDeviceCallback)(),
			core::dimension2d<s32> newResolution, u16 newFSAA);
then open ../source/CNullDriver.cpp and add def.:

Code: Select all

void CNullDriver::ResetDevice(
			void (*OnLostDeviceCallback)(), 
			void (*OnResetDeviceCallback)(),
			core::dimension2d<s32> newResolution, u16 newFSAA)
{
	// do nothing
}
then open ../source/CD3D9Driver.h and add decl.:

Code: Select all

virtual void ResetDevice(
			void (*OnLostDeviceCallback)(), 
			void (*OnResetDeviceCallback)(),
			core::dimension2d<s32> newResolution, u16 newFSAA);
then open ../source/CD3D9Driver.cpp and add def.:

Code: Select all

void CD3D9Driver::ResetDevice(
			void (*OnLostDeviceCallback)(), 
			void (*OnResetDeviceCallback)(),
			core::dimension2d<s32> newResolution, u16 newFSAA)
{
	HRESULT hr = NULL;

	if(DeviceLost)
	{
		Sleep(100);
		hr = pID3DDevice->TestCooperativeLevel();
		if(hr != D3DERR_DEVICENOTRESET)
			return;
	}

	DWORD qualityLevel = NULL;
	D3DMULTISAMPLE_TYPE AAtype[15] = 
	{
		D3DMULTISAMPLE_2_SAMPLES,
        D3DMULTISAMPLE_3_SAMPLES,
        D3DMULTISAMPLE_4_SAMPLES,
        D3DMULTISAMPLE_5_SAMPLES,
        D3DMULTISAMPLE_6_SAMPLES,
        D3DMULTISAMPLE_7_SAMPLES,
        D3DMULTISAMPLE_8_SAMPLES,
        D3DMULTISAMPLE_9_SAMPLES,
        D3DMULTISAMPLE_10_SAMPLES,
        D3DMULTISAMPLE_11_SAMPLES,
        D3DMULTISAMPLE_12_SAMPLES,
        D3DMULTISAMPLE_13_SAMPLES,
        D3DMULTISAMPLE_14_SAMPLES,
        D3DMULTISAMPLE_15_SAMPLES,
        D3DMULTISAMPLE_16_SAMPLES
	};

	present.Windowed         = FALSE;
	present.BackBufferWidth  = newResolution.Width;
	present.BackBufferHeight = newResolution.Height;

	if(newFSAA > 1 && newFSAA < 17)
	{
		hr = pID3D->CheckDeviceMultiSampleType(D3DADAPTER_DEFAULT,
		           D3DDEVTYPE_HAL, present.BackBufferFormat, FALSE,
		           AAtype[newFSAA - 2], &qualityLevel);
		if(hr = D3D_OK)
		{
			present.MultiSampleType    = AAtype[newFSAA - 2];
			present.MultiSampleQuality = qualityLevel - 1;
			present.SwapEffect         = D3DSWAPEFFECT_DISCARD;
		}
	}
	else
	{
		present.MultiSampleType    = D3DMULTISAMPLE_NONE;
		present.MultiSampleQuality = NULL;
		present.SwapEffect         = D3DSWAPEFFECT_FLIP;
	}

      OnLostDeviceCallback();

	hr = pID3DDevice->Reset(&present);
	if(hr != D3D_OK)
	{
            os::Printer::log("Device Reset Failed!", ELL_WARNING);
		DeviceLost = true;
		return;
	}

	os::Printer::log("Device Reset Succesfull!", ELL_WARNING);

	ResetRenderStates = true;
	LastVertexType = (E_VERTEX_TYPE)-1;

	setVertexShader(EVT_STANDARD);
	setRenderStates3DMode();
	setFog(FogColor, LinearFog, FogStart, FogEnd, FogDensity, PixelFog, RangeFog);
	setAmbientLight(AmbientLight);

	OnResetDeviceCallback();

      CNullDriver::OnResize(
		core::dimension2d<s32>(present.BackBufferWidth, present.BackBufferHeight));

	DeviceLost = false;
}
also find (in same file) and edit funcs. to look like this :

Code: Select all

bool CD3D9Driver::beginScene(bool backBuffer, bool zBuffer, SColor color)
{
	CNullDriver::beginScene(backBuffer, zBuffer, color);
	HRESULT hr;

	if (!pID3DDevice)
		return false;

	if (DeviceLost)
	{
		return false;
	}

	DWORD flags = 0;

	if (backBuffer)
		flags |= D3DCLEAR_TARGET;

	if (zBuffer)
		flags |= D3DCLEAR_ZBUFFER;

	if (StencilBuffer)
		flags |= D3DCLEAR_STENCIL;

	hr = pID3DDevice->Clear( 0, NULL, flags, color.color, 1.0, 0);
	if (FAILED(hr))
		os::Printer::log("DIRECT3D9 clear failed.", ELL_WARNING);

	hr = pID3DDevice->BeginScene();
	if (FAILED(hr))
	{
		os::Printer::log("DIRECT3D9 begin scene failed.", ELL_WARNING);
		return false;
	}

	DeviceLost = false;

	return true;
}

...

void CD3D9Driver::OnResize(const core::dimension2d<s32>& size)
{
	if (!pID3DDevice)
		return;
	DeviceLost = true;
}
then open ../source/CIrrDeviceWin32.h and find in CIrrDeviceWin32 class
class CCursorControl and add public func. to it:

Code: Select all

void ResetCC(core::dimension2d<s32> newSize)
			{
				WindowSize.Width     = newSize.Width;
				WindowSize.Height    = newSize.Height;
				InvWindowSize.Width  = 1.0f / newSize.Width;
				InvWindowSize.Height = 1.0f / newSize.Height;
				BorderX = 0;
				BorderY = 0;
			}
and to CIrrDeviceWin32 class decl.:

Code: Select all

virtual void SetResolutionAndFSAA(
			void (*OnLostDeviceCallback)(), 
			void (*OnResetDeviceCallback)(),
			core::dimension2d<s32> newRes, u16 newFSAA = 0);
then open ../source/CIrrDeviceWin32.cpp and add def.:

Code: Select all

void CIrrDeviceWin32::SetResolutionAndFSAA(
	void (*OnLostDeviceCallback)(), 
	void (*OnResetDeviceCallback)(),
	core::dimension2d<s32> newRes, u16 newFSAA)
{
	getVideoDriver()->ResetDevice(
		OnLostDeviceCallback,
		OnResetDeviceCallback,
		newRes, newFSAA);
	getWin32CursorControl()->ResetCC(
		core::dimension2d<s32>(newRes.Width, newRes.Height));
}

...
// delete content of this function:
void CIrrDeviceWin32::resizeIfNecessary()
{
}
and that should be it with the source. Next make sure you use
"Rebuild solution" instead "Build". Hopefully i didnt forgat some function
to list here but if you got stuck i'll try and help.

and here is the example (dont forget to copy newly created .dll into your
project/exe folder also):

main.cpp

Code: Select all

#include <irrlicht.h>
using namespace irr;
#pragma comment(lib, "Irrlicht.lib")

#define NULA 0

IrrlichtDevice* device           = NULA;
video::IVideoDriver* driver      = NULA;
scene::IBillboardSceneNode* bill = NULA;
video::ITexture* rtTexture       = NULA;

static s32 index = NULA;
core::dimension2d<s32> screenRes[3] = 
{
	core::dimension2d<s32>(640, 480),
	core::dimension2d<s32>(800, 600),
	core::dimension2d<s32>(1024, 768)
};

void DestroyerFunc();
void RaiseDeadFunc();

class MyEventReceiver : public IEventReceiver
{
public:
	bool OnEvent(SEvent event)
	{
		if(event.EventType == EET_KEY_INPUT_EVENT &&
			event.KeyInput.Key == KEY_ESCAPE &&
			!event.KeyInput.PressedDown)
		{
			device->closeDevice();
			return true;
		}
		if(event.EventType == EET_KEY_INPUT_EVENT &&
			event.KeyInput.Key == KEY_KEY_R &&
			!event.KeyInput.PressedDown)
		{
			device->SetResolutionAndFSAA(
				DestroyerFunc, RaiseDeadFunc,
				screenRes[index]);
			index++;
			if(index > 2)
				index = NULA;
			return true;
		}
		return false;
	}
};

class MyShaderCallBack : public video::IShaderConstantSetCallBack
{
public:
	virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData)
	{
		video::IVideoDriver* driver = services->getVideoDriver();

		core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
        invWorld.makeInverse();
		services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);

		core::matrix4 worldViewProj;
        worldViewProj = driver->getTransform(video::ETS_PROJECTION);			
        worldViewProj *= driver->getTransform(video::ETS_VIEW);
        worldViewProj *= driver->getTransform(video::ETS_WORLD);
		services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);

		core::vector3df pos = bill->getPosition();
		services->setVertexShaderConstant("mLightPos", reinterpret_cast<f32*>(&pos), 3);

		video::SColorf col(0.4f, 0.4f, 0.4f, 1.0f);
		services->setVertexShaderConstant("mLightColor", reinterpret_cast<f32*>(&col), 4);

		core::matrix4 world = driver->getTransform(video::ETS_WORLD);
        world = world.getTransposed();
		services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
	}
};

int main()
{
	MyEventReceiver receiver;
	SIrrlichtCreationParameters param;
	param.AntiAlias                 = false;
	param.Bits                      = 32;
	param.DriverType                = video::EDT_DIRECT3D9;
	param.EventReceiver             = &receiver;
	param.Fullscreen                = false;
	param.HighPrecisionFPU          = true;
	param.Stencilbuffer             = true;
	param.Vsync                     = false;
	param.WindowSize                = screenRes[index];
	device = createDeviceEx(param);

	driver  = device->getVideoDriver();
	gui::IGUIEnvironment* guid   = device->getGUIEnvironment();
	scene::ISceneManager* smgr   = device->getSceneManager();

	gui::IGUIFont* font = guid->getFont("fontcourier.bmp");
	gui::IGUIStaticText* text = guid->addStaticText(
		L"", core::rect<s32>(30, 30, 350, 60),
		true, false, 0, -1, true);
	text->setOverrideColor(video::SColor(255, 96, NULA, NULA));
	text->setOverrideFont(font);
	text->setBackgroundColor(video::SColor(255, 128, 128, 128));

	c8* shaderFileName = "d3d9.hlsl";
	s32 shaderMaterial;
	video::IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
	MyShaderCallBack* mc = new MyShaderCallBack();
	shaderMaterial = gpu->addHighLevelShaderMaterialFromFiles(
				shaderFileName,	"vertexMain", video::EVST_VS_1_1,
				shaderFileName, "pixelMain", video::EPST_PS_2_0,
				mc, video::EMT_SOLID);
	mc->drop();

	video::ITexture* texture   = driver->getTexture("rockwall.bmp");
	scene::IAnimatedMesh* room = smgr->getMesh("room.3ds");
	smgr->getMeshManipulator()->makePlanarTextureMapping(room->getMesh(NULA), 0.003f);
	scene::IAnimatedMeshSceneNode* aroom = smgr->addAnimatedMeshSceneNode(room);
	aroom->setMaterialTexture(NULA, texture);
	aroom->setMaterialType((video::E_MATERIAL_TYPE)shaderMaterial);

	video::ITexture* billTexture     = driver->getTexture("particlered.bmp");
	bill = smgr->addBillboardSceneNode();
	bill->setMaterialFlag(video::EMF_LIGHTING, false);
	bill->setMaterialTexture(0, billTexture);
	bill->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);

	scene::ISceneNodeAnimator* rotAnim = smgr->createFlyCircleAnimator(
		core::vector3df(0.0f, 200.0f, 0.0f), 100.0f, 0.001f, core::vector3df(0.0f, 1.0f, 0.0f));

	bill->addAnimator(rotAnim);
	rotAnim->drop();

	scene::IAnimatedMesh* fairy = smgr->getMesh("faerie.md2");
	scene::IAnimatedMeshSceneNode* afairy = smgr->addAnimatedMeshSceneNode(fairy);
	afairy->setMaterialFlag(video::EMF_LIGHTING, false);
	afairy->setMaterialTexture(0, driver->getTexture("faerie2.bmp"));
	afairy->setPosition(core::vector3df(0.0f, 150.0f, 30.0f));

	RaiseDeadFunc();

	scene::ISceneNode* test = smgr->addCubeSceneNode(40.0f);
	test->setPosition(core::vector3df(-100.f, 150.f, 50.0f));
	test->setMaterialFlag(video::EMF_LIGHTING, false);
	test->setMaterialTexture(0, rtTexture);

	scene::ICameraSceneNode* fpsCam = smgr->addCameraSceneNodeFPS();
	fpsCam->setPosition(core::vector3df(0.0f, 190.0f, -100.0f));
	fpsCam->setTarget(core::vector3df(0.0f, 150.0f, 1.0f));

	scene::ICameraSceneNode* fixedCam = smgr->addCameraSceneNode(
		0, core::vector3df(30.0f, 170.0f, 0.0f),
		core::vector3df(afairy->getPosition()));

	video::SColor bsClearColor = video::SColor(NULA);
	video::SColor rtClearColor = video::SColor(NULA);

	while(device->run())
	{
		if(driver->beginScene(true, true, bsClearColor))
		{	
			if(rtTexture)
			{
				driver->setRenderTarget(rtTexture, true, true, rtClearColor);

				test->setVisible(false);
				smgr->setActiveCamera(fixedCam);

				smgr->drawAll();

				driver->setRenderTarget(0);

				test->setVisible(true);
				smgr->setActiveCamera(fpsCam);
			}

			smgr->drawAll();
			guid->drawAll();

			core::stringw str;
			str += L"FPS[";
			str += driver->getFPS();
			str += L"]";
			str += L" Resolution: ";
			str += driver->getScreenSize().Width;
			str += L"x";
			str += driver->getScreenSize().Height;
			text->setText(str.c_str());
			driver->endScene();
		}
		else
		{
			device->SetResolutionAndFSAA(
				DestroyerFunc, RaiseDeadFunc, screenRes[index]);
		}
	}
	DestroyerFunc();
	device->drop();
	return 0;
}

void DestroyerFunc()
{
	device->getLogger()->log("OnLostDeviceCallback() Called!", ELL_WARNING);
	if(rtTexture)
	{
		driver->removeTexture(rtTexture);
		rtTexture->drop();
		rtTexture = NULA;
	}
}

void RaiseDeadFunc()
{
	device->getLogger()->log("OnResetDeviceCallback() Called!", ELL_WARNING);
	rtTexture = driver->createRenderTargetTexture(
		core::dimension2d<s32>(128, 128));
}
All necesarry media you can find into irrlicht/media folder witch comes with SDK.

For the end i would like to point few problematic things (that i know of for now
and/or/will fix):
1. Camera target resets to (0, 0, 0) when alt-tabbing. (maybie save cam target pos
just befor device reset)
2. My card only supports max 4x AA so i could not try others.
3. I'v read somewhere that RT and AA doesnt fit together.
4. Could add changing screen refresh rate too.

If you have comments/corrections/addons i would like to hear them.

Hope you find this usefull. Enjoy!
Belfegor.
Small FPS demo made using Irrlicht&NewtonDEMO
InfoHERE
Its at very early stage but i think im crazy enough to finish it all alone.
BlindSide
Admin
Posts: 2821
Joined: Thu Dec 08, 2005 9:09 am
Location: NZ!

Post by BlindSide »

This looks great, I'll have to try it sometime.
ShadowMapping for Irrlicht!: Get it here
Need help? Come on the IRC!: #irrlicht on irc://irc.freenode.net
belfegor
Posts: 383
Joined: Mon Sep 18, 2006 7:22 pm
Location: Serbia

Post by belfegor »

I always like the games when you don't need to restart to change certain options. For me it is a big +.

As i understand all irrlicht texture (except RT) are created in D3DPOOL_MANAGED so you dont need to release/recreate them witch gives on a speed when switching certain game options.

And for irrlicht doesnt use Hardware buffers for meshes there isnt nothing to with the meshes to.
Small FPS demo made using Irrlicht&NewtonDEMO
InfoHERE
Its at very early stage but i think im crazy enough to finish it all alone.
Post Reply