Browse Source

implemented directx 9 support for panda

Asad M. Zaman 22 years ago
parent
commit
906a0547b7

+ 207 - 0
panda/src/dxgsg9/config_dxgsg9.cxx

@@ -0,0 +1,207 @@
+// Filename: config_dxgsg8.cxx
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_dxgsg9.h"
+#include "dxGraphicsStateGuardian9.h"
+#include "dxSavedFrameBuffer9.h"
+#include "dxTextureContext9.h"
+#include "graphicsPipeSelection.h"
+#include "wdxGraphicsWindow9.h"
+#include "wdxGraphicsPipe9.h"
+
+#include <dconfig.h>
+
+Configure(config_dxgsg9);
+//NotifyCategoryDef(dxgsg9, ":display:gsg");  dont want to merge this with the regular parent class dbg output
+NotifyCategoryDef(dxgsg9, "dxgsg");
+NotifyCategoryDef(wdxdisplay9, "windisplay");
+
+// Configure this variable true to cause the DXGSG to show each
+// transform space it renders by drawing a little unit axis.  This
+// cannot be enabled when the player is compiled in NDEBUG mode.
+bool dx_show_transforms = config_dxgsg9.GetBool("dx-show-transforms", false);
+
+//  Configure this to TRUE if you want DirectX to control the entire screen,
+//  If false, it will just blit into a window.
+bool dx_full_screen = config_dxgsg9.GetBool("dx-full-screen", false);
+
+//  Configure this true to force the rendering to sync to the video
+//  refresh, or false to let your frame rate go as high as it can,
+//  irrespective of the video refresh.
+bool dx_sync_video = config_dxgsg9.GetBool("sync-video", true);
+
+// Set Level of MultiSampling to be used, if HW supports it.  Valid values are 2-16.
+DWORD dx_multisample_antialiasing_level = (DWORD) config_dxgsg9.GetInt("dx-multisample-antialiasing-level", 0);
+
+// Configure this true to perform a cull traversal over the geometry
+// by default, false otherwise.  The cull traversal provides support
+// for state-sorting, z-sorting, and binning.
+bool dx_cull_traversal = config_dxgsg9.GetBool("dx-cull-traversal", true);
+
+// if true, if card only supports per-vertex fog, it will be treated as no-HW fog capability
+bool dx_no_vertex_fog = config_dxgsg9.GetBool("dx-no-vertex-fog", false);
+
+// if true, overwrite cursor bitmap tip with "D3D" to distinguish it from GDI cursor 
+bool dx_show_cursor_watermark = config_dxgsg9.GetBool("dx-show-cursor-watermark", 
+#ifdef _DEBUG
+    true
+#else
+    false
+#endif
+    );
+
+// if true, triangle filter will be used to generate mipmap levels instead of default box filter
+bool dx_use_triangle_mipgen_filter = config_dxgsg9.GetBool("dx-use-triangle-mipgen-filter", false);
+
+// Configure this true to cause all lighting normals to automatically
+// be normalized by the CPU before rendering.  This is
+// necessary if you intend to render things under scale transforms and
+// expect lighting to work correctly.  Maybe one day there will be
+// another way to set this at runtime, instead of only as a configure
+// variable
+bool dx_auto_normalize_lighting = config_dxgsg9.GetBool("auto-normalize-lighting", true);
+
+bool dx_show_fps_meter = config_dxgsg9.GetBool("show-fps-meter", false);
+float dx_fps_meter_update_interval = max(0.5,config_dxgsg9.GetFloat("fps-meter-update-interval", 1.7));
+
+#ifndef NDEBUG
+// debugging flag
+// values are same as D3DCULL enumtype, 0 - no force, 1 - force none, 2 - force CW, 3 - force CCW
+int dx_force_backface_culling = config_dxgsg9.GetInt("dx-force-backface-culling", 0);
+#endif
+
+bool dx_mipmap_everything = config_dxgsg9.GetBool("dx-mipmap-everything", false);
+bool dx_ignore_mipmaps = config_dxgsg9.GetBool("dx-ignore-mipmaps", false);
+
+// if this is set, more accurate but more expensive fog computations are performed
+bool dx_use_rangebased_fog = config_dxgsg9.GetBool("dx-use-rangebased-fog", false);
+bool dx_force_16bpptextures = config_dxgsg9.GetBool("dx-force-16bpptextures", false);
+bool dx_no_dithering = config_dxgsg9.GetBool("dx-no-dithering", false);
+bool dx_force_16bpp_zbuffer = config_dxgsg9.GetBool("dx-force-16bpp-zbuffer", false);
+bool dx_do_vidmemsize_check = config_dxgsg9.GetBool("do-vidmemsize-check", true);
+// Setting this true theoretically hinders render performance, because
+// it forces the FPU to go through some extra work to clean itself up
+// after rendering a frame, but the performance cost seems to be
+// small.  On the other hand, setting it false can force the
+// application to run in single-precision arithmetic mode, even if
+// it believes it is using double-precision variables.
+bool dx_preserve_fpu_state = config_dxgsg9.GetBool("dx-preserve-fpu-state", true);
+
+// if true, override win-width/height and use driver vidmem info to
+// pick what will be a fullscreen window size close to the best perf
+// capability of card, based on a heuristic
+bool dx_pick_best_screenres = config_dxgsg9.GetBool("pick-best-screenres", false);
+
+int dx_preferred_device_id = config_dxgsg9.GetInt("dx-preferred-device-id", -1);
+
+#ifdef _DEBUG
+float dx_global_miplevel_bias = config_dxgsg9.GetFloat("dx-global-miplevel-bias", 0.0);
+bool dx_debug_view_mipmaps = config_dxgsg9.GetBool("dx-debug-view-mipmaps", false);
+//int dx_print_texstats = config_dxgsg9.GetBool("dx-print-texstats", 0);
+#endif
+
+// use dx9 or GDI mouse cursor in fullscreen mode?
+// Nvidia dx9 cursor is invisible as of 28.32 drivers, so using GDI in fullscrn by default for now
+bool dx_use_dx_cursor = config_dxgsg9.GetBool("dx-use-dx-cursor", false);
+
+bool dx_force_anisotropic_filtering = config_dxgsg9.GetBool("dx-force-anisotropic-filtering", false);
+
+// set 'retained-mode #t' and this to have prepare_geom concatenate all tristrips within a geom 
+// together using degenerate tris
+const bool link_tristrips = config_dxgsg9.GetBool("link-tristrips", false);
+
+// note:  offset currently disabled since it wasnt working properly
+DXDecalType dx_decal_type = GDT_mask;
+
+// Note: must be a ptr not a regular string because the init-string constructor for
+// a global/static string variable will run AFTER the dll static init fn
+// init_libdxgsg9(), which means the string will be reset to "" after we read it in
+string *pdx_vertexshader_filename=NULL;
+string *pdx_pixelshader_filename=NULL;
+
+// texture file to be set globally, usually for special FX
+string *pdx_globaltexture_filename=NULL;
+// tex stagenum to set the global tex to
+UINT dx_globaltexture_stagenum = (UINT) config_dxgsg9.GetInt("dx-globaltexture-stagenum", 0);
+
+static DXDecalType
+parse_decal_type(const string &type) {
+  if (type == "mask") {
+    return GDT_mask;
+  } else if (type == "blend") {
+    return GDT_blend;
+  } else if (type == "offset") {
+    return GDT_offset;
+  }
+  dxgsg9_cat.error() << "Invalid dx-decal-type: " << type << "\n";
+  return GDT_mask;
+}
+
+ConfigureFn(config_dxgsg9) {
+  init_libdxgsg9();
+}
+
+void init_config_string(string *&pFname,const char *ConfigrcVarname) {
+  // pFname is reference to string ptr
+
+  // dont try to take the & of a soon-to-be-gone stack var string, this must be on the heap!
+  pFname = new string(config_dxgsg9.GetString(ConfigrcVarname, ""));
+  if(pFname->empty()) {
+      delete pFname;
+      pFname=NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: init_libdxgsg
+//  Description: Initializes the library.  This must be called at
+//               least once before any of the functions or classes in
+//               this library can be used.  Normally it will be
+//               called by the static initializers and need not be
+//               called explicitly, but special cases exist.
+////////////////////////////////////////////////////////////////////
+void
+init_libdxgsg9() {
+  static bool initialized = false;
+  if (initialized) {
+    return;
+  }
+  initialized = true;
+
+  string decal_type = config_dxgsg9.GetString("dx-decal-type", "");
+  if (!decal_type.empty()) {
+    dx_decal_type = parse_decal_type(decal_type);
+  }
+
+  init_config_string(pdx_vertexshader_filename,"dx-vertexshader-filename");
+  init_config_string(pdx_pixelshader_filename,"dx-pixelshader-filename");
+  init_config_string(pdx_globaltexture_filename,"dx-globaltexture-filename");
+
+  DXGraphicsStateGuardian9::init_type();
+  DXSavedFrameBuffer9::init_type();
+  DXTextureContext9::init_type();
+  DXGeomNodeContext9::init_type();
+
+  wdxGraphicsPipe9::init_type();
+  wdxGraphicsWindow9::init_type();
+
+  GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
+  selection->add_pipe_type(wdxGraphicsPipe9::get_class_type(),
+                           wdxGraphicsPipe9::pipe_constructor);
+
+}

+ 82 - 0
panda/src/dxgsg9/config_dxgsg9.h

@@ -0,0 +1,82 @@
+// Filename: config_dxgsg.h
+// Created by:  masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef CONFIG_DXGSG9_H
+#define CONFIG_DXGSG9_H
+
+#include "pandabase.h"
+#include "notifyCategoryProxy.h"
+#include "dxgsg9base.h"
+
+NotifyCategoryDecl(dxgsg9, EXPCL_PANDADX, EXPTP_PANDADX);
+NotifyCategoryDecl(wdxdisplay9, EXPCL_PANDADX, EXPTP_PANDADX);
+
+extern bool dx_full_screen;
+extern bool dx_sync_video;
+extern bool dx_cull_traversal;
+extern bool dx_show_fps_meter;
+extern bool dx_no_vertex_fog;
+extern bool dx_show_cursor_watermark;
+extern bool dx_full_screen_antialiasing;
+extern float dx_fps_meter_update_interval;
+extern bool dx_auto_normalize_lighting;
+extern bool dx_use_rangebased_fog;
+extern const bool link_tristrips;
+extern DWORD dx_multisample_antialiasing_level;
+extern bool dx_use_triangle_mipgen_filter;
+extern bool dx_use_dx_cursor;
+
+
+// debug flags we might want to use in full optimized build
+extern bool dx_ignore_mipmaps;
+extern bool dx_mipmap_everything;
+extern bool dx_show_transforms;
+extern bool dx_force_16bpptextures;
+extern bool dx_no_dithering;
+extern bool dx_force_anisotropic_filtering;
+extern bool dx_force_16bpp_zbuffer;
+extern bool dx_do_vidmemsize_check;
+extern bool dx_preserve_fpu_state;
+extern bool dx_pick_best_screenres;
+extern int dx_preferred_device_id;
+
+extern string *pdx_vertexshader_filename;
+extern string *pdx_pixelshader_filename;
+extern string *pdx_globaltexture_filename;
+extern UINT   dx_globaltexture_stagenum;
+
+#ifndef NDEBUG
+extern int dx_force_backface_culling;
+#endif
+
+#ifdef _DEBUG
+extern float dx_global_miplevel_bias;
+extern bool dx_debug_view_mipmaps;
+#endif
+
+// Ways to implement decals.
+enum DXDecalType {
+  GDT_mask,   // GL 1.0 style, involving three steps and double-draw of polygon
+  GDT_blend,  // As above, but slower; use blending to disable colorbuffer writes
+  GDT_offset  // The fastest, using GL 1.1 style glPolygonOffset
+};
+extern DXDecalType dx_decal_type;
+
+extern EXPCL_PANDADX void init_libdxgsg9();
+
+#endif

+ 946 - 0
panda/src/dxgsg9/d3dfont9.cxx

@@ -0,0 +1,946 @@
+//-----------------------------------------------------------------------------
+// File: D3DFont.cpp
+//
+// Desc: Texture-based font class
+//       modified from a modified version of DXSDK CD3DFont from http://www.lafaqmfc.com/directx.htm
+//       Note that this is faster than ID3DXFont, which calls GDI in Draw()
+//-----------------------------------------------------------------------------
+#ifndef STRICT
+#define STRICT
+#endif
+
+#include "dxgsg9base.h"
+#include <stdio.h>
+#include <tchar.h>
+#include <d3dx9.h>
+#include "d3dfont9.h"
+
+//-----------------------------------------------------------------------------
+// Custom vertex types for rendering text
+//-----------------------------------------------------------------------------
+
+struct FONT2DVERTEX {
+    D3DXVECTOR4 p;   DWORD color;     FLOAT tu, tv;
+};
+struct FONT3DVERTEX {
+    D3DXVECTOR3 p;   D3DXVECTOR3 n;   FLOAT tu, tv;
+};
+
+inline FONT2DVERTEX InitFont2DVertex( const D3DXVECTOR4& p, D3DCOLOR color,
+                                      FLOAT tu, FLOAT tv ) {
+    FONT2DVERTEX v;   v.p = p;   v.color = color;   v.tu = tu;   v.tv = tv;
+    return v;
+}
+
+inline FONT3DVERTEX InitFont3DVertex( const D3DXVECTOR3& p, const D3DXVECTOR3& n,
+                                      FLOAT tu, FLOAT tv ) {
+    FONT3DVERTEX v;   v.p = p;   v.n = n;   v.tu = tu;   v.tv = tv;
+    return v;
+}
+
+//-----------------------------------------------------------------------------
+// Name: CD3DFont()
+// Desc: Font class constructor
+//-----------------------------------------------------------------------------
+CD3DFont::CD3DFont( TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags ) {
+    _tcscpy( m_strFontName, strFontName );
+    m_dwFontHeight         = dwHeight;
+    m_dwFontFlags          = dwFlags;
+
+    m_pd3dDevice           = NULL;
+    m_pTexture             = NULL;
+    m_pVB                  = NULL;
+
+    m_pSBSavedStateBlock    = NULL;
+    m_pSBDrawTextStateBlock = NULL;
+
+    ClearBeginEndData ( ) ; 
+    m_bBeginText = false ; 
+}
+
+//-----------------------------------------------------------------------------
+// Name: ~CD3DFont()
+// Desc: Font class destructor
+//-----------------------------------------------------------------------------
+CD3DFont::~CD3DFont() {
+    DeleteDeviceObjects();
+}
+
+//-----------------------------------------------------------------------------
+// Name: InitDeviceObjects()
+// Desc: Initializes device-dependent objects, including the vertex buffer used
+//       for rendering text and the texture map which stores the font image.
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::InitDeviceObjects( LPDIRECT3DDEVICE9 pd3dDevice ) {
+    HRESULT hr;
+
+    // Keep a local copy of the device
+    m_pd3dDevice = pd3dDevice;
+
+    // Establish the font and texture size
+    m_fTextScale  = 1.0f; // Draw fonts into texture without scaling
+
+    // Large fonts need larger textures 
+    // We can be generous at this step, this is an estimate
+    if(m_dwFontHeight > 40)
+        m_dwTexWidth = m_dwTexHeight = 2048;
+    else if(m_dwFontHeight > 32)
+        m_dwTexWidth = m_dwTexHeight = 1024;
+    else if(m_dwFontHeight > 16)
+        m_dwTexWidth = m_dwTexHeight = 512;
+    else
+        m_dwTexWidth = m_dwTexHeight = 256;
+
+    // Prepare to create a bitmap
+    DWORD*      pBitmapBits;
+    BITMAPINFO bmi;
+    ZeroMemory( &bmi.bmiHeader,  sizeof(BITMAPINFOHEADER) );
+    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
+    bmi.bmiHeader.biWidth       =  (int)m_dwTexWidth;
+    bmi.bmiHeader.biHeight      = -(int)m_dwTexHeight;
+    bmi.bmiHeader.biPlanes      = 1;
+    bmi.bmiHeader.biCompression = BI_RGB;
+    bmi.bmiHeader.biBitCount    = 32;
+
+    // Create a DC and a bitmap for the font
+    HDC     hDC       = CreateCompatibleDC( NULL );
+    HBITMAP hbmBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS,
+                                          (VOID**)&pBitmapBits, NULL, 0 );
+    SetMapMode( hDC, MM_TEXT );
+
+    // Create a font.  By specifying ANTIALIASED_QUALITY, we might get an
+    // antialiased font, but this is not guaranteed.
+    INT nHeight    = -MulDiv( m_dwFontHeight, 
+                              (INT)(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72 );
+    DWORD dwBold   = (m_dwFontFlags&D3DFONT_BOLD)   ? FW_BOLD : FW_NORMAL;
+    DWORD dwItalic = (m_dwFontFlags&D3DFONT_ITALIC) ? TRUE    : FALSE;
+    HFONT hFont    = CreateFont( nHeight, 0, 0, 0, dwBold, 
+
+                                 FALSE ,   // dwItalic, // NO! We should not do that...
+                                    // See below comment about GetTextExtentPoint32
+
+                                 FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
+                                 CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
+                                 VARIABLE_PITCH, m_strFontName );
+    if(NULL==hFont) {
+        dxgsg9_cat.error() << "CD3DFont InitDeviceObjects(): initial CreateFont failed!  GetLastError=" << GetLastError() << endl;
+        return E_FAIL;
+    }
+
+    HBITMAP hbmOld = ( HBITMAP ) SelectObject ( hDC, hbmBitmap );
+    HFONT   hfOld  = ( HFONT ) SelectObject ( hDC, hFont );
+
+    // Set text properties
+    SetTextColor( hDC, RGB(255,255,255) );
+    SetBkColor(   hDC, 0x00000000 );
+    SetTextAlign( hDC, TA_TOP );
+
+    // First Loop through all printable characters 
+    // in order to determine the smallest necessary texture
+    DWORD x = 0;
+    DWORD y = 0;
+    TCHAR str[2] = _T("x");
+    SIZE size;
+    SIZE sizes [ 127 - 32 ] ;
+
+    TCHAR c;
+    for(c=32; c<127; c++) {
+        str[0] = c;
+        // GetTextExtentPoint32 does not care that the font is Italic or not, it will 
+        // return the same value. However, if we specify an Italic font, the output 
+        // on the bitmap will use more pixels. 
+        // If the font is Italic we have to output the standard character 
+        // and bend our vertices.
+        GetTextExtentPoint32 ( hDC, str, 1, & sizes [ c - 32 ] );
+    } ; 
+
+    static DWORD TexturesSizes [ 5 ] = { 128 , 256 , 512 , 1024 , 2048} ; 
+    DWORD dwTexSize = 0 ;  
+    for(DWORD iTex = 0 ; iTex < 5 ; ++ iTex) {
+        // fake the tex coord loop calculation 
+        x = 0 ; 
+        y = 0 ; 
+        for(TCHAR c=32; c<127; c++) {
+            if((DWORD)( x + sizes[ c - 32 ].cx+1) > TexturesSizes [ iTex ]) {
+                x  = 0;
+                y += sizes[ c - 32 ].cy+1;
+                // new y size 
+                if((DWORD) ( y + sizes[ 0 ].cy + 1 ) >= TexturesSizes [ iTex ]) {
+                    // does not fit, let's try a larger one 
+                    break ; 
+                }; 
+            }; 
+            x += sizes[ c - 32 ].cx + 2 ;
+        } ; 
+
+        if((DWORD) ( y + sizes[ 0 ].cy + 1 ) < TexturesSizes [ iTex ]) {
+            // Yahoo! it fits! 
+            dwTexSize = TexturesSizes [ iTex ] ; 
+            break ; 
+        }; 
+    } ; 
+
+    m_dwTexWidth = m_dwTexHeight = dwTexSize ;
+
+    // Select old objects ( added ) 
+    // Is this needed for compatible DCs? 
+    // The old handles are not not NULL, so it is done "by the book"
+    SelectObject ( hDC, hbmOld );
+    SelectObject ( hDC, hfOld );
+
+    // delete our gdi objects
+    DeleteObject( hbmBitmap );
+    DeleteObject( hFont );
+    DeleteDC( hDC );
+
+    // moved here to allow deletion of GDI objects 
+    if(dwTexSize == 0) {
+        dxgsg9_cat.error() << "CD3DFont InitDeviceObjects() error: Texture didnt fit, creation failed!\n";
+        return E_FAIL;
+    }
+
+    // Re-Create new GDI stuff with the optimal size 
+    // 
+    // Prepare to create a bitmap
+    ZeroMemory( &bmi.bmiHeader,  sizeof(BITMAPINFOHEADER) );
+    bmi.bmiHeader.biSize        = sizeof(BITMAPINFOHEADER);
+    bmi.bmiHeader.biWidth       =  (int)m_dwTexWidth;
+    bmi.bmiHeader.biHeight      = -(int)m_dwTexHeight;
+    bmi.bmiHeader.biPlanes      = 1;
+    bmi.bmiHeader.biCompression = BI_RGB;
+    bmi.bmiHeader.biBitCount    = 32;
+
+    // Create a DC and a bitmap for the font
+    hDC       = CreateCompatibleDC( NULL );
+    hbmBitmap = CreateDIBSection( hDC, &bmi, DIB_RGB_COLORS,
+                                  (VOID**)&pBitmapBits, NULL, 0 );
+    SetMapMode( hDC, MM_TEXT );
+
+    // Create a font.  By specifying ANTIALIASED_QUALITY, we might get an
+    // antialiased font, but this is not guaranteed.
+    nHeight  = -MulDiv( m_dwFontHeight, 
+                        (INT)(GetDeviceCaps(hDC, LOGPIXELSY) * m_fTextScale), 72 );
+    dwBold   = (m_dwFontFlags&D3DFONT_BOLD)   ? FW_BOLD : FW_NORMAL;
+    dwItalic = (m_dwFontFlags&D3DFONT_ITALIC) ? TRUE    : FALSE;
+    hFont    = CreateFont( nHeight, 0, 0, 0, dwBold, 
+                           FALSE , // was dwItalic, // see above 
+                           FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
+                           CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
+                           VARIABLE_PITCH, m_strFontName );
+
+    if(NULL==hFont) {
+        dxgsg9_cat.error() << "CD3DFont InitDeviceObjects(): optimal CreateFont failed!  GetLastError=" << GetLastError() << endl;
+        return E_FAIL;
+    }
+
+    hbmOld = ( HBITMAP ) SelectObject ( hDC, hbmBitmap );
+    hfOld  = ( HFONT ) SelectObject ( hDC, hFont );
+
+    // Set text properties
+    SetTextColor( hDC, RGB(255,255,255) );
+    SetBkColor(   hDC, 0x00000000 );
+    SetTextAlign( hDC, TA_TOP );
+
+    // If requested texture is too big, use a smaller texture and smaller font,
+    // and scale up when rendering.
+    D3DCAPS9 d3dCaps;
+    m_pd3dDevice->GetDeviceCaps( &d3dCaps );
+
+    if(m_dwTexWidth > d3dCaps.MaxTextureWidth) {
+        m_fTextScale = (FLOAT)d3dCaps.MaxTextureWidth / (FLOAT)m_dwTexWidth;
+        m_dwTexWidth = m_dwTexHeight = d3dCaps.MaxTextureWidth;
+    }; 
+
+    // Create a new texture for the font
+    hr = m_pd3dDevice->CreateTexture( m_dwTexWidth, m_dwTexHeight, 1,
+                                      0, D3DFMT_A4R4G4B4,
+                                      D3DPOOL_MANAGED, &m_pTexture, NULL);
+
+    if(FAILED(hr)) {
+        SelectObject ( hDC, hbmOld );
+        SelectObject ( hDC, hfOld );
+
+        DeleteObject( hbmBitmap );
+        DeleteObject( hFont );
+        DeleteDC( hDC );
+
+        dxgsg9_cat.error() << "CD3DFont InitDeviceObjs CreateTexture failed!" << D3DERRORSTRING(hr);
+        return hr;
+    }; 
+
+    // Loop through all printable character and output them to the bitmap..
+    // Meanwhile, keep track of the corresponding tex coords for each character.
+    x = 0 ; 
+    y = 0 ; 
+
+    for(c=32; c<127; c++) {
+        str[0] = c;
+        GetTextExtentPoint32( hDC, str, 1, &size );
+        if((DWORD)(x+size.cx+1) > m_dwTexWidth) {
+            x  = 0;
+            y += size.cy+1;
+        }
+
+        // We need one pixel on both sides 
+
+        // plus one here for one pixel on the left
+        ExtTextOut( hDC, x + 1, y, ETO_OPAQUE, NULL, str, 1, NULL );
+
+        m_fTexCoords[c-32][0] = ( (FLOAT) x + 0.5f ) / m_dwTexWidth ;
+        m_fTexCoords[c-32][1] = ( (FLOAT) y + 0.5f ) / m_dwTexHeight ;
+        m_fTexCoords[c-32][2] = ( (FLOAT) x + 0.5f + size.cx ) / m_dwTexWidth;
+        m_fTexCoords[c-32][3] = ( (FLOAT) y + 0.5f + size.cy ) / m_dwTexHeight;
+
+        // plus two here because we also need one more pixel on the right side
+        x += size.cx + 2 ;
+    }
+
+    // Lock the surface and write the alpha values for the set pixels
+    D3DLOCKED_RECT d3dlr;
+    m_pTexture->LockRect( 0, &d3dlr, 0, 0 );
+    BYTE* pDstRow = (BYTE*)d3dlr.pBits;
+    WORD* pDst16;
+    BYTE bAlpha; // 4-bit measure of pixel intensity
+
+    for(y=0; y < m_dwTexHeight; y++) {
+        pDst16 = (WORD*)pDstRow;
+        for(x=0; x < m_dwTexWidth; x++) {
+            bAlpha = (BYTE)((pBitmapBits[m_dwTexWidth*y + x] & 0xff) >> 4);
+            if(bAlpha > 0) {
+                *pDst16++ = (bAlpha << 12) | 0x0fff;
+            } else {
+                *pDst16++ = 0x0000;
+            }
+        }
+        pDstRow += d3dlr.Pitch;
+    }
+
+    // Done updating texture, so clean up used objects
+    m_pTexture->UnlockRect(0);
+
+    SelectObject ( hDC, hbmOld );
+    SelectObject ( hDC, hfOld );
+
+    DeleteObject( hbmBitmap );
+    DeleteObject( hFont );
+    DeleteDC( hDC );
+
+    return RestoreDeviceObjects();
+}
+
+//-----------------------------------------------------------------------------
+// Name: RestoreDeviceObjects()
+// Desc:
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::RestoreDeviceObjects() {
+    HRESULT hr;
+
+    // Create vertex buffer for the letters
+    if(FAILED( hr = m_pd3dDevice->CreateVertexBuffer(
+            // can be rendered 3d 
+                                                    MAX_NUM_VERTICES*sizeof(FONT3DVERTEX /*FONT2DVERTEX */ ),
+                                                    D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC, 0,
+                                                    D3DPOOL_DEFAULT,  // D3DUSAGE_DYNAMIC makes D3DPOOL_MANAGED impossible
+                                                    &m_pVB, NULL ) )) {
+        dxgsg9_cat.error() << "CD3DFont CreateVB failed!" << D3DERRORSTRING(hr);
+        return hr;
+    }
+
+    PRINT_REFCNT(dxgsg9,m_pd3dDevice);
+
+    // Create the state blocks for rendering text
+    for(UINT which=0; which<2; which++) {
+        m_pd3dDevice->BeginStateBlock();
+        m_pd3dDevice->SetTexture( 0, m_pTexture );
+
+        if(D3DFONT_ZENABLE & m_dwFontFlags)
+            m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
+        else
+            m_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
+
+        m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
+        m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND,   D3DBLEND_SRCALPHA );
+        m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND,  D3DBLEND_INVSRCALPHA );
+        m_pd3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE,  TRUE );
+        m_pd3dDevice->SetRenderState( D3DRS_ALPHAREF,         0x08 );
+        m_pd3dDevice->SetRenderState( D3DRS_ALPHAFUNC,  D3DCMP_GREATEREQUAL );
+        m_pd3dDevice->SetRenderState( D3DRS_FILLMODE,   D3DFILL_SOLID );
+        m_pd3dDevice->SetRenderState( D3DRS_CULLMODE,   D3DCULL_CCW );
+        m_pd3dDevice->SetRenderState( D3DRS_STENCILENABLE,    FALSE );
+        m_pd3dDevice->SetRenderState( D3DRS_CLIPPING,         TRUE );
+        m_pd3dDevice->SetRenderState( D3DRS_ANTIALIASEDLINEENABLE,    FALSE );
+        m_pd3dDevice->SetRenderState( D3DRS_CLIPPLANEENABLE,  FALSE );
+        m_pd3dDevice->SetRenderState( D3DRS_VERTEXBLEND,      FALSE );
+        m_pd3dDevice->SetRenderState( D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE );
+        m_pd3dDevice->SetRenderState( D3DRS_FOGENABLE,        FALSE );
+        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_POINT );
+        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_POINT );
+        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MIPFILTER, D3DTEXF_NONE );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );
+        m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_DISABLE );
+        m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_DISABLE );
+
+        if(which==0)
+            m_pd3dDevice->EndStateBlock( &m_pSBSavedStateBlock );
+        else
+            m_pd3dDevice->EndStateBlock( &m_pSBDrawTextStateBlock );
+    }
+
+    return S_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Name: InvalidateDeviceObjects()
+// Desc: Destroys all device-dependent objects
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::InvalidateDeviceObjects() {
+    HRESULT hr;
+
+    PRINT_REFCNT(dxgsg9,m_pd3dDevice);
+
+    if(IS_VALID_PTR(m_pd3dDevice)) {
+        // undo SetStreamSource before releasing VB
+
+        IDirect3DVertexBuffer9 *pStreamData=NULL;
+        UINT StreamStride;
+        hr = m_pd3dDevice->GetStreamSource(0,&pStreamData,NULL,&StreamStride);
+        SAFE_RELEASE(pStreamData);  // undo GetStreamSource AddRef
+        if(pStreamData==m_pVB)
+            hr = m_pd3dDevice->SetStreamSource(0,NULL,0,0);
+    }
+
+    PRINT_REFCNT(dxgsg9,m_pVB);
+
+    RELEASE(m_pVB,dxgsg9,"d3dfont VB",RELEASE_ONCE);
+
+    PRINT_REFCNT(dxgsg9,m_pd3dDevice);
+
+    /* not necessary in DX9
+    // Delete the state blocks
+    if(m_pd3dDevice) {
+        assert(IS_VALID_PTR(m_pd3dDevice));
+        if(m_pSBSavedStateBlock)
+            m_pd3dDevice->DeleteStateBlock( m_pSBSavedStateBlock );
+        if(m_pSBDrawTextStateBlock)
+            m_pd3dDevice->DeleteStateBlock( m_pSBDrawTextStateBlock );
+    }
+    */
+
+    PRINT_REFCNT(dxgsg9,m_pd3dDevice);
+
+    m_pSBSavedStateBlock    = NULL;
+    m_pSBDrawTextStateBlock = NULL;
+
+    return S_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Name: DeleteDeviceObjects()
+// Desc: Destroys all device-dependent objects
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::DeleteDeviceObjects() {
+    PRINT_REFCNT(dxgsg9,m_pd3dDevice);
+
+    InvalidateDeviceObjects();
+
+    SAFE_RELEASE( m_pTexture );
+
+    PRINT_REFCNT(dxgsg9,m_pd3dDevice);
+
+    m_pd3dDevice = NULL;
+
+    return S_OK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Name: GetTextExtent()
+// Desc: Get the dimensions of a text string
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::GetTextExtent( TCHAR* strText, SIZE* pSize ) {
+    if(NULL==strText || NULL==pSize)
+        return E_FAIL;
+
+    FLOAT fRowWidth  = 0.0f;
+    FLOAT fRowHeight = (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight;
+    FLOAT fWidth     = 0.0f;
+    FLOAT fHeight    = fRowHeight;
+
+    while(*strText) {
+        TCHAR c = *strText++;
+
+        if(c == _T('\n')) {
+            fRowWidth = 0.0f;
+            fHeight  += fRowHeight;
+        }
+        if(c < _T(' '))
+            continue;
+
+        FLOAT tx1 = m_fTexCoords[c-32][0];
+        FLOAT tx2 = m_fTexCoords[c-32][2];
+
+        fRowWidth += (tx2-tx1)*m_dwTexWidth;
+
+        if(fRowWidth > fWidth)
+            fWidth = fRowWidth;
+    }
+
+    pSize->cx = (int)fWidth;
+    pSize->cy = (int)fHeight;
+
+    return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Name: DrawTextScaled()
+// Desc: Draws scaled 2D text.  Note that x and y are in viewport coordinates
+//       (ranging from -1 to +1).  fXScale and fYScale are the size fraction 
+//       relative to the entire viewport.  For example, a fXScale of 0.25 is
+//       1/8th of the screen width.  This allows you to output text at a fixed
+//       fraction of the viewport, even if the screen or window size changes.
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::DrawTextScaled( FLOAT x, FLOAT y, FLOAT z,
+                                  FLOAT fXScale, FLOAT fYScale, DWORD dwColor,
+                                  TCHAR* strText, DWORD dwFlags ) {
+    if(m_pd3dDevice == NULL)
+        return E_FAIL;
+
+    HRESULT hr ; 
+    if(m_bBeginText) {
+        hr = DeferedDrawTextScaled ( x, y, z, fXScale, fYScale, dwColor, strText, dwFlags ) ; 
+    } else {
+        BeginText ( ) ; 
+        hr = DeferedDrawTextScaled ( x, y, z, fXScale, fYScale, dwColor, strText, dwFlags ) ; 
+        if(! FAILED ( hr ))
+            EndText ( ) ;
+    } ; 
+
+    return hr ;
+}
+
+//-----------------------------------------------------------------------------
+// Name: DrawText()
+// Desc: Draws 2D text
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::DrawText( FLOAT sx, FLOAT sy, DWORD dwColor,
+                            TCHAR* strText, DWORD dwFlags ) {
+    if(m_pd3dDevice == NULL)
+        return E_FAIL;
+
+    HRESULT hr ; 
+    if(m_bBeginText) {
+        hr = DeferedDrawText ( sx, sy, dwColor, strText, dwFlags ) ; 
+    } else {
+        BeginText(); 
+        hr = DeferedDrawText ( sx, sy, dwColor, strText, dwFlags ) ; 
+        if(! FAILED ( hr ))
+            EndText ( ) ;
+    } ; 
+
+    return hr ;
+}
+
+
+void CD3DFont::ClearBeginEndData ( void ) {
+    m_nDeferedCalls = 0 ; 
+    m_TextBuffer [ 0 ] = 0 ; 
+    m_pTextBuffer = & m_TextBuffer [ 0 ] ; 
+} 
+
+HRESULT CD3DFont::BeginText ( void ) {
+    m_bBeginText = true ; 
+    ClearBeginEndData() ; 
+
+    return S_OK ; 
+} 
+
+HRESULT CD3DFont::DeferedDrawTextScaled
+( FLOAT x, FLOAT y, FLOAT z, 
+  FLOAT fXScale, FLOAT fYScale, DWORD dwColor, 
+  TCHAR* strText, DWORD dwFlags ) {
+    return 
+    DeferedDraw ( true , x, y, z, fXScale, fYScale, dwColor, strText, dwFlags ) ; 
+} 
+
+HRESULT CD3DFont::DeferedDrawText
+( FLOAT x, FLOAT y, DWORD dwColor, 
+  TCHAR* strText, DWORD dwFlags ) {
+    return 
+    DeferedDraw ( false , x, y, 0.0f , 0.0f , 0.0f , dwColor, strText, dwFlags ) ; 
+} 
+
+HRESULT CD3DFont::DeferedDraw
+( bool bScaled , 
+  FLOAT x, FLOAT y, FLOAT z, 
+  FLOAT fXScale, FLOAT fYScale, DWORD dwColor, 
+  TCHAR* strText, DWORD dwFlags ) {
+    if(m_nDeferedCalls >= MaxCalls) {
+        dxgsg9_cat.error() << "CD3DFont DeferedDraw() error, MaxCalls exceeded!\n";
+        return E_FAIL ;
+    }
+
+    // we need to make a deep copy of the string 
+    // the user object might have fallen out of scope
+    // when it will be time to render 
+    int nStrLen = strlen ( strText ) + 1 ; 
+    int nUsed = m_pTextBuffer - & m_pTextBuffer [ 0 ] ; 
+    if(nUsed + nStrLen > TextBufferLength) {
+        dxgsg9_cat.error() << "CD3DFont DeferedDraw() error, TextBufferLength exceeded!\n";
+        return E_FAIL ;
+    }
+
+    strcpy ( m_pTextBuffer , strText ) ; 
+    m_DTArgs [ m_nDeferedCalls ].m_strText = m_pTextBuffer ; 
+    m_pTextBuffer += nStrLen ; 
+
+    m_DTArgs [ m_nDeferedCalls ].m_bScaled = bScaled ; 
+    m_DTArgs [ m_nDeferedCalls ].m_x = x ; 
+    m_DTArgs [ m_nDeferedCalls ].m_y = y ; 
+    m_DTArgs [ m_nDeferedCalls ].m_z = z ; 
+    m_DTArgs [ m_nDeferedCalls ].m_fXScale = fXScale ; 
+    m_DTArgs [ m_nDeferedCalls ].m_fYScale = fYScale ; 
+    m_DTArgs [ m_nDeferedCalls ].m_dwColor = dwColor ; 
+    m_DTArgs [ m_nDeferedCalls ].m_dwFlags = dwFlags ; 
+
+    m_nDeferedCalls ++ ; 
+
+    return S_OK ; 
+} 
+
+HRESULT CD3DFont::EndText ( void ) {
+    if(m_pd3dDevice == NULL)
+        return E_FAIL;
+    HRESULT hr;
+
+    assert(IS_VALID_PTR(m_pVB));
+
+    UINT SavedStreamStride;
+    IDirect3DVertexBuffer9 *pSavedStreamData=NULL;
+    IDirect3DVertexShader9 *pSavedVertexShader=NULL;
+    IDirect3DPixelShader9 *pSavedPixelShader=NULL;
+
+    hr = m_pd3dDevice->GetVertexShader(&pSavedVertexShader);
+    hr = m_pd3dDevice->GetPixelShader(&pSavedPixelShader);
+
+    // Set up renderstate
+    hr = m_pSBSavedStateBlock->Capture();
+    hr = m_pSBDrawTextStateBlock->Apply();
+    /*
+    if(pSavedVertexShader!=D3DFVF_FONT2DVERTEX)
+        hr = m_pd3dDevice->SetVertexShader(D3DFVF_FONT2DVERTEX);
+    if(pSavedPixelShader!=NULL)
+        hr = m_pd3dDevice->SetPixelShader(NULL);
+    */
+    hr = m_pd3dDevice->SetVertexShader(NULL);
+    hr = m_pd3dDevice->SetPixelShader(NULL);
+
+    hr = m_pd3dDevice->GetStreamSource(0,&pSavedStreamData,NULL,&SavedStreamStride);
+    if(FAILED(hr)) {
+        dxgsg9_cat.error() << "CD3DFont EndText GetStreamSource() failed!" << D3DERRORSTRING(hr);
+        return E_FAIL;
+    }
+
+    // undo GetStreamSource AddRef
+    SAFE_RELEASE(pSavedStreamData);
+
+    if((pSavedStreamData!=m_pVB)||(SavedStreamStride!=sizeof(FONT2DVERTEX))) {
+        hr = m_pd3dDevice->SetStreamSource(0,m_pVB,0,sizeof(FONT2DVERTEX));
+        if(FAILED(hr)) {
+            dxgsg9_cat.error() << "CD3DFont EndText initial SetStreamSource() failed!" << D3DERRORSTRING(hr);
+            return E_FAIL;
+        }
+    }
+
+    // Set filter states
+    //
+    // filter if any in our list is specified filtered 
+    //
+    // This functionality is different from the original D3DFont 
+    // but is a significant speed increase
+    //
+    // User will make another batch if necessary
+    //  
+    bool bFiltered = false ; 
+    UINT i;
+    for(i = 0 ; i < m_nDeferedCalls ; ++ i) {
+        DWORD   dwFlags = m_DTArgs [ i ].m_dwFlags ; 
+        if(dwFlags & D3DFONT_FILTERED) {
+            bFiltered = true ; 
+            break ; 
+        }
+    } ; 
+    if(bFiltered) {
+        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );
+        m_pd3dDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
+    }; 
+
+    // useless if nothing is scaled but should be fast enough 
+    D3DVIEWPORT9 vp;
+    m_pd3dDevice->GetViewport( &vp );
+    FLOAT fLineHeight = ( m_fTexCoords[0][3] - m_fTexCoords[0][1] ) * m_dwTexHeight;
+
+    // Fill vertex buffer
+    FONT2DVERTEX* pVertices;
+    DWORD         dwNumTriangles = 0L;
+    m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
+
+    bool bItalic = 0 != ( m_dwFontFlags & D3DFONT_ITALIC ) ; 
+    // loop on our batched sets of arguments 
+    for(i = 0 ; i < m_nDeferedCalls ; ++ i) {
+        bool    bScaled = m_DTArgs [ i ].m_bScaled ; 
+        FLOAT   x       = m_DTArgs [ i ].m_x       ; 
+        FLOAT   y       = m_DTArgs [ i ].m_y       ; 
+        FLOAT   z       = m_DTArgs [ i ].m_z       ; 
+        FLOAT   fXScale = m_DTArgs [ i ].m_fXScale ; 
+        FLOAT   fYScale = m_DTArgs [ i ].m_fYScale ; 
+        DWORD   dwColor = m_DTArgs [ i ].m_dwColor ; 
+        TCHAR * strText = m_DTArgs [ i ].m_strText ; 
+
+        if(bScaled) {
+
+            FLOAT sx  = (x+1.0f)*vp.Width/2;
+            FLOAT sy  = (y-1.0f)*vp.Height/2;
+            FLOAT sz  = z;
+            FLOAT rhw = 1.0f;
+            FLOAT fStartX = sx;
+
+            FLOAT fBend = 0.0f ; 
+            if(bItalic)
+                fBend = fYScale*vp.Height / 4.0f ;
+
+            while(*strText) {
+                TCHAR c = *strText++;
+
+                if(c == _T('\n')) {
+                    sx  = fStartX;
+                    sy += fYScale*vp.Height;
+                }
+                if(c < _T(' '))
+                    continue;
+
+                FLOAT tx1 = m_fTexCoords[c-32][0];
+                FLOAT ty1 = m_fTexCoords[c-32][1];
+                FLOAT tx2 = m_fTexCoords[c-32][2];
+                FLOAT ty2 = m_fTexCoords[c-32][3];
+
+                FLOAT w = (tx2-tx1)*m_dwTexWidth;
+                FLOAT h = (ty2-ty1)*m_dwTexHeight;
+
+                w *= (fXScale*vp.Height)/fLineHeight;
+                h *= (fYScale*vp.Height)/fLineHeight;
+
+                if(c != _T(' ')) {
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx1, ty2 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f + fBend,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f + fBend,sy+0-0.5f,sz,rhw), dwColor, tx2, ty1 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,sz,rhw), dwColor, tx2, ty2 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f + fBend,sy+0-0.5f,sz,rhw), dwColor, tx1, ty1 );
+                    dwNumTriangles += 2;
+
+                    if(dwNumTriangles*3 > (MAX_NUM_VERTICES-6)) {
+                        // Unlock, render, and relock the vertex buffer
+                        m_pVB->Unlock();
+                        m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
+                        m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
+                        dwNumTriangles = 0L;
+                    }
+                }
+
+                sx += w;
+            } ; 
+        } else {  // not scaled 
+            FLOAT fBend = 0.0f ; 
+            if(bItalic)
+                fBend = fLineHeight / 4.0f ;
+
+            // Lazy guy...
+            FLOAT sx = x ; 
+            FLOAT sy = y ; 
+
+            FLOAT fStartX = sx;
+            while(*strText) {
+                TCHAR c = *strText++;
+
+                if(c == _T('\n')) {
+                    sx = fStartX ;
+                    sy += fLineHeight ;
+                }
+                if(c < _T(' '))
+                    continue;
+
+                FLOAT tx1 = m_fTexCoords[c-32][0];
+                FLOAT ty1 = m_fTexCoords[c-32][1];
+                FLOAT tx2 = m_fTexCoords[c-32][2];
+                FLOAT ty2 = m_fTexCoords[c-32][3];
+
+                FLOAT w = (tx2-tx1) *  m_dwTexWidth / m_fTextScale;
+                FLOAT h = (ty2-ty1) * m_dwTexHeight / m_fTextScale;
+
+                if(c != _T(' ')) {
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx1, ty2 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f + fBend,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f + fBend,sy+0-0.5f,0.9f,1.0f), dwColor, tx2, ty1 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+w-0.5f,sy+h-0.5f,0.9f,1.0f), dwColor, tx2, ty2 );
+                    *pVertices++ = InitFont2DVertex( D3DXVECTOR4(sx+0-0.5f + fBend,sy+0-0.5f,0.9f,1.0f), dwColor, tx1, ty1 );
+                    dwNumTriangles += 2;
+
+                    if(dwNumTriangles*3 > (MAX_NUM_VERTICES-6)) {
+                        // Unlock, render, and relock the vertex buffer
+                        m_pVB->Unlock();
+                        m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
+                        pVertices = NULL;
+                        m_pVB->Lock( 0, 0, (void**)&pVertices, D3DLOCK_DISCARD );
+                        dwNumTriangles = 0L;
+                    }
+                };  // endif not blank 
+
+                sx += w;
+            } ; // end while 
+
+        } ; // end if else scaled 
+
+    } ; // end for 
+
+    m_bBeginText = false ; 
+    ClearBeginEndData ( ) ; 
+
+    // Unlock and render the vertex buffer
+    m_pVB->Unlock();
+    if(dwNumTriangles > 0)
+        m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
+
+    // Restore the modified renderstates
+    m_pSBSavedStateBlock->Apply();
+    /*
+    if((hSavedVertexShader!=NULL) && (hSavedVertexShader!=D3DFVF_FONT2DVERTEX))
+        m_pd3dDevice->SetVertexShader(hSavedVertexShader);
+    if(hSavedPixelShader!=NULL)
+        m_pd3dDevice->SetPixelShader(hSavedPixelShader);
+    */
+    m_pd3dDevice->SetVertexShader(pSavedVertexShader);
+    m_pd3dDevice->SetPixelShader(pSavedPixelShader);
+
+    if(IS_VALID_PTR(pSavedStreamData) && ((pSavedStreamData!=m_pVB)||(SavedStreamStride!=sizeof(FONT2DVERTEX)))) {
+        hr = m_pd3dDevice->SetStreamSource(0,pSavedStreamData,0,SavedStreamStride);
+        if(FAILED(hr)) {
+            dxgsg9_cat.error() << "CD3DFont EndText restore SetStreamSource() failed!" << D3DERRORSTRING(hr);
+            return E_FAIL;
+        }
+        pSavedStreamData->Release();
+    }
+
+    return S_OK;
+} 
+
+#if 0
+// dont need this now
+//-----------------------------------------------------------------------------
+// Name: Render3DText()
+// Desc: Renders 3D text
+//-----------------------------------------------------------------------------
+HRESULT CD3DFont::Render3DText( TCHAR* strText, DWORD dwFlags ) {
+    if(m_pd3dDevice == NULL)
+        return E_FAIL;
+
+    // Setup renderstate
+    m_pd3dDevice->CaptureStateBlock( m_pSBSavedStateBlock );
+    m_pd3dDevice->ApplyStateBlock( m_pSBDrawTextStateBlock );
+    m_pd3dDevice->SetVertexShader( D3DFVF_FONT3DVERTEX );
+    m_pd3dDevice->SetPixelShader( NULL );
+    m_pd3dDevice->SetStreamSource( 0, m_pVB, sizeof(FONT3DVERTEX) );
+
+    // Set filter states
+    if(dwFlags & D3DFONT_FILTERED) {
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR );
+        m_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
+    }
+
+    // Position for each text element
+    FLOAT x = 0.0f;
+    FLOAT y = 0.0f;
+
+    // Center the text block at the origin
+    if(dwFlags & D3DFONT_CENTERED) {
+        SIZE sz;
+        GetTextExtent( strText, &sz );
+        x = -(((FLOAT)sz.cx)/10.0f)/2.0f;
+        y = -(((FLOAT)sz.cy)/10.0f)/2.0f;
+    }
+
+    // Turn off culling for two-sided text
+    if(dwFlags & D3DFONT_TWOSIDED)
+        m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
+
+    FLOAT fStartX = x;
+    TCHAR c;
+
+    // Fill vertex buffer
+    FONT3DVERTEX* pVertices;
+    // DWORD         dwVertex       = 0L; // not ref'ed
+    DWORD         dwNumTriangles = 0L;
+    m_pVB->Lock( 0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD );
+
+    bool bItalic = 0 != ( m_dwFontFlags & D3DFONT_ITALIC ) ; 
+    FLOAT fBend = 0.0f ; 
+    if(bItalic)
+        fBend = ( ( m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight/10.0f ) / 4.0f ;
+
+    while(c = *strText++) {
+        if(c == '\n') {
+            x = fStartX;
+            y -= (m_fTexCoords[0][3]-m_fTexCoords[0][1])*m_dwTexHeight/10.0f;
+        }
+        if(c < 32)
+            continue;
+
+        FLOAT tx1 = m_fTexCoords[c-32][0];
+        FLOAT ty1 = m_fTexCoords[c-32][1];
+        FLOAT tx2 = m_fTexCoords[c-32][2];
+        FLOAT ty2 = m_fTexCoords[c-32][3];
+
+        FLOAT w = (tx2-tx1) * m_dwTexWidth  / ( 10.0f * m_fTextScale );
+        FLOAT h = (ty2-ty1) * m_dwTexHeight / ( 10.0f * m_fTextScale );
+
+        if(c != _T(' ')) {
+            *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0,y+0,0), D3DXVECTOR3(0,0,-1), tx1, ty2 );
+            *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0 + fBend ,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
+            *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
+            *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w + fBend ,y+h,0), D3DXVECTOR3(0,0,-1), tx2, ty1 );
+            *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+w,y+0,0), D3DXVECTOR3(0,0,-1), tx2, ty2 );
+            *pVertices++ = InitFont3DVertex( D3DXVECTOR3(x+0 + fBend ,y+h,0), D3DXVECTOR3(0,0,-1), tx1, ty1 );
+            dwNumTriangles += 2;
+
+            if(dwNumTriangles*3 > (MAX_NUM_VERTICES-6)) {
+                // Unlock, render, and relock the vertex buffer
+                m_pVB->Unlock();
+                m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
+                m_pVB->Lock( 0, 0, (BYTE**)&pVertices, D3DLOCK_DISCARD );
+                dwNumTriangles = 0L;
+            }
+        }
+
+        x += w;
+    }
+
+    // Unlock and render the vertex buffer
+    m_pVB->Unlock();
+    if(dwNumTriangles > 0)
+        m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, dwNumTriangles );
+
+    // Restore the modified renderstates
+    m_pd3dDevice->ApplyStateBlock( m_pSBSavedStateBlock );
+
+    return S_OK;
+}
+#endif

+ 112 - 0
panda/src/dxgsg9/d3dfont9.h

@@ -0,0 +1,112 @@
+//--------------------------------------------------------------------------------------------
+// File: D3DFont.h
+//
+// Desc: Texture-based font class
+//       based on a modified version of DXSDK CD3DFont from http://www.lafaqmfc.com/directx.htm
+//       Note that this is faster than ID3DXFont, which calls GDI in Draw()
+//---------------------------------------------------------------------------------------------
+#ifndef D3DFONT_H
+#define D3DFONT_H
+#include <tchar.h>
+#include <d3d9.h>
+
+// Font creation flags
+#define D3DFONT_BOLD        0x0001
+#define D3DFONT_ITALIC      0x0002
+#define D3DFONT_ZENABLE     0x0004
+
+// Font rendering flags
+#define D3DFONT_CENTERED    0x0001
+#define D3DFONT_TWOSIDED    0x0002
+#define D3DFONT_FILTERED    0x0004
+
+//-----------------------------------------------------------------------------
+// Name: class CD3DFont
+// Desc: Texture-based font class for doing text in a 3D scene.
+//-----------------------------------------------------------------------------
+class CD3DFont
+{
+    enum 
+    { 
+        D3DFVF_FONT2DVERTEX = (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1) , 
+        D3DFVF_FONT3DVERTEX = (D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1) , 
+        TextBufferLength = 1024 , 
+        MAX_NUM_VERTICES = TextBufferLength * 6 , 
+        MaxCalls = 30 
+    } ; 
+
+    TCHAR   m_strFontName[80];            // Font properties
+    DWORD   m_dwFontHeight;
+    DWORD   m_dwFontFlags;
+
+    LPDIRECT3DDEVICE9       m_pd3dDevice; // A D3DDevice used for rendering
+    LPDIRECT3DTEXTURE9      m_pTexture;   // The d3d texture for this font
+    LPDIRECT3DVERTEXBUFFER9 m_pVB;        // VertexBuffer for rendering text
+    DWORD   m_dwTexWidth;                 // Texture dimensions
+    DWORD   m_dwTexHeight;
+    FLOAT   m_fTextScale;
+    FLOAT   m_fTexCoords[128-32][4];
+
+    // Stateblocks for setting and restoring render states
+    IDirect3DStateBlock9* m_pSBSavedStateBlock;
+    IDirect3DStateBlock9* m_pSBDrawTextStateBlock;
+
+    struct DrawTextArgs 
+    {
+        bool  m_bScaled ; 
+        FLOAT m_x ;  FLOAT m_y ;  FLOAT m_z ; 
+        FLOAT m_fXScale ; FLOAT m_fYScale ; 
+        DWORD m_dwColor ;  
+        TCHAR *m_strText ; 
+        DWORD m_dwFlags ; 
+    } ; 
+
+    DrawTextArgs m_DTArgs [ MaxCalls ] ; 
+    char    m_TextBuffer  [ TextBufferLength ] ; 
+    char   *m_pTextBuffer ; 
+    UINT    m_nDeferedCalls ;
+    bool    m_bBeginText ; 
+    inline  HRESULT DeferedDrawText( FLOAT x, FLOAT y, DWORD dwColor, 
+                      TCHAR* strText, DWORD dwFlags=0L );
+    inline  HRESULT DeferedDrawTextScaled ( FLOAT x, FLOAT y, FLOAT z, 
+                            FLOAT fXScale, FLOAT fYScale, DWORD dwColor, 
+                            TCHAR* strText, DWORD dwFlags=0L ) ; 
+    inline  HRESULT DeferedDraw
+            ( bool bScaled , 
+              FLOAT x, FLOAT y, FLOAT z, 
+              FLOAT fXScale, FLOAT fYScale, DWORD dwColor, 
+              TCHAR* strText, DWORD dwFlags ) ; 
+
+    inline  void ClearBeginEndData (void ) ; 
+
+
+public:
+    // 2D and 3D text drawing functions
+    HRESULT BeginText ( void ) ;
+    HRESULT EndText ( void ) ;
+
+    HRESULT DrawText( FLOAT x, FLOAT y, DWORD dwColor, 
+                      TCHAR* strText, DWORD dwFlags=0L );
+    HRESULT DrawTextScaled ( FLOAT x, FLOAT y, FLOAT z, 
+                            FLOAT fXScale, FLOAT fYScale, DWORD dwColor, 
+                            TCHAR* strText, DWORD dwFlags=0L ) ; 
+
+   // HRESULT Render3DText( TCHAR* strText, DWORD dwFlags=0L );
+    
+    // Function to get extent of text
+    HRESULT GetTextExtent( TCHAR* strText, SIZE* pSize );
+
+    // Initializing and destroying device-dependent objects
+    HRESULT InitDeviceObjects(LPDIRECT3DDEVICE9 pd3dDevice);
+    HRESULT RestoreDeviceObjects(void);
+    HRESULT InvalidateDeviceObjects(void);
+    HRESULT DeleteDeviceObjects(void);
+
+    // Constructor / destructor
+    CD3DFont( TCHAR* strFontName, DWORD dwHeight, DWORD dwFlags=0L );
+    ~CD3DFont();
+};
+
+#endif
+
+

+ 33 - 0
panda/src/dxgsg9/dxGeomNodeContext9.I

@@ -0,0 +1,33 @@
+// Filename: dxGeomNodeContext.I
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGeomNodeContext9::Constructor
+//       Access: Public                                 
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DXGeomNodeContext9::
+DXGeomNodeContext9(GeomNode *node) : GeomNodeContext(node)
+{
+  _num_verts = 0;
+/*
+  _pVB = NULL;
+  _pXformed_VB = NULL;
+*/  
+}

+ 35 - 0
panda/src/dxgsg9/dxGeomNodeContext9.cxx

@@ -0,0 +1,35 @@
+// Filename: dxGeomNodeContext.cxx
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dxGeomNodeContext9.h"
+
+TypeHandle DXGeomNodeContext9::_type_handle;
+
+DXGeomNodeContext9::~DXGeomNodeContext9()
+{
+/*
+   if(_pXformed_VB!=NULL)
+       _pXformed_VB->Release();
+   _pXformed_VB=NULL;
+
+   if(_pVB!=NULL)
+       _pVB->Release();
+   _pVB=NULL;
+*/   
+}
+

+ 89 - 0
panda/src/dxgsg9/dxGeomNodeContext9.h

@@ -0,0 +1,89 @@
+// Filename: dxGeomNodeContext8.h
+// Created by:  masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXGEOMNODECONTEXT9_H
+#define DXGEOMNODECONTEXT9_H
+
+#include "dxgsg9base.h"
+
+#include "geomNodeContext.h"
+#include "geomNode.h"
+#include "pvector.h"
+
+typedef struct {
+     DWORD nVerts;
+     D3DPRIMITIVETYPE primtype;
+} DPInfo;
+
+// empty shell unimplemented for DX9 right now
+
+////////////////////////////////////////////////////////////////////
+//       Class : DXGeomNodeContext9
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXGeomNodeContext9 : public GeomNodeContext {
+public:
+  INLINE DXGeomNodeContext9(GeomNode *node);
+  ~DXGeomNodeContext9();
+
+  // A list of the dynamic Geoms within the GeomNode; these aren't
+  // part of the above display list and must be drawn separately
+  typedef pvector< PT(Geom) > Geoms;
+  Geoms _cached_geoms,_other_geoms;
+
+  // VB's are already reference-counted by D3D, no need to make separate panda object to do that
+  // but we will want a way to know if VB has already been xformed by ProcessVerts this frame
+  // if multiple geomnodes share VBs
+
+/*  unimplemented right now
+  LPDIRECT3DVERTEXBUFFER7 _pVB;
+  LPDIRECT3DVERTEXBUFFER7 _pXformed_VB;
+*/  
+  
+
+  int _start_index;   // starting offset of this geom's verts within the VB
+  int _num_verts;     // number of verts used by this geomcontext within the VB
+  
+  BYTE *_pEndofVertData;  // ptr to end of current vert data in VB (note: only used/valid during setup)
+
+  // for multiple geoms per VB, either will have to regen lengths based on per* flags, or store
+  // per geom vector of perprim vectors lengths
+  vector<DPInfo> _PrimInfo;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    GeomNodeContext::init_type();
+    register_type(_type_handle, "DXGeomNodeContext9",
+                  GeomNodeContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "dxGeomNodeContext9.I"
+
+#endif
+

+ 46 - 0
panda/src/dxgsg9/dxGraphicsDevice9.cxx

@@ -0,0 +1,46 @@
+// Filename: dxGraphicsDevice.cxx
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_dxgsg9.h"
+#include "dxGraphicsDevice9.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsDevice9::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXGraphicsDevice9::
+DXGraphicsDevice9(wdxGraphicsPipe9 *pipe) :
+  GraphicsDevice(pipe) {
+
+    _pScrn = NULL;
+    _pD3DDevice = NULL;
+    _pSwapChain = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsDevice9::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXGraphicsDevice9::
+~DXGraphicsDevice9() {
+
+}
+

+ 55 - 0
panda/src/dxgsg9/dxGraphicsDevice9.h

@@ -0,0 +1,55 @@
+// Filename: dxGraphicsDevice.h
+// Created by:  masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXGRAPHICSDEVICE_H
+#define DXGRAPHICSDEVICE_H
+
+//#define GSG_VERBOSE 1
+
+#include "dxgsg9base.h"
+#include "graphicsDevice.h"
+#include "wdxGraphicsPipe9.h"
+
+
+////////////////////////////////////////////////////////////////////
+//   Class : DXGraphicsDevice9
+// Description : A GraphicsDevice necessary for multi-window rendering
+//               in DX. 
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXGraphicsDevice9 : public GraphicsDevice {
+  friend class wdxGraphicsPipe9;
+
+public:
+  DXGraphicsDevice9(wdxGraphicsPipe9 *pipe);
+  ~DXGraphicsDevice9();
+
+  DXScreenData *_pScrn;
+  LPDIRECT3DDEVICE9 _pD3DDevice;  // same as pScrn->_pD3DDevice, cached for spd
+  IDirect3DSwapChain9 *_pSwapChain;
+
+#if 0
+protected:
+  static TypeHandle get_class_type(void);
+  static void init_type(void);
+  virtual TypeHandle get_type(void) const;
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+#endif
+};
+
+#endif
+

+ 428 - 0
panda/src/dxgsg9/dxGraphicsStateGuardian9.I

@@ -0,0 +1,428 @@
+// Filename: dxGraphicsStateGuardian8.I
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+// for dxgsg9_cat stuff
+#include "config_dxgsg9.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_line_smooth
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_line_smooth(bool val) {
+  if(_line_smooth_enabled != val) {
+    _line_smooth_enabled = val;
+  #ifdef NDEBUG
+    {
+        if(val && (_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_ANTIALIASEDGES))
+           dxgsg9_cat.error() << "no HW support for line smoothing!!\n";
+    }
+  #endif
+
+    _pD3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_dither
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_dither(bool val) {
+  if (_dither_enabled != val) {
+
+  #ifdef _DEBUG
+    {
+        if(val && !(_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_DITHER))
+           dxgsg9_cat.error() << "no HW support for color dithering!!\n";
+        return;
+    }
+  #endif
+
+    _dither_enabled = val;
+    _pD3DDevice->SetRenderState(D3DRS_DITHERENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_stencil_test
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_stencil_test(bool val) {
+  if (_stencil_test_enabled != val) {
+    _stencil_test_enabled = val;
+    _pD3DDevice->SetRenderState(D3DRS_STENCILENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_color_material
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_color_material(bool val) {
+  if (_color_material_enabled != val) {
+    _color_material_enabled = val;
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_blend
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_blend(bool val) {
+  if (_blend_enabled != val) {
+    _blend_enabled = val;
+    _pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::set_color_writemask
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+set_color_writemask(UINT color_writemask) {
+  if (_color_writemask != color_writemask) {
+    _color_writemask = color_writemask;
+    if(_pScrn->bCanDirectDisableColorWrites) {
+        // only newer HW supports this rstate
+        _pD3DDevice->SetRenderState(D3DRS_COLORWRITEENABLE, (DWORD)color_writemask);
+    } else {
+        // blending can only handle on/off
+        assert((color_writemask==0x0)||(color_writemask==0xFFFFFFFF));
+        set_blend_mode(_color_write_mode, _color_blend_mode, _transparency_mode);        
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_blend
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_gouraud_shading(bool val) {
+  if (_bGouraudShadingOn != val) {
+    _bGouraudShadingOn = val;
+    _pD3DDevice->SetRenderState(D3DRS_SHADEMODE, (val ? D3DSHADE_GOURAUD : D3DSHADE_FLAT));
+  }
+}
+
+INLINE void DXGraphicsStateGuardian9::
+enable_primitive_clipping(bool val) {
+  if (_clipping_enabled != val) {
+    _clipping_enabled = val;
+    _pD3DDevice->SetRenderState(D3DRS_CLIPPING, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_fog
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_fog(bool val) {
+  if ((_fog_enabled != val) && (_doFogType!=None)) {
+    _fog_enabled = val;
+    _pD3DDevice->SetRenderState(D3DRS_FOGENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::set_vertex_format
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+set_vertex_format(DWORD NewFvfType) {
+#ifdef USE_VERTEX_SHADERS
+  if(_CurVertexShader!=NULL) {
+    // this needs optimization
+    HRESULT hr = _pD3DDevice->SetVertexShader(_CurVertexShader);
+    #ifndef NDEBUG
+    if(FAILED(hr)) {
+           dxgsg9_cat.error() << "SetVertexShader for custom vtx shader failed" << D3DERRORSTRING(hr);
+           exit(1);
+    }
+    #endif
+    _CurFVFType = NewFvfType;    
+    return;
+  }
+#endif
+
+  if (_CurFVFType != NewFvfType) {
+    _CurFVFType = NewFvfType;
+
+ HRESULT hr = _pD3DDevice->SetVertexShader(NULL);
+ hr = _pD3DDevice->SetFVF(NewFvfType);
+
+ //    HRESULT hr = _pD3DDevice->SetVertexShader((IDirect3DVertexShader9*)NewFvfType);
+ #ifndef NDEBUG
+    if(FAILED(hr)) {
+           dxgsg9_cat.error() << "SetVertexShader(0x" << (void*)NewFvfType<<") failed" << D3DERRORSTRING(hr);
+           exit(1);
+    }
+ #endif
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_alpha_test
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_alpha_test(bool val )
+{
+  if (_alpha_test_enabled != val) {
+    _alpha_test_enabled = val;
+    _pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, (DWORD)val);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::call_dxLightModelAmbient
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+call_dxLightModelAmbient( const Colorf& color)
+{
+  if (_lmodel_ambient != color) {
+    _lmodel_ambient = color;
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "dxLightModel(LIGHT_MODEL_AMBIENT, " << color << ")" << endl;
+#endif
+    _pD3DDevice->SetRenderState( D3DRS_AMBIENT,
+                D3DCOLOR_COLORVALUE(color[0], color[1], color[2], color[3]));
+  }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::call_dxAlphaFunc
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+call_dxAlphaFunc(D3DCMPFUNC func, float reference_alpha) {
+  if (_alpha_func != func) {
+    _alpha_func = func;
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "dxAlphaFunc(";
+    switch (func) {
+    case D3DCMP_NEVER:
+      dxgsg9_cat.debug(false) << "D3DCMP_NEVER";
+      break;
+    case D3DCMP_LESS:
+      dxgsg9_cat.debug(false) << "D3DCMP_LESS";
+      break;
+    case D3DCMP_EQUAL:
+      dxgsg9_cat.debug(false) << "D3DCMP_EQUAL";
+      break;
+#ifdef D3DCMP_LEQUAL
+    case D3DCMP_LEQUAL:
+      dxgsg9_cat.debug(false) << "D3DCMP_LEQUAL";
+      break;
+#endif
+    case D3DCMP_GREATER:
+      dxgsg9_cat.debug(false) << "D3DCMP_GREATER";
+      break;
+    case D3DCMP_NOTEQUAL:
+      dxgsg9_cat.debug(false) << "D3DCMP_NOTEQUAL";
+      break;
+#ifdef D3DCMP_GEQUAL
+    case D3DCMP_GEQUAL:
+      dxgsg9_cat.debug(false) << "D3DCMP_GEQUAL";
+      break;
+#endif
+    case D3DCMP_ALWAYS:
+      dxgsg9_cat.debug(false) << "D3DCMP_ALWAYS";
+      break;
+    }
+    dxgsg9_cat.debug() << " , " << reference_alpha << ")" << endl;
+#endif
+    _pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, func);
+  }
+
+  if(_alpha_func_refval != reference_alpha) {
+      _alpha_func_refval = reference_alpha;
+      _pD3DDevice->SetRenderState(D3DRS_ALPHAREF, (UINT) (reference_alpha*255.0f));  //d3d uses 0x0-0xFF, not a float
+  }
+}
+
+
+INLINE void DXGraphicsStateGuardian9::
+call_dxBlendFunc(D3DBLEND sfunc, D3DBLEND dfunc )
+{
+  if (_blend_source_func != sfunc)
+    {
+    _blend_source_func = sfunc;
+    _pD3DDevice->SetRenderState(D3DRS_SRCBLEND, sfunc);
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "dxSrcBlendFunc(";
+    switch (sfunc)
+        {
+        case D3DBLEND_ZERO:
+          dxgsg9_cat.debug(false) << "ZERO, ";
+          break;
+        case D3DBLEND_ONE:
+          dxgsg9_cat.debug(false) << "ONE, ";
+          break;
+        case D3DBLEND_DESTCOLOR:
+          dxgsg9_cat.debug(false) << "DESTCOLOR, ";
+          break;
+        case D3DBLEND_INVDESTCOLOR:
+          dxgsg9_cat.debug(false) << "INVDESTCOLOR, ";
+          break;
+        case D3DBLEND_SRCALPHA:
+          dxgsg9_cat.debug(false) << "SRCALPHA, ";
+          break;
+        case D3DBLEND_INVSRCALPHA:
+          dxgsg9_cat.debug(false) << "INVSRCALPHA, ";
+          break;
+        case D3DBLEND_DESTALPHA:
+          dxgsg9_cat.debug(false) << "DESTALPHA, ";
+          break;
+        case D3DBLEND_INVDESTALPHA:
+          dxgsg9_cat.debug(false) << "INVDESTALPHA, ";
+          break;
+        case D3DBLEND_SRCALPHASAT:
+          dxgsg9_cat.debug(false) << "SRCALPHASAT, ";
+          break;
+        default:
+          dxgsg9_cat.debug(false) << "unknown, ";
+          break;
+        }
+    dxgsg9_cat.debug(false) << endl;
+#endif
+    }
+  if ( _blend_dest_func != dfunc)
+    {
+    _blend_dest_func = dfunc;
+    _pD3DDevice->SetRenderState(D3DRS_DESTBLEND, dfunc);
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "dxDstBlendFunc(";
+    switch (dfunc)
+        {
+        case D3DBLEND_ZERO:
+          dxgsg9_cat.debug(false) << "ZERO, ";
+          break;
+        case D3DBLEND_ONE:
+          dxgsg9_cat.debug(false) << "ONE, ";
+          break;
+        case D3DBLEND_DESTCOLOR:
+          dxgsg9_cat.debug(false) << "DESTCOLOR, ";
+          break;
+        case D3DBLEND_INVDESTCOLOR:
+          dxgsg9_cat.debug(false) << "INVDESTCOLOR, ";
+          break;
+        case D3DBLEND_SRCALPHA:
+          dxgsg9_cat.debug(false) << "SRCALPHA, ";
+          break;
+        case D3DBLEND_INVSRCALPHA:
+          dxgsg9_cat.debug(false) << "INVSRCALPHA, ";
+          break;
+        case D3DBLEND_DESTALPHA:
+          dxgsg9_cat.debug(false) << "DESTALPHA, ";
+          break;
+        case D3DBLEND_INVDESTALPHA:
+          dxgsg9_cat.debug(false) << "INVDESTALPHA, ";
+          break;
+        case D3DBLEND_SRCALPHASAT:
+          dxgsg9_cat.debug(false) << "SRCALPHASAT, ";
+          break;
+        }
+    dxgsg9_cat.debug(false) << endl;
+#endif
+    }
+}
+
+INLINE void DXGraphicsStateGuardian9::
+enable_zwritemask(bool val) {
+    if (_depth_write_enabled != val) {
+        _depth_write_enabled = val;
+        _pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, val);
+    }
+}
+
+/**  unimplemented
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_multisample
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_multisample(bool val) {
+  _multisample_enabled = val;
+  #ifdef NDEBUG
+    dxgsg9_cat.error() << "dx multisample unimplemented!!\n";
+  #endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_multisample_alpha_one
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_multisample_alpha_one(bool val) {
+  if (_multisample_alpha_one_enabled != val) {
+    _multisample_alpha_one_enabled = val;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_multisample_alpha_mask
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_multisample_alpha_mask(bool val) {
+  if (_multisample_alpha_mask_enabled != val) {
+    _multisample_alpha_mask_enabled = val;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_point_smooth
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_point_smooth(bool val) {
+  // _point_smooth_enabled = val;
+
+  #ifdef NDEBUG
+    dxgsg9_cat.error() << "dx point smoothing unimplemented!!\n";
+  #endif
+}
+*/
+

+ 5339 - 0
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -0,0 +1,5339 @@
+// Filename: dxGraphicsStateGuardian.cxx
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dxGraphicsStateGuardian9.h"
+#include "config_dxgsg9.h"
+#include <d3dx9.h>
+#include "displayRegion.h"
+#include "renderBuffer.h"
+#include "geom.h"
+#include "geomSphere.h"
+#include "geomIssuer.h"
+#include "graphicsWindow.h"
+#include "graphicsEngine.h"
+#include "graphicsChannel.h"
+#include "lens.h"
+#include "perspectiveLens.h"
+#include "ambientLight.h"
+#include "directionalLight.h"
+#include "pointLight.h"
+#include "spotlight.h"
+#include "textureAttrib.h"
+#include "lightAttrib.h"
+#include "cullFaceAttrib.h"
+#include "transparencyAttrib.h"
+#include "alphaTestAttrib.h"
+#include "depthTestAttrib.h"
+#include "depthWriteAttrib.h"
+#include "colorWriteAttrib.h"
+#include "texMatrixAttrib.h"
+#include "materialAttrib.h"
+#include "renderModeAttrib.h"
+#include "fogAttrib.h"
+#include "depthOffsetAttrib.h"
+#include "fog.h"
+#include "throw_event.h"
+
+#ifdef DO_PSTATS
+#include "pStatTimer.h"
+#include "pStatCollector.h"
+#endif
+
+// disable nameless struct 'warning'
+#pragma warning (disable : 4201)
+
+#include <mmsystem.h>
+
+// print out simple drawprim stats every few secs
+//#define COUNT_DRAWPRIMS
+//#define PRINT_RESOURCESTATS  // uses d3d GetInfo
+
+//#define DISABLE_DECALING
+#define DISABLE_POLYGON_OFFSET_DECALING
+// currently doesnt work well enough in toontown models for us to use
+// prob is when viewer gets close to decals, they disappear into wall poly, need to investigate
+
+//const int VERT_BUFFER_SIZE = (8*1024L);
+// For sparkle particles, we can have 4 vertices per sparkle, and a
+// particle pool size of 1024 particles
+
+// for sprites, 1000 prims, 6 verts/prim, 24 bytes/vert
+const int VERT_BUFFER_SIZE = (32*6*1024L);
+
+// if defined, pandadx only handles 1 panda display region
+// note multiple region code doesnt work now (see prepare_display_region,set_clipper)
+#define NO_MULTIPLE_DISPLAY_REGIONS
+
+TypeHandle DXGraphicsStateGuardian9::_type_handle;
+
+// bit masks used for drawing primitives
+// bitmask type: normal=0x1,color=0x2,texcoord=0x4
+typedef enum { NothingSet=0,NormalOnly,ColorOnly,Normal_Color,TexCoordOnly,
+               Normal_TexCoord,Color_TexCoord,Normal_Color_TexCoord
+} DrawLoopFlags;
+
+#define PER_NORMAL   NormalOnly
+#define PER_COLOR    ColorOnly
+#define PER_TEXCOORD TexCoordOnly
+
+// xform mat for vshader will usually be loaded at constant regs c4-c7
+#define VSHADER_XFORMMATRIX_CONSTANTREGNUMSTART 4
+
+static D3DMATRIX matIdentity;
+
+#define __D3DLIGHT_RANGE_MAX ((float)sqrt(FLT_MAX))  //for some reason this is missing in dx9 hdrs
+
+#ifdef COUNT_DRAWPRIMS
+// instead of this use nvidia stat drvr or GetInfo VtxStats?
+static DWORD cDPcount=0;
+static DWORD cVertcount=0;
+static DWORD cTricount=0;
+static DWORD cGeomcount=0;
+
+static IDirect3DTexture9 *pLastTexture=NULL;
+static DWORD cDP_noTexChangeCount=0;
+static LPDIRECT3DDEVICE9 global_pD3DDevice = NULL;
+
+static void CountDPs(DWORD nVerts,DWORD nTris) {
+    cDPcount++;
+    cVertcount+=nVerts;
+    cTricount+=nTris;
+
+    if(_pCurDeviceTexture==pLastTexture) {
+        cDP_noTexChangeCount++;
+    } else pLastTexture = _pCurDeviceTexture;
+}
+#else
+#define CountDPs(nv,nt)
+#endif
+
+#define MY_D3DRGBA(r,g,b,a) ((D3DCOLOR) D3DCOLOR_COLORVALUE(r,g,b,a))
+
+#if defined(DO_PSTATS) || defined(PRINT_RESOURCESTATS)
+static bool bTexStatsRetrievalImpossible=false;
+#endif
+
+//#define Colorf_to_D3DCOLOR(out_color) (MY_D3DRGBA((out_color)[0], (out_color)[1], (out_color)[2], (out_color)[3]))
+
+INLINE DWORD
+Colorf_to_D3DCOLOR(const Colorf &cColorf) {
+// MS VC defines _M_IX86 for x86.  gcc should define _X86_
+#if defined(_M_IX86) || defined(_X86_)
+    DWORD d3dcolor,tempcolorval=255;
+
+    // note the default FPU rounding mode will give 255*0.5f=0x80, not 0x7F as VC would force it to by resetting rounding mode
+    // dont think this makes much difference
+
+    __asm {
+        push ebx   ; want to save this in case this fn is inlined
+        push ecx
+        mov ecx, cColorf
+        fild tempcolorval
+        fld DWORD PTR [ecx]
+        fmul ST(0),ST(1)
+        fistp tempcolorval  ; no way to store directly to int register
+        mov eax, tempcolorval
+        shl eax, 16
+
+        fld DWORD PTR [ecx+4]  ;grn
+        fmul ST(0),ST(1)
+        fistp tempcolorval
+        mov ebx,tempcolorval
+        shl ebx, 8
+        or eax,ebx
+
+        fld DWORD PTR [ecx+8]  ;blue
+        fmul ST(0),ST(1)
+        fistp tempcolorval
+        or eax,tempcolorval
+
+        fld DWORD PTR [ecx+12] ;alpha
+        fmul ST(0),ST(1)
+        fistp tempcolorval
+        ; simulate pop 255.0 off FP stack w/o store, mark top as empty and increment stk ptr
+        ffree ST(0)
+        fincstp
+        mov ebx,tempcolorval
+        shl ebx, 24
+        or eax,ebx
+        mov d3dcolor,eax
+        pop ecx
+        pop ebx
+    }
+
+   //   dxgsg9_cat.debug() << (void*)d3dcolor << endl;
+   return d3dcolor;
+#else //!_X86_
+   return MY_D3DRGBA(cColorf[0], cColorf[1], cColorf[2], cColorf[3]);
+#endif //!_X86_
+}
+
+void DXGraphicsStateGuardian9::
+set_color_clear_value(const Colorf& value) {
+  _color_clear_value = value;
+  _d3dcolor_clear_value =  Colorf_to_D3DCOLOR(value);
+}
+
+IDirect3DPixelShader9* DXGraphicsStateGuardian9::
+read_pixel_shader(string &filename) {
+    HRESULT hr;
+    IDirect3DPixelShader9* hShader=NULL;
+    HANDLE hFile=NULL;
+    BYTE *pShaderBytes=NULL;
+    LPD3DXBUFFER pD3DXBuf_Constants=NULL,pD3DXBuf_CompiledShader=NULL,pD3DXBuf_CompilationErrors=NULL;
+
+    assert(_pD3DDevice!=NULL);
+    assert(_pScrn->bCanUsePixelShaders);
+    bool bIsCompiledShader=(filename.find(".pso")!=string::npos);
+
+    if(bIsCompiledShader) {
+        hFile = CreateFile(filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+        if(hFile == INVALID_HANDLE_VALUE) {
+            dxgsg9_cat.error() << "Could not find shader file '"<< filename << "'\n";
+            return NULL;
+        }
+
+        UINT BytesRead,FileSize = GetFileSize(hFile, NULL);
+
+        pShaderBytes = new BYTE[FileSize];
+        if (pShaderBytes==NULL) {
+            dxgsg9_cat.error() << "MemAlloc failed for shader file '"<< filename << "'\n";
+            goto exit_create_pshader;
+        }
+
+        ReadFile(hFile, (void*)pShaderBytes, FileSize, (LPDWORD)&BytesRead, NULL);
+        assert(BytesRead==FileSize);
+    } else {
+        #if defined(NDEBUG) && !defined(COMPILE_TEXT_SHADERFILES)
+            // want to keep bulky d3dx shader assembler stuff out of publish build
+            dxgsg9_cat.error() << "publish build only reads .vso compiled shaders!\n";
+            exit(1);
+        #else
+           // check for file existence
+           WIN32_FIND_DATA Junk;
+           HANDLE FindFileHandle = FindFirstFile(filename.c_str(),&Junk);
+           if ( FindFileHandle == INVALID_HANDLE_VALUE ) {
+                dxgsg9_cat.error() << "Could not find shader file '"<< filename << "'\n";
+                return NULL;
+           }
+           FindClose(FindFileHandle);
+
+           hr = D3DXAssembleShaderFromFile(filename.c_str(),NULL,NULL,D3DXSHADER_DEBUG,&pD3DXBuf_CompiledShader,&pD3DXBuf_CompilationErrors);
+           if(FAILED(hr)) {
+               dxgsg9_cat.error() << "D3DXAssembleShader failed for '"<< filename << "' " << D3DERRORSTRING(hr);
+               if(pD3DXBuf_CompilationErrors!=NULL) {
+                   dxgsg9_cat.error() << "Compilation Errors: " << (char*) pD3DXBuf_CompilationErrors->GetBufferPointer() << endl;
+               }
+               exit(1);
+           }
+           assert(pD3DXBuf_CompilationErrors==NULL);
+        #endif
+   }
+
+   hr = _pD3DDevice->CreatePixelShader((DWORD*) ((pD3DXBuf_CompiledShader!=NULL) ? pD3DXBuf_CompiledShader->GetBufferPointer() : pShaderBytes),
+                                     &hShader);
+   if (FAILED(hr)) {
+        dxgsg9_cat.error() << "CreatePixelShader failed for '"<< filename << "' " << D3DERRORSTRING(hr);
+        hShader=NULL;
+   }
+
+   assert(hShader!=NULL);   // NULL is invalid I hope
+
+   #ifdef _DEBUG
+      dxgsg9_cat.debug() <<  "CreatePixelShader succeeded for "<< filename << endl;
+   #endif
+
+ exit_create_pshader:
+   SAFE_RELEASE(pD3DXBuf_CompiledShader);
+   if(hFile!=NULL)
+     CloseHandle(hFile);
+   SAFE_DELETE(pShaderBytes);
+   return hShader;
+}
+
+
+IDirect3DVertexShader9* DXGraphicsStateGuardian9::
+read_vertex_shader(string &filename) {
+#ifndef USE_VERTEX_SHADERS
+    return NULL;
+#else
+    HRESULT hr;
+    IDirect3DVertexShader9* hShader=NULL;
+    HANDLE hFile=NULL;
+    BYTE *pShaderBytes=NULL;
+    LPD3DXBUFFER pD3DXBuf_Constants=NULL,pD3DXBuf_CompiledShader=NULL,pD3DXBuf_CompilationErrors=NULL;
+    #define VSDDECL_BUFSIZE 1024
+    UINT ShaderDeclHeader[VSDDECL_BUFSIZE];
+
+    // simple decl for posn + color
+    // need way to encode header decl with vsh files (stick in comment?)  (use ID3DXEffect files?)
+    UINT Predefined_DeclArray[] = {
+        D3DVSD_STREAM(0),
+        D3DVSD_REG(D3DVSDE_POSITION, D3DVSDT_FLOAT3 ),      // input register v0
+        D3DVSD_REG(D3DVSDE_DIFFUSE, D3DVSDT_D3DCOLOR ),     // input Register v5
+      //  D3DVSD_CONST(0,1),*(DWORD*)&c[0],*(DWORD*)&c[1],*(DWORD*)&c[2],*(DWORD*)&c[3],
+    };
+
+    memcpy(ShaderDeclHeader,Predefined_DeclArray,sizeof(Predefined_DeclArray));
+
+    // need to append any compiled constants to instr array
+    UINT ShaderDeclHeader_UINTSize=sizeof(Predefined_DeclArray)/sizeof(UINT);
+
+    assert(_pD3DDevice!=NULL);
+    bool bIsCompiledShader=(filename.find(".vso")!=string::npos);
+
+    if(bIsCompiledShader) {
+        hFile = CreateFile(filename.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
+        if(hFile == INVALID_HANDLE_VALUE) {
+            dxgsg9_cat.error() << "Could not find shader file '"<< filename << "'\n";
+            return NULL;
+        }
+
+        UINT BytesRead,FileSize = GetFileSize(hFile, NULL);
+
+        pShaderBytes = new BYTE[FileSize];
+        if (pShaderBytes==NULL) {
+            dxgsg9_cat.error() << "MemAlloc failed for shader file '"<< filename << "'\n";
+            goto exit_create_vshader;
+        }
+
+        ReadFile(hFile, (void*)pShaderBytes, FileSize, (LPDWORD)&BytesRead, NULL);
+        assert(BytesRead==FileSize);
+    } else {
+        #if defined(NDEBUG) && !defined(COMPILE_TEXT_SHADERFILES)
+            // want to keep bulky d3dx shader assembler stuff out of publish build
+            dxgsg9_cat.error() << "publish build only reads .vso compiled shaders!\n";
+            exit(1);
+        #else
+           // check for file existence
+           WIN32_FIND_DATA Junk;
+           HANDLE FindFileHandle = FindFirstFile(filename.c_str(),&Junk);
+           if ( FindFileHandle == INVALID_HANDLE_VALUE ) {
+                dxgsg9_cat.error() << "Could not find shader file '"<< filename << "'\n";
+                return NULL;
+           }
+           FindClose(FindFileHandle);
+
+           hr = D3DXAssembleShaderFromFile(filename.c_str(),D3DXASM_DEBUG,&pD3DXBuf_Constants,&pD3DXBuf_CompiledShader,&pD3DXBuf_CompilationErrors);
+           if(FAILED(hr)) {
+               dxgsg9_cat.error() << "D3DXAssembleShader failed for '"<< filename << "' " << D3DERRORSTRING(hr);
+               if(pD3DXBuf_CompilationErrors!=NULL) {
+                   dxgsg9_cat.error() << "Compilation Errors: " << (char*) pD3DXBuf_CompilationErrors->GetBufferPointer() << endl;
+               }
+               exit(1);
+           }
+           assert(pD3DXBuf_CompilationErrors==NULL);
+
+           if(pD3DXBuf_Constants!=NULL) {
+               // need to insert defined constants after shader decl
+               memcpy(&ShaderDeclHeader[ShaderDeclHeader_UINTSize],pD3DXBuf_Constants->GetBufferPointer(),pD3DXBuf_Constants->GetBufferSize());
+               ShaderDeclHeader_UINTSize+=pD3DXBuf_Constants->GetBufferSize()/sizeof(UINT);
+               pD3DXBuf_Constants->Release();
+           }
+        #endif
+   }
+
+   assert(VSDDECL_BUFSIZE >= (ShaderDeclHeader_UINTSize+1));
+   ShaderDeclHeader[ShaderDeclHeader_UINTSize]=D3DVSD_END();
+
+   UINT UsageFlags = (_pScrn->bCanUseHWVertexShaders ? 0x0 : D3DUSAGE_SOFTWAREPROCESSING);
+   hr = _pD3DDevice->CreateVertexShader((DWORD*)ShaderDeclHeader,
+                                     (DWORD*) ((pD3DXBuf_CompiledShader!=NULL) ? pD3DXBuf_CompiledShader->GetBufferPointer() : pShaderBytes),
+                                     &hShader, UsageFlags);
+   if(FAILED(hr)) {
+        dxgsg9_cat.error() << "CreateVertexShader failed for '"<< filename << "' " << D3DERRORSTRING(hr);
+        hShader=NULL;
+   }
+
+   assert(hShader!=NULL);   // NULL is invalid I hope
+
+   #ifdef _DEBUG
+      dxgsg9_cat.debug() <<  "CreateVertexShader succeeded for "<< filename << endl;
+   #endif
+
+ exit_create_vshader:
+   SAFE_RELEASE(pD3DXBuf_CompiledShader);
+   if(hFile!=NULL)
+     CloseHandle(hFile);
+   SAFE_DELETE(pShaderBytes);
+   return hShader;
+#endif
+}
+
+void DXGraphicsStateGuardian9::
+reset_panda_gsg(void) {
+    GraphicsStateGuardian::reset();
+
+    // overwrite gsg defaults with these values
+
+    // All implementations have the following buffers.
+    _buffer_mask = (RenderBuffer::T_color |
+                    RenderBuffer::T_back
+//                    RenderBuffer::T_depth |
+//                  RenderBuffer::T_stencil |
+//                  RenderBuffer::T_accum
+                    );
+
+    //   stmt below is incorrect for general mono displays, need both right and left flags set.
+    //   stereo not supported in dx9
+    //    _buffer_mask &= ~RenderBuffer::T_right;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXGraphicsStateGuardian9::
+DXGraphicsStateGuardian9(const FrameBufferProperties &properties) :
+  GraphicsStateGuardian(properties) {
+
+    reset_panda_gsg();
+
+    _pScrn = NULL;
+    
+    _bDXisReady = false;
+    _overlay_windows_supported = false;
+
+    _pFvfBufBasePtr = NULL;
+    _index_buf=NULL;
+
+    // may persist across dx_init's?  (for dx8.1 but not dx8.0?)
+    _CurVertexShader = NULL;
+    _CurPixelShader = NULL;
+    _pGlobalTexture = NULL;
+
+    //    _max_light_range = __D3DLIGHT_RANGE_MAX;
+
+    // non-dx obj values inited here should not change if resize is
+    // called and dx objects need to be recreated (otherwise they
+    // belong in dx_init, with other renderstate
+
+    ZeroMemory(&matIdentity,sizeof(D3DMATRIX));
+    matIdentity._11 = matIdentity._22 = matIdentity._33 = matIdentity._44 = 1.0f;
+
+    _cur_read_pixel_buffer=RenderBuffer::T_front;
+    set_color_clear_value(_color_clear_value);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXGraphicsStateGuardian9::
+~DXGraphicsStateGuardian9() {
+    if (IS_VALID_PTR(_pD3DDevice)) 
+        _pD3DDevice->SetTexture(0, NULL);  // this frees reference to the old texture
+    _pCurTexContext = NULL;
+
+    //free_d3d_device();   ???
+
+    free_nondx_resources();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::reset
+//       Access: Public, Virtual
+//  Description: Resets all internal state as if the gsg were newly
+//               created.  The GraphicsWindow pointer represents a
+//               typical window that might be used for this context;
+//               it may be required to set up the frame buffer
+//               properly the first time.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+reset() {
+    reset_panda_gsg();
+    dxgsg9_cat.error() << "DXGSG reset() not implemented properly yet!\n";
+    // what else do we need to do?
+    // delete all the objs too, right?
+    // need to do a
+    //dx_init();
+}
+
+// setup up for re-calling dx_init(), this is not the final exit cleanup routine (see dx_cleanup)
+void DXGraphicsStateGuardian9::
+free_d3d_device(void) {
+    // dont want a full reset of gsg, just a state clear
+    set_state(RenderState::make_empty());
+    // want gsg to pass all state settings through
+
+    _bDXisReady = false;
+
+    if(_pD3DDevice!=NULL)
+     for(int i=0;i<D3D_MAXTEXTURESTAGES;i++)
+         _pD3DDevice->SetTexture(i,NULL);  // d3d should release this stuff internally anyway, but whatever
+
+    DeleteAllDeviceObjects();
+
+    if (_pD3DDevice!=NULL)
+        RELEASE(_pD3DDevice,dxgsg9,"d3dDevice",RELEASE_DOWN_TO_ZERO);
+
+    free_nondx_resources();
+
+    // obviously we dont release ID3D9, just ID3DDevice9
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::free_nondx_resources
+//       Access: Public
+//  Description: Frees some memory that was explicitly allocated
+//               within the dxgsg.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+free_nondx_resources() {
+    // this must not release any objects associated with D3D/DX!
+    // those should be released in free_dxgsg_objects instead
+    SAFE_DELETE_ARRAY(_index_buf);
+    SAFE_DELETE_ARRAY(_pFvfBufBasePtr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::reset
+//       Access: Public, Virtual
+//  Description: Handles initialization which assumes that DX has already been
+//               set up.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+dx_init(void) {
+    HRESULT hr;
+
+    // make sure gsg passes all current state down to us
+    set_state(RenderState::make_empty());
+    // want gsg to pass all state settings down so any non-matching defaults we set here get overwritten
+
+    assert(_pScrn->pD3D9!=NULL);
+    assert(_pD3DDevice!=NULL);
+
+    ZeroMemory(&_lmodel_ambient,sizeof(Colorf));
+    _pD3DDevice->SetRenderState(D3DRS_AMBIENT, 0x0);
+
+    if(_pFvfBufBasePtr==NULL)
+        _pFvfBufBasePtr = new BYTE[VERT_BUFFER_SIZE];  // allocate storage for vertex info.
+    if(_index_buf==NULL)
+        _index_buf = new WORD[PANDA_MAXNUMVERTS];  // allocate storage for vertex index info.
+
+    _pCurFvfBufPtr = NULL;
+
+    _clip_plane_bits = 0;
+    _pD3DDevice->SetRenderState(D3DRS_CLIPPLANEENABLE , 0x0);
+
+    _pD3DDevice->SetRenderState(D3DRS_CLIPPING, true);
+    _clipping_enabled = true;
+
+    // these both reflect d3d defaults
+    _color_writemask = 0xFFFFFFFF;
+    _CurFVFType = 0x0;  // guards SetVertexShader fmt
+
+    _bGouraudShadingOn = false;
+    _pD3DDevice->SetRenderState(D3DRS_SHADEMODE, D3DSHADE_FLAT);
+
+//   this specifies if lighting model uses material color or vertex color
+//   (not related to gouraud/flat shading)
+//   _pD3DDevice->SetRenderState(D3DRS_COLORVERTEX, true);
+
+    _depth_test_enabled = true;
+    _pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, _depth_test_enabled);
+
+    _pCurTexContext = NULL;
+
+    _line_smooth_enabled = false;
+    _pD3DDevice->SetRenderState(D3DRS_ANTIALIASEDLINEENABLE, false);
+
+    _color_material_enabled = false;
+    _normals_enabled = false;
+
+    _depth_test_enabled = D3DZB_FALSE;
+    _pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
+
+    _blend_enabled = false;
+    _pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, (DWORD)_blend_enabled);
+
+    // just use whatever d3d defaults to here
+    _pD3DDevice->GetRenderState(D3DRS_SRCBLEND, (DWORD*)&_blend_source_func);
+    _pD3DDevice->GetRenderState(D3DRS_DESTBLEND, (DWORD*)&_blend_dest_func);
+
+    _fog_enabled = false;
+    _pD3DDevice->SetRenderState(D3DRS_FOGENABLE, _fog_enabled);
+
+    _current_projection_mat = LMatrix4f::ident_mat();
+    _projection_mat_stack_count = 0;
+    _has_scene_graph_color = false;
+
+//  GL stuff that hasnt been translated to DX
+    // none of these are implemented
+    //_multisample_enabled = false;         // bugbug:  translate this to dx_multisample_antialiasing_level?
+    //_point_smooth_enabled = false;
+
+//    _scissor_enabled = false;
+//    _multisample_alpha_one_enabled = false;
+//    _multisample_alpha_mask_enabled = false;
+//    _line_width = 1.0f;
+//    _point_size = 1.0f;
+
+#ifdef COUNT_DRAWPRIMS
+     global_pD3DDevice = pDevice;
+#endif
+    _pCurrentGeomContext = NULL;
+    _bDrawPrimDoSetupVertexBuffer = false;
+
+    _last_testcooplevel_result = D3D_OK;
+
+#if 0
+    // unused now
+    // only 1 channel on dx currently
+    _panda_gfx_channel = _win->get_channel(0);
+#endif
+
+    for(int i=0;i<MAX_POSSIBLE_TEXFMTS;i++) {
+      // look for all possible DX9 texture fmts
+      D3DFORMAT_FLAG fmtflag=D3DFORMAT_FLAG(1<<i);
+      hr = _pScrn->pD3D9->CheckDeviceFormat(_pScrn->CardIDNum,D3DDEVTYPE_HAL,_pScrn->DisplayMode.Format,
+                                            0x0,D3DRTYPE_TEXTURE,g_D3DFORMATmap[fmtflag]);
+      if(SUCCEEDED(hr)){
+        _pScrn->SupportedTexFmtsMask|=fmtflag;
+      }
+    }
+
+    // s3 virge drivers sometimes give crap values for these
+    if(_pScrn->d3dcaps.MaxTextureWidth==0)
+       _pScrn->d3dcaps.MaxTextureWidth=256;
+
+    if(_pScrn->d3dcaps.MaxTextureHeight==0)
+       _pScrn->d3dcaps.MaxTextureHeight=256;
+
+    if ((dx_decal_type==GDT_offset) && !(_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS)) {
+       if(_pScrn->d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) {
+         if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "dx-decal-type 'offset' not supported by hardware, switching to mask-type decals\n";
+         dx_decal_type = GDT_mask;
+       } else {
+         if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "dx-decal-type 'offset' and color-writemasking not supported by hardware, switching to decal double-draw blend-based masking\n";
+         dx_decal_type = GDT_blend;
+       }
+    }
+
+#ifdef DISABLE_POLYGON_OFFSET_DECALING
+    if(dx_decal_type==GDT_offset) {
+        if(dxgsg9_cat.is_spam())
+           dxgsg9_cat.spam() << "polygon-offset decaling disabled in dxgsg, switching to double-draw decaling\n";
+
+        if(_pScrn->d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) {
+         if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "using dx-decal-type 'GDT_mask'\n";
+         dx_decal_type = GDT_mask;
+        } else {
+         if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "dx-decal-type 'mask' not supported by hardware, switching to GDT_blend\n";
+         dx_decal_type = GDT_blend;
+        }
+    }
+#endif
+
+    if (((dx_decal_type==GDT_blend)||(dx_decal_type==GDT_mask)) && !(_pScrn->d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_MASKZ)) {
+        dxgsg9_cat.error() << "dx-decal-types mask&blend impossible to implement, no hardware support for Z-masking, decals will not appear correctly!\n";
+    }
+
+#define REQUIRED_DESTBLENDCAPS (D3DPBLENDCAPS_ZERO|D3DPBLENDCAPS_ONE| D3DPBLENDCAPS_SRCALPHA)
+#define REQUIRED_SRCBLENDCAPS  (D3DPBLENDCAPS_ZERO|D3DPBLENDCAPS_ONE| D3DPBLENDCAPS_INVSRCALPHA)
+
+    if (((_pScrn->d3dcaps.SrcBlendCaps & REQUIRED_SRCBLENDCAPS)!=REQUIRED_SRCBLENDCAPS) ||
+        ((_pScrn->d3dcaps.DestBlendCaps & REQUIRED_DESTBLENDCAPS)!=REQUIRED_DESTBLENDCAPS)) {
+        dxgsg9_cat.error() << "device is missing alpha blending capabilities, blending may not work correctly: SrcBlendCaps: 0x"<< (void*) _pScrn->d3dcaps.SrcBlendCaps << "  DestBlendCaps: "<< (void*) _pScrn->d3dcaps.DestBlendCaps << endl;
+    }
+
+// just 'require' bilinear with mip nearest.
+#define REQUIRED_TEXFILTERCAPS (D3DPTFILTERCAPS_MAGFLINEAR | D3DPTFILTERCAPS_MIPFPOINT | D3DPTFILTERCAPS_MINFLINEAR)
+
+    if ((_pScrn->d3dcaps.TextureFilterCaps & REQUIRED_TEXFILTERCAPS)!=REQUIRED_TEXFILTERCAPS) {
+        dxgsg9_cat.error() << "device is missing texture bilinear filtering capability, textures may appear blocky!  TextureFilterCaps: 0x"<< (void*) _pScrn->d3dcaps.TextureFilterCaps << endl;
+    }
+
+#define TRILINEAR_MIPMAP_TEXFILTERCAPS (D3DPTFILTERCAPS_MIPFLINEAR | D3DPTFILTERCAPS_MINFLINEAR)
+
+    // give a warning if we dont at least have bilinear + nearest mip filtering
+    if (!(_pScrn->d3dcaps.TextureCaps & D3DPTEXTURECAPS_MIPMAP)) {
+        if(dxgsg9_cat.is_debug())
+           dxgsg9_cat.debug() << "device does not have mipmap texturing filtering capability! TextureFilterCaps: 0x"<< (void*) _pScrn->d3dcaps.TextureFilterCaps << endl;
+        dx_ignore_mipmaps = TRUE;
+    } else if ((_pScrn->d3dcaps.TextureFilterCaps & TRILINEAR_MIPMAP_TEXFILTERCAPS)!=TRILINEAR_MIPMAP_TEXFILTERCAPS) {
+        if(dxgsg9_cat.is_debug())
+           dxgsg9_cat.debug() << "device is missing tri-linear mipmap filtering capability, textures may look crappy\n";
+    } else if(_pScrn->d3dcaps.DevCaps & D3DDEVCAPS_SEPARATETEXTUREMEMORIES) {
+        // this cap is pretty much voodoo2-specific
+        // turn off trilinear filtering on voodoo2 since it doubles the reqd texture memory, degrade to mip point filtering
+        _pScrn->d3dcaps.TextureFilterCaps &= (~D3DPTFILTERCAPS_MIPFLINEAR);
+    }
+
+#define REQUIRED_TEXBLENDCAPS (D3DTEXOPCAPS_MODULATE | D3DTEXOPCAPS_SELECTARG1 | D3DTEXOPCAPS_SELECTARG2)
+    if ((_pScrn->d3dcaps.TextureOpCaps & REQUIRED_TEXBLENDCAPS)!=REQUIRED_TEXBLENDCAPS) {
+        dxgsg9_cat.error() << "device is missing some required texture blending capabilities, texture blending may not work properly! TextureOpCaps: 0x"<< (void*) _pScrn->d3dcaps.TextureOpCaps << endl;
+    }
+
+    if(_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGTABLE) {
+        // watch out for drivers that emulate per-pixel fog with per-vertex fog (Riva128, Matrox Millen G200)
+        // some of these require gouraud-shading to be set to work, as if you were using vertex fog
+        _doFogType=PerPixelFog;
+    } else {
+        // every card is going to have vertex fog, since it's implemented in d3d runtime
+        assert((_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGVERTEX )!=0);
+
+        // vtx fog may look crappy if you have large polygons in the foreground and they get clipped,
+        // so you may want to disable it
+
+        if(dx_no_vertex_fog) {
+            _doFogType = None;
+        } else {
+            _doFogType = PerVertexFog;
+
+            // range-based fog only works with vertex fog in dx7/8
+            if(dx_use_rangebased_fog && (_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGRANGE))
+                _pD3DDevice->SetRenderState(D3DRS_RANGEFOGENABLE, true);
+        }
+    }
+
+    _pScrn->bCanDirectDisableColorWrites=((_pScrn->d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE)!=0);
+
+    // Lighting, let's turn it off by default
+    _pD3DDevice->SetRenderState(D3DRS_LIGHTING, false);
+
+    // turn on dithering if the rendertarget is < 8bits/color channel
+    _dither_enabled = ((!dx_no_dithering) && IS_16BPP_DISPLAY_FORMAT(_pScrn->PresParams.BackBufferFormat)
+                        && (_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_DITHER));
+    _pD3DDevice->SetRenderState(D3DRS_DITHERENABLE, _dither_enabled);
+
+    _pD3DDevice->SetRenderState(D3DRS_CLIPPING,true);
+
+    // Stencil test is off by default
+    _stencil_test_enabled = false;
+    _pD3DDevice->SetRenderState(D3DRS_STENCILENABLE, _stencil_test_enabled);
+
+    // Antialiasing.
+    enable_line_smooth(false);
+//  enable_multisample(true);
+
+    _current_fill_mode = RenderModeAttrib::M_filled;
+    _pD3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
+
+    _pD3DDevice->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1);  // Use the diffuse vertex color.
+
+    /*
+      Panda no longer requires us to specify the maximum number of
+      lights up front, but instead we can define slot_new_light() to
+      decide one-at-a-time whether a particular light fits within our
+      limit or not.  Until we override this function, there is no
+      limit.
+
+    if(_pScrn->d3dcaps.MaxActiveLights==0) {
+        // 0 indicates no limit on # of lights, but we use DXGSG_MAX_LIGHTS anyway for now
+      init_lights(DXGSG_MAX_LIGHTS);
+    } else {
+      init_lights(min(DXGSG_MAX_LIGHTS,_pScrn->d3dcaps.MaxActiveLights));
+    }
+    */
+
+    if(dx_auto_normalize_lighting)
+         _pD3DDevice->SetRenderState(D3DRS_NORMALIZENORMALS, true);
+
+    // must do SetTSS here because redundant states are filtered out by our code based on current values above, so
+    // initial conditions must be correct
+
+    _CurTexBlendMode = TextureApplyAttrib::M_modulate;
+    SetTextureBlendMode(_CurTexBlendMode,false);
+    _texturing_enabled = false;
+    _pD3DDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DISABLE);  // disables texturing
+
+    // Init more Texture State
+    _CurTexMagFilter=_CurTexMinFilter=_CurTexMipFilter=D3DTEXF_NONE;
+    _CurTexWrapModeU=_CurTexWrapModeV=Texture::WM_clamp;
+    _CurTexAnisoDegree=1;
+
+    // this code must match apply_texture() code for states above
+    // so DX TSS renderstate matches dxgsg state
+
+    _pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
+    _pD3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
+    _pD3DDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
+    _pD3DDevice->SetSamplerState(0, D3DSAMP_MAXANISOTROPY,_CurTexAnisoDegree);
+    _pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU,get_texture_wrap_mode(_CurTexWrapModeU));
+    _pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV,get_texture_wrap_mode(_CurTexWrapModeV));
+
+#ifdef _DEBUG
+    if ((_pScrn->d3dcaps.RasterCaps & D3DPRASTERCAPS_MIPMAPLODBIAS) &&
+        (dx_global_miplevel_bias!=0.0f)) {
+        _pD3DDevice->SetSamplerState(0, D3DSAMP_MIPMAPLODBIAS, *((LPDWORD) (&dx_global_miplevel_bias)) );
+    }
+#endif
+
+#ifndef NDEBUG
+    if(dx_force_backface_culling!=0) {
+      if((dx_force_backface_culling > 0) &&
+         (dx_force_backface_culling < D3DCULL_FORCE_DWORD)) {
+             _pD3DDevice->SetRenderState(D3DRS_CULLMODE, dx_force_backface_culling);
+      } else {
+          dx_force_backface_culling=0;
+          if(dxgsg9_cat.is_debug())
+              dxgsg9_cat.debug() << "error, invalid value for dx-force-backface-culling\n";
+      }
+    }
+    _pD3DDevice->SetRenderState(D3DRS_CULLMODE, dx_force_backface_culling);
+#else
+    _pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+#endif
+
+    _alpha_func = D3DCMP_ALWAYS;
+    _alpha_func_refval = 1.0f;
+    _pD3DDevice->SetRenderState(D3DRS_ALPHAFUNC, _alpha_func);
+    _pD3DDevice->SetRenderState(D3DRS_ALPHAREF, (UINT)(_alpha_func_refval*255.0f));
+    _alpha_test_enabled = false;
+    _pD3DDevice->SetRenderState(D3DRS_ALPHATESTENABLE, _alpha_test_enabled);
+
+    // this is a new DX9 state that lets you do additional operations other than ADD (e.g. subtract/max/min)
+    // must check (_pScrn->d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_BLENDOP) (yes on GF2/Radeon8500, no on TNT)
+    _pD3DDevice->SetRenderState(D3DRS_BLENDOP,D3DBLENDOP_ADD);
+
+    PRINT_REFCNT(dxgsg9,_pD3DDevice);
+
+    // Make sure the DX state matches all of our initial attribute states.
+    CPT(RenderAttrib) dta = DepthTestAttrib::make(DepthTestAttrib::M_less);
+    CPT(RenderAttrib) dwa = DepthWriteAttrib::make(DepthWriteAttrib::M_on);
+    CPT(RenderAttrib) cfa = CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise);
+
+    dta->issue(this);
+    dwa->issue(this);
+    cfa->issue(this);
+
+    // this is all prelim hacked in testing stuff
+    init_vertex_shader(_CurVertexShader,pdx_vertexshader_filename);
+    init_pixel_shader(_CurPixelShader,pdx_pixelshader_filename);
+
+    if(pdx_globaltexture_filename!=NULL) {
+        // bypasses panda tex mechanism
+        hr = D3DXCreateTextureFromFile(_pD3DDevice,pdx_globaltexture_filename->c_str(),&_pGlobalTexture);
+        if(FAILED(hr)) {
+            dxgsg9_cat.fatal() << "CreateTexFromFile failed" << D3DERRORSTRING(hr);
+            exit(1);
+        }
+
+        hr=_pD3DDevice->SetTexture(dx_globaltexture_stagenum,_pGlobalTexture);
+        if(FAILED(hr)) {
+               dxgsg9_cat.fatal() << "SetTexture failed" << D3DERRORSTRING(hr);
+               exit(1);
+        }
+    }
+
+    PRINT_REFCNT(dxgsg9,_pD3DDevice);
+}
+
+void DXGraphicsStateGuardian9::
+init_vertex_shader(IDirect3DVertexShader9* &hShader,string *pFname) {
+
+  if((pFname==NULL) || pFname->empty()) {
+    hShader=NULL;
+    return;
+  }
+  
+  HRESULT hr;
+  
+  if((hShader!=NULL)&&(!_pScrn->bIsDX9)) {
+    // for dx9.0, need to release and recreate shaders after Reset() has been called
+    hr = _pD3DDevice->SetVertexShader(NULL);
+    if(FAILED(hr))
+      dxgsg9_cat.error() << "Delete Vertex Shader failed!" << D3DERRORSTRING(hr);
+    hShader=NULL;
+  }
+  
+  if(hShader==NULL) {
+    // doing SetShader globally for testing purps.  this really should be an object attribute
+    // like current-texture is so it gets set and unset during traversal
+    
+    hShader=read_vertex_shader(*pFname);
+    hr = _pD3DDevice->SetVertexShader(hShader);
+    
+    if(FAILED(hr))
+      dxgsg9_cat.error() << "Set Vertex Shader failed!" << D3DERRORSTRING(hr);
+  }
+}
+
+void DXGraphicsStateGuardian9::
+init_pixel_shader(IDirect3DPixelShader9* &hShader,string *pFname) {
+
+  if((pFname==NULL) || pFname->empty()) {
+    hShader=NULL;
+    return;
+  }
+  
+  HRESULT hr;
+  
+  if(!_pScrn->bCanUsePixelShaders) {
+    // for dx9.0, need to release and recreate shaders after Reset() has been called
+    dxgsg9_cat.error() << "HW doesnt support pixel shaders!\n";
+    exit(1);
+  }
+  if((hShader!=NULL)&&(!_pScrn->bIsDX9)) {
+    hr = _pD3DDevice->SetPixelShader(NULL);
+    if(FAILED(hr))
+      dxgsg9_cat.error() << "Delete Pixel Shader failed!" << D3DERRORSTRING(hr);
+    hShader=NULL;
+  }
+
+  if(hShader==NULL) {
+    // doing SetShader globally for testing purps.  this really should be an object attribute
+    // like current-texture is so it gets set and unset during traversal
+    hShader=read_pixel_shader(*pFname);
+    hr = _pD3DDevice->SetPixelShader(hShader);
+    if(FAILED(hr))
+      dxgsg9_cat.error() << "Set Pixel Shader failed!" << D3DERRORSTRING(hr);
+  }
+}
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::support_overlay_window
+//       Access: Public
+//  Description: Specifies whether dialog windows placed on top of the
+//               dx rendering window should be supported.  This
+//               requires a bit of extra overhead, so it should only
+//               be activated when necessary; however, if it is not
+//               activated, a window that pops up over the fullscreen
+//               DX window (like a dialog box, or particularly like
+//               the IME composition or candidate windows) may not be
+//               visible.
+//
+//               This is not necessary when running in windowed mode,
+//               but it does no harm.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+support_overlay_window(bool flag) {
+  // How is this supposed to be done in DX9?
+
+  /*
+  if (_overlay_windows_supported && !flag) {
+    // Disable support for overlay windows.
+    _overlay_windows_supported = false;
+
+    if (dx_full_screen) {
+      _pScrn->pddsPrimary->SetClipper(NULL);
+    }
+
+  } else if (!_overlay_windows_supported && flag) {
+    // Enable support for overlay windows.
+    _overlay_windows_supported = true;
+
+    if (dx_full_screen) {
+      // Create a Clipper object to blt the whole screen.
+      LPDIRECTDRAWCLIPPER Clipper;
+
+      if (_pScrn->pDD->CreateClipper(0, &Clipper, NULL) == DD_OK) {
+        Clipper->SetHWnd(0, _pScrn->hWnd);
+        _pScrn->pddsPrimary->SetClipper(Clipper);
+      }
+      _pScrn->pDD->FlipToGDISurface();
+      Clipper->Release();
+    }
+  }
+  */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::do_clear
+//       Access: Public, Virtual
+//  Description: Clears all of the indicated buffers to their assigned
+//               colors.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+do_clear(const RenderBuffer &buffer) {
+  //    DO_PSTATS_STUFF(PStatTimer timer(_win->_clear_pcollector));
+
+    nassertv(buffer._gsg == this);
+    int buffer_type = buffer._buffer_type;
+
+    DWORD flags = 0;
+
+    if(buffer_type & RenderBuffer::T_depth) {
+        flags |=  D3DCLEAR_ZBUFFER;
+        assert(_pScrn->PresParams.EnableAutoDepthStencil);
+    }
+
+    if(buffer_type & RenderBuffer::T_back)       //set appropriate flags
+        flags |=  D3DCLEAR_TARGET;
+
+    if(buffer_type & RenderBuffer::T_stencil) {
+        flags |=  D3DCLEAR_STENCIL;
+        assert(_pScrn->PresParams.EnableAutoDepthStencil && IS_STENCIL_FORMAT(_pScrn->PresParams.AutoDepthStencilFormat));
+    }
+
+    HRESULT hr = _pD3DDevice->Clear(0, NULL, flags, _d3dcolor_clear_value,
+                                         _depth_clear_value, (DWORD)_stencil_clear_value);
+    if(FAILED(hr)) {
+        dxgsg9_cat.error() << "clear_buffer failed:  Clear returned " << D3DERRORSTRING(hr);
+        throw_event("panda3d-render-error");
+    }
+    /* The following line will cause the background to always clear to a medium red
+    _color_clear_value[0] = .5;
+    /* The following lines will cause the background color to cycle from black to red.
+    _color_clear_value[0] += .001;
+     if (_color_clear_value[0] > 1.0f) _color_clear_value[0] = 0.0f;
+     */
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::prepare_display_region
+//       Access: Public, Virtual
+//  Description: Prepare a display region for rendering (set up
+//       scissor region and viewport)
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+prepare_display_region() {
+  if (_current_display_region == (DisplayRegion*)0L) {
+    dxgsg9_cat.error()
+      << "Invalid NULL display region in prepare_display_region()\n";
+  } else if (_current_display_region != _actual_display_region) {
+    _actual_display_region = _current_display_region;
+    
+    int l, u, w, h;
+    _actual_display_region->get_region_pixels_i(l, u, w, h);
+
+    // Create the viewport
+    D3DVIEWPORT9 vp = { l, u, w, h, 0.0f, 1.0f };
+    HRESULT hr = _pD3DDevice->SetViewport( &vp );
+    if (FAILED(hr)) {
+      dxgsg9_cat.error()
+        << "SetViewport(" << l << ", " << u << ", " << w << ", " << h
+        << ") failed" << D3DERRORSTRING(hr);
+      throw_event("panda3d-render-error");
+      nassertv(false);
+    }
+    // Note: for DX9, also change scissor clipping state here
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::prepare_lens
+//       Access: Public, Virtual
+//  Description: Makes the current lens (whichever lens was most
+//               recently specified with push_lens()) active, so that
+//               it will transform future rendered geometry.  Normally
+//               this is only called from the draw process, and
+//               usually it is called immediately after a call to
+//               push_lens().
+//
+//               The return value is true if the lens is acceptable,
+//               false if it is not.
+////////////////////////////////////////////////////////////////////
+bool DXGraphicsStateGuardian9::
+prepare_lens() {
+  if (_current_lens == (Lens *)NULL) {
+    return false;
+  }
+
+  if (!_current_lens->is_linear()) {
+    return false;
+  }
+
+  // lets get the lens perspective matrix
+  const LMatrix4f &projection_mat = _current_lens->get_projection_mat();
+  
+  // The projection matrix must always be left-handed Y-up internally,
+  // even if our coordinate system of choice is otherwise.
+  LMatrix4f new_projection_mat =
+    LMatrix4f::convert_mat(CS_yup_left, _current_lens->get_coordinate_system()) *
+    projection_mat;
+  
+  float vfov = _current_lens->get_vfov();
+  float nearf = _current_lens->get_near();
+  float farf = _current_lens->get_far();
+
+  //dxgsg9_cat.debug() << new_projection_mat << endl;
+  
+  HRESULT hr;
+  if (_current_lens->get_type().get_name() == "PerspectiveLens") {
+#if 0
+    const LMatrix4f mat_temp;
+
+    float hfov = _current_lens->get_hfov();
+    float ar = _current_lens->get_aspect_ratio();
+    float nearf = _current_lens->get_near();
+    float farf = _current_lens->get_far();
+    double vfov_radian = vfov * 0.0174532925;
+
+    dxgsg9_cat.debug() << "hfov " << hfov << " vfov " << vfov << " ar " << ar << " near " << nearf << " far " << farf << endl;
+    D3DXMatrixPerspectiveFovLH( (D3DXMATRIX*)mat_temp.get_data(), vfov_radian, ar, nearf, farf );
+
+    hr = _pD3DDevice->SetTransform(D3DTS_PROJECTION,
+                                   (D3DMATRIX*)mat_temp.get_data());
+    dxgsg9_cat.debug() << mat_temp << endl;
+#endif
+    
+    ((D3DXMATRIX*)new_projection_mat.get_data())->_33 = farf / (farf-nearf);
+    ((D3DXMATRIX*)new_projection_mat.get_data())->_43 = -nearf * farf / (farf - nearf);
+
+    hr = _pD3DDevice->SetTransform(D3DTS_PROJECTION,
+                                   (D3DMATRIX*)new_projection_mat.get_data());
+    //dxgsg9_cat.debug() << new_projection_mat << endl;
+    //dxgsg9_cat.debug() << "using perspective projection" << endl;
+  }
+  else {
+    ((D3DXMATRIX*)new_projection_mat.get_data())->_33 = 1/(farf-nearf);
+    ((D3DXMATRIX*)new_projection_mat.get_data())->_43 = -nearf/(farf-nearf);
+    
+    hr = _pD3DDevice->SetTransform(D3DTS_PROJECTION,
+                                   (D3DMATRIX*)new_projection_mat.get_data());
+    //dxgsg9_cat.debug() << new_projection_mat << endl;
+    //dxgsg9_cat.debug() << "using ortho projection" << endl;
+  }
+  return SUCCEEDED(hr);
+}
+
+#ifndef NO_MULTIPLE_DISPLAY_REGIONS
+////////////////////////////////////////////////////////////////////
+//     Function: set_clipper
+//       Access:
+//  Description: Useless in DX at the present time
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::set_clipper(RECT cliprect) {
+
+    LPDIRECTDRAWCLIPPER Clipper;
+    HRESULT result;
+
+    // For windowed mode, the clip region is associated with the window,
+    // and DirectX does not allow you to create clip regions.
+    if (dx_full_screen) return;
+
+    /* The cliprect we receive is normalized so that (0,0) means the upper left of
+       the client portion of the window.
+        At least, I think that's true, and the following code assumes that.
+        So we must adjust the clip region by offsetting it to the origin of the
+        view rectangle.
+    */
+    clip_rect = cliprect;       // store the normalized clip rect
+    cliprect.left += _view_rect.left;
+    cliprect.right += _view_rect.left;
+    cliprect.top += _view_rect.top;
+    cliprect.bottom += _view_rect.top;
+    RGNDATA *rgn_data = (RGNDATA *)malloc(sizeof(RGNDATAHEADER) + sizeof(RECT));
+    HRGN hrgn = CreateRectRgn(cliprect.left, cliprect.top, cliprect.right, cliprect.bottom);
+    GetRegionData(hrgn, sizeof(RGNDATAHEADER) + sizeof(RECT), rgn_data);
+
+    if (_pD3DDevicesPrimary->GetClipper(&Clipper) != DD_OK) {
+        result = _pD3DDevice->CreateClipper(0, &Clipper, NULL);
+        result = Clipper->SetClipList(rgn_data, 0);
+        result = _pD3DDevicesPrimary->SetClipper(Clipper);
+    } else {
+        result = Clipper->SetClipList(rgn_data, 0 );
+        if (result == DDERR_CLIPPERISUSINGHWND) {
+            result = _pD3DDevicesPrimary->SetClipper(NULL);
+            result = _pD3DDevice->CreateClipper(0, &Clipper, NULL);
+            result = Clipper->SetClipList(rgn_data, 0 ) ;
+            result = _pD3DDevicesPrimary->SetClipper(Clipper);
+        }
+    }
+    free(rgn_data);
+    DeleteObject(hrgn);
+}
+#endif
+
+#if defined(_DEBUG) || defined(COUNT_DRAWPRIMS)
+typedef enum {DrawPrim,DrawIndexedPrim} DP_Type;
+static const char *DP_Type_Strs[3] = {"DrawPrimitive","DrawIndexedPrimitive"};
+
+void INLINE TestDrawPrimFailure(DP_Type dptype,HRESULT hr,IDirect3DDevice9 *pD3DDevice,DWORD nVerts,DWORD nTris) {
+        if(FAILED(hr)) {
+            // loss of exclusive mode is not a real DrawPrim problem, ignore it
+            HRESULT testcooplvl_hr = pD3DDevice->TestCooperativeLevel();
+            if((testcooplvl_hr != D3DERR_DEVICELOST)||(testcooplvl_hr != D3DERR_DEVICENOTRESET)) {
+                dxgsg9_cat.fatal() << DP_Type_Strs[dptype] << "() failed: result = " << D3DERRORSTRING(hr);
+                exit(1);
+            }
+        }
+
+        CountDPs(nVerts,nTris);
+}
+#else
+#define TestDrawPrimFailure(a,b,c,nVerts,nTris) CountDPs(nVerts,nTris);
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::report_texmgr_stats
+//       Access: Protected
+//  Description: Reports the DX texture manager's activity to PStats.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+report_texmgr_stats() {
+
+#if defined(DO_PSTATS)||defined(PRINT_RESOURCESTATS)
+  HRESULT hr;
+
+#ifdef TEXMGRSTATS_USES_GETAVAILVIDMEM
+  DWORD dwTexTotal,dwTexFree,dwVidTotal,dwVidFree;
+
+#ifndef PRINT_RESOURCESTATS
+  if (_total_texmem_pcollector.is_active())
+#endif
+  {
+      DDSCAPS2 ddsCaps;
+
+      ZeroMemory(&ddsCaps,sizeof(ddsCaps));
+
+      ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;
+      if(FAILED(  hr = _pD3DDevice->GetAvailableVidMem(&ddsCaps,&dwVidTotal,&dwVidFree))) {
+            dxgsg9_cat.fatal() << "report_texmgr GetAvailableVidMem for VIDMEM failed : result = " << D3DERRORSTRING(hr);
+            exit(1);
+      }
+
+      ddsCaps.dwCaps = DDSCAPS_TEXTURE;
+      if(FAILED(  hr = _pD3DDevice->GetAvailableVidMem(&ddsCaps,&dwTexTotal,&dwTexFree))) {
+            dxgsg9_cat.fatal() << "report_texmgr GetAvailableVidMem for TEXTURE failed : result = " << D3DERRORSTRING(hr);
+            exit(1);
+      }
+  }
+#endif
+
+  IDirect3DQuery9 *pQuery = NULL;
+  D3DDEVINFO_RESOURCEMANAGER all_resource_stats;
+  ZeroMemory(&all_resource_stats,sizeof(D3DDEVINFO_RESOURCEMANAGER));
+
+  if(!bTexStatsRetrievalImpossible) {
+    hr = _pD3DDevice->CreateQuery(D3DQUERYTYPE_RESOURCEMANAGER, &pQuery);
+    hr = pQuery->Issue(D3DISSUE_END);
+    hr = pQuery->GetData((void*)&all_resource_stats,sizeof(D3DDEVINFO_RESOURCEMANAGER), 0);
+    if (hr!=D3D_OK) {
+      if (hr==S_FALSE) {
+        static int PrintedMsg=2;
+        if(PrintedMsg>0) {
+          if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "Error: texstats GetInfo() requires debug DX DLLs to be installed!!  ***********\n";
+          ZeroMemory(&all_resource_stats,sizeof(D3DDEVINFO_RESOURCEMANAGER));
+          bTexStatsRetrievalImpossible=true;
+        }
+      } else {
+        dxgsg9_cat.error() << "GetInfo(RESOURCEMANAGER) failed to get tex stats: result = " << D3DERRORSTRING(hr);
+        return;
+      }
+    }
+  }
+
+#ifdef PRINT_RESOURCESTATS
+#ifdef TEXMGRSTATS_USES_GETAVAILVIDMEM
+    char tmpstr1[50],tmpstr2[50],tmpstr3[50],tmpstr4[50];
+    sprintf(tmpstr1,"%.4g",dwVidTotal/1000000.0);
+    sprintf(tmpstr2,"%.4g",dwVidFree/1000000.0);
+    sprintf(tmpstr3,"%.4g",dwTexTotal/1000000.0);
+    sprintf(tmpstr4,"%.4g",dwTexFree/1000000.0);
+    dxgsg9_cat.debug() << "\nAvailableVidMem for RenderSurfs: (megs) total: " << tmpstr1 << "  free: " << tmpstr2
+                      << "\nAvailableVidMem for Textures:    (megs) total: " << tmpstr3 << "  free: " << tmpstr4 << endl;
+#endif
+
+   #define REAL_D3DRTYPECOUNT ((UINT) D3DRTYPE_INDEXBUFFER)     // d3d boneheads defined D3DRTYPECOUNT wrong
+   static char *ResourceNameStrs[REAL_D3DRTYPECOUNT]={"SURFACE","VOLUME","TEXTURE","VOLUME TEXTURE","CUBE TEXTURE","VERTEX BUFFER","INDEX BUFFER"};
+   static bool bDoGetInfo[REAL_D3DRTYPECOUNT]={true,false,true,false,false,true,false};  // not using volume or cube textures yet
+
+   if(!bTexStatsRetrievalImpossible) {
+        for(UINT r=0; r<(UINT)REAL_D3DRTYPECOUNT;r++) {
+            if(!bDoGetInfo[r])
+               continue;
+
+            D3DRESOURCESTATS *pRStats=&all_resource_stats.stats[r];
+            if(pRStats->NumUsed>0) {
+                char hitrate_str[20];
+                float fHitRate = (pRStats->NumUsedInVidMem * 100.0f) / pRStats->NumUsed;
+                sprintf(hitrate_str,"%.1f",fHitRate);
+
+                dxgsg9_cat.spam()
+                    << "\n***** Stats for " << ResourceNameStrs[r] << " ********"
+                    << "\n HitRate:\t" << hitrate_str << "%"
+                    << "\n bThrashing:\t" << pRStats->bThrashing
+                    << "\n NumEvicts:\t" << pRStats->NumEvicts
+                    << "\n NumVidCreates:\t" << pRStats->NumVidCreates
+                    << "\n NumUsed:\t" << pRStats->NumUsed
+                    << "\n NumUsedInVidMem:\t" << pRStats->NumUsedInVidMem
+                    << "\n WorkingSet:\t" << pRStats->WorkingSet
+                    << "\n WorkingSetBytes:\t" << pRStats->WorkingSetBytes
+                    << "\n ApproxBytesDownloaded:\t" << pRStats->ApproxBytesDownloaded
+                    << "\n TotalManaged:\t" << pRStats->TotalManaged
+                    << "\n TotalBytes:\t" << pRStats->TotalBytes
+                    << "\n LastPri:\t" << pRStats->LastPri << endl;
+            } else {
+                dxgsg9_cat.spam()
+                    << "\n***** Stats for " << ResourceNameStrs[r] << " ********"
+                    << "\n NumUsed: 0\n";
+            }
+        }
+
+        D3DDEVINFO_D3DVERTEXSTATS vtxstats;
+        ZeroMemory(&vtxstats,sizeof(D3DDEVINFO_D3DVERTEXSTATS));
+        hr = _pD3DDevice->GetInfo(D3DDEVINFOID_VERTEXSTATS,&vtxstats,sizeof(D3DDEVINFO_D3DVERTEXSTATS));
+        if (hr!=D3D_OK) {
+            dxgsg9_cat.error() << "GetInfo(D3DVERTEXSTATS) failed : result = " << D3DERRORSTRING(hr);
+            return;
+        } else {
+            dxgsg9_cat.spam()
+            << "\n***** Triangle Stats ********"
+            << "\n NumRenderedTriangles:\t" << vtxstats.NumRenderedTriangles
+            << "\n NumExtraClippingTriangles:\t" << vtxstats.NumExtraClippingTriangles << endl;
+        }
+    }
+#endif
+
+#ifdef DO_PSTATS
+  // Tell PStats about the state of the texture memory.
+
+  if (_texmgrmem_total_pcollector.is_active()) {
+      // report zero if no debug dlls, to signal this info is invalid
+      _texmgrmem_total_pcollector.set_level(all_resource_stats.stats[D3DRTYPE_TEXTURE].TotalBytes);
+      _texmgrmem_resident_pcollector.set_level(all_resource_stats.stats[D3DRTYPE_TEXTURE].WorkingSetBytes);
+  }
+#ifdef TEXMGRSTATS_USES_GETAVAILVIDMEM
+  if (_total_texmem_pcollector.is_active()) {
+    _total_texmem_pcollector.set_level(dwTexTotal);
+    _used_texmem_pcollector.set_level(dwTexTotal - dwTexFree);
+  }
+#endif
+#endif
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::add_to_FVFBuf
+//       Access: Private
+//  Description: This adds data to the flexible vertex format
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+add_to_FVFBuf(void *data,  size_t bytes) {
+    memcpy(_pCurFvfBufPtr, data, bytes);
+    _pCurFvfBufPtr += bytes;
+}
+
+// generates slightly fewer instrs
+#define add_DWORD_to_FVFBuf(data) { *((DWORD *)_pCurFvfBufPtr) = (DWORD) data;  _pCurFvfBufPtr += sizeof(DWORD);}
+
+typedef enum {
+    FlatVerts,IndexedVerts,MixedFmtVerts
+} GeomVertFormat;
+
+#define COPYVERTDATA_2_VERTEXBUFFER(PrimType,NumVertices) {                                     \
+            DWORD numVertBytes=_pCurFvfBufPtr-_pFvfBufBasePtr;                                  \
+            memcpy(_pCurrentGeomContext->_pEndofVertData,_pFvfBufBasePtr,numVertBytes);         \
+            DPInfo dpInfo;                                                                      \
+            dpInfo.nVerts=NumVertices;                                                          \
+            dpInfo.primtype=PrimType;                                                           \
+            _pCurrentGeomContext->_PrimInfo.push_back(dpInfo);                                  \
+            _pCurrentGeomContext->_num_verts+=dpInfo.nVerts;                                    \
+            _pCurrentGeomContext->_pEndofVertData+=numVertBytes; }
+
+
+INLINE void DXGraphicsStateGuardian9::
+transform_color(Colorf &InColor,D3DCOLOR &OutRGBAColor) {
+  Colorf transformed
+    ((InColor[0] * _current_color_scale[0]) + _current_color_offset[0],
+     (InColor[1] * _current_color_scale[1]) + _current_color_offset[1],
+     (InColor[2] * _current_color_scale[2]) + _current_color_offset[2],
+     (InColor[3] * _current_color_scale[3]) + _current_color_offset[3]);
+  OutRGBAColor = Colorf_to_D3DCOLOR(transformed);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_prim_setup
+//       Access: Private
+//  Description: This adds data to the flexible vertex format
+////////////////////////////////////////////////////////////////////
+size_t DXGraphicsStateGuardian9::
+draw_prim_setup(const Geom *geom) {
+    //  Set the flags for the flexible vertex format and compute the bytes
+    //  required to store a single vertex.
+    // Assumes _perVertex,_perPrim,_perComp flags are setup prior to entry
+    // (especially for shademode).  maybe should change this, since we usually
+    // get attr info anyway)
+
+    #ifdef _DEBUG
+      assert(geom->get_binding(G_COORD) != G_OFF);
+    #endif
+
+#define GET_NEXT_VERTEX(NEXTVERT) { NEXTVERT = geom->get_next_vertex(vi); }
+#define GET_NEXT_NORMAL() { p_normal = geom->get_next_normal(ni); }
+#define GET_NEXT_TEXCOORD() { p_texcoord = geom->get_next_texcoord(ti); }
+
+#define GET_NEXT_COLOR() {                                                           \
+    Colorf tempcolor = geom->get_next_color(ci);                                     \
+    if(_color_transform_enabled == 0) {                                              \
+        _curD3Dcolor = Colorf_to_D3DCOLOR(tempcolor);                                \
+    } else {                                                                         \
+        transform_color(tempcolor,_curD3Dcolor);                                     \
+    }}
+
+////////
+
+   // this stuff should eventually replace the iterators below
+   PTA_Vertexf coords;
+   PTA_ushort vindexes;
+
+   geom->get_coords(coords,vindexes);
+   if(vindexes!=NULL) {
+      _pCurCoordIndex = _coordindex_array = &vindexes[0];
+   } else {
+      _pCurCoordIndex = _coordindex_array = NULL;
+   }
+   _pCurCoord = _coord_array = &coords[0];
+
+   ///////////////
+
+   vi = geom->make_vertex_iterator();
+   DWORD newFVFflags = D3DFVF_XYZ;
+   size_t vertex_size = sizeof(float) * 3;
+
+   GeomBindType ColorBinding=geom->get_binding(G_COLOR);
+   bool bDoColor=(ColorBinding != G_OFF);
+
+   if (bDoColor || _has_scene_graph_color) {
+        ci = geom->make_color_iterator();
+        newFVFflags |= D3DFVF_DIFFUSE;
+        vertex_size += sizeof(D3DCOLOR);
+
+        if (_has_scene_graph_color) {
+            if (_scene_graph_color_stale) {
+              // Compute the D3DCOLOR for the scene graph override color.
+              if(_color_transform_enabled == 0) {
+                _scene_graph_color_D3DCOLOR = Colorf_to_D3DCOLOR(_scene_graph_color);
+              } else {
+                transform_color(_scene_graph_color, _scene_graph_color_D3DCOLOR);
+              }
+              _scene_graph_color_stale = false;
+            }
+            _curD3Dcolor = _scene_graph_color_D3DCOLOR;  // set primitive color if there is one.
+
+            _perVertex &= ~PER_COLOR;
+            _perPrim &= ~PER_COLOR;
+            _perComp &= ~PER_COLOR;
+         } else if(ColorBinding == G_OVERALL){
+            GET_NEXT_COLOR();
+            _perVertex &= ~PER_COLOR;
+            _perPrim &= ~PER_COLOR;
+            _perComp &= ~PER_COLOR;
+        }
+   }
+   if (geom->get_binding(G_NORMAL) != G_OFF) {
+        ni = geom->make_normal_iterator();
+        newFVFflags |= D3DFVF_NORMAL;
+        vertex_size += sizeof(float) * 3;
+
+        if (geom->get_binding(G_NORMAL) == G_OVERALL)
+            p_normal = geom->get_next_normal(ni);    // set overall normal if there is one
+   }
+
+
+   GeomBindType TexCoordBinding;
+   PTA_TexCoordf texcoords;
+   PTA_ushort tindexes;
+   geom->get_texcoords(texcoords,TexCoordBinding,tindexes);
+   if (TexCoordBinding != G_OFF) {
+       assert(TexCoordBinding == G_PER_VERTEX);
+
+       // used by faster path
+       if(tindexes!=NULL) {
+          _pCurTexCoordIndex = _texcoordindex_array = &tindexes[0];
+       } else {
+          _pCurTexCoordIndex = _texcoordindex_array = NULL;
+       }
+       _pCurTexCoord = _texcoord_array = &texcoords[0];
+       //////
+
+       ti = geom->make_texcoord_iterator();
+        newFVFflags |= (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0));
+       vertex_size += sizeof(float) * 2;
+   }
+
+    // If we have per-vertex colors or normals, we need smooth shading.
+    // Otherwise we want flat shading for performance reasons.
+
+   // Note on fogging:
+   // the fogging expression should really be || (_fog_enabled && (_doFogType==PerVertexFog))
+   // instead of just || (_fog_enabled), since GOURAUD shading should not be required for PerPixel
+   // fog, but the problem is some cards (Riva128,Matrox G200) emulate pixel fog with table fog
+   // but dont force the shading mode to gouraud internally, so you end up with flat-shaded fog colors
+   // (note, TNT does the right thing tho).  So I guess we must do gouraud shading for all fog rendering for now
+   // note that if _doFogType==None, _fog_enabled will always be false
+
+   bool need_gouraud_shading = ((_perVertex & (PER_COLOR | (wants_normals() ? PER_NORMAL : 0))) || _fog_enabled);
+
+   enable_gouraud_shading(need_gouraud_shading);
+   set_vertex_format(newFVFflags);
+
+   return vertex_size;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_prim_inner_loop
+//       Access: Private
+//  Description: This adds data to the flexible vertex format with a check
+//               for component normals and color
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_prim_inner_loop(int nVerts, const Geom *geom, ushort perFlags) {
+    Vertexf NextVert;
+
+    for(;nVerts > 0;nVerts--) {
+         // coord info will always be _perVertex
+        GET_NEXT_VERTEX(NextVert);     // need to optimize these
+        add_to_FVFBuf((void *)&NextVert, 3*sizeof(float));
+
+        if(perFlags==(ushort)TexCoordOnly) {
+            // break out the common case (for animated chars) 1st
+            GET_NEXT_TEXCOORD();
+        } else {
+            switch (DrawLoopFlags(perFlags)) {
+                case Color_TexCoord:
+                    GET_NEXT_TEXCOORD();
+                case ColorOnly:
+                    GET_NEXT_COLOR();
+                    break;
+                case Normal_Color:
+                    GET_NEXT_COLOR();
+                case NormalOnly:
+                    GET_NEXT_NORMAL();
+                    break;
+                case Normal_Color_TexCoord:
+                    GET_NEXT_COLOR();
+                case Normal_TexCoord:
+                    GET_NEXT_NORMAL();
+                // case TexCoordOnly:
+                    GET_NEXT_TEXCOORD();
+                    break;
+            }
+        }
+
+        if (_CurFVFType & D3DFVF_NORMAL)
+            add_to_FVFBuf((void *)&p_normal, 3*sizeof(float));
+        if (_CurFVFType & D3DFVF_DIFFUSE)
+            add_DWORD_to_FVFBuf(_curD3Dcolor);
+        if (_CurFVFType & D3DFVF_TEXCOUNT_MASK)
+            add_to_FVFBuf((void *)&p_texcoord, sizeof(TexCoordf));
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_prim_inner_loop_coordtexonly
+//       Access: Private
+//  Description: FastPath loop used by animated character data
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_prim_inner_loop_coordtexonly(int nVerts, const Geom *geom) {
+    // assumes coord and texcoord data is per-vertex,
+    // color is not per-vert/component (which would require fetching new vals in the vertex loop),
+    // and no normal data. this should be common situation for animated character data
+    // inc'ing local ptrs instead of member ones, seems to optimize better
+    // bypass all the slow vertex iterator stuff
+
+    #ifdef _DEBUG
+     {
+      assert(geom->get_binding(G_NORMAL) == G_OFF);
+      GeomBindType ColorBinding = geom->get_binding(G_COLOR);
+      assert((ColorBinding != G_PER_VERTEX) || (ColorBinding != G_PER_COMPONENT));
+      assert(geom->get_binding(G_TEXCOORD) == G_PER_VERTEX);
+     }
+    #endif
+
+    Vertexf *pCurCoord = _pCurCoord;
+    ushort *pCurCoordIndex = _pCurCoordIndex;
+    TexCoordf *pCurTexCoord = _pCurTexCoord;
+    ushort *pCurTexCoordIndex = _pCurTexCoordIndex;
+
+    BYTE *pLocalFvfBufPtr = _pCurFvfBufPtr;
+    DWORD cur_color = _curD3Dcolor;
+    bool bDoIndexedTexCoords = (_texcoordindex_array != NULL);
+    bool bDoIndexedCoords = (_coordindex_array != NULL);
+
+    for(;nVerts>0;nVerts--) {
+        if(bDoIndexedCoords) {
+           memcpy(pLocalFvfBufPtr,(void*)&_coord_array[*pCurCoordIndex],3*sizeof(float));
+           pCurCoordIndex++;
+        } else {
+           memcpy(pLocalFvfBufPtr,(void*)pCurCoord,3*sizeof(float));
+           pCurCoord++;
+        }
+
+        pLocalFvfBufPtr+=3*sizeof(float);
+
+        *((DWORD *)pLocalFvfBufPtr) = cur_color;
+        pLocalFvfBufPtr += sizeof(DWORD);
+
+        if(bDoIndexedTexCoords) {
+           memcpy(pLocalFvfBufPtr,(void*)&_texcoord_array[*pCurTexCoordIndex],sizeof(TexCoordf));
+           pCurTexCoordIndex++;
+        } else {
+           memcpy(pLocalFvfBufPtr,(void*)pCurTexCoord,sizeof(TexCoordf));
+           pCurTexCoord++;
+        }
+        pLocalFvfBufPtr+=sizeof(TexCoordf);
+    }
+
+    _pCurFvfBufPtr=pLocalFvfBufPtr;
+    _pCurCoord = pCurCoord;
+    _pCurCoordIndex = pCurCoordIndex;
+    _pCurTexCoord = pCurTexCoord;
+    _pCurTexCoordIndex = pCurTexCoordIndex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_point
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_point(GeomPoint *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_point()" << endl;
+#endif
+
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+    // The DX Way
+
+    int nPrims = geom->get_num_prims();
+
+    if (nPrims==0) {
+        dxgsg9_cat.warning() << "draw_point() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+#ifdef _DEBUG
+    static bool bPrintedMsg=false;
+
+    if (!bPrintedMsg && (geom->get_size()!=1.0f)) {
+        bPrintedMsg=true;
+        dxgsg9_cat.warning() << "D3D does not support drawing points of non-unit size, setting point size to 1.0f!\n";
+    }
+#endif
+
+    nassertv(nPrims < PANDA_MAXNUMVERTS );
+
+    PTA_Vertexf coords;
+    PTA_Normalf norms;
+    PTA_Colorf colors;
+    PTA_TexCoordf texcoords;
+    GeomBindType bind;
+    PTA_ushort vindexes,nindexes,tindexes,cindexes;
+
+    geom->get_coords(coords,vindexes);
+    geom->get_normals(norms,bind,nindexes);
+    geom->get_colors(colors,bind,cindexes);
+    geom->get_texcoords(texcoords,bind,tindexes);
+
+    // for Indexed Prims and mixed indexed/non-indexed prims, we will use old pipeline for now
+    // need to add code to handle fully indexed mode (and handle cases with index arrays of different lengths,
+    // values (may only be possible to handle certain cases without reverting to old pipeline)
+
+        _perVertex = 0x0;
+        _perPrim = 0;
+        if (geom->get_binding(G_NORMAL) == G_PER_VERTEX) _perVertex |= PER_NORMAL;
+        if (geom->get_binding(G_COLOR) == G_PER_VERTEX) _perVertex |= PER_COLOR;
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+        nassertv(nPrims * vertex_size < VERT_BUFFER_SIZE);
+        _pCurFvfBufPtr = _pFvfBufBasePtr;          // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+        // iterate through the point
+        draw_prim_inner_loop(nPrims, geom, _perVertex | _perPrim);
+
+        if(!_bDrawPrimDoSetupVertexBuffer) {
+           HRESULT hr = _pD3DDevice->DrawPrimitiveUP(D3DPT_POINTLIST, nPrims, _pFvfBufBasePtr, vertex_size);
+           TestDrawPrimFailure(DrawPrim,hr,_pD3DDevice,nPrims,0);
+        } else {
+            COPYVERTDATA_2_VERTEXBUFFER(D3DPT_POINTLIST,nPrims);
+        }
+
+
+    _pCurFvfBufPtr = NULL;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_line
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_line(GeomLine* geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_line()" << endl;
+#endif
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+#ifdef _DEBUG
+    static bool bPrintedMsg=false;
+
+    // note: need to implement approximation of non-1.0 width lines with quads
+
+    if (!bPrintedMsg && (geom->get_width()!=1.0f)) {
+        bPrintedMsg=true;
+        if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "DX does not support drawing lines with a non-1.0f pixel width, setting width to 1.0f!\n";
+    }
+#endif
+
+    int nPrims = geom->get_num_prims();
+
+    if (nPrims==0) {
+        if(dxgsg9_cat.is_debug())
+           dxgsg9_cat.debug() << "draw_line() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+    _perVertex = 0x0;
+    _perPrim = 0x0;
+    _perComp = 0x0;
+
+    switch(geom->get_binding(G_COLOR)) {
+        case G_PER_VERTEX:
+            _perVertex |=  PER_COLOR;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |= PER_COLOR;
+            break;
+        default:
+            _perPrim |= PER_COLOR;
+    }
+
+    switch(geom->get_binding(G_NORMAL)) {
+        case G_PER_VERTEX:
+            _perVertex |=  PER_NORMAL;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |=  PER_NORMAL;
+            break;
+        default:
+            _perPrim |=  PER_NORMAL;
+    }
+
+    size_t vertex_size = draw_prim_setup(geom);
+
+    BYTE *_tmp_fvfOverrunBuf = NULL;
+    nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+//  nassertv(nPrims * 2 * vertex_size < VERT_BUFFER_SIZE);
+
+    if (nPrims * 2 * vertex_size > VERT_BUFFER_SIZE) {
+        // bugbug: need cleaner way to handle tmp buffer size overruns (malloc/realloc?)
+        _pCurFvfBufPtr = _tmp_fvfOverrunBuf = new BYTE[nPrims * 2 * vertex_size];
+    } else  _pCurFvfBufPtr = _pFvfBufBasePtr;            // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+    for (int i = 0; i < nPrims; i++) {
+        if (_perPrim & PER_COLOR) {
+            GET_NEXT_COLOR();
+        }
+        if (_perPrim & PER_NORMAL)
+            p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+        draw_prim_inner_loop(2, geom, _perVertex);
+    }
+
+    HRESULT hr;
+
+    DWORD nVerts = nPrims<<1;
+
+    if(!_bDrawPrimDoSetupVertexBuffer) {
+        if (_tmp_fvfOverrunBuf == NULL) {
+            nassertv((nVerts*vertex_size) == (_pCurFvfBufPtr-_pFvfBufBasePtr));
+            hr = _pD3DDevice->DrawPrimitiveUP(D3DPT_LINELIST, nPrims, _pFvfBufBasePtr, vertex_size);
+        } else {
+            nassertv((nVerts*vertex_size) == (_pCurFvfBufPtr-_tmp_fvfOverrunBuf));
+            hr = _pD3DDevice->DrawPrimitiveUP(D3DPT_LINELIST, nPrims, _tmp_fvfOverrunBuf, vertex_size);
+            delete [] _tmp_fvfOverrunBuf;
+        }
+        TestDrawPrimFailure(DrawPrim,hr,_pD3DDevice,nVerts,0);
+    } else {
+        COPYVERTDATA_2_VERTEXBUFFER(D3DPT_LINELIST,nVerts);
+    }
+
+    _pCurFvfBufPtr = NULL;
+}
+
+void DXGraphicsStateGuardian9::
+draw_linestrip(GeomLinestrip* geom, GeomContext *gc) {
+
+#ifdef _DEBUG
+    static BOOL bPrintedMsg=false;
+
+    if (!bPrintedMsg && (geom->get_width()!=1.0f)) {
+        bPrintedMsg=true;
+        dxgsg9_cat.warning() << "DX does not support drawing lines with a non-1.0f pixel width, setting width to 1.0f!\n";
+    }
+#endif
+
+  draw_linestrip_base(geom,gc,false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_linestrip
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_linestrip_base(Geom* geom, GeomContext *gc, bool bConnectEnds) {
+// Note draw_linestrip_base() may be called from non-line draw_fns to support wireframe mode
+
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_linestrip()" << endl;
+#endif
+
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+    int nPrims = geom->get_num_prims();
+    const int *pLengthArr = geom->get_lengths();
+
+    if(nPrims==0) {
+        if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "draw_linestrip() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+    _perVertex = 0x0;
+    _perPrim = 0x0;
+    _perComp = 0x0;
+
+    switch(geom->get_binding(G_COLOR)) {
+        case G_PER_VERTEX:
+            _perVertex |= PER_COLOR;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |= PER_COLOR;
+            break;
+        default:
+            _perPrim |= PER_COLOR;
+    }
+
+    switch(geom->get_binding(G_NORMAL)) {
+        case G_PER_VERTEX:
+            _perVertex |=  PER_NORMAL;
+            break;
+        case G_PER_COMPONENT:
+            _perComp |=  PER_NORMAL;
+            break;
+        default:
+            _perPrim |= PER_NORMAL;
+    }
+
+    size_t vertex_size = draw_prim_setup(geom);
+    ushort perFlags = _perVertex | _perComp;
+
+    bool bPerPrimColor = ((_perPrim & PER_COLOR)!=0);
+    bool bPerPrimNormal = ((_perPrim & PER_NORMAL)!=0);
+
+    DWORD nVerts;
+
+    if(pLengthArr==NULL) // we've been called by draw_quad, which has no lengths array
+      nVerts=4;
+
+    for (int i = 0; i < nPrims; i++) {
+        if (bPerPrimColor) {
+            GET_NEXT_COLOR();
+        }
+
+        if (bPerPrimNormal) {
+            p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+        }
+
+        if(pLengthArr!=NULL) {
+            nVerts= *(pLengthArr++);
+            nassertv(nVerts >= 2);
+        }
+
+        nassertv(_pCurFvfBufPtr == NULL);   // make sure the storage pointer is clean.
+        nassertv(nVerts * vertex_size < VERT_BUFFER_SIZE);
+        _pCurFvfBufPtr = _pFvfBufBasePtr;   // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+        draw_prim_inner_loop(nVerts, geom, perFlags);
+
+        if(bConnectEnds) {
+             // append first vertex to end
+             memcpy(_pCurFvfBufPtr,_pFvfBufBasePtr,vertex_size);
+             _pCurFvfBufPtr+=vertex_size;
+             nVerts++;
+        }
+
+        nassertv((nVerts*vertex_size) == (_pCurFvfBufPtr-_pFvfBufBasePtr));
+
+        if(!_bDrawPrimDoSetupVertexBuffer) {
+            HRESULT hr = _pD3DDevice->DrawPrimitiveUP(D3DPT_LINESTRIP, nVerts-1, _pFvfBufBasePtr, vertex_size);
+            TestDrawPrimFailure(DrawPrim,hr,_pD3DDevice,nVerts,0);
+        } else {
+            COPYVERTDATA_2_VERTEXBUFFER(D3DPT_LINESTRIP,nVerts);
+        }
+
+        _pCurFvfBufPtr = NULL;
+    }
+}
+
+// this class exists because an alpha sort is necessary for correct
+// sprite rendering, and we can't simply sort the vertex arrays as
+// each vertex may or may not have corresponding information in the
+// x/y texel-world-ratio and rotation arrays.
+typedef struct {
+    Vertexf _v;
+    D3DCOLOR _c;
+    float _x_ratio;
+    float _y_ratio;
+    float _theta;
+} WrappedSprite;
+
+class WrappedSpriteSortPtr {
+public:
+    float z;
+    WrappedSprite *pSpr;
+};
+
+// this struct exists because the STL can sort faster than i can.
+struct draw_sprite_vertex_less {
+    INLINE bool operator ()(const WrappedSpriteSortPtr& v0,
+                            const WrappedSpriteSortPtr& v1) const {
+        return v0.z > v1.z; // reversed from gl due to left-handed coordsys of d3d
+    }
+};
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_sprite
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_sprite(GeomSprite *geom, GeomContext *gc) {
+
+    // this is a little bit of a mess, but it's ok.  Here's the deal:
+    // we want to draw, and draw quickly, an arbitrarily large number
+    // of sprites all facing the screen.  Performing the billboard math
+    // for ~1000 sprites is way too slow.  Ideally, we want one
+    // matrix transformation that will handle everything, and this is
+    // just about what ends up happening. We're getting the front-facing
+    // effect by setting up a new frustum (of the same z-depth as the
+    // current one) that is very small in x and y.  This way regularly
+    // rendered triangles that might not be EXACTLY facing the camera
+    // will certainly look close enough.  Then, we transform to camera-space
+    // by hand and apply the inverse frustum to the transformed point.
+    // For some cracked out reason, this actually works.
+
+
+    // Note: for DX9, try to use the PointSprite primitive instead of doing all the stuff below
+
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_sprite()" << endl;
+#endif
+    // get the array traversal set up.
+    int nPrims = geom->get_num_prims();
+
+    if (nPrims==0) {
+        return;
+    }
+
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(nPrims));
+
+    D3DMATRIX OldD3DWorldMatrix;
+    _pD3DDevice->GetTransform(D3DTS_WORLD, &OldD3DWorldMatrix);
+
+    bool bReEnableDither=false;
+
+    _pD3DDevice->GetTransform(D3DTS_WORLD, &OldD3DWorldMatrix);
+
+    Geom::VertexIterator vi = geom->make_vertex_iterator();
+    Geom::ColorIterator ci = geom->make_color_iterator();
+
+    // note although sprite particles technically dont require a texture,
+    // the texture dimensions are used to initialize the size calculations
+    // the code in spriteParticleRenderer.cxx does not handle the no-texture case now
+
+    float tex_xsize = 1.0f;
+    float tex_ysize = 1.0f;
+
+    Texture *tex = geom->get_texture();
+    if(tex !=NULL) {
+      // set up the texture-rendering state
+      modify_state(RenderState::make
+                   (TextureAttrib::make(tex),
+                    TextureApplyAttrib::make(TextureApplyAttrib::M_modulate)));
+      tex_xsize = tex->_pbuffer->get_xsize();
+      tex_ysize = tex->_pbuffer->get_ysize();
+    }
+
+    // save the modelview matrix
+    const LMatrix4f &modelview_mat = _transform->get_mat();
+
+    // We don't need to mess with the aspect ratio, since we are now
+    // using the default projection matrix, which has the right aspect
+    // ratio built in.
+
+    // null the world xform, so sprites are orthog to scrn
+    _pD3DDevice->SetTransform(D3DTS_WORLD, &matIdentity);
+    // only need to change _WORLD xform, _VIEW xform is Identity
+
+    // precomputation stuff
+    float tex_left = geom->get_ll_uv()[0];
+    float tex_right = geom->get_ur_uv()[0];
+    float tex_bottom = geom->get_ll_uv()[1];
+    float tex_top = geom->get_ur_uv()[1];
+
+    float half_width =  0.5f * tex_xsize * fabs(tex_right - tex_left);
+    float half_height = 0.5f * tex_ysize * fabs(tex_top - tex_bottom);
+    float scaled_width, scaled_height;
+
+    // the user can override alpha sorting if they want
+    bool alpha = false;
+
+    if (!geom->get_alpha_disable()) {
+      // figure out if alpha's enabled (if not, no reason to sort)
+      const TransparencyAttrib *trans = _state->get_transparency();
+      if (trans != (const TransparencyAttrib *)NULL) {
+        alpha = (trans->get_mode() != TransparencyAttrib::M_none);
+      }
+    }
+
+    // inner loop vars
+    int i;
+    Vertexf source_vert, cameraspace_vert;
+    float *x_walk, *y_walk, *theta_walk;
+    float theta;
+
+    nassertv(geom->get_x_bind_type() != G_PER_VERTEX);
+    nassertv(geom->get_y_bind_type() != G_PER_VERTEX);
+
+    // set up the non-built-in bindings
+    bool x_overall = (geom->get_x_bind_type() == G_OVERALL);
+    bool y_overall = (geom->get_y_bind_type() == G_OVERALL);
+    bool theta_overall = (geom->get_theta_bind_type() == G_OVERALL);
+    bool color_overall = (geom->get_binding(G_COLOR) == G_OVERALL);
+    bool theta_on = !(geom->get_theta_bind_type() == G_OFF);
+
+    // x direction
+    if (x_overall)
+        scaled_width = geom->_x_texel_ratio[0] * half_width;
+    else {
+        nassertv(((int)geom->_x_texel_ratio.size() >= geom->get_num_prims()));
+        x_walk = &geom->_x_texel_ratio[0];
+    }
+
+    // y direction
+    if (y_overall)
+        scaled_height = geom->_y_texel_ratio[0] * half_height;
+    else {
+        nassertv(((int)geom->_y_texel_ratio.size() >= geom->get_num_prims()));
+        y_walk = &geom->_y_texel_ratio[0];
+    }
+
+    // theta
+    if (theta_on) {
+        if (theta_overall)
+            theta = geom->_theta[0];
+        else {
+            nassertv(((int)geom->_theta.size() >= geom->get_num_prims()));
+            theta_walk = &geom->_theta[0];
+        }
+    }
+
+    /////////////////////////////////////////////////////////////////////
+    // INNER LOOP PART 1 STARTS HERE
+    // Here we transform each point to cameraspace and fill our sort
+    // vector with the final geometric information.
+    /////////////////////////////////////////////////////////////////////
+
+    Colorf v_color;
+
+    // sort container and iterator
+    pvector< WrappedSpriteSortPtr > sorted_sprite_vector;
+    pvector< WrappedSpriteSortPtr >::iterator sorted_vec_iter;
+
+    WrappedSprite *SpriteArray = new WrappedSprite[nPrims];
+
+    //BUGBUG: could we use _fvfbuf for this to avoid perframe alloc?
+    // alternately, alloc once when retained mode becomes available
+
+    if (SpriteArray==NULL) {
+        dxgsg9_cat.fatal() << "draw_sprite() out of memory!!" << endl;
+        return;
+    }
+
+    // the state is set, start running the prims
+
+    WrappedSprite *pSpr;
+
+    for (pSpr=SpriteArray,i = 0; i < nPrims; i++,pSpr++) {
+
+        source_vert = geom->get_next_vertex(vi);
+        cameraspace_vert = source_vert * modelview_mat;
+
+        pSpr->_v.set(cameraspace_vert[0],cameraspace_vert[1],cameraspace_vert[2]);
+
+        if (!color_overall) {
+            GET_NEXT_COLOR();
+            pSpr->_c = _curD3Dcolor;
+        }
+        if (!x_overall)
+            pSpr->_x_ratio = *x_walk++;
+        if (!y_overall)
+            pSpr->_y_ratio = *y_walk++;    // go along array of ratio values stored in geom
+        if (theta_on && (!theta_overall))
+            pSpr->_theta = *theta_walk++;
+    }
+
+    if (alpha) {
+        sorted_sprite_vector.reserve(nPrims);   //pre-alloc space for nPrims
+
+        for (pSpr=SpriteArray,i = 0; i < nPrims; i++,pSpr++) {   // build STL-sortable array
+            WrappedSpriteSortPtr ws_ptr;
+            ws_ptr.z=pSpr->_v[2];
+            ws_ptr.pSpr=pSpr;
+            sorted_sprite_vector.push_back(ws_ptr);
+        }
+
+        // sort the verts properly by alpha (if necessary).  Of course,
+        // the sort is only local, not scene-global, so if you look closely you'll
+        // notice that alphas may be screwy.  It's ok though, because this is fast.
+        // if you want accuracy, use billboards and take the speed hit.
+
+        sort(sorted_sprite_vector.begin(), sorted_sprite_vector.end(), draw_sprite_vertex_less());
+        sorted_vec_iter = sorted_sprite_vector.begin();
+
+        // disabling dither for alpha particle-systems.
+        // ATI sez:  most applications ignore the fact that since alpha blended primitives
+        // combine the data in the frame buffer with the data in the current pixel, pixels
+        // can be dithered multiple times and accentuate the dither pattern. This is particularly
+        // true in particle systems which rely on the cumulative visual effect of many overlapping
+        // alpha blended primitives.
+
+        if(_dither_enabled) {
+            bReEnableDither=true;
+            enable_dither(false);
+        }
+    }
+
+    Vertexf ul, ur, ll, lr;
+
+    ////////////////////////////////////////////////////////////////////////////
+    // INNER LOOP PART 2 STARTS HERE
+    // Now we run through the cameraspace vector and compute the geometry for each
+    // tristrip.  This includes scaling as per the ratio arrays, as well as
+    // rotating in the z.
+    ////////////////////////////////////////////////////////////////////////////
+
+    D3DCOLOR CurColor;
+    DWORD FVFType = D3DFVF_XYZ | (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0)) | D3DFVF_DIFFUSE;
+    DWORD vertex_size = sizeof(float) * 2 + sizeof(float) * 3 + sizeof(D3DCOLOR);
+
+    if (color_overall) {
+        GET_NEXT_COLOR();
+        CurColor = _curD3Dcolor;
+    }
+
+    // see note on fog and gouraud-shading in draw_prim_setup
+    bool bUseGouraudShadedColor=_fog_enabled;
+    enable_gouraud_shading(_fog_enabled);
+    set_vertex_format(FVFType);
+
+    #ifdef _DEBUG
+     nassertv(_pCurFvfBufPtr == NULL);   // make sure the storage pointer is clean.
+     nassertv(nPrims * 4 * vertex_size < VERT_BUFFER_SIZE);
+     nassertv(nPrims * 6 < PANDA_MAXNUMVERTS );
+    #endif
+
+    _pCurFvfBufPtr = _pFvfBufBasePtr;          // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+    const float TexCrdSets[4][2] = {
+      { tex_left, tex_bottom },
+      { tex_right, tex_bottom },
+      { tex_left, tex_top },
+      { tex_right, tex_top }
+    };
+
+#define QUADVERTLISTLEN 6
+
+    DWORD QuadVertIndexList[QUADVERTLISTLEN] = { 0, 1, 2, 3, 2, 1};
+    DWORD CurDPIndexArrLength=0,CurVertCount=0;
+
+    for (pSpr=SpriteArray,i = 0; i < nPrims; i++,pSpr++) {   // build STL-sortable array
+
+        if (alpha) {
+            pSpr = sorted_vec_iter->pSpr;
+            sorted_vec_iter++;
+        }
+
+        // if not G_OVERALL, calculate the scale factors    //huh??
+        if (!x_overall)
+            scaled_width = pSpr->_x_ratio * half_width;
+
+        if (!y_overall)
+            scaled_height = pSpr->_y_ratio * half_height;
+
+        // if not G_OVERALL, do some trig for this z rotate   //what is the theta angle??
+        if (theta_on) {
+            if (!theta_overall)
+                theta = pSpr->_theta;
+
+            // create the rotated points.  BUGBUG: this matmult will be slow if we dont get inlining
+            // rotate_mat calls sin() on an unbounded val, possible to make it faster with lookup table (modulate to 0-360 range?)
+
+            LMatrix3f xform_mat = LMatrix3f::rotate_mat(theta) *
+                                  LMatrix3f::scale_mat(scaled_width, scaled_height);
+
+            ur = (LVector3f( 1.0f,  1.0f, 0.0f) * xform_mat) + pSpr->_v;
+            ul = (LVector3f(-1.0f,  1.0f, 0.0f) * xform_mat) + pSpr->_v;
+            lr = (LVector3f( 1.0f, -1.0f, 0.0f) * xform_mat) + pSpr->_v;
+            ll = (LVector3f(-1.0f, -1.0f, 0.0f) * xform_mat) + pSpr->_v;
+        } else {
+            // create points for unrotated rect sprites
+            float x,y,negx,negy,z;
+
+            x = pSpr->_v[0] + scaled_width;
+            y = pSpr->_v[1] + scaled_height;
+            negx = pSpr->_v[0] - scaled_width;
+            negy = pSpr->_v[1] - scaled_height;
+            z = pSpr->_v[2];
+
+            ur.set(x, y, z);
+            ul.set(negx, y, z);
+            lr.set(x, negy, z);
+            ll.set(negx, negy, z);
+        }
+
+        // can no longer assume flat-shaded (because of vtx fog), so always copy full color in there
+
+        /*********  LL vertex  **********/
+
+        add_to_FVFBuf((void *)ll.get_data(), 3*sizeof(float));
+        if (!color_overall)  // otherwise its already been set globally
+           CurColor = pSpr->_c;
+        add_DWORD_to_FVFBuf(CurColor); // only need to cpy color on 1st vert, others are just empty ignored space
+        add_to_FVFBuf((void *)TexCrdSets[0], sizeof(float)*2);
+
+        /*********  LR vertex  **********/
+
+        add_to_FVFBuf((void *)lr.get_data(), 3*sizeof(float));
+
+        // if flat shading, dont need to write color for middle vtx, just incr ptr
+        if(bUseGouraudShadedColor)
+            *((DWORD *)_pCurFvfBufPtr) = (DWORD) CurColor;
+        _pCurFvfBufPtr += sizeof(D3DCOLOR);
+
+        add_to_FVFBuf((void *)TexCrdSets[1], sizeof(float)*2);
+
+        /*********  UL vertex  **********/
+
+        add_to_FVFBuf((void *)ul.get_data(), 3*sizeof(float));
+        // if flat shading, dont need to write color for middle vtx, just incr ptr
+        if(bUseGouraudShadedColor)
+            *((DWORD *)_pCurFvfBufPtr) = (DWORD) CurColor;
+        _pCurFvfBufPtr += sizeof(D3DCOLOR);
+        add_to_FVFBuf((void *)TexCrdSets[2], sizeof(float)*2);
+
+        /*********  UR vertex  **********/
+
+        add_to_FVFBuf((void *)ur.get_data(), 3*sizeof(float));
+        add_DWORD_to_FVFBuf(CurColor);
+        add_to_FVFBuf((void *)TexCrdSets[3], sizeof(float)*2);
+
+        for (int ii=0;ii<QUADVERTLISTLEN;ii++) {
+            _index_buf[CurDPIndexArrLength+ii]=QuadVertIndexList[ii]+CurVertCount;
+        }
+        CurDPIndexArrLength+=QUADVERTLISTLEN;
+        CurVertCount+=4;
+    }
+
+    DWORD nVerts= nPrims << 2;  // 4*nPrims verts in vert array
+    DWORD numTris = nPrims << 1;  // 2*nPrims
+
+    // cant do tristrip/fan since multiple quads arent connected
+    // best we can do is indexed primitive, which sends 2 redundant indices instead of sending 2 redundant full verts
+    HRESULT hr = _pD3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0,  // start index in array
+                                                         nVerts, numTris,
+                                                         _index_buf, D3DFMT_INDEX16,
+                                                         _pFvfBufBasePtr, vertex_size);
+    TestDrawPrimFailure(DrawIndexedPrim,hr,_pD3DDevice,QUADVERTLISTLEN*nPrims,numTris);
+
+    _pCurFvfBufPtr = NULL;
+    delete [] SpriteArray;
+
+    // restore the matrices
+    _pD3DDevice->SetTransform(D3DTS_WORLD,
+                                  (D3DMATRIX*)modelview_mat.get_data());
+
+    if(bReEnableDither)
+        enable_dither(true);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_polygon
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_polygon(GeomPolygon *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+   dxgsg9_cat.debug() << "draw_polygon()" << endl;
+#endif
+   DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+   DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+   // wireframe polygon will be drawn as linestrip, otherwise draw as multi-tri trifan
+   DWORD rstate;
+   _pD3DDevice->GetRenderState(D3DRS_FILLMODE, &rstate);
+   if(rstate==D3DFILL_WIREFRAME) {
+       draw_linestrip_base(geom,gc,true);
+   } else {
+       draw_multitri(geom, D3DPT_TRIANGLEFAN);
+   }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_quad
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_quad(GeomQuad *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_quad()" << endl;
+#endif
+   DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+   DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+   // wireframe quad will be drawn as linestrip, otherwise draw as multi-tri trifan
+   DWORD rstate;
+   _pD3DDevice->GetRenderState(D3DRS_FILLMODE, &rstate);
+   if(rstate==D3DFILL_WIREFRAME) {
+       draw_linestrip_base(geom,gc,true);
+   } else {
+       draw_multitri(geom, D3DPT_TRIANGLEFAN);
+   }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_tri
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_tri(GeomTri *geom, GeomContext *gc) {
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_tri()" << endl;
+#endif
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_tri_pcollector.add_level(geom->get_num_vertices()));
+
+#if 0
+    if (_pCurTexContext!=NULL) {
+        dxgsg9_cat.spam() << "Cur active DX texture: " << _pCurTexContext->_tex->get_name() << "\n";
+    }
+#endif
+
+#ifdef COUNT_DRAWPRIMS
+    cGeomcount++;
+#endif
+
+    DWORD nPrims = geom->get_num_prims();
+    HRESULT hr;
+
+    PTA_Vertexf coords;
+    PTA_Normalf norms;
+    PTA_Colorf colors;
+    PTA_TexCoordf texcoords;
+    GeomBindType TexCoordBinding,ColorBinding,NormalBinding;
+    PTA_ushort vindexes,nindexes,tindexes,cindexes;
+
+    geom->get_coords(coords,vindexes);
+    geom->get_normals(norms,NormalBinding,nindexes);
+    geom->get_colors(colors,ColorBinding,cindexes);
+    geom->get_texcoords(texcoords,TexCoordBinding,tindexes);
+
+    // this is the old geom setup, it reformats every vtx into an output array passed to d3d
+
+    _perVertex = 0x0;
+    _perPrim = 0x0;
+    
+    bool bUseTexCoordOnlyLoop = ((ColorBinding != G_PER_VERTEX) &&
+                                 (NormalBinding == G_OFF) &&
+                                 (TexCoordBinding != G_OFF));
+    
+    bool bPerPrimNormal;
+    
+    bool bPerPrimColor=(ColorBinding == G_PER_PRIM);
+    if(bPerPrimColor)
+      _perPrim = PER_COLOR;
+    else if(ColorBinding == G_PER_VERTEX)
+      _perVertex = PER_COLOR;
+    
+    if(bUseTexCoordOnlyLoop) {
+      _perVertex |= PER_TEXCOORD;  // TexCoords are either G_OFF or G_PER_VERTEX
+    } else {
+      if(NormalBinding == G_PER_VERTEX)
+        _perVertex |= PER_NORMAL;
+      else if(NormalBinding == G_PER_PRIM)
+        _perPrim |= PER_NORMAL;
+
+      bPerPrimNormal=((_perPrim & PER_NORMAL)!=0);
+
+      if(TexCoordBinding == G_PER_VERTEX)
+        _perVertex |= PER_TEXCOORD;
+    }
+    
+    size_t vertex_size = draw_prim_setup(geom);
+    
+    // Note: draw_prim_setup could unset color flags if global color is set, so must
+    //       recheck this flag here!
+    bPerPrimColor=(_perPrim & PER_COLOR)!=0x0;
+    
+#ifdef _DEBUG
+    // is it Ok not to recompute bUseTexCoordOnlyLoop even if draw_prim_setup unsets color flags?
+    // add this check to make sure
+    bool bNewUseTexCoordOnlyLoop = (((_perVertex & PER_COLOR)==0x0) &&
+                                    ((_CurFVFType & D3DFVF_NORMAL)==0x0) &&
+                                    ((_CurFVFType & D3DFVF_TEX1)!=0x0));
+    if(bUseTexCoordOnlyLoop && (!bNewUseTexCoordOnlyLoop)) {
+      // ok for bUseTexCoordOnlyLoop to be false, and bNew to be true.
+      // draw_prim_setup can sometimes turn off the _perComp color for
+      // G_OVERALL and scene-graph-color cases, which causes bNew to be true,
+      // while the original bUseTexCoordOnly is still false.
+      // the case we want to prevent is accidently using the texcoordloop
+      // instead of the general one, using the general one should always work.
+      
+      DebugBreak();
+      assert(0);
+    }
+#endif
+    
+    nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+    nassertv(nPrims * 3 * vertex_size < VERT_BUFFER_SIZE);
+    _pCurFvfBufPtr = _pFvfBufBasePtr;          // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+    
+    // iterate through the triangle primitive
+    
+    for (uint i = 0; i < nPrims; i++) {
+      if(bPerPrimColor) {  // remember color might be G_OVERALL too!
+        GET_NEXT_COLOR();
+      }
+      
+      if(bUseTexCoordOnlyLoop) {
+        draw_prim_inner_loop_coordtexonly(3, geom);
+      } else {
+        if(bPerPrimNormal)
+          p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+        
+        draw_prim_inner_loop(3, geom, _perVertex);
+      }
+    }
+    
+    DWORD nVerts=nPrims*3;
+
+    nassertv((nVerts*vertex_size) == (_pCurFvfBufPtr-_pFvfBufBasePtr));
+    
+    if(!_bDrawPrimDoSetupVertexBuffer) {
+      hr = _pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST, nPrims, _pFvfBufBasePtr, vertex_size);
+      TestDrawPrimFailure(DrawPrim,hr,_pD3DDevice,nVerts,nPrims);
+    } else {
+      COPYVERTDATA_2_VERTEXBUFFER(D3DPT_TRIANGLELIST,nVerts);
+    }
+    
+    _pCurFvfBufPtr = NULL;
+    
+    
+    ///////////////////////////
+#if 0
+    // test triangle for me to dbg experiments only
+    float vert_buf[15] = {
+      0.0f, 0.0f, 0.0f,  0.0f, 0.0f,
+      33.0, 0.0f, 0.0f,  0.0f, 2.0,
+      0.0f, 0.0f, 33.0,  2.0, 0.0f
+    };
+
+    _pD3DDevice->SetTextureStageState(0,D3DTSS_ADDRESSU,D3DTADDRESS_BORDER);
+    _pD3DDevice->SetTextureStageState(0,D3DTSS_ADDRESSV,D3DTADDRESS_BORDER);
+    _pD3DDevice->SetTextureStageState(0,D3DTSS_BORDERCOLOR,MY_D3DRGBA(0,0,0,0));
+    
+    DWORD FVFType =  D3DFVF_XYZ | (D3DFVF_TEX1 | D3DFVF_TEXCOORDSIZE2(0)) ;
+    set_vertex_format(FVFType);
+    HRESULT hr = _pD3DDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,  vert_buf, 1, 5*sizeof(float));
+    TestDrawPrimFailure(DrawPrim,hr,_pD3DDevice,3,1);
+#endif
+
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_tristrip
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_tristrip(GeomTristrip *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+  dxgsg9_cat.debug() << "draw_tristrip()" << endl;
+#endif
+  DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+  DO_PSTATS_STUFF(_vertices_tristrip_pcollector.add_level(geom->get_num_vertices()));
+
+  draw_multitri(geom, D3DPT_TRIANGLESTRIP);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_trifan
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_trifan(GeomTrifan *geom, GeomContext *gc) {
+
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_trifan()" << endl;
+#endif
+  DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+  DO_PSTATS_STUFF(_vertices_trifan_pcollector.add_level(geom->get_num_vertices()));
+
+  draw_multitri(geom, D3DPT_TRIANGLEFAN);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_multitri
+//       Access: Public, Virtual
+//  Description: handles trifans and tristrips
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_multitri(Geom *geom, D3DPRIMITIVETYPE trilisttype) {
+
+    DWORD nPrims = geom->get_num_prims();
+    const uint *pLengthArr = (const uint *) ((const int *)geom->get_lengths());
+    HRESULT hr;
+
+    if(nPrims==0) {
+        #ifdef _DEBUG
+          dxgsg9_cat.warning() << "draw_multitri() called with ZERO vertices!!" << endl;
+        #endif
+        return;
+    }
+
+#ifdef COUNT_DRAWPRIMS
+    cGeomcount++;
+#endif
+
+    PTA_Vertexf coords;
+    PTA_Normalf norms;
+    PTA_Colorf colors;
+    PTA_TexCoordf texcoords;
+    GeomBindType TexCoordBinding,ColorBinding,NormalBinding;
+    PTA_ushort vindexes,nindexes,tindexes,cindexes;
+
+    geom->get_coords(coords,vindexes);
+    geom->get_normals(norms,NormalBinding,nindexes);
+    geom->get_colors(colors,ColorBinding,cindexes);
+    geom->get_texcoords(texcoords,TexCoordBinding,tindexes);
+
+    {
+        // this is the old geom setup, it reformats every vtx into an output array passed to d3d
+        _perVertex = 0x0;
+        _perPrim = 0x0;
+        _perComp = 0x0;
+
+        bool bIsTriList=(trilisttype==D3DPT_TRIANGLESTRIP);
+        bool bPerPrimColor=(ColorBinding == G_PER_PRIM);
+        bool bPerPrimNormal;
+        bool bUseTexCoordOnlyLoop = (((ColorBinding == G_OVERALL) || bPerPrimColor) &&
+                                     (NormalBinding == G_OFF) &&
+                                     (TexCoordBinding != G_OFF));
+
+        if(bUseTexCoordOnlyLoop) {
+           if(bPerPrimColor) {
+                _perPrim = PER_COLOR;
+           }
+        } else {
+            switch (ColorBinding) {
+                case G_PER_PRIM:
+                    _perPrim = PER_COLOR;
+                    break;
+                case G_PER_COMPONENT:
+                    _perComp = PER_COLOR;
+                    break;
+                case G_PER_VERTEX:
+                    _perVertex = PER_COLOR;
+                    break;
+            }
+
+            switch (NormalBinding) {
+                case G_PER_VERTEX:
+                    _perVertex |= PER_NORMAL;
+                    break;
+                case G_PER_PRIM:
+                    _perPrim |= PER_NORMAL;
+                    break;
+                case G_PER_COMPONENT:
+                    _perComp |= PER_NORMAL;
+                    break;
+            }
+
+            bPerPrimNormal=((_perPrim & PER_NORMAL)!=0);
+
+            if (TexCoordBinding == G_PER_VERTEX)
+                _perVertex |= PER_TEXCOORD;
+        }
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        // Note: draw_prim_setup could unset color flags if global color is set, so must
+        //       recheck this flag here!
+        bPerPrimColor=(_perPrim & PER_COLOR)!=0;
+
+        #ifdef _DEBUG
+          // is it Ok not to recompute bUseTexCoordOnlyLoop even if draw_prim_setup unsets color flags?
+          // add this check to make sure.  texcoordonly needs input that with unchanging color, except per-prim
+           bool bNewUseTexCoordOnlyLoop = ((((_perComp|_perVertex) & PER_COLOR)==0x0) &&
+                                           ((_CurFVFType & D3DFVF_NORMAL)==0x0) &&
+                                           ((_CurFVFType & D3DFVF_TEX1)!=0x0));
+
+           if(bUseTexCoordOnlyLoop && (!bNewUseTexCoordOnlyLoop)) {
+               // ok for bUseTexCoordOnlyLoop to be false, and bNew to be true.
+               // draw_prim_setup can sometimes turn off the _perComp color for
+               // G_OVERALL and scene-graph-color cases, which causes bNew to be true,
+               // while the original bUseTexCoordOnly is still false.
+               // the case we want to prevent is accidently using the texcoordloop
+               // instead of the general one, using the general one should always work.
+
+               DebugBreak();
+               assert(0);
+           }
+
+        #endif
+
+        // iterate through the triangle primitives
+
+        int nVerts;
+        if(pLengthArr==NULL) {
+           // we've been called by draw_quad, which has no lengths array
+           nVerts=4;
+        }
+
+        for (uint i = 0; i < nPrims; i++) {
+
+            if(pLengthArr!=NULL) {
+              nVerts = *(pLengthArr++);
+            }
+
+            if(bPerPrimColor) {  // remember color might be G_OVERALL too!
+                GET_NEXT_COLOR();
+            }
+
+#ifdef _DEBUG
+            nassertv(nVerts >= 3);
+            nassertv(_pCurFvfBufPtr == NULL);    // make sure the storage pointer is clean.
+            nassertv(nVerts * vertex_size < VERT_BUFFER_SIZE);
+#endif
+            _pCurFvfBufPtr = _pFvfBufBasePtr;            // _pCurFvfBufPtr changes,  _pFvfBufBasePtr doesn't
+
+            if(_perComp==0x0) {
+                 if(bUseTexCoordOnlyLoop) {
+                    draw_prim_inner_loop_coordtexonly(nVerts, geom);
+                 } else {
+                     if (bPerPrimNormal)
+                         p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+
+                     draw_prim_inner_loop(nVerts, geom, _perVertex);
+                 }
+            } else {
+                if(bPerPrimNormal)
+                    p_normal = geom->get_next_normal(ni);   // set primitive normal if there is one.
+
+                if(bIsTriList) {
+                   // in flat shade mode, D3D strips color using the 1st vertex.
+                   // (note: differs from OGL, which always uses last vtx for strips&fans
+
+                    // Store all but last 2 verts
+                    draw_prim_inner_loop(nVerts-2, geom, _perVertex | _perComp);
+
+                    // _perComp attribs should not be fetched for last 2 verts
+                    draw_prim_inner_loop(2, geom, _perVertex);
+                } else {
+                   // in flat shade mode, D3D fans color using the 2nd vertex.
+                   // (note: differs from OGL, which always uses last vtx for strips&fans
+                   // _perComp attribs should not be fetched for first & last verts, they will
+                   // be associated with middle n-2 verts
+
+                    draw_prim_inner_loop(1, geom, _perVertex);
+                    draw_prim_inner_loop(nVerts-2, geom, _perVertex | _perComp);
+                    draw_prim_inner_loop(1, geom, _perVertex);
+                }
+            }
+
+            assert((nVerts*vertex_size) == (_pCurFvfBufPtr-_pFvfBufBasePtr));
+            DWORD numTris=nVerts-2;
+
+            if(!_bDrawPrimDoSetupVertexBuffer) {
+                hr = _pD3DDevice->DrawPrimitiveUP(trilisttype, numTris, _pFvfBufBasePtr, vertex_size);
+                TestDrawPrimFailure(DrawPrim,hr,_pD3DDevice,nVerts,numTris);
+            } else {
+                COPYVERTDATA_2_VERTEXBUFFER(trilisttype,nVerts);
+            }
+
+            _pCurFvfBufPtr = NULL;
+        }
+    }
+
+}
+
+//-----------------------------------------------------------------------------
+// Name: GenerateSphere()
+// Desc: Makes vertex and index data for ellipsoid w/scaling factors sx,sy,sz
+//       tries to match gluSphere behavior
+//-----------------------------------------------------------------------------
+
+// probably want to replace this with D3DX9 call
+
+void DXGraphicsStateGuardian9::
+GenerateSphere(void *pVertexSpace,DWORD dwVertSpaceByteSize,
+               void *pIndexSpace,DWORD dwIndexSpaceByteSize,
+               D3DXVECTOR3 *pCenter, float fRadius,
+               DWORD wNumRings, DWORD wNumSections, float sx, float sy, float sz,
+               DWORD *pNumVertices,DWORD *pNumTris,DWORD fvfFlags,DWORD dwVertSize) {
+    float x, y, z, rsintheta;
+    D3DXVECTOR3 vPoint;
+
+//#define DBG_GENSPHERE
+#define M_PI 3.1415926f   // probably should get this from mathNumbers.h instead
+
+    nassertv(wNumRings>=2 && wNumSections>=2);
+    wNumRings--;  // wNumRings indicates number of vertex rings (not tri-rings).
+                  // gluSphere 'stacks' arg for 1 vert ring is 2, so convert to our '1'.
+    wNumSections++;  // to make us equiv to gluSphere
+
+    //Figure out needed space for the triangles and vertices.
+    DWORD dwNumVertices,dwNumIndices,dwNumTriangles;
+
+#define DO_SPHERE_TEXTURING (fvfFlags & D3DFVF_TEXCOUNT_MASK)
+#define DO_SPHERE_NORMAL    (fvfFlags & D3DFVF_NORMAL)
+#define DO_SPHERE_COLOR     (fvfFlags & D3DFVF_DIFFUSE)
+
+    if (DO_SPHERE_TEXTURING) {
+        // if texturing, we need full rings of identical position verts at poles to hold diff texture coords
+        wNumRings+=2;
+        dwNumVertices = *pNumVertices = wNumRings * wNumSections;
+        dwNumTriangles = (wNumRings-1) * wNumSections * 2;
+    } else {
+        dwNumVertices = *pNumVertices = wNumRings * wNumSections + 2;
+        dwNumTriangles = wNumRings*wNumSections*2;
+    }
+
+    dwNumIndices = dwNumTriangles*3;
+    *pNumTris = dwNumTriangles;
+
+//    D3DVERTEX* pvVertices = (D3DVERTEX*) pVertexSpace;
+    WORD *pwIndices = (WORD *) pIndexSpace;
+
+    nassertv(dwNumVertices*dwVertSize < VERT_BUFFER_SIZE);
+    nassertv(dwNumIndices < PANDA_MAXNUMVERTS );
+
+    // Generate vertex at the top point
+    D3DXVECTOR3 vTopPoint  = *pCenter;
+    D3DXVECTOR3 vBotPoint  = *pCenter;
+    float yRadius=sy*fRadius;
+    vTopPoint.y+=yRadius;
+    vBotPoint.y-=yRadius;
+    D3DXVECTOR3 vNormal = D3DXVECTOR3( 0.0f, 1.0f, 0.0f);
+    float texCoords[2];
+
+    nassertv(pVertexSpace==_pCurFvfBufPtr);  // add_to_FVFBuf requires this
+
+#define ADD_GENSPHERE_VERTEX_TO_BUFFER(VERT)                      \
+    add_to_FVFBuf((void *)&(VERT), 3*sizeof(float));            \
+    if(fvfFlags & D3DFVF_NORMAL)                                  \
+        add_to_FVFBuf((void *)&vNormal, 3*sizeof(float));       \
+    if(fvfFlags & D3DFVF_DIFFUSE)                                 \
+        add_DWORD_to_FVFBuf(_curD3Dcolor);                        \
+    if(fvfFlags & D3DFVF_TEXCOUNT_MASK)                           \
+        add_to_FVFBuf((void *)texCoords, sizeof(TexCoordf));
+
+#ifdef DBG_GENSPHERE
+    int nvs_written=0;
+    memset(pVertexSpace,0xFF,dwNumVertices*dwVertSize);
+#endif
+
+    if (! DO_SPHERE_TEXTURING) {
+        ADD_GENSPHERE_VERTEX_TO_BUFFER(vTopPoint);
+#ifdef DBG_GENSPHERE
+        nvs_written++;
+#endif
+    }
+
+    // Generate vertex points for rings
+    float inv_radius = 1.0f/fRadius;
+    const float reciprocal_PI=1.0f/M_PI;
+    const float reciprocal_2PI=1.0f/(2.0*M_PI);
+    DWORD i;
+    float theta,dtheta;
+
+    if (DO_SPHERE_TEXTURING) {
+        // numRings already includes 1st and last rings for this case
+        dtheta = (float)(M_PI / (wNumRings-1));     //Angle between each ring (ignore 2 fake rings)
+        theta = 0.0f;
+    } else {
+        dtheta = (float)(M_PI / (wNumRings + 1));   //Angle between each ring
+        theta = dtheta;
+    }
+    float phi,dphi   = (float)(2*M_PI / (wNumSections-1)); //Angle between each section
+
+    for (i = 0; i < wNumRings; i++) {
+        float costheta,sintheta,cosphi,sinphi;
+        phi =   0.0f;
+
+        if (DO_SPHERE_TEXTURING) {
+            texCoords[1] = theta * reciprocal_PI;  // v is the same for each ring
+        }
+
+        // could optimize all this sin/cos stuff w/tables
+        csincos(theta,&sintheta,&costheta);
+        y = fRadius * costheta;     // y is the same for each ring
+
+        rsintheta = fRadius * sintheta;
+
+        for (DWORD j = 0; j < wNumSections; j++) {
+            csincos(phi,&sinphi,&cosphi);
+            x = rsintheta * sinphi;
+            z = rsintheta * cosphi;
+
+#ifdef DBG_GENSPHERE
+            nvs_written++;
+#endif
+            vPoint.x = pCenter->x + sx*x;
+            vPoint.y = pCenter->y + sy*y;
+            vPoint.z = pCenter->z + sz*z;
+
+            add_to_FVFBuf((void *)&vPoint, 3*sizeof(float));
+
+            if (DO_SPHERE_NORMAL) {
+                // bugbug: this is wrong normal for the non-spherical case (i think you need to multiply by 1/scale factor per component)
+                D3DXVECTOR3 vVec = D3DXVECTOR3( x*inv_radius, y*inv_radius, z*inv_radius );
+                D3DXVec3Normalize(&vNormal,&vVec);
+                add_to_FVFBuf((float *)&vNormal, 3*sizeof(float));
+            }
+
+            if (DO_SPHERE_COLOR)
+                add_DWORD_to_FVFBuf(_curD3Dcolor);
+
+            if (DO_SPHERE_TEXTURING) {
+                texCoords[0] = 1.0f - phi*reciprocal_2PI;
+                add_to_FVFBuf((void *)texCoords, sizeof(TexCoordf));
+            }
+
+            phi += dphi;
+        }
+        theta += dtheta;
+    }
+
+    if (! DO_SPHERE_TEXTURING) {
+        // Generate bottom vertex
+        vNormal = D3DXVECTOR3( 0.0f, -1.0f, 0.0f );
+        ADD_GENSPHERE_VERTEX_TO_BUFFER(vBotPoint);
+#ifdef DBG_GENSPHERE
+        nvs_written++;
+#endif
+    }
+
+#ifdef DBG_GENSPHERE
+    assert(nvs_written == dwNumVertices);
+#endif
+
+
+#ifdef DBG_GENSPHERE
+    memset(pwIndices,0xFF,dwNumIndices*sizeof(WORD));
+#endif
+
+    // inited for textured case
+    DWORD cur_vertring_startidx=0;    // first vertex in current ring
+    DWORD CurFinalTriIndex = 0;       // index of next tri to be written
+
+    if (! DO_SPHERE_TEXTURING) {
+        // Generate caps using unique the bot/top vert
+        // for non-textured case, could render the caps as indexed trifans,
+        // but should be no perf difference b/w indexed trilists and indexed trifans
+        // and this has advantage of being aggregable into 1 big DPrim call for whole sphere
+
+        for (i = 0; i < wNumSections; i++) {
+            DWORD TopCapTriIndex=3*i;
+            DWORD BotCapTriIndex=3*(dwNumTriangles - wNumSections + i);
+            DWORD i_incd = ((i + 1) % wNumSections);
+
+            pwIndices[TopCapTriIndex++] = 0;
+            pwIndices[TopCapTriIndex++] = i + 1;
+            pwIndices[TopCapTriIndex] =  i_incd + 1;
+
+            pwIndices[BotCapTriIndex++] = (WORD)( dwNumVertices - 1 );
+            pwIndices[BotCapTriIndex++] = (WORD)( dwNumVertices - 2 - i );
+            pwIndices[BotCapTriIndex] = (WORD)( dwNumVertices - 2 - i_incd);
+        }
+
+        cur_vertring_startidx = 1;          // first vertex in current ring (skip top vert)
+        CurFinalTriIndex = wNumSections;    // index of tri to be written, wNumSections to skip the top cap row
+    }
+
+    DWORD j_incd,base_index;
+
+    // technically we could break into a strip for every row (or 1 big strip connected w/degenerate tris)
+    // but indexed trilists should actually be just as fast on HW
+
+    // Generate triangles for the rings
+    for (i = 0; i < wNumRings-1; i++) {
+        for (DWORD j = 0; j < wNumSections; j++) {
+
+            base_index=3*CurFinalTriIndex;  // final vert index is 3*finaltriindex
+            j_incd=(j+1) % wNumSections;
+
+            DWORD v1_row1_idx,v2_row1_idx,v1_row2_idx,v2_row2_idx;
+
+            v1_row1_idx = cur_vertring_startidx + j;
+            v2_row1_idx = cur_vertring_startidx + j_incd;
+            v1_row2_idx = v1_row1_idx + wNumSections;
+            v2_row2_idx = v2_row1_idx + wNumSections;
+
+#ifdef DBG_GENSPHERE
+            assert(v2_row2_idx<dwNumVertices);
+            assert(v1_row2_idx<dwNumVertices);
+            assert(v2_row1_idx<dwNumVertices);
+            assert(v1_row1_idx<dwNumVertices);
+#endif
+
+            pwIndices[base_index++] = v1_row1_idx;
+            pwIndices[base_index++] = v1_row2_idx;
+            pwIndices[base_index++] = v2_row2_idx;
+
+            pwIndices[base_index++] = v1_row1_idx;
+            pwIndices[base_index++] = v2_row2_idx;
+            pwIndices[base_index++] = v2_row1_idx;
+
+            CurFinalTriIndex += 2;  // we wrote 2 tris, add 2 to finaltriindex
+        }
+        cur_vertring_startidx += wNumSections;
+    }
+
+#ifdef DBG_GENSPHERE
+    if (DO_SPHERE_TEXTURING) {
+        assert(CurFinalTriIndex == dwNumTriangles);
+        assert(base_index == dwNumIndices);
+    } else {
+        assert(CurFinalTriIndex == dwNumTriangles-wNumSections);
+        assert(base_index == dwNumIndices-wNumSections*3);
+    }
+
+    for (i = 0; i < dwNumIndices; i++)
+        assert(pwIndices[i] <dwNumVertices);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_sphere
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_sphere(GeomSphere *geom, GeomContext *gc) {
+
+#define SPHERE_NUMSLICES 16
+#define SPHERE_NUMSTACKS 10
+
+#ifdef GSG_VERBOSE
+    dxgsg9_cat.debug() << "draw_sphere()" << endl;
+#endif
+    DO_PSTATS_STUFF(PStatTimer timer(_draw_primitive_pcollector));
+    DO_PSTATS_STUFF(_vertices_other_pcollector.add_level(geom->get_num_vertices()));
+
+    int nprims = geom->get_num_prims();
+
+    if (nprims==0) {
+        dxgsg9_cat.warning() << "draw_sphere() called with ZERO vertices!!" << endl;
+        return;
+    }
+
+    Geom::VertexIterator vi = geom->make_vertex_iterator();
+    Geom::ColorIterator ci;
+    bool bPerPrimColor = (geom->get_binding(G_COLOR) == G_PER_PRIM);
+    if (bPerPrimColor)
+        ci = geom->make_color_iterator();
+
+    for (int i = 0; i < nprims; i++) {
+
+        DWORD nVerts,nTris;
+        Vertexf center = geom->get_next_vertex(vi);
+        Vertexf edge = geom->get_next_vertex(vi);
+        LVector3f v = edge - center;
+        float fRadius = sqrt(dot(v, v));
+
+        size_t vertex_size = draw_prim_setup(geom);
+
+        _pCurFvfBufPtr = _pFvfBufBasePtr;
+
+        if (bPerPrimColor) {
+            GET_NEXT_COLOR();
+        }
+
+        GenerateSphere(_pCurFvfBufPtr, VERT_BUFFER_SIZE,
+                       _index_buf, PANDA_MAXNUMVERTS*2,
+                       (D3DXVECTOR3 *)&center, fRadius,
+                       SPHERE_NUMSTACKS, SPHERE_NUMSLICES,
+                       1.0f, 1.0f, 1.0f,  // no scaling factors, do a sphere not ellipsoid
+                       &nVerts,&nTris,_CurFVFType,vertex_size);
+
+        // possible optimization: make DP 1 for all spheres call here, since trilist is independent tris.
+        // indexes couldnt start w/0 tho, need to pass offset to gensph
+        HRESULT hr = _pD3DDevice->DrawIndexedPrimitiveUP(D3DPT_TRIANGLELIST, 0,  // start index in array
+                                                         nVerts, nTris, _index_buf, D3DFMT_INDEX16,
+                                                         _pFvfBufBasePtr, vertex_size);
+        TestDrawPrimFailure(DrawIndexedPrim,hr,_pD3DDevice,nVerts,nTris);
+    }
+
+    _pCurFvfBufPtr = NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::prepare_texture
+//       Access: Public, Virtual
+//  Description: Creates a new retained-mode representation of the
+//               given texture, and returns a newly-allocated
+//               TextureContext pointer to reference it.  It is the
+//               responsibility of the calling function to later
+//               call release_texture() with this same pointer (which
+//               will also delete the pointer).
+////////////////////////////////////////////////////////////////////
+TextureContext *DXGraphicsStateGuardian9::
+prepare_texture(Texture *tex) {
+
+    DXTextureContext9 *dtc = new DXTextureContext9(tex);
+#ifdef WBD_GL_MODE
+    glGenTextures(1, &gtc->_index);
+
+    bind_texture(gtc);
+    glPrioritizeTextures(1, &gtc->_index, &gtc->_priority);
+    specify_texture(tex);
+    apply_texture_immediate(tex);
+#else
+
+    if (dtc->CreateTexture(*_pScrn) == NULL) {
+        delete dtc;
+        return NULL;
+    }
+#endif              // WBD_GL_MODE
+
+    bool inserted = mark_prepared_texture(dtc);
+
+    // If this assertion fails, the same texture was prepared twice,
+    // which shouldn't be possible, since the texture itself should
+    // detect this.
+    nassertr(inserted, NULL);
+
+    return dtc;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::apply_texture
+//       Access: Public, Virtual
+//  Description: Makes the texture the currently available texture for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+apply_texture(TextureContext *tc) {
+    if (tc==NULL) {
+        return;  // use enable_texturing to disable/enable
+    }
+    #ifdef DO_PSTATS
+       add_to_texture_record(tc);
+    #endif
+
+//  bind_texture(tc);
+
+//  specify_texture(tc->_texture);
+    // Note: if this code changes, make sure to change initialization SetTSS code in dx_init as well
+    // so DX TSS renderstate matches dxgsg state
+
+    DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
+
+    int dirty = dtc->get_dirty_flags();
+
+    if (dirty) {
+      // If the texture image has changed, or if its use of mipmaps has
+      // changed, we need to re-create the image.  Ignore other types of
+      // changes, which arent significant for dx
+
+      if((dirty & (Texture::DF_image | Texture::DF_mipmap)) != 0) {
+          // If this is *only* because of a mipmap change, issue a
+          // warning--it is likely that this change is the result of an
+          // error or oversight.
+          if ((dirty & Texture::DF_image) == 0) {
+            dxgsg9_cat.warning()
+              << "Texture " << *dtc->_texture << " has changed mipmap state.\n";
+          }
+
+          dtc->DeleteTexture();
+          if (dtc->CreateTexture(*_pScrn) == NULL) {
+
+            // Oops, we can't re-create the texture for some reason.
+            dxgsg9_cat.error() << "Unable to re-create texture " << *dtc->_texture << endl;
+
+            release_texture(dtc);
+            enable_texturing(false);
+            return;
+          }
+      }
+      dtc->clear_dirty_flags();
+    } else {
+       if(_pCurTexContext == dtc) {
+          return;  // tex already set (and possible problem in state-sorting?)
+       }
+    }
+
+    Texture *tex = tc->_texture;
+    Texture::WrapMode wrapU,wrapV;
+    wrapU=tex->get_wrapu();
+    wrapV=tex->get_wrapv();
+
+    if (wrapU!=_CurTexWrapModeU) {
+        _pD3DDevice->SetSamplerState(0,D3DSAMP_ADDRESSU,get_texture_wrap_mode(wrapU));
+        _CurTexWrapModeU = wrapU;
+    }
+    if (wrapV!=_CurTexWrapModeV) {
+        _pD3DDevice->SetSamplerState(0,D3DSAMP_ADDRESSV,get_texture_wrap_mode(wrapV));
+        _CurTexWrapModeV = wrapV;
+    }
+
+    uint aniso_degree=tex->get_anisotropic_degree();
+    Texture::FilterType ft=tex->get_magfilter();
+
+    if(_CurTexAnisoDegree != aniso_degree) {
+        _pD3DDevice->SetSamplerState(0,D3DSAMP_MAXANISOTROPY,aniso_degree);
+        _CurTexAnisoDegree = aniso_degree;
+    }
+
+    D3DTEXTUREFILTERTYPE newMagFilter;
+    if (aniso_degree<=1) {
+        newMagFilter=((ft!=Texture::FT_nearest) ? D3DTEXF_LINEAR : D3DTEXF_POINT);
+
+        #ifdef _DEBUG
+        if((ft!=Texture::FT_linear)&&(ft!=Texture::FT_nearest)) {
+             dxgsg9_cat.error() << "MipMap filter type setting for texture magfilter makes no sense,  texture: " << tex->get_name() << "\n";
+        }
+        #endif
+    } else {
+        newMagFilter=D3DTEXF_ANISOTROPIC;
+    }
+
+    if(_CurTexMagFilter!=newMagFilter) {
+        _CurTexMagFilter=newMagFilter;
+        _pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, newMagFilter);
+    }
+
+#ifdef _DEBUG
+    assert(Texture::FT_linear_mipmap_linear < 8);
+#endif
+/*
+ enum FilterType {
+    FT_nearest,FT_linear,FT_nearest_mipmap_nearest,FT_linear_mipmap_nearest,
+    FT_nearest_mipmap_linear, FT_linear_mipmap_linear, };
+*/
+ // map Panda composite min+mip filter types to d3d's separate min & mip filter types
+ static D3DTEXTUREFILTERTYPE PandaToD3DMinType[8] =
+    {D3DTEXF_POINT,D3DTEXF_LINEAR,D3DTEXF_POINT,D3DTEXF_LINEAR,D3DTEXF_POINT,D3DTEXF_LINEAR};
+ static D3DTEXTUREFILTERTYPE PandaToD3DMipType[8] =
+    {D3DTEXF_NONE,D3DTEXF_NONE,D3DTEXF_POINT,D3DTEXF_POINT,D3DTEXF_LINEAR,D3DTEXF_LINEAR};
+
+    ft=tex->get_minfilter();
+
+#ifdef _DEBUG
+    if(ft > Texture::FT_linear_mipmap_linear) {
+                dxgsg9_cat.error() << "Unknown tex filter type for tex: " << tex->get_name() << "  filter: "<<(DWORD)ft<<"\n";
+                return;
+    }
+#endif
+
+    D3DTEXTUREFILTERTYPE newMipFilter = PandaToD3DMipType[(DWORD)ft];
+
+    #ifndef NDEBUG
+      // sanity check
+    extern char *PandaFilterNameStrs[];
+    if((!(dtc->_bHasMipMaps))&&(newMipFilter!=D3DTEXF_NONE)) {
+        dxgsg9_cat.error() << "Trying to set mipmap filtering for texture with no generated mipmaps!! texname[" << tex->get_name() << "], filter("<<PandaFilterNameStrs[ft]<<")\n";
+        newMipFilter=D3DTEXF_NONE;
+    }
+    #endif
+
+
+    D3DTEXTUREFILTERTYPE newMinFilter = PandaToD3DMinType[(DWORD)ft];
+
+    if(aniso_degree>=2) {
+        newMinFilter=D3DTEXF_ANISOTROPIC;
+    }
+
+    if(newMinFilter!=_CurTexMinFilter) {
+        _CurTexMinFilter = newMinFilter;
+        _pD3DDevice->SetSamplerState(0, D3DSAMP_MINFILTER, newMinFilter);
+    }
+
+    if(newMipFilter!=_CurTexMipFilter) {
+        _CurTexMipFilter = newMipFilter;
+        _pD3DDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, newMipFilter);
+    }
+
+    // bugbug:  does this handle the case of untextured geometry?
+    //          we dont see this bug cause we never mix textured/untextured
+    _pD3DDevice->SetTexture(0,dtc->_pD3DTexture9);
+
+#if 0
+    if (dtc!=NULL) {
+        dxgsg9_cat.spam() << "Setting active DX texture: " << dtc->_tex->get_name() << "\n";
+    }
+#endif
+
+    _pCurTexContext = dtc;   // enable_texturing needs this
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::release_texture
+//       Access: Public, Virtual
+//  Description: Frees the GL resources previously allocated for the
+//               texture.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+release_texture(TextureContext *tc) {
+    DXTextureContext9 *gtc = DCAST(DXTextureContext9, tc);
+    Texture *tex = tc->_texture;
+
+    gtc->DeleteTexture();
+    bool erased = unmark_prepared_texture(gtc);
+
+    // If this assertion fails, a texture was released that hadn't been
+    // prepared (or a texture was released twice).
+    nassertv(erased);
+
+    tex->clear_gsg(this);
+
+    delete gtc;
+}
+
+// copies current display region in framebuffer to the texture
+// usually its more efficient to do SetRenderTgt
+void DXGraphicsStateGuardian9::
+copy_texture(TextureContext *tc, const DisplayRegion *dr) {
+
+  HRESULT hr;
+  int xo, yo, w, h;
+  dr->get_region_pixels(xo, yo, w, h);
+
+  DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
+  PixelBuffer *pb = dtc->_tex->_pbuffer;
+  pb->set_size(0,0,w-xo,h-yo);
+
+  IDirect3DSurface9 *pTexSurfaceLev0,*pCurRenderTarget;
+  hr = dtc->_pD3DTexture9->GetSurfaceLevel(0,&pTexSurfaceLev0);
+  if(FAILED(hr)) {
+    dxgsg9_cat.error() << "GetSurfaceLev failed in copy_texture" << D3DERRORSTRING(hr);
+    exit(1);
+  }
+
+  hr = _pD3DDevice->GetRenderTarget(0, &pCurRenderTarget);
+  if(FAILED(hr)) {
+    dxgsg9_cat.error() << "GetRenderTgt failed in copy_texture" << D3DERRORSTRING(hr);
+    exit(1);
+  }
+
+
+  RECT SrcRect;
+
+  SrcRect.left = xo;
+  SrcRect.right = xo+w;
+  SrcRect.top = yo;
+  SrcRect.bottom = yo+h;
+
+  // now copy from fb to tex
+  hr = _pD3DDevice->UpdateSurface(pCurRenderTarget,&SrcRect,pTexSurfaceLev0,NULL);
+  
+  if(FAILED(hr)) {
+    dxgsg9_cat.error() << "CopyRects failed in copy_texture" << D3DERRORSTRING(hr);
+    exit(1);
+  }
+
+  SAFE_RELEASE(pCurRenderTarget);
+  SAFE_RELEASE(pTexSurfaceLev0);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::copy_texture
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+copy_texture(TextureContext *tc, const DisplayRegion *dr, const RenderBuffer &rb) {
+    set_read_buffer(rb);
+    copy_texture(tc, dr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::texture_to_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb) {
+ // This code is now invalidated by the new design; perhaps the
+  // interface is not needed anyway.
+#if 0
+
+    nassertv(tc != NULL && pb != NULL);
+
+    Texture *tex = tc->_texture;
+
+    int w = tex->_pbuffer->get_xsize();
+    int h = tex->_pbuffer->get_ysize();
+
+    PT(DisplayRegion) dr = _win->make_scratch_display_region(w, h);
+
+    FrameBufferStack old_fb = push_frame_buffer
+                              (get_render_buffer(RenderBuffer::T_back | RenderBuffer::T_depth),
+                               dr);
+
+    texture_to_pixel_buffer(tc, pb, dr);
+
+    pop_frame_buffer(old_fb);
+#else
+    dxgsg9_cat.error() << "texture_to_pixel_buffer unimplemented for DX!\n";
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::texture_to_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb,
+                        const DisplayRegion *dr) {
+    dxgsg9_cat.error()
+      << "texture_to_pixel_buffer unimplemented!\n";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::copy_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr) {
+
+    RECT SrcCopyRect;
+    nassertv(pb != NULL && dr != NULL);
+
+    int xo, yo, w, h;
+    dr->get_region_pixels(xo, yo, w, h);
+
+    // only handled simple case
+    nassertv(xo==0);
+    nassertv(yo==0);
+    nassertv(w==pb->get_xsize());
+    nassertv(h==pb->get_ysize());
+
+    IDirect3DSurface9 *pD3DSurf;
+    HRESULT hr;
+
+    RECT WindRect;
+    GetWindowRect(_pScrn->hWnd,&WindRect);
+
+    // just handling front and backbuf for now, not textures yet
+    if(_cur_read_pixel_buffer & RenderBuffer::T_back) {
+       hr=_pD3DDevice->GetBackBuffer(0, 0,D3DBACKBUFFER_TYPE_MONO,&pD3DSurf);
+
+       if(FAILED(hr)) {
+           dxgsg9_cat.error() << "GetBackBuffer failed" << D3DERRORSTRING(hr);
+           exit(1);
+       }
+
+       D3DSURFACE_DESC SurfDesc;
+       hr = pD3DSurf->GetDesc(&SurfDesc);
+
+       SrcCopyRect.top=SrcCopyRect.left=0;
+       SrcCopyRect.right=SurfDesc.Width;
+       SrcCopyRect.bottom=SurfDesc.Height;
+
+       // note if you try to grab the backbuffer and full-screen anti-aliasing is on,
+       // the backbuffer might be larger than the window size.  for screenshots its safer to get the front buffer.
+
+    } else if(_cur_read_pixel_buffer & RenderBuffer::T_front) {
+       // must create a A8R8G8B8 sysmem surface for GetFrontBuffer to copy to
+
+        DWORD TmpSurfXsize,TmpSurfYsize;
+
+        if(_pScrn->PresParams.Windowed) {
+            // GetFrontBuffer retrieves the entire desktop for a monitor, so need space for that
+
+            MONITORINFO minfo;
+            minfo.cbSize = sizeof(MONITORINFO);
+            GetMonitorInfo(_pScrn->hMon, &minfo);   // have to use GetMonitorInfo, since this gsg may not be for primary monitor
+
+            TmpSurfXsize=RECT_XSIZE(minfo.rcMonitor);
+            TmpSurfYsize=RECT_YSIZE(minfo.rcMonitor);
+
+            // set SrcCopyRect to client area of window in scrn coords
+            GetClientRect( _pScrn->hWnd, &SrcCopyRect);
+            ClientToScreen( _pScrn->hWnd, (POINT*)&SrcCopyRect.left );
+            ClientToScreen( _pScrn->hWnd, (POINT*)&SrcCopyRect.right );
+        } else {
+           TmpSurfXsize=RECT_XSIZE(WindRect);
+           TmpSurfYsize=RECT_YSIZE(WindRect);
+
+           SrcCopyRect.top=SrcCopyRect.left=0;
+           SrcCopyRect.right=TmpSurfXsize;
+           SrcCopyRect.bottom=TmpSurfYsize;
+        }
+
+        hr=_pD3DDevice->CreateOffscreenPlainSurface(TmpSurfXsize,TmpSurfYsize,D3DFMT_A8R8G8B8,D3DPOOL_SYSTEMMEM, &pD3DSurf, NULL);
+        if(FAILED(hr)) {
+           dxgsg9_cat.error() << "CreateImageSurface failed in copy_pixel_buffer()" << D3DERRORSTRING(hr);
+           exit(1);
+        }
+
+        hr=_pD3DDevice->GetFrontBufferData(0, pD3DSurf);
+
+        if(hr==D3DERR_DEVICELOST) {
+           // dont necessary want to exit in this case
+           pD3DSurf->Release();
+           dxgsg9_cat.error() << "copy_pixel_buffer failed: device lost\n";
+           return;
+        }
+    } else {
+        dxgsg9_cat.error() << "copy_pixel_buffer: unhandled current_read_pixel_buffer type\n";
+    }
+
+    if((RECT_XSIZE(SrcCopyRect)>w) || (RECT_YSIZE(SrcCopyRect)>h)) {
+     dxgsg9_cat.error() << "copy_pixel_buffer: pixel buffer size does not match selected screen RenderBuffer size!\n";
+     exit(1);
+    }
+
+    (void) ConvertD3DSurftoPixBuf(SrcCopyRect,pD3DSurf,pb);
+
+    RELEASE(pD3DSurf,dxgsg9,"pD3DSurf",RELEASE_ONCE);
+
+    nassertv(!pb->_image.empty());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::copy_pixel_buffer
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                  const RenderBuffer &rb) {
+    set_read_buffer(rb);
+    copy_pixel_buffer(pb, dr);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::apply_material
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::apply_material( const Material* material ) {
+    D3DMATERIAL9 cur_material;
+    cur_material.Diffuse = *(D3DCOLORVALUE *)(material->get_diffuse().get_data());
+    cur_material.Ambient = *(D3DCOLORVALUE *)(material->get_ambient().get_data());
+    cur_material.Specular = *(D3DCOLORVALUE *)(material->get_specular().get_data());
+    cur_material.Emissive = *(D3DCOLORVALUE *)(material->get_emission().get_data());
+    cur_material.Power   =  material->get_shininess();
+    _pD3DDevice->SetMaterial(&cur_material);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::apply_fog
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+apply_fog(Fog *fog) {
+
+    if(_doFogType==None)
+      return;
+
+    Fog::Mode panda_fogmode = fog->get_mode();
+    D3DFOGMODE d3dfogmode = get_fog_mode_type(panda_fogmode);
+
+
+    // should probably avoid doing redundant SetRenderStates, but whatever
+    _pD3DDevice->SetRenderState((D3DRENDERSTATETYPE)_doFogType, d3dfogmode);
+
+    const Colorf &fog_colr = fog->get_color();
+    _pD3DDevice->SetRenderState(D3DRS_FOGCOLOR,
+                   MY_D3DRGBA(fog_colr[0], fog_colr[1], fog_colr[2], 0.0f));  // Alpha bits are not used
+
+    // do we need to adjust fog start/end values based on D3DPRASTERCAPS_WFOG/D3DPRASTERCAPS_ZFOG ?
+    // if not WFOG, then docs say we need to adjust values to range [0,1]
+
+    switch (panda_fogmode) {
+        case Fog::M_linear:
+            {
+                float onset, opaque;
+                fog->get_linear_range(onset, opaque);
+
+                _pD3DDevice->SetRenderState( D3DRS_FOGSTART,
+                                            *((LPDWORD) (&onset)) );
+                _pD3DDevice->SetRenderState( D3DRS_FOGEND,
+                                            *((LPDWORD) (&opaque)) );
+            }
+            break;
+        case Fog::M_exponential:
+        case Fog::M_exponential_squared:
+            {
+                // Exponential fog is always camera-relative.
+                float fog_density = fog->get_exp_density();
+                _pD3DDevice->SetRenderState( D3DRS_FOGDENSITY,
+                            *((LPDWORD) (&fog_density)) );
+            }
+            break;
+    }
+}
+
+void DXGraphicsStateGuardian9::SetTextureBlendMode(TextureApplyAttrib::Mode TexBlendMode,bool bCanJustEnable) {
+
+/*class TextureApplyAttrib {
+  enum Mode {
+    M_modulate,M_decal,M_blend,M_replace,M_add};
+*/
+    static D3DTEXTUREOP TexBlendColorOp1[/* TextureApplyAttrib::Mode maxval*/ 10] =
+    {D3DTOP_MODULATE,D3DTOP_BLENDTEXTUREALPHA,D3DTOP_MODULATE,D3DTOP_SELECTARG1,D3DTOP_ADD};
+
+    //if bCanJustEnable, then we only need to make sure ColorOp is turned on and set properly
+    if (bCanJustEnable && (TexBlendMode==_CurTexBlendMode)) {
+        // just reset COLOROP 0 to enable pipeline, rest is already set properly
+        _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, TexBlendColorOp1[TexBlendMode] );
+        return;
+    }
+
+    _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, TexBlendColorOp1[TexBlendMode] );
+
+    switch (TexBlendMode) {
+
+        case TextureApplyAttrib::M_modulate:
+            // emulates GL_MODULATE glTexEnv mode
+            // want to multiply tex-color*pixel color to emulate GL modulate blend (see glTexEnv)
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
+
+            break;
+        case TextureApplyAttrib::M_decal:
+            // emulates GL_DECAL glTexEnv mode
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_DIFFUSE );
+
+            break;
+        case TextureApplyAttrib::M_replace:
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+            break;
+        case TextureApplyAttrib::M_add:
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+
+            // since I'm making up 'add' mode, use modulate.  "adding" alpha never makes sense right?
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+            _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
+
+            break;
+        case TextureApplyAttrib::M_blend:
+            dxgsg9_cat.error()
+            << "Impossible to emulate GL_BLEND in DX exactly " << (int) TexBlendMode << endl;
+/*
+           // emulate GL_BLEND glTexEnv
+
+           GL requires 2 independent operations on 3 input vars for this mode
+           DX texture pipeline requires re-using input of last stage on each new op, so I dont think
+           exact emulation is possible
+           _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
+           _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE | D3DTA_COMPLEMENT );
+           _pD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
+
+           _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_MODULATE );
+           _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
+           _pD3DDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
+
+           need to SetTexture(1,tex) also
+           _pD3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE ); wrong
+           _pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
+           _pD3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_TFACTOR );
+
+           _pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );
+           _pD3DDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_CURRENT );
+*/
+
+
+            break;
+        default:
+            dxgsg9_cat.error() << "Unknown texture blend mode " << (int) TexBlendMode << endl;
+            break;
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_texturing
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_texturing(bool val) {
+//  if (_texturing_enabled == val) {  // this check is mostly for internal gsg calls, panda already screens out redundant state changes
+//        return;
+//  }
+
+    _texturing_enabled = val;
+
+//  assert(_pCurTexContext!=NULL);  we're definitely called with it NULL for both true and false
+//  I'm going to allow enabling texturing even if no tex has been set yet, seems to cause no probs
+
+    if (val == false) {
+        _pD3DDevice->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_DISABLE);
+    } else {
+          SetTextureBlendMode(_CurTexBlendMode,true);
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_transform
+//       Access: Public, Virtual
+//  Description: Sends the indicated transform matrix to the graphics
+//               API to be applied to future vertices.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_transform(const TransformState *transform) {
+  DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1));
+
+  // if we're using ONLY vertex shaders, could get avoid calling SetTrans
+  D3DMATRIX *pMat = (D3DMATRIX*)transform->get_mat().get_data();
+  _pD3DDevice->SetTransform(D3DTS_WORLD,pMat);
+
+#ifdef USE_VERTEX_SHADERS
+  if(_CurVertexShader!=NULL) {
+    // vertex shaders need access to the current xform matrix,
+    // so need to reset this vshader 'constant' every time view matrix changes
+      HRESULT hr =  _pD3DDevice->SetVertexShaderConstant(VSHADER_XFORMMATRIX_CONSTANTREGNUMSTART, pMat, 4);
+      #ifdef _DEBUG
+      if(FAILED(hr)) {
+        dxgsg9_cat.error() << "SetVertexShader failed" << D3DERRORSTRING(hr);
+        exit(1);
+      }
+      #endif
+  }
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_tex_matrix
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_tex_matrix(const TexMatrixAttrib *attrib) {
+  const LMatrix4f &m = attrib->get_mat();
+
+  // This is ugly.  Need to make this a simple boolean test instead of
+  // a matrix compare.
+  if (m == LMatrix4f::ident_mat()) {
+    _pD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 
+                                      D3DTTFF_DISABLE);
+  } else {
+    // We have to reorder the elements of the matrix for some reason.
+    LMatrix4f dm(m(0, 0), m(0, 1), m(0, 3), 0.0f,
+                 m(1, 0), m(1, 1), m(1, 3), 0.0f,
+                 m(3, 0), m(3, 1), m(3, 3), 0.0f,
+                 0.0f, 0.0f, 0.0f, 1.0f);
+    _pD3DDevice->SetTransform(D3DTS_TEXTURE0, (D3DMATRIX *)dm.get_data());
+    _pD3DDevice->SetTextureStageState(0, D3DTSS_TEXTURETRANSFORMFLAGS, 
+                                      D3DTTFF_COUNT2);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_texture
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_texture(const TextureAttrib *attrib) {
+  DO_PSTATS_STUFF(_texture_state_pcollector.add_level(1));
+  if (attrib->is_off()) {
+    enable_texturing(false);
+  } else {
+    enable_texturing(true);
+    Texture *tex = attrib->get_texture();
+    nassertv(tex != (Texture *)NULL);
+    tex->apply(this);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_material
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_material(const MaterialAttrib *attrib) {
+  const Material *material = attrib->get_material();
+  if (material != (const Material *)NULL) {
+    apply_material(material);
+  } else {
+    // Apply a default material when materials are turned off.
+    Material empty;
+    apply_material(&empty);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_render_mode
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_render_mode(const RenderModeAttrib *attrib) {
+  RenderModeAttrib::Mode mode = attrib->get_mode();
+
+  switch (mode) {
+  case RenderModeAttrib::M_filled:
+    _pD3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
+    break;
+
+  case RenderModeAttrib::M_wireframe:
+    _pD3DDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
+    break;
+
+  default:
+    dxgsg9_cat.error()
+      << "Unknown render mode " << (int)mode << endl;
+  }
+
+  _current_fill_mode = mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_texture_apply
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_texture_apply(const TextureApplyAttrib *attrib) {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_depth_test
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_depth_test(const DepthTestAttrib *attrib) {
+  DepthTestAttrib::PandaCompareFunc mode = attrib->get_mode();
+  if (mode == DepthTestAttrib::M_none) {
+    _depth_test_enabled = false;
+    _pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_FALSE);
+  } else {
+    _depth_test_enabled = true;
+    _pD3DDevice->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
+    _pD3DDevice->SetRenderState(D3DRS_ZFUNC, (D3DCMPFUNC) mode);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_alpha_test
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_alpha_test(const AlphaTestAttrib *attrib) {
+  AlphaTestAttrib::PandaCompareFunc mode = attrib->get_mode();
+  if (mode == AlphaTestAttrib::M_none) {
+    enable_alpha_test(false);
+  } else {
+    //  AlphaTestAttrib::PandaCompareFunc === D3DCMPFUNC
+    call_dxAlphaFunc((D3DCMPFUNC)mode, attrib->get_reference_alpha());
+    enable_alpha_test(true);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_depth_write
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_depth_write(const DepthWriteAttrib *attrib) {
+  enable_zwritemask(attrib->get_mode() == DepthWriteAttrib::M_on);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_cull_face
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_cull_face(const CullFaceAttrib *attrib) {
+  CullFaceAttrib::Mode mode = attrib->get_effective_mode();
+
+  switch (mode) {
+  case CullFaceAttrib::M_cull_none:
+    _pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
+    break;
+  case CullFaceAttrib::M_cull_clockwise:
+    _pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
+    break;
+  case CullFaceAttrib::M_cull_counter_clockwise:
+    _pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
+    break;
+  default:
+    dxgsg9_cat.error()
+      << "invalid cull face mode " << (int)mode << endl;
+    break;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_fog
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_fog(const FogAttrib *attrib) {
+  if (!attrib->is_off()) {
+    enable_fog(true);
+    Fog *fog = attrib->get_fog();
+    nassertv(fog != (Fog *)NULL);
+    apply_fog(fog);
+  } else {
+    enable_fog(false);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::issue_depth_offset
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+issue_depth_offset(const DepthOffsetAttrib *attrib) {
+  int offset = attrib->get_offset();
+  _pD3DDevice->SetRenderState(D3DRS_DEPTHBIAS, offset);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::bind_light
+//       Access: Public, Virtual
+//  Description: Called the first time a particular light has been
+//               bound to a given id within a frame, this should set
+//               up the associated hardware light with the light's
+//               properties.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+bind_light(PointLight *light, int light_id) {
+  // Get the light in "world coordinates".  This means the light in
+  // the coordinate space of the camera, converted to DX's coordinate
+  // system.
+  NodePath light_np(light);
+  const LMatrix4f &light_mat = light_np.get_mat(_scene_setup->get_camera_path());
+  LMatrix4f rel_mat = light_mat * LMatrix4f::convert_mat(CS_yup_left, CS_default);
+  LPoint3f pos = light->get_point() * rel_mat;
+
+  D3DCOLORVALUE black;
+  black.r = black.g = black.b = black.a = 0.0f;
+  D3DLIGHT9 alight;
+  alight.Type =  D3DLIGHT_POINT;
+  alight.Diffuse  = *(D3DCOLORVALUE *)(light->get_color().get_data());
+  alight.Ambient  =  black ;
+  alight.Specular = *(D3DCOLORVALUE *)(light->get_specular_color().get_data());
+
+  // Position needs to specify x, y, z, and w
+  // w == 1 implies non-infinite position
+  alight.Position = *(D3DVECTOR *)pos.get_data();
+
+  alight.Range =  __D3DLIGHT_RANGE_MAX;
+  alight.Falloff =  1.0f;
+
+  const LVecBase3f &att = light->get_attenuation();
+  alight.Attenuation0 = att[0];
+  alight.Attenuation1 = att[1];
+  alight.Attenuation2 = att[2];
+
+  HRESULT res = _pD3DDevice->SetLight(light_id, &alight);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::bind_light
+//       Access: Public, Virtual
+//  Description: Called the first time a particular light has been
+//               bound to a given id within a frame, this should set
+//               up the associated hardware light with the light's
+//               properties.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+bind_light(DirectionalLight *light, int light_id) {
+  // Get the light in "world coordinates".  This means the light in
+  // the coordinate space of the camera, converted to DX's coordinate
+  // system.
+  NodePath light_np(light);
+  const LMatrix4f &light_mat = light_np.get_mat(_scene_setup->get_camera_path());
+  LMatrix4f rel_mat = light_mat * LMatrix4f::convert_mat(CS_yup_left, CS_default);
+  LVector3f dir = light->get_direction() * rel_mat;
+
+  D3DCOLORVALUE black;
+  black.r = black.g = black.b = black.a = 0.0f;
+
+  D3DLIGHT9 alight;
+  ZeroMemory(&alight, sizeof(D3DLIGHT9));
+
+  alight.Type =  D3DLIGHT_DIRECTIONAL;
+  alight.Diffuse  = *(D3DCOLORVALUE *)(light->get_color().get_data());
+  alight.Ambient  =  black ;
+  alight.Specular = *(D3DCOLORVALUE *)(light->get_specular_color().get_data());
+
+  alight.Direction = *(D3DVECTOR *)dir.get_data();
+
+  alight.Range =  __D3DLIGHT_RANGE_MAX;
+  alight.Falloff =  1.0f;
+
+  alight.Attenuation0 = 1.0f;       // constant
+  alight.Attenuation1 = 0.0f;       // linear
+  alight.Attenuation2 = 0.0f;       // quadratic
+
+  HRESULT res = _pD3DDevice->SetLight(light_id, &alight);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::bind_light
+//       Access: Public, Virtual
+//  Description: Called the first time a particular light has been
+//               bound to a given id within a frame, this should set
+//               up the associated hardware light with the light's
+//               properties.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+bind_light(Spotlight *light, int light_id) {
+  Lens *lens = light->get_lens();
+  nassertv(lens != (Lens *)NULL);
+
+  // Get the light in "world coordinates".  This means the light in
+  // the coordinate space of the camera, converted to DX's coordinate
+  // system.
+  NodePath light_np(light);
+  const LMatrix4f &light_mat = light_np.get_mat(_scene_setup->get_camera_path());
+  LMatrix4f rel_mat = light_mat * LMatrix4f::convert_mat(CS_yup_left, CS_default);
+  LPoint3f pos = lens->get_nodal_point() * rel_mat;
+  LVector3f dir = lens->get_view_vector() * rel_mat;
+
+  D3DCOLORVALUE black;
+  black.r = black.g = black.b = black.a = 0.0f;
+
+  D3DLIGHT9  alight;
+  ZeroMemory(&alight, sizeof(D3DLIGHT9));
+
+  alight.Type =  D3DLIGHT_SPOT;
+  alight.Ambient  =  black ;
+  alight.Diffuse  = *(D3DCOLORVALUE *)(light->get_color().get_data());
+  alight.Specular = *(D3DCOLORVALUE *)(light->get_specular_color().get_data());
+
+  alight.Position = *(D3DVECTOR *)pos.get_data();
+
+  alight.Direction = *(D3DVECTOR *)dir.get_data();
+
+  alight.Range =  __D3DLIGHT_RANGE_MAX;
+  alight.Falloff =  1.0f;
+  alight.Theta =  0.0f;
+  alight.Phi =  lens->get_hfov();
+
+  const LVecBase3f &att = light->get_attenuation();
+  alight.Attenuation0 = att[0];
+  alight.Attenuation1 = att[1];
+  alight.Attenuation2 = att[2];
+
+  HRESULT res = _pD3DDevice->SetLight(light_id, &alight);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::begin_frame
+//       Access: Public, Virtual
+//  Description: Called before each frame is rendered, to allow the
+//               GSG a chance to do any internal cleanup before
+//               beginning the frame.
+//
+//               The return value is true if successful (in which case
+//               the frame will be drawn and end_frame() will be
+//               called later), or false if unsuccessful (in which
+//               case nothing will be drawn and end_frame() will not
+//               be called).
+////////////////////////////////////////////////////////////////////
+bool DXGraphicsStateGuardian9::
+begin_frame() {
+  return GraphicsStateGuardian::begin_frame();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::begin_scene
+//       Access: Public, Virtual
+//  Description: Called between begin_frame() and end_frame() to mark
+//               the beginning of drawing commands for a "scene"
+//               (usually a particular DisplayRegion) within a frame.
+//               All 3-D drawing commands, except the clear operation,
+//               must be enclosed within begin_scene() .. end_scene().
+//
+//               The return value is true if successful (in which case
+//               the scene will be drawn and end_scene() will be
+//               called later), or false if unsuccessful (in which
+//               case nothing will be drawn and end_scene() will not
+//               be called).
+////////////////////////////////////////////////////////////////////
+bool DXGraphicsStateGuardian9::
+begin_scene() {
+  if (!GraphicsStateGuardian::begin_scene()) {
+    return false;
+  }
+
+  HRESULT hr = _pD3DDevice->BeginScene();
+
+  if (FAILED(hr)) {
+    if (hr == D3DERR_DEVICELOST) {
+      if (dxgsg9_cat.is_debug()) {
+        dxgsg9_cat.debug()
+          << "BeginScene returns D3DERR_DEVICELOST" << endl;
+      }
+      
+      CheckCooperativeLevel();
+
+    } else {
+      dxgsg9_cat.error()
+        << "BeginScene failed, unhandled error hr == "
+        << D3DERRORSTRING(hr) << endl;
+      exit(1);
+    }
+    return false;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::end_scene
+//       Access: Public, Virtual
+//  Description: Called between begin_frame() and end_frame() to mark
+//               the end of drawing commands for a "scene" (usually a
+//               particular DisplayRegion) within a frame.  All 3-D
+//               drawing commands, except the clear operation, must be
+//               enclosed within begin_scene() .. end_scene().
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+end_scene() {
+  HRESULT hr = _pD3DDevice->EndScene();
+  
+  if (FAILED(hr)) {
+
+    if (hr == D3DERR_DEVICELOST) {
+      if(dxgsg9_cat.is_debug()) {
+        dxgsg9_cat.debug()
+          << "EndScene returns DeviceLost\n";
+      }
+      CheckCooperativeLevel();
+
+    } else {
+      dxgsg9_cat.error()
+        << "EndScene failed, unhandled error hr == " << D3DERRORSTRING(hr);
+      exit(1);
+    }
+    return;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::end_frame
+//       Access: Public, Virtual
+//  Description: Called after each frame is rendered, to allow the
+//               GSG a chance to do any internal cleanup after
+//               rendering the frame, and before the window flips.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+end_frame() {
+#ifdef COUNT_DRAWPRIMS
+    {
+        #define FRAMES_PER_DPINFO 90
+        static DWORD LastDPInfoFrame=0;
+        static DWORD LastTickCount=0;
+        const float one_thousandth = 1.0f/1000.0f;
+
+        if (_cur_frame_count-LastDPInfoFrame > FRAMES_PER_DPINFO) {
+            DWORD CurTickCount=GetTickCount();
+            float delta_secs=(CurTickCount-LastTickCount)*one_thousandth;
+
+            float numframes=_cur_frame_count-LastDPInfoFrame;
+            float verts_per_frame = cVertcount/numframes;
+            float tris_per_frame = cTricount/numframes;
+            float DPs_per_frame = cDPcount/numframes;
+            float DPs_notexchange_per_frame = cDP_noTexChangeCount/numframes;
+            float verts_per_DP = cVertcount/(float)cDPcount;
+            float verts_per_sec = cVertcount/delta_secs;
+            float tris_per_sec = cTricount/delta_secs;
+            float Geoms_per_frame = cGeomcount/numframes;
+            float DrawPrims_per_Geom = cDPcount/(float)cGeomcount;
+            float verts_per_Geom = cVertcount/(float)cGeomcount;
+
+            dxgsg9_cat.debug() << "==================================="
+                << "\n Avg Verts/sec:\t\t" << verts_per_sec
+                << "\n Avg Tris/sec:\t\t" << tris_per_sec
+                << "\n Avg Verts/frame:\t" << verts_per_frame
+                << "\n Avg Tris/frame:\t" << tris_per_frame
+                << "\n Avg DrawPrims/frm:\t" << DPs_per_frame
+                << "\n Avg Verts/DrawPrim:\t" << verts_per_DP
+                << "\n Avg DrawPrims w/no Texture Change from prev DrawPrim/frm:\t" << DPs_notexchange_per_frame
+                << "\n Avg Geoms/frm:\t" << Geoms_per_frame
+                << "\n Avg DrawPrims/Geom:\t" << DrawPrims_per_Geom
+                << "\n Avg Verts/Geom:\t" << verts_per_Geom
+                << endl;
+
+            LastDPInfoFrame=_cur_frame_count;
+            cDPcount = cVertcount=cTricount=cDP_noTexChangeCount=cGeomcount=0;
+            LastTickCount=CurTickCount;
+        }
+    }
+#endif
+
+#if defined(DO_PSTATS)||defined(PRINT_RESOURCESTATS)
+#ifndef PRINT_RESOURCESTATS
+  if (_texmgrmem_total_pcollector.is_active())
+#endif
+  {
+      #define TICKS_PER_GETTEXINFO (2.5*1000)   // 2.5 second interval
+      static DWORD LastTickCount=0;
+      DWORD CurTickCount=GetTickCount();
+
+      if (CurTickCount-LastTickCount > TICKS_PER_GETTEXINFO) {
+          LastTickCount=CurTickCount;
+          report_texmgr_stats();
+      }
+  }
+#endif
+
+  // Note: regular GraphicsWindow::end_frame is being called,
+  // but we override gsg::end_frame, so need to explicitly call it here
+  // (currently it's an empty fn)
+  GraphicsStateGuardian::end_frame();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::wants_normals
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool DXGraphicsStateGuardian9::
+wants_normals() const {
+    return (_lighting_enabled || _normals_enabled);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::wants_texcoords
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE bool DXGraphicsStateGuardian9::
+wants_texcoords() const {
+    return _texturing_enabled;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::depth_offset_decals
+//       Access: Public, Virtual
+//  Description: Returns true if this GSG can implement decals using a
+//               DepthOffsetAttrib, or false if that is unreliable
+//               and the three-step rendering process should be used
+//               instead.
+////////////////////////////////////////////////////////////////////
+bool DXGraphicsStateGuardian9::
+depth_offset_decals() {
+  // False for now.
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::get_internal_coordinate_system
+//       Access: Public, Virtual
+//  Description: Should be overridden by derived classes to return the
+//               coordinate system used internally by the GSG, if any
+//               one particular coordinate system is used.  The
+//               default, CS_default, indicates that the GSG can use
+//               any coordinate system.
+//
+//               If this returns other than CS_default, the
+//               GraphicsEngine will automatically convert all
+//               transforms into the indicated coordinate system.
+////////////////////////////////////////////////////////////////////
+CoordinateSystem DXGraphicsStateGuardian9::
+get_internal_coordinate_system() const {
+  return CS_yup_left;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::compute_distance_to
+//       Access: Public, Virtual
+//  Description: This function may only be called during a render
+//               traversal; it will compute the distance to the
+//               indicated point, assumed to be in modelview
+//               coordinates, from the camera plane.
+////////////////////////////////////////////////////////////////////
+INLINE float DXGraphicsStateGuardian9::
+compute_distance_to(const LPoint3f &point) const {
+    // In the case of a DXGraphicsStateGuardian9, we know that the
+    // modelview matrix already includes the relative transform from the
+    // camera, as well as a to-y-up conversion.  Thus, the distance to
+    // the camera plane is simply the +z distance.  (negative of gl compute_distance_to,
+    // since d3d uses left-hand coords)
+
+    return point[2];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::set_draw_buffer
+//       Access: Protected
+//  Description: Sets up the glDrawBuffer to render into the buffer
+//               indicated by the RenderBuffer object.  This only sets
+//               up the color bits; it does not affect the depth,
+//               stencil, accum layers.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+set_draw_buffer(const RenderBuffer &rb) {
+    dxgsg9_cat.fatal() << "DX set_draw_buffer unimplemented!!!";
+    return;
+
+#ifdef WBD_GL_MODE
+    switch (rb._buffer_type & RenderBuffer::T_color) {
+        case RenderBuffer::T_front:
+            call_glDrawBuffer(GL_FRONT);
+            break;
+
+        case RenderBuffer::T_back:
+            call_glDrawBuffer(GL_BACK);
+            break;
+
+        case RenderBuffer::T_right:
+            call_glDrawBuffer(GL_RIGHT);
+            break;
+
+        case RenderBuffer::T_left:
+            call_glDrawBuffer(GL_LEFT);
+            break;
+
+        case RenderBuffer::T_front_right:
+            call_glDrawBuffer(GL_FRONT_RIGHT);
+            break;
+
+        case RenderBuffer::T_front_left:
+            call_glDrawBuffer(GL_FRONT_LEFT);
+            break;
+
+        case RenderBuffer::T_back_right:
+            call_glDrawBuffer(GL_BACK_RIGHT);
+            break;
+
+        case RenderBuffer::T_back_left:
+            call_glDrawBuffer(GL_BACK_LEFT);
+            break;
+
+        default:
+            call_glDrawBuffer(GL_FRONT_AND_BACK);
+    }
+#endif              // WBD_GL_MODE
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::set_read_buffer
+//       Access: Protected
+//  Description: Vestigial analog of glReadBuffer
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+set_read_buffer(const RenderBuffer &rb) {
+
+    if(rb._buffer_type & RenderBuffer::T_front) {
+            _cur_read_pixel_buffer=RenderBuffer::T_front;
+    } else  if(rb._buffer_type & RenderBuffer::T_back) {
+            _cur_read_pixel_buffer=RenderBuffer::T_back;
+    } else {
+            dxgsg9_cat.error() << "Invalid or unimplemented Argument to set_read_buffer!\n";
+    }
+    return;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::get_texture_wrap_mode
+//       Access: Protected
+//  Description: Maps from the Texture's internal wrap mode symbols to
+//               GL's.
+////////////////////////////////////////////////////////////////////
+INLINE D3DTEXTUREADDRESS DXGraphicsStateGuardian9::
+get_texture_wrap_mode(Texture::WrapMode wm) const {
+  static D3DTEXTUREADDRESS PandaTexWrapMode_to_D3DTexWrapMode[Texture::WM_invalid] = {
+    D3DTADDRESS_CLAMP,D3DTADDRESS_WRAP,D3DTADDRESS_MIRROR,D3DTADDRESS_MIRRORONCE,D3DTADDRESS_BORDER};
+
+    return PandaTexWrapMode_to_D3DTexWrapMode[wm];
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::get_fog_mode_type
+//       Access: Protected
+//  Description: Maps from the fog types to gl version
+////////////////////////////////////////////////////////////////////
+INLINE D3DFOGMODE DXGraphicsStateGuardian9::
+get_fog_mode_type(Fog::Mode m) const {
+  switch (m) {
+  case Fog::M_linear:
+    return D3DFOG_LINEAR;
+  case Fog::M_exponential:
+    return D3DFOG_EXP;
+  case Fog::M_exponential_squared:
+    return D3DFOG_EXP2;
+  }
+  dxgsg9_cat.error() << "Invalid Fog::Mode value" << endl;
+  return D3DFOG_EXP;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_lighting
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable or disable the use of lighting overall.  This
+//               is called by issue_light() according to whether any
+//               lights are in use or not.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+enable_lighting(bool enable) {
+  _pD3DDevice->SetRenderState(D3DRS_LIGHTING, (DWORD)enable);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::set_ambient_light
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               indicate the color of the ambient light that should
+//               be in effect.  This is called by issue_light() after
+//               all other lights have been enabled or disabled.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+set_ambient_light(const Colorf &color) {
+  _pD3DDevice->SetRenderState(D3DRS_AMBIENT,
+                                  Colorf_to_D3DCOLOR(color));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_light
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable the indicated light id.  A specific Light will
+//               already have been bound to this id via bind_light().
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+enable_light(int light_id, bool enable) {
+  HRESULT res = _pD3DDevice->LightEnable(light_id, enable);
+
+#ifdef GSG_VERBOSE
+  dxgsg9_cat.debug()
+    << "LightEnable(" << light_id << "=" << enable << ")" << endl;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::slot_new_clip_plane
+//       Access: Protected, Virtual
+//  Description: This will be called by the base class before a
+//               particular clip plane id will be used for the first
+//               time.  It is intended to allow the derived class to
+//               reserve any additional resources, if required, for
+//               the new clip plane; and also to indicate whether the
+//               hardware supports this many simultaneous clipping
+//               planes.
+//
+//               The return value should be true if the additional
+//               plane is supported, or false if it is not.
+////////////////////////////////////////////////////////////////////
+bool DXGraphicsStateGuardian9::
+slot_new_clip_plane(int plane_id) {
+  return (plane_id < D3DMAXUSERCLIPPLANES);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::enable_clip_plane
+//       Access: Protected, Virtual
+//  Description: Intended to be overridden by a derived class to
+//               enable the indicated clip_plane id.  A specific
+//               PlaneNode will already have been bound to this id via
+//               bind_clip_plane().
+////////////////////////////////////////////////////////////////////
+INLINE void DXGraphicsStateGuardian9::
+enable_clip_plane(int plane_id, bool enable) {
+  assert(plane_id < D3DMAXUSERCLIPPLANES);
+
+  DWORD bitflag = ((DWORD)1 << plane_id);
+  if (enable) {
+    _clip_plane_bits |= bitflag;
+  } else {
+    _clip_plane_bits &= ~bitflag;
+  }
+
+  _pD3DDevice->SetRenderState(D3DRS_CLIPPLANEENABLE, _clip_plane_bits);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::bind_clip_plane
+//       Access: Protected, Virtual
+//  Description: Called the first time a particular clip_plane has been
+//               bound to a given id within a frame, this should set
+//               up the associated hardware clip_plane with the clip_plane's
+//               properties.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+bind_clip_plane(PlaneNode *plane, int plane_id) {
+  // Get the plane in "world coordinates".  This means the plane in
+  // the coordinate space of the camera, converted to DX's coordinate
+  // system.
+  NodePath plane_np(plane);
+  const LMatrix4f &plane_mat = plane_np.get_mat(_scene_setup->get_camera_path());
+  LMatrix4f rel_mat = plane_mat * LMatrix4f::convert_mat(CS_yup_left, CS_default);
+  Planef world_plane = plane->get_plane() * rel_mat;
+
+  _pD3DDevice->SetClipPlane(plane_id, world_plane.get_data());
+}
+
+void DXGraphicsStateGuardian9::
+issue_color_write(const ColorWriteAttrib *attrib) {
+  _color_write_mode = attrib->get_mode();
+  set_color_writemask((_color_write_mode ==ColorWriteAttrib::M_on) ? 0xFFFFFFFF : 0x0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::set_blend_mode
+//       Access: Protected, Virtual
+//  Description: Called after any of these three blending states have
+//               changed; this function is responsible for setting the
+//               appropriate color blending mode based on the given
+//               properties.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
+               ColorBlendAttrib::Mode color_blend_mode,
+               TransparencyAttrib::Mode transparency_mode) {
+
+  if((color_write_mode == ColorWriteAttrib::M_off) && !_pScrn->bCanDirectDisableColorWrites) {
+    // need !_pScrn->bCanDirectDisableColorWrites guard because other issue_colorblend,issue_transp
+    // will come this way, and they should ignore the colorwriteattrib value since it's been
+    // handled separately in set_color_writemask
+    enable_blend(true);
+    call_dxBlendFunc(D3DBLEND_ZERO, D3DBLEND_ONE);
+    return;
+  }
+
+  // Is there a color blend set?
+  switch (color_blend_mode) {
+  case ColorBlendAttrib::M_none:
+    break;
+
+  case ColorBlendAttrib::M_multiply:
+    enable_blend(true);
+    call_dxBlendFunc(D3DBLEND_DESTCOLOR, D3DBLEND_ZERO);
+    return;
+
+  case ColorBlendAttrib::M_add:
+    enable_blend(true);
+    call_dxBlendFunc(D3DBLEND_ONE, D3DBLEND_ONE);
+    return;
+
+  case ColorBlendAttrib::M_multiply_add:
+    enable_blend(true);
+    call_dxBlendFunc(D3DBLEND_DESTCOLOR, D3DBLEND_ONE);
+    return;
+
+  default:
+    dxgsg9_cat.error()
+      << "Unknown color blend mode " << (int)color_blend_mode << endl;
+    break;
+  }
+
+  // No color blend; is there a transparency set?
+  switch (transparency_mode) {
+  case TransparencyAttrib::M_none:
+  case TransparencyAttrib::M_binary:
+    break;
+
+  case TransparencyAttrib::M_alpha:
+  case TransparencyAttrib::M_alpha_sorted:
+  case TransparencyAttrib::M_multisample:
+  case TransparencyAttrib::M_multisample_mask:
+  case TransparencyAttrib::M_dual:
+    enable_blend(true);
+    call_dxBlendFunc(D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
+    return;
+
+  default:
+    dxgsg9_cat.error()
+      << "invalid transparency mode " << (int)transparency_mode << endl;
+    break;
+  }
+
+  // Nothing's set, so disable blending.
+  enable_blend(false);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::save_frame_buffer
+//       Access: Public
+//  Description: Saves the indicated planes of the frame buffer
+//               (within the indicated display region) and returns it
+//               in some meaningful form that can be restored later
+//               via restore_frame_buffer().  This is a helper
+//               function for push_frame_buffer() and
+//               pop_frame_buffer().
+////////////////////////////////////////////////////////////////////
+PT(SavedFrameBuffer) DXGraphicsStateGuardian9::
+save_frame_buffer(const RenderBuffer &buffer,
+                  CPT(DisplayRegion) dr) {
+
+    dxgsg9_cat.error() << "save_frame_buffer unimplemented!!\n";
+    return NULL;
+
+#if 0
+    DXSavedFrameBuffer *sfb = new DXSavedFrameBuffer(buffer, dr);
+
+    if (buffer._buffer_type & RenderBuffer::T_depth) {
+        // Save the depth buffer.
+        sfb->_depth =
+        new PixelBuffer(PixelBuffer::depth_buffer(dr->get_pixel_width(),
+                                                  dr->get_pixel_height()));
+        copy_pixel_buffer(sfb->_depth, dr, buffer);
+    }
+
+    if (buffer._buffer_type & RenderBuffer::T_back) {
+        // Save the color buffer.
+        sfb->_back_rgba = new Texture;
+        copy_texture(sfb->_back_rgba->prepare(this), dr, buffer);
+    }
+
+    return sfb;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::restore_frame_buffer
+//       Access: Public
+//  Description: Restores the frame buffer that was previously saved.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+restore_frame_buffer(SavedFrameBuffer *frame_buffer) {
+    dxgsg9_cat.error() << "restore_frame_buffer unimplemented!!\n";
+    return;
+}
+
+TypeHandle DXGraphicsStateGuardian9::get_type(void) const {
+    return get_class_type();
+}
+
+TypeHandle DXGraphicsStateGuardian9::get_class_type(void) {
+    return _type_handle;
+}
+
+void DXGraphicsStateGuardian9::init_type(void) {
+    GraphicsStateGuardian::init_type();
+    register_type(_type_handle, "DXGraphicsStateGuardian9",
+                  GraphicsStateGuardian::get_class_type());
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: dx_cleanup
+//  Description: Clean up the DirectX environment, accounting for exit()
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+dx_cleanup(bool bRestoreDisplayMode,bool bAtExitFnCalled) {
+  static bool bAtExitFnEverCalled=false;
+
+    if(dxgsg9_cat.is_spam()) {
+        dxgsg9_cat.spam() << "dx_cleanup called, bAtExitFnCalled=" << bAtExitFnCalled << ", bAtExitFnEverCalled=" << bAtExitFnEverCalled << endl;
+    }
+
+    bAtExitFnEverCalled = (bAtExitFnEverCalled || bAtExitFnCalled);
+
+    // for now, I can't trust any of the ddraw/d3d releases during atexit(),
+    // so just return directly.  maybe revisit this later, if have problems
+    // restarting d3d/ddraw after one of these uncleaned-up exits
+    if(bAtExitFnEverCalled)
+      return;
+
+    // unsafe to do the D3D releases after exit() called, since DLL_PROCESS_DETACH
+    // msg already delivered to d3d.dll and it's unloaded itself
+
+    free_nondx_resources();
+
+    PRINT_REFCNT(dxgsg9,_pD3DDevice);
+
+    // these 2 calls release ddraw surfaces and vbuffers.  unsafe unless not on exit
+    release_all_textures();
+    release_all_geoms();
+
+    PRINT_REFCNT(dxgsg9,_pD3DDevice);
+
+    // delete non-panda-texture/geom DX objects (VBs/textures/shaders)
+    //SAFE_DELSHADER(Vertex,_CurVertexShader,_pD3DDevice);
+    //SAFE_DELSHADER(Pixel,_CurPixelShader,_pD3DDevice);
+    SAFE_RELEASE(_pGlobalTexture);
+
+    PRINT_REFCNT(dxgsg9,_pD3DDevice);
+
+    // Do a safe check for releasing the D3DDEVICE. RefCount should be zero.
+    // if we're called from exit(), _pD3DDevice may already have been released
+    if (_pD3DDevice!=NULL) {
+        for(int i=0;i<D3D_MAXTEXTURESTAGES;i++)
+           _pD3DDevice->SetTexture(i,NULL);  // d3d should release this stuff internally anyway, but whatever
+        RELEASE(_pD3DDevice,dxgsg9,"d3dDevice",RELEASE_DOWN_TO_ZERO);
+        _pScrn->pD3DDevice = NULL;
+    }
+
+    // Releasing pD3D is now the responsibility of the GraphicsPipe destructor
+}
+
+void DXGraphicsStateGuardian9::  
+set_context(DXScreenData *pNewContextData) {
+    // dont do copy from window since dx_init sets fields too.
+    // simpler to keep all of it in one place, so use ptr to window struct
+
+    assert(pNewContextData!=NULL);
+    _pScrn = pNewContextData;
+    _pD3DDevice = _pScrn->pD3DDevice;   //copy this one field for speed of deref
+    _pSwapChain = _pScrn->pSwapChain;   //copy this one field for speed of deref
+ 
+    //wdxdisplay9_cat.debug() << "SwapChain = "<< _pSwapChain << "\n";
+}
+
+void DXGraphicsStateGuardian9::  
+create_swap_chain(DXScreenData *pNewContextData) {
+    // Instead of creating a device and rendering as d3ddevice->present()
+    // we should render using SwapChain->present(). This is done to support
+    // multiple windows rendering. For that purpose, we need to set additional
+    // swap chains here.
+
+    HRESULT hr;
+    hr = pNewContextData->pD3DDevice->CreateAdditionalSwapChain(&pNewContextData->PresParams, &pNewContextData->pSwapChain);
+    if (FAILED(hr))
+      wdxdisplay9_cat.debug() << "Swapchain creation failed :"<<D3DERRORSTRING(hr)<<"\n";
+    //    set_context(pNewContextData);
+}
+
+void DXGraphicsStateGuardian9::  
+release_swap_chain(DXScreenData *pNewContextData) {
+    // Release the swap chain on this DXScreenData
+    pNewContextData->pSwapChain->Release();
+}
+
+bool refill_tex_callback(TextureContext *tc,void *void_dxgsg_ptr) {
+     DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
+//   DXGraphicsStateGuardian9 *dxgsg = (DXGraphicsStateGuardian9 *)void_dxgsg_ptr; not needed?
+
+     // Re-fill the contents of textures and vertex buffers
+     // which just got restored now.
+     HRESULT hr=dtc->FillDDSurfTexturePixels();
+     return hr==S_OK;
+}
+
+bool delete_tex_callback(TextureContext *tc,void *void_dxgsg_ptr) {
+     DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
+
+     // release DDSurf (but not the texture context)
+     dtc->DeleteTexture();
+     return true;
+}
+
+bool recreate_tex_callback(TextureContext *tc,void *void_dxgsg_ptr) {
+     DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
+     DXGraphicsStateGuardian9 *dxgsg = (DXGraphicsStateGuardian9 *)void_dxgsg_ptr;
+
+     // Re-fill the contents of textures and vertex buffers
+     // which just got restored now.
+
+     IDirect3DTexture9 *ddtex = dtc->CreateTexture(*dxgsg->_pScrn);
+     return ddtex!=NULL;
+}
+
+// release all textures and vertex/index buffers
+HRESULT DXGraphicsStateGuardian9::DeleteAllDeviceObjects(void) {
+  // BUGBUG: need to release any vertexbuffers here
+
+  // cant access template in libpanda.dll directly due to vc++ limitations, use traverser to get around it
+
+  // dont call release_all_textures() because we want the panda tex obj around so it can reload its texture
+
+  // bugbug:  do I still need to delete all the textures since they are all D3DPOOL_MANAGED now?
+  traverse_prepared_textures(delete_tex_callback,this);
+
+  if(dxgsg9_cat.is_debug())
+      dxgsg9_cat.debug() << "release of all textures complete\n";
+
+  // delete non-panda-texture/geom DX objects (VBs/textures/shaders)
+  //SAFE_DELSHADER(Vertex,_CurVertexShader,_pD3DDevice);
+  //SAFE_DELSHADER(Pixel,_CurPixelShader,_pD3DDevice);
+  SAFE_RELEASE(_pGlobalTexture);
+
+  assert(_pD3DDevice);
+
+  //SAFE_DELSHADER(Vertex,_CurVertexShader,_pD3DDevice);
+  //SAFE_DELSHADER(Pixel,_CurPixelShader,_pD3DDevice);
+  SAFE_RELEASE(_pGlobalTexture);
+
+  return S_OK;
+}
+
+// recreate all textures and vertex/index buffers
+HRESULT DXGraphicsStateGuardian9::RecreateAllDeviceObjects(void) {
+  // BUGBUG: need to handle vertexbuffer handling here
+
+  // cant access template in libpanda.dll directly due to vc++ limitations, use traverser to get around it
+  traverse_prepared_textures(recreate_tex_callback,this);
+
+  if(dxgsg9_cat.is_debug())
+      dxgsg9_cat.debug() << "recreation of all textures complete\n";
+  return S_OK;
+}
+
+HRESULT DXGraphicsStateGuardian9::ReleaseAllDeviceObjects(void) {
+    // release any D3DPOOL_DEFAULT objects here (currently none)
+    return S_OK;
+}
+
+#if 0
+////////////////////////////////////////////////////////////////////
+//     Function: show_frame
+//       Access:
+//       Description:   redraw primary buffer
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::show_frame(bool bNoNewFrameDrawn) {
+  if(_pD3DDevice==NULL)
+    return;
+
+  //  DO_PSTATS_STUFF(PStatTimer timer(_win->_swap_pcollector));  // this times just the flip, so it must go here in dxgsg, instead of wdxdisplay, which would time the whole frame
+  HRESULT hr;
+
+  if(bNoNewFrameDrawn) {
+      // a new frame has not been rendered, we just want to display the last thing
+      // that was drawn into backbuf, if backbuf is valid
+      if(_pScrn->PresParams.SwapEffect==D3DSWAPEFFECT_DISCARD) {
+          // in DISCARD mode, old backbufs are not guaranteed to have valid pixels,
+          // so we cant copy back->front here.  just give up.
+          return;
+      } else if(_pScrn->PresParams.SwapEffect==D3DSWAPEFFECT_FLIP) {
+         /* bugbug:  here we should use CopyRects here to copy backbuf to front (except in
+                     the case of frames 1 and 2 where we have no valid data in the backbuffer yet,
+                     for those cases give up and return).
+                     not implemented yet since right now we always do discard mode for fullscrn Present()
+                     for speed.
+          */
+          return;
+      }
+
+      // otherwise we have D3DSWAPEFFECT_COPY, so fall-thru to normal Present()
+      // may work ok as long as backbuf hasnt been touched
+  }
+
+  hr = _pD3DDevice->Present((CONST RECT*)NULL,(CONST RECT*)NULL,(HWND)NULL,NULL);
+  if(FAILED(hr)) {
+    if(hr == D3DERR_DEVICELOST) {
+        CheckCooperativeLevel();
+    } else {
+      dxgsg9_cat.error() << "show_frame() - Present() failed" << D3DERRORSTRING(hr);
+      exit(1);
+    }
+  }
+}
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: show_frame
+//       Access:
+//       Description:   redraw primary buffer
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::show_frame(bool bNoNewFrameDrawn) {
+  if(_pD3DDevice==NULL)
+    return;
+
+  //  DO_PSTATS_STUFF(PStatTimer timer(_win->_swap_pcollector));  // this times just the flip, so it must go here in dxgsg, instead of wdxdisplay, which would time the whole frame
+  HRESULT hr;
+
+  if(bNoNewFrameDrawn) {
+      // a new frame has not been rendered, we just want to display the last thing
+      // that was drawn into backbuf, if backbuf is valid
+      if(_pScrn->PresParams.SwapEffect==D3DSWAPEFFECT_DISCARD) {
+          // in DISCARD mode, old backbufs are not guaranteed to have valid pixels,
+          // so we cant copy back->front here.  just give up.
+          return;
+      } else if(_pScrn->PresParams.SwapEffect==D3DSWAPEFFECT_FLIP) {
+         /* bugbug:  here we should use CopyRects here to copy backbuf to front (except in
+                     the case of frames 1 and 2 where we have no valid data in the backbuffer yet,
+                     for those cases give up and return).
+                     not implemented yet since right now we always do discard mode for fullscrn Present()
+                     for speed.
+          */
+          return;
+      }
+
+      // otherwise we have D3DSWAPEFFECT_COPY, so fall-thru to normal Present()
+      // may work ok as long as backbuf hasnt been touched
+  }
+
+  if (_pSwapChain)
+    hr = _pSwapChain->Present((CONST RECT*)NULL,(CONST RECT*)NULL,(HWND)NULL,NULL, 0);
+  else
+    hr = _pD3DDevice->Present((CONST RECT*)NULL,(CONST RECT*)NULL,(HWND)NULL,NULL);
+
+  if(FAILED(hr)) {
+    if(hr == D3DERR_DEVICELOST) {
+        CheckCooperativeLevel();
+    } else {
+      dxgsg9_cat.error() << "show_frame() - Present() failed" << D3DERRORSTRING(hr);
+      exit(1);
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: set_render_target
+//       Access:
+//       Description: Set render target to the backbuffer of
+//                    current swap chain.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::set_render_target() {
+  LPDIRECT3DSURFACE9 pBack=NULL, pStencil=NULL;
+
+  if (!_pSwapChain)  //maybe fullscreen mode or main/single window
+    _pD3DDevice->GetBackBuffer(0, 0,D3DBACKBUFFER_TYPE_MONO,&pBack);
+  else
+    _pSwapChain->GetBackBuffer(0,D3DBACKBUFFER_TYPE_MONO,&pBack);
+
+  //wdxdisplay9_cat.debug() << "swapchain is " << _pSwapChain << "\n";
+  //wdxdisplay9_cat.debug() << "back buffer is " << pBack << "\n";
+
+  _pD3DDevice->GetDepthStencilSurface(&pStencil);
+  _pD3DDevice->SetDepthStencilSurface(pStencil);
+  if (pBack)
+    pBack->Release();
+  if (pStencil)
+    pStencil->Release();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: copy_pres_reset
+//       Access:
+//       Description: copies the PresReset from passed DXScreenData
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::copy_pres_reset(DXScreenData *pScrn) {
+  memcpy(&_PresReset, &_pScrn->PresParams,sizeof(D3DPRESENT_PARAMETERS));
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+//  Function: reset_d3d_device
+//  Access:
+//  Description: This function checks current device's framebuffer dimension against
+//               passed pPresParams backbuffer dimension to determine a device reset
+//               if there is only one window or it is the main window or fullscreen
+//               mode then, it resets the device. Finally it returns the new
+//               DXScreenData through parameter pScrn
+/////////////////////////////////////////////////////////////////////////////////////
+HRESULT DXGraphicsStateGuardian9::
+reset_d3d_device(D3DPRESENT_PARAMETERS *pPresParams, DXScreenData **pScrn) {
+  HRESULT hr;
+
+  assert(IS_VALID_PTR(pPresParams));
+  assert(IS_VALID_PTR(_pScrn->pD3D9));
+  assert(IS_VALID_PTR(_pD3DDevice));
+
+  ReleaseAllDeviceObjects();
+
+  if(!dx_full_screen) {
+      // for windowed make sure out format matches the desktop fmt, in case the
+      // desktop mode has been changed
+
+       _pScrn->pD3D9->GetAdapterDisplayMode(_pScrn->CardIDNum, &_pScrn->DisplayMode);
+       pPresParams->BackBufferFormat = _pScrn->DisplayMode.Format;
+  }
+  // here we have to look at the device's frame buffer dimension
+  // if current window's dimension is bigger than device's frame buffer
+  // we have to reset the device before creating new swapchain.
+  // inorder to reset properly, we need to release all swapchains
+  D3DSURFACE_DESC DeviceDesc;
+  LPDIRECT3DSURFACE9 pDeviceBack;
+  _pD3DDevice->GetBackBuffer(0, 0,D3DBACKBUFFER_TYPE_MONO,&pDeviceBack);
+  pDeviceBack->GetDesc(&DeviceDesc);
+  pDeviceBack->Release();
+  if ( !(_pScrn->pSwapChain)
+       || (DeviceDesc.Width < pPresParams->BackBufferWidth)
+       || (DeviceDesc.Height < pPresParams->BackBufferHeight) ) {
+
+    get_engine()->reset_all_windows(false);// reset old swapchain by releasing
+
+    _PresReset.BackBufferWidth = pPresParams->BackBufferWidth;
+    _PresReset.BackBufferHeight = pPresParams->BackBufferHeight;
+    hr=_pD3DDevice->Reset(&_PresReset);
+    if (FAILED(hr)) {
+      return hr;
+    }
+
+    get_engine()->reset_all_windows(true);// reset with new swapchains by creating
+  }
+  // release the old swapchain and create a new one
+  if (_pScrn->pSwapChain) {
+    _pScrn->pSwapChain->Release();
+    _pScrn->pSwapChain = NULL;
+    hr=_pD3DDevice->CreateAdditionalSwapChain(pPresParams,&_pScrn->pSwapChain);
+  }
+  if(SUCCEEDED(hr)) {
+     if(pPresParams!=&_pScrn->PresParams)
+         memcpy(&_pScrn->PresParams,pPresParams,sizeof(D3DPRESENT_PARAMETERS));
+     if (pScrn)
+       *pScrn = _pScrn;
+  }
+  return hr;
+}
+
+bool DXGraphicsStateGuardian9::
+CheckCooperativeLevel(bool bDoReactivateWindow) {
+  HRESULT hr = _pD3DDevice->TestCooperativeLevel();
+
+  if(SUCCEEDED(hr)) {
+    assert(SUCCEEDED(_last_testcooplevel_result));
+    return true;
+  }
+
+  switch(hr) {
+      case D3DERR_DEVICENOTRESET:
+        _bDXisReady = false;
+        hr=reset_d3d_device(&_pScrn->PresParams);
+        if (FAILED(hr)) {
+          // I think this shouldnt fail unless I've screwed up the PresParams from the original working ones somehow
+          dxgsg9_cat.error()
+            << "CheckCooperativeLevel Reset() failed, hr = " << D3DERRORSTRING(hr);
+          // drose is commenting out this exit() call; it's getting
+          // triggered on some actual client hardware (with
+          // DRIVERINTERNALERROR) but maybe that's ok.
+          //exit(1);
+        }
+    
+        // BUGBUG: is taking this out wrong??
+        /*
+        if(bDoReactivateWindow) {
+           _win->reactivate_window();  //must reactivate window before you can restore surfaces (otherwise you are in WRONGVIDEOMODE, and DDraw RestoreAllSurfaces fails)
+        }
+        */
+
+        hr = _pD3DDevice->TestCooperativeLevel();
+        if(FAILED(hr)) {
+          // internal chk, shouldnt fail
+          dxgsg9_cat.error()
+            << "TestCooperativeLevel following Reset() failed, hr = " << D3DERRORSTRING(hr);
+
+          // drose is commenting out this exit() call; maybe it's ok if the above fails.
+          //exit(1);
+        }
+    
+        _bDXisReady = TRUE;
+        break;
+    
+      case D3DERR_DEVICELOST:
+        if(SUCCEEDED(_last_testcooplevel_result)) {
+          if(_bDXisReady) {
+            //                   _win->deactivate_window();
+            _bDXisReady = false;
+            if(dxgsg9_cat.is_debug())
+              dxgsg9_cat.debug() << "D3D Device was Lost, waiting...\n";
+          }
+        }
+  }
+
+  _last_testcooplevel_result = hr;
+  return SUCCEEDED(hr);
+}
+
+/*
+////////////////////////////////////////////////////////////////////
+//     Function: adjust_view_rect
+//       Access:
+//  Description: we receive the new x and y position of the client
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::adjust_view_rect(int x, int y) {
+    if (_pScrn->view_rect.left != x || _pScrn->view_rect.top != y) {
+
+        _pScrn->view_rect.right = x + RECT_XSIZE(_pScrn->view_rect);
+        _pScrn->view_rect.left = x;
+        _pScrn->view_rect.bottom = y + RECT_YSIZE(_pScrn->view_rect);
+        _pScrn->view_rect.top = y;
+
+//  set_clipper(clip_rect);
+    }
+}
+*/
+
+#if 0
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLGraphicsStateGuardian::save_mipmap_images
+//       Access: Protected
+//  Description: Saves out each mipmap level of the indicated texture
+//               (which must also be the currently active texture in
+//               the GL state) as a separate image file to disk.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::read_mipmap_images(Texture *tex) {
+   Filename filename = tex->get_name();
+   string name;
+   if (filename.empty()) {
+     static index = 0;
+     name = "texture" + format_string(index);
+     index++;
+   } else {
+     name = filename.get_basename_wo_extension();
+   }
+
+   PixelBuffer *pb = tex->get_ram_image();
+   nassertv(pb != (PixelBuffer *)NULL);
+
+   GLenum external_format = get_external_image_format(pb->get_format());
+   GLenum type = get_image_type(pb->get_image_type());
+
+   int xsize = pb->get_xsize();
+   int ysize = pb->get_ysize();
+
+   // Specify byte-alignment for the pixels on output.
+   glPixelStorei(GL_PACK_ALIGNMENT, 1);
+
+   int mipmap_level = 0;
+   do {
+     xsize = max(xsize, 1);
+     ysize = max(ysize, 1);
+
+     PT(PixelBuffer) mpb =
+       new PixelBuffer(xsize, ysize, pb->get_num_components(),
+                       pb->get_component_width(), pb->get_image_type(),
+                       pb->get_format());
+     glGetTexImage(GL_TEXTURE_2D, mipmap_level, external_format,
+                   type, mpb->_image);
+     Filename mipmap_filename = name + "_" + format_string(mipmap_level) + ".pnm";
+     nout << "Writing mipmap level " << mipmap_level
+          << " (" << xsize << " by " << ysize << ") "
+          << mipmap_filename << "\n";
+     mpb->write(mipmap_filename);
+
+     xsize >>= 1;
+     ysize >>= 1;
+     mipmap_level++;
+   } while (xsize > 0 && ysize > 0);
+}
+#endif
+
+
+#if 0
+//-----------------------------------------------------------------------------
+// Name: SetViewMatrix()
+// Desc: Given an eye point, a lookat point, and an up vector, this
+//       function builds a 4x4 view matrix.
+//-----------------------------------------------------------------------------
+HRESULT SetViewMatrix( D3DMATRIX& mat, D3DXVECTOR3& vFrom, D3DXVECTOR3& vAt,
+                       D3DXVECTOR3& vWorldUp ) {
+    // Get the z basis vector, which points straight ahead. This is the
+    // difference from the eyepoint to the lookat point.
+    D3DXVECTOR3 vView = vAt - vFrom;
+
+    float fLength = Magnitude( vView );
+    if (fLength < 1e-6f)
+        return E_INVALIDARG;
+
+    // Normalize the z basis vector
+    vView /= fLength;
+
+    // Get the dot product, and calculate the projection of the z basis
+    // vector onto the up vector. The projection is the y basis vector.
+    float fDotProduct = DotProduct( vWorldUp, vView );
+
+    D3DXVECTOR3 vUp = vWorldUp - fDotProduct * vView;
+
+    // If this vector has near-zero length because the input specified a
+    // bogus up vector, let's try a default up vector
+    if (1e-6f > ( fLength = Magnitude( vUp ) )) {
+        vUp = D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) - vView.y * vView;
+
+        // If we still have near-zero length, resort to a different axis.
+        if (1e-6f > ( fLength = Magnitude( vUp ) )) {
+            vUp = D3DXVECTOR3( 0.0f, 0.0f, 1.0f ) - vView.z * vView;
+
+            if (1e-6f > ( fLength = Magnitude( vUp ) ))
+                return E_INVALIDARG;
+        }
+    }
+
+    // Normalize the y basis vector
+    vUp /= fLength;
+
+    // The x basis vector is found simply with the cross product of the y
+    // and z basis vectors
+    D3DXVECTOR3 vRight = CrossProduct( vUp, vView );
+
+    // Start building the matrix. The first three rows contains the basis
+    // vectors used to rotate the view to point at the lookat point
+    mat._11 = vRight.x;  mat._12 = vUp.x;  mat._13 = vView.x;  mat._14 = 0.0f;
+    mat._21 = vRight.y;  mat._22 = vUp.y;  mat._23 = vView.y;  mat._24 = 0.0f;
+    mat._31 = vRight.z;  mat._32 = vUp.z;  mat._33 = vView.z;  mat._34 = 0.0f;
+
+    // Do the translation values (rotations are still about the eyepoint)
+    mat._41 = - DotProduct( vFrom, vRight );
+    mat._42 = - DotProduct( vFrom, vUp );
+    mat._43 = - DotProduct( vFrom, vView );
+    mat._44 = 1.0f;
+
+    return S_OK;
+}
+
+#endif
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::prepare_geom_node
+//       Access: Public, Virtual
+//  Description: Prepares the indicated GeomNode for retained-mode
+//               rendering.  If this function returns non-NULL, the
+//               value returned will be passed back to a future call
+//               to draw_geom_node(), which is expected to draw the
+//               contents of the node.
+////////////////////////////////////////////////////////////////////
+GeomNodeContext *DXGraphicsStateGuardian9::
+prepare_geom_node(GeomNode *node) {
+  dxgsg9_cat.error() << "prepare_geom_node unimplemented for DX9!\n";
+  return NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::draw_geom_node
+//       Access: Public, Virtual
+//  Description: Draws a GeomNode previously indicated by a call to
+//               prepare_geom_node().
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+draw_geom_node(GeomNode *node, const RenderState *state,
+               GeomNodeContext *gnc) {
+  return;  // unimplemented
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXGraphicsStateGuardian9::release_geom_node
+//       Access: Public, Virtual
+//  Description: Frees the resources previously allocated via a call
+//               to prepare_geom_node(), including deleting the
+//               GeomNodeContext itself, if necessary.
+////////////////////////////////////////////////////////////////////
+void DXGraphicsStateGuardian9::
+release_geom_node(GeomNodeContext *gnc) {
+}
+
+HRESULT CreateDX9Cursor(LPDIRECT3DDEVICE9 pd3dDevice, HCURSOR hCursor,BOOL bAddWatermark) {
+// copied directly from dxsdk SetDeviceCursor
+    HRESULT hr = E_FAIL;
+    ICONINFO iconinfo;
+    LPDIRECT3DSURFACE9 pCursorBitmap = NULL;
+    HDC hdcColor = NULL;
+    HDC hdcMask = NULL;
+    HDC hdcScreen = NULL;
+    BITMAP bm;
+    DWORD dwWidth,dwHeightSrc,dwHeightDest;
+    COLORREF crColor,crMask;
+    UINT x,y;
+    BITMAPINFO bmi;
+    COLORREF* pcrArrayColor = NULL;
+    COLORREF* pcrArrayMask = NULL;
+    DWORD* pBitmap;
+    HGDIOBJ hgdiobjOld;
+    bool bBWCursor;
+
+    ZeroMemory( &iconinfo, sizeof(iconinfo) );
+    if( !GetIconInfo( hCursor, &iconinfo ) )
+        goto End;
+
+    if (0 == GetObject((HGDIOBJ)iconinfo.hbmMask, sizeof(BITMAP), (LPVOID)&bm))
+        goto End;
+    dwWidth = bm.bmWidth;
+    dwHeightSrc = bm.bmHeight;
+
+    if( iconinfo.hbmColor == NULL ) {
+        bBWCursor = true;
+        dwHeightDest = dwHeightSrc / 2;
+    } else {
+        bBWCursor = false;
+        dwHeightDest = dwHeightSrc;
+    }
+
+    // Create a surface for the cursor
+    if( FAILED( hr = pd3dDevice->CreateOffscreenPlainSurface( dwWidth, dwHeightDest,
+        D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pCursorBitmap, NULL ) ) ) {
+        goto End;
+    }
+
+    pcrArrayMask = new DWORD[dwWidth * dwHeightSrc];
+
+    ZeroMemory(&bmi, sizeof(bmi));
+    bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+    bmi.bmiHeader.biWidth = dwWidth;
+    bmi.bmiHeader.biHeight = dwHeightSrc;
+    bmi.bmiHeader.biPlanes = 1;
+    bmi.bmiHeader.biBitCount = 32;
+    bmi.bmiHeader.biCompression = BI_RGB;
+
+    hdcScreen = GetDC( NULL );
+    hdcMask = CreateCompatibleDC( hdcScreen );
+    if( hdcMask == NULL )
+    {
+        hr = E_FAIL;
+        goto End;
+    }
+    hgdiobjOld = SelectObject(hdcMask, iconinfo.hbmMask);
+    GetDIBits(hdcMask, iconinfo.hbmMask, 0, dwHeightSrc,
+        pcrArrayMask, &bmi, DIB_RGB_COLORS);
+    SelectObject(hdcMask, hgdiobjOld);
+
+    if (!bBWCursor)
+    {
+        pcrArrayColor = new DWORD[dwWidth * dwHeightDest];
+        hdcColor = CreateCompatibleDC( GetDC( NULL ) );
+        if( hdcColor == NULL )
+        {
+            hr = E_FAIL;
+            goto End;
+        }
+        SelectObject(hdcColor, iconinfo.hbmColor);
+        GetDIBits(hdcColor, iconinfo.hbmColor, 0, dwHeightDest,
+            pcrArrayColor, &bmi, DIB_RGB_COLORS);
+    }
+
+    // Transfer cursor image into the surface
+    D3DLOCKED_RECT lr;
+    pCursorBitmap->LockRect( &lr, NULL, 0 );
+    pBitmap = (DWORD*)lr.pBits;
+    for( y = 0; y < dwHeightDest; y++ )
+    {
+        for( x = 0; x < dwWidth; x++ )
+        {
+            if (bBWCursor)
+            {
+                crColor = pcrArrayMask[dwWidth*(dwHeightDest-1-y) + x];
+                crMask = pcrArrayMask[dwWidth*(dwHeightSrc-1-y) + x];
+            }
+            else
+            {
+                crColor = pcrArrayColor[dwWidth*(dwHeightDest-1-y) + x];
+                crMask = pcrArrayMask[dwWidth*(dwHeightDest-1-y) + x];
+            }
+            if (crMask == 0)
+                pBitmap[dwWidth*y + x] = 0xff000000 | crColor;
+            else
+                pBitmap[dwWidth*y + x] = 0x00000000;
+
+            // It may be helpful to make the D3D cursor look slightly
+            // different from the Windows cursor so you can distinguish
+            // between the two when developing/testing code.  When
+            // bAddWatermark is TRUE, the following code adds some
+            // small grey "D3D" characters to the upper-left corner of
+            // the D3D cursor image.
+            if( bAddWatermark && x < 12 && y < 5 )
+            {
+                // 11.. 11.. 11.. .... CCC0
+                // 1.1. ..1. 1.1. .... A2A0
+                // 1.1. .1.. 1.1. .... A4A0
+                // 1.1. ..1. 1.1. .... A2A0
+                // 11.. 11.. 11.. .... CCC0
+
+                const WORD wMask[5] = { 0xccc0, 0xa2a0, 0xa4a0, 0xa2a0, 0xccc0 };
+                if( wMask[y] & (1 << (15 - x)) )
+                {
+                    pBitmap[dwWidth*y + x] |= 0xff808080;
+                }
+            }
+        }
+    }
+    pCursorBitmap->UnlockRect();
+
+    // Set the device cursor
+    if( FAILED( hr = pd3dDevice->SetCursorProperties( iconinfo.xHotspot,
+        iconinfo.yHotspot, pCursorBitmap ) ) )
+    {
+        goto End;
+    }
+
+    hr = S_OK;
+
+End:
+    if( iconinfo.hbmMask != NULL )
+        DeleteObject( iconinfo.hbmMask );
+    if( iconinfo.hbmColor != NULL )
+        DeleteObject( iconinfo.hbmColor );
+    if( hdcScreen != NULL )
+        ReleaseDC( NULL, hdcScreen );
+    if( hdcColor != NULL )
+        DeleteDC( hdcColor );
+    if( hdcMask != NULL )
+        DeleteDC( hdcMask );
+
+    SAFE_DELETE_ARRAY( pcrArrayColor );
+    SAFE_DELETE_ARRAY( pcrArrayMask );
+    RELEASE(pCursorBitmap,dxgsg9,"pCursorBitmap",RELEASE_ONCE);
+    return hr;
+}
+
+#ifdef _DEBUG
+// defns for print formatting in debugger
+typedef struct {
+  float x,y,z;
+  float nx,ny,nz;
+  D3DCOLOR diffuse;
+  float u,v;
+} POS_NORM_COLOR_TEX_VERTEX;
+
+typedef struct {
+  float x,y,z;
+  D3DCOLOR diffuse;
+  float u,v;
+} POS_COLOR_TEX_VERTEX;
+
+typedef struct {
+  float x,y,z;
+  float u,v;
+} POS_TEX_VERTEX;
+
+// define junk vars so symbols are included in dbginfo
+POS_TEX_VERTEX junk11;
+POS_COLOR_TEX_VERTEX junk22;
+POS_NORM_COLOR_TEX_VERTEX junk33;
+#endif
+

+ 386 - 0
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -0,0 +1,386 @@
+// Filename: dxGraphicsStateGuardian.h
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXGRAPHICSSTATEGUARDIAN_H
+#define DXGRAPHICSSTATEGUARDIAN_H
+
+//#define GSG_VERBOSE 1
+
+#include "dxgsg9base.h"
+#include "dxGeomNodeContext9.h"
+#include "dxTextureContext9.h"
+#include "d3dfont9.h"
+
+#include "graphicsStateGuardian.h"
+#include "geomprimitives.h"
+#include "texture.h"
+#include "pixelBuffer.h"
+#include "displayRegion.h"
+#include "material.h"
+#include "depthTestAttrib.h"
+#include "renderModeAttrib.h"
+#include "textureApplyAttrib.h"
+#include "fog.h"
+#include "pointerToArray.h"
+
+//#define USE_VERTEX_SHADERS
+
+class Light;
+
+//#if defined(NOTIFY_DEBUG) || defined(DO_PSTATS)
+//#ifdef _DEBUG
+// is there something in DX9 to replace this?
+#if 0
+// This function now serves both to print a debug message to the
+// console, as well as to notify PStats about the change in texture
+// memory.  Thus, we compile it in if we are building with support for
+// either notify debug messages or PStats; otherwise, we compile it
+// out.
+extern void dbgPrintVidMem(LPDIRECTDRAW7 pDD, LPDDSCAPS2 lpddsCaps,const char *pMsg);
+#define PRINTVIDMEM(pDD,pCaps,pMsg) dbgPrintVidMem(pDD,pCaps,pMsg)
+#else
+#define PRINTVIDMEM(pDD,pCaps,pMsg)
+#endif
+
+////////////////////////////////////////////////////////////////////
+//   Class : DXGraphicsStateGuardian9
+// Description : A GraphicsStateGuardian specialized for rendering
+//               into DX.  There should be no DX calls
+//               outside of this object.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXGraphicsStateGuardian9 : public GraphicsStateGuardian {
+  friend class wdxGraphicsWindow9;
+  friend class wdxGraphicsPipe9;
+  friend class wdxGraphicsWindowGroup9;
+  friend class DXTextureContext9;
+
+public:
+  DXGraphicsStateGuardian9(const FrameBufferProperties &properties);
+  ~DXGraphicsStateGuardian9();
+
+  virtual void reset();
+
+  virtual void do_clear(const RenderBuffer &buffer);
+
+  virtual void prepare_display_region();
+  virtual bool prepare_lens();
+
+  virtual void draw_point(GeomPoint *geom, GeomContext *gc);
+  virtual void draw_line(GeomLine *geom, GeomContext *gc);
+  virtual void draw_linestrip(GeomLinestrip *geom, GeomContext *gc);
+  void draw_linestrip_base(Geom *geom, GeomContext *gc, bool bConnectEnds);
+  virtual void draw_sprite(GeomSprite *geom, GeomContext *gc);
+  virtual void draw_polygon(GeomPolygon *geom, GeomContext *gc);
+  virtual void draw_quad(GeomQuad *geom, GeomContext *gc);
+  virtual void draw_tri(GeomTri *geom, GeomContext *gc);
+  virtual void draw_tristrip(GeomTristrip *geom, GeomContext *gc);
+  virtual void draw_trifan(GeomTrifan *geom, GeomContext *gc);
+  virtual void draw_sphere(GeomSphere *geom, GeomContext *gc);
+
+  virtual GeomNodeContext *prepare_geom_node(GeomNode *node);
+  virtual void draw_geom_node(GeomNode *node, const RenderState *state,
+                              GeomNodeContext *gnc);
+  virtual void release_geom_node(GeomNodeContext *gnc);
+
+  virtual TextureContext *prepare_texture(Texture *tex);
+  virtual void apply_texture(TextureContext *tc);
+  virtual void release_texture(TextureContext *tc);
+
+  virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr);
+  virtual void copy_texture(TextureContext *tc, const DisplayRegion *dr,
+                            const RenderBuffer &rb);
+
+  virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb);
+  virtual void texture_to_pixel_buffer(TextureContext *tc, PixelBuffer *pb,
+                const DisplayRegion *dr);
+
+  virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr);
+  virtual void copy_pixel_buffer(PixelBuffer *pb, const DisplayRegion *dr,
+                                 const RenderBuffer &rb);
+
+  virtual void apply_material(const Material *material);
+  virtual void apply_fog(Fog *fog);
+
+  virtual void issue_transform(const TransformState *transform);
+  virtual void issue_tex_matrix(const TexMatrixAttrib *attrib);
+  virtual void issue_texture(const TextureAttrib *attrib);
+  virtual void issue_material(const MaterialAttrib *attrib);
+  virtual void issue_render_mode(const RenderModeAttrib *attrib);
+  virtual void issue_texture_apply(const TextureApplyAttrib *attrib);
+  virtual void issue_alpha_test(const AlphaTestAttrib *attrib);
+  virtual void issue_depth_test(const DepthTestAttrib *attrib);
+  virtual void issue_depth_write(const DepthWriteAttrib *attrib);
+  virtual void issue_color_write(const ColorWriteAttrib *attrib);
+  virtual void issue_cull_face(const CullFaceAttrib *attrib);
+  virtual void issue_fog(const FogAttrib *attrib);
+  virtual void issue_depth_offset(const DepthOffsetAttrib *attrib);
+
+  virtual void bind_light(PointLight *light, int light_id);
+  virtual void bind_light(DirectionalLight *light, int light_id);
+  virtual void bind_light(Spotlight *light, int light_id);
+
+  virtual bool begin_frame();
+  virtual bool begin_scene();
+  virtual void end_scene();
+  virtual void end_frame();
+
+  virtual bool wants_normals(void) const;
+  virtual bool wants_texcoords(void) const;
+
+  virtual bool depth_offset_decals();
+
+  virtual CoordinateSystem get_internal_coordinate_system() const;
+  INLINE float compute_distance_to(const LPoint3f &point) const;
+  virtual void set_color_clear_value(const Colorf& value);
+
+public:
+  // recreate_tex_callback needs these to be public
+  DXScreenData *_pScrn;
+  LPDIRECT3DDEVICE9 _pD3DDevice;  // same as pScrn->_pD3DDevice, cached for spd
+  IDirect3DSwapChain9 *_pSwapChain;
+  D3DPRESENT_PARAMETERS _PresReset;  // This is built during reset device
+
+protected:
+  virtual void enable_lighting(bool enable);
+  virtual void set_ambient_light(const Colorf &color);
+  virtual void enable_light(int light_id, bool enable);
+
+  virtual bool slot_new_clip_plane(int plane_id);
+  virtual void enable_clip_plane(int plane_id, bool enable);
+  virtual void bind_clip_plane(PlaneNode *plane, int plane_id);
+
+  virtual void set_blend_mode(ColorWriteAttrib::Mode color_write_mode,
+                              ColorBlendAttrib::Mode color_blend_mode,
+                              TransparencyAttrib::Mode transparency_mode);
+
+  void free_nondx_resources();            // free local internal buffers
+  void free_d3d_device(void);
+  virtual PT(SavedFrameBuffer) save_frame_buffer(const RenderBuffer &buffer,
+                         CPT(DisplayRegion) dr);
+  virtual void restore_frame_buffer(SavedFrameBuffer *frame_buffer);
+
+  void set_draw_buffer(const RenderBuffer &rb);
+  void set_read_buffer(const RenderBuffer &rb);
+
+  INLINE void add_to_FVFBuf(void *data,  size_t bytes) ;
+
+  bool                  _bDXisReady;
+  HRESULT               _last_testcooplevel_result;
+  DXTextureContext9  *_pCurTexContext;
+
+  bool              _bTransformIssued;  // decaling needs to tell when a transform has been issued
+  D3DMATRIX         _SavedTransform;
+
+  RenderBuffer::Type _cur_read_pixel_buffer;  // source for copy_pixel_buffer operation
+
+  void GenerateSphere(void *pVertexSpace,DWORD dwVertSpaceByteSize,
+                    void *pIndexSpace,DWORD dwIndexSpaceByteSize,
+                    D3DXVECTOR3 *pCenter, float fRadius,
+                    DWORD wNumRings, DWORD wNumSections, float sx, float sy, float sz,
+                    DWORD *pNumVertices,DWORD *pNumTris,DWORD fvfFlags,DWORD dwVertSize);
+  HRESULT ReleaseAllDeviceObjects(void);
+  HRESULT RecreateAllDeviceObjects(void);
+  HRESULT DeleteAllDeviceObjects(void);
+
+/*
+  INLINE void enable_multisample_alpha_one(bool val);
+  INLINE void enable_multisample_alpha_mask(bool val);
+  INLINE void enable_multisample(bool val);
+*/
+
+  INLINE void enable_color_material(bool val);
+  INLINE void enable_fog(bool val);
+  INLINE void enable_zwritemask(bool val);
+  INLINE void set_color_writemask(UINT color_writemask);
+  INLINE void enable_gouraud_shading(bool val);
+  INLINE void set_vertex_format(DWORD NewFvfType);
+
+  INLINE D3DTEXTUREADDRESS get_texture_wrap_mode(Texture::WrapMode wm) const;
+  INLINE D3DFOGMODE get_fog_mode_type(Fog::Mode m) const;
+
+  INLINE void enable_primitive_clipping(bool val);
+  INLINE void enable_alpha_test(bool val);
+  INLINE void enable_line_smooth(bool val);
+  INLINE void enable_blend(bool val);
+  INLINE void enable_point_smooth(bool val);
+  INLINE void enable_texturing(bool val);
+  INLINE void call_dxLightModelAmbient(const Colorf& color);
+  INLINE void call_dxAlphaFunc(D3DCMPFUNC func, float refval);
+  INLINE void call_dxBlendFunc(D3DBLEND sfunc, D3DBLEND dfunc);
+  INLINE void enable_dither(bool val);
+  INLINE void enable_stencil_test(bool val);
+  void report_texmgr_stats();
+  void draw_multitri(Geom *geom, D3DPRIMITIVETYPE tri_id);
+
+  void draw_prim_inner_loop(int nVerts, const Geom *geom, ushort perFlags);
+  void draw_prim_inner_loop_coordtexonly(int nVerts, const Geom *geom);
+  size_t draw_prim_setup(const Geom *geom) ;
+
+  //   for drawing primitives
+  Normalf   p_normal;  // still used to hold G_OVERALL, G_PER_PRIM values
+  TexCoordf p_texcoord;
+  D3DCOLOR  _curD3Dcolor;
+  DWORD     _perPrim,_perVertex,_perComp;   //  these hold DrawLoopFlags bitmask values
+  DWORD     _CurFVFType;
+  // for storage of the flexible vertex format
+  BYTE *_pCurFvfBufPtr,*_pFvfBufBasePtr;
+  WORD *_index_buf;  // base of malloced array
+
+  D3DCOLOR _scene_graph_color_D3DCOLOR;
+  D3DCOLOR _d3dcolor_clear_value;
+//  D3DSHADEMODE _CurShadeMode;
+  bool _bGouraudShadingOn;
+  UINT _color_writemask;
+  bool _bDrawPrimDoSetupVertexBuffer;       // if true, draw methods just copy vertex data into pCurrentGeomContext
+  DXGeomNodeContext9 *_pCurrentGeomContext;  // used in vertex buffer setup
+
+  // iterators for primitives
+  Geom::VertexIterator vi;
+  Geom::NormalIterator ni;
+  Geom::TexCoordIterator ti;
+  Geom::ColorIterator ci;
+
+  // these are used for fastpaths that bypass the iterators above
+  // pointers to arrays in current geom, used to traverse indexed and non-indexed arrays
+  Vertexf *_coord_array,*_pCurCoord;
+  ushort *_coordindex_array,*_pCurCoordIndex;
+
+  TexCoordf *_texcoord_array,*_pCurTexCoord;
+  ushort *_texcoordindex_array,*_pCurTexCoordIndex;
+
+/*
+  PTA_Normalf _norms;
+  PTA_Colorf _colors;
+  PTA_ushort _cindexes,_nindexes;
+*/
+
+  Colorf _lmodel_ambient;
+  float _material_ambient;
+  float _material_diffuse;
+  float _material_specular;
+  float _material_shininess;
+  float _material_emission;
+
+  typedef enum {None,
+                PerVertexFog=D3DRS_FOGVERTEXMODE,
+                PerPixelFog=D3DRS_FOGTABLEMODE
+               } DxgsgFogType;
+  DxgsgFogType _doFogType;
+  bool _fog_enabled;
+/*
+  TODO: cache fog state
+  float _fog_start,_fog_end,_fog_density,float _fog_color;
+*/
+
+  float      _alpha_func_refval;  // d3d stores UINT, panda stores this as float.  we store float
+  D3DCMPFUNC _alpha_func;
+
+  D3DBLEND _blend_source_func;
+  D3DBLEND _blend_dest_func;
+
+  bool _line_smooth_enabled;
+  bool _color_material_enabled;
+  bool _texturing_enabled;
+  bool _clipping_enabled;
+  bool _dither_enabled;
+  bool _stencil_test_enabled;
+  bool _blend_enabled;
+  bool _depth_test_enabled;
+  bool _depth_write_enabled;
+  bool _alpha_test_enabled;
+  DWORD _clip_plane_bits;
+
+  RenderModeAttrib::Mode _current_fill_mode;  //poinr/wireframe/solid
+
+  // unused right now
+  //GraphicsChannel *_panda_gfx_channel;  // cache the 1 channel dx supports
+
+  // Cur Texture State
+  TextureApplyAttrib::Mode _CurTexBlendMode;
+  D3DTEXTUREFILTERTYPE _CurTexMagFilter,_CurTexMinFilter,_CurTexMipFilter;
+  DWORD _CurTexAnisoDegree;
+  Texture::WrapMode _CurTexWrapModeU,_CurTexWrapModeV;
+  LMatrix4f _current_projection_mat;
+  int _projection_mat_stack_count;
+
+  CPT(DisplayRegion) _actual_display_region;
+
+  // Color/Alpha Matrix Transition stuff
+  INLINE void transform_color(Colorf &InColor,D3DCOLOR &OutColor);
+
+  bool _overlay_windows_supported;
+
+#if 0
+  // This is here just as a temporary hack so this file will still
+  // compile.  However, it is never initialized and will certainly
+  // cause the code to crash when it is referenced.  (This used to be
+  // inherited from the base class, but the new design requires that a
+  // GSG may be used for multiple windows, so it doesn't make sense to
+  // store a single window pointer any more.)
+  GraphicsWindow *_win;
+#endif
+
+public:
+  static GraphicsStateGuardian*
+  make_DXGraphicsStateGuardian9(const FactoryParams &params);
+  void set_context(DXScreenData *pNewContextData);
+  void set_render_target();
+
+  static TypeHandle get_class_type(void);
+  static void init_type(void);
+  virtual TypeHandle get_type(void) const;
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  INLINE void SetDXReady(bool status)  { _bDXisReady = status; }
+  INLINE bool GetDXReady(void)  { return _bDXisReady;}
+  void DXGraphicsStateGuardian9::SetTextureBlendMode(TextureApplyAttrib::Mode TexBlendMode,bool bJustEnable);
+
+  // Shader Stuff
+  IDirect3DVertexShader9* read_vertex_shader(string &filename);
+  IDirect3DPixelShader9* read_pixel_shader(string &filename);
+  void init_vertex_shader(IDirect3DVertexShader9* &hShader,string *pFname);
+  void init_pixel_shader(IDirect3DPixelShader9* &hShader,string *pFname);
+  IDirect3DTexture9 *_pGlobalTexture;  // used for FX
+  //DXShaderHandle _CurVertexShader,_CurPixelShader;
+  IDirect3DVertexShader9* _CurVertexShader;
+  IDirect3DPixelShader9* _CurPixelShader;
+
+  void  dx_cleanup(bool bRestoreDisplayMode,bool bAtExitFnCalled);
+  void reset_panda_gsg(void);
+  HRESULT reset_d3d_device(D3DPRESENT_PARAMETERS *pPresParams, DXScreenData **pScrn=NULL);
+
+  #define DO_REACTIVATE_WINDOW true
+  bool CheckCooperativeLevel(bool bDoReactivateWindow = false);
+
+  void show_frame(bool bNoNewFrameDrawn = false);
+  void dx_init(void);
+
+  void support_overlay_window(bool flag);
+
+  void create_swap_chain (DXScreenData *pNewContextData);
+  void release_swap_chain (DXScreenData *pNewContextData);
+  void copy_pres_reset(DXScreenData *pNewContextData);
+
+private:
+  static TypeHandle _type_handle;
+};
+
+HRESULT CreateDX9Cursor(LPDIRECT3DDEVICE9 pd3dDevice, HCURSOR hCursor,BOOL bAddWatermark);
+
+#include "DXGraphicsStateGuardian9.I"
+#endif
+

+ 276 - 0
panda/src/dxgsg9/dxInput9.cxx

@@ -0,0 +1,276 @@
+// Filename: dxInput8.cxx
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "config_wdxdisplay9.h"
+#include <dxInput9.h>
+
+#define AXIS_RESOLUTION 2000   // use this many levels of resolution by default (could be more if needed and device supported it)
+#define AXIS_RANGE_CENTERED    // if defined, axis range is centered on 0, instead of starting on 0
+
+BOOL CALLBACK EnumGameCtrlsCallback( const DIDEVICEINSTANCE* pdidInstance,
+                                     VOID* pContext ) {
+    DI_DeviceInfos *pDevInfos = (DI_DeviceInfos *)pContext;
+
+    (*pDevInfos).push_back(*pdidInstance);
+
+    if(wdxdisplay_cat.is_debug())
+        wdxdisplay_cat.debug() << "Found DevType 0x" << (void*)pdidInstance->dwDevType << ": " << pdidInstance->tszInstanceName << ": " << pdidInstance->tszProductName <<endl;
+
+    return DIENUM_CONTINUE;
+}
+
+extern BOOL CALLBACK EnumObjectsCallbackJoystick(const DIDEVICEOBJECTINSTANCE* pdidoi,VOID* pContext);
+
+DInput9Info::DInput9Info() {
+    _pDInput9 = NULL;
+    _hDInputDLL = NULL;
+    _JoystickPollTimer = NULL;
+}
+
+DInput9Info::~DInput9Info() {
+  for(UINT i=0;i<_DeviceList.size();i++) {
+      _DeviceList[i]->Unacquire();
+      SAFE_RELEASE(_DeviceList[i]);
+  }
+
+  // bugbug: need to handle this
+  // if(_JoystickPollTimer!=NULL)
+  //   KillTimer(...)
+
+  SAFE_RELEASE(_pDInput9);
+  if(_hDInputDLL) {
+      FreeLibrary(_hDInputDLL);
+      _hDInputDLL=NULL;
+  }
+}
+
+bool DInput9Info::InitDirectInput(void) {
+    HRESULT hr;
+
+    // assumes dx9 exists
+    // use dynamic load so non-dinput programs dont have to load dinput
+    #define DLLNAME "dinput9.dll"
+    #define DINPUTCREATE "DirectInput9Create"
+
+    HINSTANCE _hDInputDLL = LoadLibrary(DLLNAME);
+    if(_hDInputDLL == 0) {
+        wdxdisplay_cat.fatal() << "LoadLibrary(" << DLLNAME <<") failed!, error=" << GetLastError() << endl;
+        exit(1);
+    }
+
+    typedef HRESULT (WINAPI * LPDIRECTINPUT9CREATE)(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID *ppvOut, LPUNKNOWN punkOuter);
+    LPDIRECTINPUT9CREATE pDInputCreate9;
+
+    pDInputCreate9 = (LPDIRECTINPUT9CREATE) GetProcAddress(_hDInputDLL,DINPUTCREATE);
+    if(pDInputCreate9 == NULL) {
+        wdxdisplay_cat.fatal() << "GetProcAddr failed for " << DINPUTCREATE << endl;
+        exit(1);
+    }
+
+    // Register with the DirectInput subsystem and get a pointer
+    // to a IDirectInput interface we can use.
+    // Create a DInput object
+    if( FAILED( hr = (*pDInputCreate9)(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
+                                         IID_IDirectInput9, (VOID**)&_pDInput9, NULL ) ) ) {
+        wdxdisplay_cat.error() << DINPUTCREATE << "failed" << D3DERRORSTRING(hr);
+        return false;
+    }
+
+    // enum all the joysticks,etc  (but not keybd/mouse)
+    if( FAILED( hr = _pDInput9->EnumDevices(DI9DEVCLASS_GAMECTRL,
+                                         EnumGameCtrlsCallback,
+                                         (LPVOID)&_DevInfos, DIEDFL_ATTACHEDONLY ) ) ) {
+        wdxdisplay_cat.error() << "EnumDevices failed" << D3DERRORSTRING(hr);
+        return false;
+    }
+
+    return true;
+}
+
+bool DInput9Info::CreateJoystickOrPad(HWND hWnd) {
+    bool bFoundDev = false;
+    UINT devnum=0;
+    char *errstr=NULL;
+
+    // look through the list for the first joystick or gamepad
+    for(;devnum<_DevInfos.size();devnum++) {
+        DWORD devType = GET_DIDEVICE_TYPE(_DevInfos[devnum].dwDevType);
+        if((devType==DI9DEVTYPE_GAMEPAD) ||(devType==DI9DEVTYPE_JOYSTICK)) {
+          bFoundDev=true;
+          break;
+        }
+    }
+
+    if(!bFoundDev) {
+        wdxdisplay_cat.error() << "Cant find an attached Joystick or GamePad!\n";
+        return false;
+    }
+
+    LPDIRECTINPUTDEVICE9 pJoyDevice;
+
+    // Obtain an interface to the enumerated joystick.
+    HRESULT hr = _pDInput9->CreateDevice(_DevInfos[devnum].guidInstance, &pJoyDevice, NULL );
+    if(FAILED(hr)) {
+        errstr="CreateDevice";
+        goto handle_error;
+    }
+
+    assert(pJoyDevice!=NULL);
+    _DeviceList.push_back(pJoyDevice);
+
+    // Set the data format to "simple joystick" - a predefined data format
+    //
+    // A data format specifies which controls on a device we are interested in,
+    // and how they should be reported. This tells DInput that we will be
+    // passing a DIJOYSTATE2 structure to IDirectInputDevice::GetDeviceState().
+    hr = pJoyDevice->SetDataFormat(&c_dfDIJoystick2);
+    if(FAILED(hr)) {
+        errstr="SetDataFormat";
+        goto handle_error;
+    }
+
+    // must be called AFTER SetDataFormat to get all the proper flags
+    DX_DECLARE_CLEAN(DIDEVCAPS, DIDevCaps);
+    hr = pJoyDevice->GetCapabilities(&DIDevCaps);
+    assert(SUCCEEDED(hr));
+
+    _DevCaps.push_back(DIDevCaps);
+
+    if(wdxdisplay_cat.is_debug())
+        wdxdisplay_cat.debug() << "Joy/Pad has " << DIDevCaps.dwAxes << " Axes, " <<  DIDevCaps.dwButtons << " Buttons, " <<  DIDevCaps.dwPOVs << " POVs" << endl;
+
+    // Set the cooperative level to let DInput know how this device should
+    // interact with the system and with other DInput applications.
+    hr = pJoyDevice->SetCooperativeLevel( hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND);
+    if(FAILED(hr)) {
+        errstr="SetCooperativeLevel";
+        goto handle_error;
+    }
+
+    // set the min/max values property for discovered axes.
+    hr = pJoyDevice->EnumObjects(EnumObjectsCallbackJoystick, (LPVOID)pJoyDevice, DIDFT_AXIS);
+    if(FAILED(hr)) {
+        errstr="EnumObjects";
+        goto handle_error;
+    }
+
+    return true;
+
+  handle_error:
+    wdxdisplay_cat.error() << errstr << " failed for (" << _DevInfos[devnum].tszInstanceName << ":" << _DevInfos[devnum].tszProductName << ")" << D3DERRORSTRING(hr);
+    return false;
+}
+
+//-----------------------------------------------------------------------------
+// Name: EnumObjectsCallback()
+// Desc: Callback function for enumerating objects (axes, buttons, POVs) on a
+//       joystick. This function enables user interface elements for objects
+//       that are found to exist, and scales axes min/max values.
+//-----------------------------------------------------------------------------
+BOOL CALLBACK EnumObjectsCallbackJoystick( const DIDEVICEOBJECTINSTANCE* pdidoi,
+                                   VOID* pContext ) {
+
+    LPDIRECTINPUTDEVICE9 pJoyDevice = (LPDIRECTINPUTDEVICE9) pContext;
+    HRESULT hr;
+
+    // For axes that are returned, set the DIPROP_RANGE property for the
+    // enumerated axis in order to scale min/max values.
+    if( pdidoi->dwType & DIDFT_AXIS ) {
+        DIPROPRANGE diprg;
+        diprg.diph.dwSize       = sizeof(DIPROPRANGE);
+        diprg.diph.dwHeaderSize = sizeof(DIPROPHEADER);
+        diprg.diph.dwHow        = DIPH_BYID;
+        diprg.diph.dwObj        = pdidoi->dwType; // Specify the enumerated axis
+
+     #ifdef AXIS_RANGE_CENTERED
+        diprg.lMin              = -AXIS_RESOLUTION/2;
+        diprg.lMax              = +AXIS_RESOLUTION/2;
+     #else
+        diprg.lMin              = 0;
+        diprg.lMax              = +AXIS_RESOLUTION;
+     #endif
+
+        // Set the range for the axis
+        hr = pJoyDevice->SetProperty( DIPROP_RANGE, &diprg.diph);
+        if(FAILED(hr)) {
+          wdxdisplay_cat.error() << "SetProperty on axis failed" << D3DERRORSTRING(hr);
+          return DIENUM_STOP;
+        }
+    }
+
+    return DIENUM_CONTINUE;
+}
+
+bool DInput9Info::ReadJoystick(int devnum, DIJOYSTATE2 &js) {
+    LPDIRECTINPUTDEVICE9 pJoystick = _DeviceList[devnum];
+    assert(pJoystick!=NULL);
+    HRESULT hr;
+    char *errstr;
+
+    // Poll the device to read the current state
+
+    hr = pJoystick->Poll();
+
+    if( FAILED(hr) ) {
+        // DInput is telling us that the input stream has been
+        // interrupted. We aren't tracking any state between polls, so
+        // we don't have any special reset that needs to be done. We
+        // just re-acquire and try again.
+
+        if((hr==DIERR_NOTACQUIRED)||(hr == DIERR_INPUTLOST)) {
+            hr = pJoystick->Acquire();
+
+            if(FAILED(hr)) {
+                if(wdxdisplay_cat.is_spam())
+                   wdxdisplay_cat.spam() << "Acquire failed" << D3DERRORSTRING(hr);
+
+                // hr may be DIERR_OTHERAPPHASPRIO or other errors.  This
+                // may occur when the app is minimized or in the process of
+                // switching, so just try again later
+                return false;
+            }
+
+            hr = pJoystick->Poll();
+            if(FAILED(hr)) {
+                // should never happen!
+                errstr = "Poll after successful Acquire failed";
+                goto handle_error;
+            }
+        } else {
+            errstr =  "Unknown Poll failure";
+            goto handle_error;
+        }
+    }
+
+    // should we make a vector of devstate dataformats to generalize this fn for all device types?
+
+    // Get the input's device state
+    hr = pJoystick->GetDeviceState( sizeof(DIJOYSTATE2), &js);
+    if(FAILED(hr)) {
+        errstr =  "GetDeviceState failed";
+        goto handle_error;
+    }
+
+    return true;
+
+  handle_error:
+     wdxdisplay_cat.fatal() << errstr << D3DERRORSTRING(hr);
+     return false;
+}
+
+

+ 45 - 0
panda/src/dxgsg9/dxInput9.h

@@ -0,0 +1,45 @@
+// Filename: dxInput8.h
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXINPUT9_H
+#define DXINPUT9_H
+
+#define DIRECTINPUT_VERSION 0x800
+#include <dinput.h>
+typedef vector<DIDEVICEINSTANCE> DI_DeviceInfos;
+typedef vector<DIDEVICEOBJECTINSTANCE> DI_DeviceObjInfos;
+
+class DInput9Info {
+public:
+ DInput9Info(void);
+ ~DInput9Info(void);
+ bool InitDirectInput(void);
+ bool CreateJoystickOrPad(HWND hWnd);
+ bool ReadJoystick(int devnum, DIJOYSTATE2 &js);
+
+ HINSTANCE _hDInputDLL;
+ UINT_PTR  _JoystickPollTimer;
+ LPDIRECTINPUT8 _pDInput9;
+ DI_DeviceInfos _DevInfos;
+ // arrays for all created devices.  Should probably put these together in a struct,
+ // along with the data fmt info
+ vector<LPDIRECTINPUTDEVICE8> _DeviceList;
+ vector<DIDEVCAPS> _DevCaps;
+};
+
+#endif

+ 39 - 0
panda/src/dxgsg9/dxSavedFrameBuffer9.I

@@ -0,0 +1,39 @@
+// Filename: dxSavedFrameBuffer8.I
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXSavedFrameBuffer9::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DXSavedFrameBuffer9::
+DXSavedFrameBuffer9(const RenderBuffer &buffer, CPT(DisplayRegion) dr) :
+  SavedFrameBuffer(buffer, dr)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXSavedFrameBuffer9::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE DXSavedFrameBuffer9::
+~DXSavedFrameBuffer9() {
+}
+

+ 21 - 0
panda/src/dxgsg9/dxSavedFrameBuffer9.cxx

@@ -0,0 +1,21 @@
+// Filename: dxSavedFrameBuffer8.cxx
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "dxSavedFrameBuffer9.h"
+
+TypeHandle DXSavedFrameBuffer9::_type_handle;

+ 64 - 0
panda/src/dxgsg9/dxSavedFrameBuffer9.h

@@ -0,0 +1,64 @@
+// Filename: dxSavedFrameBuffer8.h
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXSAVEDFRAMEBUFFER9_H
+#define DXSAVEDFRAMEBUFFER9_H
+
+#include "pandabase.h"
+
+#include <savedFrameBuffer.h>
+#include <texture.h>
+#include <textureContext.h>
+#include <pixelBuffer.h>
+
+
+////////////////////////////////////////////////////////////////////
+//   Class : DXSavedFrameBuffer9
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXSavedFrameBuffer9 : public SavedFrameBuffer {
+public:
+  INLINE DXSavedFrameBuffer9(const RenderBuffer &buffer,
+                CPT(DisplayRegion) dr);
+  INLINE ~DXSavedFrameBuffer9();
+
+  PT(Texture) _back_rgba;
+  PT(PixelBuffer) _depth;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    SavedFrameBuffer::init_type();
+    register_type(_type_handle, "DXSavedFrameBuffer9",
+          SavedFrameBuffer::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "DXSavedFrameBuffer9.I"
+
+#endif
+

+ 1628 - 0
panda/src/dxgsg9/dxTextureContext9.cxx

@@ -0,0 +1,1628 @@
+// Filename: dxTextureContext8.cxx
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include <assert.h>
+#include <time.h>
+#include "dxTextureContext9.h"
+#include "config_dxgsg9.h"
+#include "dxGraphicsStateGuardian9.h"
+//#include "pnmImage.h"
+#include "d3dx9tex.h"
+
+//#define FORCE_16bpp_1555
+static const DWORD g_LowByteMask = 0x000000FF;
+
+#define PANDA_BGRA_ORDER
+
+#ifdef PANDA_BGRA_ORDER
+// assume Panda uses byte-order BGRA/LA to store pixels, which when read into little-endian word is ARGB/AL
+// these macros GET from PixelBuffer, (wont work from DDSurface)
+#define GET_RED_BYTE(PIXEL_DWORD)  ((BYTE)((PIXEL_DWORD >> 16) & g_LowByteMask))
+#define GET_BLUE_BYTE(PIXEL_DWORD) ((BYTE)((PIXEL_DWORD)       & g_LowByteMask))
+#else
+// otherwise Panda uses int ABGR (big-endian RGBA order), (byte-order RGBA or RGB)
+#define GET_RED_BYTE(PIXEL_DWORD)  ((BYTE)(PIXEL_DWORD         & g_LowByteMask))
+#define GET_BLUE_BYTE(PIXEL_DWORD) ((BYTE)((PIXEL_DWORD >> 16) & g_LowByteMask))
+#endif
+
+#define GET_GREEN_BYTE(PIXEL_DWORD) ((BYTE)((PIXEL_DWORD >> 8) & g_LowByteMask))
+#define GET_ALPHA_BYTE(PIXEL_DWORD) ((BYTE)(((DWORD)PIXEL_DWORD) >> 24))  // unsigned >> shifts in 0's, so dont need to mask off upper bits
+
+#ifdef DO_CUSTOM_CONVERSIONS
+typedef enum {
+    None,Conv32to32,Conv32to32_NoAlpha,Conv32to24,Conv32to16_X555,
+    Conv32to16_1555,Conv32to16_0565,Conv32to16_4444,Conv24to32,Conv24to24,
+    Conv24to16_X555,Conv24to16_0565,ConvLum16to16_1555,ConvLum16to16_4444,
+    ConvLum16to32,ConvLum16to16,ConvLum8to8,ConvLum8to24,ConvLum8to32,ConvLum8to16_X555,ConvLum8to16_0565,ConvLum8to16_A8L8,
+    ConvAlpha8to16_4444,ConvAlpha8to32,ConvAlpha8to8,ConvAlpha8to16_A8L8
+} ConversionType;
+
+#ifndef NDEBUG
+char *ConvNameStrs[] = {"None","Conv32to32","Conv32to32_NoAlpha","Conv32to24","Conv32to16_X555",
+    "Conv32to16_1555","Conv32to16_0565","Conv32to16_4444","Conv24to32","Conv24to24","Conv24to16_X555","Conv24to16_0565",
+    "ConvLum16to16_1555","ConvLum16to16_4444","ConvLum16to32","ConvLum16to16","ConvLum8to8","ConvLum8to24","ConvLum8to32",
+    "ConvLum8to16_X555","ConvLum8to16_0565","ConvLum8to16_A8L8",
+    "ConvAlpha8to16_4444","ConvAlpha8to32","ConvAlpha8to8","ConvAlpha8to16_A8L8"
+};
+#endif
+#endif
+
+char *PandaFilterNameStrs[] = {"FT_nearest","FT_linear","FT_nearest_mipmap_nearest","FT_linear_mipmap_nearest",
+    "FT_nearest_mipmap_linear", "FT_linear_mipmap_linear"
+};
+
+
+TypeHandle DXTextureContext9::_type_handle;
+
+#define SWAPDWORDS(X,Y)  { DWORD temp=X;  X=Y; Y=temp; }
+
+#ifdef _DEBUG
+/*
+static void DebugPrintPixFmt(DDPIXELFORMAT* pddpf) {
+    static int iddpfnum=0;
+    ostream *dbgout = &dxgsg_cat.debug();
+
+    *dbgout << "DDPF[" << iddpfnum << "]: RGBBitCount:" << pddpf->dwRGBBitCount
+    << " Flags:"  << (void *)pddpf->dwFlags ;
+
+    if(pddpf->dwFlags & DDPF_RGB) {
+        *dbgout << " RGBmask:" << (void *) (pddpf->dwRBitMask | pddpf->dwGBitMask | pddpf->dwBBitMask);
+        *dbgout << " Rmask:" << (void *) (pddpf->dwRBitMask);
+    }
+
+    if(pddpf->dwFlags & DDPF_ALPHAPIXELS) {
+        *dbgout << " Amask:" << (void *) pddpf->dwRGBAlphaBitMask;
+    }
+
+    if(pddpf->dwFlags & DDPF_LUMINANCE) {
+        *dbgout << " Lummask:" << (void *) pddpf->dwLuminanceBitMask;
+    }
+
+    *dbgout << endl;
+
+    iddpfnum++;
+}
+*/
+void PrintLastError(char *msgbuf) {
+    DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+
+    if(msgbuf==NULL) {
+        LPVOID lpMsgBuf;
+        dwFlags|=FORMAT_MESSAGE_ALLOCATE_BUFFER;
+        FormatMessage( dwFlags,
+                       NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+                       (LPTSTR) &lpMsgBuf,0,NULL );
+        MessageBox( NULL, (LPCTSTR)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
+        LocalFree(lpMsgBuf);
+    } else {
+        FormatMessage( dwFlags,
+                       NULL,GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+                       (LPTSTR) msgbuf,500,NULL );
+    }
+}
+
+#endif
+
+
+/* for reference
+enum Format {
+  F_color_index,
+  F_stencil_index,
+  F_depth_component,
+  F_red,
+  F_green,
+  F_blue,
+  F_alpha,
+  F_rgb,     // any suitable RGB mode, whatever the hardware prefers
+  F_rgb5,    // specifically, 5 bits per R,G,B channel
+  F_rgb8,    // 8 bits per R,G,B channel
+  F_rgb12,   // 12 bits per R,G,B channel
+  F_rgb332,  // 3 bits per R & G, 2 bits for B
+  F_rgba,    // any suitable RGBA mode, whatever the hardware prefers
+  F_rgbm,    // as above, but only requires 1 bit for alpha (i.e. mask)
+  F_rgba4,   // 4 bits per R,G,B,A channel
+  F_rgba5,   // 5 bits per R,G,B channel, 1 bit alpha
+  F_rgba8,   // 8 bits per R,G,B,A channel
+  F_rgba12,  // 12 bits per R,G,B,A channel
+  F_luminance,
+  F_luminance_alpha
+};
+
+  enum Type {
+    T_unsigned_byte,   // 1 byte per channel
+    T_unsigned_short,  // 2 byte per channel
+    T_unsigned_byte_332,  // RGB in 1 byte
+    T_float,             // 1 channel stored as float
+  };
+*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXTextureContext9::get_bits_per_pixel
+//       Access: Protected
+//  Description: Maps from the PixelBuffer's Format symbols
+//               to bpp.  returns # of alpha bits
+//               Note: PixelBuffer's format indicates REQUESTED final format,
+//                     not the stored format, which is indicated by pixelbuffer type
+////////////////////////////////////////////////////////////////////
+
+unsigned int DXTextureContext9::
+get_bits_per_pixel(PixelBuffer::Format format, int *alphbits) {
+    *alphbits = 0;      // assume no alpha bits
+    switch(format) {
+        case PixelBuffer::F_alpha:
+            *alphbits = 8;
+        case PixelBuffer::F_color_index:
+        case PixelBuffer::F_red:
+        case PixelBuffer::F_green:
+        case PixelBuffer::F_blue:
+        case PixelBuffer::F_rgb332:
+            return 8;
+        case PixelBuffer::F_luminance_alphamask:
+            *alphbits = 1;
+            return 16;
+        case PixelBuffer::F_luminance_alpha:
+            *alphbits = 8;
+            return 16;
+        case PixelBuffer::F_luminance:
+            return 8;
+        case PixelBuffer::F_rgba4:
+            *alphbits = 4;
+            return 16;
+        case PixelBuffer::F_rgba5:
+            *alphbits = 1;
+            return 16;
+        case PixelBuffer::F_depth_component:
+        case PixelBuffer::F_rgb5:
+            return 16;
+        case PixelBuffer::F_rgb8:
+        case PixelBuffer::F_rgb:
+            return 24;
+        case PixelBuffer::F_rgba8:
+        case PixelBuffer::F_rgba:
+        case PixelBuffer::F_rgbm:
+            if(format==PixelBuffer::F_rgbm)   // does this make any sense?
+             *alphbits = 1;
+            else *alphbits = 8;
+            return 32;
+        case PixelBuffer::F_rgb12:
+            return 36;
+        case PixelBuffer::F_rgba12:
+            *alphbits = 12;
+            return 48;
+    }
+    return 8;
+}
+
+/*   // This is superseded by D3DXLoadSurfaceFromMemory(), but keep this stuff around in case its needed
+
+#ifdef DO_CUSTOM_CONVERSIONS
+HRESULT ConvertPixBuftoDDSurf(ConversionType ConvNeeded,BYTE *pbuf,LPDIRECTDRAWSURFACE7 pDDSurf) {
+    HRESULT hr;
+    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+
+    if(IsBadWritePtr(pDDSurf,sizeof(DWORD))) {
+        dxgsg_cat.error() << "ConvertPixBuftoDDSurf failed: bad pDDSurf ptr value (" << ((void*)pDDSurf) << ")\n";
+        exit(1);
+    }
+
+    if(FAILED( hr = pDDSurf->Lock( NULL, &ddsd,  DDLOCK_NOSYSLOCK | DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL ))) {
+        dxgsg_cat.error() << "CreateTexture failed: _surface->Lock() failed on texture! hr = " << ConvD3DErrorToString(hr) << "\n";
+        return hr;
+    }
+
+    //pbuf contains raw ARGB in PixelBuffer byteorder
+
+    DWORD lPitch = ddsd.lPitch;
+    BYTE* pDDSurfBytes = (BYTE*)ddsd.lpSurface;
+    DWORD dwOrigWidth=ddsd.dwWidth,dwOrigHeight=ddsd.dwHeight;
+
+    switch(ConvNeeded) {
+        case Conv32to32:
+        case Conv32to32_NoAlpha: {
+
+#ifdef PANDA_BGRA_ORDER
+                if(ConvNeeded==Conv32to32) {
+                    memcpy(pDDSurfBytes,(BYTE*) pbuf,dwOrigWidth*dwOrigHeight*sizeof(DWORD));
+                } else {
+                    DWORD *pSrcWord = (DWORD *) pbuf;
+                    DWORD *pDstWord;
+
+                    // need to set all pixels alpha to 0xFF
+                    for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                        pDstWord = (DWORD*)pDDSurfBytes;
+                        for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                            *pDstWord = *pSrcWord | 0xFF000000;
+                        }
+                    }
+                }
+#else
+                DWORD *pDstWord,*pSrcWord = (DWORD *) pbuf;
+                DWORD dwAlphaMaskOn = (ConvNeeded==Conv32to32_NoAlpha) ? 0xFF000000 : 0x0;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (DWORD*)pDDSurfBytes;
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel = *pSrcWord;
+
+                        // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                        // need to change byte order to ARGB
+
+                        BYTE r,b;
+                        // just swap r & b
+                        b = GET_BLUE_BYTE(dwPixel);
+                        r = GET_RED_BYTE(dwPixel);
+                        *pDstWord = ((dwPixel & 0xff00ff00) | (r<<16) | b) | dwAlphaMaskOn;
+                    }
+                }
+#endif
+                break;
+            }
+
+        case Conv32to16_1555:
+        case Conv32to16_X555: {
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                WORD *pDstWord;
+
+                unsigned short dwAlphaMaskOn = (ConvNeeded==Conv32to16_X555) ? 0x8000 : 0x0;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00);
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        BYTE r,g,b;
+                        DWORD dwPixel = *pSrcWord;
+                        unsigned short abit;
+
+                        // just look at most-signf-bit for alpha.  (alternately, could
+                        // convert any non-zero alpha to full transparent)
+
+                        abit = ((dwPixel>>16) & 0x00008000) | dwAlphaMaskOn;  // just copy high bit
+                        g = GET_GREEN_BYTE(dwPixel) >> 3;
+                        b = GET_BLUE_BYTE(dwPixel) >> 3;
+                        r = GET_RED_BYTE(dwPixel) >> 3;
+
+                        // truncates 8 bit values to 5 bit (or 1 for alpha)
+
+                        *pDstWord = (abit | (r << 10)| (g << 5) | b);
+                    }
+                }
+                break;
+            }
+
+        case Conv32to16_0565: {   // could merge this w/above case, but whatever
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800);
+                // for some reason, bits are 'in-order' when converting to 16bit
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        BYTE r,g,b;
+                        DWORD dwPixel = *pSrcWord;
+
+                        g = GET_GREEN_BYTE(dwPixel) >> 2;
+                        b = GET_BLUE_BYTE(dwPixel) >> 3;
+                        r = GET_RED_BYTE(dwPixel) >> 3;
+                        *pDstWord = ((r << 11)| (g << 5) | b);
+                    }
+                }
+                break;
+            }
+
+        case Conv32to16_4444: {
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0xf000);  // assumes ARGB
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0x0f00);  // assumes ARGB
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        BYTE r,g,b,a;
+                        DWORD dwPixel = *pSrcWord;
+
+                        a = GET_ALPHA_BYTE(dwPixel)  >> 4;
+                        g = GET_GREEN_BYTE(dwPixel)  >> 4;
+                        b = GET_BLUE_BYTE(dwPixel)   >> 4;
+                        r = GET_RED_BYTE(dwPixel)    >> 4;
+
+                        *pDstWord = (a << 12) | (r << 8)| (g << 4) | b;
+                    }
+                }
+                break;
+            }
+
+        case Conv32to24: {
+
+                DWORD *pSrcWord = (DWORD *) pbuf;
+                BYTE *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (BYTE*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord+=3) {
+                        BYTE r,g,b;
+                        DWORD dwPixel = *pSrcWord;
+
+                        r = GET_RED_BYTE(dwPixel);
+                        g = GET_GREEN_BYTE(dwPixel);
+                        b = GET_BLUE_BYTE(dwPixel);
+
+                        *pDstWord     = r;
+                        *(pDstWord+1) = g;
+                        *(pDstWord+2) = b;
+                    }
+                }
+                break;
+            }
+
+
+        case Conv24to24: {
+            #ifdef PANDA_BGRA_ORDER
+                memcpy(pDDSurfBytes,(BYTE*)pbuf,dwOrigHeight*dwOrigWidth*3);
+            #else
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                BYTE *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (BYTE*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord+=3) {
+                        BYTE r,g,b;
+
+                        b = *pSrcWord;
+                        g = *(pSrcWord+1);
+                        r = *(pSrcWord+2);
+
+                        *pDstWord     = r;
+                        *(pDstWord+1) = g;
+                        *(pDstWord+2) = b;
+                    }
+                }
+             #endif
+                break;
+            }
+
+        case Conv24to16_X555: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00);
+             // for some reason, bits are 'in-order' when converting to 16bit
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord++) {
+                        BYTE r,g,b;
+
+                    #ifdef PANDA_BGRA_ORDER
+                        b = *pSrcWord       >> 3;
+                        r = *(pSrcWord+2)   >> 3;
+                    #else
+                        r = *pSrcWord       >> 3;
+                        b = *(pSrcWord+2)   >> 3;
+                    #endif
+                        g = *(pSrcWord+1)   >> 3;
+
+                        *pDstWord = 0x8000 | (r << 10)| (g << 5) | b;
+                    }
+                }
+                break;
+            }
+
+        case Conv24to16_0565: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800);
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord++) {
+                        BYTE r,g,b;
+
+                    #ifdef PANDA_BGRA_ORDER
+                        b = *pSrcWord       >> 3;
+                        g = *(pSrcWord+1)   >> 2;
+                        r = *(pSrcWord+2)   >> 3;
+                    #else
+                        r = *pSrcWord       >> 3;
+                        g = *(pSrcWord+1)   >> 2;
+                        b = *(pSrcWord+2)   >> 3;
+                    #endif
+                     // code truncates 8 bit values to 5 bit
+                     *pDstWord = (r << 11)| (g << 5) | b;
+                    }
+                }
+                break;
+            }
+
+        case Conv24to32: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord+=3,pDstWord++) {
+                        BYTE r,g,b;
+                        // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                        // need to change byte order to ARGB
+
+                    #ifdef PANDA_BGRA_ORDER
+                        b = *pSrcWord;
+                        r = *(pSrcWord+2);
+                    #else
+                        r = *pSrcWord;
+                        b = *(pSrcWord+2);
+                    #endif
+                        g = *(pSrcWord+1);
+
+                        *pDstWord = 0xFF000000 | (r << 16) | (g << 8) | b;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to32: {
+                WORD *pSrcWord = (WORD *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                        // need to change byte order to ARGB
+                        DWORD dwPixel=*pSrcWord;
+                        BYTE lum,a;
+
+                        a = dwPixel >> 8;
+                        lum = dwPixel & 0xFF;
+                        *pDstWord = (a<<24) | lum | (lum << 8) | (lum << 16);
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to16_4444: {
+                WORD *pSrcWord = (WORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0xf000);  // assumes ARGB
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord;
+                        BYTE lum,a;
+                        dwPixel = *pSrcWord;
+
+                        a =   (BYTE)(dwPixel>>8)           >> 4;
+                        lum = (BYTE)(dwPixel & 0x000000ff) >> 4;
+
+                        *pDstWord = (a << 12) | lum | (lum << 4)| (lum << 8);
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to16_1555: {
+                WORD *pSrcWord = (WORD *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0x8000);  // assumes ARGB
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        WORD dwPixel=*pSrcWord;
+                        BYTE lum;
+
+                        lum = (BYTE)(dwPixel & 0x00FF) >> 3;
+
+                        *pDstWord = (dwPixel & 0x8000) | lum | (lum << 5) | (lum << 10);
+                    }
+                }
+                break;
+            }
+
+        case ConvLum16to16: {
+                // All bytes are in same order?
+                CopyMemory(pDDSurfBytes,pbuf,dwOrigWidth*dwOrigHeight*2);
+                break;
+            }
+
+        case ConvLum8to16_0565:
+        case ConvLum8to16_X555: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+                DWORD FarShift,OrVal,MiddleRoundShift;
+
+                if(ConvNeeded==ConvLum8to16_X555) {
+                    FarShift=10;  OrVal=0x8000;  // turn on alpha bit, just in case
+                    MiddleRoundShift = 3;
+                    assert(ddsd.ddpfPixelFormat.dwRBitMask==0x7C00);
+                } else {
+                    FarShift=11;  OrVal=0x0;
+                    MiddleRoundShift = 2;
+                    assert(ddsd.ddpfPixelFormat.dwRBitMask==0xF800);
+                }
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes+=ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord,GrnVal;
+                        BYTE r;
+
+                        r = (BYTE) dwPixel >> 3;
+                        GrnVal = (BYTE) dwPixel >> MiddleRoundShift;
+
+                        // code truncates 8 bit values to 5 bit  (set alpha=1 for opaque)
+
+                        *pDstWord = ((r << FarShift)| (GrnVal << 5) | r) | OrVal;
+                    }
+                }
+                break;
+            }
+
+        case ConvLum8to8: {
+                CopyMemory(pDDSurfBytes,pbuf,dwOrigWidth*dwOrigHeight);
+                break;
+            }
+
+        case ConvLum8to32: {
+
+          // this is kind of a waste of space, but we trade it for lum resolution
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord;
+
+                        *pDstWord = 0xFF000000 | dwPixel | (dwPixel << 8) | (dwPixel<<16);
+                    }
+                }
+                break;
+            }
+
+        case ConvLum8to24: {
+                // this is kind of a waste of space, but we trade it for lum resolution
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                BYTE *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (BYTE *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        DWORD dwPixel=*pSrcWord;
+
+                        *pDstWord++ = dwPixel;
+                        *pDstWord++ = dwPixel;
+                        *pDstWord   = dwPixel;
+                    }
+                }
+                break;
+            }
+
+       case ConvLum8to16_A8L8: {
+                // wastes space, since alpha is just fully opaque, but Lum-only may not be avail
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        *pDstWord = 0xFF00 | *pSrcWord;   // add fully-opaque alpha
+                    }
+                }
+                break;
+        }
+
+        case ConvAlpha8to16_A8L8: {
+            // need to investigate why alpha-only A8 surfaces dont work on voodoo's
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        *pDstWord = (*pSrcWord << 8) | 0x00FF;   // add full white
+                    }
+                }
+                break;
+        }
+
+        case ConvAlpha8to32: {
+              //  huge waste of space, but this may be only fmt where we get 8bits alpha resolution
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                DWORD *pDstWord;
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (DWORD *)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        // OR alpha with full white
+                        *pDstWord = (*pSrcWord << 24) | 0x00FFFFFF;
+                    }
+                }
+                break;
+            }
+
+        case ConvAlpha8to16_4444: {
+                BYTE *pSrcWord = (BYTE *) pbuf;
+                WORD *pDstWord;
+
+                assert(ddsd.ddpfPixelFormat.dwRGBAlphaBitMask==0xf000);  // assumes ARGB order
+
+                for(DWORD y=0; y<dwOrigHeight; y++,pDDSurfBytes += ddsd.lPitch) {
+                    pDstWord = (WORD*)pDDSurfBytes;
+
+                    for(DWORD x=0; x<dwOrigWidth; x++,pSrcWord++,pDstWord++) {
+                        WORD a = (BYTE)(*pSrcWord>>4);
+                        *pDstWord = (a << 12) | 0x0FFF; // OR alpha with full white
+                    }
+                }
+                break;
+            }
+
+        default:
+            dxgsg_cat.error() << "CreateTexture failed! unhandled texture conversion type: "<< ConvNeeded <<" \n";
+            pDDSurf->Unlock(NULL);
+            return E_INVALIDARG;
+    }
+
+    pDDSurf->Unlock(NULL);
+
+    return S_OK;
+}
+#endif
+*/
+
+// still need custom conversion since d3d/d3dx has no way to convert arbitrary fmt to ARGB in-memory user buffer
+HRESULT ConvertD3DSurftoPixBuf(RECT &SrcRect,IDirect3DSurface9 *pD3DSurf9,PixelBuffer *pixbuf) {
+// copies SrcRect in pD3DSurf to upper left of pixbuf
+    HRESULT hr;
+    DWORD dwNumComponents=pixbuf->get_num_components();
+
+    assert(pixbuf->get_component_width()==sizeof(BYTE));   // cant handle anything else now
+    assert(pixbuf->get_image_type()==PixelBuffer::T_unsigned_byte);   // cant handle anything else now
+    assert((dwNumComponents==3) || (dwNumComponents==4));  // cant handle anything else now
+    assert(IS_VALID_PTR(pD3DSurf9));
+
+    BYTE *pbuf=pixbuf->_image.p();
+
+    if(IsBadWritePtr(pD3DSurf9,sizeof(DWORD))) {
+        dxgsg9_cat.error() << "ConvertDDSurftoPixBuf failed: bad pD3DSurf ptr value (" << ((void*)pD3DSurf9) << ")\n";
+        exit(1);
+    }
+
+    DWORD dwXWindowOffset,dwYWindowOffset;
+    DWORD dwCopyWidth,dwCopyHeight;
+
+    D3DLOCKED_RECT LockedRect;
+    D3DSURFACE_DESC SurfDesc;
+
+    hr = pD3DSurf9->GetDesc(&SurfDesc);
+
+    dwXWindowOffset=SrcRect.left,dwYWindowOffset=SrcRect.top;
+    dwCopyWidth=RECT_XSIZE(SrcRect);
+    dwCopyHeight=RECT_YSIZE(SrcRect);
+
+    //make sure there's enough space in the pixbuf, its size must match (especially xsize)
+   // or scanlines will be too long
+
+    if(!((dwCopyWidth==pixbuf->get_xsize()) && (dwCopyHeight<=(DWORD)pixbuf->get_ysize()))) {
+        dxgsg9_cat.error() << "ConvertDDSurftoPixBuf, PixBuf size too small to hold display surface!\n";
+        assert(0);
+        return E_FAIL;
+    }
+
+    hr = pD3DSurf9->LockRect(&LockedRect,(CONST RECT*)NULL,(D3DLOCK_READONLY | D3DLOCK_NO_DIRTY_UPDATE /* | D3DLOCK_NOSYSLOCK */));
+    if(FAILED(hr)) {
+        dxgsg9_cat.error() << "ConvertDDSurftoPixBuf LockRect() failed!" << D3DERRORSTRING(hr);
+        return hr;
+    }
+
+    // ones not listed not handled yet
+    assert((SurfDesc.Format==D3DFMT_A8R8G8B8)||(SurfDesc.Format==D3DFMT_X8R8G8B8)||(SurfDesc.Format==D3DFMT_R8G8B8)||
+           (SurfDesc.Format==D3DFMT_R5G6B5)||(SurfDesc.Format==D3DFMT_X1R5G5B5)||(SurfDesc.Format==D3DFMT_A1R5G5B5)||
+           (SurfDesc.Format==D3DFMT_A4R4G4B4));
+
+    //pbuf contains raw ARGB in PixelBuffer byteorder
+
+    DWORD BytePitch = LockedRect.Pitch;
+    BYTE* pSurfBytes = (BYTE*)LockedRect.pBits;
+
+    // writes out last line in DDSurf first in PixelBuf, so Y line order precedes inversely
+
+    if(dxgsg9_cat.is_debug()) {
+        dxgsg9_cat.debug() << "ConvertD3DSurftoPixBuf converting " << D3DFormatStr(SurfDesc.Format) << "bpp DDSurf to "
+                          <<  dwNumComponents << "-channel panda PixelBuffer\n";
+    }
+
+    DWORD *pDstWord = (DWORD *) pbuf;
+    BYTE *pDstByte = (BYTE *) pbuf;
+
+    switch(SurfDesc.Format) {
+        case D3DFMT_A8R8G8B8:
+        case D3DFMT_X8R8G8B8: {
+            if(dwNumComponents==4) {
+                    DWORD *pSrcWord;
+                   #ifdef PANDA_BGRA_ORDER
+                    BYTE *pDstLine = (BYTE*)pDstWord;
+                   #endif
+
+                        pSurfBytes+=BytePitch*(dwYWindowOffset+dwCopyHeight-1);
+                        for(DWORD y=0; y<dwCopyHeight; y++,pSurfBytes-=BytePitch) {
+                            pSrcWord = ((DWORD*)pSurfBytes)+dwXWindowOffset;
+                            #ifdef PANDA_BGRA_ORDER
+                                memcpy(pDstLine,pSrcWord,BytePitch);
+                                pDstLine+=BytePitch;
+                            #else
+                                for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++,pDstWord++) {
+                                      DWORD dwPixel = *pSrcWord;
+                                      BYTE r,b;
+
+                                      // DDsurf is in ARGB
+                                      r=  (BYTE) ((dwPixel >> 16) & g_LowByteMask);
+                                      b = (BYTE)  (dwPixel & g_LowByteMask);
+
+                                      // want to write out ABGR
+                                      *pDstWord = (dwPixel & 0xFF00FF00) | (b<<16) | r;
+                                }
+                            #endif
+                        }
+            } else {
+                // 24bpp pixbuf case (numComponents==3)
+                DWORD *pSrcWord;
+                pSurfBytes+=BytePitch*(dwYWindowOffset+dwCopyHeight-1);
+                for(DWORD y=0; y<dwCopyHeight; y++,pSurfBytes-=BytePitch) {
+                    pSrcWord = ((DWORD*)pSurfBytes)+dwXWindowOffset;
+
+                    for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++) {
+                        BYTE r,g,b;
+                        DWORD dwPixel = *pSrcWord;
+
+                        r = (BYTE)((dwPixel>>16) & g_LowByteMask);
+                        g = (BYTE)((dwPixel>> 8) & g_LowByteMask);
+                        b = (BYTE)((dwPixel    ) & g_LowByteMask);
+
+                        #ifdef PANDA_BGRA_ORDER
+                            *pDstByte++ = b;
+                            *pDstByte++ = g;
+                            *pDstByte++ = r;
+                        #else
+                            *pDstByte++ = r;
+                            *pDstByte++ = g;
+                            *pDstByte++ = b;
+                        #endif
+                    }
+                }
+            }
+            break;
+        }
+
+        case D3DFMT_R8G8B8: {
+                BYTE *pSrcByte;
+                pSurfBytes+=BytePitch*(dwYWindowOffset+dwCopyHeight-1);
+
+                if(dwNumComponents==4) {
+                    for(DWORD y=0; y<dwCopyHeight; y++,pSurfBytes-=BytePitch) {
+                        pSrcByte = pSurfBytes+dwXWindowOffset*3*sizeof(BYTE);
+                        for(DWORD x=0; x<dwCopyWidth; x++,pDstWord++) {
+                            DWORD r,g,b;
+
+                            b = *pSrcByte++;
+                            g = *pSrcByte++;
+                            r = *pSrcByte++;
+
+                            #ifdef PANDA_BGRA_ORDER
+                               *pDstWord = 0xFF000000 | (r << 16) | (g << 8) | b;
+                            #else
+                               *pDstWord = 0xFF000000 | (b << 16) | (g << 8) | r;
+                            #endif
+                        }
+                    }
+                } else {
+                    // 24bpp pixbuf case (numComponents==3)
+                    for(DWORD y=0; y<dwCopyHeight; y++,pSurfBytes-=BytePitch) {
+                        pSrcByte = pSurfBytes+dwXWindowOffset*3*sizeof(BYTE);
+                     #ifdef PANDA_BGRA_ORDER
+                        memcpy(pDstByte,pSrcByte,BytePitch);
+                        pDstByte+=BytePitch;
+                     #else
+                        for(DWORD x=0; x<dwCopyWidth; x++) {
+                            BYTE r,g,b;
+
+                            // pixel buffer is in ABGR format(it stores big-endian RGBA)
+                            // need to change byte order from ARGB
+
+                            b = *pSrcByte++;
+                            g = *pSrcByte++;
+                            r = *pSrcByte++;
+
+                            *pDstByte++ = r;
+                            *pDstByte++ = g;
+                            *pDstByte++ = b;
+                        }
+                      #endif
+                    }
+                }
+                break;
+        }
+
+        case D3DFMT_R5G6B5:
+        case D3DFMT_X1R5G5B5:
+        case D3DFMT_A1R5G5B5:
+        case D3DFMT_A4R4G4B4: {
+                WORD  *pSrcWord;
+                // handle 0555,1555,0565,4444 in same loop
+
+                BYTE redshift,greenshift,blueshift;
+                DWORD redmask,greenmask,bluemask;
+
+                if(SurfDesc.Format==D3DFMT_R5G6B5) {
+                    redshift=(11-3);
+                    redmask=0xF800;
+                    greenmask=0x07E0;
+                    greenshift=(5-2);
+                    bluemask=0x001F;
+                    blueshift=3;
+                } else if(SurfDesc.Format==D3DFMT_A4R4G4B4) {
+                    redmask=0x0F00;
+                    redshift=4;
+                    greenmask=0x00F0;
+                    greenshift=0;
+                    bluemask=0x000F;
+                    blueshift=4;
+                } else {  // 1555 or x555
+                    redmask=0x7C00;
+                    redshift=(10-3);
+                    greenmask=0x03E0;
+                    greenshift=(5-3);
+                    bluemask=0x001F;
+                    blueshift=3;
+                }
+
+                pSurfBytes+=BytePitch*(dwYWindowOffset+dwCopyHeight-1);
+                if(dwNumComponents==4) {
+                    // Note: these 16bpp loops ignore input alpha completely (alpha is set to fully opaque in pixbuf!)
+                    //       if we need to capture alpha, probably need to make separate loops for diff 16bpp fmts
+                    //       for best speed
+
+                    for(DWORD y=0; y<dwCopyHeight; y++,pSurfBytes-=BytePitch) {
+                        pSrcWord = ((WORD*)pSurfBytes)+dwXWindowOffset;
+                        for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++,pDstWord++) {
+                            WORD dwPixel = *pSrcWord;
+                            BYTE r,g,b;
+
+                            b = (dwPixel & bluemask) << blueshift;
+                            g = (dwPixel & greenmask) >> greenshift;
+                            r = (dwPixel & redmask) >> redshift;
+
+                            // alpha is just set to 0xFF
+
+                            #ifdef PANDA_BGRA_ORDER
+                              *pDstWord = 0xFF000000 | (r << 16) | (g << 8) | b;
+                            #else
+                              *pDstWord = 0xFF000000 | (b << 16) | (g << 8) | r;
+                            #endif
+                        }
+                    }
+                } else {
+                    // 24bpp pixbuf case (numComponents==3)
+                    for(DWORD y=0; y<dwCopyHeight; y++,pSurfBytes-=BytePitch) {
+                        pSrcWord = ((WORD*)pSurfBytes)+dwXWindowOffset;
+                        for(DWORD x=0; x<dwCopyWidth; x++,pSrcWord++) {
+                            WORD dwPixel = *pSrcWord;
+                            BYTE r,g,b;
+
+                            b = (dwPixel & bluemask) << blueshift;
+                            g = (dwPixel & greenmask) >> greenshift;
+                            r = (dwPixel & redmask) >> redshift;
+
+                            #ifdef PANDA_BGRA_ORDER
+                                *pDstByte++ = b;
+                                *pDstByte++ = g;
+                                *pDstByte++ = r;
+                            #else
+                                *pDstByte++ = r;
+                                *pDstByte++ = g;
+                                *pDstByte++ = b;
+                            #endif
+                        }
+                    }
+                }
+                break;
+        }
+
+        default:
+            dxgsg9_cat.error() << "ConvertD3DSurftoPixBuf: unsupported D3DFORMAT!\n";
+    }
+
+    pD3DSurf9->UnlockRect();
+    return S_OK;
+}
+
+//-----------------------------------------------------------------------------
+// Name: CreateTexture()
+// Desc: Use panda texture's pixelbuffer to create a texture for the specified device.
+//       This code gets the attributes of the texture from the bitmap, creates the
+//       texture, and then copies the bitmap into the texture.
+//-----------------------------------------------------------------------------
+IDirect3DTexture9 *DXTextureContext9::CreateTexture(DXScreenData &scrn) {
+    HRESULT hr;
+    int cNumAlphaBits;     //  number of alpha bits in texture pixfmt
+    D3DFORMAT TargetPixFmt=D3DFMT_UNKNOWN;
+    bool bNeedLuminance = false;
+
+    assert(IS_VALID_PTR(_texture));
+
+    PixelBuffer *pbuf = _texture->_pbuffer;
+    // bpp indicates requested fmt, not pixbuf fmt
+    DWORD target_bpp = get_bits_per_pixel(pbuf->get_format(), &cNumAlphaBits);
+    PixelBuffer::Type pixbuf_type = pbuf->get_image_type();
+    DWORD cNumColorChannels = pbuf->get_num_components();
+
+    assert(pbuf->get_component_width()==sizeof(BYTE));   // cant handle anything else now
+    assert(pixbuf_type==PixelBuffer::T_unsigned_byte);   // cant handle anything else now
+
+    //PRINT_REFCNT(dxgsg9,scrn.pD3D9);
+
+    if((pixbuf_type!=PixelBuffer::T_unsigned_byte) || (pbuf->get_component_width()!=1)) {
+        dxgsg9_cat.error() << "CreateTexture failed, havent handled non 8-bit channel pixelbuffer types yet! \n";
+        return NULL;
+    }
+
+    DWORD dwOrigWidth  = (DWORD)pbuf->get_xsize();
+    DWORD dwOrigHeight = (DWORD)pbuf->get_ysize();
+
+    if((pbuf->get_format() == PixelBuffer::F_luminance_alpha)||
+       (pbuf->get_format() == PixelBuffer::F_luminance_alphamask) ||
+       (pbuf->get_format() == PixelBuffer::F_luminance)) {
+        bNeedLuminance = true;
+    }
+
+    if(cNumAlphaBits>0) {
+        if(cNumColorChannels==3) {
+            dxgsg9_cat.error() << "ERROR: texture " << _tex->get_name() << " has no inherent alpha channel, but alpha format is requested (that would be wasteful)!\n";
+            exit(1);
+        }
+    }
+
+    _PixBufD3DFmt=D3DFMT_UNKNOWN;
+
+#ifndef DO_CUSTOM_CONVERSIONS
+    // figure out what 'D3DFMT' the PixelBuffer is in, so D3DXLoadSurfFromMem knows how to perform copy
+
+    switch(cNumColorChannels) {
+        case 1:
+            if(cNumAlphaBits>0)
+                _PixBufD3DFmt=D3DFMT_A8;
+            else if(bNeedLuminance)
+                   _PixBufD3DFmt=D3DFMT_L8;
+            break;
+        case 2:
+            assert(bNeedLuminance && (cNumAlphaBits>0));
+            _PixBufD3DFmt=D3DFMT_A8L8;
+            break;
+        case 3:
+            _PixBufD3DFmt=D3DFMT_R8G8B8;
+            break;
+        case 4:
+            _PixBufD3DFmt=D3DFMT_A8R8G8B8;
+            break;
+    }
+
+    // make sure we handled all the possible cases
+    assert(_PixBufD3DFmt!=D3DFMT_UNKNOWN);
+#endif
+
+    DWORD TargetWidth=dwOrigWidth;
+    DWORD TargetHeight=dwOrigHeight;
+
+    if(!ISPOW2(dwOrigWidth) || !ISPOW2(dwOrigHeight)) {
+        dxgsg9_cat.error() << "ERROR: texture dimensions are not a power of 2 for " << _tex->get_name() << "! Please rescale them so it doesnt have to be done at runtime.\n";
+        #ifndef NDEBUG
+          exit(1);  // want to catch badtexsize errors
+        #else
+          goto error_exit;
+        #endif
+    }
+
+    bool bShrinkOriginal;
+    bShrinkOriginal=false;
+
+    if((dwOrigWidth>scrn.d3dcaps.MaxTextureWidth)||(dwOrigHeight>scrn.d3dcaps.MaxTextureHeight)) {
+        #ifdef _DEBUG
+           dxgsg9_cat.error() << "WARNING: " <<_tex->get_name() << ": Image size exceeds max texture dimensions of (" << scrn.d3dcaps.MaxTextureWidth << "," << scrn.d3dcaps.MaxTextureHeight << ") !!\n"
+           << "Scaling "<< _tex->get_name() << " ("<< dwOrigWidth<<"," <<dwOrigHeight << ") => ("<<  scrn.d3dcaps.MaxTextureWidth << "," << scrn.d3dcaps.MaxTextureHeight << ") !\n";
+        #endif
+
+        if(dwOrigWidth>scrn.d3dcaps.MaxTextureWidth)
+            TargetWidth=scrn.d3dcaps.MaxTextureWidth;
+        if(dwOrigHeight>scrn.d3dcaps.MaxTextureHeight)
+            TargetHeight=scrn.d3dcaps.MaxTextureHeight;
+        bShrinkOriginal=true;
+    }
+
+    // checks for SQUARE reqmt (nvidia riva128 needs this)
+    if((TargetWidth != TargetHeight) && (scrn.d3dcaps.TextureCaps & D3DPTEXTURECAPS_SQUAREONLY)) {
+        // assume pow2 textures.   sum exponents, divide by 2 rounding down to get sq size
+        int i,width_exp,height_exp;
+        for(i=TargetWidth,width_exp=0;i>1;width_exp++,i>>=1);
+        for(i=TargetHeight,height_exp=0;i>1;height_exp++,i>>=1);
+        TargetHeight = TargetWidth = 1<<((width_exp+height_exp)>>1);
+        bShrinkOriginal=true;
+
+#ifdef _DEBUG
+        dxgsg9_cat.debug() << "Scaling "<< _tex->get_name() << " ("<< dwOrigWidth<<"," <<dwOrigHeight << ") => ("<< TargetWidth<<"," << TargetHeight << ") to meet HW square texture reqmt\n";
+#endif
+    }
+/*
+    // we now use D3DXLoadSurfFromMem to do resizing as well as fmt conversion
+    if(bShrinkOriginal) {
+        // need 2 add checks for errors
+        PNMImage pnmi_src;
+        PNMImage *pnmi = new PNMImage(TargetWidth, TargetHeight, cNumColorChannels);
+        pbuf->store(pnmi_src);
+        pnmi->quick_filter_from(pnmi_src,0,0);
+
+        pbuf->load(*pnmi);  // violates device independence of pixbufs
+
+        dwOrigWidth  = (DWORD)pbuf->get_xsize();
+        dwOrigHeight = (DWORD)pbuf->get_ysize();
+        delete pnmi;
+    }
+*/
+
+    char *szErrorMsg;
+
+    szErrorMsg = "CreateTexture failed: couldn't find compatible device Texture Pixel Format for input texture";
+
+    if(dxgsg9_cat.is_spam())
+        dxgsg9_cat.spam() << "CreateTexture handling target bitdepth: " << target_bpp << " alphabits: " << cNumAlphaBits << endl;
+
+    // I could possibly replace some of this logic with D3DXCheckTextureRequirements(), but
+    // it wouldnt handle all my specialized low-memory cases perfectly
+
+#ifdef DO_CUSTOM_CONVERSIONS
+    ConversionType ConvNeeded;
+
+#define CONVTYPE_STMT ConvNeeded=CONV
+#else
+#define CONVTYPE_STMT
+#endif
+
+#define CHECK_FOR_FMT(FMT,CONV)  \
+                    if(scrn.SupportedTexFmtsMask & FMT##_FLAG) {   \
+                        CONVTYPE_STMT;                             \
+                        TargetPixFmt=D3DFMT_##FMT;                 \
+                        goto found_matching_format; }
+
+    // handle each target bitdepth separately.  might be less confusing to reorg by cNumColorChannels (input type, rather
+    // than desired 1st target)
+    switch(target_bpp) {
+
+    // IMPORTANT NOTE:
+    // target_bpp is REQUESTED bpp, not what exists in the pixbuf array (the pixbuf array contains cNumColorChannels*8bits)
+
+        case 32:
+            if(!((cNumColorChannels==3) || (cNumColorChannels==4)))
+                break; //bail
+
+            if(!dx_force_16bpptextures) {
+                if(cNumColorChannels==4) {
+                    CHECK_FOR_FMT(A8R8G8B8,Conv32to32);
+                } else {
+                    CHECK_FOR_FMT(A8R8G8B8,Conv24to32);
+                }
+            }
+
+            if(cNumAlphaBits>0) {
+                assert(cNumColorChannels==4);
+
+            // no 32-bit fmt, look for 16 bit w/alpha  (1-15)
+
+            // 32 bit RGBA was requested, but only 16 bit alpha fmts are avail
+            // by default, convert to 4-4-4-4 which has 4-bit alpha for blurry edges
+            // if we know tex only needs 1 bit alpha (i.e. for a mask), use 1555 instead
+
+//                ConversionType ConvTo1=Conv32to16_4444,ConvTo2=Conv32to16_1555;
+//                DWORD dwAlphaMask1=0xF000,dwAlphaMask2=0x8000;
+            // assume ALPHAMASK is x8000 and RGBMASK is x7fff to simplify 32->16 conversion
+            // this should be true on most cards.
+
+#ifndef FORCE_16bpp_1555
+                if(cNumAlphaBits==1)
+#endif
+                {
+                    CHECK_FOR_FMT(A1R5G5B5,Conv32to16_1555);
+                }
+
+                // normally prefer 4444 due to better alpha channel resolution
+                CHECK_FOR_FMT(A4R4G4B4,Conv32to16_4444);
+                CHECK_FOR_FMT(A1R5G5B5,Conv32to16_1555);
+
+                // at this point, bail.  dont worry about converting to non-alpha formats yet,
+                // I think this will be a very rare case
+                szErrorMsg = "CreateTexture failed: couldn't find compatible Tex DDPIXELFORMAT! no available 16 or 32-bit alpha formats!";
+            } else {
+                // convert 3 or 4 channel to closest 16bpp color fmt
+
+                if(cNumColorChannels==3) {
+                    CHECK_FOR_FMT(R5G6B5,Conv24to16_4444);
+                    CHECK_FOR_FMT(X1R5G5B5,Conv24to16_X555);
+                } else {
+                    CHECK_FOR_FMT(R5G6B5,Conv32to16_4444);
+                    CHECK_FOR_FMT(X1R5G5B5,Conv32to16_X555);
+                }
+            }
+            break;
+
+        case 24:
+            assert(cNumColorChannels==3);
+
+            if(!dx_force_16bpptextures) {
+                CHECK_FOR_FMT(R8G8B8,Conv24to24);
+
+                // no 24-bit fmt.  look for 32 bit fmt  (note: this is memory-hogging choice
+                // instead I could look for memory-conserving 16-bit fmt).
+
+                CHECK_FOR_FMT(X8R8G8B8,Conv24to32);
+            }
+
+             // no 24-bit or 32 fmt.  look for 16 bit fmt (higher res 565 1st)
+            CHECK_FOR_FMT(R5G6B5,Conv24to16_0565);
+            CHECK_FOR_FMT(X1R5G5B5,Conv24to16_X555);
+            break;
+
+        case 16:
+            if(bNeedLuminance) {
+                assert(cNumAlphaBits>0);
+                assert(cNumColorChannels==2);
+
+                CHECK_FOR_FMT(A8L8,ConvLum16to16);
+
+                if(!dx_force_16bpptextures) {
+                    CHECK_FOR_FMT(A8R8G8B8,ConvLum16to32);
+                }
+
+              #ifndef FORCE_16bpp_1555
+                if(cNumAlphaBits==1)
+              #endif
+                {
+                    CHECK_FOR_FMT(A1R5G5B5,ConvLum16to16_1555);
+                }
+
+                // normally prefer 4444 due to better alpha channel resolution
+                CHECK_FOR_FMT(A4R4G4B4,ConvLum16to16_4444);
+                CHECK_FOR_FMT(A1R5G5B5,ConvLum16to16_1555);
+            } else {
+               assert((cNumColorChannels==3)||(cNumColorChannels==4));
+          // look for compatible 16bit fmts, if none then give up
+          // (dont worry about other bitdepths for 16 bit)
+                switch(cNumAlphaBits) {
+                    case 0:
+                      if(cNumColorChannels==3) {
+                          CHECK_FOR_FMT(R5G6B5,Conv24to16_0565);
+                          CHECK_FOR_FMT(X1R5G5B5,Conv24to16_X555);
+                      } else {
+                          assert(cNumColorChannels==4);
+                        // it could be 4 if user asks us to throw away the alpha channel
+                          CHECK_FOR_FMT(R5G6B5,Conv32to16_0565);
+                          CHECK_FOR_FMT(X1R5G5B5,Conv32to16_X555);
+                      }
+                      break;
+                    case 1:
+                      // app specifically requests 1-5-5-5 F_rgba5 case, where you explicitly want 1-5-5-5 fmt, as opposed
+                      // to F_rgbm, which could use 32bpp ARGB.  fail if this particular fmt not avail.
+                      assert(cNumColorChannels==4);
+                      CHECK_FOR_FMT(X1R5G5B5,Conv32to16_X555);
+                      break;
+                    case 4:
+                      // app specifically requests 4-4-4-4 F_rgba4 case, as opposed to F_rgba, which could use 32bpp ARGB
+                      assert(cNumColorChannels==4);
+                      CHECK_FOR_FMT(A4R4G4B4,Conv32to16_4444);
+                      break;
+                    default: assert(0);  // problem in get_bits_per_pixel()?
+                }
+            }
+        case 8:
+            if(bNeedLuminance) {
+                // dont bother handling those other 8bit lum fmts like 4-4, since 16 8-8 is usually supported too
+                assert(cNumColorChannels==1);
+
+                // look for native lum fmt first
+                CHECK_FOR_FMT(L8,ConvLum8to8);
+                CHECK_FOR_FMT(L8,ConvLum8to16_A8L8);
+
+                if(!dx_force_16bpptextures) {
+                    CHECK_FOR_FMT(R8G8B8,ConvLum8to24);
+                    CHECK_FOR_FMT(X8R8G8B8,ConvLum8to32);
+                }
+
+                CHECK_FOR_FMT(R5G6B5,ConvLum8to16_0565);
+                CHECK_FOR_FMT(X1R5G5B5,ConvLum8to16_X555);
+
+            } else if(cNumAlphaBits==8) {
+                // look for 16bpp A8L8, else 32-bit ARGB, else 16-4444.
+
+                // skip 8bit alpha only (D3DFMT_A8), because I think only voodoo supports it
+                // and the voodoo support isn't the kind of blending model we need somehow
+                // (is it that voodoo assumes color is white?  isnt that what we do in ConvAlpha8to32 anyway?)
+
+                CHECK_FOR_FMT(A8L8,ConvAlpha8to16_A8L8);
+
+                if(!dx_force_16bpptextures) {
+                    CHECK_FOR_FMT(A8R8G8B8,ConvAlpha8to32);
+                }
+
+                CHECK_FOR_FMT(A4R4G4B4,ConvAlpha8to16_4444);
+            }
+            break;
+
+        default:
+            szErrorMsg = "CreateTexture failed: unhandled pixel bitdepth in DX loader";
+    }
+
+    // if we've gotten here, haven't found a match
+    dxgsg9_cat.error() << szErrorMsg << ": " << _tex->get_name() << endl
+                      << "NumColorChannels: " <<cNumColorChannels << "; NumAlphaBits: " << cNumAlphaBits
+                      << "; targetbpp: " <<target_bpp << "; SupportedTexFmtsMask: 0x" << (void*)scrn.SupportedTexFmtsMask
+                      << "; NeedLuminance: " << bNeedLuminance << endl;
+    goto error_exit;
+
+    ///////////////////////////////////////////////////////////
+
+ found_matching_format:
+    // validate magfilter setting
+    // degrade filtering if no HW support
+
+    Texture::FilterType ft;
+
+    ft =_tex->get_magfilter();
+    if((ft!=Texture::FT_linear) && ft!=Texture::FT_nearest) {
+    // mipmap settings make no sense for magfilter
+        if(ft==Texture::FT_nearest_mipmap_nearest)
+            ft=Texture::FT_nearest;
+        else ft=Texture::FT_linear;
+    }
+
+    if((ft==Texture::FT_linear) && !(scrn.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFLINEAR))
+        ft=Texture::FT_nearest;
+    _tex->set_magfilter(ft);
+
+    // figure out if we are mipmapping this texture
+    ft =_tex->get_minfilter();
+    _bHasMipMaps=FALSE;
+
+    if(!dx_ignore_mipmaps) {  // set if no HW mipmap capable
+        switch(ft) {
+            case Texture::FT_nearest_mipmap_nearest:
+            case Texture::FT_linear_mipmap_nearest:
+            case Texture::FT_nearest_mipmap_linear:  // pick nearest in each, interpolate linearly b/w them
+            case Texture::FT_linear_mipmap_linear:
+                _bHasMipMaps=TRUE;
+        }
+
+        if(dx_mipmap_everything) {  // debug toggle, ok to leave in since its just a creation cost
+           _bHasMipMaps=TRUE;
+           if(dxgsg9_cat.is_spam()) {
+               if(ft != Texture::FT_linear_mipmap_linear)
+                   dxgsg9_cat.spam() << "Forcing trilinear mipmapping on DX texture [" << _tex->get_name() << "]\n";
+           }
+           ft = Texture::FT_linear_mipmap_linear;
+           _tex->set_minfilter(ft);
+        }
+    } else if((ft==Texture::FT_nearest_mipmap_nearest) ||   // cvt to no-mipmap filter types
+              (ft==Texture::FT_nearest_mipmap_linear)) {
+        ft=Texture::FT_nearest;
+    } else if((ft==Texture::FT_linear_mipmap_nearest) ||
+              (ft==Texture::FT_linear_mipmap_linear)) {
+        ft=Texture::FT_linear;
+    }
+
+    assert((scrn.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFPOINT)!=0);
+
+#define TRILINEAR_MIPMAP_TEXFILTERCAPS (D3DPTFILTERCAPS_MIPFLINEAR | D3DPTFILTERCAPS_MINFLINEAR)
+
+    // do any other filter type degradations necessary
+    switch(ft) {
+        case Texture::FT_linear_mipmap_linear:
+            if((scrn.d3dcaps.TextureFilterCaps & TRILINEAR_MIPMAP_TEXFILTERCAPS)!=TRILINEAR_MIPMAP_TEXFILTERCAPS) {
+               if(scrn.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)
+                   ft=Texture::FT_linear_mipmap_nearest;
+                else ft=Texture::FT_nearest_mipmap_nearest;  // if you cant do linear in a level, you probably cant do linear b/w levels, so just do nearest-all
+            }
+            break;
+        case Texture::FT_nearest_mipmap_linear:
+            // if we dont have bilinear, do nearest_nearest
+            if(!((scrn.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MIPFPOINT) &&
+                 (scrn.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR)))
+                ft=Texture::FT_nearest_mipmap_nearest;
+            break;
+        case Texture::FT_linear_mipmap_nearest:
+            // if we dont have mip linear, do nearest_nearest
+            if(!(scrn.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MIPFLINEAR))
+                ft=Texture::FT_nearest_mipmap_nearest;
+            break;
+        case Texture::FT_linear:
+            if(!(scrn.d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MINFLINEAR))
+                ft=Texture::FT_nearest;
+            break;
+    }
+
+    _tex->set_minfilter(ft);
+
+    uint aniso_degree;
+
+    aniso_degree=1;
+    if(scrn.d3dcaps.RasterCaps & D3DPRASTERCAPS_ANISOTROPY) {
+        aniso_degree=_tex->get_anisotropic_degree();
+        if((aniso_degree>scrn.d3dcaps.MaxAnisotropy) || dx_force_anisotropic_filtering)
+            aniso_degree=scrn.d3dcaps.MaxAnisotropy;
+    }
+    _tex->set_anisotropic_degree(aniso_degree);
+
+#ifdef _DEBUG
+    dxgsg9_cat.spam() << "CreateTexture: setting aniso degree for "<< _tex->get_name() << " to: " << aniso_degree << endl;
+#endif
+
+    UINT cMipLevelCount;
+
+    if(_bHasMipMaps) {
+        cMipLevelCount=0;  // tell CreateTex to alloc space for all mip levels down to 1x1
+
+        if(dxgsg9_cat.is_debug())
+            dxgsg9_cat.debug() << "CreateTexture: generating mipmaps for "<< _tex->get_name() << endl;
+    } else cMipLevelCount=1;
+
+    if(FAILED( hr = scrn.pD3DDevice->CreateTexture(TargetWidth,TargetHeight,cMipLevelCount,0x0,
+                                                   TargetPixFmt,D3DPOOL_MANAGED,&_pD3DTexture9, NULL) )) {
+        dxgsg9_cat.error() << "D3D CreateTexture failed!" << D3DERRORSTRING(hr);
+        goto error_exit;
+    }
+
+#ifdef DO_CUSTOM_CONVERSIONS
+    _PixBufConversionType=ConvNeeded;
+#endif
+
+#ifdef _DEBUG
+#ifdef DO_CUSTOM_CONVERSIONS
+    dxgsg9_cat.debug() << "CreateTexture: "<< _tex->get_name() <<" converting " << ConvNameStrs[ConvNeeded] << " \n";
+#else
+    dxgsg9_cat.debug() << "CreateTexture: "<< _tex->get_name() <<" converting panda equivalent of " << D3DFormatStr(_PixBufD3DFmt) << " => " << D3DFormatStr(TargetPixFmt) << endl;
+#endif
+#endif
+
+    // Note: user may want to create an empty "texture" that will be written to by rendering and copy operations.
+    //       this will never have a backing store of main memory in panda fmt, and on disk in a file.
+    //       so for this case, you dont want to call FillDDSurf.
+    //       need a better way for user to indicate this usage than lack of ram_image, because it conflicts
+    //       with the multi-open case mentioned below
+
+    if(_texture->has_ram_image()) {
+        hr = FillDDSurfTexturePixels();
+        if(FAILED(hr)) {
+            goto error_exit;
+        }
+    }
+
+    // PRINT_REFCNT(dxgsg9,scrn.pD3D9);
+
+    // Return the newly created texture
+    return _pD3DTexture9;
+
+  error_exit:
+
+    RELEASE(_pD3DTexture9,dxgsg9,"texture",RELEASE_ONCE);
+    return NULL;
+}
+
+HRESULT DXTextureContext9::
+FillDDSurfTexturePixels(void) {
+    HRESULT hr=E_FAIL;
+    assert(IS_VALID_PTR(_texture));
+
+    // It is a mistake to insist that has_ram_image() be true before
+    // we try to load the texture.  This function only indicates
+    // whether the texture image is already present in main ram or
+    // not; it has nothing to do with whether get_ram_image() will
+    // fail.  When there is only one GSG in the world, has_ram_image()
+    // will generally be true whenever the texture has not been loaded
+    // before, but when there are multiple GSG's (for instance, if we
+    // close and reopen the main window), then has_ram_image() is
+    // largely irrelevant to the GSG.
+    /*
+    if(!_texture->has_ram_image()) {
+      dxgsg9_cat.warning() << "CreateTexture: tried to fill surface that has no ram image!\n";
+      return S_OK;
+    }
+    */
+
+    PixelBuffer *pbuf = _texture->get_ram_image();
+    if (pbuf == (PixelBuffer *)NULL) {
+      dxgsg9_cat.fatal() << "CreateTexture: get_ram_image() failed\n";
+      // The texture doesn't have an image to load.
+      return E_FAIL;
+    }
+
+    assert(IS_VALID_PTR(_pD3DTexture9));
+
+    DWORD OrigWidth  = (DWORD) pbuf->get_xsize();
+    DWORD OrigHeight = (DWORD) pbuf->get_ysize();
+    DWORD cNumColorChannels = pbuf->get_num_components();
+    D3DFORMAT SrcFormat=_PixBufD3DFmt;
+    BYTE *pPixels=(BYTE*)pbuf->_image.p();
+
+    assert(IS_VALID_PTR(pPixels));
+
+    IDirect3DSurface9 *pMipLevel0;
+    hr=_pD3DTexture9->GetSurfaceLevel(0,&pMipLevel0);
+    if(FAILED(hr)) {
+       dxgsg9_cat.error() << "FillDDSurfaceTexturePixels failed for "<< _tex->get_name() <<", GetSurfaceLevel failed" << D3DERRORSTRING(hr);
+       return E_FAIL;
+    }
+
+    RECT SrcSize;
+    SrcSize.left = SrcSize.top = 0;
+    SrcSize.right = OrigWidth;
+    SrcSize.bottom = OrigHeight;
+
+    UINT SrcPixBufRowByteLength=OrigWidth*cNumColorChannels;
+
+    DWORD Lev0Filter,MipFilterFlags;
+    bool bUsingTempPixBuf=false;
+
+    // need filtering if size changes, (also if bitdepth reduced (need dithering)??)
+    Lev0Filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER;  //dithering looks ugly on i810 for 4444 textures
+
+    // D3DXLoadSurfaceFromMemory will load black luminance and we want full white,
+    // so convert to explicit luminance-alpha format
+    if(_PixBufD3DFmt==D3DFMT_A8) {
+        // alloc buffer for explicit D3DFMT_A8L8
+        USHORT *pTempPixBuf=new USHORT[OrigWidth*OrigHeight];
+        if(!IS_VALID_PTR(pTempPixBuf)) {
+            dxgsg9_cat.error() << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
+            goto exit_FillDDSurf;
+        }
+        bUsingTempPixBuf=true;
+
+        USHORT *pOutPix=pTempPixBuf;
+        BYTE *pSrcPix=pPixels;
+        for(UINT y=0;y<OrigHeight;y++)
+          for(UINT x=0;x<OrigWidth;x++,pSrcPix++,pOutPix++)
+              *pOutPix = ((*pSrcPix) << 8 ) | 0xFF;  // add full white, which is our interpretation of alpha-only (similar to default adding full opaque alpha 0xFF to RGB-only textures)
+
+        SrcFormat=D3DFMT_A8L8;
+        SrcPixBufRowByteLength=OrigWidth*sizeof(USHORT);
+        pPixels=(BYTE*)pTempPixBuf;
+    }
+
+    // filtering may be done here if texture if targetsize!=origsize
+    hr=D3DXLoadSurfaceFromMemory(pMipLevel0,(PALETTEENTRY*)NULL,(RECT*)NULL,(LPCVOID)pPixels,SrcFormat,
+                                 SrcPixBufRowByteLength,(PALETTEENTRY*)NULL,&SrcSize,Lev0Filter,(D3DCOLOR)0x0);
+    if(FAILED(hr)) {
+      dxgsg9_cat.error() << "FillDDSurfaceTexturePixels failed for "<< _tex->get_name() <<", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr);
+      goto exit_FillDDSurf;
+    }
+
+    if(_bHasMipMaps) {
+      if(!dx_use_triangle_mipgen_filter)
+        MipFilterFlags = D3DX_FILTER_BOX;
+      else MipFilterFlags = D3DX_FILTER_TRIANGLE;
+      
+      //    MipFilterFlags|= D3DX_FILTER_DITHER;
+      
+      hr=D3DXFilterTexture(_pD3DTexture9,(PALETTEENTRY*)NULL,0,MipFilterFlags);
+      if(FAILED(hr)) {
+        dxgsg9_cat.error() << "FillDDSurfaceTexturePixels failed for "<< _tex->get_name() <<", D3DXFilterTex failed" << D3DERRORSTRING(hr);
+        goto exit_FillDDSurf;
+      }
+    }
+
+ exit_FillDDSurf:
+    if(bUsingTempPixBuf) {
+      SAFE_DELETE_ARRAY(pPixels);
+    }
+    RELEASE(pMipLevel0,dxgsg9,"FillDDSurf MipLev0 texture ptr",RELEASE_ONCE);
+    return hr;
+}
+
+//-----------------------------------------------------------------------------
+// Name: DeleteTexture()
+// Desc: Release the surface used to store the texture
+//-----------------------------------------------------------------------------
+void DXTextureContext9::
+DeleteTexture( ) {
+    if(_pD3DTexture9==NULL) {
+        // dont bother printing the msg below, since we already released it.
+        return;
+    }
+
+    if(dxgsg9_cat.is_spam()) {
+        dxgsg9_cat.spam() << "Deleting DX texture for " << _tex->get_name() << "\n";
+    }
+
+    RELEASE(_pD3DTexture9,dxgsg9,"texture",RELEASE_ONCE);
+/*
+#ifdef DEBUG_RELEASES
+    if(_surface) {
+        LPDIRECTDRAW7 pDD;
+        _surface->GetDDInterface( (VOID**)&pDD );
+        pDD->Release();
+
+        PRINTREFCNT(pDD,"before DeleteTex, IDDraw7");
+        RELEASE(_surface,dxgsg9,"texture",false);
+        PRINTREFCNT(pDD,"after DeleteTex, IDDraw7");
+    }
+#else
+
+    RELEASE(_pD3DSurf9,dxgsg9,"texture",false);
+ #endif
+*/
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DXTextureContext9::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+DXTextureContext9::
+DXTextureContext9(Texture *tex) :
+TextureContext(tex) {
+
+    if(dxgsg9_cat.is_spam()) {
+       dxgsg9_cat.spam() << "Creating DX texture [" << tex->get_name() << "], minfilter(" << PandaFilterNameStrs[tex->get_minfilter()] << "), magfilter("<<PandaFilterNameStrs[tex->get_magfilter()] << "), anisodeg(" << tex->get_anisotropic_degree() << ")\n";
+    }
+
+    _pD3DTexture9 = NULL;
+    _bHasMipMaps = FALSE;
+    _tex = tex;
+}
+
+DXTextureContext9::
+~DXTextureContext9() {
+    if(dxgsg9_cat.is_spam()) {
+        dxgsg9_cat.spam() << "Deleting DX9 TexContext for " << _tex->get_name() << "\n";
+    }
+    DeleteTexture();
+    TextureContext::~TextureContext();
+    _tex = NULL;
+}
+

+ 80 - 0
panda/src/dxgsg9/dxTextureContext9.h

@@ -0,0 +1,80 @@
+// Filename: dxTextureContext8.h
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXTEXTURECONTEXT9_H
+#define DXTEXTURECONTEXT9_H
+
+#include "dxgsg9base.h"
+#include "texture.h"
+#include "textureContext.h"
+
+//#define DO_CUSTOM_CONVERSIONS
+
+////////////////////////////////////////////////////////////////////
+//   Class : DXTextureContext9
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX DXTextureContext9 : public TextureContext {
+  friend class DXGraphicsStateGuardian;
+  friend class wdxGraphicsWindow;
+
+public:
+  DXTextureContext9(Texture *tex);
+  ~DXTextureContext9();
+
+  IDirect3DTexture9  *_pD3DTexture9;
+  Texture *_tex;            // ptr to parent, primarily for access to namestr
+  IDirect3DTexture9 *CreateTexture(DXScreenData &scrn);
+
+  D3DFORMAT _PixBufD3DFmt;    // the 'D3DFORMAT' the Panda PixelBuffer fmt corresponds to
+
+  bool _bHasMipMaps;
+
+#ifdef DO_CUSTOM_CONVERSIONS
+  DWORD _PixBufConversionType;  // enum ConversionType
+#endif
+
+  // must be public since called from global callback fns
+  void DeleteTexture(void);
+  HRESULT FillDDSurfTexturePixels(void);
+
+protected:
+    unsigned int get_bits_per_pixel(PixelBuffer::Format format, int *alphbits);
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TextureContext::init_type();
+    register_type(_type_handle, "DXTextureContext9",
+          TextureContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+extern HRESULT ConvertD3DSurftoPixBuf(RECT &SrcRect,IDirect3DSurface9 *pD3DSurf9,PixelBuffer *pixbuf);
+
+#endif
+

+ 9 - 0
panda/src/dxgsg9/dxgsg9_composite1.cxx

@@ -0,0 +1,9 @@
+#include "dxgsg9base.h"
+#include "config_dxgsg9.cxx"
+#include "dxSavedFrameBuffer9.cxx"
+#include "dxTextureContext9.cxx"
+#include "dxGeomNodeContext9.cxx"
+#include "d3dfont9.cxx"
+#include "wdxGraphicsPipe9.cxx"
+#include "wdxGraphicsWindow9.cxx"
+#include "dxGraphicsDevice9.cxx"

+ 222 - 0
panda/src/dxgsg9/dxgsg9base.h

@@ -0,0 +1,222 @@
+// Filename: dxgsg8base.h
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef DXGSG9BASE_H
+#define DXGSG9BASE_H
+
+// include win32 defns for everything up to WinServer2003, and assume I'm smart enough to
+// use GetProcAddress for backward compat on newer fns
+// Note DX9 cannot be installed on w95, so OK to assume base of win98
+#define _WIN32_WINNT 0x0502
+
+#define WIN32_LEAN_AND_MEAN   // get rid of mfc win32 hdr stuff
+#ifndef STRICT
+// enable strict type checking in windows.h, see msdn
+#define STRICT
+#endif
+
+#include <windows.h>
+
+#define D3D_OVERLOADS   //  get D3DVECTOR '+' operator, etc from d3dtypes.h
+#include <d3d9.h>
+#include <d3dx9.h>
+#include <dxerr9.h>
+#undef WIN32_LEAN_AND_MEAN
+
+#include "pandabase.h"
+#include "graphicsWindow.h"
+
+#if D3D_SDK_VERSION != 31
+#error you have DX 8.0/8.1 headers, not DX 9, you need to install DX 9 SDK!
+#endif
+
+#if DIRECT3D_VERSION != 0x0900
+#error DX9 headers not available, you need to install newer MS Platform SDK!
+#endif
+
+#ifndef D3DCAPS3_ALPHA_FULLSCREEN_FLIP_OR_DISCARD
+#error you have pre-release DX8.1 headers, you need to install final DX 8.1 SDK!
+#endif
+
+#ifndef D3DERRORSTRING
+#ifdef NDEBUG
+#define D3DERRORSTRING(HRESULT) " at (" << __FILE__ << ":" << __LINE__ << "), hr=" <<  DXGetErrorString9(HRESULT) << endl  // leave out descriptions to shrink release build
+#else
+#define D3DERRORSTRING(HRESULT) " at (" << __FILE__ << ":" << __LINE__ << "), hr=" <<  DXGetErrorString9(HRESULT) << ": " << DXGetErrorDescription9(HRESULT) << endl
+#endif
+#endif
+
+// imperfect method to ID NVid? could also scan desc str, but that isnt fullproof either
+#define IS_NVIDIA(DDDEVICEID) ((DDDEVICEID.VendorId==0x10DE) || (DDDEVICEID.VendorId==0x12D2))
+#define IS_ATI(DDDEVICEID) (DDDEVICEID.VendorId==0x1002)
+#define IS_MATROX(DDDEVICEID) (DDDEVICEID.VendorId==0x102B)
+
+#define D3D_MAXTEXTURESTAGES 8
+
+typedef enum {VertexShader,PixelShader} ShaderType;
+typedef DWORD DXShaderHandle;
+
+#define ISPOW2(X) (((X) & ((X)-1))==0)
+#define IS_VALID_PTR(PTR)  (!IsBadWritePtr(PTR,sizeof(void*)))
+
+#define DX_DECLARE_CLEAN(type, var) \
+    type var;                       \
+    ZeroMemory(&var, sizeof(type)); \
+    var.dwSize = sizeof(type);
+    
+#define SAFE_DELSHADER(TYPE,HANDLE,PDEVICE)  \
+  if((HANDLE!=NULL)&&IS_VALID_PTR(PDEVICE)) { PDEVICE->Delete##TYPE##Shader(HANDLE);  HANDLE=NULL; }
+
+#define SAFE_DELETE(p)       { if(p) { assert(IS_VALID_PTR(p));   delete (p);     (p)=NULL; } }
+#define SAFE_DELETE_ARRAY(p) { if(p) { assert(IS_VALID_PTR(p));   delete [] (p);   (p)=NULL; } }
+
+// for stuff outside a panda class
+#define SAFE_RELEASE(p)      { if(p) { assert(IS_VALID_PTR(p)); (p)->Release(); (p)=NULL; } }
+#define SAFE_FREELIB(hDLL)   { if(hDLL!=NULL) {  FreeLibrary(hDLL);hDLL = NULL; } }
+
+// this is bDoDownToZero argument to RELEASE()
+#define RELEASE_DOWN_TO_ZERO true
+#define RELEASE_ONCE false
+
+
+// uncomment to add refcnt debug output 
+#define DEBUG_RELEASES
+
+#ifdef DEBUG_RELEASES
+#define RELEASE(OBJECT,MODULE,DBGSTR,bDoDownToZero)             {  \
+   ULONG refcnt;                                                \
+   if(IS_VALID_PTR(OBJECT)) {                                   \
+        refcnt = (OBJECT)->Release();                           \
+        MODULE##_cat.debug() << DBGSTR << " released, refcnt = " << refcnt << " at " << __FILE__ << ":" << __LINE__ << endl; \
+        if((bDoDownToZero) && (refcnt>0)) {                     \
+              MODULE##_cat.warning() << DBGSTR << " released but still has a non-zero refcnt(" << refcnt << "), multi-releasing it down to zero!\n"; \
+              do {                                \
+                refcnt = (OBJECT)->Release();     \
+              } while(refcnt>0);                  \
+        }                                         \
+        (OBJECT) = NULL;                          \
+      } else {                                    \
+        MODULE##_cat.debug() << DBGSTR << " not released, ptr == NULL" << endl;  \
+      }}
+
+#define PRINT_REFCNT(MODULE,p) { ULONG refcnt;  (p)->AddRef();  refcnt=(p)->Release(); \
+                                 MODULE##_cat.debug() << #p << " has refcnt = " << refcnt << " at " << __FILE__ << ":" << __LINE__ << endl; }
+                                 
+#else
+#define RELEASE(OBJECT,MODULE,DBGSTR,bDoDownToZero)   { \
+   ULONG refcnt;                                        \
+   if(IS_VALID_PTR(OBJECT))                           { \
+        refcnt=(OBJECT)->Release();                     \
+        if((bDoDownToZero) && (refcnt>0)) {             \
+              MODULE##_cat.warning() << DBGSTR << " released but still has a non-zero refcnt(" << refcnt << "), multi-releasing it down to zero!\n"; \
+              do {                                \
+                refcnt = (OBJECT)->Release();     \
+              } while(refcnt>0);                  \
+        }                                         \
+        (OBJECT) = NULL;                          \
+   }}
+
+#define PRINT_REFCNT(MODULE,p)
+#endif    
+
+#ifdef DO_PSTATS
+#define DO_PSTATS_STUFF(XX) XX;
+#else
+#define DO_PSTATS_STUFF(XX)
+#endif
+
+#define PANDA_MAXNUMVERTS 0xFFFF  // Note Device may support more than this if it supports D3DFMT_INDEX32 indexbufs.
+
+#define FLG(NN) (1<<NN)
+#define MAX_POSSIBLE_TEXFMTS 32
+typedef enum {
+    R8G8B8_FLAG =       FLG(0),
+    A8R8G8B8_FLAG =     FLG(1),
+    X8R8G8B8_FLAG =     FLG(2),
+    R5G6B5_FLAG =       FLG(3),
+    X1R5G5B5_FLAG =     FLG(4),
+    A1R5G5B5_FLAG =     FLG(5),
+    A4R4G4B4_FLAG =     FLG(6),
+    R3G3B2_FLAG =       FLG(7),
+    A8_FLAG =           FLG(8),
+    A8R3G3B2_FLAG =     FLG(9),
+    X4R4G4B4_FLAG =     FLG(10),
+    A2B10G10R10_FLAG =  FLG(11),
+    G16R16_FLAG =       FLG(12),
+    A8P8_FLAG =         FLG(13),
+    P8_FLAG =           FLG(14),
+    L8_FLAG =           FLG(15),
+    A8L8_FLAG =         FLG(16),
+    A4L4_FLAG =         FLG(17),
+    V8U8_FLAG =         FLG(18),
+    L6V5U5_FLAG =       FLG(19),
+    X8L8V8U8_FLAG =     FLG(20),
+    Q8W8V8U8_FLAG =     FLG(21),
+    V16U16_FLAG =       FLG(22),
+    W11V11U10_FLAG =    FLG(23),
+    A2W10V10U10_FLAG =  FLG(24),
+    UYVY_FLAG =         FLG(25),
+    YUY2_FLAG =         FLG(26),
+    DXT1_FLAG =         FLG(27),
+    DXT2_FLAG =         FLG(28),
+    DXT3_FLAG =         FLG(29),
+    DXT4_FLAG =         FLG(30),
+    DXT5_FLAG =         FLG(31)
+} D3DFORMAT_FLAG;
+
+// this is only used in conjunction w/rendertgt fmts, so just make it something that can never be a rtgt
+#define DISPLAY_32BPP_REQUIRES_16BPP_ZBUFFER_FLAG DXT1_FLAG
+#define DISPLAY_16BPP_REQUIRES_16BPP_ZBUFFER_FLAG DXT2_FLAG
+
+#define IS_16BPP_DISPLAY_FORMAT(FMT) (((FMT)==D3DFMT_R5G6B5)||((FMT)==D3DFMT_X1R5G5B5)||((FMT)==D3DFMT_A1R5G5B5))
+#define IS_16BPP_ZBUFFER(FMT) ((FMT==D3DFMT_D16)||(FMT==D3DFMT_D15S1))
+#define IS_STENCIL_FORMAT(FMT) (((FMT)==D3DFMT_D24S8) || ((FMT)==D3DFMT_D15S1) || ((FMT)==D3DFMT_D24X4S4))
+#define RECT_XSIZE(REC) (REC.right-REC.left)
+#define RECT_YSIZE(REC) (REC.bottom-REC.top)
+
+typedef struct {
+      LPDIRECT3DDEVICE9 pD3DDevice;
+      IDirect3DSwapChain9 *pSwapChain;
+      LPDIRECT3D9       pD3D9;  // copied from DXGraphicsPipe9 for convenience
+      HWND              hWnd;
+      HMONITOR          hMon;
+      DWORD             MaxAvailVidMem;
+      ushort            CardIDNum;  // adapter ID
+      ushort            depth_buffer_bitdepth;  //GetSurfaceDesc is not reliable so must store this explicitly
+      bool              bCanDirectDisableColorWrites;  // if true, dont need blending for this
+      bool              bIsLowVidMemCard;
+      bool              bIsTNLDevice;
+      bool              bCanUseHWVertexShaders;
+      bool              bCanUsePixelShaders;
+      bool              bIsDX9;
+      UINT              SupportedScreenDepthsMask;
+      UINT              SupportedTexFmtsMask;
+      D3DCAPS9          d3dcaps;
+      D3DDISPLAYMODE    DisplayMode;
+      D3DPRESENT_PARAMETERS PresParams;  // not redundant with DisplayMode since width/height must be 0 for windowed mode
+      D3DADAPTER_IDENTIFIER9 DXDeviceID;
+} DXScreenData;
+
+
+//utility stuff
+extern map<D3DFORMAT_FLAG,D3DFORMAT> g_D3DFORMATmap;
+extern void Init_D3DFORMAT_map(void);
+extern const char *D3DFormatStr(D3DFORMAT fmt);
+
+#endif
+

+ 17 - 0
panda/src/dxgsg9/wdxGraphicsPipe9.I

@@ -0,0 +1,17 @@
+// Filename: wdxGraphicsPipe8.I
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////

+ 946 - 0
panda/src/dxgsg9/wdxGraphicsPipe9.cxx

@@ -0,0 +1,946 @@
+// Filename: wdxGraphicsPipe9.cxx
+// Created by:  masad (05Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "wdxGraphicsPipe9.h"
+#include "dxGraphicsDevice9.h"
+#include "wdxGraphicsWindow9.h"
+#include "config_dxgsg9.h"
+
+TypeHandle wdxGraphicsPipe9::_type_handle;
+
+// #define LOWVIDMEMTHRESHOLD 3500000
+#define LOWVIDMEMTHRESHOLD 5700000  // 4MB cards should fall below this
+#define CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD 1000000  // if # is > 1MB, card is lying and I cant tell what it is
+#define UNKNOWN_VIDMEM_SIZE 0xFFFFFFFF
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+wdxGraphicsPipe9::
+wdxGraphicsPipe9() {
+  _hDDrawDLL = NULL;
+  _hD3D9_DLL = NULL;
+  _pD3D9 = NULL;
+  _is_valid = init();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::Destructor
+//       Access: Public, Virtual
+//  Description: 
+////////////////////////////////////////////////////////////////////
+wdxGraphicsPipe9::
+~wdxGraphicsPipe9() {
+
+  RELEASE(_pD3D9,wdxdisplay9,"ID3D9",RELEASE_DOWN_TO_ZERO);
+  SAFE_FREELIB(_hD3D9_DLL);
+  SAFE_FREELIB(_hDDrawDLL);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::get_interface_name
+//       Access: Published, Virtual
+//  Description: Returns the name of the rendering interface
+//               associated with this GraphicsPipe.  This is used to
+//               present to the user to allow him/her to choose
+//               between several possible GraphicsPipes available on a
+//               particular platform, so the name should be meaningful
+//               and unique for a given platform.
+////////////////////////////////////////////////////////////////////
+string wdxGraphicsPipe9::
+get_interface_name() const {
+  return "DirectX9";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::pipe_constructor
+//       Access: Public, Static
+//  Description: This function is passed to the GraphicsPipeSelection
+//               object to allow the user to make a default
+//               wdxGraphicsPipe9.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsPipe) wdxGraphicsPipe9::
+pipe_constructor() {
+  return new wdxGraphicsPipe9;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::make_window
+//       Access: Protected, Virtual
+//  Description: Creates a new window on the pipe, if possible.
+////////////////////////////////////////////////////////////////////
+PT(GraphicsWindow) wdxGraphicsPipe9::
+make_window(GraphicsStateGuardian *gsg) {
+  if (!_is_valid) {
+    return NULL;
+  }
+
+  // thanks to the dumb threading requirements this constructor actually does nothing but create an empty c++ object
+  // no windows are really opened until wdxGraphicsWindow9->open_window() is called
+  return new wdxGraphicsWindow9(this, gsg);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::init
+//       Access: Private
+//  Description: Performs some initialization steps to load up
+//               function pointers from the relevant DLL's, and
+//               determine the number and type of available graphics
+//               adapters, etc.  Returns true on success, false on
+//               failure.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsPipe9::
+init() {
+  if(!MyLoadLib(_hDDrawDLL,"ddraw.dll")) {
+      goto error;
+  }
+
+  if(!MyGetProcAddr(_hDDrawDLL, (FARPROC*)&_DirectDrawCreateEx, "DirectDrawCreateEx")) {
+      goto error;
+  }
+
+  if(!MyGetProcAddr(_hDDrawDLL, (FARPROC*)&_DirectDrawEnumerateExA, "DirectDrawEnumerateExA")) {
+      goto error;
+  }
+
+  if(!MyLoadLib(_hD3D9_DLL,"d3d9.dll")) {
+      goto error;
+  }
+
+  if(!MyGetProcAddr(_hD3D9_DLL, (FARPROC*)&_Direct3DCreate9, "Direct3DCreate9")) {
+      goto error;
+  }
+/*
+  wdxGraphicsPipe9 *dxpipe;
+  DCAST_INTO_V(dxpipe, _pipe);
+
+  nassertv(_gsg == (GraphicsStateGuardian *)NULL);
+  _dxgsg = new DXGraphicsStateGuardian9(this);
+  _gsg = _dxgsg;
+
+  // Tell the associated dxGSG about the window handle.
+  _dxgsg->scrn.hWnd = _hWnd;
+ */
+
+  // Create a Direct3D object.
+
+  // these were taken from the 9.0 and 9.0b d3d9.h SDK headers
+  #define D3D_SDK_VERSION_9_a  31
+  //#define D3D_SDK_VERSION_9_b   don't know yet
+
+  /*
+  // are we using 9.0 or 9.0b?
+  WIN32_FIND_DATA TempFindData;
+  HANDLE hFind;
+  char tmppath[_MAX_PATH + 128];
+  GetSystemDirectory(tmppath, MAX_PATH);
+  strcat(tmppath, "\\dpnhpast.dll");
+  hFind = FindFirstFile (tmppath, &TempFindData);
+  if (hFind != INVALID_HANDLE_VALUE) {
+    FindClose(hFind);
+    _bIsDX9 = true;
+    _pD3D9 = (*_Direct3DCreate9)(D3D_SDK_VERSION_9);
+  } else {
+    _bIsDX91 = false;
+    _pD3D9 = (*_Direct3DCreate9)(D3D_SDK_VERSION_9);
+  }
+  */
+
+  // I think a simpler check is to look in your d3d9.h for version
+  if (D3D_SDK_VERSION == 31) {
+    _bIsDX9 = true;
+    _pD3D9 = (*_Direct3DCreate9)(D3D_SDK_VERSION);
+  }
+
+  if (_pD3D9 == NULL) {
+    wdxdisplay9_cat.error() << "Direct3DCreate9(9." << (_bIsDX9 ? "1" : "0") << ") failed!, error=" << GetLastError() << endl;
+    //release_gsg();
+    goto error;
+  }
+
+  Init_D3DFORMAT_map();
+
+  return find_all_card_memavails();
+
+  error:
+    // wdxdisplay9_cat.error() << ", error=" << GetLastError << endl;
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::find_all_card_memavails
+//       Access: Private
+//  Description: Uses DX7 calls to determine how much video memory is
+//               available for each video adapter in the system.
+//               Returns true on success, false on failure.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsPipe9::
+find_all_card_memavails() {
+  HRESULT hr;
+
+  hr = (*_DirectDrawEnumerateExA)(dx7_driver_enum_callback, this, 
+                                  DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES);
+  if (FAILED(hr)) {
+    wdxdisplay9_cat.fatal()
+      << "DirectDrawEnumerateEx failed" << D3DERRORSTRING(hr);
+    return false;
+  }
+
+  if (_card_ids.empty()) {
+    wdxdisplay9_cat.error()
+      << "DirectDrawEnumerateEx enum'ed no devices!\n";
+    return false;
+  }
+
+  GUID ZeroGUID;
+  ZeroMemory(&ZeroGUID, sizeof(GUID));
+
+  if (_card_ids.size() > 1) {
+    assert(IsEqualGUID(ZeroGUID, _card_ids[0].DX7_DeviceGUID));
+    // delete enum of primary display (always the first), since it is
+    // duplicated by explicit entry
+    _card_ids.erase(_card_ids.begin());
+  }
+
+  for (UINT i=0; i < _card_ids.size(); i++) {
+    LPDIRECTDRAW7 pDD;
+    BYTE ddd_space[sizeof(DDDEVICEIDENTIFIER2)+4];  //bug in DX7 requires 4 extra bytes for GetDeviceID
+    DDDEVICEIDENTIFIER2 *pDX7DeviceID=(DDDEVICEIDENTIFIER2 *)&ddd_space[0];
+    GUID *pGUID= &(_card_ids[i].DX7_DeviceGUID);
+
+    if (IsEqualGUID(*pGUID, ZeroGUID)) {
+      pGUID=NULL;
+    }
+
+    // Create the Direct Draw Object
+    hr = (*_DirectDrawCreateEx)(pGUID,(void **)&pDD, IID_IDirectDraw7, NULL);
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.error()
+        << "DirectDrawCreateEx failed for device (" << i
+        << ")" << D3DERRORSTRING(hr);
+      continue;
+    }
+
+    ZeroMemory(ddd_space, sizeof(DDDEVICEIDENTIFIER2));
+
+    hr = pDD->GetDeviceIdentifier(pDX7DeviceID, 0x0);
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.error()
+        << "GetDeviceID failed for device ("<< i << ")" << D3DERRORSTRING(hr);
+      continue;
+    }
+
+    _card_ids[i].DeviceID = pDX7DeviceID->dwDeviceId;
+    _card_ids[i].VendorID = pDX7DeviceID->dwVendorId;
+
+    // Get Current VidMem avail.  Note this is only an estimate, when
+    // we switch to fullscreen mode from desktop, more vidmem will be
+    // available (typically 1.2 meg).  I dont want to switch to
+    // fullscreen more than once due to the annoying monitor flicker,
+    // so try to figure out optimal mode using this estimate
+    DDSCAPS2 ddsGAVMCaps;
+    DWORD dwVidMemTotal,dwVidMemFree;
+    dwVidMemTotal=dwVidMemFree=0;
+    {
+      // print out total INCLUDING AGP just for information purposes
+      // and future use.  The real value I'm interested in for
+      // purposes of measuring possible valid screen sizes shouldnt
+      // include AGP.
+      ZeroMemory(&ddsGAVMCaps, sizeof(DDSCAPS2));
+      ddsGAVMCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
+
+      hr = pDD->GetAvailableVidMem(&ddsGAVMCaps, &dwVidMemTotal, &dwVidMemFree);
+      if (FAILED(hr)) {
+        wdxdisplay9_cat.error()
+          << "GetAvailableVidMem failed for device #" << i 
+          << D3DERRORSTRING(hr);
+        //goto skip_device;
+        //exit(1);  // probably want to exit, since it may be my fault
+      }
+    }
+
+    wdxdisplay9_cat.info()
+      << "GetAvailableVidMem (including AGP) returns Total: "
+      << dwVidMemTotal <<", Free: " << dwVidMemFree
+      << " for device #" << i << endl;
+
+    ZeroMemory(&ddsGAVMCaps, sizeof(DDSCAPS2));
+
+    // just want to measure localvidmem, not AGP texmem
+    ddsGAVMCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
+
+    hr = pDD->GetAvailableVidMem(&ddsGAVMCaps, &dwVidMemTotal, &dwVidMemFree);
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.error() << "GetAvailableVidMem failed for device #"<< i<< D3DERRORSTRING(hr);
+      // sometimes GetAvailableVidMem fails with hr=DDERR_NODIRECTDRAWHW for some unknown reason (bad drivers?)
+      // see bugs: 15327,18122, others.  is it because D3D8 object has already been created?
+      if(hr==DDERR_NODIRECTDRAWHW)
+          continue;
+      exit(1);  // probably want to exit, since it may be my fault
+    }
+
+    wdxdisplay9_cat.info()
+      << "GetAvailableVidMem (no AGP) returns Total: " << dwVidMemTotal
+      << ", Free: " << dwVidMemFree << " for device #"<< i<< endl;
+
+    pDD->Release();  // release DD obj, since this is all we needed it for
+
+    if (!dx_do_vidmemsize_check) {
+      // still calling the DD stuff to get deviceID, etc.  is this necessary?
+      _card_ids[i].MaxAvailVidMem = UNKNOWN_VIDMEM_SIZE;
+      _card_ids[i].bIsLowVidMemCard = false;
+      continue;
+    }
+
+    if (dwVidMemTotal == 0) {  // unreliable driver
+      dwVidMemTotal = UNKNOWN_VIDMEM_SIZE;
+    } else {
+      if (!ISPOW2(dwVidMemTotal)) {
+        // assume they wont return a proper max value, so
+        // round up to next pow of 2
+        UINT count=0;
+        while ((dwVidMemTotal >> count) != 0x0) {
+          count++;
+        }
+        dwVidMemTotal = (1 << count);
+      }
+    }
+
+    // after SetDisplayMode, GetAvailVidMem totalmem seems to go down
+    // by 1.2 meg (contradicting above comment and what I think would
+    // be correct behavior (shouldnt FS mode release the desktop
+    // vidmem?), so this is the true value
+    _card_ids[i].MaxAvailVidMem = dwVidMemTotal;
+
+    // I can never get this stuff to work reliably, so I'm just
+    // rounding up to nearest pow2.  Could try to get
+    // HardwareInformation.Memory_size MB number from registry like
+    // video control panel, but its not clear how to find the proper
+    // registry location for a given card
+
+    // assume buggy drivers (this means you, FireGL2) may return zero
+    // (or small amts) for dwVidMemTotal, so ignore value if its < CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD
+    bool bLowVidMemFlag = 
+      ((dwVidMemTotal > CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD) && 
+       (dwVidMemTotal< LOWVIDMEMTHRESHOLD));
+
+    _card_ids[i].bIsLowVidMemCard = bLowVidMemFlag;
+    wdxdisplay9_cat.info() 
+      << "SetLowVidMem flag to " << bLowVidMemFlag
+      << " based on adjusted VidMemTotal: " << dwVidMemTotal << endl;
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipe9::dx7_driver_enum_callback
+//       Access: Private, Static
+//  Description: 
+////////////////////////////////////////////////////////////////////
+BOOL WINAPI wdxGraphicsPipe9::
+dx7_driver_enum_callback(GUID *pGUID, TCHAR *strDesc, TCHAR *strName,
+                         VOID *argptr, HMONITOR hm) {
+  //    #define PRNT(XX) ((XX!=NULL) ? XX : "NULL")
+  //    cout << "strDesc: "<< PRNT(strDesc) << "  strName: "<< PRNT(strName)<<endl;
+  wdxGraphicsPipe9 *self = (wdxGraphicsPipe9 *)argptr;
+
+  CardID card_id;
+  ZeroMemory(&card_id, sizeof(CardID));
+
+  if (hm == NULL) {
+    card_id.hMon = MonitorFromWindow(GetDesktopWindow(), 
+                                     MONITOR_DEFAULTTOPRIMARY);
+  } else {
+    card_id.hMon = hm;
+  }
+
+  if (pGUID != NULL) {
+    memcpy(&card_id.DX7_DeviceGUID, pGUID, sizeof(GUID));
+  }
+
+  card_id.MaxAvailVidMem = UNKNOWN_VIDMEM_SIZE;
+
+  self->_card_ids.push_back(card_id);
+
+  return DDENUMRET_OK;
+}
+
+//////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::find_best_depth_format
+//       Access: Private
+//  Description: 
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsPipe9::
+find_best_depth_format(DXScreenData &Display, D3DDISPLAYMODE &TestDisplayMode,
+                       D3DFORMAT *pBestFmt, bool bWantStencil,
+                       bool bForce16bpp, bool bVerboseMode) const {
+  // list fmts in order of preference
+#define NUM_TEST_ZFMTS 3
+  static D3DFORMAT NoStencilPrefList[NUM_TEST_ZFMTS]={D3DFMT_D32,D3DFMT_D24X8,D3DFMT_D16};
+  static D3DFORMAT StencilPrefList[NUM_TEST_ZFMTS]={D3DFMT_D24S8,D3DFMT_D24X4S4,D3DFMT_D15S1};
+
+  // do not use Display.DisplayMode since that is probably not set yet, use TestDisplayMode instead
+
+  // int want_color_bits = _props._want_color_bits;
+  // int want_depth_bits = _props._want_depth_bits;  should we pay attn to these so panda user can select bitdepth?
+
+  *pBestFmt = D3DFMT_UNKNOWN;
+  HRESULT hr;
+
+    // nvidia likes zbuf depth to match rendertarget depth
+  bool bOnlySelect16bpp = (bForce16bpp ||
+                           (IS_NVIDIA(Display.DXDeviceID) && IS_16BPP_DISPLAY_FORMAT(TestDisplayMode.Format)));
+
+  if (bVerboseMode) {
+    wdxdisplay9_cat.info()
+      << "FindBestDepthFmt: bSelectOnly16bpp: " << bOnlySelect16bpp << endl;
+  }
+
+  for (int i=0; i < NUM_TEST_ZFMTS; i++) {
+    D3DFORMAT TestDepthFmt = 
+      (bWantStencil ? StencilPrefList[i] : NoStencilPrefList[i]);
+
+    if (bOnlySelect16bpp && !IS_16BPP_ZBUFFER(TestDepthFmt)) {
+      continue;
+    }
+
+    hr = Display.pD3D9->CheckDeviceFormat(Display.CardIDNum,
+                                          D3DDEVTYPE_HAL,
+                                          TestDisplayMode.Format,
+                                          D3DUSAGE_DEPTHSTENCIL,
+                                          D3DRTYPE_SURFACE,TestDepthFmt);
+
+    if (FAILED(hr)) {
+      if (hr == D3DERR_NOTAVAILABLE) {
+        if (bVerboseMode)
+          wdxdisplay9_cat.info() 
+            << "FindBestDepthFmt: ChkDevFmt returns NotAvail for " 
+            << D3DFormatStr(TestDepthFmt) << endl;
+        continue;
+      }
+
+      wdxdisplay9_cat.error()
+        << "unexpected CheckDeviceFormat failure" << D3DERRORSTRING(hr) 
+        << endl;
+      exit(1);
+    }
+
+    hr = Display.pD3D9->CheckDepthStencilMatch(Display.CardIDNum,
+                                               D3DDEVTYPE_HAL,
+                                               TestDisplayMode.Format,   // adapter format
+                                               TestDisplayMode.Format,   // backbuffer fmt  (should be the same in my apps)
+                                               TestDepthFmt);
+    if (SUCCEEDED(hr)) {
+      *pBestFmt = TestDepthFmt;
+      break;
+    } else {
+      if (hr==D3DERR_NOTAVAILABLE) {
+        if (bVerboseMode) {
+          wdxdisplay9_cat.info()
+            << "FindBestDepthFmt: ChkDepMatch returns NotAvail for "
+            << D3DFormatStr(TestDisplayMode.Format) << ", " 
+            << D3DFormatStr(TestDepthFmt) << endl;
+        }
+      } else {
+        wdxdisplay9_cat.error()
+          << "unexpected CheckDepthStencilMatch failure for "
+          << D3DFormatStr(TestDisplayMode.Format) << ", " 
+          << D3DFormatStr(TestDepthFmt) << endl;
+        exit(1);
+      }
+    }
+  }
+
+  if (bVerboseMode) {
+    wdxdisplay9_cat.info()
+      << "FindBestDepthFmt returns fmt " << D3DFormatStr(*pBestFmt) << endl;
+  }
+
+  return (*pBestFmt != D3DFMT_UNKNOWN);
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::special_check_fullscreen_resolution
+//       Access: Private
+//  Description: overrides of the general estimator for known working
+//               cases
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsPipe9::
+special_check_fullscreen_resolution(DXScreenData &scrn,UINT x_size,UINT y_size) {
+  DWORD VendorId = scrn.DXDeviceID.VendorId;
+  DWORD DeviceId = scrn.DXDeviceID.DeviceId;
+
+  switch (VendorId) {
+      case 0x8086:  // Intel
+        /*for now, just validate all the intel cards at these resolutions.
+          I dont have a complete list of intel deviceIDs (missing 82830, 845, etc)
+          // Intel i810,i815,82810
+          if ((DeviceId==0x7121)||(DeviceId==0x7123)||(DeviceId==0x7125)||
+          (DeviceId==0x1132))
+        */
+        if ((x_size == 640) && (y_size == 480)) {
+          return true;
+        }
+        if ((x_size == 800) && (y_size == 600)) {
+          return true;
+        }
+        if ((x_size == 1024) && (y_size == 768)) {
+          return true;
+        }
+        break;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::search_for_valid_displaymode
+//       Access: Private
+//  Description: All ptr args are output parameters.  If no valid mode
+//               found, returns *pSuggestedPixFmt = D3DFMT_UNKNOWN;
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsPipe9::
+search_for_valid_displaymode(DXScreenData &scrn,
+                             UINT RequestedX_Size, UINT RequestedY_Size,
+                             bool bWantZBuffer, bool bWantStencil,
+                             UINT *pSupportedScreenDepthsMask,
+                             bool *pCouldntFindAnyValidZBuf,
+                             D3DFORMAT *pSuggestedPixFmt,
+                             bool bForce16bppZBuffer,
+                             bool bVerboseMode) {
+
+  assert(IS_VALID_PTR(scrn.pD3D9));
+  HRESULT hr;
+
+#ifndef NDEBUG
+  //   no longer true, due to special_check_fullscreen_res, where lowvidmem cards are allowed higher resolutions
+  //    if (_dxgsg->scrn.bIsLowVidMemCard)
+  //        nassertv((RequestedX_Size==640)&&(RequestedY_Size==480));
+#endif
+
+  *pSuggestedPixFmt = D3DFMT_UNKNOWN;
+  *pSupportedScreenDepthsMask = 0x0;
+  *pCouldntFindAnyValidZBuf = false;
+
+  int cNumModes = scrn.pD3D9->GetAdapterModeCount(scrn.CardIDNum, D3DFMT_A8R8G8B8);
+  D3DDISPLAYMODE BestDispMode;
+  ZeroMemory(&BestDispMode,sizeof(BestDispMode));
+
+  //  if (bVerboseMode)
+    {
+    wdxdisplay9_cat.info()
+      << "searching for valid display modes at res: ("
+      << RequestedX_Size << "," << RequestedY_Size
+      << "), TotalModes: " << cNumModes << endl;
+  }
+
+  // ignore memory based checks for min res 640x480.  some cards just
+  // dont give accurate memavails.  (should I do the check anyway for
+  // 640x480 32bpp?)
+  bool bDoMemBasedChecks = 
+    ((!((RequestedX_Size==640)&&(RequestedY_Size==480))) &&
+     (scrn.MaxAvailVidMem!=UNKNOWN_VIDMEM_SIZE) &&
+     (!special_check_fullscreen_resolution(scrn,RequestedX_Size,RequestedY_Size)));
+
+  if (bVerboseMode || wdxdisplay9_cat.is_spam()) {
+    wdxdisplay9_cat.info()
+      << "DoMemBasedChecks = " << bDoMemBasedChecks << endl;
+  }
+
+  for (int i=0; i < cNumModes; i++) {
+    D3DDISPLAYMODE dispmode;
+    hr = scrn.pD3D9->EnumAdapterModes(scrn.CardIDNum,D3DFMT_A8R8G8B8,i,&dispmode);
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.error()
+        << "EnumAdapterDisplayMode failed for device #"
+        << scrn.CardIDNum << D3DERRORSTRING(hr);
+      exit(1);
+    }
+
+    if ((dispmode.Width!=RequestedX_Size) ||
+        (dispmode.Height!=RequestedY_Size)) {
+      continue;
+    }
+
+    if ((dispmode.RefreshRate<60) && (dispmode.RefreshRate>1)) {
+      // dont want refresh rates under 60Hz, but 0 or 1 might indicate
+      // a default refresh rate, which is usually >=60
+      if (bVerboseMode) {
+        wdxdisplay9_cat.info()
+          << "skipping mode[" << i << "], bad refresh rate: " 
+          << dispmode.RefreshRate << endl;
+      }
+      continue;
+    }
+
+    // Note no attempt is made to verify if format will work at
+    // requested size, so even if this call succeeds, could still get
+    // an out-of-video-mem error
+
+    hr = scrn.pD3D9->CheckDeviceFormat(scrn.CardIDNum, D3DDEVTYPE_HAL, dispmode.Format,
+                                               D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE,
+                                               dispmode.Format);
+    if (FAILED(hr)) {
+      if (hr==D3DERR_NOTAVAILABLE) {
+        if (bVerboseMode) {
+          wdxdisplay9_cat.info()
+            << "skipping mode[" << i 
+            << "], CheckDevFmt returns NotAvail for fmt: "
+            << D3DFormatStr(dispmode.Format) << endl;
+        }
+        continue;
+      } else {
+        wdxdisplay9_cat.error()
+          << "CheckDeviceFormat failed for device #" 
+          << scrn.CardIDNum << D3DERRORSTRING(hr);
+        exit(1);
+      }
+    }
+
+    bool bIs16bppRenderTgt = IS_16BPP_DISPLAY_FORMAT(dispmode.Format);
+    float RendTgtMinMemReqmt;
+
+    // if we have a valid memavail value, try to determine if we have
+    // enough space
+    if (bDoMemBasedChecks) {
+      // assume user is testing fullscreen, not windowed, so use the
+      // dwTotal value see if 3 scrnbufs (front/back/z)at 16bpp at
+      // x_size*y_size will fit with a few extra megs for texmem
+
+      // 8MB Rage Pro says it has 6.8 megs Total free and will run at
+      // 1024x768, so formula makes it so that is OK
+
+#define REQD_TEXMEM 1800000
+
+      float bytes_per_pixel = (bIs16bppRenderTgt ? 2 : 4);
+      
+//    cant do this check yet since gsg doesnt exist!
+//    assert((_gsg->get_properties().get_frame_buffer_mode() & FrameBufferProperties::FM_double_buffer) != 0);
+
+      // *2 for double buffer
+
+      RendTgtMinMemReqmt = 
+        ((float)RequestedX_Size) * ((float)RequestedY_Size) * 
+        bytes_per_pixel * 2 + REQD_TEXMEM;
+
+      if (bVerboseMode || wdxdisplay9_cat.is_spam())
+        wdxdisplay9_cat.info()
+          << "Testing Mode (" <<RequestedX_Size<<"x" << RequestedY_Size 
+          << "," << D3DFormatStr(dispmode.Format) << ")\nReqdVidMem: "
+          << (int)RendTgtMinMemReqmt << " AvailVidMem: " 
+          << scrn.MaxAvailVidMem << endl;
+
+      if (RendTgtMinMemReqmt > scrn.MaxAvailVidMem) {
+        if (bVerboseMode || wdxdisplay9_cat.is_debug())
+          wdxdisplay9_cat.info()
+            << "not enough VidMem for render tgt, skipping display fmt "
+            << D3DFormatStr(dispmode.Format) << " (" 
+            << (int)RendTgtMinMemReqmt << " > " 
+            << scrn.MaxAvailVidMem << ")\n";
+        continue;
+      }
+    }
+
+    if (bWantZBuffer) {
+      D3DFORMAT zformat;
+      if (!find_best_depth_format(scrn,dispmode, &zformat,
+                                  bWantStencil, bForce16bppZBuffer)) {
+        *pCouldntFindAnyValidZBuf=true;
+        continue;
+      }
+
+      float MinMemReqmt = 0.0f;
+
+      if (bDoMemBasedChecks) {
+        // test memory again, this time including zbuf size
+        float zbytes_per_pixel = (IS_16BPP_ZBUFFER(zformat) ? 2 : 4);
+        float MinMemReqmt = RendTgtMinMemReqmt + ((float)RequestedX_Size)*((float)RequestedY_Size)*zbytes_per_pixel;
+
+        if (bVerboseMode || wdxdisplay9_cat.is_spam())
+          wdxdisplay9_cat.info()
+            << "Testing Mode w/Z (" << RequestedX_Size << "x"
+            << RequestedY_Size << "," << D3DFormatStr(dispmode.Format)
+            << ")\nReqdVidMem: "<< (int)MinMemReqmt << " AvailVidMem: " 
+            << scrn.MaxAvailVidMem << endl;
+
+        if (MinMemReqmt > scrn.MaxAvailVidMem) {
+          if (bVerboseMode || wdxdisplay9_cat.is_debug())
+            wdxdisplay9_cat.info()
+              << "not enough VidMem for RendTgt+zbuf, skipping display fmt "
+              << D3DFormatStr(dispmode.Format) << " (" << (int)MinMemReqmt
+              << " > " << scrn.MaxAvailVidMem << ")\n";
+          continue;
+        }
+      }
+
+      if ((!bDoMemBasedChecks) || (MinMemReqmt<scrn.MaxAvailVidMem)) {
+        if (!IS_16BPP_ZBUFFER(zformat)) {
+          // see if things fit with a 16bpp zbuffer
+
+          if (!find_best_depth_format(scrn, dispmode, &zformat, 
+                                      bWantStencil, true, bVerboseMode)) {
+            if (bVerboseMode)
+              wdxdisplay9_cat.info()
+                << "FindBestDepthFmt rejected Mode[" << i << "] (" 
+                << RequestedX_Size << "x" << RequestedY_Size 
+                << "," << D3DFormatStr(dispmode.Format) << endl;
+            *pCouldntFindAnyValidZBuf=true;
+            continue;
+          }
+          
+          // right now I'm not going to use these flags, just let the
+          // create fail out-of-mem and retry at 16bpp
+          *pSupportedScreenDepthsMask |= 
+            (IS_16BPP_DISPLAY_FORMAT(dispmode.Format) ? DISPLAY_16BPP_REQUIRES_16BPP_ZBUFFER_FLAG : DISPLAY_32BPP_REQUIRES_16BPP_ZBUFFER_FLAG);
+        }
+      }
+    }
+
+    if (bVerboseMode || wdxdisplay9_cat.is_spam())
+      wdxdisplay9_cat.info()
+        << "Validated Mode (" << RequestedX_Size << "x" 
+        << RequestedY_Size << "," << D3DFormatStr(dispmode.Format) << endl;
+
+    switch (dispmode.Format) {
+    case D3DFMT_X1R5G5B5:
+      *pSupportedScreenDepthsMask |= X1R5G5B5_FLAG;
+      break;
+    case D3DFMT_X8R8G8B8:
+      *pSupportedScreenDepthsMask |= X8R8G8B8_FLAG;
+      break;
+    case D3DFMT_R8G8B8:
+      *pSupportedScreenDepthsMask |= R8G8B8_FLAG;
+      break;
+    case D3DFMT_R5G6B5:
+      *pSupportedScreenDepthsMask |= R5G6B5_FLAG;
+      break;
+    default:
+      // Render target formats should be only D3DFMT_X1R5G5B5,
+      // D3DFMT_R5G6B5, D3DFMT_X8R8G8B8 (or R8G8B8?)
+      wdxdisplay9_cat.error()
+        << "unrecognized supported fmt "<< D3DFormatStr(dispmode.Format)
+        << " returned by EnumAdapterDisplayModes!\n";
+    }
+  }
+
+  // note: this chooses 32bpp, which may not be preferred over 16 for
+  // memory & speed reasons on some older cards in particular
+  if (*pSupportedScreenDepthsMask & X8R8G8B8_FLAG) {
+    *pSuggestedPixFmt = D3DFMT_X8R8G8B8;
+  } else if (*pSupportedScreenDepthsMask & R8G8B8_FLAG) {
+    *pSuggestedPixFmt = D3DFMT_R8G8B8;
+  } else if (*pSupportedScreenDepthsMask & R5G6B5_FLAG) {
+    *pSuggestedPixFmt = D3DFMT_R5G6B5;
+  } else if (*pSupportedScreenDepthsMask & X1R5G5B5_FLAG) {
+    *pSuggestedPixFmt = D3DFMT_X1R5G5B5;
+  }
+
+  if (bVerboseMode || wdxdisplay9_cat.is_spam()) {
+    wdxdisplay9_cat.info() 
+      << "search_for_valid_device returns fmt: "
+      << D3DFormatStr(*pSuggestedPixFmt) << endl;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipew9::make_device
+//       Access: Public, Virtual
+//  Description: Creates a new device.  ??????
+////////////////////////////////////////////////////////////////////
+
+PT(GraphicsDevice) wdxGraphicsPipe9::
+make_device(void *scrn) {
+
+  // FrameBufferProperties really belongs as part of the window/renderbuffer specification
+  // put here because of GLX multithreading requirement
+  PT(DXGraphicsDevice9) device = new DXGraphicsDevice9(this);
+  device->_pScrn = (DXScreenData*) scrn;
+  device->_pD3DDevice = device->_pScrn->pD3DDevice;
+
+  _device = device;
+  wdxdisplay9_cat.error() << "walla: device" << device << "\n";
+
+  return device.p();
+
+/*
+  nassertv(_gsg == (GraphicsStateGuardian *)NULL);
+  _dxgsg = new DXGraphicsStateGuardian9(this);
+  _gsg = _dxgsg;
+
+  // Tell the associated dxGSG about the window handle.
+  _dxgsg->scrn.hWnd = _hWnd;
+
+  if (pD3D9 == NULL) {
+    wdxdisplay9_cat.error()
+      << "Direct3DCreate9 failed!\n";
+    release_gsg();
+    return;
+  }
+
+  if (!choose_adapter(pD3D9)) {
+    wdxdisplay9_cat.error()
+      << "Unable to find suitable rendering device.\n";
+    release_gsg();
+    return;
+  }
+
+  create_screen_buffers_and_device(_dxgsg->scrn, dx_force_16bpp_zbuffer);
+  */
+}
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsPipew9::make_gsg
+//       Access: Public, Virtual
+//  Description: Creates a new empty GSG.  Can do no real initialization
+//               until window is opened, and we know the full device requirements
+////////////////////////////////////////////////////////////////////
+
+PT(GraphicsStateGuardian) wdxGraphicsPipe9::
+make_gsg(const FrameBufferProperties &properties) {
+
+
+  // FrameBufferProperties really belongs as part of the window/renderbuffer specification
+  // put here because of GLX multithreading requirement
+  PT(DXGraphicsStateGuardian9) gsg = new DXGraphicsStateGuardian9(properties);
+  return gsg.p();
+
+/*
+  nassertv(_gsg == (GraphicsStateGuardian *)NULL);
+  _dxgsg = new DXGraphicsStateGuardian9(this);
+  _gsg = _dxgsg;
+
+  // Tell the associated dxGSG about the window handle.
+  _dxgsg->scrn.hWnd = _hWnd;
+
+  if (pD3D9 == NULL) {
+    wdxdisplay9_cat.error()
+      << "Direct3DCreate9 failed!\n";
+    release_gsg();
+    return;
+  }
+
+  if (!choose_adapter(pD3D9)) {
+    wdxdisplay9_cat.error()
+      << "Unable to find suitable rendering device.\n";
+    release_gsg();
+    return;
+  }
+
+  create_screen_buffers_and_device(_dxgsg->scrn, dx_force_16bpp_zbuffer);
+  */
+}
+
+map<D3DFORMAT_FLAG,D3DFORMAT> g_D3DFORMATmap;
+
+void Init_D3DFORMAT_map(void) {
+  if(g_D3DFORMATmap.size()!=0)
+    return;
+
+    #define INSERT_ELEM(XX)  g_D3DFORMATmap[XX##_FLAG] = D3DFMT_##XX;
+
+    INSERT_ELEM(R8G8B8);
+    INSERT_ELEM(A8R8G8B8);
+    INSERT_ELEM(X8R8G8B8);
+    INSERT_ELEM(R5G6B5);
+    INSERT_ELEM(X1R5G5B5);
+    INSERT_ELEM(A1R5G5B5);
+    INSERT_ELEM(A4R4G4B4);
+    INSERT_ELEM(R3G3B2);
+    INSERT_ELEM(A8);
+    INSERT_ELEM(A8R3G3B2);
+    INSERT_ELEM(X4R4G4B4);
+    INSERT_ELEM(A2B10G10R10);
+    INSERT_ELEM(G16R16);
+    INSERT_ELEM(A8P8);
+    INSERT_ELEM(P8);
+    INSERT_ELEM(L8);
+    INSERT_ELEM(A8L8);
+    INSERT_ELEM(A4L4);
+    INSERT_ELEM(V8U8);
+    INSERT_ELEM(L6V5U5);
+    INSERT_ELEM(X8L8V8U8);
+    INSERT_ELEM(Q8W8V8U8);
+    INSERT_ELEM(V16U16);
+    //INSERT_ELEM(W11V11U10);
+    INSERT_ELEM(A2W10V10U10);
+    INSERT_ELEM(UYVY);
+    INSERT_ELEM(YUY2);
+    INSERT_ELEM(DXT1);
+    INSERT_ELEM(DXT2);
+    INSERT_ELEM(DXT3);
+    INSERT_ELEM(DXT4);
+    INSERT_ELEM(DXT5);
+}
+
+
+const char *D3DFormatStr(D3DFORMAT fmt) {
+
+#define CASESTR(XX) case XX: return #XX;
+
+  switch(fmt) {
+    CASESTR(D3DFMT_UNKNOWN);
+    CASESTR(D3DFMT_R8G8B8);
+    CASESTR(D3DFMT_A8R8G8B8);
+    CASESTR(D3DFMT_X8R8G8B8);
+    CASESTR(D3DFMT_R5G6B5);
+    CASESTR(D3DFMT_X1R5G5B5);
+    CASESTR(D3DFMT_A1R5G5B5);
+    CASESTR(D3DFMT_A4R4G4B4);
+    CASESTR(D3DFMT_R3G3B2);
+    CASESTR(D3DFMT_A8);
+    CASESTR(D3DFMT_A8R3G3B2);
+    CASESTR(D3DFMT_X4R4G4B4);
+    CASESTR(D3DFMT_A2B10G10R10);
+    CASESTR(D3DFMT_G16R16);
+    CASESTR(D3DFMT_A8P8);
+    CASESTR(D3DFMT_P8);
+    CASESTR(D3DFMT_L8);
+    CASESTR(D3DFMT_A8L8);
+    CASESTR(D3DFMT_A4L4);
+    CASESTR(D3DFMT_V8U8);
+    CASESTR(D3DFMT_L6V5U5);
+    CASESTR(D3DFMT_X8L8V8U8);
+    CASESTR(D3DFMT_Q8W8V8U8);
+    CASESTR(D3DFMT_V16U16);
+    //CASESTR(D3DFMT_W11V11U10);
+    CASESTR(D3DFMT_A2W10V10U10);
+    CASESTR(D3DFMT_UYVY);
+    CASESTR(D3DFMT_YUY2);
+    CASESTR(D3DFMT_DXT1);
+    CASESTR(D3DFMT_DXT2);
+    CASESTR(D3DFMT_DXT3);
+    CASESTR(D3DFMT_DXT4);
+    CASESTR(D3DFMT_DXT5);
+    CASESTR(D3DFMT_D16_LOCKABLE);
+    CASESTR(D3DFMT_D32);
+    CASESTR(D3DFMT_D15S1);
+    CASESTR(D3DFMT_D24S8);
+    CASESTR(D3DFMT_D16);
+    CASESTR(D3DFMT_D24X8);
+    CASESTR(D3DFMT_D24X4S4);
+    CASESTR(D3DFMT_VERTEXDATA);
+    CASESTR(D3DFMT_INDEX16);
+    CASESTR(D3DFMT_INDEX32);
+  }
+
+  return "Invalid D3DFORMAT";
+}
+

+ 130 - 0
panda/src/dxgsg9/wdxGraphicsPipe9.h

@@ -0,0 +1,130 @@
+// Filename: wdxGraphicsPipe8.h
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef WDXGRAPHICSPIPE9_H
+#define WDXGRAPHICSPIPE9_H
+
+#include "pandabase.h"
+#include "winGraphicsPipe.h"
+#include "pvector.h"
+#include "dxgsg9base.h"
+#include <ddraw.h>
+
+typedef struct {
+   UINT    cardID;
+   char    szDriver[MAX_DEVICE_IDENTIFIER_STRING];
+   char    szDescription[MAX_DEVICE_IDENTIFIER_STRING];
+   GUID    guidDeviceIdentifier;
+   DWORD   VendorID, DeviceID;
+   HMONITOR hMon;
+} DXDeviceInfo;
+typedef pvector<DXDeviceInfo> DXDeviceInfoVec;
+
+////////////////////////////////////////////////////////////////////
+//       Class : wdxGraphicsPipe9
+// Description : This graphics pipe represents the interface for
+//               creating DirectX graphics windows.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX wdxGraphicsPipe9 : public WinGraphicsPipe {
+public:
+  wdxGraphicsPipe9();
+  virtual ~wdxGraphicsPipe9();
+
+  virtual string get_interface_name() const;
+  static PT(GraphicsPipe) pipe_constructor();
+
+  virtual PT(GraphicsStateGuardian) make_gsg(const FrameBufferProperties &properties);
+  virtual PT(GraphicsDevice) make_device(void *scrn);
+
+  bool find_best_depth_format(DXScreenData &Display, D3DDISPLAYMODE &TestDisplayMode,
+                       D3DFORMAT *pBestFmt, bool bWantStencil,
+                       bool bForce16bpp, bool bVerboseMode = false) const;
+
+  void search_for_valid_displaymode(DXScreenData &scrn,
+                             UINT RequestedX_Size, UINT RequestedY_Size,
+                             bool bWantZBuffer, bool bWantStencil,
+                             UINT *pSupportedScreenDepthsMask,
+                             bool *pCouldntFindAnyValidZBuf,
+                             D3DFORMAT *pSuggestedPixFmt,
+                             bool bForce16bppZBuffer,
+                             bool bVerboseMode = false);
+
+   bool special_check_fullscreen_resolution(DXScreenData &scrn, UINT x_size,UINT y_size);
+
+protected:
+  virtual PT(GraphicsWindow) make_window(GraphicsStateGuardian *gsg);
+
+private:
+  bool init();
+  bool find_all_card_memavails();
+
+  static BOOL WINAPI
+  dx7_driver_enum_callback(GUID *pGUID, TCHAR *strDesc, TCHAR *strName,
+                           VOID *argptr, HMONITOR hm);
+
+private:
+  HINSTANCE _hDDrawDLL;
+  HINSTANCE _hD3D9_DLL;
+  LPDIRECT3D9 _pD3D9;
+
+
+  typedef LPDIRECT3D9 (WINAPI *Direct3DCreate9_ProcPtr)(UINT SDKVersion);
+  typedef HRESULT (WINAPI * LPDIRECTDRAWCREATEEX)(GUID FAR * lpGuid, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter);
+
+  LPDIRECTDRAWCREATEEX _DirectDrawCreateEx;
+  LPDIRECTDRAWENUMERATEEX _DirectDrawEnumerateExA;
+  Direct3DCreate9_ProcPtr _Direct3DCreate9;
+
+  // CardID is used in DX7 lowmem card-classification pass so DX9 can
+  // establish correspondence b/w DX7 mem info & DX8 device
+  struct CardID {
+    HMONITOR hMon;
+    DWORD MaxAvailVidMem;
+    bool  bIsLowVidMemCard;
+    GUID  DX7_DeviceGUID;
+    DWORD VendorID, DeviceID;
+    //   char  szDriver[MAX_DEVICE_IDENTIFIER_STRING];
+  };
+  
+  typedef pvector<CardID> CardIDs;
+  CardIDs _card_ids;
+  bool _bIsDX9;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    WinGraphicsPipe::init_type();
+    register_type(_type_handle, "wdxGraphicsPipe9",
+                  WinGraphicsPipe::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class wdxGraphicsWindow9;
+};
+
+#include "wdxGraphicsPipe9.I"
+
+#endif

+ 17 - 0
panda/src/dxgsg9/wdxGraphicsWindow9.I

@@ -0,0 +1,17 @@
+// Filename: wdxGraphicsWindow8.I
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////

+ 1809 - 0
panda/src/dxgsg9/wdxGraphicsWindow9.cxx

@@ -0,0 +1,1809 @@
+// Filename: wdxGraphicsWindow8.cxx
+// Created by:  masad (05Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include <errno.h>
+#include <time.h>
+#include <math.h>
+#include <tchar.h>
+#include "wdxGraphicsPipe9.h"
+#include "wdxGraphicsWindow9.h"
+#include "config_dxgsg9.h"
+#include "config_display.h"
+
+#include "keyboardButton.h"
+#include "mouseButton.h"
+#include "throw_event.h"
+
+#ifdef DO_PSTATS
+#include "pStatTimer.h"
+#endif
+
+#include <ddraw.h>
+#include <map>
+
+TypeHandle wdxGraphicsWindow9::_type_handle;
+
+#define WDX_WINDOWCLASSNAME "wdxDisplay"
+#define WDX_WINDOWCLASSNAME_NOCURSOR WDX_WINDOWCLASSNAME "_NoCursor"
+#define DEFAULT_CURSOR IDC_ARROW
+
+// define this to enable debug testing of dinput joystick
+//#define DINPUT_DEBUG_POLL
+
+typedef map<HWND,wdxGraphicsWindow9 *> HWND_PANDAWIN_MAP;
+
+HWND_PANDAWIN_MAP hwnd_pandawin_map;
+wdxGraphicsWindow9* global_wdxwinptr = NULL;  // need this for temporary windproc
+
+#define MAX_DISPLAYS 20
+
+#define PAUSED_TIMER_ID        7   // completely arbitrary choice
+#define JOYSTICK_POLL_TIMER_ID 8
+#define DX_IS_READY ((_dxgsg!=NULL)&&(_dxgsg->GetDXReady()))
+
+LONG WINAPI static_window_proc(HWND hwnd, UINT msg, WPARAM wparam,LPARAM lparam);
+
+/*
+// because we dont have access to ModifierButtons, as a hack just synchronize state of these
+// keys on get/lose keybd focus
+#define NUM_MODIFIER_KEYS 16
+unsigned int hardcoded_modifier_buttons[NUM_MODIFIER_KEYS]={VK_SHIFT,VK_MENU,VK_CONTROL,VK_SPACE,VK_TAB,
+                                                            VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT,VK_PRIOR,VK_NEXT,VK_HOME,VK_END,
+                                                            VK_INSERT,VK_DELETE,VK_ESCAPE};
+*/
+//#define UNKNOWN_VIDMEM_SIZE 0xFFFFFFFF
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+wdxGraphicsWindow9::
+wdxGraphicsWindow9(GraphicsPipe *pipe, GraphicsStateGuardian *gsg) :
+  WinGraphicsWindow(pipe, gsg) 
+{
+  // dont actually create the window in the constructor.  reason: multi-threading requires
+  // panda C++ window object to exist in separate thread from actual API window
+
+  _dxgsg = DCAST(DXGraphicsStateGuardian9, gsg);
+  _depth_buffer_bpp = 0;
+  _awaiting_restore = false;
+  ZeroMemory(&_wcontext,sizeof(_wcontext));
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+wdxGraphicsWindow9::
+~wdxGraphicsWindow9() {
+}
+
+void wdxGraphicsWindow9::
+make_current(void) {
+  DXGraphicsStateGuardian9 *dxgsg;
+  DCAST_INTO_V(dxgsg, _gsg);
+  //wglMakeCurrent(_hdc, wdxgsg->_context);
+  dxgsg->set_context(&_wcontext);
+
+  // Now that we have made the context current to a window, we can
+  // reset the GSG state if this is the first time it has been used.
+  // (We can't just call reset() when we construct the GSG, because
+  // reset() requires having a current context.)
+  dxgsg->reset_if_new();
+
+  //wdxdisplay9_cat.debug() << "this is " << this << "\n";
+}
+
+/* BUGBUG:  need to reinstate these methods ASAP.  they were incorrectly moved from the GraphicsWindow to the GSG
+            apps need to know the framebuffer format so they can create texture/rendertgt with same fmt
+int wdxGraphicsWindow9::
+get_depth_bitwidth(void) {
+    assert(_dxgsg!=NULL);
+    if(_dxgsg->scrn.PresParams.EnableAutoDepthStencil)
+       return _dxgsg->scrn.depth_buffer_bitdepth;
+     else return 0;
+
+// GetSurfaceDesc is not reliable, on GF2, GetSurfDesc returns 32bpp when you created a 24bpp zbuf
+// instead store the depth used at creation time
+
+//    DX_DECLARE_CLEAN(DDSURFACEDESC2, ddsd);
+//    _dxgsg->_zbuf->GetSurfaceDesc(&ddsd);
+//  return ddsd.ddpfPixelFormat.dwRGBBitCount;
+}
+
+void wdxGraphicsWindow9::
+get_framebuffer_format(PixelBuffer::Type &fb_type, PixelBuffer::Format &fb_format) {
+    assert(_dxgsg!=NULL);
+
+    fb_type = PixelBuffer::T_unsigned_byte;
+    // this is sortof incorrect, since for F_rgb5 it's really 5 bits per channel
+    //would have to change a lot of texture stuff to make this correct though
+
+    if(IS_16BPP_DISPLAY_FORMAT(_dxgsg->scrn.PresParams.BackBufferFormat))
+        fb_format = PixelBuffer::F_rgb5;
+     else fb_format = PixelBuffer::F_rgb;
+}
+*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::verify_window_sizes
+//       Access: Public, Virtual
+//  Description: Determines which of the indicated window sizes are
+//               supported by available hardware (e.g. in fullscreen
+//               mode).
+//
+//               On entry, dimen is an array containing contiguous x,y
+//               pairs specifying possible display sizes; it is
+//               numsizes*2 words long.  The function will zero out
+//               any invalid x,y size pairs.  The return value is the
+//               number of valid sizes that were found.
+////////////////////////////////////////////////////////////////////
+int wdxGraphicsWindow9::
+verify_window_sizes(int numsizes, int *dimen) {
+  // unfortunately this only works AFTER you make the window
+  // initially, so its really mostly useful for resizes only
+  assert(IS_VALID_PTR(_dxgsg));
+
+  int num_valid_modes = 0;
+
+  wdxGraphicsPipe9 *dxpipe;
+  DCAST_INTO_R(dxpipe, _pipe, 0);
+
+  // not requesting same refresh rate since changing res might not
+  // support same refresh rate at new size
+
+  int *pCurDim = dimen;
+
+  for (int i=0; i < numsizes; i++, pCurDim += 2) {
+    int x_size = pCurDim[0];
+    int y_size = pCurDim[1];
+
+    bool bIsGoodMode = false;
+    bool CouldntFindAnyValidZBuf;
+    D3DFORMAT newPixFmt = D3DFMT_UNKNOWN;
+
+    if (dxpipe->special_check_fullscreen_resolution(_wcontext, x_size, y_size)) {
+      // bypass the test below for certain cards we know have valid modes
+      bIsGoodMode=true;
+
+    } else {
+      if (_wcontext.bIsLowVidMemCard) {
+        bIsGoodMode = ((x_size == 640) && (y_size == 480));
+      } else  {
+        dxpipe->search_for_valid_displaymode(_wcontext, x_size, y_size, _wcontext.PresParams.EnableAutoDepthStencil != false,
+                                     IS_STENCIL_FORMAT(_wcontext.PresParams.AutoDepthStencilFormat),
+                                     &_wcontext.SupportedScreenDepthsMask,
+                                     &CouldntFindAnyValidZBuf, &newPixFmt, dx_force_16bpp_zbuffer);
+        bIsGoodMode = (newPixFmt != D3DFMT_UNKNOWN);
+      }
+    }
+
+    if (bIsGoodMode) {
+      num_valid_modes++;
+    } else {
+      // tell caller the mode is invalid
+      pCurDim[0] = 0;
+      pCurDim[1] = 0;
+    }
+
+    if (wdxdisplay9_cat.is_spam()) {
+      wdxdisplay9_cat.spam()
+        << "Fullscrn Mode (" << x_size << "," << y_size << ")\t" 
+        << (bIsGoodMode ? "V" : "Inv") <<"alid\n";
+    }
+  }
+
+  return num_valid_modes;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::begin_frame
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               before beginning rendering for a given frame.  It
+//               should do whatever setup is required, and return true
+//               if the frame should be rendered, or false if it
+//               should be skipped.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow9::
+begin_frame() {
+  if (_awaiting_restore) {
+    // The fullscreen window was recently restored; we can't continue
+    // until the GSG says we can.
+    if (!_dxgsg->CheckCooperativeLevel()) {
+      // Keep waiting.
+      return false;
+    }
+    _awaiting_restore = false;
+
+    init_resized_window();
+  }
+
+  bool return_val = WinGraphicsWindow::begin_frame();
+  _dxgsg->set_render_target();
+  return return_val;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::end_flip
+//       Access: Public, Virtual
+//  Description: This function will be called within the draw thread
+//               after begin_flip() has been called on all windows, to
+//               finish the exchange of the front and back buffers.
+//
+//               This should cause the window to wait for the flip, if
+//               necessary.
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow9::
+end_flip() {
+  if (_dxgsg != (DXGraphicsStateGuardian9 *)NULL && is_active()) {
+    make_current();
+    //wdxdisplay9_cat.debug() << "current swapchain from end_flip is " << _wcontext.pSwapChain << "\n";
+    _dxgsg->show_frame();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::fullscreen_restored
+//       Access: Protected, Virtual
+//  Description: This is a hook for derived classes to do something
+//               special, if necessary, when a fullscreen window has
+//               been restored after being minimized.  The given
+//               WindowProperties struct will be applied to this
+//               window's properties after this function returns.
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow9::
+fullscreen_restored(WindowProperties &properties) {
+  // In DX9, unlike DX7, for some reason we can't immediately start
+  // rendering as soon as the window is restored, even though
+  // BeginScene() says we can.  Instead, we have to wait until
+  // TestCooperativeLevel() lets us in.  We need to set a flag so we
+  // can handle this special case in begin_frame().
+  if (_dxgsg != NULL) {
+    _awaiting_restore = true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::handle_reshape
+//       Access: Protected, Virtual
+//  Description: Called in the window thread when the window size or
+//               location is changed, this updates the properties
+//               structure accordingly.
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow9::
+handle_reshape() {
+  GdiFlush();
+  WinGraphicsWindow::handle_reshape();
+
+  if (_dxgsg != NULL) {
+    // create the new resized rendertargets
+    WindowProperties props = get_properties();
+    int x_size = props.get_x_size();
+    int y_size = props.get_y_size();
+    bool resize_succeeded = reset_device_resize_window(x_size, y_size);
+    if (!resize_succeeded) {
+      if (wdxdisplay9_cat.is_debug()) {
+        wdxdisplay9_cat.debug()
+          << "windowed_resize to size: (" << x_size << "," << y_size
+          << ") failed due to out-of-memory\n";
+      } else {
+        if (wdxdisplay9_cat.is_debug()) {
+          int x_origin = props.get_x_origin();
+          int y_origin = props.get_y_origin();
+          wdxdisplay9_cat.debug()
+            << "windowed_resize to origin: (" << x_origin << ","
+            << y_origin << "), size: (" << x_size
+            << "," << y_size << ")\n";
+        }
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::do_fullscreen_resize
+//       Access: Protected, Virtual
+//  Description: Called in the window thread to resize a fullscreen
+//               window.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow9::
+do_fullscreen_resize(int x_size, int y_size) {
+  bool bCouldntFindValidZBuf;
+  D3DFORMAT pixFmt;
+  bool bNeedZBuffer = (_wcontext.PresParams.EnableAutoDepthStencil!=false);
+  bool bNeedStencilBuffer = IS_STENCIL_FORMAT(_wcontext.PresParams.AutoDepthStencilFormat);
+
+  wdxGraphicsPipe9 *dxpipe;
+  DCAST_INTO_R(dxpipe, _pipe, false);
+
+  bool bIsGoodMode=false;
+  bool bResizeSucceeded=false;
+
+  if (!dxpipe->special_check_fullscreen_resolution(_wcontext, x_size,y_size)) {
+    // bypass the lowvidmem test below for certain "lowmem" cards we know have valid modes
+
+    // wdxdisplay9_cat.info() << "1111111 lowvidmemcard="<< _wcontext.bIsLowVidMemCard << endl;
+    if (_wcontext.bIsLowVidMemCard && (!((x_size==640) && (y_size==480)))) {
+      wdxdisplay9_cat.error() << "resize() failed: will not try to resize low vidmem device #" << _wcontext.CardIDNum << " to non-640x480!\n";
+      goto Error_Return;
+    }
+  }
+
+  // must ALWAYS use search_for_valid_displaymode even if we know
+  // a-priori that res is valid so we can get a valid pixfmt
+  dxpipe->search_for_valid_displaymode(_wcontext, x_size, y_size, 
+                               bNeedZBuffer, bNeedStencilBuffer,
+                               &_wcontext.SupportedScreenDepthsMask,
+                               &bCouldntFindValidZBuf,
+                               &pixFmt, dx_force_16bpp_zbuffer);
+  bIsGoodMode=(pixFmt!=D3DFMT_UNKNOWN);
+
+  if (!bIsGoodMode) {
+    wdxdisplay9_cat.error() << "resize() failed: "
+                           << (bCouldntFindValidZBuf ? "Couldnt find valid zbuffer format to go with FullScreen mode" : "No supported FullScreen modes")
+                           << " at " << x_size << "x" << y_size << " for device #" << _wcontext.CardIDNum <<endl;
+    goto Error_Return;
+  }
+
+  // reset_device_resize_window handles both windowed & fullscrn,
+  // so need to set new displaymode manually here
+  _wcontext.DisplayMode.Width=x_size;
+  _wcontext.DisplayMode.Height=y_size;
+  _wcontext.DisplayMode.Format = pixFmt;
+  _wcontext.DisplayMode.RefreshRate = D3DPRESENT_RATE_DEFAULT;
+
+  _wcontext.PresParams.BackBufferFormat = pixFmt;   // make reset_device_resize use presparams or displaymode??
+
+  bResizeSucceeded = reset_device_resize_window(x_size, y_size);
+
+  if (!bResizeSucceeded) {
+    wdxdisplay9_cat.error() << "resize() failed with OUT-OF-MEMORY error!\n";
+
+    if ((!IS_16BPP_DISPLAY_FORMAT(_wcontext.PresParams.BackBufferFormat)) &&
+       (_wcontext.SupportedScreenDepthsMask & (R5G6B5_FLAG|X1R5G5B5_FLAG))) {
+      // fallback strategy, if we trying >16bpp, fallback to 16bpp buffers
+      _wcontext.DisplayMode.Format = ((_wcontext.SupportedScreenDepthsMask & R5G6B5_FLAG) ? D3DFMT_R5G6B5 : D3DFMT_X1R5G5B5);
+      dx_force_16bpp_zbuffer=true;
+      if (wdxdisplay9_cat.info())
+        wdxdisplay9_cat.info() << "CreateDevice failed with out-of-vidmem, retrying w/16bpp buffers on device #"<< _wcontext.CardIDNum << endl;
+
+      bResizeSucceeded= reset_device_resize_window(x_size, y_size);  // create the new resized rendertargets
+    }
+  }
+
+ Error_Return:
+
+  if (wdxdisplay9_cat.is_debug())
+    wdxdisplay9_cat.debug() << "fullscrn resize("<<x_size<<","<<y_size<<") " << (bResizeSucceeded ? "succeeds\n" : "fails\n");
+
+  return bResizeSucceeded;
+}
+
+#if 1
+//////////////////////////////////////////////////////////////////
+//     Function: WinGraphicsWindow::window_proc
+//       Access: Private
+//  Description: This is the nonstatic window_proc function.  It is
+//               called to handle window events for this particular
+//               window.
+////////////////////////////////////////////////////////////////////
+LONG wdxGraphicsWindow9::
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+  return WinGraphicsWindow::window_proc(hwnd,msg,wparam,lparam);
+}
+
+#else
+
+////////////////////////////////////////////////////////////////////
+//     Function: window_proc
+//       Access:
+//  Description:
+////////////////////////////////////////////////////////////////////
+LONG wdxGraphicsWindow9::
+window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) {
+    int button = -1;
+    int x, y, width, height;
+
+    switch(msg) {
+        case WM_SETCURSOR: {
+            // Turn off any GDI window cursor
+            //  dx9 cursor not working yet
+
+            if(dx_use_dx_cursor && is_fullscreen()) {
+                //            SetCursor( NULL );
+            //                _dxgsg->scrn.pD3DDevice->ShowCursor(true);
+
+                set_cursor_visibility(true);
+                return TRUE; // prevent Windows from setting cursor to window class cursor (see docs on WM_SETCURSOR)
+            }
+            break;
+        }
+
+        case WM_PAINT: {
+           // primarily seen when app window is 'uncovered'
+           if((_WindowAdjustingType != NotAdjusting) || (!DX_IS_READY)) {
+             // let DefWndProc do WM_ERASEBKGND & just draw black,
+             // rather than forcing Present to stretchblt the old window contents
+             // into the new size
+             break;
+           }
+
+           PAINTSTRUCT ps;
+           BeginPaint(hwnd, &ps);
+           if(DX_IS_READY) {
+              _dxgsg->show_frame(true);  // 'true' since just want to show the last rendered backbuf, if any
+           }
+           EndPaint(hwnd, &ps);
+           return 0;
+        }
+
+        case WM_IME_STARTCOMPOSITION:
+          // In case we're running fullscreen mode, we have to turn on
+          // explicit DX support for overlay windows now, so we'll be able
+          // to see the IME window.
+          _dxgsg->support_overlay_window(true);
+          break;
+
+        case WM_IME_ENDCOMPOSITION:
+          // Turn off the support for overlay windows, since we're done
+          // with the IME window for now and it just slows things down.
+          _dxgsg->support_overlay_window(false);
+          break;
+
+        case WM_ENTERSIZEMOVE:
+             if(_dxgsg!=NULL) 
+                _dxgsg->SetDXReady(false);   // dont see pic during resize
+             _WindowAdjustingType = MovingOrResizing;
+          break;
+
+        case WM_EXITSIZEMOVE: {
+            #ifdef _DEBUG
+              wdxdisplay_cat.spam()  << "WM_EXITSIZEMOVE received"  << endl;
+            #endif
+
+            if(_WindowAdjustingType==Resizing) {
+                bool bSucceeded=handle_windowed_resize(hwnd,true);
+
+                if(!bSucceeded) {
+                    #if 0
+                                bugbug need to fix this stuff
+                                SetWindowPos(hwnd,NULL,0,0,lastxsize,lastysize,SWP_NOMOVE |
+                    #endif
+                }
+            }
+
+            _WindowAdjustingType = NotAdjusting;
+            _dxgsg->SetDXReady(true);
+            return 0;
+        }
+
+        case WM_SIZE: {
+
+            #ifdef _DEBUG
+                {
+                    width = LOWORD(lparam);  height = HIWORD(lparam);
+                    wdxdisplay_cat.spam() << "WM_SIZE received with width:" << width << "  height: " << height << " flags: " <<
+                    ((wparam == SIZE_MAXHIDE)? "SIZE_MAXHIDE " : "") << ((wparam == SIZE_MAXSHOW)? "SIZE_MAXSHOW " : "") <<
+                    ((wparam == SIZE_MINIMIZED)? "SIZE_MINIMIZED " : "") << ((wparam == SIZE_RESTORED)? "SIZE_RESTORED " : "") <<
+                    ((wparam == SIZE_MAXIMIZED)? "SIZE_MAXIMIZED " : "") << endl;
+                }
+            #endif
+                // old comment -- added SIZE_RESTORED to handle 3dfx case
+                if(_props._fullscreen || ((_dxgsg==NULL) || (_dxgsg->scrn.hWnd==NULL)) || ((wparam != SIZE_RESTORED) && (wparam != SIZE_MAXIMIZED)))
+                    break;
+
+                width = LOWORD(lparam);  height = HIWORD(lparam);
+
+                if((_props._xsize != width) || (_props._ysize != height)) {
+                    _WindowAdjustingType = Resizing;
+
+                 // for maximized,unmaximize, need to call resize code artificially
+                 // since no WM_EXITSIZEMOVE is generated.
+                 if(wparam==SIZE_MAXIMIZED) {
+                       _bSizeIsMaximized=TRUE;
+                       window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0);
+                 } else if((wparam==SIZE_RESTORED) && _bSizeIsMaximized) {
+                       _bSizeIsMaximized=FALSE;  // only want to reinit dx if restoring from maximized state
+                       window_proc(hwnd, WM_EXITSIZEMOVE, 0x0,0x0);
+                 }
+                }
+
+                break;
+            }
+    
+        case WM_ERASEBKGND: {
+            // WM_ERASEBKGND will be ignored during resizing, because
+            // we dont want WM_PAINT's generated as user is manually resizing window.
+            
+            // for the intermediate resizing images that WM_PAINT would show to be useful, 
+            // the panda window parameters need to be reset on every
+            // WM_SIZE event and that isnt happening yet
+
+            if(_WindowAdjustingType)
+                break;
+            return 0;  // dont let GDI waste time redrawing the deflt background
+        }
+
+        case WM_TIMER:
+            // 2 cases of app deactivation:
+            //
+            // 1) user has switched out of fullscreen mode
+            //    this is first signalled when ACTIVATEAPP returns false
+            //    for this case, we dont wake up until WM_SIZE returns restore or maximize
+            //    and WM_TIMER just periodically reawakens app for idle processing
+
+            //    unfortunately this doesnt seem to work because RestoreAllSurfaces doesn't
+            //    seem to think we're back in the original displaymode even after I've received
+            //    the WM_DISPLAYCHANGE msg, and returns WRONGMODE error.  So the only way I can
+            //    think of to make this work is to have the timer periodically check for restored
+            //    coop level, as it does in case 2)
+
+            //
+            // 2) windowed app has lost access to dx because another app has taken dx exclusive mode
+            //    here we rely on WM_TIMER to periodically check if it is ok to reawaken app.
+            //    windowed apps currently run regardless of if its window is in the foreground
+            //    so we cannot rely on window messages to reawaken app
+
+            if((wparam==_PandaPausedTimer) && ((!_window_active)||_active_minimized_fullscreen)) {
+                assert(_dxgsg!=NULL);
+                _dxgsg->CheckCooperativeLevel(DO_REACTIVATE_WINDOW);
+
+                // wdxdisplay_cat.spam() << "periodic return of control to app\n";
+                _return_control_to_app = true;
+                // throw_event("PandaPaused");
+                // do we still need to do this since I return control to app periodically using timer msgs?
+                // does app need to know to avoid major computation?
+            }
+
+         #ifdef DINPUT_DEBUG_POLL
+            // probably want to get rid of this in favor of event-based input
+            if(dx_use_joystick && (wparam==_pParentWindowGroup->_pDInputInfo->_JoystickPollTimer)) {
+                DIJOYSTATE2 js;
+                ZeroMemory(&js,sizeof(js));
+                if(_pParentWindowGroup->_pDInputInfo->ReadJoystick(0,js)) {
+                    // for now just print stuff out to make sure it works
+                    wdxdisplay_cat.debug() << "joyPos (X: " << js.lX << ",Y: " << js.lY << ",Z: " << js.lZ << ")\n";
+                    for(int i=0;i<128;i++) {
+                        if(js.rgbButtons[i]!=0)
+                            wdxdisplay_cat.debug() << "joyButton "<< i << " pressed\n";
+                    }
+                } else {
+                    wdxdisplay_cat.error() << "read of Joystick failed!\n";
+                    exit(1);
+                }
+            }
+          #endif
+            return 0;
+
+        case WM_CLOSE:
+            #ifdef _DEBUG
+              wdxdisplay_cat.spam()  << "WM_CLOSE received\n";
+            #endif
+         // close_window();
+          delete _pParentWindowGroup;
+
+          // BUGBUG:  right now there is no way to tell the panda app the graphics window is invalid or
+          //          has been closed by the user, to prevent further methods from being called on the window.
+          //          this needs to be added to panda for multiple windows to work.  in the meantime, just
+          //          trigger an exit here if # windows==0, since that is the expected behavior when all
+          //          windows are closed (should be done by the app though, and it assumes you only make this
+          //          type of panda gfx window)
+
+          if(hwnd_pandawin_map.size()==0) {
+              exit(0);
+          }
+          return 0;
+
+        case WM_ACTIVATEAPP: {
+            #ifdef _DEBUG
+              wdxdisplay_cat.spam()  << "WM_ACTIVATEAPP(" << (bool)(wparam!=0) <<") received\n";
+            #endif
+
+           if((!wparam) && _props._fullscreen) {
+               deactivate_window();
+               return 0;
+           }         // dont want to reactivate until window is actually un-minimized (see WM_SIZE)
+           break;
+        }
+    }
+
+  return WinGraphicsWindow::window_proc(hwnd,msg,wparam,lparam);
+}
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::create_screen_buffers_and_device
+//       Access: Private
+//  Description: Called whenever the window is resized, this recreates
+//               the necessary buffers for rendering.
+//
+//               Sets _depth_buffer_bpp appropriately.
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow9::
+create_screen_buffers_and_device(DXScreenData &Display, bool force_16bpp_zbuffer) {
+  wdxGraphicsPipe9 *dxpipe;
+  DCAST_INTO_V(dxpipe, _pipe);
+
+  // only want dx_pick_best_screenres to apply to initial startup, and 
+  // since the initial res has already been picked, dont use auto-res-select in any future init sequence.
+  dx_pick_best_screenres = false;
+
+  DWORD dwRenderWidth=Display.DisplayMode.Width;
+  DWORD dwRenderHeight=Display.DisplayMode.Height;
+  DWORD dwBehaviorFlags=0x0;
+  LPDIRECT3D9 pD3D9=Display.pD3D9;
+  D3DCAPS9 *pD3DCaps = &Display.d3dcaps;
+  D3DPRESENT_PARAMETERS* pPresParams = &Display.PresParams;
+  RECT view_rect;
+  HRESULT hr;
+
+  // BUGBUG: need to change panda to put frame buffer properties with GraphicsWindow, not GSG!!
+  int frame_buffer_mode = _gsg->get_properties().get_frame_buffer_mode();
+  bool bWantStencil = ((frame_buffer_mode & FrameBufferProperties::FM_stencil) != 0);
+
+  PRINT_REFCNT(wdxdisplay9,pD3D9);
+
+  assert(pD3D9!=NULL);
+  assert(pD3DCaps->DevCaps & D3DDEVCAPS_HWRASTERIZATION);
+
+  pPresParams->BackBufferFormat = Display.DisplayMode.Format;  // dont need dest alpha, so just use adapter format
+
+  if (dx_sync_video && !(pD3DCaps->Caps & D3DCAPS_READ_SCANLINE)) {
+    wdxdisplay9_cat.info() << "HW doesnt support syncing to vertical refresh, ignoring dx_sync_video\n";
+    dx_sync_video=false;
+  }
+
+  // verify the rendertarget fmt one last time
+  if (FAILED(pD3D9->CheckDeviceFormat(Display.CardIDNum, D3DDEVTYPE_HAL, Display.DisplayMode.Format,D3DUSAGE_RENDERTARGET,
+                                     D3DRTYPE_SURFACE, pPresParams->BackBufferFormat))) {
+    wdxdisplay9_cat.error() << "device #"<<Display.CardIDNum<< " CheckDeviceFmt failed for surface fmt "<< D3DFormatStr(pPresParams->BackBufferFormat) << endl;
+    goto Fallback_to_16bpp_buffers;
+  }
+
+  if (FAILED(pD3D9->CheckDeviceType(Display.CardIDNum,D3DDEVTYPE_HAL, Display.DisplayMode.Format,pPresParams->BackBufferFormat,
+                                   is_fullscreen()))) {
+    wdxdisplay9_cat.error() << "device #"<<Display.CardIDNum<< " CheckDeviceType failed for surface fmt "<< D3DFormatStr(pPresParams->BackBufferFormat) << endl;
+    goto Fallback_to_16bpp_buffers;
+  }
+
+  if (Display.PresParams.EnableAutoDepthStencil) {
+    if (!dxpipe->find_best_depth_format(Display, Display.DisplayMode,
+                               &Display.PresParams.AutoDepthStencilFormat,
+                               bWantStencil, false)) {
+      wdxdisplay9_cat.error()
+        << "find_best_depth_format failed in CreateScreenBuffers for device #"
+        << Display.CardIDNum << endl;
+      goto Fallback_to_16bpp_buffers;
+    }
+    _depth_buffer_bpp = D3DFMT_to_DepthBits(Display.PresParams.AutoDepthStencilFormat);
+  } else {
+    _depth_buffer_bpp = 0;
+  }
+
+  pPresParams->Windowed = !is_fullscreen();
+
+  if (dx_multisample_antialiasing_level>1) {
+    // need to check both rendertarget and zbuffer fmts
+    hr = pD3D9->CheckDeviceMultiSampleType(Display.CardIDNum, D3DDEVTYPE_HAL, Display.DisplayMode.Format,
+                                           is_fullscreen(), D3DMULTISAMPLE_TYPE(dx_multisample_antialiasing_level), NULL);
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.fatal() << "device #"<<Display.CardIDNum<< " doesnt support multisample level "<<dx_multisample_antialiasing_level <<"surface fmt "<< D3DFormatStr(Display.DisplayMode.Format) <<endl;
+      exit(1);
+    }
+
+    if (Display.PresParams.EnableAutoDepthStencil) {
+      hr = pD3D9->CheckDeviceMultiSampleType(Display.CardIDNum, D3DDEVTYPE_HAL, Display.PresParams.AutoDepthStencilFormat,
+                                             is_fullscreen(), D3DMULTISAMPLE_TYPE(dx_multisample_antialiasing_level), NULL);
+      if (FAILED(hr)) {
+        wdxdisplay9_cat.fatal() << "device #"<<Display.CardIDNum<< " doesnt support multisample level "<<dx_multisample_antialiasing_level <<"surface fmt "<< D3DFormatStr(Display.PresParams.AutoDepthStencilFormat) <<endl;
+        exit(1);
+      }
+    }
+
+    pPresParams->MultiSampleType = D3DMULTISAMPLE_TYPE(dx_multisample_antialiasing_level);
+
+    if (wdxdisplay9_cat.is_info())
+      wdxdisplay9_cat.info() << "device #"<<Display.CardIDNum<< " using multisample antialiasing level "<<dx_multisample_antialiasing_level <<endl;
+  }
+
+  pPresParams->BackBufferCount = 1;
+  pPresParams->Flags = 0x0;
+  pPresParams->hDeviceWindow = Display.hWnd;
+  pPresParams->BackBufferWidth = Display.DisplayMode.Width;
+  pPresParams->BackBufferHeight = Display.DisplayMode.Height;
+
+#if 0
+  GetClientRect(GetDesktopWindow(), &view_rect);
+  pPresParams->BackBufferWidth = view_rect.right;
+  pPresParams->BackBufferHeight = view_rect.bottom;
+  wdxdisplay9_cat.debug()<<"width "<<view_rect.right<<" and height "<<view_rect.bottom<<"\n";
+#endif
+
+  if (_wcontext.bIsTNLDevice) {
+    dwBehaviorFlags|=D3DCREATE_HARDWARE_VERTEXPROCESSING;
+    // note: we could create a pure device in this case if I eliminated the GetRenderState calls in dxgsg
+
+    // also, no software vertex processing available since I specify D3DCREATE_HARDWARE_VERTEXPROCESSING
+    // and not D3DCREATE_MIXED_VERTEXPROCESSING
+  } else {
+    dwBehaviorFlags|=D3DCREATE_SOFTWARE_VERTEXPROCESSING;
+  }
+
+  if (dx_preserve_fpu_state)
+    dwBehaviorFlags|=D3DCREATE_FPU_PRESERVE;
+
+  // if window is not foreground in exclusive mode, ddraw thinks you are 'not active', so
+  // it changes your WM_ACTIVATEAPP from true to false, causing us
+  // to go into a 'wait-for WM_ACTIVATEAPP true' loop, and the event never comes so we hang
+  // in fullscreen wait.  also doing this for windowed mode since it was requested.
+  if (!SetForegroundWindow(Display.hWnd)) {
+    wdxdisplay9_cat.warning() << "SetForegroundWindow() failed!\n";
+  }
+
+  if (is_fullscreen()) {
+    pPresParams->SwapEffect = D3DSWAPEFFECT_DISCARD;  // we dont care about preserving contents of old frame
+    pPresParams->PresentationInterval = (dx_sync_video ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE);
+    pPresParams->FullScreen_RefreshRateInHz = Display.DisplayMode.RefreshRate;
+
+#ifdef _DEBUG
+    if (pPresParams->MultiSampleType != D3DMULTISAMPLE_NONE)
+      assert(pPresParams->SwapEffect == D3DSWAPEFFECT_DISCARD);  // only valid effect for multisample
+#endif
+
+    ClearToBlack(Display.hWnd, get_properties());
+
+    hr = pD3D9->CreateDevice(Display.CardIDNum, D3DDEVTYPE_HAL, _hWnd,
+                             dwBehaviorFlags, pPresParams, &Display.pD3DDevice);
+
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.fatal() << "D3D CreateDevice failed for device #" << Display.CardIDNum << ", " << D3DERRORSTRING(hr);
+
+      if (hr == D3DERR_OUTOFVIDEOMEMORY)
+        goto Fallback_to_16bpp_buffers;
+    }
+
+    SetRect(&view_rect, 0, 0, dwRenderWidth, dwRenderHeight);
+  }   // end create full screen buffers
+
+  else {          // CREATE WINDOWED BUFFERS
+
+    /* not necessary anymore...all cards can do this now a days
+    if (!(pD3DCaps->Caps2 & D3DCAPS2_CANRENDERWINDOWED)) {
+      wdxdisplay9_cat.fatal() << "the 3D HW cannot render windowed, exiting..." << endl;
+      exit(1);
+    }
+    */
+
+    D3DDISPLAYMODE dispmode;
+    hr = Display.pD3D9->GetAdapterDisplayMode(Display.CardIDNum, &dispmode);
+
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.fatal() << "GetAdapterDisplayMode failed" << D3DERRORSTRING(hr);
+      exit(1);
+    }
+
+    if (dispmode.Format == D3DFMT_P8) {
+      wdxdisplay9_cat.fatal() << "Can't run windowed in an 8-bit or less display mode" << endl;
+      exit(1);
+    }
+
+    pPresParams->PresentationInterval = 0;
+
+    if (dx_multisample_antialiasing_level<2) {
+      if (dx_sync_video) {
+        pPresParams->SwapEffect = D3DSWAPEFFECT_COPY;
+      } else {
+        pPresParams->SwapEffect = D3DSWAPEFFECT_DISCARD;  //D3DSWAPEFFECT_COPY;  does this make any difference?
+      }
+    } else {
+      pPresParams->SwapEffect = D3DSWAPEFFECT_DISCARD;
+    }
+
+    //    assert((dwRenderWidth==pPresParams->BackBufferWidth)&&(dwRenderHeight==pPresParams->BackBufferHeight));
+
+    hr = pD3D9->CreateDevice(Display.CardIDNum, D3DDEVTYPE_HAL, _hWnd,
+                             dwBehaviorFlags, pPresParams, &Display.pD3DDevice);
+
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.fatal() << "D3D CreateDevice failed for device #" << Display.CardIDNum << D3DERRORSTRING(hr);
+      exit(1);
+    }
+  }  // end create windowed buffers
+
+#if 0
+  pPresParams->BackBufferWidth = Display.DisplayMode.Width;
+  pPresParams->BackBufferHeight = Display.DisplayMode.Height;
+#endif
+
+  //  ========================================================
+
+  PRINT_REFCNT(wdxdisplay9,_wcontext.pD3DDevice);
+
+  if (pPresParams->EnableAutoDepthStencil) {
+    _dxgsg->_buffer_mask |= RenderBuffer::T_depth;
+    if (IS_STENCIL_FORMAT(pPresParams->AutoDepthStencilFormat))
+      _dxgsg->_buffer_mask |= RenderBuffer::T_stencil;
+  }
+
+  init_resized_window();
+
+  return;
+
+ Fallback_to_16bpp_buffers:
+
+  if ((!IS_16BPP_DISPLAY_FORMAT(pPresParams->BackBufferFormat)) &&
+     (Display.SupportedScreenDepthsMask & (R5G6B5_FLAG|X1R5G5B5_FLAG))) {
+    // fallback strategy, if we trying >16bpp, fallback to 16bpp buffers
+
+    Display.DisplayMode.Format = ((Display.SupportedScreenDepthsMask & R5G6B5_FLAG) ? D3DFMT_R5G6B5 : D3DFMT_X1R5G5B5);
+
+    if (wdxdisplay9_cat.info()) {
+      wdxdisplay9_cat.info()
+        << "CreateDevice failed with out-of-vidmem, retrying w/16bpp buffers on device #"
+        << Display.CardIDNum << endl;
+    }
+    create_screen_buffers_and_device(Display, true);
+    return;
+
+  } else if (!((dwRenderWidth==640)&&(dwRenderHeight==480))) {
+    if (wdxdisplay9_cat.info())
+      wdxdisplay9_cat.info() << "CreateDevice failed w/out-of-vidmem, retrying at 640x480 w/16bpp buffers on device #"<< Display.CardIDNum << endl;
+    // try final fallback to 640x480x16
+    Display.DisplayMode.Width=640;
+    Display.DisplayMode.Height=480;
+    create_screen_buffers_and_device(Display, true);
+    return;
+
+  } else {
+    wdxdisplay9_cat.fatal() 
+      << "Can't create any screen buffers, bailing out.\n";
+    exit(1);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::choose_device
+//       Access: Private
+//  Description: Looks at the list of available graphics adapters and
+//               chooses a suitable one for the window.
+//
+//               Returns true if successful, false on failure.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow9::
+choose_device(void) {
+  HRESULT hr;
+
+  wdxGraphicsPipe9 *dxpipe;
+  DCAST_INTO_R(dxpipe, _pipe, false);
+
+  int num_adapters = dxpipe->_pD3D9->GetAdapterCount();
+  DXDeviceInfoVec device_infos;
+
+  for (int i = 0; i < num_adapters; i++) {
+    D3DADAPTER_IDENTIFIER9 adapter_info;
+    ZeroMemory(&adapter_info, sizeof(D3DADAPTER_IDENTIFIER9));
+    hr = dxpipe->_pD3D9->GetAdapterIdentifier(i, 0, &adapter_info);
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.fatal()
+        << "D3D GetAdapterID(" << i << ") failed: "
+        << D3DERRORSTRING(hr) << endl;
+      continue;
+    }
+     
+    LARGE_INTEGER *DrvVer = &adapter_info.DriverVersion;
+
+    wdxdisplay9_cat.info()
+      << "D3D9." << (dxpipe->_bIsDX9 ?"1":"0") << " Adapter[" << i << "]: " << adapter_info.Description 
+      << ", Driver: " << adapter_info.Driver << ", DriverVersion: ("
+      << HIWORD(DrvVer->HighPart) << "." << LOWORD(DrvVer->HighPart) << "."
+      << HIWORD(DrvVer->LowPart) << "." << LOWORD(DrvVer->LowPart)
+      << ")\nVendorID: 0x" << (void*) adapter_info.VendorId 
+      << " DeviceID: 0x" <<  (void*) adapter_info.DeviceId
+      << " SubsysID: 0x" << (void*) adapter_info.SubSysId
+      << " Revision: 0x" << (void*) adapter_info.Revision << endl;
+    
+    HMONITOR hMon = dxpipe->_pD3D9->GetAdapterMonitor(i);
+    if (hMon == NULL) {
+      wdxdisplay9_cat.info()
+        << "D3D9 Adapter[" << i << "]: seems to be disabled, skipping it\n";
+      continue;
+    }
+
+    DXDeviceInfo devinfo;
+    ZeroMemory(&devinfo, sizeof(devinfo));
+    memcpy(&devinfo.guidDeviceIdentifier, &adapter_info.DeviceIdentifier, 
+           sizeof(GUID));
+    strncpy(devinfo.szDescription, adapter_info.Description,
+            MAX_DEVICE_IDENTIFIER_STRING);
+    strncpy(devinfo.szDriver, adapter_info.Driver,
+            MAX_DEVICE_IDENTIFIER_STRING);
+    devinfo.VendorID = adapter_info.VendorId;
+    devinfo.DeviceID = adapter_info.DeviceId;
+    devinfo.hMon = hMon;
+    devinfo.cardID = i;
+
+    device_infos.push_back(devinfo);
+  }
+
+  if (device_infos.empty()) {
+    wdxdisplay9_cat.error()
+      << "No available D3D9 devices found.\n";
+    return false;
+  }
+
+  // Since some adapters may have been disabled, we should re-obtain
+  // the number of available adapters.
+  num_adapters = (int)device_infos.size();
+
+  // Now choose a suitable adapter.
+
+  int adapter_num = D3DADAPTER_DEFAULT;
+
+  // Eventually, we should have some interface for specifying a device
+  // index interactively, instead of only via Configrc.
+  if (dx_preferred_device_id != -1) {
+    if (dx_preferred_device_id < 0 || dx_preferred_device_id >= num_adapters) {
+      wdxdisplay9_cat.error()
+        << "invalid 'dx-preferred-device-id', valid values are 0-" 
+        << num_adapters - 1 << ", using default adapter instead.\n";
+    } else {
+      adapter_num = dx_preferred_device_id;
+    }
+  }
+
+  UINT good_device_count=0;
+  for(UINT devnum=0;devnum<device_infos.size() /*&& (good_device_count < num_windows)*/;devnum++) {
+      if(search_for_device(dxpipe,&device_infos[devnum]))
+          good_device_count++;
+  }
+
+  if(good_device_count==0) {
+     wdxdisplay9_cat.fatal() << "no usable display devices, exiting...\n";
+     return false;
+  }
+
+  return true;
+}
+
+/*
+primary init sequence of old method, still need to integrate multi-window functionality
+void wdxGraphicsWindow9Group::initWindowGroup(void) {
+    HRESULT hr;
+
+    assert(_windows.size()>0);
+    _hOldForegroundWindow=GetForegroundWindow();
+    _bClosingAllWindows= false;
+
+    UINT num_windows=_windows.size();
+
+    #define D3D9_NAME "d3d9.dll"
+    #define D3DCREATE9 "Direct3DCreate9"
+
+    _hD3D9_DLL = LoadLibrary(D3D9_NAME);
+    if(_hD3D9_DLL == 0) {
+        wdxdisplay_cat.fatal() << "PandaDX9 requires DX9, can't locate " << D3D9_NAME <<"!\n";
+        exit(1);
+    }
+
+    _hMouseCursor = NULL;
+    _bLoadedCustomCursor = false;
+
+    _pDInputInfo = NULL;
+
+    // can only get multimon HW acceleration in fullscrn on DX7
+
+    UINT numMonitors = GetSystemMetrics(SM_CMONITORS);
+
+    if(numMonitors < num_windows) {
+        if(numMonitors==0) {
+             numMonitors=1;   //win95 system will fail this call
+          } else {
+              wdxdisplay_cat.fatal() << "system has only " << numMonitors << " monitors attached, couldn't find enough devices to meet multi window reqmt of " << num_windows << endl;
+              exit(1);
+          }
+    }
+
+   // Do all DX7 stuff first
+   //  find_all_card_memavails();
+
+    LPDIRECT3D9 pD3D9;
+
+    typedef LPDIRECT3D9 (WINAPI *Direct3DCreate9_ProcPtr)(UINT SDKVersion);
+
+    // dont want to statically link to possibly non-existent d3d9 dll, so must call D3DCr9 indirectly
+    Direct3DCreate9_ProcPtr D3DCreate9_Ptr =
+        (Direct3DCreate9_ProcPtr) GetProcAddress(_hD3D9_DLL, D3DCREATE9);
+
+    if(D3DCreate9_Ptr == NULL) {
+        wdxdisplay_cat.fatal() << "GetProcAddress for "<< D3DCREATE9 << "failed!" << endl;
+        exit(1);
+    }
+
+// these were taken from the 8.0 and 8.1 d3d8.h SDK headers
+#define D3D_SDK_VERSION_8_0  120
+#define D3D_SDK_VERSION_8_1  220
+
+    // are we using 8.0 or 8.1?
+    WIN32_FIND_DATA TempFindData;
+    HANDLE hFind;
+    char tmppath[MAX_PATH];
+    GetSystemDirectory(tmppath,MAX_PATH);
+    strcat(tmppath,"\\dpnhpast.dll");
+    hFind = FindFirstFile ( tmppath,&TempFindData );
+    if(hFind != INVALID_HANDLE_VALUE) {
+         FindClose(hFind);
+         _bIsDX81=true;
+         pD3D8 = (*D3DCreate8_Ptr)(D3D_SDK_VERSION_8_1);
+    } else {
+        _bIsDX81=false;
+        pD3D8 = (*D3DCreate8_Ptr)(D3D_SDK_VERSION_8_0);
+    }
+
+    if(pD3D8==NULL) {
+        wdxdisplay_cat.fatal() << D3DCREATE8 << " failed!\n";
+        exit(1);
+    }
+
+    _numAdapters = pD3D8->GetAdapterCount();
+    if(_numAdapters < num_windows) {
+        wdxdisplay_cat.fatal() << "couldn't find enough devices attached to meet multi window reqmt of " << num_windows << endl;
+        exit(1);
+    }
+
+    for(UINT i=0;i<_numAdapters;i++) {
+        D3DADAPTER_IDENTIFIER8 adapter_info;
+        ZeroMemory(&adapter_info,sizeof(D3DADAPTER_IDENTIFIER8));
+        hr = pD3D8->GetAdapterIdentifier(i,D3DENUM_NO_WHQL_LEVEL,&adapter_info);
+        if(FAILED(hr)) {
+            wdxdisplay_cat.fatal() << "D3D GetAdapterID failed" << D3DERRORSTRING(hr);
+        }
+
+        LARGE_INTEGER *DrvVer=&adapter_info.DriverVersion;
+
+        wdxdisplay_cat.info() << "D3D8 Adapter[" << i << "]: " << adapter_info.Description <<
+                               ", Driver: " << adapter_info.Driver << ", DriverVersion: ("
+            << HIWORD(DrvVer->HighPart) << "." << LOWORD(DrvVer->HighPart) << "."
+            << HIWORD(DrvVer->LowPart) << "." << LOWORD(DrvVer->LowPart) << ")\nVendorID: 0x"
+            << (void*) adapter_info.VendorId << " DeviceID: 0x" <<  (void*) adapter_info.DeviceId
+            << " SubsysID: 0x" << (void*) adapter_info.SubSysId << " Revision: 0x"
+            << (void*) adapter_info.Revision << endl;
+
+        HMONITOR hMon=pD3D8->GetAdapterMonitor(i);
+        if(hMon==NULL) {
+            wdxdisplay_cat.info() << "D3D8 Adapter[" << i << "]: seems to be disabled, skipping it\n";
+            continue;
+        }
+
+        DXDeviceInfo devinfo;
+        ZeroMemory(&devinfo,sizeof(devinfo));
+        memcpy(&devinfo.guidDeviceIdentifier,&adapter_info.DeviceIdentifier,sizeof(GUID));
+        strncpy(devinfo.szDescription,adapter_info.Description,MAX_DEVICE_IDENTIFIER_STRING);
+        strncpy(devinfo.szDriver,adapter_info.Driver,MAX_DEVICE_IDENTIFIER_STRING);
+        devinfo.VendorID=adapter_info.VendorId;
+        devinfo.DeviceID=adapter_info.DeviceId;
+        devinfo.hMon=hMon;
+        devinfo.cardID=i;
+
+        _DeviceInfoVec.push_back(devinfo);
+    }
+
+    for(UINT i=0;i<num_windows;i++) {
+        _windows[i]->config_window(this);
+    }
+
+    UINT good_device_count=0;
+
+    if(num_windows==1) {
+        UINT D3DAdapterNum = D3DADAPTER_DEFAULT;
+
+        if(dx_preferred_deviceID!=-1) {
+            if(dx_preferred_deviceID>=(int)_numAdapters) {
+                wdxdisplay_cat.fatal() << "invalid 'dx-preferred-device-id', valid values are 0-" << _numAdapters-1 << ", using default adapter 0 instead\n";
+            } else D3DAdapterNum=dx_preferred_deviceID;
+        }
+        if(_windows[0]->search_for_device(pD3D8,&(_DeviceInfoVec[D3DAdapterNum])))
+            good_device_count=1;
+    } else {
+        for(UINT devnum=0;devnum<_DeviceInfoVec.size() && (good_device_count < num_windows);devnum++) {
+            if(_windows[devnum]->search_for_device(pD3D8,&(_DeviceInfoVec[devnum])))
+                good_device_count++;
+        }
+    }
+
+    if(good_device_count < num_windows) {
+      if(good_device_count==0)
+         wdxdisplay_cat.fatal() << "no usable display devices, exiting...\n";
+       else wdxdisplay_cat.fatal() << "multi-device request for " << num_windows << "devices, found only "<< good_device_count << " usable ones, exiting!";
+      exit(1);
+    }
+
+    _DeviceInfoVec.clear();  // dont need this anymore
+
+    if(wdxdisplay_cat.is_debug() && (g_pCardIDVec!=NULL)) {
+      // print out the MaxAvailVidMems
+      for(UINT i=0;i<_windows.size();i++) {
+        D3DADAPTER_IDENTIFIER8 adapter_info;
+        pD3D8->GetAdapterIdentifier(_windows[i]->_wcontext.CardIDNum,D3DENUM_NO_WHQL_LEVEL,&adapter_info);
+        wdxdisplay_cat.info() << "D3D8 Adapter[" << i << "]: " << adapter_info.Description
+                              << ", MaxAvailVideoMem: " << _windows[i]->_wcontext.MaxAvailVidMem
+                              << ", IsLowVidMemCard: " << (_windows[i]->_wcontext.bIsLowVidMemCard ? "true" : "false") << endl;
+      }
+    }
+
+    CreateWindows();  // creates win32 windows  (need to do this before Setting coopLvls and display modes,
+                      // but after we have all the monitor handles needed by CreateWindow()
+
+//    SetCoopLevelsAndDisplayModes();
+
+    if(dx_show_fps_meter)
+       _windows[0]->_dxgsg->_bShowFPSMeter = true;  // just show fps on 1st mon
+
+    for(UINT i=0;i<num_windows;i++) {
+        _windows[i]->CreateScreenBuffersAndDevice(_windows[i]->_wcontext);
+    }
+
+    for(UINT i=0;i<num_windows;i++) {
+        _windows[i]->finish_window_setup();
+    }
+
+    SAFE_DELETE(g_pCardIDVec);  // dont need this anymore
+
+    for(UINT i=0;i<num_windows;i++) {
+        _windows[i]->_dxgsg->SetDXReady(true);
+    }
+
+    dx_pick_best_screenres = false;   // only want to do this on startup, not resize
+
+  #ifdef DINPUT_DEBUG_POLL
+    if(dx_use_joystick) {
+        _pDInputInfo = new DInput8Info;
+        assert(_pDInputInfo !=NULL);
+       if(!_pDInputInfo->InitDirectInput()) {
+           wdxdisplay_cat.error() << "InitDirectInput failed!\n";
+           exit(1);
+       }
+
+       if(!_pDInputInfo->CreateJoystickOrPad(_hParentWindow)) {  // associate w/parent window of group for now
+           wdxdisplay_cat.error() << "CreateJoystickOrPad failed!\n";
+           exit(1);
+       }
+
+        // for now, just set up a WM_TIMER to poll the joystick.
+        // could configure it to do event-based input, and that is default w/action mapping
+        // which would be better, less processor intensive
+
+        #define POLL_FREQUENCY_HZ  3
+        _pDInputInfo->_JoystickPollTimer = SetTimer(_hParentWindow, JOYSTICK_POLL_TIMER_ID, 1000/POLL_FREQUENCY_HZ, NULL);
+        if(_pDInputInfo->_JoystickPollTimer!=JOYSTICK_POLL_TIMER_ID) {
+           wdxdisplay_cat.error() << "Error in joystick SetTimer!\n";
+       }
+    }
+  #endif
+}
+*/
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::search_for_device
+//       Access: Private
+//  Description: Searches for a suitable hardware device for
+//               rendering.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow9::
+search_for_device(wdxGraphicsPipe9 *dxpipe, DXDeviceInfo *device_info) {
+
+  assert(dxpipe != NULL);  
+  WindowProperties properties = get_properties();
+  DWORD dwRenderWidth = properties.get_x_size();
+  DWORD dwRenderHeight = properties.get_y_size();
+  HRESULT hr;
+  LPDIRECT3D9 pD3D9 = dxpipe->_pD3D9;
+
+  assert(_dxgsg != NULL);  
+  _wcontext.pD3D9 = pD3D9;
+  _wcontext.bIsDX9 = dxpipe->_bIsDX9;
+  _wcontext.CardIDNum = device_info->cardID;  // could this change by end?
+
+  int frame_buffer_mode = _gsg->get_properties().get_frame_buffer_mode();
+  bool bWantStencil = ((frame_buffer_mode & FrameBufferProperties::FM_stencil) != 0);
+  
+  hr = pD3D9->GetAdapterIdentifier(device_info->cardID, 0,
+                                   &_wcontext.DXDeviceID);
+  if (FAILED(hr)) {
+    wdxdisplay9_cat.error()
+      << "D3D GetAdapterID failed" << D3DERRORSTRING(hr);
+    return false;
+  }
+  
+  D3DCAPS9 d3dcaps;
+  hr = pD3D9->GetDeviceCaps(device_info->cardID,D3DDEVTYPE_HAL,&d3dcaps);
+  if (FAILED(hr)) {
+    if ((hr==D3DERR_INVALIDDEVICE)||(hr==D3DERR_NOTAVAILABLE)) {
+      wdxdisplay9_cat.error()
+        << "No DirectX 9 D3D-capable 3D hardware detected for device # "
+        << device_info->cardID << " (" <<device_info->szDescription 
+        << ")!\n";
+    } else {
+      wdxdisplay9_cat.error()
+        << "GetDeviceCaps failed: " << D3DERRORSTRING(hr) << endl;
+    }
+    return false;
+  }
+  
+  //search_for_valid_displaymode needs these to be set
+  memcpy(&_wcontext.d3dcaps, &d3dcaps,sizeof(D3DCAPS9));
+  _wcontext.CardIDNum = device_info->cardID;
+  
+  _wcontext.MaxAvailVidMem = UNKNOWN_VIDMEM_SIZE;
+  _wcontext.bIsLowVidMemCard = false;
+  
+  // bugbug: wouldnt we like to do GetAVailVidMem so we can do
+  // upper-limit memory computation for dx9 cards too?  otherwise
+  // verify_window_sizes cant do much
+  if ((d3dcaps.MaxStreams==0) || dx_pick_best_screenres) {
+    if (wdxdisplay9_cat.is_debug()) {
+      wdxdisplay9_cat.debug()
+        << "checking vidmem size\n";
+    }
+    //    assert(IS_VALID_PTR(_pParentWindowGroup));
+    
+    // look for low memory video cards
+    //    _pParentWindowGroup->find_all_card_memavails();
+    
+    UINT IDnum;
+    
+    // simple linear search to match DX7 card info w/DX9 card ID
+    for (IDnum=0; IDnum < dxpipe->_card_ids.size(); IDnum++) {
+      //      wdxdisplay9_cat.info()
+      //        << "comparing '" << dxpipe->_card_ids[IDnum].Driver
+      //        << "' to '" << _wcontext.DXDeviceID.Driver << "'\n";
+      if (//(stricmp(dxpipe->_card_ids[IDnum].szDriver,device_info->szDriver)==0) &&
+         (device_info->VendorID==dxpipe->_card_ids[IDnum].VendorID) &&
+         (device_info->DeviceID==dxpipe->_card_ids[IDnum].DeviceID) &&
+         (device_info->hMon==dxpipe->_card_ids[IDnum].hMon))
+        break;
+    }
+    
+    if (IDnum < dxpipe->_card_ids.size()) {
+      _wcontext.MaxAvailVidMem = dxpipe->_card_ids[IDnum].MaxAvailVidMem;
+      _wcontext.bIsLowVidMemCard = dxpipe->_card_ids[IDnum].bIsLowVidMemCard;
+    } else {
+      wdxdisplay9_cat.error()
+        << "Error: couldnt find a CardID match in DX7 info, assuming card is not a lowmem card\n";
+    }
+  }
+
+  if ((bWantStencil) && (d3dcaps.StencilCaps==0x0)) {
+    wdxdisplay9_cat.fatal()
+      << "Stencil ability requested, but device #" << device_info->cardID
+      << " (" << _wcontext.DXDeviceID.Description
+      << "), has no stencil capability!\n";
+    return false;
+  }
+
+  // just because TNL is true, it doesnt mean vtx shaders are
+  // supported in HW (see GF2) for this case, you probably want MIXED
+  // processing to use HW for fixed-fn vertex processing and SW for
+  // vtx shaders
+  _wcontext.bIsTNLDevice = 
+    ((d3dcaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0);
+  _wcontext.bCanUseHWVertexShaders = 
+    (d3dcaps.VertexShaderVersion >= D3DVS_VERSION(1, 0));
+  _wcontext.bCanUsePixelShaders = 
+    (d3dcaps.PixelShaderVersion >= D3DPS_VERSION(1, 0));
+
+  bool bNeedZBuffer = 
+    ((!(d3dcaps.RasterCaps & D3DPRASTERCAPS_ZBUFFERLESSHSR )) &&
+     ((frame_buffer_mode & FrameBufferProperties::FM_depth) != 0));
+
+  _wcontext.PresParams.EnableAutoDepthStencil = bNeedZBuffer;
+
+  D3DFORMAT pixFmt = D3DFMT_UNKNOWN;
+
+  if (is_fullscreen()) {
+    bool bCouldntFindValidZBuf;
+    if (!_wcontext.bIsLowVidMemCard) {
+      bool bUseDefaultSize = dx_pick_best_screenres &&
+        ((_wcontext.MaxAvailVidMem == UNKNOWN_VIDMEM_SIZE) ||
+         is_badvidmem_card(&_wcontext.DXDeviceID));
+
+      if (dx_pick_best_screenres && !bUseDefaultSize) {
+        typedef struct {
+          UINT memlimit;
+          DWORD scrnX,scrnY;
+        } Memlimres;
+
+        const Memlimres MemRes[] = {
+          {       0,  640, 480},
+          { 8000000,  800, 600},
+#if 0
+          {16000000, 1024, 768},
+          {32000000, 1280,1024},  // 32MB+ cards will choose this
+#else
+          // unfortunately the 32MB card perf varies greatly (TNT2-GF2),
+          // so we need to be conservative since frame rate difference
+          // can change from 15->30fps when going from 1280x1024->800x600
+          // on low-end 32mb cards
+          {16000000,  800, 600},
+          {32000000,  800, 600},  // 32MB+ cards will choose this
+#endif
+          // some monitors have trouble w/1600x1200, so dont pick this by deflt,
+          // even though 64MB cards should handle it              
+          {64000000, 1280,1024}   // 64MB+ cards will choose this
+        };
+        const NumResLims = (sizeof(MemRes)/sizeof(Memlimres));
+
+        for(int i = NumResLims - 1; i >= 0; i--) {
+          // find biggest slot card can handle
+          if (_wcontext.MaxAvailVidMem > MemRes[i].memlimit) {
+            dwRenderWidth = MemRes[i].scrnX;
+            dwRenderHeight = MemRes[i].scrnY;
+
+            wdxdisplay9_cat.info()
+              << "pick_best_screenres: trying " << dwRenderWidth 
+              << "x" << dwRenderHeight << " based on "
+              << _wcontext.MaxAvailVidMem << " bytes avail\n";
+
+             dxpipe->search_for_valid_displaymode(_wcontext,dwRenderWidth, dwRenderHeight, 
+                                         bNeedZBuffer, bWantStencil,
+                                         &_wcontext.SupportedScreenDepthsMask,
+                                         &bCouldntFindValidZBuf,
+                                         &pixFmt, dx_force_16bpp_zbuffer);
+
+            // note I'm not saving refresh rate, will just use adapter
+            // default at given res for now
+
+            if (pixFmt != D3DFMT_UNKNOWN) {
+              break;
+            }
+
+            wdxdisplay9_cat.info()
+              << "skipping scrnres; "
+              << (bCouldntFindValidZBuf ? "Couldnt find valid zbuffer format to go with FullScreen mode" : "No supported FullScreen modes")
+              << " at " << dwRenderWidth << "x" << dwRenderHeight
+              << " for device #" << _wcontext.CardIDNum << endl;
+          }
+        }
+        // otherwise just go with whatever was specified (we probably shouldve marked this card as lowmem if it gets to end of loop w/o breaking
+      }
+
+      if (pixFmt == D3DFMT_UNKNOWN) {
+        if (bUseDefaultSize) {
+          wdxdisplay9_cat.info()
+            << "pick_best_screenres: defaulted 800x600 based on no reliable vidmem size\n";
+          dwRenderWidth=800;
+          dwRenderHeight=600;
+        }
+
+        dxpipe->search_for_valid_displaymode(_wcontext, dwRenderWidth, dwRenderHeight,
+                                     bNeedZBuffer, bWantStencil,
+                                     &_wcontext.SupportedScreenDepthsMask,
+                                     &bCouldntFindValidZBuf,
+                                     &pixFmt, dx_force_16bpp_zbuffer);
+
+        // note I'm not saving refresh rate, will just use adapter
+        // default at given res for now
+
+        if (pixFmt == D3DFMT_UNKNOWN) {
+          wdxdisplay9_cat.error()
+            << (bCouldntFindValidZBuf ? "Couldnt find valid zbuffer format to go with FullScreen mode" : "No supported FullScreen modes")
+            << " at " << dwRenderWidth << "x" << dwRenderHeight << " for device #" << _wcontext.CardIDNum <<endl;
+
+          // run it again in verbose mode to get more dbg info to log
+          dxpipe->search_for_valid_displaymode(_wcontext,dwRenderWidth, dwRenderHeight,
+                                       bNeedZBuffer, bWantStencil,
+                                       &_wcontext.SupportedScreenDepthsMask,
+                                       &bCouldntFindValidZBuf,
+                                       &pixFmt, dx_force_16bpp_zbuffer, true);
+          return false;
+        }
+      }
+    } else {
+      // Low Memory card
+      dwRenderWidth=640;
+      dwRenderHeight=480;
+      dx_force_16bpptextures = true;
+
+      // need to autoforce 16bpp zbuf?  or let user use that extra mem for textures/framebuf res/etc?
+      // most lowmem cards only do 16bpp Z anyway, but we wont force it for now
+
+      dxpipe->search_for_valid_displaymode(_wcontext,dwRenderWidth, dwRenderHeight,
+                                   bNeedZBuffer, bWantStencil,
+                                   &_wcontext.SupportedScreenDepthsMask,
+                                   &bCouldntFindValidZBuf,
+                                   &pixFmt, dx_force_16bpp_zbuffer);
+
+      // hack: figuring out exactly what res to use is tricky, instead I will
+      // just use 640x480 if we have < 3 meg avail
+
+      if (_wcontext.SupportedScreenDepthsMask & R5G6B5_FLAG) {
+        pixFmt = D3DFMT_R5G6B5;
+      } else if (_wcontext.SupportedScreenDepthsMask & X1R5G5B5_FLAG) {
+        pixFmt = D3DFMT_X1R5G5B5;
+      } else {
+        wdxdisplay9_cat.fatal()
+          << "Low Memory VidCard has no supported FullScreen 16bpp resolutions at "
+          << dwRenderWidth << "x" << dwRenderHeight << " for device #"
+          << device_info->cardID << " (" 
+          << _wcontext.DXDeviceID.Description << "), skipping device...\n";
+
+        // run it again in verbose mode to get more dbg info to log
+        dxpipe->search_for_valid_displaymode(_wcontext, dwRenderWidth, dwRenderHeight,
+                                     bNeedZBuffer, bWantStencil,
+                                     &_wcontext.SupportedScreenDepthsMask,
+                                     &bCouldntFindValidZBuf,
+                                     &pixFmt, dx_force_16bpp_zbuffer, 
+                                     true /* verbose mode on*/);
+        return false;
+      }
+
+      if (wdxdisplay9_cat.is_info()) {
+        wdxdisplay9_cat.info()
+          << "Available VidMem (" << _wcontext.MaxAvailVidMem
+          << ") is under threshold, using 640x480 16bpp rendertargets to save tex vidmem.\n";
+      }
+    }
+  } else {
+    // Windowed Mode
+
+    D3DDISPLAYMODE dispmode;
+    hr = pD3D9->GetAdapterDisplayMode(device_info->cardID,&dispmode);
+    if (FAILED(hr)) {
+      wdxdisplay9_cat.error()
+        << "GetAdapterDisplayMode(" << device_info->cardID
+        << ") failed" << D3DERRORSTRING(hr);
+      return false;
+    }
+    pixFmt = dispmode.Format;
+  }
+
+  _wcontext.DisplayMode.Width = dwRenderWidth;
+  _wcontext.DisplayMode.Height = dwRenderHeight;
+  _wcontext.DisplayMode.Format = pixFmt;
+  _wcontext.DisplayMode.RefreshRate = D3DPRESENT_RATE_DEFAULT;
+  _wcontext.hMon = device_info->hMon;
+
+  if (dwRenderWidth != properties.get_x_size() ||
+      dwRenderHeight != properties.get_y_size()) {
+    // This is probably not the best place to put this; I'm just putting
+    // it here for now because if dx_pick_best_screenres is true, the
+    // code above might have changed the size of the window
+    // unexpectedly.  This code gets called when make_gsg() is called,
+    // which means it is called in the draw thread, but this method
+    // should really be called from the window thread.  In DirectX those
+    // may always be the same threads anyway, so we may be all right.
+    // Still, it's a little strange that the window may change size
+    // after it has already been opened, at the time we create the GSG
+    // for it; it would be better if we could find a way to do this
+    // resolution-selection logic earlier, say at the time the window is
+    // created.
+    system_changed_size(dwRenderWidth, dwRenderHeight);
+    WindowProperties resized_props;
+    resized_props.set_size(dwRenderWidth, dwRenderHeight);
+    _properties.add_properties(resized_props);
+  }
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::reset_device_resize_window
+//       Access: Private
+//  Description: Called after a window (either fullscreen or windowed)
+//               has been resized, this recreates the D3D structures
+//               to match the new size.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow9::
+reset_device_resize_window(UINT new_xsize, UINT new_ysize) {
+  assert((new_xsize > 0) && (new_ysize > 0));
+  bool bRetval = true;
+
+  DXScreenData *pScrn;
+  D3DPRESENT_PARAMETERS d3dpp;
+  memcpy(&d3dpp, &_wcontext.PresParams, sizeof(D3DPRESENT_PARAMETERS));
+  d3dpp.BackBufferWidth = new_xsize;
+  d3dpp.BackBufferHeight = new_ysize;
+  make_current();
+  HRESULT hr = _dxgsg->reset_d3d_device(&d3dpp, &pScrn);
+  
+  if (FAILED(hr)) {
+    bRetval = false;
+    wdxdisplay9_cat.error()
+      << "reset_device_resize_window Reset() failed" << D3DERRORSTRING(hr);
+    if (hr == D3DERR_OUTOFVIDEOMEMORY) {
+      hr = _dxgsg->reset_d3d_device(&_wcontext.PresParams, &pScrn);
+      if (FAILED(hr)) {
+        wdxdisplay9_cat.error()
+          << "reset_device_resize_window Reset() failed OutOfVidmem, then failed again doing Reset w/original params:" << D3DERRORSTRING(hr);
+        exit(1);
+      } else {
+        if (wdxdisplay9_cat.is_info())
+          wdxdisplay9_cat.info()
+            << "reset of original size (" << _wcontext.PresParams.BackBufferWidth
+            << "," << _wcontext.PresParams.BackBufferHeight << ") succeeded\n";
+      }
+    } else {
+      wdxdisplay9_cat.fatal() 
+        << "Can't reset device, bailing out.\n";
+      exit(1);
+    }
+  }
+  // before you init_resized_window you need to copy certain changes to _wcontext
+  _wcontext.pSwapChain = pScrn->pSwapChain;
+  wdxdisplay9_cat.debug() << "swapchain is " << _wcontext.pSwapChain << "\n";
+  init_resized_window();
+  return bRetval;
+}
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::init_resized_window
+//       Access: Private
+//  Description: Reinitializes the window after it has been resized,
+//               or after it is first created.
+//
+//               Assumes CreateDevice or Device->Reset() has just been
+//               called, and the new size is specified in
+//               _wcontext.PresParams.
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow9::
+init_resized_window() {
+  HRESULT hr;
+
+  DWORD newWidth = _wcontext.PresParams.BackBufferWidth;
+  DWORD newHeight = _wcontext.PresParams.BackBufferHeight;
+
+  assert((newWidth!=0) && (newHeight!=0));
+  assert(_wcontext.hWnd!=NULL);
+
+  if (_wcontext.PresParams.Windowed) {
+    POINT ul,lr;
+    RECT client_rect;
+
+    // need to figure out x,y origin offset of window client area on screen
+    // (we already know the client area size)
+
+    GetClientRect(_wcontext.hWnd, &client_rect);
+    ul.x = client_rect.left;
+    ul.y = client_rect.top;
+    lr.x = client_rect.right;
+    lr.y=client_rect.bottom;
+    ClientToScreen(_wcontext.hWnd, &ul);
+    ClientToScreen(_wcontext.hWnd, &lr);
+    client_rect.left = ul.x;
+    client_rect.top = ul.y;
+    client_rect.right = lr.x;
+    client_rect.bottom = lr.y;
+    //    _props._xorg = client_rect.left;  // _props should reflect view rectangle
+    //    _props._yorg = client_rect.top;
+
+    /*
+#ifdef _DEBUG
+    // try to make sure GDI and DX agree on window client area size
+    // but client rect will not include any offscreen areas, so dont
+    // do check if window was bigger than screen (there are other bad
+    // cases too, like when window is positioned partly offscreen,
+    // or if window trim border make size bigger than screen)
+
+    RECT desktop_rect;
+    GetClientRect(GetDesktopWindow(), &desktop_rect);
+    int x_size = get_properties().get_x_size();
+    int y_size = get_properties().get_y_size();
+    if ((x_size < RECT_X_SIZE(desktop_rect)) && 
+        (y_size < RECT_Y_SIZE(desktop_rect)))
+      assert((RECT_X_SIZE(client_rect) == newWidth) &&
+             (RECT_Y_SIZE(client_rect) == newHeight));
+#endif
+    */
+  }
+
+  //  resized(newWidth, newHeight);  // update panda channel/display rgn info, _props.x_size, _props.y_size
+
+  // clear window to black ASAP
+  assert(_wcontext.hWnd!=NULL);
+  ClearToBlack(_wcontext.hWnd, get_properties());
+
+  // clear textures and VB's out of video&AGP mem, so cache is reset
+  hr = _wcontext.pD3DDevice->EvictManagedResources();
+  if (FAILED(hr)) {
+    wdxdisplay9_cat.error()
+      << "ResourceManagerDiscardBytes failed for device #" 
+      << _wcontext.CardIDNum << D3DERRORSTRING(hr);
+  }
+
+  _dxgsg->set_context(&_wcontext); 
+  // Note: dx_init will fill in additional fields in _wcontext, like supportedtexfmts
+  _dxgsg->dx_init();
+
+  if(dx_use_dx_cursor && is_fullscreen()) {
+      hr = CreateDX9Cursor(_wcontext.pD3DDevice,_mouse_cursor,dx_show_cursor_watermark);
+      if(FAILED(hr))
+          wdxdisplay9_cat.error() << "CreateDX9Cursor failed!" <<  D3DERRORSTRING(hr);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::D3DFMT_to_DepthBits
+//       Access: Private, Static
+//  Description: Returns the number of depth bits represented by the
+//               indicated D3DFORMAT value.
+////////////////////////////////////////////////////////////////////
+int wdxGraphicsWindow9::
+D3DFMT_to_DepthBits(D3DFORMAT fmt) {
+  switch(fmt) {
+  case D3DFMT_D16:
+    return 16;
+
+  case D3DFMT_D24X8:
+  case D3DFMT_D24X4S4:
+  case D3DFMT_D24S8:
+    return 24;
+
+  case D3DFMT_D32:
+    return 32;
+
+  case D3DFMT_D15S1:
+    return 15;
+
+  default:
+    wdxdisplay9_cat.debug()
+      << "D3DFMT_DepthBits: unhandled D3DFMT!\n";
+    return 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::is_badvidmem_card
+//       Access: Private, Static
+//  Description: Returns true if the indicated video adapter card is
+//               known to report an inaccurate figure for available
+//               video memory.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow9::
+is_badvidmem_card(D3DADAPTER_IDENTIFIER9 *pDevID) {
+  // dont trust Intel cards since they often use regular memory as vidmem
+  if (pDevID->VendorId == 0x00008086) {
+    return true;
+  }
+
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::reset_window
+//       Access: Public, Virtual
+//  Description: Resets the window framebuffer right now.  Called 
+//               from graphicsEngine. It releases the current swap
+//               chain / creates a new one. If this is the initial
+//               window and swapchain is false, then it calls reset_
+//               main_device to Reset the device.
+////////////////////////////////////////////////////////////////////
+void wdxGraphicsWindow9::
+reset_window(bool swapchain) {
+  DXGraphicsStateGuardian9 *dxgsg;
+  DCAST_INTO_V(dxgsg,_gsg);
+  if (swapchain) {
+    if (_wcontext.pSwapChain) {
+      dxgsg->create_swap_chain(&_wcontext);
+      wdxdisplay9_cat.debug() << "created swapchain " << _wcontext.pSwapChain << "\n";
+    }
+  }
+  else {
+    if (_wcontext.pSwapChain) {
+      dxgsg->release_swap_chain(&_wcontext);
+      wdxdisplay9_cat.debug() << "released swapchain " << _wcontext.pSwapChain << "\n";
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: wdxGraphicsWindow9::open_window
+//       Access: Protected, Virtual
+//  Description: Opens the window right now.  Called from the window
+//               thread.  Returns true if the window is successfully
+//               opened, or false if there was a problem.
+////////////////////////////////////////////////////////////////////
+bool wdxGraphicsWindow9::
+open_window(void) {
+  PT(DXGraphicsDevice9) dxdev;
+  DXGraphicsStateGuardian9 *dxgsg;
+  DCAST_INTO_R(dxgsg,_gsg,false);
+  WindowProperties props;
+
+  if(!choose_device()) {
+      return false;
+  }
+  if (dxgsg->get_pipe()->get_device() && !multiple_windows)
+    return false;
+
+  wdxdisplay9_cat.debug() << "_wcontext.hWnd is " << _wcontext.hWnd << "\n";
+  if (!WinGraphicsWindow::open_window()) {
+    return false;
+  }
+  _wcontext.hWnd = _hWnd;
+
+  wdxdisplay9_cat.debug() << "_wcontext.hWnd is " << _wcontext.hWnd << "\n";
+  
+  // Here check if a device already exists. If so, then this open_window
+  // call may be an extension to create multiple windows on same device
+  // In that case just create an additional swapchain for this window
+
+  if (dxgsg->get_pipe()->get_device() == NULL) {
+    create_screen_buffers_and_device(_wcontext, dx_force_16bpp_zbuffer);
+    dxgsg->get_pipe()->make_device((void*)(&_wcontext));
+    dxgsg->copy_pres_reset(&_wcontext);
+    if (multiple_windows) // then we have no choice but to waist a framebuffer
+      dxgsg->create_swap_chain(&_wcontext);
+  }
+  else {
+
+    // fill in the DXScreenData from dxdevice here and change the reference to hWnd.
+    dxdev = (DXGraphicsDevice9*)dxgsg->get_pipe()->get_device();
+    props = get_properties();
+
+    memcpy(&_wcontext,dxdev->_pScrn,sizeof(DXScreenData));
+    _wcontext.hWnd = _hWnd;
+    _wcontext.PresParams.hDeviceWindow = _hWnd;
+    _wcontext.PresParams.BackBufferWidth = props.get_x_size();
+    _wcontext.PresParams.BackBufferHeight = props.get_y_size();
+
+    wdxdisplay9_cat.debug()<<"device width "<<_wcontext.PresParams.BackBufferWidth<<"\n";
+
+    init_resized_window();
+
+
+    if (wdxdisplay9_cat.is_debug()) {
+      wdxdisplay9_cat.debug() << "Current device is " << dxdev << "\n";
+      
+      dxgsg->create_swap_chain(&_wcontext);
+    }
+  }
+  wdxdisplay9_cat.debug() << "swapchain is " << _wcontext.pSwapChain << "\n";
+  return true;
+}
+
+bool wdxGraphicsWindow9::
+handle_mouse_motion(int x, int y) {
+  (void) WinGraphicsWindow::handle_mouse_motion(x,y);
+  if(dx_use_dx_cursor && is_fullscreen() && (_wcontext.pD3DDevice!=NULL)) {
+      _wcontext.pD3DDevice->SetCursorPosition(x,y,D3DCURSOR_IMMEDIATE_UPDATE);
+      // return true to indicate wind_proc should return 0 instead of going to DefaultWindowProc
+      return true;
+  }
+  return false;
+}
+
+#if 0
+// does NOT override _props._bCursorIsVisible
+INLINE void wdxGraphicsWindow::
+set_cursor_visibility(bool bVisible) {
+  if(_props._bCursorIsVisible) {
+      if(dx_use_dx_cursor) {
+          ShowCursor(false);
+          if(IS_VALID_PTR(_wcontext.pD3DDevice))
+              _dxgsg->scrn.pD3DDevice->ShowCursor(bVisible);
+      } else {
+         ShowCursor(bVisible);
+      }
+  } else {
+      ShowCursor(false);
+      if(dx_use_dx_cursor && IS_VALID_PTR(_wcontext.pD3DDevice))
+          _dxgsg->scrn.pD3DDevice->ShowCursor(false);
+  }
+}
+#endif

+ 111 - 0
panda/src/dxgsg9/wdxGraphicsWindow9.h

@@ -0,0 +1,111 @@
+// Filename: wdxGraphicsWindow8.h
+// Created by:   masad (02Jan04)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef wdxGraphicsWindow9_H
+#define wdxGraphicsWindow9_H
+
+#include "pandabase.h"
+#include "winGraphicsWindow.h"
+#include "dxGraphicsStateGuardian9.h"
+#include "dxInput9.h"
+#include "wdxGraphicsPipe9.h"
+
+class wdxGraphicsPipe9;
+
+static const int WDXWIN_CONFIGURE = 4;
+static const int WDXWIN_EVENT = 8;
+
+//#define FIND_CARD_MEMAVAILS
+
+////////////////////////////////////////////////////////////////////
+//       Class : wdxGraphicsWindow9
+// Description : A single graphics window for rendering DirectX under
+//               Microsoft Windows.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDADX wdxGraphicsWindow9 : public WinGraphicsWindow {
+public:
+  wdxGraphicsWindow9(GraphicsPipe *pipe, GraphicsStateGuardian *gsg);
+  virtual ~wdxGraphicsWindow9();
+  virtual bool open_window(void);
+  virtual void reset_window(bool swapchain);
+
+  virtual int verify_window_sizes(int numsizes, int *dimen);
+
+  virtual bool begin_frame();
+  virtual void end_flip();
+  virtual LONG window_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
+  virtual bool handle_mouse_motion(int x, int y);
+
+protected:
+  virtual void fullscreen_restored(WindowProperties &properties);
+  virtual void handle_reshape();
+  virtual bool do_fullscreen_resize(int x_size, int y_size);
+
+private:
+  //  bool set_to_temp_rendertarget();
+  void create_screen_buffers_and_device(DXScreenData &Display,
+                                        bool force_16bpp_zbuffer);
+
+  bool choose_device(void);
+  bool search_for_device(wdxGraphicsPipe9 *dxpipe, DXDeviceInfo *device_info);
+
+  //  void set_coop_levels_and_display_modes();
+/*
+  void search_for_valid_displaymode(UINT RequestedX_Size, UINT RequestedY_Size,
+                                    bool bWantZBuffer, bool bWantStencil,
+                                    UINT *pSupportedScreenDepthsMask,
+                                    bool *pCouldntFindAnyValidZBuf,
+                                    D3DFORMAT *pSuggestedPixFmt,
+                                    bool bVerboseMode = false);
+*/
+  bool reset_device_resize_window(UINT new_xsize, UINT new_ysize);
+  void init_resized_window();
+  static int D3DFMT_to_DepthBits(D3DFORMAT fmt);
+  static bool is_badvidmem_card(D3DADAPTER_IDENTIFIER9 *pDevID);
+
+  DXGraphicsStateGuardian9 *_dxgsg;
+  DXScreenData _wcontext;
+
+  int _depth_buffer_bpp;
+  bool _awaiting_restore;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    WinGraphicsWindow::init_type();
+    register_type(_type_handle, "wdxGraphicsWindow9",
+                  WinGraphicsWindow::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+  virtual void make_current(void);
+
+private:
+  static TypeHandle _type_handle;
+  friend class wdxGraphicsPipe9;
+};
+
+//extern bool is_badvidmem_card(D3DADAPTER_IDENTIFIER9 *pDevID);
+
+#include "wdxGraphicsWindow9.I"
+
+#endif