visual-c++gtkdirect3dcairodirect3d9

IDirect3D9::CreateDevice prevents GTK+ 2 Cairo rendering


I need to call the Cairo Graphics API (included with GTK+ 2.24.10 bundle), whilst also using the Direct3D 9 API (DirectX SDK March 2009).

To test, I make basic Cairo function calls as follows:

#include <cairo\cairo.h>

...

cairo_surface_t *surface;
cairo_t *cr;
cairo_status_t status;

surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 390, 60);
status = cairo_surface_status(surface);

cr = cairo_create(surface);
status = cairo_status(cr);

cairo_set_source_rgba(cr, 0, 0, 0, 1);
status = cairo_status(cr);

cairo_rectangle(cr, 175, 10, 40, 40);
status = cairo_status(cr);

cairo_fill(cr);
status = cairo_status(cr);

cairo_surface_flush(surface);
status = cairo_surface_write_to_png(surface, "c:\\cairo_test.png");

cairo_destroy(cr);
cairo_surface_destroy(surface);

...

If these Cairo API calls are made before a call to IDirect3D9::CreateDevice, then the following .PNG is output:

PNG of black square created with Cairo API

After making the call to IDirect3D9::CreateDevice, the same Cairo API calls create a blank .PNG:

Blank PNG created with Cairo API when it should contain a black square

The IDirect3D9::CreateDevice call is parameterised as follows:

Direct3DCreate9(D3D_SDK_VERSION)->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, WindowHandle, D3DCREATE_HARDWARE_VERTEXPROCESSING, &PresentParameters, &PDevice);

Where the object parameters contain the following member variables:

WindowHandle    0x001b07f8 {unused=13111204 }   HWND__ *
    unused  13111204    int

PresentParameters   {BackBufferWidth=0 BackBufferHeight=0 BackBufferFormat=D3DFMT_UNKNOWN (0) ...}  _D3DPRESENT_PARAMETERS_
        BackBufferWidth 0   unsigned int
        BackBufferHeight    0   unsigned int
        BackBufferFormat    D3DFMT_UNKNOWN (0)  _D3DFORMAT
        BackBufferCount 1   unsigned int
        MultiSampleType D3DMULTISAMPLE_NONE (0) _D3DMULTISAMPLE_TYPE
        MultiSampleQuality  0   unsigned long
        SwapEffect  D3DSWAPEFFECT_DISCARD (1)   _D3DSWAPEFFECT
+       hDeviceWindow   0x001b07f8 {unused=13111204 }   HWND__ *
        Windowed    1   int
        EnableAutoDepthStencil  0   int
        AutoDepthStencilFormat  D3DFMT_UNKNOWN (0)  _D3DFORMAT
        Flags   1   unsigned long
        FullScreen_RefreshRateInHz  0   unsigned int
        PresentationInterval    0   unsigned int

PDevice 0x00000000 <NULL>   IDirect3DDevice9 *
+       IUnknown    <struct at NULL>    IUnknown

The question is:


Solution

  • This behaviour is a result of an incompatibility between the Direct3D 9 API (DirectX SDK March 2009) and Cairo Graphics API (1.10.2).

    The following mailing list thread from 2006-10-16 details how creating a Direct3D 9 device alters the FPU to single-precision mode, and that this version of the Cairo API assumed a higher level of precision when making its colour calculations:

    http://cairo.cairographics.narkive.com/L9XYWFkQ/cairo-direct3d-interaction-bug-and-fix-x86-fpu-precision

    A fix for this bug was introduced immediately:

    https://bugs.freedesktop.org/show_bug.cgi?id=7497

    Cairo v1.10.2 was released 2010-12-25, which postdates this fix by 4 years, but the erroneous behaviour is still present.

    However, the fix must have made it into the Cairo code base at some point, as there is no evidence of the bug in Cairo v1.14.6, which can be downloaded from the HexChat project:

    https://github.com/hexchat/gtk-win32

    N.B. the libraries in this project's distribution are not Windows XP compatible. If you target Windows XP, you will need to build your own versions of the binaries.

    A massive thank you to the mystery friend who used their search-fu to find the original thread concerning the Cairo bug.