Skip to content

Latest commit

 

History

History

windows

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
======================================================================================

SUMMARY
*******

1) libfreenect: incompatibilities with Visual C++
   This section is here merely for historical reasons. All of the issues documented
   here were already fixed in the libfreenect repository and CMake should be able to
   produce a project that is ready to build libfreenect in Windows with Visual Studio.
   Consider browsing this section if experiencing compilation issues under different
   platforms, compilers and/or IDEs.

2) libusb-1.0 vs. libusbemu: Issues and Concept
   The current port of libfreenect for Windows uses a libusb-1.0 emulation layer since
   a proper port of libusb-1.0 for Windows is not yet available. Such emulation layer
   allows Windows development to keep in sync with the official development branch of
   libfreenect, without the need of dedicated drivers/implementations. This section
   discusses why and how the current libfreenect Windows port moved in this direction.

3) libusbemu: Tips, Hints and Best Practices
   The current status of libusbemu is quite reliable under normal usage circumstances,
   but by no means stable: caution is advised. This section provides some guidelines
   to avoid potential pitfalls and keep the application running safely under Windows.
   They are simple and natural to follow, so they should not impose any special design
   considerations for the application.

4) Overall performance of libfreenect in Windows
   The current Windows port of libfreenect has some performance overhead over other
   platforms and dedicated Win32 driver implementations due to the libusbemu module.
   This section contains a benchmark scenario and a also a discussion on the results.
   In short, the overhead is negligible and should not prevent anyone from using it.

======================================================================================

1) libfreenect source code: incompatibilities with Visual C++
   **********************************************************

----------------------------------------------------------------------------------
Language issues: The Microsoft C compiler does not implement all the C99 standard.
----------------------------------------------------------------------------------

An attempt to compile the current libfreenect with Visual C++ will trigger a lot
of errors. A simple workaround is to tell Visual Studio to force compilation all the
".c" files within the project using the C++ compiler:
  Project >> Properties >> C/C++ >> Advanced >> Compile As: Compile as C++ Code (/TP)

This will get rid of most errors, except those regarding implicit pointer casts.
Here are a few examples of such implicit pointer casts from within libfreenect:
  tilt.c      dev->raw_state.tilt_status = buf[9];
  core.c      *ctx = malloc(sizeof(freenect_context));
  cameras.c   strm->raw_buf = strm->proc_buf;
It seems that it is not possible to force Visual C++ to perform such implicit casts
(if anyone knows how, please share! :-)

Such implicit casts then have to be made explicit:
              dev->raw_state.tilt_status = (freenect_tilt_status_code)buf[9];
              *ctx = (freenect_context*)malloc(sizeof(freenect_context));
              strm->raw_buf = (uint8_t*)strm->proc_buf;
Fortunately, they are not many, and it can be done in a couple of minutes.
This should impose a minimal burden to the Win32 repository maintainers.

NOTE: Sometimes, even when "Compile as C++ Code" is specified, the build will insist
on using the C-compiler to compile .c files (this happens with MSVC versions prior to
2010). To work around this, just set the "Compile As" to "Default", click "Apply" and
then set it back to "Compile as C++ Code (/TP)", click "Apply" and then "OK".

Another problem is that Visual C++ does not offer <unistd.h>. However, it
can be emulated by #include <stdint.h> and defining the "ssize_t" type.
The implementation of <unistd.h> is located at:
  "libfreenect\platform\windows\unistd.h"

NOTE: MSVC versions prior to 2010 do not provide the <stdint.h> header. An ISO C9x
compilant <stdint.h> header for such MSVC versions can be found at the wikipedia entry
of stdint.h (at the "External links" section of the page):
  http://en.wikipedia.org/wiki/Stdint.h
or directly through this link:
  http://msinttypes.googlecode.com/svn/trunk/stdint.h
A copy of such header will be also provided within libfreenect in the future.

The "freenect_internal.h" makes use of GCC's "__attribute__" keyword, and there is no
such a thing in Visual C++. Fortunately, this header is not exposed for the library
user and is just required during the library build. There are a few simple solutions
for this issue: a) remove this keyword or b) define a dummy macro for it.

Another issue is that since all .c files were forced to be compiled as C++ code,
the "libfreenect.h" header no longer requires the "#ifdef __cpluscplus extern C"
idiom. Commenting out this guard will do the trick, but better checking is possible.

-----------------------------------------------------------------------------------
Library issues: libfreenect uses libusb-1.0 which is not yet available for Windows.
-----------------------------------------------------------------------------------

The final issue is regarding the default USB back-end used by libfreenect, libusb-1.0,
which is not yet available for Windows. This restriction forces the Windows port of
libfreenect to implement its own back-end, which then splits the Windows port from the
main development branch of libfreenect as new device features are reverse-engineered
and added to the library. Fortunately, such restriction can be alleviated through the
libusb-1.0 API "emulator" and keep the Windows port in sync with the current status of
libfreenect. More on this subject in the following section.

======================================================================================

2) libusb-1.0 vs. libusbemu: Issues and Concept
   ********************************************

