Re: [AD] New possible driver and accelerated features?

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


> As for a new (gfx) driver idea, what about a DirectX 9 (or even 7 or
> 8)  driver that's seperate from the current one? I don't use Windows
> anymore, but given the advantages that 7/8/9 have over 3 (or 2?), I
> think having a newer version available would be well worth it, even
> if  it is a seperate driver.

In case it motivates anyone else to try, I thought I'd attach my DX8 driver
(in so far as it got), since I won't be tinkering with it for a while.

Basically, I wrote this driver to test 2D graphics using DX8 but have been
sourly disappointed by the speed. I've tried both with a lockable back
buffer and with texture rendering for the back buffer, but both are
significantly slower than the current allegro DDraw driver.

The test program I've been using is as follows:

#include <allegro.h>
#include <winalleg.h>
#include <stdio.h>

extern HWND allegro_wnd;

int main (int argc, char* argv[]) {

  int starttime, loops = 0;
  char buff[100];
  int now;

  install_allegro (SYSTEM_AUTODETECT, &errno, atexit);
  install_keyboard();

  set_color_depth(32);
  if (set_gfx_mode(GFX_DIRECT3D, 640, 480, 0, 0)) {
  //if (set_gfx_mode(GFX_DIRECTX, 640, 480, 0, 0)) {
    MessageBox(allegro_wnd, allegro_error, "Init", MB_OK);
    return 1;
  }

  starttime = time(NULL);
  while (!key[KEY_ESC]) {
    clear_to_color(screen, rand() % 0xffffff);
    loops++;
  }

  now = time(NULL);
  sprintf(buff, "%d loops in %d secs; %d fps", loops, now-starttime, loops /
(now-starttime));
  MessageBox(allegro_wnd, buff, "What", MB_OK);
}


With my D3D driver, I get about 35 fps; if I swap the commented set_gfx_mode
lines, I get about 3200 fps with the standard DDraw driver.

I hope someone finds the attached useful - it would be cool to have a D3D
driver in allegro, if the performance issues can be resolved.

Cheers,
Chris
/*         ______   ___    ___ 
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      ** IN DEVELOPMENT ** Direct3D gfx drivers.
 *
 *
 *      See readme.txt for copyright information.
 */
#define D3D_SURFACE_OF(bmp) ((D3DBITMAPEXTRA*)(bmp->extra))

#include "wddraw.h"
#include <d3d8.h>
#include <d3dx8math.h>

const int USE_LOCKABLE_BACKBUFFER = 0;

// The custom FVF, which describes the custom vertex structure.
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1)

typedef struct 
{
    D3DVECTOR   position; // The position.
    D3DCOLOR    color;    // The color.
    FLOAT       tu, tv;   // The texture coordinates.
} CUSTOMVERTEX;

LPDIRECT3D8 direct3d = NULL;
LPDIRECT3DDEVICE8 direct3ddevice = NULL;
LPDIRECT3DVERTEXBUFFER8 vertexbuffer = NULL;

HMODULE d3dlib = NULL;
/* DirectDraw internals */
static PALETTEENTRY palette_entry[256];


static struct BITMAP *init_direct3d_accel(int w, int h, int v_w, int v_h, int color_depth);
static void gfx_direct3d_set_palette(AL_CONST struct RGB *p, int from, int to, int vsync);
static void gfx_direct3d_exit(struct BITMAP *bmp);
static void finalize_fullscreen_init(void);
static void switch_in_fullscreen(void);
int exit_direct3d(void);


GFX_DRIVER gfx_direct3d =
{
   GFX_DIRECT3D,
   empty_string,
   empty_string,
   "Direct3D accel",
   init_direct3d_accel,
   gfx_direct3d_exit,
   NULL,                         // AL_METHOD(int, scroll, (int x, int y)); 
   gfx_directx_sync,
   gfx_direct3d_set_palette,
   NULL,                         // AL_METHOD(int, request_scroll, (int x, int y));
   NULL,                         // AL_METHOD(int, poll_scroll, (void));
   NULL,                         // AL_METHOD(void, enable_triple_buffer, (void));
   gfx_directx_create_video_bitmap,
   gfx_directx_destroy_video_bitmap,
   gfx_directx_show_video_bitmap,
   gfx_directx_request_video_bitmap,
   gfx_directx_create_system_bitmap,
   gfx_directx_destroy_system_bitmap,
   NULL,                         // AL_METHOD(int, set_mouse_sprite, (struct BITMAP *sprite, int xfocus, int yfocus));
   NULL,                         // AL_METHOD(int, show_mouse, (struct BITMAP *bmp, int x, int y));
   NULL,                         // AL_METHOD(void, hide_mouse, (void));
   NULL,                         // AL_METHOD(void, move_mouse, (int x, int y));
   NULL,                         // AL_METHOD(void, drawing_mode, (void));
   NULL,                         // AL_METHOD(void, save_video_state, (void*));
   NULL,                         // AL_METHOD(void, restore_video_state, (void*));
   gfx_directx_fetch_mode_list,
   0, 0,                         // physical (not virtual!) screen size
   TRUE,                         // true if video memory is linear
   0,                            // bank size, in bytes
   0,                            // bank granularity, in bytes
   0,                            // video memory size, in bytes
   0,                            // physical address of video memory
   FALSE                         // true if driver runs windowed
};


