Sign in to follow this  

40 fps max for a 2D arkanoid-like game

This topic is 4512 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I've a problem, my game runs at 40 fps max windowed and it's lagging. Even if I remove most of the textures, that wouldn't change anything. I need it to run at 60 fps but can't figure it out. It runs at 50 fps on fullscreen. I'm using D3D to render 2D sprites. I'm not using the ID3DXSprite class, but my own 2D motor using textures on rectangles. Here's the code : Render code :
			// Clear the backbuffer to a black color
			Device_D3D->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 0, 0, 0 ), 1.0f, 0 );

		KeyPressedProcess();

		if ( BALLE.IsMoving == true )
		{
			BallMove();
		}

		Blit( Contour, 0, 0, RectContour, DefaultRect, 0xFFFFFFFF);
		//DrawBG(CONTOURJEU.TextureName, 0.0f, 0.0f);

		if( LaunchPixelTrace == false )
			Blit( Fond, 64, 64, RectFond, DefaultRect, 0xFFFFFFFF);
			//DrawBG(FOND.TextureName, 64.0f, 64.0f);

		Blit( Sprites, (int) RAQUETTE.x, (int) RAQUETTE.y, RectRaquette, DefaultRect, 0xFFFFFFFF);
		Blit( Sprites, (int) BALLE.x, (int) BALLE.y, RectBalle, DefaultRect, 0xFFFF00FF);
		//DrawSprite(TEXTURE.TextureName, RectRaquette, RAQUETTE.x, RAQUETTE.y);
		//DrawSprite(TEXTURE.TextureName, RectBalle, BALLE.x, BALLE.y);

		if( LaunchPixelTrace == true && BALLE.IsMoving == true )
		{
			DrawPixelTest( BALLE.x, BALLE.y );

			for( int i = 0; i < 600; i++ )
			{
				for( int j = 0; j < 800; j++ )
				{
					if( PTRACE[i][j].IsAlive == true )
						Blit( PxTrace, (int) PTRACE[i][j].x, (int) PTRACE[i][j].y, RectPTrace, DefaultRect, 0xFFFFFFFF);
						//DrawSprite(PIXELTRACE.TextureName, RectPTrace, PTRACE[i][j].x, PTRACE[i][j].y);
				}
			}
		}

		for( int i = 0; i < 10; i++ )
		{
			for( int j = 0; j < 21; j++ )
			{
				if( BRIQUE[i][j].IsAlive == true )
					Blit( Sprites, (int) BRIQUE[i][j].x, (int) BRIQUE[i][j].y, RectBrique, DefaultRect, 0xFFFFFFFF);
					//DrawSprite(TEXTURE.TextureName, RectBrique, BRIQUE[i][j].x, BRIQUE[i][j].y);
			}
		}

		Device_D3D->SetRenderTarget(D3DUSAGE_RENDERTARGET, Contour.lpSurface);
		WriteText( CalculFPS(), 80, 0, 0xFFFFFFFF );
		Device_D3D->SetRenderTarget(D3DUSAGE_RENDERTARGET, BackBuffer_Surface);

        // End the scene
        Device_D3D->EndScene();


