API Question Blitting/Graphics optimization

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
I have several cockpit displays in my VC that need to be updated every timestep or at least several times a second in order to be useful. The problem of course is that blitting operations are processor intensive and come with a corresponding hit to frame-rate.

While looking at code samples for clues on how others have solved this issue I noticed that all of the stock DG's displays seem to be derived from a single massive texture "dg_panel.dds". Is there a significant savings derived from blitting to and from the same surface?

Aside drom restriction all blits to pixel values that are powers of two what other methods can I use minimize overhead on my redraw functions?
 
Last edited:

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Will do but is there anything in particular that I should pay attention to?
 

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
Not really... Theres no magic involved and the code is quite readable.

The basic idea is this:
- keep src/dst sizes and color coding equal (duh!)
- if you are under the inline client, avoid BitBlt(). Instead, cast SURFHANDLE to LPDIRECTDRAW7SURFACE and blit using DX7 functions.
- if you are running under D3D9Client, just use BitBlt()
- to detect if you have D3D9Client, GetModuleHandle() is your friend
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,877
Reaction score
2,131
Points
203
Location
between the planets
Is there a significant savings derived from blitting to and from the same surface?

Not that I know of. Mass-blitting from the same texture has significant performance gains, because you only have to lock and free them once, but orbiter doesn't allow that level of control. I assume that it just locks and frees the texture for every single opperation.

For optimising blitting operations, the main thing you can do is reduce the actual ammount of operations. blitting two small areas takes more cycles than blitting a single large one (especially with Orbiters high-level approach). As such, make sure that you only blit the actual things that change. All static text, for example, doesn't need to be blitted every frame, but only the values that actually do change (most prominently numbers, I assume).

Then there's another trick, which is a bit work intensive: Instead of blitting, you can change texture coordinates. Say you have for example a blinking light, it is more efficient to change the texture coordinates of the element between the on and off texture coordinates than blitting the images onto the element. This can also be done with numbers displays if things get really wild, although it's quite a bit of work.

In the end,y ou should really think about what elements do really have to update every timestep. even reducing it to every 0.1 seconds or so already brings a huge improvement, and is usually enough (in many cases, it is even better readable, because fast-changing numbers aren't easy on the eye).

And then there's of course also the possibilities to check if the displayed value has actually changed by a significant enough ammount, and only blitting in those cases. There's a lot of values on which you might need to have a constant watch, because you can not foresee when they will change, and the easiest way to do that is to reblit them every frame... but with a bit more effort you can just check if they actually need redrawing.


- if you are under the inline client, avoid BitBlt(). Instead, cast SURFHANDLE to LPDIRECTDRAW7SURFACE and blit using DX7 functions.
- if you are running under D3D9Client, just use BitBlt()

Uhm... we can use standard DX functions in Orbiter? I must admitt the thought never occured to me. I guess it should work when you include the DX sdk in your project.
 
Last edited:

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
Uhm... we can use standard DX functions in Orbiter?

Yes. We are just not supposed to do that.

To be honest, for some time I was planning to extract the blitting code from VNCMFD and make it standalone library for other people to use, but I cannot find the time...

I guess it should work when you include the DX sdk in your project.

Precisely :) Feel free to download VNCMFD sources and see for yourself...

More fun: IIRC, VNCMFD uses two different versions of DirectX at the same time. DX7 is used for blitting, while DX8 (I think) is used for joystick (Cougar frame) input...
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Then there's another trick, which is a bit work intensive: Instead of blitting, you can change texture coordinates. Say you have for example a blinking light, it is more efficient to change the texture coordinates of the element between the on and off texture coordinates than blitting the images onto the element. This can also be done with numbers displays if things get really wild, although it's quite a bit of work.

I do have several items, circuit breaker indicators, caution lights, etc... that match this description so If you could explain the actual "how" or point me towards some examples it would be much appreciated.

