Panda3D
wdxGraphicsPipe9.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file wdxGraphicsPipe9.cxx
10  * @author drose
11  * @date 2002-12-20
12  */
13 
14 #include "wdxGraphicsPipe9.h"
15 #include "dxGraphicsDevice9.h"
16 #include "wdxGraphicsWindow9.h"
17 #include "wdxGraphicsBuffer9.h"
18 #include "config_dxgsg9.h"
19 
20 using std::endl;
21 
22 TypeHandle wdxGraphicsPipe9::_type_handle;
23 
24 static bool MyGetProcAddr(HINSTANCE hDLL, FARPROC *pFn, const char *szExportedFnName) {
25  *pFn = (FARPROC) GetProcAddress(hDLL, szExportedFnName);
26  if (*pFn == nullptr) {
27  wdxdisplay9_cat.error()
28  << "GetProcAddr failed for " << szExportedFnName << ", error=" << GetLastError() <<endl;
29  return false;
30  }
31  return true;
32 }
33 
34 #define LOWVIDMEMTHRESHOLD 5700000 // 4MB cards should fall below this
35 #define CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD 1000000 // if # is > 1MB, card is lying and I cant tell what it is
36 #define UNKNOWN_VIDMEM_SIZE 0xFFFFFFFF
37 
38 /**
39  *
40  */
41 wdxGraphicsPipe9::
42 wdxGraphicsPipe9() {
43  _hDDrawDLL = nullptr;
44  _hD3D9_DLL = nullptr;
45  __d3d9 = nullptr;
46  _is_valid = init();
47 }
48 
49 /**
50  *
51  */
52 wdxGraphicsPipe9::
53 ~wdxGraphicsPipe9() {
54  RELEASE(__d3d9, wdxdisplay9, "ID3D9", RELEASE_DOWN_TO_ZERO);
55  SAFE_FREELIB(_hD3D9_DLL);
56  SAFE_FREELIB(_hDDrawDLL);
57 }
58 
59 /**
60  * Returns the name of the rendering interface associated with this
61  * GraphicsPipe. This is used to present to the user to allow him/her to
62  * choose between several possible GraphicsPipes available on a particular
63  * platform, so the name should be meaningful and unique for a given platform.
64  */
65 std::string wdxGraphicsPipe9::
66 get_interface_name() const {
67  return "DirectX9";
68 }
69 
70 /**
71  * This function is passed to the GraphicsPipeSelection object to allow the
72  * user to make a default wdxGraphicsPipe9.
73  */
74 PT(GraphicsPipe) wdxGraphicsPipe9::
75 pipe_constructor() {
76  return new wdxGraphicsPipe9;
77 }
78 
79 /**
80  * Creates a new window on the pipe, if possible.
81  */
82 PT(GraphicsOutput) wdxGraphicsPipe9::
83 make_output(const std::string &name,
84  const FrameBufferProperties &fb_prop,
85  const WindowProperties &win_prop,
86  int flags,
87  GraphicsEngine *engine,
89  GraphicsOutput *host,
90  int retry,
91  bool &precertify) {
92 
93  if (!_is_valid) {
94  return nullptr;
95  }
96 
97  DXGraphicsStateGuardian9 *wdxgsg = 0;
98  if (gsg != 0) {
99  DCAST_INTO_R(wdxgsg, gsg, nullptr);
100  }
101 
102  // First thing to try: a visible window.
103 
104  if (retry == 0) {
105  if (((flags&BF_require_parasite)!=0)||
106  ((flags&BF_refuse_window)!=0)||
107  ((flags&BF_resizeable)!=0)||
108  ((flags&BF_size_track_host)!=0)||
109  ((flags&BF_rtt_cumulative)!=0)||
110  ((flags&BF_can_bind_color)!=0)||
111  ((flags&BF_can_bind_every)!=0)) {
112  return nullptr;
113  }
114  // Early failure - if we are sure that this buffer WONT meet specs, we can
115  // bail out early.
116  if ((flags & BF_fb_props_optional) == 0) {
117  if ((fb_prop.get_aux_rgba() > 0)||
118  (fb_prop.get_aux_rgba() > 0)||
119  (fb_prop.get_aux_float() > 0)) {
120  return nullptr;
121  }
122  }
123  return new wdxGraphicsWindow9(engine, this, name, fb_prop, win_prop,
124  flags, gsg, host);
125  }
126 
127  // Second thing to try: a wdxGraphicsBuffer9
128 
129  if (retry == 1) {
130  if ((!support_render_texture)||
131  ((flags&BF_require_parasite)!=0)||
132  ((flags&BF_require_window)!=0)||
133  ((flags&BF_rtt_cumulative)!=0)||
134  ((flags&BF_can_bind_every)!=0)) {
135  return nullptr;
136  }
137  // Early failure - if we are sure that this buffer WONT meet specs, we can
138  // bail out early.
139  if ((flags & BF_fb_props_optional) == 0) {
140  if (fb_prop.get_indexed_color() ||
141  (fb_prop.get_back_buffers() > 0)||
142  (fb_prop.get_accum_bits() > 0)||
143  (fb_prop.get_multisamples() > 0)) {
144  return nullptr;
145  }
146  }
147 
148  // Early success - if we are sure that this buffer WILL meet specs, we can
149  // precertify it. This looks rather overly optimistic -- ie, buggy.
150  if ((wdxgsg != nullptr) && wdxgsg->is_valid() && !wdxgsg->needs_reset() &&
151  wdxgsg->get_supports_render_texture()) {
152  precertify = true;
153  }
154  return new wdxGraphicsBuffer9(engine, this, name, fb_prop, win_prop,
155  flags, gsg, host);
156  }
157 
158  // Nothing else left to try.
159  return nullptr;
160 }
161 
162 /**
163  * Performs some initialization steps to load up function pointers from the
164  * relevant DLL's, and determine the number and type of available graphics
165  * adapters, etc. Returns true on success, false on failure.
166  */
167 bool wdxGraphicsPipe9::
168 init() {
169  _hDDrawDLL = LoadLibrary("ddraw.dll");
170  if (_hDDrawDLL == nullptr) {
171  wdxdisplay9_cat.error()
172  << "LoadLibrary failed for ddraw.dll, error=" << GetLastError() <<endl;
173  goto error;
174  }
175 
176  if (!MyGetProcAddr(_hDDrawDLL, (FARPROC*)&_DirectDrawCreateEx, "DirectDrawCreateEx")) {
177  goto error;
178  }
179 
180  if (!MyGetProcAddr(_hDDrawDLL, (FARPROC*)&_DirectDrawEnumerateExA, "DirectDrawEnumerateExA")) {
181  goto error;
182  }
183 
184  _hD3D9_DLL = LoadLibrary("d3d9.dll");
185  if (_hD3D9_DLL == nullptr) {
186  wdxdisplay9_cat.error()
187  << "LoadLibrary failed for d3d9.dll, error=" << GetLastError() <<endl;
188  goto error;
189  }
190 
191  if (!MyGetProcAddr(_hD3D9_DLL, (FARPROC*)&_Direct3DCreate9, "Direct3DCreate9")) {
192  goto error;
193  }
194 
195  // Create a Direct3D object.
196  __d3d9 = (*_Direct3DCreate9)(D3D_SDK_VERSION);
197 
198  if (__d3d9 == nullptr) {
199  wdxdisplay9_cat.error() << "Direct3DCreate9 failed!, error = " << GetLastError() << endl;
200  // release_gsg();
201  goto error;
202  }
203 
204  Init_D3DFORMAT_map();
205 
206  if (dx_count_all_cards_memory){
207  return find_all_card_memavails();
208  }
209 
210  return true;
211 
212  error:
213  return false;
214 }
215 
216 /**
217  * Uses DX7 calls to determine how much video memory is available for each
218  * video adapter in the system. Returns true on success, false on failure.
219  */
220 bool wdxGraphicsPipe9::
221 find_all_card_memavails() {
222  HRESULT hr;
223 
224  hr = (*_DirectDrawEnumerateExA)(dx7_driver_enum_callback, this,
225  DDENUM_ATTACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES);
226  if (FAILED(hr)) {
227  wdxdisplay9_cat.fatal()
228  << "DirectDrawEnumerateEx failed" << D3DERRORSTRING(hr);
229  return false;
230  }
231 
232  if (_card_ids.empty()) {
233  wdxdisplay9_cat.error()
234  << "DirectDrawEnumerateEx enum'ed no devices!\n";
235  return false;
236  }
237 
238  GUID ZeroGUID;
239  ZeroMemory(&ZeroGUID, sizeof(GUID));
240 
241  if (_card_ids.size() > 1) {
242  assert(IsEqualGUID(ZeroGUID, _card_ids[0].DX7_DeviceGUID));
243  // delete enum of primary display (always the first), since it is
244  // duplicated by explicit entry
245  _card_ids.erase(_card_ids.begin());
246  }
247 
248  for (UINT i = 0; i < _card_ids.size(); i++) {
249  LPDIRECTDRAW7 pDD;
250  BYTE ddd_space[sizeof(DDDEVICEIDENTIFIER2)+4]; //bug in DX7 requires 4 extra bytes for GetDeviceID
251  DDDEVICEIDENTIFIER2 *pDX7DeviceID = (DDDEVICEIDENTIFIER2 *)&ddd_space[0];
252  GUID *pGUID = &(_card_ids[i].DX7_DeviceGUID);
253 
254  if (IsEqualGUID(*pGUID, ZeroGUID)) {
255  pGUID = nullptr;
256  }
257 
258  // Create the Direct Draw Object
259  hr = (*_DirectDrawCreateEx)(pGUID, (void **)&pDD, IID_IDirectDraw7, nullptr);
260  if (FAILED(hr)) {
261  wdxdisplay9_cat.error()
262  << "DirectDrawCreateEx failed for device (" << i
263  << ")" << D3DERRORSTRING(hr);
264  continue;
265  }
266 
267  ZeroMemory(ddd_space, sizeof(DDDEVICEIDENTIFIER2));
268 
269  hr = pDD->GetDeviceIdentifier(pDX7DeviceID, 0x0);
270  if (FAILED(hr)) {
271  wdxdisplay9_cat.error()
272  << "GetDeviceID failed for device (" << i << ")" << D3DERRORSTRING(hr);
273  continue;
274  }
275 
276  _card_ids[i].DeviceID = pDX7DeviceID->dwDeviceId;
277  _card_ids[i].VendorID = pDX7DeviceID->dwVendorId;
278 
279  // Get Current VidMem avail. Note this is only an estimate, when we
280  // switch to fullscreen mode from desktop, more vidmem will be available
281  // (typically 1.2 meg). I don't want to switch to fullscreen more than
282  // once due to the annoying monitor flicker, so try to figure out optimal
283  // mode using this estimate
284  DDSCAPS2 ddsGAVMCaps;
285  DWORD dwVidMemTotal, dwVidMemFree;
286  dwVidMemTotal = dwVidMemFree = 0;
287  {
288  // print out total INCLUDING AGP just for information purposes and
289  // future use. The real value I'm interested in for purposes of
290  // measuring possible valid screen sizes shouldnt include AGP.
291  ZeroMemory(&ddsGAVMCaps, sizeof(DDSCAPS2));
292  ddsGAVMCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
293 
294  hr = pDD->GetAvailableVidMem(&ddsGAVMCaps, &dwVidMemTotal, &dwVidMemFree);
295  if (FAILED(hr)) {
296  wdxdisplay9_cat.error()
297  << "GetAvailableVidMem failed for device #" << i
298  << D3DERRORSTRING(hr);
299  // goto skip_device; exit(1); probably want to exit, since it may be
300  // my fault
301  }
302  }
303 
304  wdxdisplay9_cat.info()
305  << "DX 9.0c GetAvailableVidMem (including AGP) returns Total: "
306  << dwVidMemTotal <<", Free: " << dwVidMemFree
307  << " for device #" << i << endl;
308 
309  ZeroMemory(&ddsGAVMCaps, sizeof(DDSCAPS2));
310 
311  // just want to measure localvidmem, not AGP texmem
312  ddsGAVMCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;
313 
314  hr = pDD->GetAvailableVidMem(&ddsGAVMCaps, &dwVidMemTotal, &dwVidMemFree);
315  if (FAILED(hr)) {
316  wdxdisplay9_cat.error() << "GetAvailableVidMem failed for device #" << i<< D3DERRORSTRING(hr);
317  // sometimes GetAvailableVidMem fails with hr = DDERR_NODIRECTDRAWHW for
318  // some unknown reason (bad drivers?) see bugs: 15327, 18122, others.
319  // is it because D3D9 object has already been created?
320  if (hr == DDERR_NODIRECTDRAWHW)
321  continue;
322  exit(1); // probably want to exit, since it may be my fault
323  }
324 
325  wdxdisplay9_cat.info()
326  << "GetAvailableVidMem (no AGP) returns Total: " << dwVidMemTotal
327  << ", Free: " << dwVidMemFree << " for device #" << i<< endl;
328 
329  pDD->Release(); // release DD obj, since this is all we needed it for
330 
331  if (!dx_do_vidmemsize_check) {
332  // still calling the DD stuff to get deviceID, etc. is this necessary?
333  _card_ids[i]._max_available_video_memory = UNKNOWN_VIDMEM_SIZE;
334  _card_ids[i]._is_low_memory_card = false;
335  continue;
336  }
337 
338  if (dwVidMemTotal == 0) { // unreliable driver
339  dwVidMemTotal = UNKNOWN_VIDMEM_SIZE;
340  } else {
341  if (!ISPOW2(dwVidMemTotal)) {
342  // assume they wont return a proper max value, so round up to next pow
343  // of 2
344  int count = get_next_higher_bit((uint32_t)(dwVidMemTotal - 1u));
345  if (count >= 32u) {
346  // Maximum value that fits in a UINT.
347  dwVidMemTotal = 0xffffffffu;
348  } else {
349  dwVidMemTotal = (1u << count);
350  }
351  }
352  }
353 
354  // after Set_display_mode, GetAvailVidMem totalmem seems to go down by 1.2
355  // meg (contradicting above comment and what I think would be correct
356  // behavior (shouldnt FS mode release the desktop vidmem?), so this is the
357  // true value
358  _card_ids[i]._max_available_video_memory = dwVidMemTotal;
359 
360  // I can never get this stuff to work reliably, so I'm just rounding up to
361  // nearest pow2. Could try to get HardwareInformation.Memory_size MB
362  // number from registry like video control panel, but its not clear how to
363  // find the proper registry location for a given card
364 
365  // assume buggy drivers (this means you, FireGL2) may return zero (or
366  // small amts) for dwVidMemTotal, so ignore value if its <
367  // CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD
368  bool bLowVidMemFlag =
369  ((dwVidMemTotal > CRAPPY_DRIVER_IS_LYING_VIDMEMTHRESHOLD) &&
370  (dwVidMemTotal< LOWVIDMEMTHRESHOLD));
371 
372  _card_ids[i]._is_low_memory_card = bLowVidMemFlag;
373  wdxdisplay9_cat.info()
374  << "SetLowVidMem flag to " << bLowVidMemFlag
375  << " based on adjusted VidMemTotal: " << dwVidMemTotal << endl;
376  }
377  return true;
378 }
379 
380 /**
381  *
382  */
383 BOOL WINAPI wdxGraphicsPipe9::
384 dx7_driver_enum_callback(GUID *pGUID, TCHAR *strDesc, TCHAR *strName,
385  VOID *argptr, HMONITOR hm) {
386  wdxGraphicsPipe9 *self = (wdxGraphicsPipe9 *)argptr;
387 
388  CardID card_id;
389  ZeroMemory(&card_id, sizeof(CardID));
390 
391  if (hm == nullptr) {
392  card_id._monitor = MonitorFromWindow(GetDesktopWindow(),
393  MONITOR_DEFAULTTOPRIMARY);
394  } else {
395  card_id._monitor = hm;
396  }
397 
398  if (pGUID != nullptr) {
399  memcpy(&card_id.DX7_DeviceGUID, pGUID, sizeof(GUID));
400  }
401 
402  card_id._max_available_video_memory = UNKNOWN_VIDMEM_SIZE;
403 
404  self->_card_ids.push_back(card_id);
405 
406  return DDENUMRET_OK;
407 }
408 
409 /**
410  *
411  */
412 bool wdxGraphicsPipe9::
413 find_best_depth_format(DXScreenData &Display, D3DDISPLAYMODE &Test_display_mode,
414  D3DFORMAT *pBestFmt, bool bWantStencil,
415  bool bForce16bpp, bool bVerboseMode) const {
416  if (dxgsg9_cat.is_debug()) {
417  bVerboseMode = true;
418  }
419 
420  // list formats to try in order of preference.
421 
422 #define NUM_TEST_ZFMTS 6
423 #define FIRST_NON_STENCIL_ZFMT 3
424  static D3DFORMAT FormatPrefList[NUM_TEST_ZFMTS] = {
425  D3DFMT_D24S8, D3DFMT_D24X4S4, D3DFMT_D15S1, // with stencil
426  D3DFMT_D32, D3DFMT_D24X8, D3DFMT_D16 // without stencil
427  };
428 
429  // do not use Display._display_mode since that is probably not set yet, use
430  // Test_display_mode instead
431 
432  *pBestFmt = D3DFMT_UNKNOWN;
433  HRESULT hr;
434 
435  // nvidia likes zbuf depth to match rendertarget depth
436  bool bOnlySelect16bpp = (bForce16bpp ||
437  (IS_NVIDIA(Display._dx_device_id) && IS_16BPP_DISPLAY_FORMAT(Test_display_mode.Format)));
438 
439  if (bVerboseMode) {
440  wdxdisplay9_cat.info()
441  << "FindBestDepthFmt: bSelectOnly16bpp: " << bOnlySelect16bpp << endl;
442  }
443 
444  int first_format = (bWantStencil ? 0 : FIRST_NON_STENCIL_ZFMT);
445  for (int i = first_format; i < NUM_TEST_ZFMTS; i++) {
446  D3DFORMAT TestDepthFmt = FormatPrefList[i];
447 
448  if (bOnlySelect16bpp && !IS_16BPP_ZBUFFER(TestDepthFmt)) {
449  continue;
450  }
451 
452  hr = Display._d3d9->CheckDeviceFormat(Display._card_id,
453  D3DDEVTYPE_HAL,
454  Test_display_mode.Format,
455  D3DUSAGE_DEPTHSTENCIL,
456  D3DRTYPE_SURFACE, TestDepthFmt);
457 
458  if (FAILED(hr)) {
459  if (hr == D3DERR_NOTAVAILABLE) {
460  if (bVerboseMode)
461  wdxdisplay9_cat.info()
462  << "FindBestDepthFmt: ChkDevFmt returns NotAvail for "
463  << D3DFormatStr(TestDepthFmt) << endl;
464  continue;
465  }
466 
467  wdxdisplay9_cat.error()
468  << "unexpected CheckDeviceFormat failure" << D3DERRORSTRING(hr)
469  << endl;
470  exit(1);
471  }
472 
473  hr = Display._d3d9->CheckDepthStencilMatch(Display._card_id,
474  D3DDEVTYPE_HAL,
475  Test_display_mode.Format, // adapter format
476  Test_display_mode.Format, // backbuffer fmt (should be the same in my apps)
477  TestDepthFmt);
478  if (SUCCEEDED(hr)) {
479  *pBestFmt = TestDepthFmt;
480  break;
481  } else {
482  if (hr == D3DERR_NOTAVAILABLE) {
483  if (bVerboseMode) {
484  wdxdisplay9_cat.info()
485  << "FindBestDepthFmt: ChkDepMatch returns NotAvail for "
486  << D3DFormatStr(Test_display_mode.Format) << ", "
487  << D3DFormatStr(TestDepthFmt) << endl;
488  }
489  } else {
490  wdxdisplay9_cat.error()
491  << "unexpected CheckDepthStencilMatch failure for "
492  << D3DFormatStr(Test_display_mode.Format) << ", "
493  << D3DFormatStr(TestDepthFmt) << endl;
494  exit(1);
495  }
496  }
497  }
498 
499  if (bVerboseMode) {
500  wdxdisplay9_cat.info()
501  << "FindBestDepthFmt returns fmt " << D3DFormatStr(*pBestFmt) << endl;
502  }
503 
504  return (*pBestFmt != D3DFMT_UNKNOWN);
505 }
506 
507 
508 /**
509  * overrides of the general estimator for known working cases
510  */
512 special_check_fullscreen_resolution(DXScreenData &scrn, UINT x_size, UINT y_size) {
513  DWORD VendorId = scrn._dx_device_id.VendorId;
514  DWORD DeviceId = scrn._dx_device_id.DeviceId;
515 
516  switch (VendorId) {
517  case 0x8086: // Intel
518  if ((x_size == 640) && (y_size == 480)) {
519  return true;
520  }
521  if ((x_size == 800) && (y_size == 600)) {
522  return true;
523  }
524  if ((x_size == 1024) && (y_size == 768)) {
525  return true;
526  }
527  break;
528  }
529 
530  return false;
531 }
532 
533 /**
534  * All ptr args are output parameters. If no valid mode found, returns
535  * *pSuggestedPixFmt = D3DFMT_UNKNOWN;
536  */
539  UINT RequestedX_Size, UINT RequestedY_Size,
540  bool bWantZBuffer, bool bWantStencil,
541  UINT *p_supported_screen_depths_mask,
542  bool *pCouldntFindAnyValidZBuf,
543  D3DFORMAT *pSuggestedPixFmt,
544  bool bForce16bppZBuffer,
545  bool bVerboseMode) {
546  if (dxgsg9_cat.is_debug()) {
547  bVerboseMode = true;
548  }
549 
550  assert(IS_VALID_PTR(scrn._d3d9));
551 
552  HRESULT hr;
553 
554  *pSuggestedPixFmt = D3DFMT_UNKNOWN;
555  *p_supported_screen_depths_mask = 0x0;
556  *pCouldntFindAnyValidZBuf = false;
557 
558  #define TOTAL_D3D_FORMATS 5
559  static D3DFORMAT d3d_format_array [TOTAL_D3D_FORMATS] =
560  {
561  D3DFMT_X8R8G8B8,
562  D3DFMT_A8R8G8B8,
563  D3DFMT_R5G6B5,
564  D3DFMT_X1R5G5B5,
565  D3DFMT_A2R10G10B10, // we may want this first in the list for XBOX 360 or HDR
566  };
567 
568  // search for an adapter with a valid D3DFORMAT
569  int format_index;
570  for (format_index = 0; format_index < TOTAL_D3D_FORMATS; format_index++) {
571  D3DFORMAT d3d_format;
572 
573  d3d_format = d3d_format_array [format_index];
574 
575  int cNumModes = scrn._d3d9->GetAdapterModeCount(scrn._card_id, d3d_format);
576 
577  D3DDISPLAYMODE BestDispMode;
578  ZeroMemory(&BestDispMode, sizeof(BestDispMode));
579 
580  if (bVerboseMode) {
581  wdxdisplay9_cat.info()
582  << "searching for valid display modes at res: ("
583  << RequestedX_Size << ", " << RequestedY_Size
584  << "), TotalModes: " << cNumModes << endl;
585  }
586 
587  // ignore memory based checks for min res 640x480. some cards just don't
588  // give accurate memavails. (should I do the check anyway for 640x480
589  // 32bpp?)
590  bool bDoMemBasedChecks =
591  ((!((RequestedX_Size == 640)&&(RequestedY_Size == 480))) &&
592  (scrn._max_available_video_memory != UNKNOWN_VIDMEM_SIZE) &&
593  (!special_check_fullscreen_resolution(scrn, RequestedX_Size, RequestedY_Size)));
594 
595  if (bVerboseMode) {
596  wdxdisplay9_cat.info()
597  << "DoMemBasedChecks = " << bDoMemBasedChecks << endl;
598  }
599 
600  for (int i = 0; i < cNumModes; i++) {
601  D3DDISPLAYMODE dispmode;
602  hr = scrn._d3d9->EnumAdapterModes(scrn._card_id, d3d_format, i, &dispmode);
603  if (FAILED(hr)) {
604  wdxdisplay9_cat.error()
605  << "EnumAdapter_display_mode failed for device #"
606  << scrn._card_id << D3DERRORSTRING(hr);
607  continue;
608  }
609 
610  if ((dispmode.Width != RequestedX_Size) ||
611  (dispmode.Height != RequestedY_Size)) {
612  if (bVerboseMode) {
613  wdxdisplay9_cat.info()
614  << "Mode dimension " << dispmode.Width << "x" << dispmode.Height
615  << "; format " << D3DFormatStr(dispmode.Format)
616  << ": onto next mode\n";
617  }
618  continue;
619  }
620 
621  // disable refresh rate checking since SLI video cards may use refresh
622  // rates less than 60
623  if (0) {
624  if ((dispmode.RefreshRate<60) && (dispmode.RefreshRate>1)) {
625  // don't want refresh rates under 60Hz, but 0 or 1 might indicate a
626  // default refresh rate, which is usually > = 60
627  if (bVerboseMode) {
628  wdxdisplay9_cat.info()
629  << "skipping mode[" << i << "], bad refresh rate: "
630  << dispmode.RefreshRate << endl;
631  }
632  continue;
633  }
634  }
635 
636  // Note no attempt is made to verify if format will work at requested
637  // size, so even if this call succeeds, could still get an out-of-video-
638  // mem error
639 
640  hr = scrn._d3d9->CheckDeviceFormat(scrn._card_id, D3DDEVTYPE_HAL, dispmode.Format,
641  D3DUSAGE_RENDERTARGET, D3DRTYPE_SURFACE,
642  dispmode.Format);
643  if (FAILED(hr)) {
644  if (hr == D3DERR_NOTAVAILABLE) {
645  if (bVerboseMode) {
646  wdxdisplay9_cat.info()
647  << "skipping mode[" << i
648  << "], CheckDevFmt returns NotAvail for fmt: "
649  << D3DFormatStr(dispmode.Format) << endl;
650  }
651  continue;
652  } else {
653  wdxdisplay9_cat.error()
654  << "CheckDeviceFormat failed for device #"
655  << scrn._card_id << D3DERRORSTRING(hr);
656  continue;
657  }
658  }
659 
660  bool bIs16bppRenderTgt = IS_16BPP_DISPLAY_FORMAT(dispmode.Format);
661  PN_stdfloat RendTgtMinMemReqmt = 0.0f;
662 
663  // if we have a valid memavail value, try to determine if we have enough
664  // space
665  if (bDoMemBasedChecks) {
666  // assume user is testing fullscreen, not windowed, so use the dwTotal
667  // value see if 3 scrnbufs (frontbackz)at 16bpp at x_size*y_size will
668  // fit with a few extra megs for texmem
669 
670  // 8MB Rage Pro says it has 6.8 megs Total free and will run at
671  // 1024x768, so formula makes it so that is OK
672 
673  #define REQD_TEXMEM 1800000
674 
675  PN_stdfloat bytes_per_pixel = (bIs16bppRenderTgt ? 2 : 4);
676 
677  // *2 for double buffer
678 
679  RendTgtMinMemReqmt =
680  ((PN_stdfloat)RequestedX_Size) * ((PN_stdfloat)RequestedY_Size) *
681  bytes_per_pixel * 2 + REQD_TEXMEM;
682 
683  if (bVerboseMode)
684  wdxdisplay9_cat.info()
685  << "Testing Mode (" <<RequestedX_Size<<"x" << RequestedY_Size
686  << ", " << D3DFormatStr(dispmode.Format) << ")\nReqdVidMem: "
687  << (int)RendTgtMinMemReqmt << " AvailVidMem: "
688  << scrn._max_available_video_memory << endl;
689 
690  if (RendTgtMinMemReqmt > scrn._max_available_video_memory) {
691  if (bVerboseMode)
692  wdxdisplay9_cat.info()
693  << "not enough VidMem for render tgt, skipping display fmt "
694  << D3DFormatStr(dispmode.Format) << " ("
695  << (int)RendTgtMinMemReqmt << " > "
696  << scrn._max_available_video_memory << ")\n";
697  continue;
698  }
699  }
700 
701  if (bWantZBuffer) {
702  D3DFORMAT zformat;
703  if (!find_best_depth_format(scrn, dispmode, &zformat,
704  bWantStencil, bForce16bppZBuffer)) {
705  *pCouldntFindAnyValidZBuf = true;
706  continue;
707  }
708 
709  PN_stdfloat MinMemReqmt = 0.0f;
710 
711  if (bDoMemBasedChecks) {
712  // test memory again, this time including zbuf size
713  PN_stdfloat zbytes_per_pixel = (IS_16BPP_ZBUFFER(zformat) ? 2 : 4);
714  PN_stdfloat MinMemReqmt = RendTgtMinMemReqmt + ((PN_stdfloat)RequestedX_Size)*((PN_stdfloat)RequestedY_Size)*zbytes_per_pixel;
715 
716  if (bVerboseMode)
717  wdxdisplay9_cat.info()
718  << "Testing Mode w/Z (" << RequestedX_Size << "x"
719  << RequestedY_Size << ", " << D3DFormatStr(dispmode.Format)
720  << ")\nReqdVidMem: " << (int)MinMemReqmt << " AvailVidMem: "
721  << scrn._max_available_video_memory << endl;
722 
723  if (MinMemReqmt > scrn._max_available_video_memory) {
724  if (bVerboseMode)
725  wdxdisplay9_cat.info()
726  << "not enough VidMem for RendTgt+zbuf, skipping display fmt "
727  << D3DFormatStr(dispmode.Format) << " (" << (int)MinMemReqmt
728  << " > " << scrn._max_available_video_memory << ")\n";
729  continue;
730  }
731  }
732 
733 // Optimizing for 16-bit depth does not work in all cases so turn it off.
734  if (false) {
735  if ((!bDoMemBasedChecks) || (MinMemReqmt<scrn._max_available_video_memory)) {
736  if (!IS_16BPP_ZBUFFER(zformat)) {
737  // see if things fit with a 16bpp zbuffer
738 
739  if (!find_best_depth_format(scrn, dispmode, &zformat,
740  bWantStencil, true, bVerboseMode)) {
741  if (bVerboseMode)
742  wdxdisplay9_cat.info()
743  << "FindBestDepthFmt rejected Mode[" << i << "] ("
744  << RequestedX_Size << "x" << RequestedY_Size
745  << ", " << D3DFormatStr(dispmode.Format) << endl;
746  *pCouldntFindAnyValidZBuf = true;
747  continue;
748  }
749 
750  // right now I'm not going to use these flags, just let the
751  // create fail out-of-mem and retry at 16bpp
752  *p_supported_screen_depths_mask |=
753  (IS_16BPP_DISPLAY_FORMAT(dispmode.Format) ? DISPLAY_16BPP_REQUIRES_16BPP_ZBUFFER_FLAG : DISPLAY_32BPP_REQUIRES_16BPP_ZBUFFER_FLAG);
754  }
755  }
756  }
757  }
758 
759  if (bVerboseMode)
760  wdxdisplay9_cat.info()
761  << "Validated Mode (" << RequestedX_Size << "x"
762  << RequestedY_Size << ", " << D3DFormatStr(dispmode.Format) << endl;
763 
764  /*
765  // dx9 valid display modes for render targets.
766  D3DFMT_X1R5G5B5, D3DFMT_R5G6B5, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8, D3DFMT_A2R10G10B10
767  */
768 
769  switch (dispmode.Format) {
770  case D3DFMT_X1R5G5B5:
771  *p_supported_screen_depths_mask |= X1R5G5B5_FLAG;
772  break;
773  case D3DFMT_X8R8G8B8:
774  *p_supported_screen_depths_mask |= X8R8G8B8_FLAG;
775  break;
776  case D3DFMT_A8R8G8B8:
777  *p_supported_screen_depths_mask |= A8R8G8B8_FLAG;
778  break;
779  case D3DFMT_R5G6B5:
780  *p_supported_screen_depths_mask |= R5G6B5_FLAG;
781  break;
782  case D3DFMT_A2R10G10B10:
783  *p_supported_screen_depths_mask |= A2B10G10R10_FLAG;
784  break;
785  default:
786  wdxdisplay9_cat.error()
787  << "unrecognized supported fmt " << D3DFormatStr(dispmode.Format)
788  << " returned by EnumAdapter_display_modes!\n";
789  }
790  }
791 
792  // note: this chooses 32bpp, which may not be preferred over 16 for memory
793  // & speed reasons on some older cards in particular
794  if (*p_supported_screen_depths_mask & X8R8G8B8_FLAG) {
795  *pSuggestedPixFmt = D3DFMT_X8R8G8B8;
796  } else if (*p_supported_screen_depths_mask & A8R8G8B8_FLAG) {
797  *pSuggestedPixFmt = D3DFMT_A8R8G8B8;
798  } else if (*p_supported_screen_depths_mask & R5G6B5_FLAG) {
799  *pSuggestedPixFmt = D3DFMT_R5G6B5;
800  } else if (*p_supported_screen_depths_mask & X1R5G5B5_FLAG) {
801  *pSuggestedPixFmt = D3DFMT_X1R5G5B5;
802  } else if (*p_supported_screen_depths_mask & A2B10G10R10_FLAG) {
803  *pSuggestedPixFmt = D3DFMT_A2R10G10B10;
804  }
805 
806  if (bVerboseMode) {
807  wdxdisplay9_cat.info()
808  << "search_for_valid_device returns fmt: "
809  << D3DFormatStr(*pSuggestedPixFmt) << endl;
810  }
811 
812  if (*pSuggestedPixFmt != D3DFMT_UNKNOWN) {
813  break;
814  }
815  }
816 }
817 
818 /**
819  * Creates a new reference to a particular hardware device and associates it
820  * with the pipe.
821  */
822 PT(GraphicsDevice) wdxGraphicsPipe9::
823 make_device(void *scrn) {
824  PT(DXGraphicsDevice9) device = new DXGraphicsDevice9(this);
825  memcpy(&device->_Scrn, scrn, sizeof(device->_Scrn));
826  device->_d3d_device = device->_Scrn._d3d_device;
827 
828  _device = device;
829  wdxdisplay9_cat.info() << "walla: device" << device << "\n";
830 
831  return device;
832 }
833 
834 pmap<D3DFORMAT_FLAG, D3DFORMAT> g_D3DFORMATmap;
835 
836 void Init_D3DFORMAT_map() {
837  if (g_D3DFORMATmap.size() != 0)
838  return;
839 
840 #define INSERT_ELEM(XX) g_D3DFORMATmap[XX##_FLAG] = D3DFMT_##XX;
841 
842  INSERT_ELEM(R8G8B8);
843  INSERT_ELEM(A8R8G8B8);
844  INSERT_ELEM(X8R8G8B8);
845  INSERT_ELEM(R5G6B5);
846  INSERT_ELEM(X1R5G5B5);
847  INSERT_ELEM(A1R5G5B5);
848  INSERT_ELEM(A4R4G4B4);
849  INSERT_ELEM(R3G3B2);
850  INSERT_ELEM(A8);
851  INSERT_ELEM(A8R3G3B2);
852  INSERT_ELEM(X4R4G4B4);
853  INSERT_ELEM(A2B10G10R10);
854  INSERT_ELEM(G16R16);
855  INSERT_ELEM(A8P8);
856  INSERT_ELEM(P8);
857  INSERT_ELEM(L8);
858  INSERT_ELEM(A8L8);
859  INSERT_ELEM(A4L4);
860  INSERT_ELEM(D16);
861  INSERT_ELEM(D24X8);
862  INSERT_ELEM(D24S8);
863  INSERT_ELEM(D32);
864  INSERT_ELEM(INTZ);
865 // NOT IN DX9 INSERT_ELEM(W11V11U10);
866  INSERT_ELEM(A2W10V10U10);
867  INSERT_ELEM(ATI1);
868  INSERT_ELEM(ATI2);
869  INSERT_ELEM(DXT1);
870  INSERT_ELEM(DXT2);
871  INSERT_ELEM(DXT3);
872  INSERT_ELEM(DXT4);
873  INSERT_ELEM(DXT5);
874 }
875 
876 
877 
878 const char *D3DFormatStr(D3DFORMAT fmt) {
879 
880 #define CASESTR(XX) case XX: return #XX;
881  switch(fmt) {
882  CASESTR(D3DFMT_UNKNOWN);
883  CASESTR(D3DFMT_R8G8B8);
884  CASESTR(D3DFMT_A8R8G8B8);
885  CASESTR(D3DFMT_X8R8G8B8);
886  CASESTR(D3DFMT_R5G6B5);
887  CASESTR(D3DFMT_X1R5G5B5);
888  CASESTR(D3DFMT_A1R5G5B5);
889  CASESTR(D3DFMT_A4R4G4B4);
890  CASESTR(D3DFMT_R3G3B2);
891  CASESTR(D3DFMT_A8);
892  CASESTR(D3DFMT_A8R3G3B2);
893  CASESTR(D3DFMT_X4R4G4B4);
894  CASESTR(D3DFMT_A2B10G10R10);
895  CASESTR(D3DFMT_G16R16);
896  CASESTR(D3DFMT_A8P8);
897  CASESTR(D3DFMT_P8);
898  CASESTR(D3DFMT_L8);
899  CASESTR(D3DFMT_A8L8);
900  CASESTR(D3DFMT_A4L4);
901  CASESTR(D3DFMT_V8U8);
902  CASESTR(D3DFMT_L6V5U5);
903  CASESTR(D3DFMT_X8L8V8U8);
904  CASESTR(D3DFMT_Q8W8V8U8);
905  CASESTR(D3DFMT_V16U16);
906 // NOT IN DX9 CASESTR(D3DFMT_W11V11U10);
907  CASESTR(D3DFMT_A2W10V10U10);
908  CASESTR(D3DFMT_ATI1);
909  CASESTR(D3DFMT_ATI2);
910  CASESTR(D3DFMT_DXT1);
911  CASESTR(D3DFMT_DXT2);
912  CASESTR(D3DFMT_DXT3);
913  CASESTR(D3DFMT_DXT4);
914  CASESTR(D3DFMT_DXT5);
915  CASESTR(D3DFMT_D16_LOCKABLE);
916  CASESTR(D3DFMT_D32);
917  CASESTR(D3DFMT_D15S1);
918  CASESTR(D3DFMT_D24S8);
919  CASESTR(D3DFMT_D16);
920  CASESTR(D3DFMT_D24X8);
921  CASESTR(D3DFMT_D24X4S4);
922  CASESTR(D3DFMT_INTZ);
923  CASESTR(D3DFMT_VERTEXDATA);
924  CASESTR(D3DFMT_INDEX16);
925  CASESTR(D3DFMT_INDEX32);
926  CASESTR(D3DFMT_A16B16G16R16F);
927  CASESTR(D3DFMT_A32B32G32R32F);
928  }
929 
930  return "Invalid D3DFORMAT";
931 }
A GraphicsDevice necessary for multi-window rendering in DX.
A GraphicsStateGuardian for rendering into DirectX9 contexts.
bool get_supports_render_texture() const
Returns true if this particular GSG can render from a wdxGraphicsBuffer9 directly into a texture,...
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
An abstract device object that is part of Graphics Pipe.
This class is the main interface to controlling the render process.
This is a base class for the various different classes that represent the result of a frame of render...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
Encapsulates all the communication with a particular instance of a given rendering backend.
is_valid
Returns true if the GSG has been correctly initialized within a graphics context, false if there has ...
bool needs_reset() const
Returns true if the gsg is marked as needing a reset.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
A container for the various kinds of properties we might ask to have on a graphics window before we o...
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
An offscreen render buffer.
This graphics pipe represents the interface for creating DirectX9 graphics windows.
virtual std::string get_interface_name() const
Returns the name of the rendering interface associated with this GraphicsPipe.
bool special_check_fullscreen_resolution(DXScreenData &scrn, UINT x_size, UINT y_size)
overrides of the general estimator for known working cases
void search_for_valid_displaymode(DXScreenData &scrn, UINT RequestedX_Size, UINT RequestedY_Size, bool bWantZBuffer, bool bWantStencil, UINT *p_supported_screen_depths_mask, bool *pCouldntFindAnyValidZBuf, D3DFORMAT *pSuggestedPixFmt, bool bForce16bppZBuffer, bool bVerboseMode=false)
All ptr args are output parameters.
A single graphics window for rendering DirectX under Microsoft Windows.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int get_next_higher_bit(unsigned short x)
Returns the smallest power of 2 greater than x.
Definition: pbitops.I:328
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(GraphicsPipe) wdxGraphicsPipe9
This function is passed to the GraphicsPipeSelection object to allow the user to make a default wdxGr...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.