Init_D3D code :
HRESULT Init_D3D(HWND hWnd, bool PleinEcran, int Largeur, int Hauteur)
{
	// Crée l'objet D3D, qui est requis pour créer le D3DDevice.
	if( ( Objet_D3D = Direct3DCreate9( D3D_SDK_VERSION ) ) == NULL )
        return E_FAIL;

	// Initialisation de l’objet D3DPRESENT_PARAMETERS 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );

	// Selon le mode désiré, on lance la fonction adéquate
	if( PleinEcran )
	{
		if ( FAILED( Init_PleinEcran( hWnd, Largeur, Hauteur ) ) )
			return E_FAIL;
	}

	else
	{
		if ( FAILED( Init_Fenetre( hWnd, Largeur, Hauteur ) ) )
			return E_FAIL;
	}
	
	// Paramètres d’affichage pour l'utilisation de la clé de couleur et de la transparence
	Device_D3D->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE ); // Lance le test Alpha ( permet de tester chaque pixel )
	Device_D3D->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_NOTEQUAL ); // Permet à l'application d'accepter ou de rejeter un pixel, suivant sa valeur alpha (ici, rejette si non égal à la valeur ci-dessous)
	Device_D3D->SetRenderState( D3DRS_ALPHAREF, 0x00000000 ); // Valeur Alpha de référence (octet sur chaque pixel définissant la transparence)
	Device_D3D->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ); // Permet d'autoriser l'alpha blending (couche transparente)
	Device_D3D->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
	Device_D3D->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA );
	Device_D3D->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE );
	Device_D3D->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
	Device_D3D->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
	// Filtrage bilinéaire pour un effet d'antialiasing lors des distorsions des sprites
	Device_D3D->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	Device_D3D->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
	// Désactivation de la lumière (sinon les sprites seront noirs)
	Device_D3D->SetRenderState( D3DRS_LIGHTING, FALSE );

	// On obtient un pointeur sur la surface du BackBuffer
	if ( FAILED( Device_D3D->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &BackBuffer_Surface ) ) )
		return E_FAIL;


	//paramètres définissant les matrices de visualisation
	D3DXMatrixOrthoLH(&Ortho2D, (float)Largeur, (float)Hauteur, 0.0f, 1.0f);
	D3DXMatrixIdentity(&Identity);
	Device_D3D->SetTransform(D3DTS_PROJECTION, &Ortho2D);
	Device_D3D->SetTransform(D3DTS_WORLD, &Identity);
	Device_D3D->SetTransform(D3DTS_VIEW, &Identity);
	pVertices[0].z = pVertices[1].z = pVertices[2].z = pVertices[3].z = 1.0f;

	CurrentWidth = (float) Largeur;
	CurrentHeight = (float) Hauteur;

	return S_OK;
}


The Blit function code, which display the sprites :
void Blit(Texture IDTexture, int x_offset, int y_offset, RECT Rectangle_Source, RECT Rectangle_Destination, UINT Filtre_Transparence)
{
	float	Largeur_Destination=(float)(Rectangle_Destination.right - Rectangle_Destination.left),
			Largeur_Source=(float)(Rectangle_Source.right - Rectangle_Source.left),
			Hauteur_Source=(float)(Rectangle_Source.bottom - Rectangle_Source.top),
			Largeur_Texture=(float)IDTexture.Width,
			Hauteur_Texture=(float)IDTexture.Height;

	D3DXVECTOR2 echelle(1.0f,1.0f);

//dilatation du sprite si nécessaire
	if (Largeur_Destination!=0)
	{
		echelle.x = Largeur_Destination / Largeur_Source;
		echelle.y = (float)(Rectangle_Destination.bottom - Rectangle_Destination.top) / Hauteur_Source;
	}

	//on modifie ici les paramètres de notre rectangle 3D pour le rendu

	//couleur diffuse (avec transparence)
	pVertices[0].color = Filtre_Transparence;
	pVertices[1].color = Filtre_Transparence;
	pVertices[2].color = Filtre_Transparence;
	pVertices[3].color = Filtre_Transparence;

	//coordonnées spatiales
	//ATTENTION: l'offset de 0.5f ajouté sert à éviter un effet de bord désagréable
	pVertices[0].x = pVertices[3].x = x_offset - CurrentWidth / 2.0f - 0.5f ;
	pVertices[1].x = pVertices[2].x = x_offset - CurrentWidth / 2.0f - 0.5f + Largeur_Source * echelle.x;
	pVertices[0].y = pVertices[1].y = CurrentHeight / 2.0f - y_offset + 0.5f ;
	pVertices[2].y = pVertices[3].y = CurrentHeight / 2.0f - y_offset + 0.5f - Hauteur_Source * echelle.y;

	//coordonnées de texture
	pVertices[1].u = pVertices[2].u = Rectangle_Source.right/Largeur_Texture;
	pVertices[0].u = pVertices[3].u = Rectangle_Source.left/Largeur_Texture;
	pVertices[0].v = pVertices[1].v = Rectangle_Source.top/Hauteur_Texture;
	pVertices[2].v = pVertices[3].v = Rectangle_Source.bottom/Hauteur_Texture;

	//rendu des 4 vertices
	Device_D3D->SetVertexShader( NULL );
	Device_D3D->SetFVF( D3DFVF_RECTANGLE_VERTEX );
	Device_D3D->SetTexture( 0, IDTexture.lpTexture );
	Device_D3D->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, pVertices, sizeof(RECTANGLE_VERTEX) );
}