In the end, you should really think about what elements do really have to update every timestep. even reducing it to every 0.1 seconds or so already brings a huge improvement, and is usually enough (in many cases, it is even better readable, because fast-changing numbers aren't easy on the eye).

And then there's of course also the possibilities to check if the displayed value has actually changed by a significant enough ammount, and only blitting in those cases...

I'm already doing both of these to a degree. Most of my display functions start with some variation of the following...

Code:
if ((oapiGetSimTime () - LastRefresh) < RefreshRate) return;
else if (fabs (CurrentValue - DisplayedValue) < Threshold) return;
else
{
*Perform redraw operation
}

But the fact remains that I still have several instruments that need to update at least several times a second in order to be useful. Specifically the range, rate, and error monitor during final approach for the LM and the cross-range/velocity display for the CM.

One thing that has occurred to me is "how much does 'background' cost me?". A lot of these displays consist of a larger scale or screen onto which multiple semi-transparent needle/indicator textures are being projected so any tricks I can use to minimize my overhead in such situations would be greatly appreciated.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,877
Reaction score
2,131
Points
203
Location
between the planets
I do have several items, circuit breaker indicators, caution lights, etc... that match this description so If you could explain the actual "how" or point me towards some examples it would be much appreciated.

I never needed to do it, so unfortunately I don't have any code. The Idea is to make the blinking part an own mesh group in the panel, and then edit its UV coordinates in real time. I know it's definitely possible in Orbiter.

"how much does 'background' cost me?"

It depends on the size, but not much. In the end it's just one additional operation for which orbiter has to lock the video memory, which is really what takes most of the time. Avoid if possible, but when drawing the instrument consists of 10 blitting operations, the Background won't eat that many cycles compared to the rest...

so any tricks I can use to minimize my overhead in such situations would be greatly appreciated.

For instruments with analog hands, it's best to not blit them at all... make the hands a seperate mesh group and animate them.
 
Last edited:

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
One thing that has occurred to me is "how much does 'background' cost me?".

Hold on, you should know that before you think about optimizing anything.

For your information, this is what I used for timing stuff in VNCMFD:

Code:
#define CREATETIMER DWORD tickCounter = GetTickCount()
#define STARTTIMER (tickCounter = GetTickCount())
#define GETTIMER (GetTickCount() - tickCounter)

#define TIMER(x) STARTTIMER; x; Log("%s took %d ms", #x, GETTIMER);

Example use is:

Code:
void func() {
	CREATETIMER;	// timer is local to the function
	STARTTIMER;	// reset
	// do stuff ...
	Log("Doing stuff took %d ms", GETTIMER);
	STARTIMER;	// reset
	// do more stuff ...
	Log("Doing more stuff took %d ms", GETTIMER);

	TIMER( callFunction(par1, par2) );
	// This will output:
	// "callFunction(par1, par2) took XXXX ms"
}

Feel free to use this... And feel free to steal my Log.cpp / Log.h also :)

---------- Post added at 08:35 PM ---------- Previous post was at 08:29 PM ----------

In the end it's just one additional operation for which orbiter has to lock the video memory, which is really what takes most of the time.

It's not that locking video memory takes time, it's that locking video memory defeats multithreading. Because one of the simplest tricks is to blit the next frame while the current frame renders. Alas, if blitting locks the renderer, then your performance will be effectively the same as a single thread. That said, maybe it is possible to do background blitting between DX surfaces. I know that the global renderer locking occurs when you BitBlt() between DX and GDI surfaces. But I don't know if the same is true for two DX surfaces. I never needed to check that, because that was not the case I was dealing with.
 
Last edited:

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
For instruments with analog hands, it's best to not blit them at all... make the hands a seperate mesh group and animate them.

Already doing this as well for the tape gauges and ADI but this approach is not really practical for the CRT or LCD based displays.
 

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
Already doing this as well for the tape gauges and ADI but this approach is not really practical for the CRT or LCD based displays.

Did you check what the performance would be when you just put MFD screens on the surfaces in question?

Because if Orbiter can render a VC with many MFD screens fast enough, then just implement your screen as MFDs (i.e. derive from MFD class) and call oapiOpenMFD() to set each VC screen to the correct mode. No MFD buttons for these screens, so the mode cannot be switched by user. Problem solved.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Did you check what the performance would be when you just put MFD screens on the surfaces in question?

