Reducing function overhead

Started by
10 comments, last by orphankill 15 years, 9 months ago
I'm back again with another (probably simple) question. In my many different Render() functions, one per graphics object, I constantly find myself doing things like this:

        DWORD light, cull, alphablend, alphafunc, alpharef, alphatest, zenable;
	d3d->GetRenderState(D3DRS_LIGHTING, &light);
	d3d->GetRenderState(D3DRS_CULLMODE, &cull);
	d3d->GetRenderState(D3DRS_ZENABLE, &zenable);
	d3d->GetRenderState(D3DRS_ALPHABLENDENABLE, &alphablend);
	d3d->GetRenderState(D3DRS_ALPHAFUNC, &alphafunc);
	d3d->GetRenderState(D3DRS_ALPHAREF, &alpharef);
	d3d->GetRenderState(D3DRS_ALPHATESTENABLE, &alphatest);
	// <-------------Done Saving

	d3d->SetRenderState(D3DRS_LIGHTING, FALSE);
	d3d->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	d3d->SetRenderState(D3DRS_ZENABLE, FALSE);
	d3d->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	d3d->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
	d3d->SetRenderState(D3DRS_ALPHAREF, (DWORD)8);
	d3d->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE);

	...Render Some Stuff Here....
	
	//Restore render states ---->
	d3d->SetRenderState(D3DRS_LIGHTING, light);
	d3d->SetRenderState(D3DRS_CULLMODE, cull);
	d3d->SetRenderState(D3DRS_ZENABLE, zenable);
	d3d->SetRenderState(D3DRS_ALPHABLENDENABLE, alphablend);
	d3d->SetRenderState(D3DRS_ALPHAFUNC, alphafunc);
	d3d->SetRenderState(D3DRS_ALPHAREF, alpharef);
	d3d->SetRenderState(D3DRS_ALPHATESTENABLE, alphatest);
I feel like this is highly inefficient to be calling for every object I need to draw for every variable I need to change on the device. I am aware of the StateBlocks, but calling Save or RestoreStateBlock() more than once a frame kills performance. What do you guys do? Set settings once and render everything that uses those settings, or something else entirely?
Advertisement
The Effect framework automagically manages saving and restoring any state it changes internally. You can use the framework to modify your states and it will manage restoring it for you when you're done.

If you're not using shaders, that's not a problem, you can use the effect framework with the FFP and only use it to modify Renderstates.
Sirob Yes.» - status: Work-O-Rama.
Use the IDirect3DStateBlock9 interface
"Applications use the methods of the IDirect3DStateBlock9 interface to encapsulate render states."
Assuming you always overwrite all the saved states, you could consider skipping saving and restoring states since the next object to be rendered will set them all anyways.
It's a goo thing also draw all object with the same render state, in order to reduce changings
Quote:Use the IDirect3DStateBlock9 interface


If you read my whole post you would notice I said that the StateBlock9 interface hurts performance dramatically.

Quote:Assuming you always overwrite all the saved states, you could consider skipping saving and restoring states since the next object to be rendered will set them all anyways.


Unfortunately, changing all of the states everytime is a bit of a hassle, but I'm considering it.

Quote:The Effect framework automagically manages saving and restoring any state it changes internally. You can use the framework to modify your states and it will manage restoring it for you when you're done.


Not sure what the "Effect framework" is, but I've already written the entire engine myself. At this point, it would seem a bit backwards to try and incorporate some other framework into my game, but thank you for the suggestion.

Anyone else have any suggestions?
Not that I suggest it, but the effect framework is part of the SDK, so you can use it as you please.
Honestly I don't see why you are changing your states all the time. From your examples I think you should be able to design your game in such a way that the cull mode is fixed, the lighting is likely also fixed for the scene and maybe disabled for the GUI parts, thats one change, you could try to use the same alpha settings for all alpha blended objects and then group alphabelnded objects (or if this isnt possible i dought that enabling and disabling alphablending is a big problem).
Also I vaguely remember uing the effects framework at some point and if I remember correctly it easily works together with shader programming.

Just my thoughts.