I'm using Visual Studio 2005 beta 2 (sent by Microsoft) and Directx 9 sdk from june 2005. EDIT : Ok, sorry, i've reorganised it. [Edited by - Zack08 on August 2, 2005 6:57:58 AM]

Share this post


Link to post
Share on other sites
Nobody has time to read through your entire source to find why it doesn't run as fast as you would like. I can tell it's D3D from the top of the code but you don't even tell us this, or what the game is doing, or anything. Is it full-screen or windowed? What resolution are you using? What hardware do you have?

But looking at your vertex/index buffer locking and the creation flags you set for them might be a good start. Are you using DrawPrimitive or DrawPrimitiveUP, and how many triangles are you drawing at once? These are just general pointers though, not specific since I don't have time to wade through your pages of program!

Share this post


Link to post
Share on other sites
Yep, okay, sorry, I've edited it.

Is there something I'm missing on the Blit code ? I've tried to use ID3DXSprite class before making it by my own, but I've the same problem. I've tried to comment the texture declarations, but even if I have a black screen, the fps will always be 40~ when windowed. That's annoying since 40 fps is not enough for displaying correctly my arkanoid-like test game.

Share this post


Link to post
Share on other sites
Well, from what i can tell, you call "SetTexture" every time you Blit (eg draw a sprite). Thats a possible performance killer. Try batching.
Also, you manipulate your vertices without locking a vertex buffer. Am i missing something? And why do you call "SetVertexShader( NULL )" and "SetFVF(D3DFVF_RECTANGLE_VERTEX )" everytime before you draw a primitive?
Other than that i can only suggest that maybe its because it runs windowed.
From my experience windowed applications usually have lower FPS.

Share this post


Link to post
Share on other sites
I don't use VertexBuffer thing :s

Yeah, here's the code missing, Vertices struct and texture struct :

struct RECTANGLE_VERTEX
{
FLOAT x,y,z; //coordonnées spatiales
DWORD color; //couleur diffuse
FLOAT u, v; //coordonnées de texture
};

#define D3DFVF_RECTANGLE_VERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEX1)

//matrice de projection, de la caméra (ID) et du monde (ID)
D3DXMATRIX Ortho2D, Identity;
//vertex buffer pour la structure rectangulaire dessinant un sprite
RECTANGLE_VERTEX pVertices[4];

typedef struct Texture
{
char * File ; //nom du fichier source
UINT Width, //largeur de la texture (et non de l'image)
Height; //hauteur de la texture (et non de l'image)
LPDIRECT3DTEXTURE9 lpTexture; //pointeur sur la texture
LPDIRECT3DSURFACE9 lpSurface; //pointeur sur la surface
}Texture;



Sometimes, I get back to 85 fps, but when I reboot my computer, it's running at 40 fps again.

Share this post


Link to post
Share on other sites
Quote:
Original post by JoshM
What kind of computer / video card / etc do you have? Are you building Release or Debug? Are you using CRT or LCD and do you have vertical sync swapping enabled?


Athlon xp 2600+
1 go DDR
ATI Radeon 9000 pro 128mb