static WIN_GFX_DRIVER win_gfx_fullscreen =
{
   FALSE,                       // true if driver has backing store
   switch_in_fullscreen,
   NULL,                        // AL_METHOD(void, switch_out, (void));
   NULL,                        // AL_METHOD(void, enter_sysmode, (void));
   NULL,                        // AL_METHOD(void, exit_sysmode, (void));
   NULL,                        // AL_METHOD(void, move, (int x, int y, int w, int h));
   NULL,                        // AL_METHOD(void, iconify, (void));
   NULL                         // AL_METHOD(void, paint, (RECT *));
};

typedef struct {
  int isTexture;
  int lockCount;
  LPDIRECT3DSURFACE8 surface;
  LPDIRECT3DTEXTURE8 texture;
} D3DBITMAPEXTRA ;

static int _newmode_width, _newmode_height;
static int _newmode_depth, _newmode_refresh;

/* color_depth_to_d3d_format:
 *  Convert a colour depth into the appropriate D3D tag
 */
static D3DFORMAT color_depth_to_d3d_format(int color_depth)
{
  switch (color_depth)
  {
    case 8:
      return D3DFMT_P8;
    case 15:
      return D3DFMT_X1R5G5B5;
    case 16:
      return D3DFMT_R5G6B5;
    case 24:
      return D3DFMT_R8G8B8;
    case 32:
      return D3DFMT_A8R8G8B8;
  }
  return 0;
}

/* gfx_directx_set_palette:
 */
static void gfx_direct3d_set_palette(AL_CONST struct RGB *p, int from, int to, int vsync)
{
   int n;

   /* convert into Windows format */
   for (n = from; n <= to; n++) {
      palette_entry[n].peRed = (p[n].r << 2) | ((p[n].r & 0x30) >> 4);
      palette_entry[n].peGreen = (p[n].g << 2) | ((p[n].g & 0x30) >> 4);
      palette_entry[n].peBlue = (p[n].b << 2) | ((p[n].b & 0x30) >> 4);
      palette_entry[n].peFlags = 0xff; // alpha value
   }

   /* set the convert palette */
   IDirect3DDevice8_SetPaletteEntries(direct3ddevice, 0, &palette_entry[0]);
}

/* wnd_set_video_mode:
 *  Called by window thread to set a gfx mode; this is needed because DirectDraw can only
 *  change the mode in the thread that handles the window.
 */
