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::
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 GraphicsStateGuardian for rendering into DirectX9 contexts.
This is our own Panda specialization on the default STL map.
Definition: pmap.h:49
A GraphicsDevice necessary for multi-window rendering in DX.
int get_next_higher_bit(uint16_t x)
Returns the smallest power of 2 greater than x.
Definition: pbitops.I:244
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual std::string get_interface_name() const
Returns the name of the rendering interface associated with this GraphicsPipe.
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 container for the various kinds of properties we might ask to have on a graphics window before we o...
An offscreen render buffer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool special_check_fullscreen_resolution(DXScreenData &scrn, UINT x_size, UINT y_size)
overrides of the general estimator for known working cases
is_valid
Returns true if the GSG has been correctly initialized within a graphics context, false if there has ...
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool get_supports_render_texture() const
Returns true if this particular GSG can render from a wdxGraphicsBuffer9 directly into a texture,...
This is a base class for the various different classes that represent the result of a frame of render...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A single graphics window for rendering DirectX under Microsoft Windows.
An abstract device object that is part of Graphics Pipe.
Encapsulates all the communication with a particular instance of a given rendering backend.
PT(GraphicsPipe) wdxGraphicsPipe9
This function is passed to the GraphicsPipeSelection object to allow the user to make a default wdxGr...
bool needs_reset() const
Returns true if the gsg is marked as needing a reset.
This graphics pipe represents the interface for creating DirectX9 graphics windows.
This class is the main interface to controlling the render process.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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 frameBuffer before...