using CRT.
Vsync enabled.


There's not any problems with other games or stuffs, that's just what I've done which is lagging.

Share this post


Link to post
Share on other sites
In this block of code:

for( int i = 0; i < 600; i++ )
{
for( int j = 0; j < 800; j++ )
{
if( PTRACE[i][j].IsAlive == true )
Blit( PxTrace, (int) PTRACE[i][j].x, (int) PTRACE[i][j].y, RectPTrace, DefaultRect, 0xFFFFFFFF);
}
}



How many times out of 480,000 iterations does Blit actually get called? What is it blitting in this loop?

You can only probably draw sprites this way a few hundred at a time before it will start to slow down.

If you do the DX9 app wizard and create a default d3d app with the teapot and all that, what FPS do you get? If that runs fine then it must be something with your code.

You said you get bad performance even with just a black screen. Does that imply that the program is doing *nothing*, or just that it isn't drawing? If your app is not doing anything at all and you're getting 40 fps, AND the default appwizard program runs fast enough, then perhaps you are setting up your buffers/surfaces incorrectly or something? I dunno :-/

Share this post


Link to post
Share on other sites
Well I've installed and recompiled the SimpleSample project, which gives framerate and it runs at 822~ windowed.

Then I've just redone a prog. using my motor, but just displaying FPS on a black screen, without blitting anything or doing anything else : still 40~.

It must be a problem in my 2D motor code :
Well, here's how the windowed mode is initialised :

HRESULT Init_Fenetre( HWND hWnd, int Largeur, int Hauteur )
{
// Paramètres définissant le rendu
d3dpp.Windowed = TRUE;
d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;
d3dpp.BackBufferWidth = Largeur;
d3dpp.BackBufferHeight = Hauteur;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferCount = 2;
d3dpp.EnableAutoDepthStencil = FALSE;

// Création du device DirectGraphics
if( FAILED( Objet_D3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &Device_D3D ) ) )
{
return E_FAIL;
}

ShowCursor(TRUE); // Curseur visible par dessus la fenêtre

return S_OK;
}

Share this post


Link to post
Share on other sites
You could maybe try using D3DFMT_UNKNOWN instead of A8R8G8B8 when in windowed mode. If your desktop is not actually in format that's compatible with A8R8G8B8 for some reason then it may have to do color conversions when swapping, which could slow it down. But I doubt that's the problem. You could also try HARDWARE_VERTEXPROCESSING instead of SOFTWARE to see what happens.

I would load up that appwizard app that runs a 800fps and put a breakpoint on CreateDevice to see what the actual parameters are that it uses, and then try the same ones in your app to see if that makes any difference.

Share this post


Link to post
Share on other sites
Quote:
Original post by Nuget5555
I don't think Vsync could be limiting you to 40 fps, but try this anyway:

d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;


Thanks, now it's acting faster, maybe too fast but I'll correct it. Now, the framerate is independant from the video refresh, is that it ?


Quote:
Original post by JoshM
You could maybe try using D3DFMT_UNKNOWN instead of A8R8G8B8 when in windowed mode. If your desktop is not actually in format that's compatible with A8R8G8B8 for some reason then it may have to do color conversions when swapping, which could slow it down. But I doubt that's the problem. You could also try HARDWARE_VERTEXPROCESSING instead of SOFTWARE to see what happens.

I would load up that appwizard app that runs a 800fps and put a breakpoint on CreateDevice to see what the actual parameters are that it uses, and then try the same ones in your app to see if that makes any difference.


I previously did set the D3D format to UNKNOWN, but I've changed it, to see if it was the problem. Forgot to change it back :s

About HARDWARE_VERTEXPROCESSING, that's not changing anything :(
On the wiz app, HARDWARE_VPROC enables 822 fps while SOFT one enables 200~ fps.

Well, I will try to put a breakpoint there.

EDIT again :

d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_ONE;