Because if Orbiter can render a VC with many MFD screens fast enough, then just implement your screen as MFDs (i.e. derive from MFD class) and call oapiOpenMFD() to set each VC screen to the correct mode. No MFD buttons for these screens, so the mode cannot be switched by user. Problem solved.

That is an interesting approach that I had not conisdered. I'll have to test it.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,877
Reaction score
2,131
Points
203
Location
between the planets
But I don't know if the same is true for two DX surfaces.

I'm not quite sure, but as far as my understanding goes pretty much any access to the video memory needs locking first. I remember the Win95 days when accessing unlocked video memory in DX7 sent the whole system to kingdom come in one gloriously blue screen of death... (as did any crash that occured while the memory was locked, so if you had a bug in a block where the memory was locked, it was trial and error and restart until you found it :facepalm:)


Already doing this as well for the tape gauges and ADI but this approach is not really practical for the CRT or LCD based displays.

Hmmyeah, I can totally see that. How many hands are we talking about?
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Hmmyeah, I can totally see that. How many hands are we talking about?

For the LM?

There are 10 gauges with 2 "hands" a-piece so 20 plus some miscellaneous fidely bits. There are also the 2 ADIs or "8-balls" that that I've actually rendered by animating a spherical mesh in the VC. I have a basic LCD fuction up and running to handle the digital displays but it is in need of optimization.

My current "problem children" are the Error/Rate monitor AKA "Cross-Pointer" and the altitude/acceleration indicators.

I haven't even touched the caution and warning lights yet.

The CM has roughly twice that.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,877
Reaction score
2,131
Points
203
Location
between the planets
Particularly the LCD displays would be a good candidate to work by vertex coordinate shifting instead of blitting. Have a mesh group for every digit on the display, and instead of blitting the number from the appropriate location to where you need it, you switch the UV coordinates of each meshgroup that represents a digit to the coordinates you are currently blitting from.

My current "problem children" are the Error/Rate monitor AKA "Cross-Pointer"


I can only really see that working with SketchPad. How often does the thing need to update?

There are 10 gauges with 2 "hands" a-piece so 20 plus some miscellaneous fidely bits.

If you can by any means avoid the miscelaneous fiddly bits, that comes down to 20 blitting operations... That's a piece of cake.
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
I can only really see that working with SketchPad. How often does the thing need to update?

At least several times a second, preferably more (got it at 10hz tight now) it, along with altitude are what put the "instrument" in instrument approach.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,877
Reaction score
2,131
Points
203
Location
between the planets
At least several times a second, preferably more (got it at 10hz tight now) it, along with altitude are what put the "instrument" in instrument approach.

Up to a refresh rate of 0.1 seconds should be ok for SketchPad, plus it has better performance in D3D9 client...
 

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,870
Reaction score
3
Points
0
Location
San Diego
Related to the previous question...

How do I get oapiBlt () to respect transparency? I've already verified that the .dds is propeerly formatted and compressed (RGBA / DTX5) as per the 3dmodel.pdf in the orbiter docs folder and the texture displays as transparent when utilized outside the blitting operation so how do I get it to blit the alpha channel along with RGB?

---------- Post added at 11:30 ---------- Previous post was at 11:00 ----------

Update:

I'v found a reference to "NO_CK" vs "PREDEF_CK" in regards to transperancy in the oapiblit documentation but the features themselves and their meaning do not seem to be documented as a search of the API docs and header files turns up 0 results.

based on the one mention i managed to find "PREDEF_CK" is what I want but how do i make "PREDEF_CK" equal "Respect the mother-f***ing apha channel probe-damnit"?

---------- Post added at 13:29 ---------- Previous post was at 11:30 ----------

update to the update:

I've sort of decoded CK, it seems to be an 8 digit (RRGGBBAA) hexi-decimal color code that, in theory, sets a color or channel to be used for transparencies.

However, in practice any CK value other than "full white" (FFFFFFFF) causes the oapiblit () operation to fail.

Looks like a bug report is in order.
 

kamaz

Unicorn hunter
Addon Developer
Joined
Mar 31, 2012
Messages
2,298
Reaction score
4
Points
0
Thanks! I have hit that bug when writing slavemfd, but I couldn't figure out why it fails....
 
Top