==============================
Maintaining constant framerate
==============================

Framerate modes
===============

For many developers, it is vital that their application appear responsive to the end user at all times. During a large model visualization, most of the resources on a given system may be dedicated to rendering the details of a model.  Thus, when a user tries to examine the model by rotating the camera or zooming in and out, the application may not be able to respond in a timely manner.  This kind of experience can have an unsettling effect on any user.  To change the balance of the situation and improve the user experience, HOOPS/MVO offers a way to control how much time is spent drawing to the screen via the ``HBaseView::SetFramerateMode`` method.


Fixed framerate mode
--------------------

The fixed framerate approach is the simplest method for ensuring a good balance between application responsiveness and visual integrity. To set the framerate mode to fixed, call ``HBaseView::SetFramerateMode`` passing ``FramerateMode::FramerateFixed`` as the first parameter. The next parameter is the target framerate which is specified in seconds. If you pass nothing, it will default to 0.1 which is equivalent to 10 fps (one frame every tenth of a second). Setting the value to 0.05 is equivalent to 20 fps (one frame every twentieth of a second).

The parameter after framerate is the culling threshold defined in pixels. The default value is 150 pixels. The remaining parameters do not apply to the ``FramerateMode::FramerateFixed`` option. The following code sample shows how to turn on fixed framerate and set the target framerate to 20 fps and the culling threshold to 100 pixels::

	m_pHView->SetFramerateMode(FramerateMode::FramerateFixed, 0.05, 100);

Once the values are set on the ``FramerateMode::FramerateFixed`` option, HOOPS guarantees that the target framerate will be met. At render time, the items that exceed the screen-space bounding box defined by the culling threshold will be drawn first. If there is time remaining, the items below the culling threshold will be drawn. If LMV and static model are enabled, these itemsare drawn from largest to smallest - enabling LMV and static model will likely result in better performance while in fixed framerate mode.

Note that in fixed framerate mode, the larger the model the bigger the overhead. Thus, fewer objects may be drawn in the time it takes to achieve the set framerate. The user may also see flickering between one frame and the next.


Target framerate mode
---------------------

For developers who are interested in a smoother non-flickering rendering for very large models, the target framerate approach is one option to consider.  To set the framerate mode to target, call ``HBaseView::SetFramerateMode`` simply passing ``FramerateMode::FramerateTarget`` as the first parameter. The remaining parameters will be defaulted if you do not pass anything. The target framerate approach can be flexible with multiple degrees of freedom. This is represented in the next five parameters that you can set. Like the fixed framerate mode, you can specify a target framerate in seconds. The parameter after framerate is the culling threshold. However, unlike the fixed framerate approach, this parameter represents the maximum value that HOOPS will set for the culling threshold.  The steps parameter represents the number of maximum extent simplification levels. The enableLods flag indicates if LODs should be used in addition to culling. Note that if you set this flag to true, HOOPS assumes that LODs are defined and present. (**Important:**  We recommend testing your typical workflow with and without LODs, as LODs can negatively impact performance in certain situations.)

The following code sample shows how to turn on target framerate and set the target framerate to 20 fps, the maximum culling threshold to 100 pixels, the step to 10 and enable LODs::

	m_pHView->SetFramerateMode(FramerateMode::FramerateTarget, 0.05, 100, true, 10);

When target framerate mode is enabled, HOOPS uses nuanced logic to maintain the suggested framerate within a certain range. Using the last frame's update time as a metric, it tweaks the culling threshold, simplification levels and LODs if available to maintain a steady equilibrium between visual integrity and application responsiveness. Note that the initial parameters set when enabling target framerate mode are only suggestions. Thus, target framerate and culling threshold are not guaranteed at render time.


Additional optimizations
------------------------

While framerate logic may help ensure faster interaction speed, some fundamental HOOPS optimization settings can also help ensure that **more** objects/triangles/lines get drawn in any given update, thereby improving the user experience. For instance, it is simple for any system to maintain an extremely fast framerate for a huge model by simply drawing nothing.

The framerate logic does not automatically make such optimizations. Therefore, you should have them in place, independent of any use of the HOOPS/MVO framerate logic. Specifically, the optimizations settings in the follow sections found in the HOOPS/3dGS Programming Guide should be leveraged:

* :ref:`Segment organization <prog_guide/3dgs/07_2_performance_segments:Segment organization>`
* :ref:`Polygon handedness <prog_guide/3dgs/07_3_performance_geometry:Polygon handedness and backplane culling>`
* :ref:`Display lists <prog_guide/3dgs/07_4_performance_rendering_selection:Display lists>`

 
Customizing constant framerate
==============================

For developers who want a more customized approach to constant framerate, you can use the ``HConstantFrameRate`` class. The target framerate mode found in ``HBaseView`` uses the ``HConstantFrameRate`` class to implement the rendering tweaks needed to fall within the range of the specified framerate. The parameters set in the ``HBaseView::SetFramerateMode`` such as culling threshold and simplification steps can be set directly in ``HConstantFrameRate``. They can also be modified with more freedom and flexibility. However, with more freedom comes increased complexity. If you choose to use ``HConstantFrameRate``, we recommend that you proceed carefully. Experiment with different settings against representative set of models to see how best to set these values for your application.

At the most fundamental level, ``HConstantFrameRate`` encapsulates functionality that adjusts the detail level of a scene based on a desired target framerate. To employ this feature in your application, you need to pass a list of simplification types which are stepped through until the framerate requirement has been met.