is what would be needed for windowed mode, my fps would be set back to 85, my actual screen refresh rate.
However, the framerate will stay at 50 in fullscreen mode, even with that...

[Edited by - Zack08 on August 2, 2005 6:11:14 PM]

Share this post


Link to post
Share on other sites
Again, i suggest you to change your code.
One, i can't believe you using vertice and no vertexbuffer.
Two, this one looks scary:
for( int i = 0; i < 600; i++ )
{
for( int j = 0; j < 800; j++ )
{
if( PTRACE[i][j].IsAlive == true )
Blit( PxTrace, (int) PTRACE[i][j].x, (int) PTRACE[i][j].y, RectPTrace, DefaultRect, 0xFFFFFFFF);
}
}


Thats max. 480000 x calling "Blit", which again calling SetTexture.
Even if not all of those PTRACE objects are "alive", you're calling Settexture too damn often.
Believe me, setting your texture that often per frame will slow you down. Why not have a list, filled with your sprite/game objects, sort it by texture and then traverse for drawing. Doing so, you call SetTexture only once per texture, not once per sprite. Especially since you seem to use the very same texture in that loop. Think about it, you loop several 1000 times, setting the same texture over and over again (only changing its offset).

Share this post


Link to post
Share on other sites
Keep this in mind, while it's still good to optimize your code, a windowed game running at a steady 40fps is not that bad, especially if you have a screen full of tiles, sprites, background, score, etc. After coding in "2d via 3d" for quite a while now, I've come to the conclusion some 2D games push the 3d pipleline as hard or harder than some 3d fps games. Especially 2d games with alot of tile/sprite animation. As long as you're coding in time based movement, 40fps is not THAT bad in windowed mode. In full screen mode, every 1 or 2 vsyncs is still ok.

Share this post


Link to post
Share on other sites
I can see your using an ati card. If you have an nvidia laying around put it in and go grab NV Perf HUD stat. Thatll tell you where yer lagging. Also how many Triangles a second are you performing at? I could tell ya my game runs at 1 fps... but I could be trying to render the USA.

Also grab a profiler like GlowCode. It will let yer program run and then tell ya where all the time is spent. Profilers are a programmer's best friend.

Other idea was the present immediate, but sum1 took care of that one.

Last idea without going through all the code is I saw a DrawPrimitiveUp(...). Goto msdn.microsoft.com and checkup on DrawIndexedPrimitive(...). Ya gotta do that before you get to many things rendering.


Another note, this is in RELEASE right? not debug, because if you have any lists or vectors they wont be optimized. I can get 1 fps max in debug... but in release I hit high hundreds.

Share this post


Link to post
Share on other sites
Quote:
Original post by 2dcoder
Keep this in mind, while it's still good to optimize your code, a windowed game running at a steady 40fps is not that bad, especially if you have a screen full of tiles, sprites, background, score, etc. After coding in "2d via 3d" for quite a while now, I've come to the conclusion some 2D games push the 3d pipleline as hard or harder than some 3d fps games. Especially 2d games with alot of tile/sprite animation. As long as you're coding in time based movement, 40fps is not THAT bad in windowed mode. In full screen mode, every 1 or 2 vsyncs is still ok.


Yeah, I know but the fact is that there's only 3 textures loaded and nothing else. Plus, when I'm just displaying framerate on a black screen (no textures loaded, nothing else done), it gives me 40 fps.

I've just tried displaying the framerate on the tutorial2 "Vertices" of the DirectX SDK (see the DX Doc for C++), just added the CalculateFPS function and my text displaying function. It gives me 40 fps :\.
Maybe it's my font engine which is bad or my CalculFPS() function.

Here it is :

CalculFPS :


// enables to calculate the framerate
double current_time = 0;
double last_time = 0;
int n = 0;
double fps = 0;
int Dec;
int Sign;
char FPSDisplay[10];