-CProgrammer
Well, I've used DirectX only in combination with C# up until now (a project for my university), but I've never had the idea to let the objects decide how to render them.
In my engine I use the MVC Pattern. (Model View Controll: http://en.wikipedia.org/wiki/Model-view-controller)
So i have a Window Object which conatains a GUI object which is basically the engine. This GUI object has a Render() method. It is called with a delegate function as parameter. You could compare it to a function pointer in C++.

My programm works this way:
I create window.
The window creates a GUI object and initializes it (with all the detail like Z-Buffer, Cull-Mode, etc.)
While the window exists and the programm is running the window calls the Render() function of the GUI object with the delegate:

while (m_oMainWnd.Created)
{
if (DXWnd.GameRunning)
{
//Render the Scene
m_oMainWnd.Render(delegate
{
TimeStat.Update();
oFPS.Render();
});
}
//progress user input (mouse, keyboard, ...)
Application.DoEvents();
}
Like I said before, the delegate is like a function pointer, so the code in the delegate{} part will be executed when i call the delegate from the Render() method of the GUI object which looks like:
public void Render(RenderDelegate oDelegate)
{
if (m_oDevice == null)
return;

m_oDevice.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.FromArgb(0x30, 0x30, 0x30), 1.0f, 0);

//Beginn rendering
m_oDevice.BeginScene();

//Render the "camera"
m_oDevice.Transform.View = this.m_oCamera.ViewMatrix;
m_oDevice.Transform.Projection = this.m_oCamera.ProjectionMatrix;

//Execute the delegate function. This will render the scene.
if (oDelegate != null)
oDelegate(); <- this is where the code from before is executed.
m_oDevice.EndScene();
//End rendering
m_oDevice.Present();
}

And finally the Render method of the object:
public new void Render()
{
float fps = 1 / TimeStat.ElapsedTimeInSeconds;
this.Message = fps.ToString(".");
base.Render();
}
//base is a function from the parent class, the parent class is a class to render text on the screen. So the FPS class is a child of this text-class
The render method there is much more simplier:
public void Render()
{
m_oFont.DrawText(null,
m_sMessage,
new Rectangle(m_iX, m_iY,
m_iWidth, m_iHeight),
m_oFontFlags,
m_oFontColor);
}

I hope this doesn't look to complicated, but it's pretty easy and the most important thing is: It doesn't need many resources and it's pretty easy to change.
If I want to change the render mode for a special object (like disable lightning) I just have to pass a pointer of the GUI object to the object and change the render state there. But I haven't needed this so far.

Hopefully it's understandable, if you have questions feel free to ask :)

I haven't build it in C++ yet, but I'm working on it.

Greetings,
Skalli
"Never argue with an idiot, he'll bring you down to his level - then beat you with experience"
If you're always applying the entire list of states for each object, then there is no point in saving them.

You code when unrolled will look like this:
save set drawrestoresave set drawrestore...

why not just:
setdrawsetdraw...


Also, your renderer should organize things to minimize state changes. So you enable lighting, then you draw all your lit object, then you disable lighting, and draw all your unlit objects.

edit: I see this already been mentioned :/

IMO your graphics objects should not have a render function. They should have an enqueue function which when called, inserts the objects renderable data into the render queue. They are sorted by state on insert by Shader, Material, etc.

Since the first level of batching is the shader, this is where the majority of your state changes will take place. So your blended objects will all use the same shader, therefore when that shader is bound once, the blending state is applied once.

Render(){ RenderQueue renderQueue; //enqueue scene - replace this with spatial partitioning foreach (Scene s in m_Scenes)  foreach (Renderable r in s)   //state sorting happens in here   r.Enqueue(renderQueue);  //ready to draw foreach (MaterialChange m in renderQueue) {  m.BindShader();  m.FillShaderParameters();  m.SetRenderStates();     foreach (DrawCall d in m.DrawCalls)  {   m.SetVertexAttributes(d);   Draw(d);  } }}


[Edited by - bzroom on July 13, 2008 9:05:19 PM]

This topic is closed to new replies.

Advertisement