The libfreenect uses libusb-1.0 as its default USB back-end to communicate with Kinect
but libusb-1.0 is not yet available for Windows. The current libusb-1.0 implementation
for Windows is experimental and uses WinUSB as its USB back-end. Unfortunately, WinUSB
does not support isochronous transfers, rendering it useless for Kinect purposes.

However, all is not lost since the latest version of libusb-win32 has support for the
isochronous transfers imposed by Kinect. There are issues too: libusb-win32 is based
on the old libusb-0.1 API which is incompatible with the libusb-1.0 API. Some of the
initial efforts to port libfreenect to Windows were based on the libusb-win32, being
Zephod's Win32 Kinect driver prototype a well-known instance:
   http://ajaxorg.posterous.com/kinect-driver-for-windows-prototype

The problem with a dedicated libusb-win32 driver implementation is that it has to be
maintained separately from the current libfreenect development. As new Kinect features
are reverse-engineered and implemented in the official libfreenect branch, the Windows
port requires additional maintenance to keep it in sync with the newest updates, even
when such updates don't involve USB communication at all.

One could argue that libfreenect should abandon the use of libusb-1.0 and just adopt
the old libusb-0.1 instead, since it is has support in a wider range of platforms...
This is out of question! First of all, libusb-0.1 still exists for legacy reasons, and
it is highly recommended to move to the new API if possible. Moreover, libusb-0.1 does
not have built-in support for asynchronous transfers, which would force libfreenect to
hold and manage threads internally, which would then lead to a whole set of new issues
that are prone to hurt performance, maintainability and portability.

Fortunately, libfreenect only requires a small portion of the libusb-1.0 API, and such
subset can be emulated, at some extent, through what is provided from libusb-win32. As
a result, the burden of maintaining dedicated development branches for a Windows port
is eliminated and the port can keep in synch with updates made in the libfreenect. One
may now ask: how can libusb-win32 be of any help if it is based on the libusb-0.1 API,
which has no support for asynchronous transfers? Well, that's because it happens that
libusb-win32 is more than just a Win32 build of libusb-0.1: it is a special branch of
the 0.1 API that extends it to allow asynchronous transfers.

The normal execution flow of libfreenect is something like this:
  Application <-> libfreenect <-> libusb-1.0 <-> Kinect
In Windows, the flow is as follows:
  Application <-> libfreenect <-> libusb-1.0-emu <-> libusb-win32 <-> Kinect

The source code of the emulation layer is available at:
  libfreenect\platform\windows\libusb10emu\libusb-1.0
Keep in mind that libusb-win32 is still required, and can be downloaded from:
  http://sourceforge.net/apps/trac/libusb-win32/wiki
The latest snapshots are recommended, obtained directly from this URL:
  http://sourceforge.net/projects/libusb-win32/files/libusb-win32-snapshots

Emulation of libusb-1.0 on top of libusb-win32 is not trivial, in special because of
some Windows-specific issues. There will be performance overhead, but as discussed in
the "Overall Performance" section, this should not hurt the application performance at
significant levels. Moreover, although the libusbemu is currently quite reliable, it
is by no means stable and on par to the real libusb-1.0 semantics. Caution is advised,
so be sure to read the following section for some usage considerations.

The libusbemu sits completely hidden behind libfreenect: the application does not need
to worry or call any special functions. In fact the application is not even aware that
libusbemu exists.

======================================================================================

3) libusbemu: Tips, Hints and Best Practices
   *****************************************

---------------------------------------------------------------
TIP: Trigger the Fail Guard if the system becomes unresponsive.
---------------------------------------------------------------

Since libusbemu is quite experimental, there is a fail guard within it. If for some
reason your system renders unresponsive, try focusing any window of the application
and hold [CTRL] + [ALT] for a while.

This will trigger a special synchronization event within the libusbemu which will
interrupt the execution of any internal thread of libusbemu and prompt a message box
to the user asking for action. You can either resume execution (if you unintentionally
pressed [CTRL] + [ALT] in the console window) or abort libusbemu. The fail guard will
never trigger if there is no incoming video or depth streams.

The Fail Guard is important since in such situations one would most likely be forced
to shutdown the computer (in a not so graceful way, by holding the power button!).

----------------------------------------
TIP: Only use a single freenect context.
----------------------------------------

Multiple freenect contexts should be no problem in the future. Having more than one
device attached to the same context should work fine, but no tests were made so far.

-----------------------------------------------------
TIP: Yield the rendering thread after each iteration.
-----------------------------------------------------

In case your application performs direct rendering (OpenGL, Direct3D), it is highly
recommended to yield the rendering thread after finishing rendering each frame. This
can alleviate a lot of CPU usage. The ideal place for this yield is right after the
swap-buffers call (SwapBuffers(), glutSwapBuffers(), Present(), etc). The best way to
yield is by calling Sleep(1). Note that Sleep(0) is also possible, but the former is
more "democratic".

Note that some graphics drivers or platforms may already yield after swap-buffers.
This seems to be the case with OpenGL NVIDIA drivers for Linux. In Windows, however,
the same driver (version) does not yield. Maybe in Linux the "yielder" is not the
driver itself, but the underlying implementation of glXSwapBuffers()... Anyway, when
in doubt, it will not hurt to explicitly yield again.