char *CalculFPS()
{
n++;
current_time = (timeGetTime() / 1000);

if( (current_time - last_time) >= 1.0 )
{
// number of frames per sec
fps = n / (current_time - last_time);
n = 0;
last_time = current_time;
}

_fcvt_s( FPSDisplay, _countof( FPSDisplay ), fps, 1, &Dec, &Sign );

FPSDisplay[3] = FPSDisplay[2];
FPSDisplay[2] = '.';
FPSDisplay[4] = NULL;

return FPSDisplay;
// ...
}


InitFont

VOID InitText(int PointSize, bool Italic)
{
HRESULT hr;
HDC hDC;
int Height;

hDC = GetDC( NULL );

Height = -( MulDiv( PointSize, GetDeviceCaps(hDC, LOGPIXELSY), 72 ) );

ReleaseDC( NULL, hDC );

// Create a font for statistics and help output
hr = D3DXCreateFont( g_pd3dDevice, Height, 0, FW_BOLD, 0, Italic,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, TEXT("Arial"),
&g_pd3dxFont );

if( FAILED( hr ) )
MessageBox(NULL,"Impossible de créer la fonte!", "ERROR",MB_OK|MB_ICONEXCLAMATION);
}


Write Text

VOID WriteText(char *Text, int x, int y, D3DCOLOR TextColor)
{
RECT RectText;
SetRect( &RectText, x, y, 0, 0 );
g_pd3dxFont->DrawText( NULL, Text, -1, &RectText, DT_NOCLIP,
TextColor );
}



Quote:
Original post by xsirxx
I can see your using an ati card. If you have an nvidia laying around put it in and go grab NV Perf HUD stat. Thatll tell you where yer lagging. Also how many Triangles a second are you performing at? I could tell ya my game runs at 1 fps... but I could be trying to render the USA.

Also grab a profiler like GlowCode. It will let yer program run and then tell ya where all the time is spent. Profilers are a programmer's best friend.

Other idea was the present immediate, but sum1 took care of that one.

Last idea without going through all the code is I saw a DrawPrimitiveUp(...). Goto msdn.microsoft.com and checkup on DrawIndexedPrimitive(...). Ya gotta do that before you get to many things rendering.


Another note, this is in RELEASE right? not debug, because if you have any lists or vectors they wont be optimized. I can get 1 fps max in debug... but in release I hit high hundreds.


I'm just displaying 3 textures on 3D rectangles, with 4 Vertices (see the code).
Is there any profiler included with visual studio 2005 beta 2 ? Does Glowcode works with it ?
Yeah I'll change it to DrawIndexedPrimitive if I have to render more things, thanks.

Both RELEASE and DEBUG will run at 40 fps.

Share this post


Link to post
Share on other sites
Is it possible that the program is being stupid and if it misses the vsync, it waits until the next one... So it will jump back and forth between 85 (I assume this is your refresh rate) and 42.5... I have no idea what could cause this, but worth looking into.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sr_Guapo
Is it possible that the program is being stupid and if it misses the vsync, it waits until the next one... So it will jump back and forth between 85 (I assume this is your refresh rate) and 42.5... I have no idea what could cause this, but worth looking into.


Don't know.. The strange thing is when I use the PTrace thing, before it's slowing down, the framerate will accelerate to 85 fps after some sprites displayed, 5 or 10 secs. Then it will start to slow down.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sr_Guapo
Is it possible that the program is being stupid and if it misses the vsync, it waits until the next one... So it will jump back and forth between 85 (I assume this is your refresh rate) and 42.5... I have no idea what could cause this, but worth looking into.


That seems very possible.

Share this post


Link to post
Share on other sites
The framerate is at 40. Then some hours later, I should get back 85, then 40 later and so on..
I'll use D3DPRESENT_INTERVAL_ONE for the windowed mode but I dunno how I'll do it for the fullscreen mode :\

Share this post


Link to post
Share on other sites

This topic is 4512 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this