Here are the member functions that are needed to activate/deactivate the framerate control followed by some documented example code::

	void Init()
	void Shutdown()
	void Start()
	void Stop()


	// Obtain a pointer to the FrameRate Object of the View.
	HConstantFrameRate *cfr = m_pHView->GetConstantFrameRateObject();

	// HConstantFrameRate::Init accepts the target framerate, a list of 
	// simplification items, the length of the list as well as other 
	// parameters but we can also simply use the default values. Please 
	// note that in a successful call to HConstantFrameRate::Init(), the 
	// framerate functionality will still be in an inactive state until it 
	// has been activated by a call to the HConstantFrameRate::Start() function.
	if (usedefault)
		cfr->Init();
	else
	{
		// If we want to use our own simplification list, we need to allocate an array 
		// which holds the pointers to the different simplification objects and pass 
		// it with its length to the HConstantFramerate::Init() function.
		// The different predefined simplification types are explained below
		HConstFRSimpType **mlist=new HConstFRSimpType *[4];
		mlist[0] = new HFrNoSimp;
		mlist[1] = new HFrLodClamp(0,4);
		mlist[2] = new HFrWireframe(1,1)
		cfr->Init(20.0, mlist,3);

		// This determines if you can increase the detail level of a scene when the target 
		// framerate is significantly below the actual framerate between a start/stop interval.  
		// NoDetailChange basically locks the detail level to its lowest value.
		cfr->SetDetailIncMode(NoDetailChange);

		// After the framerate control has been set up, it is still inactive and needs 
		// to be started with a call to the Start() function. It can later be deactivated 
		// with the Stop() function.
		cfr->Start();
	}

  
Simplification objects
======================

Some of the simplification objects have additional internal detail levels which can be included or omitted by setting the ``SimpLevelMax`` and ``SimpLevelMin`` values in the list accordingly. Here is a list of the different objects:
	

HFrNoSimpAA
-----------

No simplification. The scene is also anti-aliased. This is typically the base of the list. 


HFrNoSimp
---------

No simplification. If you are not using anti-aliasing, it should be the base of the list.


HFrLodClamp
-----------

Simplification using the clamp LOD option. The simplification level corresponds directly to the clamp value rendering option.


HFrLodThreshold
---------------

Simplification using the threshold LOD option. The simplification level describes a range from 1-200 for the threshold rendering option in reverse order.


HFrSolidBBox / HFrWireframeBBox
-------------------------------

Instead of normal geometry, bounding boxes (solid or wireframe) are drawn on a segment level. A simplification level of one allows only display of the outer bounding box that surrounds the complete geometry


HFrWireframe
------------

This activates wireframe drawing and turns the hidden surface heuristic off.  A simplification level of 0 displays a no lod wireframe drawing. A simplification level of 1 takes the LOD settings in the list into account. 
  
To add additional simplification types, simply derive a new class from ``HConstFRSimpType`` which implements the Set member function and include it in the simplification list.


Implementation issues
=====================

There are different ways to use the constant framerate code. Which one you choose depends on your application needs.  In the simplest case, the constant framerate control is initialized, started once and remains active until it is turned off again by the user. This requires no further code in the main application but has some disadvantages. As long as the user e.g. rotates the camera, the framerate control code will reduce the detail level until the target framerate is reached. However, if the user stops interacting with the model, it will still remain on its lower detail level even though it would probably be desirable to revert to full detail again. 

One way to achieve this is to manually start and stop the constant framerate functionality for every operator. As long as the user orbits the scene, the detail level is adjusted but as soon as the manipulation is finished, the scene reverts back to full detail. This gives the application very precise control over when to invoke the constant framerate code but still it does not fully solve the problem of increasing the detail level if no user action occurs.  To allow the framerate logic to figure out by itself if it makes sense to increase the detail level again, the constant framerate class sets up a timer function that checks if a significant user input has occurred and allows/disallows a temporary detail increase based on that information. The main application must provide an interrupt handler that allows HOOPS to break an ongoing update cycle if a user input has occurred. Here is an example::
	
	void CVisualizeView::OnInitialUpdate() 
	...

		m_pHView->SetEventCheckerCallback(event_checker);
	...

	void CVisualizeView::event_checker(void)
	{
		
		MSG msg;

		if (pThisView->m_hWnd != 0)
		{

			// let's assume that we are only interested in mousemove events
			if (PeekMessage (&msg, pThisView->m_hWnd, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_NOREMOVE))
			{
				// We only want to stop if it is a message we care about 
				switch (msg.message) 
				{
					case WM_MOUSEMOVE:
					int state = GetAsyncKeyState(VK_LBUTTON);
					if (state & 32768)				
					{
						// any special event breaks the update cycle
						HC_Queue_Special_Event ("FRAMERATE", "INTERRUPT");		
						pThisView->m_pHView->SetUpdateInterrupted(true);
					}
					return;
					break;		 				
				}
			}
			else if (PeekMessage (&msg, pThisView->m_hWnd, WM_MOUSEWHEEL, WM_MOUSEWHEEL, PM_NOREMOVE))
			{
				// any special event breaks the update cycle
				HC_Queue_Special_Event ("FRAMERATE", "INTERRUPT");		
				pThisView->m_pHView->SetUpdateInterrupted(true);
			}
			else
				return;	
		}
	}