static int wnd_create_device(void)
{
  CUSTOMVERTEX *vertices;
  D3DPRESENT_PARAMETERS d3dpp;
  memset( &d3dpp, 0, sizeof(d3dpp) );

  d3dpp.BackBufferWidth = _newmode_width;
  d3dpp.BackBufferHeight = _newmode_height;
  d3dpp.BackBufferFormat = color_depth_to_d3d_format(_newmode_depth);
  d3dpp.BackBufferCount = 1;
  d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
  d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
  d3dpp.hDeviceWindow = allegro_wnd;
  d3dpp.Windowed = FALSE;
  d3dpp.EnableAutoDepthStencil = FALSE;
  d3dpp.Flags = 0;
  if (USE_LOCKABLE_BACKBUFFER)
    d3dpp.Flags |= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
  d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
  d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //D3DPRESENT_INTERVAL_DEFAULT;
  /* If full screen, specify the refresh rate */
  if ((d3dpp.Windowed == FALSE) && (_newmode_refresh > 0))
    d3dpp.FullScreen_RefreshRateInHz = _newmode_refresh;

  if (IDirect3D8_CreateDevice(direct3d, D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL, allegro_wnd,
                      D3DCREATE_MIXED_VERTEXPROCESSING,
                      &d3dpp, &direct3ddevice) != D3D_OK)
  {
    return -1;
  }

  // use the default palette
  IDirect3DDevice8_SetCurrentTexturePalette(direct3ddevice, 0);

  // set the render flags.
  IDirect3DDevice8_SetRenderState(direct3ddevice, D3DRS_CULLMODE, D3DCULL_NONE);
  IDirect3DDevice8_SetRenderState(direct3ddevice, D3DRS_LIGHTING, FALSE);
  IDirect3DDevice8_SetRenderState(direct3ddevice, D3DRS_ZENABLE, FALSE);

  IDirect3DDevice8_SetVertexShader(direct3ddevice, D3DFVF_CUSTOMVERTEX);

  if (IDirect3DDevice8_CreateVertexBuffer(direct3ddevice, 4*sizeof(CUSTOMVERTEX),
          0, D3DFVF_CUSTOMVERTEX, D3DPOOL_DEFAULT, &vertexbuffer) != D3D_OK) {
    IDirect3DDevice8_Release(direct3ddevice);
    direct3ddevice = NULL;
    return -1;
  }

  IDirect3DVertexBuffer8_Lock(vertexbuffer, 0, 0, (BYTE**)&vertices, 0);

  vertices[0].position.x = -1.0f;
  vertices[0].position.y = 1.0f;
  vertices[0].position.z = 0.5f;
  vertices[0].color=0xffffffff;
  vertices[0].tu=0.0;
  vertices[0].tv=0.0;
  vertices[1].position.x = 1.0f;
  vertices[1].position.y = 1.0f;
  vertices[1].position.z = 0.5f;
  vertices[1].color=0xffffffff;
  vertices[1].tu=1.0;
  vertices[1].tv=0.0;
  vertices[2].position.x = -1.0f;
  vertices[2].position.y = -1.0f;
  vertices[2].position.z = 0.5f;
  vertices[2].color=0xffffffff;
  vertices[2].tu=0.0;
  vertices[2].tv=1.0;
  vertices[3].position.x = 1.0f;
  vertices[3].position.y = -1.0f;
  vertices[3].position.z = 0.5f;
  vertices[3].color=0xffffffff;
  vertices[3].tu=1.0;
  vertices[3].tv=1.0;

  IDirect3DVertexBuffer8_Unlock(vertexbuffer);

  return 0;
}

/* make_bitmap_from_backbuffer:
 *  
 */
BITMAP *make_bitmap_from_backbuffer()
{
   BITMAP *bmp;
   int i;
   LPDIRECT3DSURFACE8 surface;
   LPDIRECT3DTEXTURE8 texture;
   D3DSURFACE_DESC surface_desc;

   if (IDirect3DDevice8_GetBackBuffer(direct3ddevice, 0, D3DBACKBUFFER_TYPE_MONO, &surface) != D3D_OK) {
     return NULL;
   }

   if (IDirect3DSurface8_GetDesc(surface, &surface_desc) != D3D_OK) {
     IDirect3DSurface8_Release(surface);
     return NULL;
   }

   bmp = (BITMAP *) malloc(sizeof(BITMAP) + sizeof(char *) * surface_desc.Height);
   if (!bmp) {
      IDirect3DSurface8_Release(surface);
      return NULL;
   }

   bmp->w = surface_desc.Width;
   bmp->h = surface_desc.Height;
   bmp->cr = bmp->w;
   bmp->cb = bmp->h;
   bmp->clip = TRUE;
   bmp->cl = 0;
   bmp->ct = 0;
   bmp->vtable = &_screen_vtable;
   // share the DirectDraw write_bank functions, since they merely
   // call a pointer which we adjust
   bmp->write_bank = gfx_directx_write_bank;
   bmp->read_bank = gfx_directx_write_bank;
   bmp->dat = NULL;
   bmp->id = BMP_ID_VIDEO;
   bmp->extra = NULL;
   bmp->x_ofs = 0;
   bmp->y_ofs = 0;
   bmp->seg = _video_ds();
   for (i = 0; i < bmp->h; i++)
      bmp->line[i] = NULL;

   if (!USE_LOCKABLE_BACKBUFFER) 
   {
     // Not a lockable backbuffer, so create a texture instead
     IDirect3DSurface8_Release(surface);

     if (IDirect3DDevice8_CreateTexture(direct3ddevice, bmp->w, bmp->h, 1, 0,
               color_depth_to_d3d_format(_color_depth),
               D3DPOOL_MANAGED, &texture) != D3D_OK) {
       free(bmp);
       return NULL;
     }
   }

   bmp->extra = malloc(sizeof(D3DBITMAPEXTRA));
   D3D_SURFACE_OF(bmp)->lockCount = 0;

   if (USE_LOCKABLE_BACKBUFFER) {
     D3D_SURFACE_OF(bmp)->isTexture = 0;
     D3D_SURFACE_OF(bmp)->surface = surface;
   }
   else {
     D3D_SURFACE_OF(bmp)->isTexture = 1;
     D3D_SURFACE_OF(bmp)->texture = texture;
   }

   return bmp;
}