--------------------------------------------------------------------------------------
TIP: Perform stream operations only in the thread that calls freenect_process_events()
--------------------------------------------------------------------------------------

By stream operations I mean these:
  freenect_start_video()
  freenect_start_depth()
  freenect_stop_video()
  freenect_stop_depth()

This tip seems to hold for other platforms as well, and it is respected through all of
the official libfreenect examples and should be of no burden for the application. This
may be due to the underlying semantics of the real libusb-1.0. Unfortunately, although
libusb-1.0 has an excellent API documentation it lacks on a proper specification, thus
difficulting the task of checking if this is indeed a restriction.

In summary, a typical libfreenect application (check the official examples) will have
a dedicated thread that calls freenect_process_events(). This function is responsible
for querying and dispatching incoming streams (a.k.a. video and depth frames) from the
Kinect device to the application (behind the scenes is a call the libusb-1.0 function
libusb_handle_events()) and it is advised that any stream operation intended by the
application (like switching to some different video format) should happen within the
thread that calls the freenect_process_events(). The official libfreenect examples do
this, so be sure to check their source code if confused. It is also advised to have
only one thread calling freenect_handle_events().

Interestingly, some tests were made with both the libusb-1.0 and libusbemu and mixing
stream operations in different threads seem to work well. However, no guarantees are
given if executed in such a fashion. Again, libusb-1.0 has no specification document
and if such behavior is really to be expected is a hard task to determine.

======================================================================================

4) Overall performance of libfreenect in Windows
   *********************************************

---------------------------------------------
THIS SECTION IS OUTDATED, GOTTA REDO IT SOON!
---------------------------------------------

Hardware:
* Notebook 
* CPU: Intel Core2 Duo 32bit [T7250] @ 2.0GHz
* RAM: 4GB RAM
* GPU: GeForce 8600M GT 256MB VRAM



Task: display of simultaneous RGB (Bayer-to-RGB) and depth streams (16bit unpadded) on
the screen through OpenGL textures. Application source code is identical for all tests
(except for the Zephod's version that required some interface adaptation).



Results: performance measurements refer to the average frame time (one loop iteration).

Linux: Ubuntu Notebook 10.10 -- gcc 4.4.5
* Debug:   1.22ms | CPU @ 77% | video @ 30Hz | depth @ 30Hz
* Release: 1.15ms | CPU @ 72% | video @ 30Hz | depth @ 30Hz

Win32: Windows 7 Enterprise 32bit -- VC++ (Professional) 2010
1) libfreenect with libusbemu:
   * Debug:   2.44ms | CPU @ 75% | video @ 30Hz | depth @ 30Hz
   * Release: 1.93ms | CPU @ 63% | video @ 30Hz | depth @ 30Hz
2) Zephod's dedicated driver (V16):
   * Debug:   2.87ms | CPU @ 82% | video @ 30Hz | depth @ 30Hz
   * Release: 2.55ms | CPU @ 77% | video @ 30Hz | depth @ 30Hz



Remarks:

Debug builds account for the library itself being built in Debug mode (libfreenect or
Zaphod's driver, whichever applies). Release builds also imply that the program was
started without any debug information embedded.

In Windows, time was measured through the Win32 exclusive QueryPerformanceFrequency()
and QueryPerformanceCounter() routines. In Linux, the measurement was performed via
POSIX gettime() function.

A Win32 MinGW-based (gcc/g++ 4.5.0) build of libfreenect with libusbemu using POSIX
gettime() also yielded to nearly identical performance results than VC++ 2010 with
Performance Counters.

All of the Win32 performance results were also double-checked with Fraps.

In Windows, a single thread yield call was placed after rendering each screen frame
(as recommended in Item 3-3). In Linux, the graphics driver - possibly glXSwapBuffers
itself - seems to be already yielding the rendering thread, and forcing it in the code
did not incur into any extra impact on performance.



Discussion:

Even though there are no streaming frequency discrepancies between platforms, one may
infer, just from the frame times, that Windows clearly has an overhead disadvantage.
However, this does not hold true: there is still plenty of time for the application
logic to run.

For a steady 60FPS (~16.66ms per frame) real-time performance, a Release build using
libusbemu in Windows would still have about 14.70ms per frame available for the client
code, while in Linux the available time would be around 15.50ms, a 0.8ms overhead
between the platforms. Such a small difference should not impose any special design
considerations for the client code.

Furthermore, note that the CPU usage in Windows tend to be lower than in Linux. The
reason behind this difference is quite difficult to summarize here, but it is probably
related to the way Windows and Linux perform asynchronous I/O in USB-mapped files.
The interested reader is encouraged to refer to the following links:
  > http://www.unwesen.de/articles/waitformultipleobjects_considered_expensive
  > http://softwarecommunity.intel.com/articles/eng/2807.htm
  > http://software.intel.com/en-us/blogs/2006/10/19/why-windows-threads-are-better-than-posix-threads/
The memory consumption overhead of libusbemu in Windows is negligible, and as far as
the VC++ memory leak detection goes, libusbemu has no memory leaks.

======================================================================================