void gfx_direct3d_unlock(BITMAP *bmp)
{
  D3DBITMAPEXTRA *bmpextra = D3D_SURFACE_OF(bmp);

  if (bmp->id & BMP_ID_VIDEO) {
    bmpextra->lockCount--;
    if (bmpextra->lockCount == 0) {
      int i;

      if (bmpextra->isTexture == 0) {
        IDirect3DSurface8_UnlockRect(bmpextra->surface);
      }
      else {
        IDirect3DTexture8_UnlockRect(bmpextra->texture, 0);
      }
      // make direct memory access impossible
      for (i = 0; i < bmp->h; i++)
        bmp->line[i] = NULL;

      bmp->id &= ~BMP_ID_LOCKED;

      //IDirect3DDevice8_Clear(direct3ddevice, 0, NULL, D3DCLEAR_TARGET,
                             //D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
      IDirect3DDevice8_BeginScene(direct3ddevice);
      if (bmpextra->isTexture) {
        IDirect3DDevice8_SetTexture(direct3ddevice, 0, (IDirect3DBaseTexture8*)bmpextra->texture);
        IDirect3DDevice8_SetStreamSource(direct3ddevice, 0, vertexbuffer, sizeof(CUSTOMVERTEX));
        IDirect3DDevice8_DrawPrimitive(direct3ddevice, D3DPT_TRIANGLESTRIP , 0, 2);
      }
      IDirect3DDevice8_EndScene(direct3ddevice);
      IDirect3DDevice8_Present(direct3ddevice, NULL, NULL, NULL, NULL);
    }
  }
}

void gfx_direct3d_lock(BITMAP *bmp)
{
  D3DBITMAPEXTRA *bmpextra = D3D_SURFACE_OF(bmp);
  // **** NEED TO HANDLE SUB BITMAPS
  if (bmp->id & BMP_ID_VIDEO) {
    if (bmpextra->lockCount == 0) {
      D3DLOCKED_RECT locked_area;
      int i;
      unsigned char *direct_ptr;

      if (bmpextra->isTexture == 0) {
        IDirect3DSurface8_LockRect(bmpextra->surface, &locked_area, NULL, D3DLOCK_NOSYSLOCK);
      }
      else {
        IDirect3DTexture8_LockRect(bmpextra->texture, 0, &locked_area, NULL, D3DLOCK_NOSYSLOCK);
      }

      direct_ptr = (unsigned char*)locked_area.pBits;
      // set up direct memory access
      for (i = 0; i < bmp->h; i++)
        bmp->line[i] = &direct_ptr[i * locked_area.Pitch];

      bmp->id |= BMP_ID_LOCKED;
    }
    bmpextra->lockCount++;
  }
}

void gfx_direct3d_autolock(BITMAP *bmp)
{
  bmp->id |= BMP_ID_AUTOLOCK;
  gfx_direct3d_lock(bmp);
}

/* gfx_directx_created_sub_bitmap:
 */
void gfx_direct3d_created_sub_bitmap(BITMAP *bmp, BITMAP *parent)
{
   bmp->extra = parent;
}



/* setup_driver:
 *  Helper function for initializing the gfx driver.
 */
int d3d_setup_driver(GFX_DRIVER *drv, int w, int h, int color_depth)
{
   /* setup the driver structure */
   drv->w = w;
   drv->h = h;
   drv->linear = 1;
   drv->vid_mem = IDirect3DDevice8_GetAvailableTextureMem(direct3ddevice);
   drv->vid_mem += w * h * BYTES_PER_PIXEL(color_depth);

   /* shoehorn ourselves into the DirectDraw asm routines */
   ptr_gfx_directx_autolock = gfx_direct3d_autolock;
   ptr_gfx_directx_unlock = gfx_direct3d_unlock;

   /* modify the vtable to work with video memory */
   memcpy(&_screen_vtable, _get_vtable(color_depth), sizeof(_screen_vtable));
   _screen_vtable.unwrite_bank = gfx_directx_unwrite_bank;
   _screen_vtable.acquire = gfx_direct3d_lock;
   _screen_vtable.release = gfx_direct3d_unlock;
   _screen_vtable.created_sub_bitmap = gfx_direct3d_created_sub_bitmap;

   return 0;
}

/* init_directx_accel:
 *  Initializes the DirectDraw fullscreen hardware-accelerated driver.
 */
static struct BITMAP *init_direct3d_accel(int w, int h, int v_w, int v_h, int color_depth)
{
   struct BITMAP *bmp = NULL;
   D3DDISPLAYMODE final_display_mode;
   IDirect3D8 * (WINAPI * lpDirect3DCreate8)(UINT);

   _enter_critical();

   d3dlib = LoadLibrary("d3d8.dll");
   if (d3dlib == NULL) {
     ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Direct3D 8 does not appear to be installed"));
     goto Error;
   }

   lpDirect3DCreate8 = (IDirect3D8 * (WINAPI *)(UINT))GetProcAddress(d3dlib, "Direct3DCreate8");
   if (lpDirect3DCreate8 == NULL) {
     ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Entry point not foudn in d3d8.dll"));
     goto Error;
   }

   direct3d = lpDirect3DCreate8( D3D_SDK_VERSION );
   if (direct3d == NULL) {
     // unable to initialize d3d
     ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Unable to initialize Direct3D 8"));
     goto Error;
   }

   _newmode_width = w;
   _newmode_height = h;
   _newmode_depth = color_depth;
   _newmode_refresh = _refresh_rate_request;
   // Set the display mode in the window's thread
   if (wnd_call_proc(wnd_create_device)) {
     ustrzcpy(allegro_error, ALLEGRO_ERROR_SIZE, get_config_text("Unable to use requested display mode"));
     goto Error;
   }

   // The display mode has been set up successfully, save the
   // final refresh rate that we are using
   if (IDirect3DDevice8_GetDisplayMode(direct3ddevice, &final_display_mode) == D3D_OK) {
     _set_current_refresh_rate(final_display_mode.RefreshRate);
   }
   else {
     _set_current_refresh_rate(0);
   }

   d3d_setup_driver(&gfx_direct3d, w, h, color_depth);

   bmp = make_bitmap_from_backbuffer();

   if (bmp) {
      finalize_fullscreen_init();
   }

   _exit_critical();

   return bmp;

Error:
   if (direct3d) {
     IDirect3D8_Release(direct3d);
     direct3d = NULL;
   }

   if (d3dlib) {
     FreeLibrary(d3dlib);
     d3dlib = NULL;
   }

   _exit_critical();
   return NULL;
}

static void gfx_direct3d_exit(struct BITMAP *bmp) {
   _enter_critical();

   _set_current_refresh_rate(0);

   if (bmp)
      clear_bitmap(bmp);

   /* disconnect from the system driver */
   win_gfx_driver = NULL;

   /* destroy primary surface *
   if (primary_surface) {
      gfx_directx_destroy_surface(primary_surface);
      primary_surface = NULL;
      forefront_bitmap = NULL;
   }

   /* normally this list must be empty *
   unregister_all_ddraw_surfaces();

   /* destroy clipper *
   if (ddclipper) {
      IDirectDrawClipper_Release(ddclipper);
      ddclipper = NULL;
   }

   /* destroy palette *
   if (ddpalette) {
      IDirectDrawPalette_Release(ddpalette);
      ddpalette = NULL;
   }

   /* free pseudo memory *
   if (pseudo_surf_mem) {
      free(pseudo_surf_mem);
      pseudo_surf_mem = NULL;
   }

   /* before restoring video mode, hide window */
   set_display_switch_mode(SWITCH_PAUSE);
   system_driver->restore_console_state();
   restore_window_style();

   /* let the window thread set the coop level back
    * to normal and destroy the directdraw object
    */
   wnd_call_proc(exit_direct3d);

   if (d3dlib)
     FreeLibrary(d3dlib);

   _exit_critical();
}

int exit_direct3d(void)
{
   if (direct3ddevice) {
      /* release DirectDraw interface */
      IDirect3DDevice8_Release(direct3ddevice);

      direct3ddevice = NULL;
   }

   if (direct3d) {

      /* release DirectDraw interface */
      IDirect3D8_Release(direct3d);

      direct3d = NULL;
   }

   return 0;
}


/* finalize_fullscreen_init:
 *  Finalizes initialization of the three fullscreen drivers.
 */
static void finalize_fullscreen_init(void)
{
   /* connect to the system driver */
   win_gfx_driver = &win_gfx_fullscreen;

   /* set the default switching policy */
   set_display_switch_mode(SWITCH_AMNESIA);

   /* grab input devices */
   win_grab_input();
}



/* switch_in_fullscreen:
 *  Handles screen switched in.
 */
static void switch_in_fullscreen(void)
{
   restore_all_ddraw_surfaces();
}



Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/