Panda3D
dxGraphicsStateGuardian9.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 dxGraphicsStateGuardian9.cxx
10  * @author mike
11  * @date 1999-02-02
12  * @author fperazzi, PandaSE
13  * @date 2010-05-05
14  */
15 
17 #include "config_dxgsg9.h"
18 #include "displayRegion.h"
19 #include "renderBuffer.h"
20 #include "geom.h"
21 #include "graphicsWindow.h"
22 #include "graphicsEngine.h"
23 #include "lens.h"
24 #include "ambientLight.h"
25 #include "directionalLight.h"
26 #include "pointLight.h"
27 #include "spotlight.h"
28 #include "textureAttrib.h"
29 #include "texGenAttrib.h"
30 #include "shadeModelAttrib.h"
31 #include "cullFaceAttrib.h"
32 #include "transparencyAttrib.h"
33 #include "alphaTestAttrib.h"
34 #include "depthTestAttrib.h"
35 #include "depthWriteAttrib.h"
36 #include "colorWriteAttrib.h"
37 #include "texMatrixAttrib.h"
38 #include "materialAttrib.h"
39 #include "renderModeAttrib.h"
40 #include "rescaleNormalAttrib.h"
41 #include "fogAttrib.h"
42 #include "depthOffsetAttrib.h"
43 #include "lightAttrib.h"
44 #include "stencilAttrib.h"
45 #include "scissorAttrib.h"
46 #include "clipPlaneAttrib.h"
47 #include "fog.h"
48 #include "throw_event.h"
49 #include "geomVertexFormat.h"
50 #include "geomVertexData.h"
51 #include "geomTriangles.h"
52 #include "geomTristrips.h"
53 #include "geomTrifans.h"
54 #include "geomLines.h"
55 #include "geomLinestrips.h"
56 #include "geomPoints.h"
57 #include "geomVertexReader.h"
58 #include "dxGeomMunger9.h"
59 #include "config_gobj.h"
60 #include "dxVertexBufferContext9.h"
61 #include "dxIndexBufferContext9.h"
63 #include "pStatTimer.h"
64 #include "pStatCollector.h"
65 #include "wdxGraphicsBuffer9.h"
66 #include "config_pgraph.h"
67 #include "shaderGenerator.h"
68 #ifdef HAVE_CG
69 #include <Cg/cgD3D9.h>
70 #endif
71 
72 #include <mmsystem.h>
73 
74 #include <dxsdkver.h>
75 
76 #define tostring(x) #x
77 #define SDK_VERSION(major,minor) tostring(major) << "." << tostring(minor)
78 #define DIRECTX_SDK_VERSION SDK_VERSION (_DXSDK_PRODUCT_MAJOR, _DXSDK_PRODUCT_MINOR) << "." << SDK_VERSION (_DXSDK_BUILD_MAJOR, _DXSDK_BUILD_MINOR)
79 
80 using std::endl;
81 using std::max;
82 using std::min;
83 
84 TypeHandle DXGraphicsStateGuardian9::_type_handle;
85 
86 D3DMATRIX DXGraphicsStateGuardian9::_d3d_ident_mat;
87 
88 unsigned char *DXGraphicsStateGuardian9::_temp_buffer = nullptr;
89 unsigned char *DXGraphicsStateGuardian9::_safe_buffer_start = nullptr;
90 
91 #ifdef HAVE_CG
92 LPDIRECT3DDEVICE9 DXGraphicsStateGuardian9::_cg_device = nullptr;
93 #endif
94 
95 #define __D3DLIGHT_RANGE_MAX ((PN_stdfloat)sqrt(FLT_MAX)) //for some reason this is missing in dx9 hdrs
96 
97 #define MY_D3DRGBA(r, g, b, a) ((D3DCOLOR) D3DCOLOR_COLORVALUE(r, g, b, a))
98 
99 /**
100  *
101  */
102 DXGraphicsStateGuardian9::
103 DXGraphicsStateGuardian9(GraphicsEngine *engine, GraphicsPipe *pipe) :
104  GraphicsStateGuardian(CS_yup_left, engine, pipe)
105 {
106  if (dxgsg9_cat.is_debug()) {
107  dxgsg9_cat.debug()
108  << "DXGraphicsStateGuardian9 " << this << " constructing\n";
109  }
110 
111  // Assume that we will get a hardware-accelerated context, unless the window
112  // tells us otherwise.
113  _is_hardware = true;
114 
115  _screen = nullptr;
116  _d3d_device = nullptr;
117 
118  _dx_is_ready = false;
119  _vertex_blending_enabled = false;
120  _overlay_windows_supported = false;
121  _tex_stats_retrieval_impossible = false;
122  _supports_render_texture = false;
123 
124  _active_ibuffer = nullptr;
125 
126  // This is a static member, but we initialize it here in the constructor
127  // anyway. It won't hurt if it gets repeatedly initalized.
128  ZeroMemory(&_d3d_ident_mat, sizeof(D3DMATRIX));
129  _d3d_ident_mat._11 = _d3d_ident_mat._22 = _d3d_ident_mat._33 = _d3d_ident_mat._44 = 1.0f;
130 
131  _cur_read_pixel_buffer = RenderBuffer::T_front;
132 
133  // DirectX drivers seem to consistently invert the texture when they copy
134  // framebuffer-to-texture. Ok.
135  _copy_texture_inverted = true;
136 
137  _gsg_managed_textures = dx_management | dx_texture_management;
138  _gsg_managed_vertex_buffers = dx_management;
139  _gsg_managed_index_buffers = dx_management;
140 
141  _last_fvf = 0;
142  _num_bound_streams = 0;
143  _white_vbuffer = nullptr;
144 
145  _vertex_shader_version_major = 0;
146  _vertex_shader_version_minor = 0;
147  _pixel_shader_version_major = 0;
148  _pixel_shader_version_minor = 0;
149 
150  _vertex_shader_profile = 0;
151  _pixel_shader_profile = 0;
152 
153  _vertex_shader_maximum_constants = 0;
154 
155  _supports_stream_offset = false;
156 
157 #ifdef HAVE_CG
158  _cg_context = 0;
159 #endif
160 
161  get_gamma_table();
162  atexit (atexit_function);
163 }
164 
165 /**
166  *
167  */
168 DXGraphicsStateGuardian9::
169 ~DXGraphicsStateGuardian9() {
170  if (dxgsg9_cat.is_debug()) {
171  dxgsg9_cat.debug()
172  << "DXGraphicsStateGuardian9 " << this << " destructing\n";
173  }
174 
175  if (IS_VALID_PTR(_d3d_device)) {
176  _d3d_device->SetTexture(0, nullptr); // this frees reference to the old texture
177  }
178 
179  free_nondx_resources();
180 }
181 
182 /**
183  * Creates a new retained-mode representation of the given texture, and
184  * returns a newly-allocated TextureContext pointer to reference it. It is
185  * the responsibility of the calling function to later call release_texture()
186  * with this same pointer (which will also delete the pointer).
187  *
188  * This function should not be called directly to prepare a texture. Instead,
189  * call Texture::prepare().
190  */
192 prepare_texture(Texture *tex, int view) {
193  DXTextureContext9 *dtc = new DXTextureContext9(_prepared_objects, tex, view);
194 
196  dxgsg9_cat.error()
197  << *dtc->get_texture() << " is stored in an unsupported compressed format.\n";
198  return nullptr;
199  }
200 
201  return dtc;
202 }
203 
204 /**
205  * Makes the texture the currently available texture for rendering on the ith
206  * stage.
207  */
209 apply_texture(int i, TextureContext *tc, const SamplerState &sampler) {
210  if (tc == nullptr) {
211  // The texture wasn't bound properly or something, so ensure texturing is
212  // disabled and just return.
213  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
214  return;
215  }
216  if (!update_texture(tc, false)) {
217  // Couldn't get the texture image or something.
218  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
219  return;
220  }
221 
222  tc->set_active(true);
223 
224  DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
225  Texture *tex = tc->get_texture();
226 
227  // if (tex->get_color_space() == CS_srgb) {
228  if (Texture::is_srgb(tex->get_format())) {
229  set_sampler_state(i, D3DSAMP_SRGBTEXTURE, TRUE);
230  } else {
231  set_sampler_state(i, D3DSAMP_SRGBTEXTURE, FALSE);
232  }
233 
234  set_sampler_state(i, D3DSAMP_ADDRESSU,
235  get_texture_wrap_mode(sampler.get_wrap_u()));
236 
237  set_sampler_state(i, D3DSAMP_ADDRESSV,
238  get_texture_wrap_mode(sampler.get_wrap_v()));
239 
240  set_sampler_state(i, D3DSAMP_ADDRESSW,
241  get_texture_wrap_mode(sampler.get_wrap_w()));
242 
243  DWORD border_color;
244  border_color = LColor_to_D3DCOLOR(sampler.get_border_color());
245 
246  set_sampler_state(i, D3DSAMP_BORDERCOLOR, border_color);
247 
248  uint aniso_degree = sampler.get_effective_anisotropic_degree();
249  SamplerState::FilterType ft = sampler.get_effective_magfilter();
250 
251  if (aniso_degree >= 1) {
252  set_sampler_state(i, D3DSAMP_MAXANISOTROPY, aniso_degree);
253  }
254 
255  int supports_anisotropic_mag_filter;
256  D3DTEXTUREFILTERTYPE new_mag_filter;
257 
258  supports_anisotropic_mag_filter = (_screen -> _d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC) != 0;
259  if (aniso_degree <= 1 || supports_anisotropic_mag_filter == 0) {
260  new_mag_filter = ((ft != SamplerState::FT_nearest) ? D3DTEXF_LINEAR : D3DTEXF_POINT);
261  } else {
262  new_mag_filter = D3DTEXF_ANISOTROPIC;
263  }
264 
265  HRESULT hr;
266  hr = set_sampler_state(i, D3DSAMP_MAGFILTER, new_mag_filter);
267  if (hr != D3D_OK) {
268  dxgsg9_cat.error()
269  << "ERROR: set_sampler_state (D3DSAMP_MAGFILTER, "
270  << new_mag_filter << ") failed for sampler: " << sampler << endl;
271  }
272 
273  // map Panda composite min+mip filter types to d3d's separate min & mip
274  // filter types
275  D3DTEXTUREFILTERTYPE new_min_filter = get_d3d_min_type(sampler.get_effective_minfilter());
276  D3DTEXTUREFILTERTYPE new_mip_filter = get_d3d_mip_type(sampler.get_effective_minfilter());
277 
278  if (!tex->might_have_ram_image()) {
279  // If the texture is completely dynamic, don't try to issue mipmaps--
280  // pandadx doesn't support auto-generated mipmaps at this point.
281  new_mip_filter = D3DTEXF_NONE;
282  }
283 
284  // sanity check
285  if (!dtc->has_mipmaps()) {
286  new_mip_filter = D3DTEXF_NONE;
287  }
288 
289  if (aniso_degree >= 2) {
290  new_min_filter = D3DTEXF_ANISOTROPIC;
291  }
292 
293  set_sampler_state(i, D3DSAMP_MINFILTER, new_min_filter);
294  set_sampler_state(i, D3DSAMP_MIPFILTER, new_mip_filter);
295 
296  float lod_bias = sampler.get_lod_bias();
297  set_sampler_state(i, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)&lod_bias);
298 
299  _d3d_device->SetTexture(i, dtc->get_d3d_texture());
300 }
301 
302 /**
303  * Ensures that the current Texture data is refreshed onto the GSG. This
304  * means updating the texture properties and/or re-uploading the texture
305  * image, if necessary. This should only be called within the draw thread.
306  *
307  * If force is true, this function will not return until the texture has been
308  * fully uploaded. If force is false, the function may choose to upload a
309  * simple version of the texture instead, if the texture is not fully resident
310  * (and if get_incomplete_render() is true).
311  */
313 update_texture(TextureContext *tc, bool force) {
314  DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
315 
316  // If the texture image has changed, or if its use of mipmaps has changed,
317  // we need to re-create the image.
318 
319  if (dtc->was_modified()) {
320  if (!upload_texture(dtc, force)) {
321  // Oops, we can't re-create the texture for some reason.
322  Texture *tex = tc->get_texture();
323  dxgsg9_cat.error()
324  << "Unable to re-create texture " << *tex << endl;
325  return false;
326  }
327  }
328  dtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
329 
330  return true;
331 }
332 
333 /**
334  * Creates a texture surface on the graphics card and fills it with its pixel
335  * data.
336  */
338 upload_texture(DXTextureContext9 *dtc, bool force) {
339  Texture *tex = dtc->get_texture();
341  dxgsg9_cat.error()
342  << *tex << " is stored in an unsupported compressed format.\n";
343  return false;
344  }
345 
346  dtc->delete_texture();
347  dtc->update_data_size_bytes(0);
348  dtc->mark_unloaded();
349 
350  if (_effective_incomplete_render && !force) {
351  bool has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
352  if (!has_image && tex->might_have_ram_image() &&
353  tex->has_simple_ram_image() &&
354  !_loader.is_null()) {
355  // If we don't have the texture data right now, go get it, but in the
356  // meantime load a temporary simple image in its place.
357  async_reload_texture(dtc);
358  has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
359  if (!has_image) {
360  if (dtc->was_simple_image_modified()) {
361  return dtc->create_simple_texture(*_screen);
362  }
363  return true;
364  }
365  }
366  }
367 
368  return dtc->create_texture(*_screen);
369 }
370 
371 /**
372  * Frees the GL resources previously allocated for the texture.
373  */
376  DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
377  delete dtc;
378 }
379 
380 /**
381  * This method should only be called by the GraphicsEngine. Do not call it
382  * directly; call GraphicsEngine::extract_texture_data() instead.
383  *
384  * This method will be called in the draw thread to download the texture
385  * memory's image into its ram_image value. It returns true on success, false
386  * otherwise.
387  */
390  bool success = true;
391 
392  int num_views = tex->get_num_views();
393  for (int view = 0; view < num_views; ++view) {
394  TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
395  nassertr(tc != nullptr, false);
396  DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
397 
398  if (!dtc->extract_texture_data(*_screen)) {
399  success = false;
400  }
401  }
402 
403  return success;
404 }
405 
406 /**
407  *
408  */
410 prepare_shader(Shader *se) {
411  PStatTimer timer(_prepare_shader_pcollector);
412 
413  switch (se->get_language()) {
414  case Shader::SL_GLSL:
415  dxgsg9_cat.error()
416  << "Tried to load GLSL shader, but GLSL shaders not supported by Direct3D 9.\n";
417  return nullptr;
418 
419  case Shader::SL_Cg:
420 #ifdef HAVE_CG
421  if (_supports_basic_shaders) {
422  return new DXShaderContext9(se, this);
423  } else {
424  dxgsg9_cat.error()
425  << "Tried to load Cg shader, but basic shaders not supported.\n";
426  return nullptr;
427  }
428 #else
429  dxgsg9_cat.error()
430  << "Tried to load Cg shader, but Cg support not compiled in.\n";
431  return nullptr;
432 #endif
433 
434  default:
435  dxgsg9_cat.error()
436  << "Tried to load shader with unsupported shader language!\n";
437  return nullptr;
438  }
439 
440  return nullptr;
441 }
442 
443 /**
444  *
445  */
448  DXShaderContext9 *gsc = DCAST(DXShaderContext9, sc);
449  delete gsc;
450 }
451 
452 /**
453  * Creates a new retained-mode representation of the given data, and returns a
454  * newly-allocated VertexBufferContext pointer to reference it. It is the
455  * responsibility of the calling function to later call
456  * release_vertex_buffer() with this same pointer (which will also delete the
457  * pointer).
458  *
459  * This function should not be called directly to prepare a buffer. Instead,
460  * call Geom::prepare().
461  */
464  DXVertexBufferContext9 *dvbc = new DXVertexBufferContext9(this, _prepared_objects, data);
465 
466  DWORD usage;
467  D3DPOOL pool;
468  if (_screen->_managed_vertex_buffers) {
469  pool = D3DPOOL_MANAGED;
470  usage = D3DUSAGE_WRITEONLY;
471  } else {
472  pool = D3DPOOL_DEFAULT;
473  usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
474  }
475 
476  int num_bytes = data->get_data_size_bytes();
477 
478  PStatTimer timer(_create_vertex_buffer_pcollector, Thread::get_current_thread());
479 
480  HRESULT hr;
481  int attempts = 0;
482  do
483  {
484  hr = _screen->_d3d_device->CreateVertexBuffer(num_bytes, usage, dvbc->_fvf, pool, &dvbc->_vbuffer, nullptr);
485  attempts++;
486  }
487  while (check_dx_allocation(hr, num_bytes, attempts));
488 
489  if (!FAILED(hr)) {
490  #if 0
491  if (dxgsg9_cat.is_debug() && DXdebug_buffers9) {
492  dxgsg9_cat.debug()
493  << "creating vertex buffer " << dvbc->_vbuffer << ": "
494  << data->get_num_rows() << " vertices "
495  << *data->get_array_format() << "\n";
496  }
497  #endif
498 
499  dvbc->update_data_size_bytes(num_bytes);
500  return dvbc;
501  } else {
502  dxgsg9_cat.error()
503  << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
504 
505  dvbc->_vbuffer = nullptr;
506  }
507 
508  return nullptr;
509 }
510 
511 /**
512  * Updates the vertex buffer with the current data, and makes it the current
513  * vertex buffer for rendering.
514  */
517  const GeomVertexArrayDataHandle *reader, bool force ) {
518 
520 
521  if (dvbc->was_modified(reader)) {
522  int num_bytes = reader->get_data_size_bytes();
523  #if 0
524  if (dxgsg9_cat.is_debug() && DXdebug_buffers9) {
525  dxgsg9_cat.debug()
526  << "copying " << num_bytes
527  << " bytes into vertex buffer " << dvbc->_vbuffer << "\n";
528  }
529  #endif
530 
531  if ( num_bytes != 0 ) {
532  const unsigned char *client_pointer = reader->get_read_pointer(force);
533  if (client_pointer == nullptr) {
534  return false;
535  }
536 
537 
538  if (dvbc->changed_size(reader)) {
539  // Destroy and recreate the buffer.
540  if (dvbc->_vbuffer != nullptr) {
541  dvbc->_vbuffer->Release();
542  dvbc->_vbuffer = nullptr;
543  }
544 
545  DWORD usage;
546  D3DPOOL pool;
547  if (_screen->_managed_vertex_buffers) {
548  pool = D3DPOOL_MANAGED;
549  usage = D3DUSAGE_WRITEONLY;
550  } else {
551  pool = D3DPOOL_DEFAULT;
552  usage = D3DUSAGE_WRITEONLY | D3DUSAGE_DYNAMIC;
553  }
554 
555  PStatTimer timer(_create_vertex_buffer_pcollector, Thread::get_current_thread());
556 
557  HRESULT hr;
558  int attempts = 0;
559  do {
560  hr = _screen->_d3d_device->CreateVertexBuffer(num_bytes, usage, dvbc->_fvf, pool, &dvbc->_vbuffer, nullptr);
561  attempts++;
562  } while (check_dx_allocation(hr, num_bytes, attempts));
563 
564  if (FAILED(hr)) {
565  dvbc->_vbuffer = nullptr;
566  dxgsg9_cat.error()
567  << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
568  return false;
569  }
570  }
571 
572  PStatTimer timer(_load_vertex_buffer_pcollector, reader->get_current_thread());
573 
574  HRESULT hr;
575  BYTE *local_pointer;
576  if (_screen->_managed_vertex_buffers) {
577  hr = dvbc->_vbuffer->Lock(0, num_bytes, (void **) &local_pointer, 0);
578  } else {
579  hr = dvbc->_vbuffer->Lock(0, num_bytes, (void **) &local_pointer, D3DLOCK_DISCARD);
580  }
581  if (FAILED(hr)) {
582  dxgsg9_cat.error()
583  << "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
584  return false;
585  }
586 
587  memcpy(local_pointer, client_pointer, num_bytes);
588 
589  dvbc->_vbuffer->Unlock();
590 
591  _data_transferred_pcollector.add_level(num_bytes);
592  }
593 
594  dvbc->mark_loaded(reader);
595  }
596  dvbc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
597 
598  return true;
599 }
600 
601 /**
602  * Frees the GL resources previously allocated for the data. This function
603  * should never be called directly; instead, call Data::release() (or simply
604  * let the Data destruct).
605  */
608 
610 
611  #if 0
612  if (dxgsg9_cat.is_debug() && DXdebug_buffers9) {
613  dxgsg9_cat.debug()
614  << "deleting vertex buffer " << dvbc->_vbuffer << "\n";
615  }
616  #endif
617 
618  if (dvbc->_vbuffer != nullptr) {
619  dvbc->_vbuffer->Release();
620  dvbc->_vbuffer = nullptr;
621  }
622 
623  delete dvbc;
624 }
625 
626 /**
627  * Internal function to bind a buffer object for the indicated data array, if
628  * appropriate, or to unbind a buffer object if it should be rendered from
629  * client memory.
630  *
631  * If the buffer object is bound, this function sets client_pointer to NULL
632  * (representing the start of the buffer object in server memory); if the
633  * buffer object is not bound, this function sets client_pointer the pointer
634  * to the data array in client memory, that is, the data array passed in.
635  *
636  * If force is not true, the function may return false indicating the data is
637  * not currently available.
638  */
641  const GeomVertexArrayDataHandle* array_reader,
642  bool force) {
643 
644  // Prepare the buffer object and bind it.
645  VertexBufferContext* vbc = ((GeomVertexArrayData *)array_reader->get_object())->prepare_now(get_prepared_objects(), this);
646  nassertr(vbc != nullptr, false);
647  if (!apply_vertex_buffer(vbc, array_reader, force)) {
648  return false;
649  }
650 
651  dvbc = (DXVertexBufferContext9*)vbc;
652  return true;
653 }
654 
655 /**
656  * Creates a new retained-mode representation of the given data, and returns a
657  * newly-allocated IndexBufferContext pointer to reference it. It is the
658  * responsibility of the calling function to later call release_index_buffer()
659  * with this same pointer (which will also delete the pointer).
660  *
661  * This function should not be called directly to prepare a buffer. Instead,
662  * call Geom::prepare().
663  */
666  DXIndexBufferContext9 *dibc = new DXIndexBufferContext9(_prepared_objects, data);
667  return dibc;
668 }
669 
670 /**
671  * Updates the index buffer with the current data, and makes it the current
672  * index buffer for rendering.
673  */
676  const GeomPrimitivePipelineReader *reader, bool force) {
677  DXIndexBufferContext9 *dibc = DCAST(DXIndexBufferContext9, ibc);
678 
679  if (dibc->_ibuffer == nullptr) {
680  // Attempt to create a new index buffer.
681  dibc->create_ibuffer(*_screen, reader);
682 
683  if (dibc->_ibuffer != nullptr) {
684  if (!dibc->upload_data(reader, force)) {
685  return false;
686  }
687  dibc->mark_loaded(reader);
688 
689  _d3d_device->SetIndices(dibc->_ibuffer);
690  _active_ibuffer = dibc;
691  dibc->set_active(true);
692 
693  } else {
694  _d3d_device->SetIndices(nullptr);
695  _active_ibuffer = nullptr;
696  }
697 
698  } else {
699  if (dibc->was_modified(reader)) {
700  if (dibc->changed_size(reader)) {
701  // We have to destroy the old index buffer and create a new one.
702  dibc->create_ibuffer(*_screen, reader);
703  }
704 
705  if (!dibc->upload_data(reader, force)) {
706  return false;
707  }
708 
709  dibc->mark_loaded(reader);
710  _active_ibuffer = nullptr;
711  }
712 
713  if (_active_ibuffer != dibc) {
714  _d3d_device->SetIndices(dibc->_ibuffer);
715  _active_ibuffer = dibc;
716  dibc->set_active(true);
717  }
718  }
719  dibc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
720 
721  return true;
722 }
723 
724 /**
725  * Frees the GL resources previously allocated for the data. This function
726  * should never be called directly; instead, call Data::release() (or simply
727  * let the Data destruct).
728  */
731  DXIndexBufferContext9 *dibc = DCAST(DXIndexBufferContext9, ibc);
732  delete dibc;
733 }
734 
735 /**
736  * Begins a new occlusion query. After this call, you may call
737  * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly.
738  * Eventually, you should call end_occlusion_query() before the end of the
739  * frame; that will return a new OcclusionQueryContext object that will tell
740  * you how many pixels represented by the bracketed geometry passed the depth
741  * test.
742  *
743  * It is not valid to call begin_occlusion_query() between another
744  * begin_occlusion_query() .. end_occlusion_query() sequence.
745  */
748  nassertv(_supports_occlusion_query);
749  nassertv(_current_occlusion_query == nullptr);
750 
751  IDirect3DQuery9 *query;
752  HRESULT hr = _d3d_device->CreateQuery(D3DQUERYTYPE_OCCLUSION, &query);
753  if (FAILED(hr)) {
754  dxgsg9_cat.warning()
755  << "Occlusion query failed.\n";
756  return;
757  }
758 
759  PT(DXOcclusionQueryContext9) queryobj = new DXOcclusionQueryContext9(query);
760 
761  if (dxgsg9_cat.is_debug()) {
762  dxgsg9_cat.debug()
763  << "beginning occlusion query " << query << "\n";
764  }
765 
766  query->Issue(D3DISSUE_BEGIN);
767  _current_occlusion_query = queryobj;
768 }
769 
770 /**
771  * Ends a previous call to begin_occlusion_query(). This call returns the
772  * OcclusionQueryContext object that will (eventually) report the number of
773  * pixels that passed the depth test between the call to
774  * begin_occlusion_query() and end_occlusion_query().
775  */
776 PT(OcclusionQueryContext) DXGraphicsStateGuardian9::
777 end_occlusion_query() {
778  if (_current_occlusion_query == nullptr) {
779  return nullptr;
780  }
781 
782  PT(OcclusionQueryContext) result = _current_occlusion_query;
783 
784  IDirect3DQuery9 *query = DCAST(DXOcclusionQueryContext9, result)->_query;
785 
786  if (dxgsg9_cat.is_debug()) {
787  dxgsg9_cat.debug()
788  << "ending occlusion query " << query << "\n";
789  }
790 
791  _current_occlusion_query = nullptr;
792  query->Issue(D3DISSUE_END);
793 
794  return result;
795 }
796 
797 /**
798  * Creates a new GeomMunger object to munge vertices appropriate to this GSG
799  * for the indicated state.
800  */
801 PT(GeomMunger) DXGraphicsStateGuardian9::
802 make_geom_munger(const RenderState *state, Thread *current_thread) {
803  PT(DXGeomMunger9) munger = new DXGeomMunger9(this, state);
804  return GeomMunger::register_munger(munger, current_thread);
805 }
806 
807 /**
808  * Clears all of the indicated buffers to their assigned colors.
809  */
811 clear(DrawableRegion *clearable) {
812 
813  DWORD main_flags = 0;
814  DWORD aux_flags = 0;
815 
816  if ((!clearable->get_clear_color_active())&&
817  (!clearable->get_clear_depth_active())&&
818  (!clearable->get_clear_stencil_active())) {
819  return;
820  }
821 
822  set_state_and_transform(RenderState::make_empty(), _internal_transform);
823 
824  D3DCOLOR color_clear_value = LColor_to_D3DCOLOR(clearable->get_clear_color());
825  PN_stdfloat depth_clear_value = clearable->get_clear_depth();
826  DWORD stencil_clear_value = (DWORD)(clearable->get_clear_stencil());
827 
828  // set appropriate flags
829  if (clearable->get_clear_color_active()) {
830  main_flags |= D3DCLEAR_TARGET;
831  }
832 
833  if (clearable->get_clear_depth_active() &&
834  _screen->_presentation_params.EnableAutoDepthStencil) {
835  aux_flags |= D3DCLEAR_ZBUFFER;
836  }
837 
838  if (clearable->get_clear_stencil_active()) {
839  // clear only if there is a stencil buffer
840  if (_screen->_presentation_params.EnableAutoDepthStencil &&
841  IS_STENCIL_FORMAT(_screen->_presentation_params.AutoDepthStencilFormat)) {
842  aux_flags |= D3DCLEAR_STENCIL;
843  }
844  }
845 
846  if ((main_flags | aux_flags) != 0) {
847  HRESULT hr = _d3d_device->Clear(0, nullptr, main_flags | aux_flags, color_clear_value,
848  depth_clear_value, stencil_clear_value);
849  if (FAILED(hr) && main_flags == D3DCLEAR_TARGET && aux_flags != 0) {
850  // Maybe there's a problem with the one or more of the auxiliary
851  // buffers.
852  hr = _d3d_device->Clear(0, nullptr, D3DCLEAR_TARGET, color_clear_value,
853  depth_clear_value, stencil_clear_value);
854  if (!FAILED(hr)) {
855  // Yep, it worked without them. That's a problem. Which buffer poses
856  // the problem?
857  if (clearable->get_clear_depth_active()) {
858  aux_flags |= D3DCLEAR_ZBUFFER;
859  HRESULT hr2 = _d3d_device->Clear(0, nullptr, D3DCLEAR_ZBUFFER, color_clear_value,
860  depth_clear_value, stencil_clear_value);
861  if (FAILED(hr2)) {
862  dxgsg9_cat.error()
863  << "Unable to clear depth buffer; removing.\n";
864  // This is really hacky code.
865  ((FrameBufferProperties *)_current_properties)->set_depth_bits(0);
866  }
867  }
868  if (clearable->get_clear_stencil_active()) {
869  aux_flags |= D3DCLEAR_STENCIL;
870  HRESULT hr2 = _d3d_device->Clear(0, nullptr, D3DCLEAR_STENCIL, color_clear_value,
871  stencil_clear_value, stencil_clear_value);
872  if (FAILED(hr2)) {
873  dxgsg9_cat.error()
874  << "Unable to clear stencil buffer; removing.\n";
875  // This is really hacky code.
876  ((FrameBufferProperties *)_current_properties)->set_stencil_bits(0);
877  _supports_stencil = false;
878  }
879  }
880  }
881  }
882 
883  if (FAILED(hr)) {
884  dxgsg9_cat.error()
885  << "clear_buffer failed: Clear returned " << D3DERRORSTRING(hr);
886  }
887  }
888 }
889 
890 /**
891  * Prepare a display region for rendering (set up scissor region and viewport)
892  */
895  nassertv(dr != nullptr);
897 
898  int l, u, w, h;
899  dr->get_region_pixels_i(l, u, w, h);
900 
901  // Create the viewport
902  D3DVIEWPORT9 vp = { (DWORD)l, (DWORD)u, (DWORD)w, (DWORD)h, 0.0f, 1.0f };
903  _current_viewport = vp;
904  HRESULT hr = _d3d_device->SetViewport(&_current_viewport);
905  if (FAILED(hr)) {
906  dxgsg9_cat.error()
907  << "_screen->_swap_chain = " << _screen->_swap_chain << " _swap_chain = " << _swap_chain << "\n";
908  dxgsg9_cat.error()
909  << "SetViewport(" << l << ", " << u << ", " << w << ", " << h
910  << ") failed" << D3DERRORSTRING(hr);
911 
912  D3DVIEWPORT9 vp_old;
913  _d3d_device->GetViewport(&vp_old);
914  dxgsg9_cat.error()
915  << "GetViewport(" << vp_old.X << ", " << vp_old.Y << ", " << vp_old.Width << ", "
916  << vp_old.Height << ") returned: Trying to set that vp---->\n";
917  hr = _d3d_device->SetViewport(&vp_old);
918  set_render_state(D3DRS_SCISSORTESTENABLE, FALSE);
919 
920  if (FAILED(hr)) {
921  dxgsg9_cat.error() << "Failed again\n";
922  throw_event("panda3d-render-error");
923  nassertv(false);
924  }
925  }
926 
927  if (_screen->_can_direct_disable_color_writes) {
928  set_render_state(D3DRS_COLORWRITEENABLE, _color_write_mask);
929  }
930 }
931 
932 /**
933  * Given a lens, calculates the appropriate projection matrix for use with
934  * this gsg. Note that the projection matrix depends a lot upon the
935  * coordinate system of the rendering API.
936  *
937  * The return value is a TransformState if the lens is acceptable, NULL if it
938  * is not.
939  */
940 CPT(TransformState) DXGraphicsStateGuardian9::
941 calc_projection_mat(const Lens *lens) {
942  if (lens == nullptr) {
943  return nullptr;
944  }
945 
946  if (!lens->is_linear()) {
947  return nullptr;
948  }
949 
950  // DirectX also uses a Z range of 0 to 1, whereas the Panda convention is
951  // for the projection matrix to produce a Z range of -1 to 1. We have to
952  // rescale to compensate.
953  static const LMatrix4 rescale_mat
954  (1, 0, 0, 0,
955  0, 1, 0, 0,
956  0, 0, 0.5, 0,
957  0, 0, 0.5, 1);
958 
959  LMatrix4 result =
960  LMatrix4::convert_mat(CS_yup_left, _current_lens->get_coordinate_system()) *
961  lens->get_projection_mat(_current_stereo_channel) *
962  rescale_mat;
963 
964  if (_scene_setup->get_inverted()) {
965  // If the scene is supposed to be inverted, then invert the projection
966  // matrix.
967  result *= LMatrix4::scale_mat(1.0f, -1.0f, 1.0f);
968  }
969 
970  return TransformState::make_mat(result);
971 }
972 
973 /**
974  * Makes the current lens (whichever lens was most recently specified with
975  * set_scene()) active, so that it will transform future rendered geometry.
976  * Normally this is only called from the draw process, and usually it is
977  * called by set_scene().
978  *
979  * The return value is true if the lens is acceptable, false if it is not.
980  */
982 prepare_lens() {
983  LMatrix4f mat = LCAST(float, _projection_mat->get_mat());
984  HRESULT hr =
985  _d3d_device->SetTransform(D3DTS_PROJECTION,
986  (D3DMATRIX*)mat.get_data());
987  return SUCCEEDED(hr);
988 }
989 
990 /**
991  * Called before each frame is rendered, to allow the GSG a chance to do any
992  * internal cleanup before beginning the frame.
993  *
994  * The return value is true if successful (in which case the frame will be
995  * drawn and end_frame() will be called later), or false if unsuccessful (in
996  * which case nothing will be drawn and end_frame() will not be called).
997  */
999 begin_frame(Thread *current_thread) {
1000 
1001  GraphicsStateGuardian::begin_frame(current_thread);
1002 
1003  if (_d3d_device == nullptr) {
1004  dxgsg9_cat.debug()
1005  << this << "::begin_frame(): no device.\n";
1006  return false;
1007  }
1008 
1009  HRESULT hr = _d3d_device->BeginScene();
1010 
1011  if (FAILED(hr)) {
1012  if (hr == D3DERR_DEVICELOST) {
1013  if (dxgsg9_cat.is_debug()) {
1014  dxgsg9_cat.debug()
1015  << "BeginScene returns D3DERR_DEVICELOST" << endl;
1016  }
1017 
1018  check_cooperative_level();
1019 
1020  } else {
1021  dxgsg9_cat.error()
1022  << "BeginScene failed, unhandled error hr == "
1023  << D3DERRORSTRING(hr) << endl;
1024  throw_event("panda3d-render-error");
1025  }
1026  return false;
1027  }
1028 
1029  if (_current_properties->get_srgb_color()) {
1030  set_render_state(D3DRS_SRGBWRITEENABLE, TRUE);
1031  } else {
1032  set_render_state(D3DRS_SRGBWRITEENABLE, FALSE);
1033  }
1034 
1035  return true;
1036 }
1037 
1038 /**
1039  * Called between begin_frame() and end_frame() to mark the beginning of
1040  * drawing commands for a "scene" (usually a particular DisplayRegion) within
1041  * a frame. All 3-D drawing commands, except the clear operation, must be
1042  * enclosed within begin_scene() .. end_scene().
1043  *
1044  * The return value is true if successful (in which case the scene will be
1045  * drawn and end_scene() will be called later), or false if unsuccessful (in
1046  * which case nothing will be drawn and end_scene() will not be called).
1047  */
1049 begin_scene() {
1051  return false;
1052  }
1053 
1054 /*
1055  HRESULT hr = _d3d_device->BeginScene();
1056 
1057  if (FAILED(hr)) {
1058  if (hr == D3DERR_DEVICELOST) {
1059  if (dxgsg9_cat.is_debug()) {
1060  dxgsg9_cat.debug()
1061  << "BeginScene returns D3DERR_DEVICELOST" << endl;
1062  }
1063 
1064  check_cooperative_level();
1065 
1066  } else {
1067  dxgsg9_cat.error()
1068  << "BeginScene failed, unhandled error hr == "
1069  << D3DERRORSTRING(hr) << endl;
1070  throw_event("panda3d-render-error");
1071  }
1072  return false;
1073  }
1074 */
1075 
1076  return true;
1077 }
1078 
1079 /**
1080  * Called between begin_frame() and end_frame() to mark the end of drawing
1081  * commands for a "scene" (usually a particular DisplayRegion) within a frame.
1082  * All 3-D drawing commands, except the clear operation, must be enclosed
1083  * within begin_scene() .. end_scene().
1084  */
1086 end_scene() {
1088 
1089  if (_vertex_array_shader_context != 0) {
1090  _vertex_array_shader_context->disable_shader_vertex_arrays(this);
1091  _vertex_array_shader = nullptr;
1092  _vertex_array_shader_context = nullptr;
1093  }
1094  if (_texture_binding_shader_context != 0) {
1095  _texture_binding_shader_context->disable_shader_texture_bindings(this);
1096  _texture_binding_shader = nullptr;
1097  _texture_binding_shader_context = nullptr;
1098  }
1099  if (_current_shader_context != 0) {
1100  _current_shader_context->unbind(this);
1101  _current_shader = nullptr;
1102  _current_shader_context = nullptr;
1103  }
1104 
1105  _dlights.clear();
1106 
1107 /*
1108  HRESULT hr = _d3d_device->EndScene();
1109 
1110  if (FAILED(hr)) {
1111  if (hr == D3DERR_DEVICELOST) {
1112  if (dxgsg9_cat.is_debug()) {
1113  dxgsg9_cat.debug()
1114  << "EndScene returns DeviceLost\n";
1115  }
1116  check_cooperative_level();
1117 
1118  } else {
1119  dxgsg9_cat.error()
1120  << "EndScene failed, unhandled error hr == " << D3DERRORSTRING(hr);
1121  throw_event("panda3d-render-error");
1122  }
1123  return;
1124  }
1125 */
1126 
1127 }
1128 
1129 /**
1130  * Called after each frame is rendered, to allow the GSG a chance to do any
1131  * internal cleanup after rendering the frame, and before the window flips.
1132  */
1134 end_frame(Thread *current_thread) {
1135 
1136  HRESULT hr = _d3d_device->EndScene();
1137 
1138  if (FAILED(hr)) {
1139  if (hr == D3DERR_DEVICELOST) {
1140  if (dxgsg9_cat.is_debug()) {
1141  dxgsg9_cat.debug()
1142  << "EndScene returns DeviceLost\n";
1143  }
1144  check_cooperative_level();
1145 
1146  } else {
1147  dxgsg9_cat.error()
1148  << "EndScene failed, unhandled error hr == " << D3DERRORSTRING(hr);
1149  throw_event("panda3d-render-error");
1150  }
1151  return;
1152  }
1153 
1154 #if defined(DO_PSTATS)
1155  if (_texmgrmem_total_pcollector.is_active()) {
1156 #define TICKS_PER_GETTEXINFO (2.5*1000) // 2.5 second interval
1157  static DWORD last_tick_count = 0;
1158  DWORD cur_tick_count = GetTickCount();
1159 
1160  if (cur_tick_count - last_tick_count > TICKS_PER_GETTEXINFO) {
1161  last_tick_count = cur_tick_count;
1162  report_texmgr_stats();
1163  }
1164  }
1165 #endif
1166 
1167  // Note: regular GraphicsWindow::end_frame is being called, but we override
1168  // gsg::end_frame, so need to explicitly call it here (currently it's an
1169  // empty fn)
1170  GraphicsStateGuardian::end_frame(current_thread);
1171 }
1172 
1173 /**
1174  * Called before a sequence of draw_primitive() functions are called, this
1175  * should prepare the vertex data for rendering. It returns true if the
1176  * vertices are ok, false to abort this group of primitives.
1177  */
1179 begin_draw_primitives(const GeomPipelineReader *geom_reader,
1180  const GeomVertexDataPipelineReader *data_reader,
1181  bool force) {
1182  if (!GraphicsStateGuardian::begin_draw_primitives(geom_reader, data_reader, force)) {
1183  return false;
1184  }
1185  nassertr(_data_reader != nullptr, false);
1186 
1187  const GeomVertexFormat *format = _data_reader->get_format();
1188 
1189  const GeomVertexAnimationSpec &animation =
1190  data_reader->get_format()->get_animation();
1191  if (animation.get_animation_type() == Geom::AT_hardware) {
1192  // Set up vertex blending.
1193  switch (animation.get_num_transforms()) {
1194  case 1:
1195  // The MSDN docs suggest we should use D3DVBF_0WEIGHTS here, but that
1196  // doesn't seem to work at all. On the other hand, D3DVBF_DISABLE
1197  // *does* work, because it disables special handling, meaning only the
1198  // world matrix affects these vertices--and by accident or design, the
1199  // first matrix, D3DTS_WORLDMATRIX(0), *is* the world matrix.
1200  set_render_state(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
1201  break;
1202  case 2:
1203  set_render_state(D3DRS_VERTEXBLEND, D3DVBF_1WEIGHTS);
1204  break;
1205  case 3:
1206  set_render_state(D3DRS_VERTEXBLEND, D3DVBF_2WEIGHTS);
1207  break;
1208  case 4:
1209  set_render_state(D3DRS_VERTEXBLEND, D3DVBF_3WEIGHTS);
1210  break;
1211  }
1212 
1213  if (animation.get_indexed_transforms()) {
1214  // Set up indexed vertex blending.
1215  set_render_state(D3DRS_INDEXEDVERTEXBLENDENABLE, TRUE);
1216  } else {
1217  set_render_state(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
1218  }
1219 
1220  const TransformTable *table = data_reader->get_transform_table();
1221  if (table != nullptr) {
1222  for (size_t i = 0; i < table->get_num_transforms(); ++i) {
1223  LMatrix4 mat;
1224  table->get_transform(i)->mult_matrix(mat, _internal_transform->get_mat());
1225  const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data();
1226  _d3d_device->SetTransform(D3DTS_WORLDMATRIX(i), d3d_mat);
1227  }
1228 
1229  // Setting the first animation matrix steps on the world matrix, so we
1230  // have to set a flag to reload the world matrix later.
1231  _transform_stale = true;
1232  }
1233  _vertex_blending_enabled = true;
1234 
1235  } else {
1236  // We're not using vertex blending.
1237  if (_vertex_blending_enabled) {
1238  set_render_state(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
1239  set_render_state(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
1240  _vertex_blending_enabled = false;
1241  }
1242 
1243  if (_transform_stale && !_data_reader->is_vertex_transformed()) {
1244  const D3DMATRIX *d3d_mat = (const D3DMATRIX *)_internal_transform->get_mat().get_data();
1245  _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
1246  _transform_stale = false;
1247  }
1248  }
1249 
1250  if (_data_reader->is_vertex_transformed()) {
1251  // If the vertex data claims to be already transformed into clip
1252  // coordinates, wipe out the current projection and modelview matrix (so
1253  // we don't attempt to transform it again).
1254 
1255  // It's tempting just to use the D3DFVF_XYZRHW specification on these
1256  // vertices, but that turns out to be a bigger hammer than we want: that
1257  // also prevents lighting calculations and user clip planes.
1258  _d3d_device->SetTransform(D3DTS_WORLD, &_d3d_ident_mat);
1259  static const LMatrix4f rescale_mat
1260  (1, 0, 0, 0,
1261  0, 1, 0, 0,
1262  0, 0, 0.5, 0,
1263  0, 0, 0.5, 1);
1264  _transform_stale = true;
1265 
1266  _d3d_device->SetTransform(D3DTS_PROJECTION, (const D3DMATRIX *)rescale_mat.get_data());
1267  }
1268 
1269  if (_current_shader_context == 0 /*|| !_current_shader_context->uses_custom_vertex_arrays()*/) {
1270  // No shader, or a non-Cg shader.
1271  if (_vertex_array_shader_context != 0) {
1272  _vertex_array_shader_context->disable_shader_vertex_arrays(this);
1273  }
1274  if (!update_standard_vertex_arrays(force)) {
1275  return false;
1276  }
1277  } else {
1278  // Cg shader.
1279  if (_vertex_array_shader_context == 0) {
1280  disable_standard_vertex_arrays();
1281  if (!_current_shader_context->update_shader_vertex_arrays(nullptr, this, force)) {
1282  return false;
1283  }
1284  } else {
1285  if (!_current_shader_context->
1286  update_shader_vertex_arrays(_vertex_array_shader_context, this, force)) {
1287  return false;
1288  }
1289  }
1290  }
1291 
1292  _vertex_array_shader = _current_shader;
1293  _vertex_array_shader_context = _current_shader_context;
1294 
1295  return true;
1296 }
1297 
1298 /**
1299  * Binds vertex buffers as stream sources and sets the correct FVF format for
1300  * fixed-function rendering. Used only when the standard (non-shader)
1301  * pipeline is about to be used - dxShaderContexts are responsible for setting
1302  * up their own vertex arrays.
1303  */
1304 bool DXGraphicsStateGuardian9::
1305 update_standard_vertex_arrays(bool force) {
1306 
1307  int fvf = 0;
1308  HRESULT hr;
1309 
1310  int number_of_arrays = _data_reader->get_num_arrays();
1311  for ( int array_index = 0; array_index < number_of_arrays; ++array_index ) {
1312  const GeomVertexArrayDataHandle* array_reader = _data_reader->get_array_reader( array_index );
1313  if ( array_reader == nullptr ) {
1314  dxgsg9_cat.error() << "Unable to get reader for array " << array_index << "\n";
1315  return false;
1316  }
1317 
1318  // Get the vertex buffer for this array.
1319  DXVertexBufferContext9* dvbc;
1320  if (!setup_array_data(dvbc, array_reader, force)) {
1321  dxgsg9_cat.error() << "Unable to setup vertex buffer for array " << array_index << "\n";
1322  return false;
1323  }
1324 
1325  // Bind this array as the data source for the corresponding stream.
1326  const GeomVertexArrayFormat* array_format = array_reader->get_array_format();
1327  hr = _d3d_device->SetStreamSource( array_index, dvbc->_vbuffer, 0, array_format->get_stride() );
1328  if (FAILED(hr)) {
1329  dxgsg9_cat.error() << "SetStreamSource failed" << D3DERRORSTRING(hr);
1330  return false;
1331  }
1332 
1333  // Update our combined set of FVF flags
1334  fvf |= dvbc->_fvf;
1335  }
1336 
1337  hr = _d3d_device->SetFVF( fvf );
1338  if (FAILED(hr)) {
1339  dxgsg9_cat.error() << "SetFVF failed" << D3DERRORSTRING(hr);
1340  return false;
1341  }
1342 
1343  return true;
1344 }
1345 
1346 /**
1347  * Unbinds all of the streams that are currently enabled. dxShaderContexts
1348  * are responsible for setting up their own streams, but before they can do
1349  * so, the standard streams need to be disabled to get them "out of the way."
1350  * Called only from begin_draw_primitives.
1351  */
1352 void DXGraphicsStateGuardian9::
1353 disable_standard_vertex_arrays() {
1354  for ( int array_index = 0; array_index < _num_bound_streams; ++array_index )
1355  {
1356  _d3d_device->SetStreamSource( array_index, nullptr, 0, 0 );
1357  }
1358  _num_bound_streams = 0;
1359 }
1360 
1361 /**
1362  * Draws a series of disconnected triangles.
1363  */
1365 draw_triangles(const GeomPrimitivePipelineReader *reader, bool force) {
1366  // PStatTimer timer(_draw_primitive_pcollector);
1367 
1368  _vertices_tri_pcollector.add_level(reader->get_num_vertices());
1369  _primitive_batches_tri_pcollector.add_level(1);
1370 
1371  if (reader->is_indexed()) {
1372  int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
1373  int max_vertex = reader->get_max_vertex();
1374 
1375  // Indexed, vbuffers.
1376  IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
1377  nassertr(ibc != nullptr, false);
1378  if (!apply_index_buffer(ibc, reader, force)) {
1379  return false;
1380  }
1381 
1382  _d3d_device->DrawIndexedPrimitive( D3DPT_TRIANGLELIST,
1383  0,
1384  min_vertex, max_vertex - min_vertex + 1,
1385  0, reader->get_num_primitives() );
1386 
1387  #if 0
1388  // Indexed, client arrays.
1389  const unsigned char *index_pointer = reader->get_read_pointer(force);
1390  if (index_pointer == nullptr) {
1391  return false;
1392  }
1393  D3DFORMAT index_type = get_index_type(reader->get_index_type());
1394  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1395  if (vertex_pointer == nullptr) {
1396  return false;
1397  }
1398 
1399  draw_indexed_primitive_up( D3DPT_TRIANGLELIST,
1400  min_vertex, max_vertex,
1401  reader->get_num_primitives(),
1402  index_pointer, index_type, vertex_pointer,
1403  _data_reader->get_format()->get_array(0)->get_stride() );
1404  #endif
1405  } else {
1406  // Nonindexed, vbuffers.
1407  _d3d_device->DrawPrimitive( D3DPT_TRIANGLELIST,
1408  reader->get_first_vertex(),
1409  reader->get_num_primitives() );
1410 
1411  #if 0
1412  // Nonindexed, client arrays.
1413  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1414  if (vertex_pointer == nullptr) {
1415  return false;
1416  }
1417 
1418  draw_primitive_up(D3DPT_TRIANGLELIST, reader->get_num_primitives(),
1419  reader->get_first_vertex(),
1420  reader->get_num_vertices(), vertex_pointer,
1421  _data_reader->get_format()->get_array(0)->get_stride());
1422  #endif
1423  }
1424 
1425  return true;
1426 }
1427 
1428 /**
1429  * Draws a series of triangle strips.
1430  */
1432 draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force) {
1433  // PStatTimer timer(_draw_primitive_pcollector);
1434 
1435  if (connect_triangle_strips && _current_fill_mode != RenderModeAttrib::M_wireframe) {
1436  // One long triangle strip, connected by the degenerate vertices that have
1437  // already been set up within the primitive.
1438  _vertices_tristrip_pcollector.add_level(reader->get_num_vertices());
1439  _primitive_batches_tristrip_pcollector.add_level(1);
1440 
1441  if (reader->is_indexed()) {
1442  int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
1443  int max_vertex = reader->get_max_vertex();
1444 
1445  // Indexed, vbuffers, one long triangle strip.
1446  IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
1447  nassertr(ibc != nullptr, false);
1448  if (!apply_index_buffer(ibc, reader, force)) {
1449  return false;
1450  }
1451 
1452  _d3d_device->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP,
1453  0,
1454  min_vertex, max_vertex - min_vertex + 1,
1455  0, reader->get_num_vertices() - 2 );
1456 
1457  #if 0
1458  // Indexed, client arrays, one long triangle strip.
1459  const unsigned char *index_pointer = reader->get_read_pointer(force);
1460  if (index_pointer == nullptr) {
1461  return false;
1462  }
1463  D3DFORMAT index_type = get_index_type(reader->get_index_type());
1464  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1465  if (vertex_pointer == nullptr) {
1466  return false;
1467  }
1468 
1469  draw_indexed_primitive_up
1470  (D3DPT_TRIANGLESTRIP,
1471  min_vertex, max_vertex,
1472  reader->get_num_vertices() - 2,
1473  index_pointer, index_type, vertex_pointer,
1474  _data_reader->get_format()->get_array(0)->get_stride());
1475  #endif
1476  } else {
1477  // Nonindexed, vbuffers, one long triangle strip.
1478  _d3d_device->DrawPrimitive( D3DPT_TRIANGLESTRIP,
1479  reader->get_first_vertex(),
1480  reader->get_num_vertices() - 2 );
1481 
1482  #if 0
1483  // Indexed, client arrays, one long triangle strip.
1484  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1485  if (vertex_pointer == nullptr) {
1486  return false;
1487  }
1488  draw_primitive_up(D3DPT_TRIANGLESTRIP,
1489  reader->get_num_vertices() - 2,
1490  reader->get_first_vertex(),
1491  reader->get_num_vertices(), vertex_pointer,
1492  _data_reader->get_format()->get_array(0)->get_stride());
1493  #endif
1494  }
1495 
1496  } else {
1497  // Send the individual triangle strips, stepping over the degenerate
1498  // vertices.
1499  CPTA_int ends = reader->get_ends();
1500  _primitive_batches_tristrip_pcollector.add_level(ends.size());
1501 
1502  if (reader->is_indexed()) {
1503  CPTA_int ends = reader->get_ends();
1504  int index_stride = reader->get_index_stride();
1505  _primitive_batches_tristrip_pcollector.add_level(ends.size());
1506 
1507  GeomVertexReader mins(reader->get_mins(), 0);
1508  GeomVertexReader maxs(reader->get_maxs(), 0);
1509  nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
1510  reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
1511 
1512  // Indexed, vbuffers, individual triangle strips.
1513  IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
1514  nassertr(ibc != nullptr, false);
1515  if (!apply_index_buffer(ibc, reader, force)) {
1516  return false;
1517  }
1518 
1519  unsigned int start = 0;
1520  for (size_t i = 0; i < ends.size(); i++) {
1521  _vertices_tristrip_pcollector.add_level(ends[i] - start);
1522  unsigned int min = mins.get_data1i();
1523  unsigned int max = maxs.get_data1i();
1524  _d3d_device->DrawIndexedPrimitive( D3DPT_TRIANGLESTRIP,
1525  0,
1526  min, max - min + 1,
1527  start, ends[i] - start - 2 );
1528  start = ends[i] + 2;
1529  }
1530 
1531  #if 0
1532  // Indexed, client arrays, individual triangle strips.
1533  int stride = _data_reader->get_format()->get_array(0)->get_stride();
1534  const unsigned char *index_pointer = reader->get_read_pointer(force);
1535  if (index_pointer == nullptr) {
1536  return false;
1537  }
1538  D3DFORMAT index_type = get_index_type(reader->get_index_type());
1539  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1540  if (vertex_pointer == nullptr) {
1541  return false;
1542  }
1543 
1544  unsigned int start = 0;
1545  for (size_t i = 0; i < ends.size(); i++) {
1546  _vertices_tristrip_pcollector.add_level(ends[i] - start);
1547  unsigned int min = mins.get_data1i();
1548  unsigned int max = maxs.get_data1i();
1549  draw_indexed_primitive_up
1550  (D3DPT_TRIANGLESTRIP,
1551  min, max,
1552  ends[i] - start - 2,
1553  index_pointer + start * index_stride, index_type,
1554  vertex_pointer, stride);
1555 
1556  start = ends[i] + 2;
1557  }
1558  #endif
1559  } else {
1560  unsigned int first_vertex = reader->get_first_vertex();
1561 
1562  // Nonindexed, vbuffers, individual triangle strips.
1563  unsigned int start = 0;
1564  for (size_t i = 0; i < ends.size(); i++) {
1565  _vertices_tristrip_pcollector.add_level(ends[i] - start);
1566  _d3d_device->DrawPrimitive( D3DPT_TRIANGLESTRIP,
1567  first_vertex + start,
1568  ends[i] - start - 2 );
1569  start = ends[i] + 2;
1570  }
1571 
1572  #if 0
1573  // Nonindexed, client arrays, individual triangle strips.
1574  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1575  if (vertex_pointer == nullptr) {
1576  return false;
1577  }
1578  int stride = _data_reader->get_format()->get_array(0)->get_stride();
1579 
1580  unsigned int start = 0;
1581  for (size_t i = 0; i < ends.size(); i++) {
1582  _vertices_tristrip_pcollector.add_level(ends[i] - start);
1583  draw_primitive_up(D3DPT_TRIANGLESTRIP, ends[i] - start - 2,
1584  first_vertex + start,
1585  ends[i] - start,
1586  vertex_pointer, stride);
1587 
1588  start = ends[i] + 2;
1589  }
1590  #endif
1591  }
1592  }
1593  return true;
1594 }
1595 
1596 /**
1597  * Draws a series of triangle fans.
1598  */
1600 draw_trifans(const GeomPrimitivePipelineReader *reader, bool force) {
1601  // PStatTimer timer(_draw_primitive_pcollector);
1602 
1603  CPTA_int ends = reader->get_ends();
1604  _primitive_batches_trifan_pcollector.add_level(ends.size());
1605 
1606  if (reader->is_indexed()) {
1607  int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
1608  int max_vertex = reader->get_max_vertex();
1609 
1610  // Send the individual triangle fans. There's no connecting fans with
1611  // degenerate vertices, so no worries about that.
1612  int index_stride = reader->get_index_stride();
1613 
1614  GeomVertexReader mins(reader->get_mins(), 0);
1615  GeomVertexReader maxs(reader->get_maxs(), 0);
1616  nassertr(reader->get_mins()->get_num_rows() == (int)ends.size() &&
1617  reader->get_maxs()->get_num_rows() == (int)ends.size(), false);
1618 
1619  // Indexed, vbuffers.
1620  IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
1621  nassertr(ibc != nullptr, false);
1622  if (!apply_index_buffer(ibc, reader, force)) {
1623  return false;
1624  }
1625 
1626  unsigned int start = 0;
1627  for (size_t i = 0; i < ends.size(); i++) {
1628  _vertices_trifan_pcollector.add_level(ends[i] - start);
1629  unsigned int min = mins.get_data1i();
1630  unsigned int max = maxs.get_data1i();
1631  _d3d_device->DrawIndexedPrimitive( D3DPT_TRIANGLEFAN,
1632  0,
1633  min, max - min + 1,
1634  start, ends[i] - start - 2 );
1635  start = ends[i];
1636  }
1637 
1638  #if 0
1639  // Indexed, client arrays.
1640  int stride = _data_reader->get_format()->get_array(0)->get_stride();
1641  const unsigned char *index_pointer = reader->get_read_pointer(force);
1642  if (index_pointer == nullptr) {
1643  return false;
1644  }
1645  D3DFORMAT index_type = get_index_type(reader->get_index_type());
1646  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1647  if (vertex_pointer == nullptr) {
1648  return false;
1649  }
1650 
1651  unsigned int start = 0;
1652  for (size_t i = 0; i < ends.size(); i++) {
1653  _vertices_trifan_pcollector.add_level(ends[i] - start);
1654  unsigned int min = mins.get_data1i();
1655  unsigned int max = maxs.get_data1i();
1656  draw_indexed_primitive_up
1657  (D3DPT_TRIANGLEFAN,
1658  min, max,
1659  ends[i] - start - 2,
1660  index_pointer + start * index_stride, index_type,
1661  vertex_pointer, stride);
1662 
1663  start = ends[i];
1664  }
1665  #endif
1666  } else {
1667  unsigned int first_vertex = reader->get_first_vertex();
1668 
1669  // Nonindexed, vbuffers.
1670  unsigned int start = 0;
1671  for (size_t i = 0; i < ends.size(); i++) {
1672  _vertices_trifan_pcollector.add_level(ends[i] - start);
1673  _d3d_device->DrawPrimitive( D3DPT_TRIANGLEFAN,
1674  first_vertex + start,
1675  ends[i] - start - 2 );
1676  start = ends[i];
1677  }
1678 
1679  #if 0
1680  // Nonindexed, client arrays.
1681  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1682  if (vertex_pointer == nullptr) {
1683  return false;
1684  }
1685  int stride = _data_reader->get_format()->get_array(0)->get_stride();
1686 
1687  unsigned int start = 0;
1688  for (size_t i = 0; i < ends.size(); i++) {
1689  _vertices_trifan_pcollector.add_level(ends[i] - start);
1690  draw_primitive_up(D3DPT_TRIANGLEFAN,
1691  ends[i] - start - 2,
1692  first_vertex,
1693  ends[i] - start,
1694  vertex_pointer, stride);
1695  start = ends[i];
1696  }
1697  #endif
1698  }
1699  return true;
1700 }
1701 
1702 /**
1703  * Draws a series of disconnected line segments.
1704  */
1706 draw_lines(const GeomPrimitivePipelineReader *reader, bool force) {
1707  // PStatTimer timer(_draw_primitive_pcollector);
1708 
1709  _vertices_other_pcollector.add_level(reader->get_num_vertices());
1710  _primitive_batches_other_pcollector.add_level(1);
1711 
1712  if (reader->is_indexed()) {
1713  int min_vertex = dx_broken_max_index ? 0 : reader->get_min_vertex();
1714  int max_vertex = reader->get_max_vertex();
1715 
1716  // Indexed, vbuffers.
1717  IndexBufferContext *ibc = ((GeomPrimitive *)(reader->get_object()))->prepare_now(get_prepared_objects(), this);
1718  nassertr(ibc != nullptr, false);
1719  if (!apply_index_buffer(ibc, reader, force)) {
1720  return false;
1721  }
1722 
1723  _d3d_device->DrawIndexedPrimitive( D3DPT_LINELIST,
1724  0,
1725  min_vertex, max_vertex - min_vertex + 1,
1726  0, reader->get_num_primitives() );
1727 
1728  #if 0
1729  // Indexed, client arrays.
1730  const unsigned char *index_pointer = reader->get_read_pointer(force);
1731  if (index_pointer == nullptr) {
1732  return false;
1733  }
1734  D3DFORMAT index_type = get_index_type(reader->get_index_type());
1735  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1736  if (vertex_pointer == nullptr) {
1737  return false;
1738  }
1739 
1740  draw_indexed_primitive_up
1741  (D3DPT_LINELIST,
1742  min_vertex, max_vertex,
1743  reader->get_num_primitives(),
1744  index_pointer, index_type, vertex_pointer,
1745  _data_reader->get_format()->get_array(0)->get_stride());
1746  #endif
1747  } else {
1748  // Nonindexed, vbuffers.
1749  _d3d_device->DrawPrimitive( D3DPT_LINELIST,
1750  reader->get_first_vertex(),
1751  reader->get_num_primitives() );
1752 
1753  #if 0
1754  // Nonindexed, client arrays.
1755  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1756  if (vertex_pointer == nullptr) {
1757  return false;
1758  }
1759  draw_primitive_up(D3DPT_LINELIST, reader->get_num_primitives(),
1760  reader->get_first_vertex(),
1761  reader->get_num_vertices(), vertex_pointer,
1762  _data_reader->get_format()->get_array(0)->get_stride());
1763  #endif
1764  }
1765  return true;
1766 }
1767 
1768 /**
1769  * Draws a series of line strips.
1770  */
1772 draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) {
1773  return false;
1774 }
1775 
1776 /**
1777  * Draws a series of disconnected points.
1778  */
1780 draw_points(const GeomPrimitivePipelineReader *reader, bool force) {
1781  // PStatTimer timer(_draw_primitive_pcollector);
1782 
1783  _vertices_other_pcollector.add_level(reader->get_num_vertices());
1784  _primitive_batches_other_pcollector.add_level(1);
1785 
1786  // The munger should have protected us from indexed points--DirectX doesn't
1787  // support them.
1788  nassertr(!reader->is_indexed(), false);
1789 
1790  // Nonindexed, vbuffers.
1791  _d3d_device->DrawPrimitive( D3DPT_POINTLIST,
1792  reader->get_first_vertex(),
1793  reader->get_num_primitives() );
1794 
1795  #if 0
1796  // Nonindexed, client arrays.
1797  const unsigned char *vertex_pointer = _data_reader->get_array_reader(0)->get_read_pointer(force);
1798  if (vertex_pointer == nullptr) {
1799  return false;
1800  }
1801  draw_primitive_up(D3DPT_POINTLIST, reader->get_num_primitives(),
1802  reader->get_first_vertex(),
1803  reader->get_num_vertices(), vertex_pointer,
1804  _data_reader->get_format()->get_array(0)->get_stride());
1805  #endif
1806 
1807  return true;
1808 }
1809 
1810 /**
1811  * Called after a sequence of draw_primitive() functions are called, this
1812  * should do whatever cleanup is appropriate.
1813  */
1816  // Turn off vertex blending--it seems to cause problems if we leave it on.
1817  if (_vertex_blending_enabled) {
1818  set_render_state(D3DRS_INDEXEDVERTEXBLENDENABLE, FALSE);
1819  set_render_state(D3DRS_VERTEXBLEND, D3DVBF_DISABLE);
1820  _vertex_blending_enabled = false;
1821  }
1822 
1823  if (_data_reader->is_vertex_transformed()) {
1824  // Restore the projection matrix that we wiped out above.
1825  LMatrix4f mat = LCAST(float, _projection_mat->get_mat());
1826  _d3d_device->SetTransform(D3DTS_PROJECTION,
1827  (D3DMATRIX*)mat.get_data());
1828  }
1829 
1831 }
1832 
1833 /**
1834  * Copy the pixels within the indicated display region from the framebuffer
1835  * into texture memory.
1836  *
1837  * If z > -1, it is the cube map index into which to copy.
1838  */
1840 framebuffer_copy_to_texture(Texture *tex, int view, int z,
1841  const DisplayRegion *dr, const RenderBuffer &rb) {
1842  set_read_buffer(rb);
1843 
1844  int orig_x = tex->get_x_size();
1845  int orig_y = tex->get_y_size();
1846 
1847  HRESULT hr;
1848  int xo, yo, w, h;
1849  dr->get_region_pixels_i(xo, yo, w, h);
1850  tex->set_size_padded(w, h);
1851 
1852  // must use a render target type texture for StretchRect
1853  tex->set_render_to_texture(true);
1854 
1855  TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
1856  if (tc == nullptr) {
1857  return false;
1858  }
1859  DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
1860  if (!dtc->create_texture(*_screen)) {
1861  // Oops, we can't re-create the texture for some reason.
1862  dxgsg9_cat.error()
1863  << "Unable to re-create texture " << *dtc->get_texture() << endl;
1864  return false;
1865  }
1866 
1867  if (tex->get_texture_type() != Texture::TT_2d_texture) {
1868  // For a specialty texture like a cube map, go the slow route through RAM
1869  // for now.
1870  return do_framebuffer_copy_to_ram(tex, view, z, dr, rb, true);
1871  }
1872  nassertr(dtc->get_d3d_2d_texture() != nullptr, false);
1873 
1874  IDirect3DSurface9 *tex_level_0;
1875  hr = dtc->get_d3d_2d_texture()->GetSurfaceLevel(0, &tex_level_0);
1876  if (FAILED(hr)) {
1877  dxgsg9_cat.error() << "GetSurfaceLev failed in copy_texture" << D3DERRORSTRING(hr);
1878  return false;
1879  }
1880 
1881  // If the texture is the wrong size, we need to do something about it.
1882  D3DSURFACE_DESC texdesc;
1883  hr = tex_level_0->GetDesc(&texdesc);
1884  if (FAILED(hr)) {
1885  dxgsg9_cat.error() << "GetDesc failed in copy_texture" << D3DERRORSTRING(hr);
1886  SAFE_RELEASE(tex_level_0);
1887  return false;
1888  }
1889  if ((texdesc.Width != tex->get_x_size())||(texdesc.Height != tex->get_y_size())) {
1890  if ((orig_x != tex->get_x_size()) || (orig_y != tex->get_y_size())) {
1891  // Texture might be wrong size because we resized it and need to
1892  // recreate.
1893  SAFE_RELEASE(tex_level_0);
1894  if (!dtc->create_texture(*_screen)) {
1895  // Oops, we can't re-create the texture for some reason.
1896  dxgsg9_cat.error()
1897  << "Unable to re-create texture " << *dtc->get_texture() << endl;
1898  return false;
1899  }
1900  hr = dtc->get_d3d_2d_texture()->GetSurfaceLevel(0, &tex_level_0);
1901  if (FAILED(hr)) {
1902  dxgsg9_cat.error() << "GetSurfaceLev failed in copy_texture" << D3DERRORSTRING(hr);
1903  return false;
1904  }
1905  hr = tex_level_0->GetDesc(&texdesc);
1906  if (FAILED(hr)) {
1907  dxgsg9_cat.error() << "GetDesc 2 failed in copy_texture" << D3DERRORSTRING(hr);
1908  SAFE_RELEASE(tex_level_0);
1909  return false;
1910  }
1911  }
1912  if ((texdesc.Width != tex->get_x_size())||(texdesc.Height != tex->get_y_size())) {
1913  // If it's still the wrong size, it's because driver can't create size
1914  // that we want. In that case, there's no helping it, we have to give
1915  // up.
1916  dxgsg9_cat.error()
1917  << "Unable to copy to texture, texture is wrong size: " << *dtc->get_texture() << endl;
1918  SAFE_RELEASE(tex_level_0);
1919  return false;
1920  }
1921  }
1922 
1923  DWORD render_target_index;
1924  IDirect3DSurface9 *render_target;
1925 
1926  /* ***** DX9 GetRenderTarget, assume only one render target so index = 0 */
1927  render_target_index = 0;
1928 
1929  hr = _d3d_device->GetRenderTarget(render_target_index, &render_target);
1930  if (FAILED(hr)) {
1931  dxgsg9_cat.error()
1932  << "GetRenderTarget failed in framebuffer_copy_to_texture"
1933  << D3DERRORSTRING(hr);
1934  SAFE_RELEASE(tex_level_0);
1935  return false;
1936  }
1937 
1938  RECT src_rect;
1939 
1940  src_rect.left = xo;
1941  src_rect.right = xo+w;
1942  src_rect.top = yo;
1943  src_rect.bottom = yo+h;
1944 
1945 // THE DX8 WAY hr = _d3d_device->CopyRects(render_target, &src_rect, 1,
1946 // tex_level_0, 0);
1947 
1948 // DX9
1949  D3DTEXTUREFILTERTYPE filter;
1950 
1951  filter = D3DTEXF_POINT;
1952 
1953  bool okflag = true;
1954  hr = _d3d_device->StretchRect(render_target, &src_rect,
1955  tex_level_0, &src_rect,
1956  filter);
1957  if (FAILED(hr)) {
1958  dxgsg9_cat.debug()
1959  << "StretchRect failed in framebuffer_copy_to_texture"
1960  << D3DERRORSTRING(hr);
1961  okflag = false;
1962  }
1963 
1964  SAFE_RELEASE(render_target);
1965  SAFE_RELEASE(tex_level_0);
1966 
1967  if (okflag) {
1968  dtc->mark_loaded();
1969  dtc->enqueue_lru(&_prepared_objects->_graphics_memory_lru);
1970 
1971  } else {
1972  // The copy failed. Fall back to copying it to RAM and back. Terribly
1973  // slow, but what are you going to do?
1974  return do_framebuffer_copy_to_ram(tex, view, z, dr, rb, true);
1975  }
1976 
1977  return true;
1978 }
1979 
1980 
1981 /**
1982  * Copy the pixels within the indicated display region from the framebuffer
1983  * into system memory, not texture memory. Returns true on success, false on
1984  * failure.
1985  *
1986  * This completely redefines the ram image of the indicated texture.
1987  */
1989 framebuffer_copy_to_ram(Texture *tex, int view, int z,
1990  const DisplayRegion *dr, const RenderBuffer &rb) {
1991  return do_framebuffer_copy_to_ram(tex, view, z, dr, rb, false);
1992 }
1993 
1994 /**
1995  * This is the implementation of framebuffer_copy_to_ram(); it adds one
1996  * additional parameter, which should be true if the framebuffer is to be
1997  * inverted during the copy (as in the same way it copies to texture memory).
1998  */
2000 do_framebuffer_copy_to_ram(Texture *tex, int view, int z,
2001  const DisplayRegion *dr, const RenderBuffer &rb,
2002  bool inverted) {
2003  set_read_buffer(rb);
2004 
2005  RECT rect;
2006  nassertr(tex != nullptr && dr != nullptr, false);
2007 
2008  int xo, yo, w, h;
2009  dr->get_region_pixels_i(xo, yo, w, h);
2010 
2011  Texture::Format format = tex->get_format();
2012  Texture::ComponentType component_type = tex->get_component_type();
2013 
2014  switch (format) {
2015  case Texture::F_depth_stencil:
2016  // Sorry, not (yet?) supported in pandadx.
2017  return false;
2018 
2019  default:
2020  format = Texture::F_rgb;
2021  component_type = Texture::T_unsigned_byte;
2022  }
2023 
2024  Texture::TextureType texture_type;
2025  if (z >= 0) {
2026  texture_type = Texture::TT_cube_map;
2027  } else {
2028  texture_type = Texture::TT_2d_texture;
2029  }
2030 
2031  if (tex->get_x_size() != w || tex->get_y_size() != h ||
2032  tex->get_component_type() != component_type ||
2033  tex->get_format() != format ||
2034  tex->get_texture_type() != texture_type) {
2035  // Re-setup the texture; its properties have changed.
2036  tex->setup_texture(texture_type, w, h, tex->get_z_size(),
2037  component_type, format);
2038  }
2039 
2040  rect.top = yo;
2041  rect.left = xo;
2042  rect.right = xo + w;
2043  rect.bottom = yo + h;
2044  bool copy_inverted = false;
2045 
2046  IDirect3DSurface9 *temp_surface = nullptr;
2047  HRESULT hr;
2048 
2049  // Note if you try to grab the backbuffer and full-screen anti-aliasing is
2050  // on, the backbuffer might be larger than the window size. For screenshots
2051  // it's safer to get the front buffer.
2052  if (_cur_read_pixel_buffer & RenderBuffer::T_back) {
2053  DWORD render_target_index;
2054  IDirect3DSurface9 *backbuffer = nullptr;
2055  // GetRenderTarget() seems to be a little more reliable than
2056  // GetBackBuffer(). Might just be related to the swap_chain thing.
2057 
2058  render_target_index = 0;
2059  hr = _d3d_device->GetRenderTarget(render_target_index, &backbuffer);
2060 
2061  if (FAILED(hr)) {
2062  dxgsg9_cat.error() << "GetRenderTarget failed" << D3DERRORSTRING(hr);
2063  return false;
2064  }
2065 
2066  // Since we might not be able to Lock the back buffer, we will need to
2067  // copy it to a temporary surface of the appropriate type first.
2068  D3DPOOL pool;
2069  D3DSURFACE_DESC surface_description;
2070 
2071  backbuffer -> GetDesc (&surface_description);
2072 
2073  pool = D3DPOOL_SYSTEMMEM;
2074  hr = _d3d_device->CreateOffscreenPlainSurface(
2075  surface_description.Width,
2076  surface_description.Height,
2077  surface_description.Format,
2078  pool,
2079  &temp_surface,
2080  nullptr);
2081  if (FAILED(hr)) {
2082  dxgsg9_cat.error()
2083  << "CreateImageSurface failed in copy_pixel_buffer()"
2084  << D3DERRORSTRING(hr);
2085  backbuffer->Release();
2086  return false;
2087  }
2088 
2089  // Now we must copy from the backbuffer to our temporary surface.
2090  hr = _d3d_device -> GetRenderTargetData (backbuffer, temp_surface);
2091  if (FAILED(hr)) {
2092  dxgsg9_cat.error() << "GetRenderTargetData failed" << D3DERRORSTRING(hr);
2093  temp_surface->Release();
2094  backbuffer->Release();
2095  return false;
2096  }
2097 
2098  copy_inverted = true;
2099 
2100  RELEASE(backbuffer, dxgsg9, "backbuffer", RELEASE_ONCE);
2101 
2102  } else if (_cur_read_pixel_buffer & RenderBuffer::T_front) {
2103 
2104  if (_screen->_presentation_params.Windowed) {
2105  // GetFrontBuffer() retrieves the entire desktop for a monitor, so we
2106  // need to reserve space for that.
2107 
2108  // We have to use GetMonitorInfo(), since this GSG may not be for the
2109  // primary monitor.
2110  MONITORINFO minfo;
2111  minfo.cbSize = sizeof(MONITORINFO);
2112  GetMonitorInfo(_screen->_monitor, &minfo);
2113 
2114  w = RECT_XSIZE(minfo.rcMonitor);
2115  h = RECT_YSIZE(minfo.rcMonitor);
2116 
2117  // set rect to client area of window in scrn coords
2118  ClientToScreen(_screen->_window, (POINT*)&rect.left);
2119  ClientToScreen(_screen->_window, (POINT*)&rect.right);
2120  }
2121 
2122  // For GetFrontBuffer(), we need a temporary surface of type A8R8G8B8.
2123  // Unlike GetBackBuffer(), GetFrontBuffer() implicitly performs a copy.
2124  hr = _d3d_device->CreateOffscreenPlainSurface(w, h, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &temp_surface, nullptr);
2125  if (FAILED(hr)) {
2126  dxgsg9_cat.error()
2127  << "CreateImageSurface failed in copy_pixel_buffer()"
2128  << D3DERRORSTRING(hr);
2129  return false;
2130  }
2131 
2132  UINT swap_chain;
2133 
2134  swap_chain = 0;
2135  hr = _d3d_device->GetFrontBufferData(swap_chain,temp_surface);
2136 
2137  if (hr == D3DERR_DEVICELOST) {
2138  dxgsg9_cat.error()
2139  << "copy_pixel_buffer failed: device lost\n";
2140  temp_surface->Release();
2141  return false;
2142  }
2143 
2144  copy_inverted = true;
2145 
2146  } else {
2147  dxgsg9_cat.error()
2148  << "copy_pixel_buffer: unhandled current_read_pixel_buffer type\n";
2149  temp_surface->Release();
2150  return false;
2151  }
2152 
2153  if (inverted) {
2154  copy_inverted = !copy_inverted;
2155  }
2156  DXTextureContext9::d3d_surface_to_texture(rect, temp_surface,
2157  copy_inverted, tex, view, z);
2158 
2159  RELEASE(temp_surface, dxgsg9, "temp_surface", RELEASE_ONCE);
2160 
2161  nassertr(tex->has_ram_image(), false);
2162  return true;
2163 }
2164 
2165 void DXGraphicsStateGuardian9::reset_render_states (void)
2166 {
2167  int index;
2168  int maximum_texture_stages;
2169 
2170  maximum_texture_stages = D3D_MAXTEXTURESTAGES;
2171 
2172  // set to invalid values so that the state will always be set the first time
2173  memset (_render_state_array, -1, sizeof (_render_state_array));
2174  memset (_texture_stage_states_array, -1, sizeof (_texture_stage_states_array));
2175 
2176  // states that may be set intially to -1 by the user, so set it to D3D's
2177  // default value
2178  _render_state_array [D3DRS_FOGCOLOR] = 0;
2179  _render_state_array [D3DRS_AMBIENT] = 0;
2180 
2181  // set to D3D default values or invalid values so that the state will always
2182  // be set the first time
2183  memset (_texture_render_states_array, 0, sizeof (_texture_render_states_array));
2184 
2185  // states that may be set intially to 0 by the user, so set it to D3D's
2186  // default value
2187  for (index = 0; index < MAXIMUM_TEXTURES; index++) {
2188  TextureRenderStates *texture_render_states;
2189 
2190  texture_render_states = &_texture_render_states_array [index];
2191  texture_render_states -> state_array [D3DSAMP_MAGFILTER] = D3DTEXF_POINT;
2192  texture_render_states -> state_array [D3DSAMP_MINFILTER] = D3DTEXF_POINT;
2193  texture_render_states -> state_array [D3DSAMP_MAXANISOTROPY] = 1;
2194  }
2195  _num_active_texture_stages = 0;
2196 
2197  set_render_state(D3DRS_NORMALIZENORMALS, false);
2198 
2199  _last_fvf = 0;
2200 }
2201 
2202 /**
2203  * Resets all internal state as if the gsg were newly created. The
2204  * GraphicsWindow pointer represents a typical window that might be used for
2205  * this context; it may be required to set up the frame buffer properly the
2206  * first time.
2207  */
2209 reset() {
2211 
2212  // Build _inv_state_mask as a mask of 1's where we don't care, and 0's where
2213  // we do care, about the state.
2214  _inv_state_mask.clear_bit(ShaderAttrib::get_class_slot());
2215  _inv_state_mask.clear_bit(AlphaTestAttrib::get_class_slot());
2216  _inv_state_mask.clear_bit(ClipPlaneAttrib::get_class_slot());
2217  _inv_state_mask.clear_bit(ColorAttrib::get_class_slot());
2218  _inv_state_mask.clear_bit(ColorScaleAttrib::get_class_slot());
2219  _inv_state_mask.clear_bit(CullFaceAttrib::get_class_slot());
2220  _inv_state_mask.clear_bit(DepthOffsetAttrib::get_class_slot());
2221  _inv_state_mask.clear_bit(DepthTestAttrib::get_class_slot());
2222  _inv_state_mask.clear_bit(DepthWriteAttrib::get_class_slot());
2223  _inv_state_mask.clear_bit(RenderModeAttrib::get_class_slot());
2224  _inv_state_mask.clear_bit(RescaleNormalAttrib::get_class_slot());
2225  _inv_state_mask.clear_bit(ShadeModelAttrib::get_class_slot());
2226  _inv_state_mask.clear_bit(TransparencyAttrib::get_class_slot());
2227  _inv_state_mask.clear_bit(ColorWriteAttrib::get_class_slot());
2228  _inv_state_mask.clear_bit(ColorBlendAttrib::get_class_slot());
2229  _inv_state_mask.clear_bit(TextureAttrib::get_class_slot());
2230  _inv_state_mask.clear_bit(TexGenAttrib::get_class_slot());
2231  _inv_state_mask.clear_bit(TexMatrixAttrib::get_class_slot());
2232  _inv_state_mask.clear_bit(MaterialAttrib::get_class_slot());
2233  _inv_state_mask.clear_bit(LightAttrib::get_class_slot());
2234  _inv_state_mask.clear_bit(StencilAttrib::get_class_slot());
2235  _inv_state_mask.clear_bit(FogAttrib::get_class_slot());
2236  _inv_state_mask.clear_bit(ScissorAttrib::get_class_slot());
2237 
2238  // D3DRS_POINTSPRITEENABLE doesn't seem to support remapping the texture
2239  // coordinates via a texture matrix, so we don't advertise
2240  // GR_point_sprite_tex_matrix.
2241  _supported_geom_rendering =
2242  Geom::GR_point | Geom::GR_point_uniform_size |
2243  Geom::GR_point_perspective | Geom::GR_point_sprite |
2244  Geom::GR_indexed_other |
2245  Geom::GR_triangle_strip | Geom::GR_triangle_fan |
2246  Geom::GR_flat_first_vertex |
2247  Geom::GR_render_mode_wireframe | Geom::GR_render_mode_point;
2248 
2249  // overwrite gsg defaults with these values
2250 
2251  HRESULT hr;
2252 
2253  // make sure gsg passes all current state down to us
2254  // set_state_and_transform(RenderState::make_empty(),
2255  // TransformState::make_identity()); want gsg to pass all state settings
2256  // down so any non-matching defaults we set here get overwritten
2257 
2258  nassertv(_screen->_d3d9 != nullptr);
2259 
2260  if (_d3d_device == nullptr) {
2261  return;
2262  }
2263 
2264  D3DCAPS9 d3d_caps;
2265  _d3d_device->GetDeviceCaps(&d3d_caps);
2266 
2267  _vertex_shader_version_major = D3DSHADER_VERSION_MAJOR (d3d_caps.VertexShaderVersion);
2268  _vertex_shader_version_minor = D3DSHADER_VERSION_MINOR (d3d_caps.VertexShaderVersion);
2269  _pixel_shader_version_major = D3DSHADER_VERSION_MAJOR (d3d_caps.PixelShaderVersion);
2270  _pixel_shader_version_minor = D3DSHADER_VERSION_MINOR (d3d_caps.PixelShaderVersion);
2271 
2272  _supports_hlsl = (_pixel_shader_version_major != 0);
2273 
2274  _vertex_shader_profile = (char *) D3DXGetVertexShaderProfile (_d3d_device);
2275  _pixel_shader_profile = (char *) D3DXGetPixelShaderProfile (_d3d_device);
2276 
2277  _vertex_shader_maximum_constants = d3d_caps.MaxVertexShaderConst;
2278 
2279  switch (_pixel_shader_version_major)
2280  {
2281  case 0:
2282  _shader_model = SM_00;
2283  break;
2284  case 1:
2285  _shader_model = SM_11;
2286  break;
2287  case 2:
2288  // minimim specification for pixel shader 2.0 is 96 instruction slots
2289  _shader_model = SM_20;
2290  if (d3d_caps.PS20Caps.NumInstructionSlots >= 512) {
2291  _shader_model = SM_2X;
2292  }
2293  break;
2294  case 3:
2295  _shader_model = SM_30;
2296  break;
2297  case 4:
2298  _shader_model = SM_40;
2299  break;
2300  case 5:
2301  default:
2302  _shader_model = SM_50;
2303  break;
2304  }
2305 
2306  _auto_detect_shader_model = _shader_model;
2307 
2308 #ifdef HAVE_CG
2309  set_cg_device(_d3d_device);
2310 
2311  _cg_context = cgCreateContext();
2312 
2313  if (cgD3D9IsProfileSupported(CG_PROFILE_PS_2_0) &&
2314  cgD3D9IsProfileSupported(CG_PROFILE_VS_2_0)) {
2315  _supports_basic_shaders = true;
2316  _shader_caps._active_vprofile = (int)cgD3D9GetLatestVertexProfile();
2317  _shader_caps._active_fprofile = (int)cgD3D9GetLatestPixelProfile();
2318  _shader_caps._ultimate_vprofile = (int)CG_PROFILE_VS_3_0;
2319  _shader_caps._ultimate_fprofile = (int)CG_PROFILE_PS_3_0;
2320 /*
2321  _shader_caps._active_vprofile = (int)CG_PROFILE_VS_2_0;
2322  _shader_caps._active_fprofile = (int)CG_PROFILE_PS_2_0;
2323  _shader_caps._ultimate_vprofile = (int)CG_PROFILE_VS_2_0;
2324  _shader_caps._ultimate_fprofile = (int)CG_PROFILE_PS_2_0;
2325 */
2326  }
2327 
2328  if (dxgsg9_cat.is_debug()) {
2329  CGprofile vertex_profile;
2330  CGprofile pixel_profile;
2331 
2332  vertex_profile = cgD3D9GetLatestVertexProfile();
2333  pixel_profile = cgD3D9GetLatestPixelProfile();
2334 
2335  const char *vertex_profile_str =
2336  cgGetProfileString(vertex_profile);
2337  const char *pixel_profile_str =
2338  cgGetProfileString(pixel_profile);
2339 
2340  if (vertex_profile_str == nullptr) {
2341  vertex_profile_str = "(null)";
2342  }
2343  if (pixel_profile_str == nullptr) {
2344  pixel_profile_str = "(null)";
2345  }
2346 
2347  dxgsg9_cat.debug()
2348  << "\nCg latest vertex profile = " << vertex_profile_str << " id = " << vertex_profile
2349  << "\nCg latest pixel profile = " << pixel_profile_str << " id = " << pixel_profile
2350  << "\nshader model = " << _shader_model
2351  << "\n";
2352  }
2353 #endif
2354 
2355  _supports_stream_offset = (d3d_caps.DevCaps2 & D3DDEVCAPS2_STREAMOFFSET) != 0;
2356  _screen->_supports_dynamic_textures = ((d3d_caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0);
2357  _screen->_supports_automatic_mipmap_generation = ((d3d_caps.Caps2 & D3DCAPS2_CANAUTOGENMIPMAP) != 0);
2358 
2359  if (support_stencil) {
2360  int min_stencil = D3DSTENCILCAPS_ZERO | D3DSTENCILCAPS_REPLACE | D3DSTENCILCAPS_INCR | D3DSTENCILCAPS_DECR;
2361  if ((d3d_caps.StencilCaps & min_stencil) == min_stencil) {
2362  if (dxgsg9_cat.is_debug()) {
2363  dxgsg9_cat.debug()
2364  << "Checking for stencil; mode = "
2365  << D3DFormatStr(_screen->_presentation_params.AutoDepthStencilFormat)
2366  << "\n";
2367  }
2368  switch (_screen->_presentation_params.AutoDepthStencilFormat) {
2369  // These are the only formats that support stencil.
2370  case D3DFMT_D15S1:
2371  case D3DFMT_D24S8:
2372  case D3DFMT_D24X4S4:
2373  _supports_stencil = true;
2374  if (dxgsg9_cat.is_debug()) {
2375  dxgsg9_cat.debug()
2376  << "Stencils supported.\n";
2377  }
2378  break;
2379 
2380  default:
2381  if (dxgsg9_cat.is_debug()) {
2382  dxgsg9_cat.debug()
2383  << "Stencils NOT supported.\n";
2384  }
2385  }
2386  }
2387  }
2388 
2389  _supports_stencil_wrap = (d3d_caps.StencilCaps & D3DSTENCILCAPS_INCR) && (d3d_caps.StencilCaps & D3DSTENCILCAPS_DECR);
2390  _supports_two_sided_stencil = ((d3d_caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED) != 0);
2391 
2392  _max_color_targets = d3d_caps.NumSimultaneousRTs;
2393 
2394  _supports_depth_bias = ((d3d_caps.RasterCaps & (D3DPRASTERCAPS_DEPTHBIAS | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS)) == (D3DPRASTERCAPS_DEPTHBIAS | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS));
2395 
2396  _supports_gamma_calibration = ((d3d_caps.Caps2 & D3DCAPS2_CANCALIBRATEGAMMA) != 0);
2397 
2398  // Test for occlusion query support
2399  hr = _d3d_device->CreateQuery(D3DQUERYTYPE_OCCLUSION, nullptr);
2400  _supports_occlusion_query = !FAILED(hr);
2401 
2402  if (dxgsg9_cat.is_error()) {
2403  dxgsg9_cat.debug()
2404  << "\nHwTransformAndLight = " << ((d3d_caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0)
2405  << "\nMaxTextureWidth = " << d3d_caps.MaxTextureWidth
2406  << "\nMaxTextureHeight = " << d3d_caps.MaxTextureHeight
2407  << "\nMaxVolumeExtent = " << d3d_caps.MaxVolumeExtent
2408  << "\nMaxTextureAspectRatio = " << d3d_caps.MaxTextureAspectRatio
2409  << "\nTexCoordCount = " << (d3d_caps.FVFCaps & D3DFVFCAPS_TEXCOORDCOUNTMASK)
2410  << "\nMaxTextureBlendStages = " << d3d_caps.MaxTextureBlendStages
2411  << "\nMaxSimultaneousTextures = " << d3d_caps.MaxSimultaneousTextures
2412  << "\nMaxActiveLights = " << d3d_caps.MaxActiveLights
2413  << "\nMaxUserClipPlanes = " << d3d_caps.MaxUserClipPlanes
2414  << "\nMaxVertexBlendMatrices = " << d3d_caps.MaxVertexBlendMatrices
2415  << "\nMaxVertexBlendMatrixIndex = " << d3d_caps.MaxVertexBlendMatrixIndex
2416  << "\nMaxPointSize = " << d3d_caps.MaxPointSize
2417  << "\nMaxPrimitiveCount = " << d3d_caps.MaxPrimitiveCount
2418  << "\nMaxVertexIndex = " << d3d_caps.MaxVertexIndex
2419  << "\nMaxStreams = " << d3d_caps.MaxStreams
2420  << "\nMaxStreamStride = " << d3d_caps.MaxStreamStride
2421  << "\nD3DTEXOPCAPS_MULTIPLYADD = " << ((d3d_caps.TextureOpCaps & D3DTEXOPCAPS_MULTIPLYADD) != 0)
2422  << "\nD3DTEXOPCAPS_LERP = " << ((d3d_caps.TextureOpCaps & D3DTEXOPCAPS_LERP) != 0)
2423  << "\nD3DPMISCCAPS_TSSARGTEMP = " << ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP) != 0)
2424  << "\nD3DPRASTERCAPS_DEPTHBIAS = " << ((d3d_caps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS) != 0)
2425  << "\nD3DPRASTERCAPS_SLOPESCALEDEPTHBIAS = " << ((d3d_caps.RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS) != 0)
2426  << "\nVertexShaderVersion = " << _vertex_shader_version_major << "." << _vertex_shader_version_minor
2427  << "\nPixelShaderVersion = " << _pixel_shader_version_major << "." << _pixel_shader_version_minor
2428  << "\nMaxVertexShaderConst = " << _vertex_shader_maximum_constants
2429  << "\nsupports_stream_offset = " << _supports_stream_offset
2430  << "\nsupports_dynamic_textures = " << _screen->_supports_dynamic_textures
2431  << "\nsupports_automatic_mipmap_generation = " << _screen->_supports_automatic_mipmap_generation
2432  << "\nsupports_stencil_wrap = " << _supports_stencil_wrap
2433  << "\nsupports_two_sided_stencil = " << _supports_two_sided_stencil
2434  << "\nsupports_occlusion_query = " << _supports_occlusion_query
2435  << "\nsupports_gamma_calibration = " << _supports_gamma_calibration
2436  << "\nMaxAnisotropy = " << d3d_caps.MaxAnisotropy
2437  << "\nNumSimultaneousRTs = " << d3d_caps.NumSimultaneousRTs
2438  << "\nD3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING = " << ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING) != 0)
2439  << "\nDirectX SDK version " DIRECTX_SDK_VERSION
2440  << "\n";
2441  }
2442 
2443  // OVERRIDE SUPPORT SINCE IT DOES NOT WORK WELL
2444  _screen->_supports_automatic_mipmap_generation = false;
2445 
2446  reset_render_states();
2447 
2448  _max_vertices_per_array = d3d_caps.MaxVertexIndex;
2449  _max_vertices_per_primitive = d3d_caps.MaxPrimitiveCount;
2450 
2451  _max_texture_stages = d3d_caps.MaxSimultaneousTextures;
2452 
2453  _max_texture_dimension = min(d3d_caps.MaxTextureWidth, d3d_caps.MaxTextureHeight);
2454 
2455  _supports_tex_non_pow2 = !(d3d_caps.TextureCaps & D3DPTEXTURECAPS_POW2);
2456 
2457  _supports_texture_combine = ((d3d_caps.TextureOpCaps & D3DTEXOPCAPS_LERP) != 0);
2458  _supports_texture_saved_result = ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP) != 0);
2459  _supports_texture_constant_color = ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_PERSTAGECONSTANT) != 0);
2460  _supports_texture_dot3 = true;
2461 
2462  if (_supports_texture_constant_color) {
2463  _constant_color_operand = D3DTA_CONSTANT;
2464  } else {
2465  _constant_color_operand = D3DTA_TFACTOR;
2466  }
2467 
2468  _screen->_managed_textures = _gsg_managed_textures;
2469  _screen->_managed_vertex_buffers = _gsg_managed_vertex_buffers;
2470  _screen->_managed_index_buffers = _gsg_managed_index_buffers;
2471 
2472  UINT available_texture_memory;
2473 
2474  available_texture_memory = _d3d_device->GetAvailableTextureMem ( );
2475  if (dxgsg9_cat.is_debug()) {
2476  dxgsg9_cat.debug() << "*** GetAvailableTextureMem = " << available_texture_memory << "\n";
2477  }
2478  _available_texture_memory = available_texture_memory;
2479 
2480  // check for render to texture support
2481  D3DDEVICE_CREATION_PARAMETERS creation_parameters;
2482 
2483  _supports_render_texture = false;
2484  _screen->_render_to_texture_d3d_format = D3DFMT_UNKNOWN;
2485  _screen->_framebuffer_d3d_format = D3DFMT_UNKNOWN;
2486 
2487  #define TOTAL_RENDER_TO_TEXTURE_FORMATS 3
2488 
2489  D3DFORMAT render_to_texture_formats [TOTAL_RENDER_TO_TEXTURE_FORMATS] =
2490  {
2491  D3DFMT_A8R8G8B8, // check for this format first
2492  D3DFMT_X8R8G8B8,
2493  D3DFMT_UNKNOWN, // place holder for _screen->_display_mode.Format
2494  };
2495 
2496  render_to_texture_formats [TOTAL_RENDER_TO_TEXTURE_FORMATS - 1] = _screen->_display_mode.Format;
2497 
2498  hr = _d3d_device->GetCreationParameters (&creation_parameters);
2499  if (SUCCEEDED (hr)) {
2500  _screen->_framebuffer_d3d_format = _screen->_display_mode.Format;
2501 
2502  int index;
2503  for (index = 0; index < TOTAL_RENDER_TO_TEXTURE_FORMATS; index++) {
2504  hr = _screen->_d3d9->CheckDeviceFormat (
2505  creation_parameters.AdapterOrdinal,
2506  creation_parameters.DeviceType,
2507  _screen->_display_mode.Format,
2508  D3DUSAGE_RENDERTARGET,
2509  D3DRTYPE_TEXTURE,
2510  render_to_texture_formats [index]);
2511  if (SUCCEEDED (hr)) {
2512  _screen->_render_to_texture_d3d_format = render_to_texture_formats [index];
2513  _supports_render_texture = true;
2514  }
2515  if (_supports_render_texture) {
2516  break;
2517  }
2518  }
2519  }
2520  if (dxgsg9_cat.is_debug()) {
2521  dxgsg9_cat.debug() << "Render to Texture Support = " << _supports_render_texture << "\n";
2522  }
2523 
2524  // override default config setting since it is really supported or not ???
2525  // support_render_texture = _supports_render_texture;
2526 
2527  _supports_3d_texture = ((d3d_caps.TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP) != 0);
2528  if (_supports_3d_texture) {
2529  _max_3d_texture_dimension = d3d_caps.MaxVolumeExtent;
2530  }
2531  _supports_cube_map = ((d3d_caps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP) != 0);
2532  if (_supports_cube_map) {
2533  _max_cube_map_dimension = _max_texture_dimension;
2534  }
2535 
2536  _max_lights = (int)d3d_caps.MaxActiveLights;
2537  _max_clip_planes = (int)d3d_caps.MaxUserClipPlanes;
2538  _max_vertex_transforms = d3d_caps.MaxVertexBlendMatrices;
2539  _max_vertex_transform_indices = d3d_caps.MaxVertexBlendMatrixIndex;
2540 
2541  set_render_state(D3DRS_AMBIENT, 0x0);
2542 
2543  _clip_plane_bits = 0;
2544  set_render_state(D3DRS_CLIPPLANEENABLE , 0x0);
2545 
2546  set_render_state(D3DRS_CLIPPING, true);
2547 
2548  set_render_state(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
2549 
2550  set_render_state(D3DRS_ZWRITEENABLE, TRUE);
2551 
2552 /* ***** DX9 ??? D3DRS_EDGEANTIALIAS NOT IN DX9 */
2553 // set_render_state(D3DRS_EDGEANTIALIAS, false);
2554 
2555  set_render_state(D3DRS_ZENABLE, D3DZB_FALSE);
2556 
2557  set_render_state(D3DRS_ALPHABLENDENABLE, FALSE);
2558 
2559  set_render_state(D3DRS_FOGENABLE, FALSE);
2560 
2561  _has_scene_graph_color = false;
2562 
2563  _last_testcooplevel_result = D3D_OK;
2564 
2565  if (dxgsg9_cat.is_debug()) {
2566  dxgsg9_cat.debug() << "Supported texture formats:\n";
2567  }
2568 
2569  for(int i = 0; i < MAX_POSSIBLE_TEXFMTS; i++) {
2570  // look for all possible DX9 texture fmts
2571  D3DFORMAT_FLAG fmtflag = D3DFORMAT_FLAG(1 << i);
2572  hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format,
2573  0x0, D3DRTYPE_TEXTURE, g_D3DFORMATmap[fmtflag]);
2574  if (SUCCEEDED(hr)) {
2575  if (dxgsg9_cat.is_debug()) {
2576  dxgsg9_cat.debug() << " " << D3DFormatStr(g_D3DFORMATmap[fmtflag]) << "\n";
2577  }
2578  _screen->_supported_tex_formats_mask |= fmtflag;
2579  }
2580  }
2581 
2582  _supports_depth_stencil = ((_screen->_supported_tex_formats_mask & D24S8_FLAG) != 0);
2583  _supports_depth_texture = _supports_depth_stencil ||
2584  (_screen->_supported_tex_formats_mask & D16_FLAG) != 0 ||
2585  (_screen->_supported_tex_formats_mask & D32_FLAG) != 0 ||
2586  (_screen->_supported_tex_formats_mask & D24X8_FLAG) != 0;
2587 
2588  // In DirectX 9, all built-in depth formats use shadow map filtering. Some
2589  // drivers (GeForce 8000+, Radeon HD 4000+, Intel G45+) expose a FourCC
2590  // format called "INTZ" that allows access to the actual depth. We don't
2591  // have a flag to indicate this, though.
2592  _supports_shadow_filter = _supports_depth_texture;
2593 
2594  // check if compressed textures are supported
2595  #define CHECK_COMPRESSED_FMT(mode, fmt) \
2596  if (_screen->_supported_tex_formats_mask & fmt##_FLAG) {\
2597  if (dxgsg9_cat.is_debug()) {\
2598  dxgsg9_cat.debug() << "Compressed texture format " << #fmt << " supported\n";\
2599  }\
2600  _supports_compressed_texture = true;\
2601  _compressed_texture_formats.set_bit(Texture::mode);\
2602  }
2603 
2604  if (_screen->_intel_compressed_texture_bug) {
2605  dxgsg9_cat.info()
2606  << "Buggy Intel driver detected; disabling compressed textures.\n";
2607  _screen->_supported_tex_formats_mask &=
2608  ~(DXT1_FLAG | DXT2_FLAG | DXT3_FLAG | DXT4_FLAG | DXT5_FLAG);
2609 
2610  } else {
2611  // Check for available compressed formats normally.
2612  CHECK_COMPRESSED_FMT(CM_dxt1, DXT1);
2613  CHECK_COMPRESSED_FMT(CM_dxt2, DXT2);
2614  CHECK_COMPRESSED_FMT(CM_dxt3, DXT3);
2615  CHECK_COMPRESSED_FMT(CM_dxt4, DXT4);
2616  CHECK_COMPRESSED_FMT(CM_dxt5, DXT5);
2617  CHECK_COMPRESSED_FMT(CM_rgtc, ATI1);
2618  CHECK_COMPRESSED_FMT(CM_rgtc, ATI2);
2619  }
2620 
2621  #undef CHECK_COMPRESSED_FMT
2622 
2623  _screen->_supports_rgba16f_texture_format = false;
2624  hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format, 0x0, D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F);
2625  if (SUCCEEDED(hr)){
2626  _screen->_supports_rgba16f_texture_format = true;
2627  }
2628  _screen->_supports_rgba32_texture_format = false;
2629  hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format, 0x0, D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F);
2630  if (SUCCEEDED(hr)){
2631  _screen->_supports_rgba32_texture_format = true;
2632  }
2633 
2634  // s3 virge drivers sometimes give crap values for these
2635  if (_screen->_d3dcaps.MaxTextureWidth == 0)
2636  _screen->_d3dcaps.MaxTextureWidth = 256;
2637 
2638  if (_screen->_d3dcaps.MaxTextureHeight == 0)
2639  _screen->_d3dcaps.MaxTextureHeight = 256;
2640 
2641  if (_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGTABLE) {
2642  // Watch out for drivers that emulate per-pixel fog with per-vertex fog
2643  // (Riva128, Matrox Millen G200). Some of these require gouraud-shading
2644  // to be set to work, as if you were using vertex fog
2645  _do_fog_type = PerPixelFog;
2646  } else {
2647  // every card is going to have vertex fog, since it's implemented in d3d
2648  // runtime.
2649  nassertv((_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGVERTEX) != 0);
2650 
2651  // vertex fog may look crappy if you have large polygons in the foreground
2652  // and they get clipped, so you may want to disable it
2653 
2654  if (dx_no_vertex_fog) {
2655  _do_fog_type = None;
2656  } else {
2657  _do_fog_type = PerVertexFog;
2658 
2659  // range-based fog only works with vertex fog in dx78
2660  if (dx_use_rangebased_fog && (_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGRANGE)) {
2661  set_render_state(D3DRS_RANGEFOGENABLE, true);
2662  }
2663  }
2664  }
2665 
2666  _screen->_can_direct_disable_color_writes = ((_screen->_d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) != 0);
2667 
2668  // Lighting, let's turn it off initially.
2669  set_render_state(D3DRS_LIGHTING, false);
2670 
2671  // turn on dithering if the rendertarget is < 8bitscolor channel
2672  bool dither_enabled = ((!dx_no_dithering) && IS_16BPP_DISPLAY_FORMAT(_screen->_presentation_params.BackBufferFormat)
2673  && (_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_DITHER));
2674  set_render_state(D3DRS_DITHERENABLE, dither_enabled);
2675 
2676  set_render_state(D3DRS_CLIPPING, true);
2677 
2678  // Stencil test is off by default
2679  set_render_state(D3DRS_STENCILENABLE, FALSE);
2680  if (_supports_two_sided_stencil) {
2681  set_render_state(D3DRS_TWOSIDEDSTENCILMODE, FALSE);
2682  }
2683 
2684  // Antialiasing.
2685 /* ***** DX9 ??? D3DRS_EDGEANTIALIAS NOT IN DX9 */
2686 // set_render_state(D3DRS_EDGEANTIALIAS, FALSE);
2687 
2688  _current_fill_mode = RenderModeAttrib::M_filled;
2689  set_render_state(D3DRS_FILLMODE, D3DFILL_SOLID);
2690 
2691  // must do SetTSS here because redundant states are filtered out by our code
2692  // based on current values above, so initial conditions must be correct
2693  set_texture_stage_state(0, D3DTSS_COLOROP, D3DTOP_DISABLE); // disables texturing
2694 
2695  _cull_face_mode = CullFaceAttrib::M_cull_none;
2696  set_render_state(D3DRS_CULLMODE, D3DCULL_NONE);
2697 
2698  set_render_state(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);
2699  set_render_state(D3DRS_ALPHAREF, 255);
2700  set_render_state(D3DRS_ALPHATESTENABLE, FALSE);
2701 
2702  // this is a new DX8 state that lets you do additional operations other than
2703  // ADD (e.g. subtractmaxmin) must check
2704  // (_screen->_d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_BLENDOP) (yes on
2705  // GF2Radeon8500, no on TNT)
2706  set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD);
2707 
2708  _current_shader = nullptr;
2709  _current_shader_context = nullptr;
2710  _vertex_array_shader = nullptr;
2711  _vertex_array_shader_context = nullptr;
2712  _texture_binding_shader = nullptr;
2713  _texture_binding_shader_context = nullptr;
2714 
2715  PRINT_REFCNT(dxgsg9, _d3d_device);
2716 
2717  // Now that the GSG has been initialized, make it available for
2718  // optimizations.
2719  add_gsg(this);
2720 }
2721 
2722 /**
2723  *
2724  */
2725 void DXGraphicsStateGuardian9::
2726 apply_fog(Fog *fog) {
2727  if (_do_fog_type == None)
2728  return;
2729 
2730  Fog::Mode panda_fogmode = fog->get_mode();
2731  D3DFOGMODE d3dfogmode = get_fog_mode_type(panda_fogmode);
2732 
2733  set_render_state((D3DRENDERSTATETYPE)_do_fog_type, d3dfogmode);
2734 
2735  const LColor &fog_colr = fog->get_color();
2736  set_render_state(D3DRS_FOGCOLOR,
2737  MY_D3DRGBA(fog_colr[0], fog_colr[1], fog_colr[2], 0.0f)); // Alpha bits are not used
2738 
2739  // do we need to adjust fog startend values based on
2740  // D3DPRASTERCAPS_WFOGD3DPRASTERCAPS_ZFOG ? if not WFOG, then docs say we
2741  // need to adjust values to range [0, 1]
2742 
2743  switch (panda_fogmode) {
2744  case Fog::M_linear:
2745  {
2746  PN_stdfloat onset, opaque;
2747  fog->get_linear_range(onset, opaque);
2748 
2749  set_render_state(D3DRS_FOGSTART,
2750  *((LPDWORD) (&onset)));
2751  set_render_state(D3DRS_FOGEND,
2752  *((LPDWORD) (&opaque)));
2753  }
2754  break;
2755  case Fog::M_exponential:
2756  case Fog::M_exponential_squared:
2757  {
2758  // Exponential fog is always camera-relative.
2759  PN_stdfloat fog_density = fog->get_exp_density();
2760  set_render_state(D3DRS_FOGDENSITY,
2761  *((LPDWORD) (&fog_density)));
2762  }
2763  break;
2764  }
2765 }
2766 
2767 /**
2768  * Sends the indicated transform matrix to the graphics API to be applied to
2769  * future vertices.
2770  *
2771  * This transform is the internal_transform, already converted into the GSG's
2772  * internal coordinate system.
2773  */
2774 void DXGraphicsStateGuardian9::
2775 do_issue_transform() {
2776  const TransformState *transform = _internal_transform;
2777  DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1));
2778 
2779  if (_current_shader_context) {
2780 // _current_shader_context->issue_transform(this);
2781  _current_shader_context->issue_parameters(this, Shader::SSD_transform);
2782 
2783 // ??? NO NEED TO SET THE D3D TRANSFORM VIA SetTransform SINCE THE TRANSFORM
2784 // IS ONLY USED IN THE SHADER
2785  LMatrix4f mat = LCAST(float, transform->get_mat());
2786  const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data();
2787  _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
2788 
2789  }
2790  else {
2791  LMatrix4f mat = LCAST(float, transform->get_mat());
2792  const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data();
2793  _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
2794 
2795 // DEBUG PRINT
2796 /*
2797  const PN_stdfloat *data;
2798  data = &d3d_mat -> _11;
2799  dxgsg9_cat.debug ( ) << "do_issue_transform\n" <<
2800  data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
2801  data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
2802  data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
2803  data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
2804 */
2805 
2806  }
2807 
2808  _transform_stale = false;
2809 }
2810 
2811 /**
2812  *
2813  */
2814 void DXGraphicsStateGuardian9::
2815 do_issue_alpha_test() {
2816  if (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test)) {
2817  set_render_state(D3DRS_ALPHATESTENABLE, FALSE);
2818  } else {
2819  const AlphaTestAttrib *target_alpha_test = DCAST(AlphaTestAttrib, _target_rs->get_attrib_def(AlphaTestAttrib::get_class_slot()));
2820  AlphaTestAttrib::PandaCompareFunc mode = target_alpha_test->get_mode();
2821  if (mode == AlphaTestAttrib::M_none) {
2822  set_render_state(D3DRS_ALPHATESTENABLE, FALSE);
2823  } else {
2824  // AlphaTestAttrib::PandaCompareFunc === D3DCMPFUNC
2825  set_render_state(D3DRS_ALPHAFUNC, (D3DCMPFUNC)mode);
2826  set_render_state(D3DRS_ALPHAREF, (UINT) (target_alpha_test->get_reference_alpha()*255.0f)); //d3d uses 0x0-0xFF, not a float
2827  set_render_state(D3DRS_ALPHATESTENABLE, TRUE);
2828  }
2829  }
2830 }
2831 
2832 /**
2833  *
2834  */
2835 void DXGraphicsStateGuardian9::
2836 do_issue_shader() {
2837 
2838  DXShaderContext9 *context = 0;
2839  Shader *shader = 0;
2840  if (_target_shader) {
2841  shader = (Shader *)(_target_shader->get_shader());
2842  }
2843  if (shader) {
2844  context = (DXShaderContext9 *)(shader->prepare_now(get_prepared_objects(), this));
2845  }
2846 
2847  if (context == 0 || (context && context -> valid (this) == false)) {
2848  if (_current_shader_context != 0) {
2849  _current_shader_context->unbind(this);
2850  _current_shader = 0;
2851  _current_shader_context = 0;
2852  disable_standard_texture_bindings();
2853  }
2854  return;
2855  }
2856 
2857  if (context != _current_shader_context) {
2858  // Use a completely different shader than before. Unbind old shader, bind
2859  // the new one.
2860  if (_current_shader_context != 0) {
2861  _current_shader_context->unbind(this);
2862  _current_shader_context = 0;
2863  _current_shader = 0;
2864  disable_standard_texture_bindings();
2865  }
2866  if (context != 0) {
2867  context->bind(this);
2868  _current_shader = shader;
2869  _current_shader_context = context;
2870  }
2871  } else {
2872  // Use the same shader as before, but with new input arguments.
2873  context->issue_parameters(this, Shader::SSD_shaderinputs);
2874  }
2875 }
2876 
2877 /**
2878  *
2879  */
2880 void DXGraphicsStateGuardian9::
2881 do_issue_render_mode() {
2882  const RenderModeAttrib *target_render_mode;
2883  _target_rs->get_attrib_def(target_render_mode);
2884  RenderModeAttrib::Mode mode = target_render_mode->get_mode();
2885 
2886  switch (mode) {
2887  case RenderModeAttrib::M_unchanged:
2888  case RenderModeAttrib::M_filled:
2889  case RenderModeAttrib::M_filled_flat:
2890  set_render_state(D3DRS_FILLMODE, D3DFILL_SOLID);
2891  break;
2892 
2893  case RenderModeAttrib::M_wireframe:
2894  set_render_state(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
2895  break;
2896 
2897  case RenderModeAttrib::M_point:
2898  set_render_state(D3DRS_FILLMODE, D3DFILL_POINT);
2899  break;
2900 
2901  default:
2902  dxgsg9_cat.error()
2903  << "Unknown render mode " << (int)mode << endl;
2904  }
2905 
2906  // This might also specify the point size.
2907  PN_stdfloat point_size = target_render_mode->get_thickness();
2908  set_render_state(D3DRS_POINTSIZE, *((DWORD*)&point_size));
2909 
2910  if (target_render_mode->get_perspective()) {
2911  set_render_state(D3DRS_POINTSCALEENABLE, TRUE);
2912 
2913  LVector3 height(0.0f, point_size, 1.0f);
2914  height = height * _projection_mat->get_mat();
2915  PN_stdfloat s = height[1] / point_size;
2916 
2917  PN_stdfloat zero = 0.0f;
2918  PN_stdfloat one_over_s2 = 1.0f / (s * s);
2919  set_render_state(D3DRS_POINTSCALE_A, *((DWORD*)&zero));
2920  set_render_state(D3DRS_POINTSCALE_B, *((DWORD*)&zero));
2921  set_render_state(D3DRS_POINTSCALE_C, *((DWORD*)&one_over_s2));
2922 
2923  } else {
2924  set_render_state(D3DRS_POINTSCALEENABLE, FALSE);
2925  }
2926 
2927  _current_fill_mode = mode;
2928 }
2929 
2930 /**
2931  *
2932  */
2933 void DXGraphicsStateGuardian9::
2934 do_issue_rescale_normal() {
2935  RescaleNormalAttrib::Mode mode = RescaleNormalAttrib::M_none;
2936 
2937  const RescaleNormalAttrib *target_rescale_normal;
2938  if (_target_rs->get_attrib(target_rescale_normal)) {
2939  mode = target_rescale_normal->get_mode();
2940  }
2941 
2942  switch (mode) {
2943  case RescaleNormalAttrib::M_none:
2944  set_render_state(D3DRS_NORMALIZENORMALS, false);
2945  break;
2946 
2947  case RescaleNormalAttrib::M_rescale:
2948  case RescaleNormalAttrib::M_normalize:
2949  set_render_state(D3DRS_NORMALIZENORMALS, true);
2950  break;
2951 
2952  default:
2953  dxgsg9_cat.error()
2954  << "Unknown rescale_normal mode " << (int)mode << endl;
2955  }
2956 }
2957 
2958 /**
2959  *
2960  */
2961 void DXGraphicsStateGuardian9::
2962 do_issue_depth_test() {
2963  const DepthTestAttrib *target_depth_test = DCAST(DepthTestAttrib, _target_rs->get_attrib_def(DepthTestAttrib::get_class_slot()));
2964  DepthTestAttrib::PandaCompareFunc mode = target_depth_test->get_mode();
2965  if (mode == DepthTestAttrib::M_none) {
2966  set_render_state(D3DRS_ZENABLE, D3DZB_FALSE);
2967  } else {
2968  set_render_state(D3DRS_ZENABLE, D3DZB_TRUE);
2969  set_render_state(D3DRS_ZFUNC, (D3DCMPFUNC) mode);
2970  }
2971 }
2972 
2973 /**
2974  *
2975  */
2976 void DXGraphicsStateGuardian9::
2977 do_issue_depth_write() {
2978  const DepthWriteAttrib *target_depth_write = DCAST(DepthWriteAttrib, _target_rs->get_attrib_def(DepthWriteAttrib::get_class_slot()));
2979  DepthWriteAttrib::Mode mode = target_depth_write->get_mode();
2980  if (mode == DepthWriteAttrib::M_on) {
2981  set_render_state(D3DRS_ZWRITEENABLE, TRUE);
2982  } else {
2983  set_render_state(D3DRS_ZWRITEENABLE, FALSE);
2984  }
2985 }
2986 
2987 /**
2988  *
2989  */
2990 void DXGraphicsStateGuardian9::
2991 do_issue_cull_face() {
2992  const CullFaceAttrib *target_cull_face = DCAST(CullFaceAttrib, _target_rs->get_attrib_def(CullFaceAttrib::get_class_slot()));
2993  _cull_face_mode = target_cull_face->get_effective_mode();
2994 
2995  switch (_cull_face_mode) {
2996  case CullFaceAttrib::M_cull_none:
2997  set_render_state(D3DRS_CULLMODE, D3DCULL_NONE);
2998 
2999 // printf ("------------------- D3DCULL_NONE\n");
3000 
3001  break;
3002  case CullFaceAttrib::M_cull_clockwise:
3003  set_render_state(D3DRS_CULLMODE, D3DCULL_CW);
3004 
3005 // printf ("------------------- D3DCULL_CW -- CLOCKWISE \n");
3006 
3007  break;
3008  case CullFaceAttrib::M_cull_counter_clockwise:
3009  set_render_state(D3DRS_CULLMODE, D3DCULL_CCW);
3010 
3011 // printf ("------------------- D3DCULL_CCW\n");
3012 
3013  break;
3014  default:
3015  dxgsg9_cat.error()
3016  << "invalid cull face mode " << (int)_cull_face_mode << endl;
3017  break;
3018  }
3019 }
3020 
3021 /**
3022  *
3023  */
3024 void DXGraphicsStateGuardian9::
3025 do_issue_fog() {
3026  const FogAttrib *target_fog = DCAST(FogAttrib, _target_rs->get_attrib_def(FogAttrib::get_class_slot()));
3027  if (!target_fog->is_off()) {
3028  set_render_state(D3DRS_FOGENABLE, TRUE);
3029  Fog *fog = target_fog->get_fog();
3030  nassertv(fog != nullptr);
3031  apply_fog(fog);
3032  } else {
3033  set_render_state(D3DRS_FOGENABLE, FALSE);
3034  }
3035 }
3036 
3037 /**
3038  *
3039  */
3040 void DXGraphicsStateGuardian9::
3041 do_issue_depth_offset() {
3042  const DepthOffsetAttrib *target_depth_offset = DCAST(DepthOffsetAttrib, _target_rs->get_attrib_def(DepthOffsetAttrib::get_class_slot()));
3043  int offset = target_depth_offset->get_offset();
3044 
3045  if (_supports_depth_bias && !dx_broken_depth_bias) {
3046  set_render_state(D3DRS_DEPTHBIAS, offset);
3047  set_render_state(D3DRS_SLOPESCALEDEPTHBIAS, offset);
3048 
3049  } else {
3050  // DirectX depth bias isn't directly supported by the driver. Cheese a
3051  // depth bias effect by sliding the viewport backward a bit.
3052  static const PN_stdfloat bias_scale = dx_depth_bias_scale;
3053  D3DVIEWPORT9 vp = _current_viewport;
3054  vp.MinZ -= bias_scale * offset;
3055  vp.MaxZ -= bias_scale * offset;
3056  _d3d_device->SetViewport(&vp);
3057  }
3058 }
3059 
3060 /**
3061  *
3062  */
3063 void DXGraphicsStateGuardian9::
3064 do_issue_shade_model() {
3065  const ShadeModelAttrib *target_shade_model = DCAST(ShadeModelAttrib, _target_rs->get_attrib_def(ShadeModelAttrib::get_class_slot()));
3066  switch (target_shade_model->get_mode()) {
3067  case ShadeModelAttrib::M_smooth:
3068  set_render_state(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
3069  break;
3070 
3071  case ShadeModelAttrib::M_flat:
3072  set_render_state(D3DRS_SHADEMODE, D3DSHADE_FLAT);
3073  break;
3074  }
3075 }
3076 
3077 /**
3078  * Simultaneously resets the render state and the transform state.
3079  *
3080  * This transform specified is the "internal" net transform, already converted
3081  * into the GSG's internal coordinate space by composing it to
3082  * get_cs_transform(). (Previously, this used to be the "external" net
3083  * transform, with the assumption that that GSG would convert it internally,
3084  * but that is no longer the case.)
3085  *
3086  * Special case: if (state==NULL), then the target state is already stored in
3087  * _target.
3088  */
3090 set_state_and_transform(const RenderState *target,
3091  const TransformState *transform) {
3092 #ifndef NDEBUG
3093  if (gsg_cat.is_spam()) {
3094  gsg_cat.spam() << "Setting GSG state to " << (void *)target << ":\n";
3095  target->write(gsg_cat.spam(false), 2);
3096  }
3097 #endif
3098  _state_pcollector.add_level(1);
3099  PStatTimer timer1(_draw_set_state_pcollector);
3100 
3101  if (transform != _internal_transform) {
3102  // PStatTimer timer(_draw_set_state_transform_pcollector);
3103  _state_pcollector.add_level(1);
3104  _internal_transform = transform;
3105  do_issue_transform();
3106  }
3107 
3108  if (target == _state_rs && (_state_mask | _inv_state_mask).is_all_on()) {
3109  return;
3110  }
3111  _target_rs = target;
3112 
3113  determine_target_shader();
3114 
3115  int alpha_test_slot = AlphaTestAttrib::get_class_slot();
3116  if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) ||
3117  !_state_mask.get_bit(alpha_test_slot)) {
3118  // PStatTimer timer(_draw_set_state_alpha_test_pcollector);
3119  do_issue_alpha_test();
3120  _state_mask.set_bit(alpha_test_slot);
3121  }
3122 
3123  int clip_plane_slot = ClipPlaneAttrib::get_class_slot();
3124  if (_target_rs->get_attrib(clip_plane_slot) != _state_rs->get_attrib(clip_plane_slot) ||
3125  !_state_mask.get_bit(clip_plane_slot)) {
3126  // PStatTimer timer(_draw_set_state_clip_plane_pcollector);
3127  do_issue_clip_plane();
3128  _state_mask.set_bit(clip_plane_slot);
3129  }
3130 
3131  int color_slot = ColorAttrib::get_class_slot();
3132  int color_scale_slot = ColorScaleAttrib::get_class_slot();
3133  if (_target_rs->get_attrib(color_slot) != _state_rs->get_attrib(color_slot) ||
3134  _target_rs->get_attrib(color_scale_slot) != _state_rs->get_attrib(color_scale_slot) ||
3135  !_state_mask.get_bit(color_slot) ||
3136  !_state_mask.get_bit(color_scale_slot)) {
3137  // PStatTimer timer(_draw_set_state_color_pcollector);
3138  do_issue_color();
3139  do_issue_color_scale();
3140  _state_mask.set_bit(color_slot);
3141  _state_mask.set_bit(color_scale_slot);
3142  if (_current_shader_context) {
3143  _current_shader_context->issue_parameters(this, Shader::SSD_color);
3144  _current_shader_context->issue_parameters(this, Shader::SSD_colorscale);
3145  }
3146  }
3147 
3148  int cull_face_slot = CullFaceAttrib::get_class_slot();
3149  if (_target_rs->get_attrib(cull_face_slot) != _state_rs->get_attrib(cull_face_slot) ||
3150  !_state_mask.get_bit(cull_face_slot)) {
3151  // PStatTimer timer(_draw_set_state_cull_face_pcollector);
3152  do_issue_cull_face();
3153  _state_mask.set_bit(cull_face_slot);
3154  }
3155 
3156  int depth_offset_slot = DepthOffsetAttrib::get_class_slot();
3157  if (_target_rs->get_attrib(depth_offset_slot) != _state_rs->get_attrib(depth_offset_slot) ||
3158  !_state_mask.get_bit(depth_offset_slot)) {
3159  // PStatTimer timer(_draw_set_state_depth_offset_pcollector);
3160  do_issue_depth_offset();
3161  _state_mask.set_bit(depth_offset_slot);
3162  }
3163 
3164  int depth_test_slot = DepthTestAttrib::get_class_slot();
3165  if (_target_rs->get_attrib(depth_test_slot) != _state_rs->get_attrib(depth_test_slot) ||
3166  !_state_mask.get_bit(depth_test_slot)) {
3167  // PStatTimer timer(_draw_set_state_depth_test_pcollector);
3168  do_issue_depth_test();
3169  _state_mask.set_bit(depth_test_slot);
3170  }
3171 
3172  int depth_write_slot = DepthWriteAttrib::get_class_slot();
3173  if (_target_rs->get_attrib(depth_write_slot) != _state_rs->get_attrib(depth_write_slot) ||
3174  !_state_mask.get_bit(depth_write_slot)) {
3175  // PStatTimer timer(_draw_set_state_depth_write_pcollector);
3176  do_issue_depth_write();
3177  _state_mask.set_bit(depth_write_slot);
3178  }
3179 
3180  int render_mode_slot = RenderModeAttrib::get_class_slot();
3181  if (_target_rs->get_attrib(render_mode_slot) != _state_rs->get_attrib(render_mode_slot) ||
3182  !_state_mask.get_bit(render_mode_slot)) {
3183  // PStatTimer timer(_draw_set_state_render_mode_pcollector);
3184  do_issue_render_mode();
3185  _state_mask.set_bit(render_mode_slot);
3186  }
3187 
3188  int rescale_normal_slot = RescaleNormalAttrib::get_class_slot();
3189  if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) ||
3190  !_state_mask.get_bit(rescale_normal_slot)) {
3191  // PStatTimer timer(_draw_set_state_rescale_normal_pcollector);
3192  do_issue_rescale_normal();
3193  _state_mask.set_bit(rescale_normal_slot);
3194  }
3195 
3196  int shade_model_slot = ShadeModelAttrib::get_class_slot();
3197  if (_target_rs->get_attrib(shade_model_slot) != _state_rs->get_attrib(shade_model_slot) ||
3198  !_state_mask.get_bit(shade_model_slot)) {
3199  // PStatTimer timer(_draw_set_state_shade_model_pcollector);
3200  do_issue_shade_model();
3201  _state_mask.set_bit(shade_model_slot);
3202  }
3203 
3204  int transparency_slot = TransparencyAttrib::get_class_slot();
3205  int color_write_slot = ColorWriteAttrib::get_class_slot();
3206  int color_blend_slot = ColorBlendAttrib::get_class_slot();
3207  if (_target_rs->get_attrib(transparency_slot) != _state_rs->get_attrib(transparency_slot) ||
3208  _target_rs->get_attrib(color_write_slot) != _state_rs->get_attrib(color_write_slot) ||
3209  _target_rs->get_attrib(color_blend_slot) != _state_rs->get_attrib(color_blend_slot) ||
3210  !_state_mask.get_bit(transparency_slot) ||
3211  !_state_mask.get_bit(color_write_slot) ||
3212  !_state_mask.get_bit(color_blend_slot) ||
3213  (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write) !=
3214  _state_shader->get_flag(ShaderAttrib::F_disable_alpha_write))) {
3215  // PStatTimer timer(_draw_set_state_blending_pcollector);
3216  do_issue_blending();
3217  _state_mask.set_bit(transparency_slot);
3218  _state_mask.set_bit(color_write_slot);
3219  _state_mask.set_bit(color_blend_slot);
3220  }
3221 
3222  if (_target_shader != _state_shader) {
3223  // PStatTimer timer(_draw_set_state_shader_pcollector);
3224  do_issue_shader();
3225  _state_shader = _target_shader;
3226  _state_mask.clear_bit(TextureAttrib::get_class_slot());
3227  }
3228 
3229  int texture_slot = TextureAttrib::get_class_slot();
3230  int tex_matrix_slot = TexMatrixAttrib::get_class_slot();
3231  int tex_gen_slot = TexGenAttrib::get_class_slot();
3232  if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
3233  _target_rs->get_attrib(tex_matrix_slot) != _state_rs->get_attrib(tex_matrix_slot) ||
3234  _target_rs->get_attrib(tex_gen_slot) != _state_rs->get_attrib(tex_gen_slot) ||
3235  !_state_mask.get_bit(texture_slot) ||
3236  !_state_mask.get_bit(tex_matrix_slot) ||
3237  !_state_mask.get_bit(tex_gen_slot)) {
3238  // PStatTimer timer(_draw_set_state_texture_pcollector);
3239  determine_target_texture();
3240  do_issue_texture();
3241 
3242  _state_texture = _target_texture;
3243  _state_mask.set_bit(texture_slot);
3244  _state_mask.set_bit(tex_matrix_slot);
3245  _state_mask.set_bit(tex_gen_slot);
3246  }
3247 
3248  int material_slot = MaterialAttrib::get_class_slot();
3249  if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) ||
3250  !_state_mask.get_bit(material_slot)) {
3251  // PStatTimer timer(_draw_set_state_material_pcollector);
3252  do_issue_material();
3253  _state_mask.set_bit(material_slot);
3254  if (_current_shader_context) {
3255  _current_shader_context->issue_parameters(this, Shader::SSD_material);
3256  }
3257  }
3258 
3259  int light_slot = LightAttrib::get_class_slot();
3260  if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) ||
3261  !_state_mask.get_bit(light_slot)) {
3262  // PStatTimer timer(_draw_set_state_light_pcollector);
3263  do_issue_light();
3264  _state_mask.set_bit(light_slot);
3265  }
3266 
3267  int stencil_slot = StencilAttrib::get_class_slot();
3268  if (_target_rs->get_attrib(stencil_slot) != _state_rs->get_attrib(stencil_slot) ||
3269  !_state_mask.get_bit(stencil_slot)) {
3270  // PStatTimer timer(_draw_set_state_stencil_pcollector);
3271  do_issue_stencil();
3272  _state_mask.set_bit(stencil_slot);
3273  }
3274 
3275  int fog_slot = FogAttrib::get_class_slot();
3276  if (_target_rs->get_attrib(fog_slot) != _state_rs->get_attrib(fog_slot) ||
3277  !_state_mask.get_bit(fog_slot)) {
3278  // PStatTimer timer(_draw_set_state_fog_pcollector);
3279  do_issue_fog();
3280  _state_mask.set_bit(fog_slot);
3281  if (_current_shader_context) {
3282  _current_shader_context->issue_parameters(this, Shader::SSD_fog);
3283  }
3284  }
3285 
3286  int scissor_slot = ScissorAttrib::get_class_slot();
3287  if (_target_rs->get_attrib(scissor_slot) != _state_rs->get_attrib(scissor_slot) ||
3288  !_state_mask.get_bit(scissor_slot)) {
3289  // PStatTimer timer(_draw_set_state_scissor_pcollector);
3290  do_issue_scissor();
3291  _state_mask.set_bit(scissor_slot);
3292  }
3293 
3294  _state_rs = _target_rs;
3295 }
3296 
3297 /**
3298  * Called the first time a particular light has been bound to a given id
3299  * within a frame, this should set up the associated hardware light with the
3300  * light's properties.
3301  */
3303 bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
3304  // Get the light in "world coordinates" (actually, view coordinates). This
3305  // means the light in the coordinate space of the camera, converted to DX's
3306  // coordinate system.
3307  CPT(TransformState) transform = light.get_transform(_scene_setup->get_camera_path());
3308  const LMatrix4 &light_mat = transform->get_mat();
3309  LMatrix4 rel_mat = light_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
3310  LPoint3f pos = LCAST(float, light_obj->get_point() * rel_mat);
3311 
3312  D3DCOLORVALUE black;
3313  black.r = black.g = black.b = black.a = 0.0f;
3314  D3DLIGHT9 alight;
3315  alight.Type = D3DLIGHT_POINT;
3316  alight.Diffuse = get_light_color(light_obj);
3317  alight.Ambient = black ;
3318  LColorf color = LCAST(float, light_obj->get_specular_color());
3319  alight.Specular = *(D3DCOLORVALUE *)(color.get_data());
3320 
3321  // Position needs to specify x, y, z, and w w == 1 implies non-infinite
3322  // position
3323  alight.Position = *(D3DVECTOR *)pos.get_data();
3324 
3325  alight.Range = __D3DLIGHT_RANGE_MAX;
3326  alight.Falloff = 1.0f;
3327 
3328  const LVecBase3 &att = light_obj->get_attenuation();
3329  alight.Attenuation0 = att[0];
3330  alight.Attenuation1 = att[1];
3331  alight.Attenuation2 = att[2];
3332 
3333  HRESULT hr = _d3d_device->SetLight(light_id, &alight);
3334  if (FAILED(hr)) {
3335  wdxdisplay9_cat.warning()
3336  << "Could not set light properties for " << light
3337  << " to id " << light_id << ": " << D3DERRORSTRING(hr) << "\n";
3338  }
3339 }
3340 
3341 /**
3342  * Called the first time a particular light has been bound to a given id
3343  * within a frame, this should set up the associated hardware light with the
3344  * light's properties.
3345  */
3347 bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
3348  static PStatCollector _draw_set_state_light_bind_directional_pcollector("Draw:Set State:Light:Bind:Directional");
3349  // PStatTimer timer(_draw_set_state_light_bind_directional_pcollector);
3350 
3351  std::pair<DirectionalLights::iterator, bool> lookup = _dlights.insert(DirectionalLights::value_type(light, D3DLIGHT9()));
3352  D3DLIGHT9 &fdata = (*lookup.first).second;
3353  if (lookup.second) {
3354  // Get the light in "world coordinates" (actually, view coordinates).
3355  // This means the light in the coordinate space of the camera, converted
3356  // to DX's coordinate system.
3357  CPT(TransformState) transform = light.get_transform(_scene_setup->get_camera_path());
3358  const LMatrix4 &light_mat = transform->get_mat();
3359  LMatrix4 rel_mat = light_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
3360  LVector3f dir = LCAST(float, light_obj->get_direction() * rel_mat);
3361 
3362  D3DCOLORVALUE black;
3363  black.r = black.g = black.b = black.a = 0.0f;
3364 
3365  ZeroMemory(&fdata, sizeof(D3DLIGHT9));
3366 
3367  fdata.Type = D3DLIGHT_DIRECTIONAL;
3368  fdata.Ambient = black ;
3369  LColorf color = LCAST(float, light_obj->get_specular_color());
3370  fdata.Specular = *(D3DCOLORVALUE *)(color.get_data());
3371 
3372  fdata.Direction = *(D3DVECTOR *)dir.get_data();
3373 
3374  fdata.Range = __D3DLIGHT_RANGE_MAX;
3375  fdata.Falloff = 1.0f;
3376 
3377  fdata.Attenuation0 = 1.0f; // constant
3378  fdata.Attenuation1 = 0.0f; // linear
3379  fdata.Attenuation2 = 0.0f; // quadratic
3380  }
3381 
3382  // We have to reset the Diffuse color at each call, because it might have
3383  // changed independently of the light object itself (due to
3384  // color_scale_via_lighting being in effect).
3385  fdata.Diffuse = get_light_color(light_obj);
3386 
3387  HRESULT hr = _d3d_device->SetLight(light_id, &fdata);
3388  if (FAILED(hr)) {
3389  wdxdisplay9_cat.warning()
3390  << "Could not set light properties for " << light
3391  << " to id " << light_id << ": " << D3DERRORSTRING(hr) << "\n";
3392  }
3393 }
3394 
3395 /**
3396  * Called the first time a particular light has been bound to a given id
3397  * within a frame, this should set up the associated hardware light with the
3398  * light's properties.
3399  */
3401 bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
3402  Lens *lens = light_obj->get_lens();
3403  nassertv(lens != nullptr);
3404 
3405  // Get the light in "world coordinates" (actually, view coordinates). This
3406  // means the light in the coordinate space of the camera, converted to DX's
3407  // coordinate system.
3408  CPT(TransformState) transform = light.get_transform(_scene_setup->get_camera_path());
3409  const LMatrix4 &light_mat = transform->get_mat();
3410  LMatrix4 rel_mat = light_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
3411  LPoint3f pos = LCAST(float, lens->get_nodal_point() * rel_mat);
3412  LVector3f dir = LCAST(float, lens->get_view_vector() * rel_mat);
3413 
3414  D3DCOLORVALUE black;
3415  black.r = black.g = black.b = black.a = 0.0f;
3416 
3417  D3DLIGHT9 alight;
3418  ZeroMemory(&alight, sizeof(D3DLIGHT9));
3419 
3420  alight.Type = D3DLIGHT_SPOT;
3421  alight.Ambient = black ;
3422  alight.Diffuse = get_light_color(light_obj);
3423  LColorf color = LCAST(float, light_obj->get_specular_color());
3424  alight.Specular = *(D3DCOLORVALUE *)(color.get_data());
3425 
3426  alight.Position = *(D3DVECTOR *)pos.get_data();
3427 
3428  alight.Direction = *(D3DVECTOR *)dir.get_data();
3429 
3430  alight.Range = __D3DLIGHT_RANGE_MAX;
3431 
3432  // I determined this formular empirically. It seems to mostly approximate
3433  // the OpenGL spotlight equation, for a reasonable range of values for FOV.
3434  PN_stdfloat fov = lens->get_hfov();
3435  alight.Falloff = light_obj->get_exponent() * (fov * fov * fov) / 1620000.0f;
3436 
3437  alight.Theta = 0.0f;
3438  alight.Phi = deg_2_rad(fov);
3439 
3440  const LVecBase3 &att = light_obj->get_attenuation();
3441  alight.Attenuation0 = att[0];
3442  alight.Attenuation1 = att[1];
3443  alight.Attenuation2 = att[2];
3444 
3445  HRESULT hr = _d3d_device->SetLight(light_id, &alight);
3446  if (FAILED(hr)) {
3447  wdxdisplay9_cat.warning()
3448  << "Could not set light properties for " << light
3449  << " to id " << light_id << ": " << D3DERRORSTRING(hr) << "\n";
3450  }
3451 }
3452 
3453 /**
3454  * Maps from the Geom's internal numeric type symbols to DirectX's.
3455  */
3457 get_index_type(Geom::NumericType numeric_type) {
3458  switch (numeric_type) {
3459  // NT_uint8 is automatically promoted to uint16.
3460  case Geom::NT_uint8:
3461  case Geom::NT_uint16:
3462  return D3DFMT_INDEX16;
3463 
3464  case Geom::NT_uint32:
3465  return D3DFMT_INDEX32;
3466  }
3467 
3468  dxgsg9_cat.error()
3469  << "Invalid index NumericType value (" << (int)numeric_type << ")\n";
3470  return D3DFMT_INDEX16;
3471 }
3472 
3473 /**
3474  *
3475  */
3476 void DXGraphicsStateGuardian9::
3477 do_issue_material() {
3478  static Material empty;
3479  const Material *material;
3480  const MaterialAttrib *target_material = DCAST(MaterialAttrib, _target_rs->get_attrib_def(MaterialAttrib::get_class_slot()));
3481  if (target_material->is_off()) {
3482  material = &empty;
3483  } else {
3484  material = target_material->get_material();
3485  }
3486 
3487  D3DMATERIAL9 cur_material;
3488  LColorf color = LCAST(float, material->get_diffuse());
3489  cur_material.Diffuse = *(D3DCOLORVALUE *)(color.get_data());
3490  color = LCAST(float, material->get_ambient());
3491  cur_material.Ambient = *(D3DCOLORVALUE *)(color.get_data());
3492  color = LCAST(float, material->get_specular());
3493  cur_material.Specular = *(D3DCOLORVALUE *)(color.get_data());
3494  color = LCAST(float, material->get_emission());
3495  cur_material.Emissive = *(D3DCOLORVALUE *)(color.get_data());
3496  cur_material.Power = material->get_shininess();
3497 
3498  if (material->has_diffuse() || material->has_base_color()) {
3499  // If the material specifies an diffuse color, use it.
3500  set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
3501  } else {
3502  // Otherwise, the diffuse color comes from the object color.
3503  if (_has_material_force_color) {
3504  color = LCAST(float, _material_force_color);
3505  cur_material.Diffuse = *(D3DCOLORVALUE *)color.get_data();
3506  set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
3507  } else {
3508  set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
3509  }
3510  }
3511  if (material->has_ambient() || material->has_base_color()) {
3512  // If the material specifies an ambient color, use it.
3513  set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
3514  } else {
3515  // Otherwise, the ambient color comes from the object color.
3516  if (_has_material_force_color) {
3517  color = LCAST(float, _material_force_color);
3518  cur_material.Ambient = *(D3DCOLORVALUE *)color.get_data();
3519  set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
3520  } else {
3521  set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1);
3522  }
3523  }
3524 
3525  if (material->has_specular() || material->has_base_color()) {
3526  set_render_state(D3DRS_SPECULARENABLE, TRUE);
3527  } else {
3528  set_render_state(D3DRS_SPECULARENABLE, FALSE);
3529  }
3530 
3531  if (material->get_local()) {
3532  set_render_state(D3DRS_LOCALVIEWER, TRUE);
3533  } else {
3534  set_render_state(D3DRS_LOCALVIEWER, FALSE);
3535  }
3536 
3537  _d3d_device->SetMaterial(&cur_material);
3538 }
3539 
3540 /**
3541  *
3542  */
3543 void DXGraphicsStateGuardian9::
3544 do_issue_texture() {
3545  DO_PSTATS_STUFF(_texture_state_pcollector.add_level(1));
3546 
3547  if (_texture_binding_shader_context==0) {
3548  if (_current_shader_context==0) {
3549  update_standard_texture_bindings();
3550  } else {
3551  disable_standard_texture_bindings();
3552  _current_shader_context->update_shader_texture_bindings(nullptr,this);
3553  }
3554  } else {
3555  if (_current_shader_context==0) {
3556  _texture_binding_shader_context->disable_shader_texture_bindings(this);
3557  update_standard_texture_bindings();
3558  } else {
3559  _current_shader_context->
3560  update_shader_texture_bindings(_texture_binding_shader_context,this);
3561  }
3562  }
3563  _texture_binding_shader = _current_shader;
3564  _texture_binding_shader_context = _current_shader_context;
3565 }
3566 
3567 /**
3568  *
3569  */
3570 void DXGraphicsStateGuardian9::
3571 disable_standard_texture_bindings() {
3572  // Disable the texture stages that are no longer used.
3573  for (int i = 0; i < _num_active_texture_stages; i++) {
3574  HRESULT hr;
3575 
3576  hr = _d3d_device -> SetTexture (i, nullptr);
3577  if (FAILED (hr)) {
3578  dxgsg9_cat.error()
3579  << "SetTexture ("
3580  << i
3581  << ", NULL) failed "
3582  << D3DERRORSTRING(hr);
3583  }
3584  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
3585  }
3586 
3587  _num_active_texture_stages = 0;
3588 }
3589 
3590 /**
3591  *
3592  */
3593 void DXGraphicsStateGuardian9::
3594 update_standard_texture_bindings() {
3595  DO_PSTATS_STUFF(_texture_state_pcollector.add_level(1));
3596 
3597  int num_stages = _target_texture->get_num_on_ff_stages();
3598  int num_old_stages = _max_texture_stages;
3599  if (_state_texture != nullptr) {
3600  num_old_stages = _state_texture->get_num_on_ff_stages();
3601  }
3602 
3603  nassertv(num_stages <= _max_texture_stages &&
3604  _num_active_texture_stages <= _max_texture_stages);
3605 
3606  _texture_involves_color_scale = false;
3607 
3608  // We have to match up the texcoord stage index to the order written out by
3609  // the DXGeomMunger. This means the texcoord names are written in the order
3610  // indicated by the TextureAttrib.
3611 
3612  int si;
3613  for (si = 0; si < num_stages; si++) {
3614  TextureStage *stage = _target_texture->get_on_ff_stage(si);
3615  int texcoord_index = _target_texture->get_ff_tc_index(si);
3616 
3617  Texture *texture = _target_texture->get_on_texture(stage);
3618  nassertv(texture != nullptr);
3619  const SamplerState &sampler = _target_texture->get_on_sampler(stage);
3620 
3621  // We always reissue every stage in DX, just in case the texcoord index or
3622  // texgen mode or some other property has changed.
3623  int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
3624  TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
3625  apply_texture(si, tc, sampler);
3626  set_texture_blend_mode(si, stage);
3627 
3628  int texcoord_dimensions = 2;
3629 
3630  CPT(TransformState) tex_mat = TransformState::make_identity();
3631  const TexMatrixAttrib *target_tex_matrix = DCAST(TexMatrixAttrib, _target_rs->get_attrib_def(TexMatrixAttrib::get_class_slot()));
3632  if (target_tex_matrix->has_stage(stage)) {
3633  tex_mat = target_tex_matrix->get_transform(stage);
3634  }
3635 
3636  // Issue the texgen mode.
3637  TexGenAttrib::Mode mode = _target_tex_gen->get_mode(stage);
3638  bool any_point_sprite = false;
3639 
3640  switch (mode) {
3641  case TexGenAttrib::M_off:
3642  case TexGenAttrib::M_unused2:
3643  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX, texcoord_index);
3644  break;
3645 
3646  case TexGenAttrib::M_eye_sphere_map:
3647  {
3648  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3649  texcoord_index | D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);
3650  // This texture matrix, applied on top of the texcoord computed by
3651  // D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR, approximates the effect
3652  // produced by OpenGL's GL_SPHERE_MAP.
3653  static CPT(TransformState) sphere_map =
3654  TransformState::make_mat(LMatrix4(0.33, 0.0f, 0.0f, 0.0f,
3655  0.0f, 0.33, 0.0f, 0.0f,
3656  0.0f, 0.0f, 1.0f, 0.0f,
3657  0.5f, 0.5f, 0.0f, 1.0f));
3658  tex_mat = tex_mat->compose(sphere_map);
3659  texcoord_dimensions = 3;
3660  }
3661  break;
3662 
3663  case TexGenAttrib::M_world_cube_map:
3664  // To achieve world reflection vector, we must transform camera
3665  // coordinates to world coordinates; i.e. apply the camera transform.
3666  // In the case of a vector, we should not apply the pos component of the
3667  // transform.
3668  {
3669  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3670  texcoord_index | D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);
3671  texcoord_dimensions = 3;
3672  CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
3673  tex_mat = tex_mat->compose(camera_transform->set_pos(LVecBase3::zero()));
3674  }
3675  break;
3676 
3677  case TexGenAttrib::M_eye_cube_map:
3678  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3679  texcoord_index | D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);
3680  tex_mat = tex_mat->compose(_inv_cs_transform);
3681  texcoord_dimensions = 3;
3682  break;
3683 
3684  case TexGenAttrib::M_world_normal:
3685  // To achieve world normal, we must transform camera coordinates to
3686  // world coordinates; i.e. apply the camera transform. In the case of
3687  // a normal, we should not apply the pos component of the transform.
3688  {
3689  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3690  texcoord_index | D3DTSS_TCI_CAMERASPACENORMAL);
3691  texcoord_dimensions = 3;
3692  CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
3693  tex_mat = tex_mat->compose(camera_transform->set_pos(LVecBase3::zero()));
3694  }
3695  break;
3696 
3697  case TexGenAttrib::M_eye_normal:
3698  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3699  texcoord_index | D3DTSS_TCI_CAMERASPACENORMAL);
3700  texcoord_dimensions = 3;
3701  tex_mat = tex_mat->compose(_inv_cs_transform);
3702  break;
3703 
3704  case TexGenAttrib::M_world_position:
3705  // To achieve world position, we must transform camera coordinates to
3706  // world coordinates; i.e. apply the camera transform.
3707  {
3708  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3709  texcoord_index | D3DTSS_TCI_CAMERASPACEPOSITION);
3710  texcoord_dimensions = 3;
3711  CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
3712  tex_mat = tex_mat->compose(camera_transform);
3713  }
3714  break;
3715 
3716  case TexGenAttrib::M_eye_position:
3717  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3718  texcoord_index | D3DTSS_TCI_CAMERASPACEPOSITION);
3719  texcoord_dimensions = 3;
3720  tex_mat = tex_mat->compose(_inv_cs_transform);
3721  break;
3722 
3723  case TexGenAttrib::M_point_sprite:
3724  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX, texcoord_index);
3725  any_point_sprite = true;
3726  break;
3727 
3728  case TexGenAttrib::M_constant:
3729  // To generate a constant UV(w) coordinate everywhere, we use
3730  // CAMERASPACEPOSITION coordinates, but we construct a special matrix
3731  // that flattens the existing values to zero and then adds our desired
3732  // value.
3733 
3734  // The only reason we need to specify CAMERASPACEPOSITION at all,
3735  // instead of using whatever texture coordinates (if any) happen to be
3736  // on the vertices, is because we need to guarantee that there are 3-d
3737  // texture coordinates, because of the 3-component texture coordinate in
3738  // get_constant_value().
3739  {
3740  set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3741  texcoord_index | D3DTSS_TCI_CAMERASPACEPOSITION);
3742  texcoord_dimensions = 3;
3743 
3744  const LTexCoord3 &v = _target_tex_gen->get_constant_value(stage);
3745  CPT(TransformState) squash =
3746  TransformState::make_pos_hpr_scale(v, LVecBase3::zero(),
3747  LVecBase3::zero());
3748  tex_mat = tex_mat->compose(squash);
3749  }
3750  break;
3751  }
3752 
3753  set_render_state(D3DRS_POINTSPRITEENABLE, any_point_sprite);
3754 
3755  if (!tex_mat->is_identity()) {
3756  if (/*tex_mat->is_2d() &&*/ texcoord_dimensions <= 2) {
3757  // For 2-d texture coordinates, we have to reorder the matrix.
3758  LMatrix4 m = tex_mat->get_mat();
3759  LMatrix4f mf;
3760  mf.set(m(0, 0), m(0, 1), m(0, 3), 0.0f,
3761  m(1, 0), m(1, 1), m(1, 3), 0.0f,
3762  m(3, 0), m(3, 1), m(3, 3), 0.0f,
3763  0.0f, 0.0f, 0.0f, 1.0f);
3764  _d3d_device->SetTransform(get_tex_mat_sym(si), (D3DMATRIX *)mf.get_data());
3765  set_texture_stage_state(si, D3DTSS_TEXTURETRANSFORMFLAGS,
3766  D3DTTFF_COUNT2);
3767  } else {
3768  LMatrix4f mf = LCAST(float, tex_mat->get_mat());
3769  _d3d_device->SetTransform(get_tex_mat_sym(si), (D3DMATRIX *)mf.get_data());
3770  DWORD transform_flags = texcoord_dimensions;
3771  if (mf.get_col(3) != LVecBase4f(0.0f, 0.0f, 0.0f, 1.0f)) {
3772  // If we have a projected texture matrix, we also need to set
3773  // D3DTTFF_COUNT4.
3774  transform_flags = D3DTTFF_COUNT4 | D3DTTFF_PROJECTED;
3775  }
3776  set_texture_stage_state(si, D3DTSS_TEXTURETRANSFORMFLAGS,
3777  transform_flags);
3778  }
3779 
3780  } else {
3781  set_texture_stage_state(si, D3DTSS_TEXTURETRANSFORMFLAGS,
3782  D3DTTFF_DISABLE);
3783  // For some reason, "disabling" texture coordinate transforms doesn't
3784  // seem to be sufficient. We'll load an identity matrix to underscore
3785  // the point.
3786  _d3d_device->SetTransform(get_tex_mat_sym(si), &_d3d_ident_mat);
3787  }
3788  }
3789 
3790  // Disable the texture stages that are no longer used.
3791  for (si = num_stages; si < _num_active_texture_stages; si++) {
3792  set_texture_stage_state(si, D3DTSS_COLOROP, D3DTOP_DISABLE);
3793  _d3d_device->SetTexture(si, nullptr);
3794  }
3795 
3796  // Save the count of texture stages for next time.
3797  _num_active_texture_stages = num_stages;
3798 }
3799 
3800 /**
3801  * Called after any of the things that might change blending state have
3802  * changed, this function is responsible for setting the appropriate color
3803  * blending mode based on the current properties.
3804  */
3805 void DXGraphicsStateGuardian9::
3806 do_issue_blending() {
3807  // Handle the color_write attrib. If color_write is off, then all the other
3808  // blending-related stuff doesn't matter. If the device doesn't support
3809  // color-write, we use blending tricks to effectively disable color write.
3810  const ColorWriteAttrib *target_color_write = DCAST(ColorWriteAttrib, _target_rs->get_attrib_def(ColorWriteAttrib::get_class_slot()));
3811  unsigned int color_channels =
3812  target_color_write->get_channels() & _color_write_mask;
3813  if (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write)) {
3814  color_channels &= ~(ColorWriteAttrib::C_alpha);
3815  }
3816  if (color_channels == ColorWriteAttrib::C_off) {
3817  if (_screen->_can_direct_disable_color_writes) {
3818  set_render_state(D3DRS_ALPHABLENDENABLE, FALSE);
3819  set_render_state(D3DRS_COLORWRITEENABLE, (DWORD)0x0);
3820  } else {
3821  set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3822  set_render_state(D3DRS_SRCBLEND, D3DBLEND_ZERO);
3823  set_render_state(D3DRS_DESTBLEND, D3DBLEND_ONE);
3824  }
3825  return;
3826  } else {
3827  if (_screen->_can_direct_disable_color_writes) {
3828  set_render_state(D3DRS_COLORWRITEENABLE, color_channels);
3829  }
3830  }
3831 
3832  const ColorBlendAttrib *color_blend;
3833  _target_rs->get_attrib_def(color_blend);
3834  ColorBlendAttrib::Mode color_blend_mode = color_blend->get_mode();
3835 
3836  const TransparencyAttrib *target_transparency;
3837  _target_rs->get_attrib_def(target_transparency);
3838  TransparencyAttrib::Mode transparency_mode = target_transparency->get_mode();
3839 
3840  // Is there a color blend set?
3841  if (color_blend_mode != ColorBlendAttrib::M_none) {
3842  set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3843  set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
3844  set_render_state(D3DRS_BLENDOP, get_blend_mode(color_blend_mode));
3845  set_render_state(D3DRS_BLENDOPALPHA, get_blend_mode(color_blend->get_alpha_mode()));
3846  set_render_state(D3DRS_SRCBLEND, get_blend_func(color_blend->get_operand_a()));
3847  set_render_state(D3DRS_DESTBLEND, get_blend_func(color_blend->get_operand_b()));
3848  set_render_state(D3DRS_SRCBLENDALPHA, get_blend_func(color_blend->get_alpha_operand_a()));
3849  set_render_state(D3DRS_DESTBLENDALPHA, get_blend_func(color_blend->get_alpha_operand_b()));
3850  return;
3851  }
3852 
3853  // No color blend; is there a transparency set?
3854  switch (transparency_mode) {
3855  case TransparencyAttrib::M_none:
3856  case TransparencyAttrib::M_binary:
3857  break;
3858 
3859  case TransparencyAttrib::M_alpha:
3860  case TransparencyAttrib::M_multisample:
3861  case TransparencyAttrib::M_multisample_mask:
3862  case TransparencyAttrib::M_dual:
3863  set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3864  if (old_alpha_blend) {
3865  set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
3866  } else {
3867  set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
3868  set_render_state(D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD);
3869  set_render_state(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
3870  set_render_state(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
3871  }
3872  set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD);
3873  set_render_state(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
3874  set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
3875  return;
3876 
3877  case TransparencyAttrib::M_premultiplied_alpha:
3878  set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3879  set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
3880  set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD);
3881  set_render_state(D3DRS_SRCBLEND, D3DBLEND_ONE);
3882  set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
3883  return;
3884 
3885  default:
3886  dxgsg9_cat.error()
3887  << "invalid transparency mode " << (int)transparency_mode << endl;
3888  break;
3889  }
3890 
3891  // Nothing's set, so disable blending.
3892  set_render_state(D3DRS_ALPHABLENDENABLE, FALSE);
3893 }
3894 
3895 /**
3896  * Called by clear_state_and_transform() to ensure that the current modelview
3897  * and projection matrices are properly loaded in the graphics state, after a
3898  * callback might have mucked them up.
3899  */
3900 void DXGraphicsStateGuardian9::
3901 reissue_transforms() {
3902  prepare_lens();
3903  do_issue_transform();
3904 }
3905 
3906 /**
3907  * Intended to be overridden by a derived class to enable or disable the use
3908  * of lighting overall. This is called by issue_light() according to whether
3909  * any lights are in use or not.
3910  */
3911 void DXGraphicsStateGuardian9::
3912 enable_lighting(bool enable) {
3913  set_render_state(D3DRS_LIGHTING, (DWORD)enable);
3914 }
3915 
3916 /**
3917  * Intended to be overridden by a derived class to indicate the color of the
3918  * ambient light that should be in effect. This is called by issue_light()
3919  * after all other lights have been enabled or disabled.
3920  */
3921 void DXGraphicsStateGuardian9::
3922 set_ambient_light(const LColor &color) {
3923  LColor c = color;
3924  c.set(c[0] * _light_color_scale[0],
3925  c[1] * _light_color_scale[1],
3926  c[2] * _light_color_scale[2],
3927  c[3] * _light_color_scale[3]);
3928 
3929  set_render_state(D3DRS_AMBIENT, LColor_to_D3DCOLOR(c));
3930 }
3931 
3932 /**
3933  * Intended to be overridden by a derived class to enable the indicated light
3934  * id. A specific Light will already have been bound to this id via
3935  * bind_light().
3936  */
3937 void DXGraphicsStateGuardian9::
3938 enable_light(int light_id, bool enable) {
3939  HRESULT hr = _d3d_device->LightEnable(light_id, enable);
3940 
3941  if (FAILED(hr)) {
3942  wdxdisplay9_cat.warning()
3943  << "Could not enable light " << light_id << ": "
3944  << D3DERRORSTRING(hr) << "\n";
3945  }
3946 }
3947 
3948 /**
3949  * Intended to be overridden by a derived class to enable the indicated
3950  * clip_plane id. A specific PlaneNode will already have been bound to this
3951  * id via bind_clip_plane().
3952  */
3953 void DXGraphicsStateGuardian9::
3954 enable_clip_plane(int plane_id, bool enable) {
3955  if (enable) {
3956  _clip_plane_bits |= ((DWORD)1 << plane_id);
3957  } else {
3958  _clip_plane_bits &= ~((DWORD)1 << plane_id);
3959  }
3960  set_render_state(D3DRS_CLIPPLANEENABLE, _clip_plane_bits);
3961 }
3962 
3963 /**
3964  * Called the first time a particular clip_plane has been bound to a given id
3965  * within a frame, this should set up the associated hardware clip_plane with
3966  * the clip_plane's properties.
3967  */
3968 void DXGraphicsStateGuardian9::
3969 bind_clip_plane(const NodePath &plane, int plane_id) {
3970  // Get the plane in "world coordinates" (actually, view coordinates). This
3971  // means the plane in the coordinate space of the camera, converted to DX's
3972  // coordinate system.
3973  CPT(TransformState) transform = plane.get_transform(_scene_setup->get_camera_path());
3974  const LMatrix4 &plane_mat = transform->get_mat();
3975  LMatrix4 rel_mat = plane_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
3976  const PlaneNode *plane_node;
3977  DCAST_INTO_V(plane_node, plane.node());
3978  LPlanef world_plane = LCAST(float, plane_node->get_plane() * rel_mat);
3979 
3980  HRESULT hr = _d3d_device->SetClipPlane(plane_id, world_plane.get_data());
3981  if (FAILED(hr)) {
3982  wdxdisplay9_cat.warning()
3983  << "Could not set clip plane for " << plane
3984  << " to id " << plane_id << ": " << D3DERRORSTRING(hr) << "\n";
3985  }
3986 }
3987 
3988 /**
3989  * This is called by the associated GraphicsWindow when close_window() is
3990  * called. It should null out the _win pointer and possibly free any open
3991  * resources associated with the GSG.
3992  */
3993 void DXGraphicsStateGuardian9::
3994 close_gsg() {
3995  GraphicsStateGuardian::close_gsg();
3996 
3997  if (_prepared_objects.is_null()) {
3998  return;
3999  }
4000 
4001  if (dxgsg9_cat.is_debug()) {
4002  dxgsg9_cat.debug()
4003  << "Closing GSG, prepared_objects count = "
4004  << _prepared_objects->get_ref_count() << "\n";
4005  }
4006 
4007  // Unlike in OpenGL, in DX9 it is safe to try to explicitly release any
4008  // textures here. And it may even be a good idea.
4009  if (_prepared_objects->get_ref_count() == 1) {
4010  release_all();
4011 
4012  // Now we need to actually delete all of the objects we just released.
4013  Thread *current_thread = Thread::get_current_thread();
4014  _prepared_objects->begin_frame(this, current_thread);
4015  _prepared_objects->end_frame(current_thread);
4016  }
4017 }
4018 
4019 /**
4020  * Frees some memory that was explicitly allocated within the dxgsg.
4021  */
4022 void DXGraphicsStateGuardian9::
4023 free_nondx_resources() {
4024 #ifdef HAVE_CG
4025  if (_cg_context) {
4026  cgDestroyContext(_cg_context);
4027  _cg_context = 0;
4028  }
4029 #endif
4030 }
4031 
4032 /**
4033  * setup for re-calling dx_init(), this is not the final exit cleanup routine
4034  * (see dx_cleanup)
4035  */
4036 void DXGraphicsStateGuardian9::
4037 free_d3d_device() {
4038  // dont want a full reset of gsg, just a state clear
4039  _state_rs = RenderState::make_empty();
4040  _state_mask.clear();
4041 
4042  // want gsg to pass all state settings through
4043 
4044  _dx_is_ready = false;
4045 
4046  if (_d3d_device != nullptr) {
4047  for(int i = 0; i < D3D_MAXTEXTURESTAGES; i++) {
4048  // d3d should release this stuff internally anyway, but whatever
4049  _d3d_device->SetTexture(i, nullptr);
4050  }
4051  }
4052 
4053  release_all();
4054 
4055  if (_d3d_device != nullptr) {
4056  RELEASE(_d3d_device, dxgsg9, "d3dDevice", RELEASE_DOWN_TO_ZERO);
4057  }
4058 
4059  free_nondx_resources();
4060 
4061  // obviously we dont release ID3D9, just ID3DDevice9
4062 }
4063 
4064 /**
4065  * Sets up the glDrawBuffer to render into the buffer indicated by the
4066  * RenderBuffer object. This only sets up the color bits; it does not affect
4067  * the depth, stencil, accum layers.
4068  */
4069 void DXGraphicsStateGuardian9::
4070 set_draw_buffer(const RenderBuffer &rb) {
4071  dxgsg9_cat.fatal() << "DX set_draw_buffer unimplemented!!!";
4072  return;
4073 }
4074 
4075 /**
4076  * Vestigial analog of glReadBuffer
4077  */
4078 void DXGraphicsStateGuardian9::
4079 set_read_buffer(const RenderBuffer &rb) {
4080  if (rb._buffer_type & RenderBuffer::T_front) {
4081  _cur_read_pixel_buffer = RenderBuffer::T_front;
4082  } else if (rb._buffer_type & RenderBuffer::T_back) {
4083  _cur_read_pixel_buffer = RenderBuffer::T_back;
4084  } else if (rb._buffer_type & RenderBuffer::T_aux_rgba_ALL) {
4085  _cur_read_pixel_buffer = RenderBuffer::T_back;
4086  } else {
4087  dxgsg9_cat.error() << "Invalid or unimplemented Argument to set_read_buffer!\n";
4088  }
4089  return;
4090 }
4091 
4092 /**
4093  * Returns the array of four floats that should be issued as the light's
4094  * color, as scaled by the current value of _light_color_scale, in the case of
4095  * color_scale_via_lighting.
4096  */
4097 const D3DCOLORVALUE &DXGraphicsStateGuardian9::
4098 get_light_color(Light *light) const {
4099  LColor c = light->get_color();
4100  static LColorf cf;
4101  cf.set(c[0] * _light_color_scale[0],
4102  c[1] * _light_color_scale[1],
4103  c[2] * _light_color_scale[2],
4104  c[3] * _light_color_scale[3]);
4105  return *(D3DCOLORVALUE *)cf.get_data();
4106 }
4107 
4108 /**
4109  * Maps from ColorBlendAttrib::Mode to D3DBLENDOP vaule.
4110  */
4111 D3DBLENDOP DXGraphicsStateGuardian9::
4112 get_blend_mode(ColorBlendAttrib::Mode mode) {
4113  switch (mode) {
4114  case ColorBlendAttrib::M_add:
4115  return D3DBLENDOP_ADD;
4116 
4117  case ColorBlendAttrib::M_subtract:
4118  return D3DBLENDOP_SUBTRACT;
4119 
4120  case ColorBlendAttrib::M_inv_subtract:
4121  return D3DBLENDOP_REVSUBTRACT;
4122 
4123  case ColorBlendAttrib::M_min:
4124  return D3DBLENDOP_MIN;
4125 
4126  case ColorBlendAttrib::M_max:
4127  return D3DBLENDOP_MAX;
4128  }
4129 
4130  dxgsg9_cat.error()
4131  << "Unknown color blend mode " << (int)mode << endl;
4132  return D3DBLENDOP_ADD;
4133 }
4134 
4135 /**
4136  * Maps from ColorBlendAttrib::Operand to D3DBLEND value.
4137  */
4138 D3DBLEND DXGraphicsStateGuardian9::
4139 get_blend_func(ColorBlendAttrib::Operand operand) {
4140  switch (operand) {
4141  case ColorBlendAttrib::O_zero:
4142  return D3DBLEND_ZERO;
4143 
4144  case ColorBlendAttrib::O_one:
4145  return D3DBLEND_ONE;
4146 
4147  case ColorBlendAttrib::O_incoming_color:
4148  return D3DBLEND_SRCCOLOR;
4149 
4150  case ColorBlendAttrib::O_one_minus_incoming_color:
4151  return D3DBLEND_INVSRCCOLOR;
4152 
4153  case ColorBlendAttrib::O_fbuffer_color:
4154  return D3DBLEND_DESTCOLOR;
4155 
4156  case ColorBlendAttrib::O_one_minus_fbuffer_color:
4157  return D3DBLEND_INVDESTCOLOR;
4158 
4159  case ColorBlendAttrib::O_incoming_alpha:
4160  return D3DBLEND_SRCALPHA;
4161 
4162  case ColorBlendAttrib::O_one_minus_incoming_alpha:
4163  return D3DBLEND_INVSRCALPHA;
4164 
4165  case ColorBlendAttrib::O_fbuffer_alpha:
4166  return D3DBLEND_DESTALPHA;
4167 
4168  case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
4169  return D3DBLEND_INVDESTALPHA;
4170 
4171  case ColorBlendAttrib::O_constant_color:
4172  // Not supported by DX.
4173  return D3DBLEND_SRCCOLOR;
4174 
4175  case ColorBlendAttrib::O_one_minus_constant_color:
4176  // Not supported by DX.
4177  return D3DBLEND_INVSRCCOLOR;
4178 
4179  case ColorBlendAttrib::O_constant_alpha:
4180  // Not supported by DX.
4181  return D3DBLEND_SRCALPHA;
4182 
4183  case ColorBlendAttrib::O_one_minus_constant_alpha:
4184  // Not supported by DX.
4185  return D3DBLEND_INVSRCALPHA;
4186 
4187  case ColorBlendAttrib::O_incoming_color_saturate:
4188  return D3DBLEND_SRCALPHASAT;
4189 
4190  case ColorBlendAttrib::O_incoming1_color:
4191  return (D3DBLEND)16; //D3DBLEND_SRCCOLOR2;
4192 
4193  case ColorBlendAttrib::O_one_minus_incoming1_color:
4194  return (D3DBLEND)17; //D3DBLEND_INVSRCCOLOR2;
4195 
4196  case ColorBlendAttrib::O_incoming1_alpha:
4197  // Not supported by DX9.
4198  return (D3DBLEND)18;
4199 
4200  case ColorBlendAttrib::O_one_minus_incoming1_alpha:
4201  // Not supported by DX9.
4202  return (D3DBLEND)19;
4203  }
4204 
4205  dxgsg9_cat.error()
4206  << "Unknown color blend operand " << (int)operand << endl;
4207  return D3DBLEND_ZERO;
4208 }
4209 
4210 /**
4211  * Reports the DX texture manager's activity to PStats.
4212  */
4213 void DXGraphicsStateGuardian9::
4214 report_texmgr_stats() {
4215 
4216 #ifdef DO_PSTATS
4217  HRESULT hr;
4218  hr = 0;
4219 
4220 #ifdef TEXMGRSTATS_USES_GETAVAILVIDMEM
4221  DWORD dwTexTotal, dwTexFree, dwVidTotal, dwVidFree;
4222 
4223  if (_total_texmem_pcollector.is_active()) {
4224  DDSCAPS2 ddsCaps;
4225 
4226  ZeroMemory(&ddsCaps, sizeof(ddsCaps));
4227 
4228  ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;
4229  if (FAILED( hr = _d3d_device->GetAvailableVidMem(&ddsCaps, &dwVidTotal, &dwVidFree))) {
4230  dxgsg9_cat.fatal() << "report_texmgr GetAvailableVidMem for VIDMEM failed : result = " << D3DERRORSTRING(hr);
4231  throw_event("panda3d-render-error");
4232  return;
4233  }
4234 
4235  ddsCaps.dwCaps = DDSCAPS_TEXTURE;
4236  if (FAILED( hr = _d3d_device->GetAvailableVidMem(&ddsCaps, &dwTexTotal, &dwTexFree))) {
4237  dxgsg9_cat.fatal() << "report_texmgr GetAvailableVidMem for TEXTURE failed : result = " << D3DERRORSTRING(hr);
4238  throw_event("panda3d-render-error");
4239  return;
4240  }
4241  }
4242 #endif // TEXMGRSTATS_USES_GETAVAILVIDMEM
4243 
4244  D3DDEVINFO_RESOURCEMANAGER all_resource_stats;
4245  ZeroMemory(&all_resource_stats, sizeof(D3DDEVINFO_RESOURCEMANAGER));
4246 
4247 /* ***** DX9, GetInfo ( ) NOT IN DX9 */
4248 /*
4249  if (!_tex_stats_retrieval_impossible) {
4250  hr = _d3d_device->GetInfo(D3DDEVINFOID_RESOURCEMANAGER, &all_resource_stats, sizeof(D3DDEVINFO_RESOURCEMANAGER));
4251  if (hr != D3D_OK) {
4252  if (hr == S_FALSE) {
4253  static int PrintedMsg = 2;
4254  if (PrintedMsg>0) {
4255  if (dxgsg9_cat.is_debug()) {
4256  dxgsg9_cat.debug()
4257  << "texstats GetInfo() requires debug DX DLLs to be installed!!\n";
4258  }
4259  ZeroMemory(&all_resource_stats, sizeof(D3DDEVINFO_RESOURCEMANAGER));
4260  _tex_stats_retrieval_impossible = true;
4261  }
4262  } else {
4263  dxgsg9_cat.error() << "GetInfo(RESOURCEMANAGER) failed to get tex stats: result = " << D3DERRORSTRING(hr);
4264  return;
4265  }
4266  }
4267  }
4268 */
4269 
4270  // Tell PStats about the state of the texture memory.
4271 
4272  if (_texmgrmem_total_pcollector.is_active()) {
4273  // report zero if no debug dlls, to signal this info is invalid
4274  _texmgrmem_total_pcollector.set_level(all_resource_stats.stats[D3DRTYPE_TEXTURE].TotalBytes);
4275  _texmgrmem_resident_pcollector.set_level(all_resource_stats.stats[D3DRTYPE_TEXTURE].WorkingSetBytes);
4276  }
4277 #ifdef TEXMGRSTATS_USES_GETAVAILVIDMEM
4278  if (_total_texmem_pcollector.is_active()) {
4279  _total_texmem_pcollector.set_level(dwTexTotal);
4280  _used_texmem_pcollector.set_level(dwTexTotal - dwTexFree);
4281  }
4282 #endif // TEXMGRSTATS_USES_GETAVAILVIDMEM
4283 #endif // DO_PSTATS
4284 }
4285 
4286 /**
4287  *
4288  */
4289 void DXGraphicsStateGuardian9::
4290 set_context(DXScreenData *new_context) {
4291  nassertv(new_context != nullptr);
4292  _screen = new_context;
4293  _d3d_device = _screen->_d3d_device; //copy this one field for speed of deref
4294  _swap_chain = _screen->_swap_chain; //copy this one field for speed of deref
4295 
4296  _screen->_dxgsg9 = this;
4297  set_cg_device(_d3d_device);
4298 }
4299 
4300 /**
4301  * Set render target to the backbuffer of current swap chain.
4302  */
4303 void DXGraphicsStateGuardian9::
4304 set_render_target() {
4305  if (_d3d_device == nullptr) {
4306  return;
4307  }
4308 
4309  LPDIRECT3DSURFACE9 back = nullptr, stencil = nullptr;
4310 
4311  UINT swap_chain;
4312 
4313  /* ***** DX9 swap_chain ??? */
4314  swap_chain = 0;
4315 
4316  if (!_swap_chain) //maybe fullscreen mode or main/single window
4317  _d3d_device->GetBackBuffer(swap_chain, 0, D3DBACKBUFFER_TYPE_MONO, &back);
4318  else
4319  _swap_chain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &back);
4320 
4321  // wdxdisplay9_cat.debug() << "swapchain is " << _swap_chain << "\n";
4322  // wdxdisplay9_cat.debug() << "back buffer is " << back << "\n";
4323 
4324  _d3d_device->GetDepthStencilSurface(&stencil);
4325 
4326 // _d3d_device->SetRenderTarget(back, stencil);
4327  DWORD render_target_index;
4328  render_target_index = 0;
4329  _d3d_device->SetRenderTarget(render_target_index, back);
4330 
4331  if (back) {
4332  back->Release();
4333  }
4334  if (stencil) {
4335  stencil->Release();
4336  }
4337 }
4338 
4339 /**
4340  *
4341  */
4342 void DXGraphicsStateGuardian9::
4343 set_texture_blend_mode(int i, const TextureStage *stage) {
4344  switch (stage->get_mode()) {
4345  case TextureStage::M_modulate:
4346  case TextureStage::M_modulate_glow:
4347  case TextureStage::M_modulate_gloss:
4348  // emulates GL_MODULATE glTexEnv mode
4349  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_MODULATE);
4350  set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4351  set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4352  set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
4353  set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4354  set_texture_stage_state(i, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
4355  break;
4356 
4357  case TextureStage::M_decal:
4358  // emulates GL_DECAL glTexEnv mode
4359  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
4360  set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4361  set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4362 
4363  set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
4364  set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
4365  break;
4366 
4367  case TextureStage::M_replace:
4368  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
4369  set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4370 
4371  set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
4372  set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4373  break;
4374 
4375  case TextureStage::M_add:
4376  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_ADD);
4377  set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4378  set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4379 
4380  set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
4381  set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4382  set_texture_stage_state(i, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
4383  break;
4384 
4385  case TextureStage::M_blend:
4386  case TextureStage::M_blend_color_scale:
4387  {
4388  set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_LERP);
4389  set_texture_stage_state(i, D3DTSS_COLORARG0, D3DTA_TEXTURE);
4390  set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4391  set_texture_stage_state(i, D3DTSS_COLORARG1, _constant_color_operand);
4392 
4393  set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
4394  set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4395  set_texture_stage_state(i, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
4396  }
4397  break;
4398 
4399  case TextureStage::M_combine:
4400  // M_combine mode begins a collection of more sophisticated modes, which
4401  // match up more closely with DirectX's built-in modes.
4403  (i, D3DTSS_COLOROP,
4404  get_texture_operation(stage->get_combine_rgb_mode(),
4405  stage->get_rgb_scale()));
4406 
4407  switch (stage->get_num_combine_rgb_operands()) {
4408  case 3:
4410  (i, D3DTSS_COLORARG0,
4411  get_texture_argument(stage->get_combine_rgb_source2(),
4412  stage->get_combine_rgb_operand2()));
4413  // fall through
4414 
4415  case 2:
4417  (i, D3DTSS_COLORARG2,
4418  get_texture_argument(stage->get_combine_rgb_source1(),
4419  stage->get_combine_rgb_operand1()));
4420  // fall through
4421 
4422  case 1:
4424  (i, D3DTSS_COLORARG1,
4425  get_texture_argument(stage->get_combine_rgb_source0(),
4426  stage->get_combine_rgb_operand0()));
4427  // fall through
4428 
4429  default:
4430  break;
4431  }
4432 
4434  (i, D3DTSS_ALPHAOP,
4435  get_texture_operation(stage->get_combine_alpha_mode(),
4436  stage->get_alpha_scale()));
4437 
4438  switch (stage->get_num_combine_alpha_operands()) {
4439  case 3:
4441  (i, D3DTSS_ALPHAARG0,
4442  get_texture_argument(stage->get_combine_alpha_source2(),
4443  stage->get_combine_alpha_operand2()));
4444  // fall through
4445 
4446  case 2:
4448  (i, D3DTSS_ALPHAARG2,
4449  get_texture_argument(stage->get_combine_alpha_source1(),
4450  stage->get_combine_alpha_operand1()));
4451  // fall through
4452 
4453  case 1:
4455  (i, D3DTSS_ALPHAARG1,
4456  get_texture_argument(stage->get_combine_alpha_source0(),
4457  stage->get_combine_alpha_operand0()));
4458  // fall through
4459 
4460  default:
4461  break;
4462  }
4463  break;
4464 
4465  default:
4466  dxgsg9_cat.error()
4467  << "Unknown texture mode " << (int)stage->get_mode() << endl;
4468  break;
4469  }
4470 
4471  if (stage->get_saved_result()) {
4472  set_texture_stage_state(i, D3DTSS_RESULTARG, D3DTA_TEMP);
4473  } else {
4474  set_texture_stage_state(i, D3DTSS_RESULTARG, D3DTA_CURRENT);
4475  }
4476 
4477  if (stage->uses_color() || stage->involves_color_scale()) {
4478  // Set up the constant color for this stage.
4479 
4480  D3DCOLOR constant_color;
4481  if (stage->involves_color_scale() && _color_scale_enabled) {
4482  LColor color = stage->get_color();
4483  color.set(color[0] * _current_color_scale[0],
4484  color[1] * _current_color_scale[1],
4485  color[2] * _current_color_scale[2],
4486  color[3] * _current_color_scale[3]);
4487  _texture_involves_color_scale = true;
4488  constant_color = LColor_to_D3DCOLOR(color);
4489  } else {
4490  constant_color = LColor_to_D3DCOLOR(stage->get_color());
4491  }
4492  if (_supports_texture_constant_color) {
4493  set_texture_stage_state(i, D3DTSS_CONSTANT, constant_color);
4494  } else {
4495  // This device doesn't supoprt a per-stage constant color, so we have to
4496  // fall back to a single constant color for the overall texture
4497  // pipeline.
4498  set_render_state(D3DRS_TEXTUREFACTOR, constant_color);
4499  }
4500  }
4501 }
4502 
4503 /**
4504  * Clean up the DirectX environment, accounting for exit()
4505  */
4506 void DXGraphicsStateGuardian9::
4507 dx_cleanup() {
4508  if (!_d3d_device) {
4509  return;
4510  }
4511 
4512  free_nondx_resources();
4513  PRINT_REFCNT(dxgsg9, _d3d_device);
4514 
4515  // Do a safe check for releasing the D3DDEVICE. RefCount should be zero. if
4516  // we're called from exit(), _d3d_device may already have been released
4517  RELEASE(_d3d_device, dxgsg9, "d3dDevice", RELEASE_DOWN_TO_ZERO);
4518  _screen->_d3d_device = nullptr;
4519 
4520  // Releasing pD3D is now the responsibility of the GraphicsPipe destructor
4521 }
4522 
4523 /**
4524  * This function checks current device's framebuffer dimension against passed
4525  * p_presentation_params backbuffer dimension to determine a device reset if
4526  * there is only one window or it is the main window or fullscreen mode then,
4527  * it resets the device. Finally it returns the new DXScreenData through
4528  * parameter screen
4529  */
4530 HRESULT DXGraphicsStateGuardian9::
4531 reset_d3d_device(D3DPRESENT_PARAMETERS *presentation_params,
4532  DXScreenData **screen) {
4533  HRESULT hr;
4534 
4535  nassertr(IS_VALID_PTR(presentation_params), E_FAIL);
4536  nassertr(IS_VALID_PTR(_screen->_d3d9), E_FAIL);
4537  nassertr(IS_VALID_PTR(_d3d_device), E_FAIL);
4538 
4539  // for windowed mode make sure our format matches the desktop fmt, in case
4540  // the desktop mode has been changed
4541  _screen->_d3d9->GetAdapterDisplayMode(_screen->_card_id, &_screen->_display_mode);
4542  presentation_params->BackBufferFormat = _screen->_display_mode.Format;
4543 
4544  // here we have to look at the _presentation_reset frame buffer dimension if
4545  // current window's dimension is bigger than _presentation_reset we have to
4546  // reset the device before creating new swapchain. inorder to reset
4547  // properly, we need to release all swapchains
4548 
4549  if (true || !(_screen->_swap_chain)
4550  || (_presentation_reset.BackBufferWidth < presentation_params->BackBufferWidth)
4551  || (_presentation_reset.BackBufferHeight < presentation_params->BackBufferHeight)) {
4552  if (wdxdisplay9_cat.is_debug()) {
4553  wdxdisplay9_cat.debug()
4554  << "swap_chain = " << _screen->_swap_chain << " _presentation_reset = "
4555  << _presentation_reset.BackBufferWidth << "x" << _presentation_reset.BackBufferHeight
4556  << " presentation_params = "
4557  << presentation_params->BackBufferWidth << "x" << presentation_params->BackBufferHeight << "\n";
4558  }
4559 
4560  get_engine()->reset_all_windows(false);// reset old swapchain by releasing
4561 
4562  if (_screen->_swap_chain) { //other windows might be using bigger buffers
4563  _presentation_reset.BackBufferWidth = max(_presentation_reset.BackBufferWidth, presentation_params->BackBufferWidth);
4564  _presentation_reset.BackBufferHeight = max(_presentation_reset.BackBufferHeight, presentation_params->BackBufferHeight);
4565 
4566  } else { // single window, must reset to the new presentation_params dimension
4567  _presentation_reset.BackBufferWidth = presentation_params->BackBufferWidth;
4568  _presentation_reset.BackBufferHeight = presentation_params->BackBufferHeight;
4569  }
4570 
4571  // Calling this forces all of the textures and vbuffers to be regenerated,
4572  // a prerequisite to calling Reset().
4573  release_all();
4574 
4575  // Just to be extra-conservative for now, we'll go ahead and release the
4576  // vbuffers and ibuffers at least; they're relatively cheap to replace.
4579 
4580  if (_white_vbuffer != nullptr) {
4581  _white_vbuffer->Release();
4582  _white_vbuffer = nullptr;
4583  }
4584 
4585  // must be called before reset
4586  Thread *current_thread = Thread::get_current_thread();
4587  _prepared_objects->begin_frame(this, current_thread);
4588 
4589  // release graphics buffer surfaces
4590  {
4591  wdxGraphicsBuffer9 *graphics_buffer;
4592  std::list <wdxGraphicsBuffer9 **>::iterator graphics_buffer_iterator;
4593 
4594  for (graphics_buffer_iterator = _graphics_buffer_list.begin( ); graphics_buffer_iterator != _graphics_buffer_list.end( ); graphics_buffer_iterator++)
4595  {
4596  graphics_buffer = **graphics_buffer_iterator;
4597  if (graphics_buffer -> _color_backing_store)
4598  {
4599  graphics_buffer -> _color_backing_store -> Release ( );
4600  graphics_buffer -> _color_backing_store = 0;
4601  }
4602  if (graphics_buffer -> _depth_backing_store)
4603  {
4604  graphics_buffer -> _depth_backing_store -> Release ( );
4605  graphics_buffer -> _depth_backing_store = 0;
4606  }
4607  }
4608  }
4609 
4610  mark_new();
4611  hr = _d3d_device->Reset(&_presentation_reset);
4612  if (FAILED(hr) && hr != D3DERR_DEVICELOST) {
4613  return hr;
4614  }
4615 
4616  get_engine()->reset_all_windows(true);// reset with new swapchains by creating
4617  if (screen) {
4618  *screen = nullptr;
4619  }
4620 
4621  if (presentation_params != &_screen->_presentation_params) {
4622  memcpy(&_screen->_presentation_params, presentation_params, sizeof(D3DPRESENT_PARAMETERS));
4623  }
4624 
4625  return hr;
4626  }
4627 
4628  // release the old swapchain and create a new one
4629  if (_screen && _screen->_swap_chain) {
4630  _screen->_swap_chain->Release();
4631  wdxdisplay9_cat.debug()
4632  << "swap chain " << _screen->_swap_chain << " is released\n";
4633  _screen->_swap_chain = nullptr;
4634  hr = _d3d_device->CreateAdditionalSwapChain(presentation_params, &_screen->_swap_chain);
4635  }
4636  if (SUCCEEDED(hr)) {
4637  if (presentation_params != &_screen->_presentation_params) {
4638  memcpy(&_screen->_presentation_params, presentation_params, sizeof(D3DPRESENT_PARAMETERS));
4639  }
4640  if (screen) {
4641  *screen = _screen;
4642  }
4643  }
4644  return hr;
4645 }
4646 
4647 /**
4648  *
4649  */
4650 bool DXGraphicsStateGuardian9::
4651 check_cooperative_level() {
4652  bool bDoReactivateWindow = false;
4653  if (_d3d_device == nullptr) {
4654  return false;
4655  }
4656 
4657  HRESULT hr = _d3d_device->TestCooperativeLevel();
4658 
4659  if (SUCCEEDED(hr)) {
4660  nassertr(SUCCEEDED(_last_testcooplevel_result), false);
4661  return true;
4662  }
4663 
4664  switch (hr) {
4665  case D3DERR_DEVICENOTRESET:
4666  _dx_is_ready = false;
4667 
4668  // call this just in case
4669  _prepared_objects->begin_frame(this, Thread::get_current_thread());
4670 
4671  hr = reset_d3d_device(&_screen->_presentation_params);
4672  if (FAILED(hr)) {
4673  // I think this shouldnt fail unless I've screwed up the
4674  // _presentation_params from the original working ones somehow
4675  dxgsg9_cat.error()
4676  << "check_cooperative_level Reset() failed, hr = " << D3DERRORSTRING(hr);
4677  }
4678 
4679  hr = _d3d_device->TestCooperativeLevel();
4680  if (FAILED(hr)) {
4681  // internal chk, shouldnt fail
4682  dxgsg9_cat.error()
4683  << "TestCooperativeLevel following Reset() failed, hr = " << D3DERRORSTRING(hr);
4684 
4685  }
4686 
4687  _dx_is_ready = TRUE;
4688  break;
4689 
4690  case D3DERR_DEVICELOST:
4691  // sleep while the device is lost to free up the CPU
4692  Sleep (10);
4693 
4694  if (SUCCEEDED(_last_testcooplevel_result)) {
4695  if (_dx_is_ready) {
4696  _dx_is_ready = false;
4697  if (dxgsg9_cat.is_debug()) {
4698  dxgsg9_cat.debug() << "D3D Device was Lost, waiting...\n";
4699  }
4700  }
4701  }
4702  }
4703 
4704  _last_testcooplevel_result = hr;
4705  return SUCCEEDED(hr);
4706 }
4707 
4708 /**
4709  * redraw primary buffer
4710  */
4711 void DXGraphicsStateGuardian9::
4712 show_frame() {
4713  if (_d3d_device == nullptr) {
4714  return;
4715  }
4716 
4717  HRESULT hr;
4718 
4719  if (_swap_chain) {
4720  DWORD flags;
4721  flags = 0;
4722 
4723  hr = _swap_chain->Present(nullptr, nullptr, (HWND)nullptr, nullptr, flags);
4724  } else {
4725  hr = _d3d_device->Present(nullptr, nullptr, (HWND)nullptr, nullptr);
4726  }
4727 
4728  if (FAILED(hr)) {
4729  if (hr == D3DERR_DEVICELOST) {
4730  check_cooperative_level();
4731  } else {
4732  dxgsg9_cat.error()
4733  << "show_frame() - Present() failed" << D3DERRORSTRING(hr);
4734  throw_event("panda3d-render-error");
4735  }
4736  }
4737 }
4738 
4739 /**
4740  *
4741  */
4742 bool DXGraphicsStateGuardian9::
4743 create_swap_chain(DXScreenData *new_context) {
4744  // Instead of creating a device and rendering as d3ddevice->present() we
4745  // should render using SwapChain->present(). This is done to support
4746  // multiple windows rendering. For that purpose, we need to set additional
4747  // swap chains here.
4748 
4749  HRESULT hr;
4750  hr = new_context->_d3d_device->CreateAdditionalSwapChain(&new_context->_presentation_params, &new_context->_swap_chain);
4751  if (FAILED(hr)) {
4752  wdxdisplay9_cat.debug() << "Swapchain creation failed :"<<D3DERRORSTRING(hr)<<"\n";
4753  return false;
4754  }
4755  return true;
4756 }
4757 
4758 /**
4759  * Release the swap chain on this DXScreenData
4760  */
4761 bool DXGraphicsStateGuardian9::
4762 release_swap_chain(DXScreenData *new_context) {
4763  HRESULT hr;
4764  if (new_context->_swap_chain) {
4765  hr = new_context->_swap_chain->Release();
4766  if (FAILED(hr)) {
4767  wdxdisplay9_cat.debug() << "Swapchain release failed:" << D3DERRORSTRING(hr) << "\n";
4768  return false;
4769  }
4770  if (new_context->_swap_chain == _swap_chain) {
4771  _swap_chain = nullptr;
4772  }
4773  }
4774  return true;
4775 }
4776 
4777 /**
4778  * copies the PresReset from passed DXScreenData
4779  */
4780 void DXGraphicsStateGuardian9::
4781 copy_pres_reset(DXScreenData *screen) {
4782  memcpy(&_presentation_reset, &_screen->_presentation_params, sizeof(D3DPRESENT_PARAMETERS));
4783 }
4784 
4785 /**
4786  *
4787  */
4788 D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian9::
4789 get_d3d_min_type(SamplerState::FilterType filter_type) {
4790  switch (filter_type) {
4791  case SamplerState::FT_nearest:
4792  return D3DTEXF_POINT;
4793 
4794  case SamplerState::FT_linear:
4795  return D3DTEXF_LINEAR;
4796 
4797  case SamplerState::FT_nearest_mipmap_nearest:
4798  return D3DTEXF_POINT;
4799 
4800  case SamplerState::FT_linear_mipmap_nearest:
4801  return D3DTEXF_LINEAR;
4802 
4803  case SamplerState::FT_nearest_mipmap_linear:
4804  return D3DTEXF_POINT;
4805 
4806  case SamplerState::FT_linear_mipmap_linear:
4807  return D3DTEXF_LINEAR;
4808 
4809  case SamplerState::FT_shadow:
4810  case SamplerState::FT_default:
4811  return D3DTEXF_LINEAR;
4812  }
4813 
4814  dxgsg9_cat.error()
4815  << "Invalid FilterType value (" << (int)filter_type << ")\n";
4816  return D3DTEXF_POINT;
4817 }
4818 
4819 /**
4820  *
4821  */
4822 D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian9::
4823 get_d3d_mip_type(SamplerState::FilterType filter_type) {
4824  switch (filter_type) {
4825  case SamplerState::FT_nearest:
4826  return D3DTEXF_NONE;
4827 
4828  case SamplerState::FT_linear:
4829  return D3DTEXF_NONE;
4830 
4831  case SamplerState::FT_nearest_mipmap_nearest:
4832  return D3DTEXF_POINT;
4833 
4834  case SamplerState::FT_linear_mipmap_nearest:
4835  return D3DTEXF_POINT;
4836 
4837  case SamplerState::FT_nearest_mipmap_linear:
4838  return D3DTEXF_LINEAR;
4839 
4840  case SamplerState::FT_linear_mipmap_linear:
4841  return D3DTEXF_LINEAR;
4842 
4843  case SamplerState::FT_shadow:
4844  case SamplerState::FT_default:
4845  return D3DTEXF_NONE;
4846  }
4847 
4848  dxgsg9_cat.error()
4849  << "Invalid FilterType value (" << (int)filter_type << ")\n";
4850  return D3DTEXF_NONE;
4851 }
4852 
4853 /**
4854  * Returns the D3DTEXTUREOP value corresponding to the indicated
4855  * TextureStage::CombineMode enumerated type.
4856  */
4857 D3DTEXTUREOP DXGraphicsStateGuardian9::
4858 get_texture_operation(TextureStage::CombineMode mode, int scale) {
4859  switch (mode) {
4860  case TextureStage::CM_undefined:
4861  case TextureStage::CM_replace:
4862  return D3DTOP_SELECTARG1;
4863 
4864  case TextureStage::CM_modulate:
4865  if (scale < 2) {
4866  return D3DTOP_MODULATE;
4867  } else if (scale < 4) {
4868  return D3DTOP_MODULATE2X;
4869  } else {
4870  return D3DTOP_MODULATE4X;
4871  }
4872 
4873  case TextureStage::CM_add:
4874  return D3DTOP_ADD;
4875 
4876  case TextureStage::CM_add_signed:
4877  if (scale < 2) {
4878  return D3DTOP_ADDSIGNED;
4879  } else {
4880  return D3DTOP_ADDSIGNED2X;
4881  }
4882 
4883  case TextureStage::CM_interpolate:
4884  return D3DTOP_LERP;
4885 
4886  case TextureStage::CM_subtract:
4887  return D3DTOP_SUBTRACT;
4888 
4889  case TextureStage::CM_dot3_rgb:
4890  case TextureStage::CM_dot3_rgba:
4891  return D3DTOP_DOTPRODUCT3;
4892  }
4893 
4894  dxgsg9_cat.error()
4895  << "Invalid TextureStage::CombineMode value (" << (int)mode << ")\n";
4896  return D3DTOP_DISABLE;
4897 }
4898 
4899 /**
4900  * Returns the D3DTA value corresponding to the indicated
4901  * TextureStage::CombineSource and TextureStage::CombineOperand enumerated
4902  * types.
4903  */
4904 DWORD DXGraphicsStateGuardian9::
4905 get_texture_argument(TextureStage::CombineSource source,
4906  TextureStage::CombineOperand operand) const {
4907  switch (source) {
4908  case TextureStage::CS_undefined:
4909  case TextureStage::CS_texture:
4910  return D3DTA_TEXTURE | get_texture_argument_modifier(operand);
4911 
4912  case TextureStage::CS_constant:
4913  case TextureStage::CS_constant_color_scale:
4914  return _constant_color_operand | get_texture_argument_modifier(operand);
4915 
4916  case TextureStage::CS_primary_color:
4917  return D3DTA_DIFFUSE | get_texture_argument_modifier(operand);
4918 
4919  case TextureStage::CS_previous:
4920  return D3DTA_CURRENT | get_texture_argument_modifier(operand);
4921 
4922  case TextureStage::CS_last_saved_result:
4923  return D3DTA_TEMP | get_texture_argument_modifier(operand);
4924  }
4925  dxgsg9_cat.error()
4926  << "Invalid TextureStage::CombineSource value (" << (int)source << ")\n";
4927  return D3DTA_CURRENT;
4928 }
4929 
4930 /**
4931  * Returns the extra bits that modify the D3DTA argument, according to the
4932  * indicated TextureStage::CombineOperand enumerated type.
4933  */
4934 DWORD DXGraphicsStateGuardian9::
4935 get_texture_argument_modifier(TextureStage::CombineOperand operand) {
4936  switch (operand) {
4937  case TextureStage::CO_src_color:
4938  return 0;
4939 
4940  case TextureStage::CO_one_minus_src_color:
4941  return D3DTA_COMPLEMENT;
4942 
4943  case TextureStage::CO_src_alpha:
4944  return D3DTA_ALPHAREPLICATE;
4945 
4946  case TextureStage::CO_one_minus_src_alpha:
4947  return D3DTA_ALPHAREPLICATE | D3DTA_COMPLEMENT;
4948 
4949  case TextureStage::CO_undefined:
4950  break;
4951  }
4952  dxgsg9_cat.error()
4953  << "Invalid TextureStage::CombineOperand value (" << (int)operand << ")\n";
4954  return 0;
4955 }
4956 
4957 /**
4958  * Issues the DrawPrimitiveUP call to draw the indicated primitive_type from
4959  * the given buffer. We add the num_vertices parameter, so we can determine
4960  * the size of the buffer.
4961  */
4962 void DXGraphicsStateGuardian9::
4963 draw_primitive_up(D3DPRIMITIVETYPE primitive_type,
4964  unsigned int primitive_count,
4965  unsigned int first_vertex,
4966  unsigned int num_vertices,
4967  const unsigned char *buffer, size_t stride) {
4968 
4969  // It appears that the common ATI driver seems to fail to draw anything in
4970  // the DrawPrimitiveUP() call if the address range of the buffer supplied
4971  // crosses over a multiple of 0x10000. That's incredibly broken, yet it
4972  // undeniably appears to be true. We'll have to hack around it.
4973 
4974  const unsigned char *buffer_start = buffer + stride * first_vertex;
4975  const unsigned char *buffer_end = buffer_start + stride * num_vertices;
4976 
4977  if (buffer_end - buffer_start > 0x10000) {
4978  // Actually, the buffer doesn't fit within the required limit anyway. Go
4979  // ahead and draw it and hope for the best.
4980  _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count,
4981  buffer_start, stride);
4982 
4983  } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) {
4984  // No problem; we can draw the buffer directly.
4985  _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count,
4986  buffer_start, stride);
4987 
4988  } else {
4989  // We have a problem--the buffer crosses over a 0x10000 boundary. We have
4990  // to copy the buffer to a temporary buffer that we can draw from.
4991  unsigned char *safe_buffer_start = get_safe_buffer_start();
4992  memcpy(safe_buffer_start, buffer_start, buffer_end - buffer_start);
4993  _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count,
4994  safe_buffer_start, stride);
4995 
4996  }
4997 }
4998 
4999 /**
5000  * Issues the DrawIndexedPrimitiveUP call to draw the indicated primitive_type
5001  * from the given buffer. As in draw_primitive_up(), above, the parameter
5002  * list is not exactly one-for-one with the DrawIndexedPrimitiveUP() call, but
5003  * it's similar (in particular, we pass max_index instead of NumVertices,
5004  * which always seemed ambiguous to me).
5005  */
5006 void DXGraphicsStateGuardian9::
5007 draw_indexed_primitive_up(D3DPRIMITIVETYPE primitive_type,
5008  unsigned int min_index, unsigned int max_index,
5009  unsigned int num_primitives,
5010  const unsigned char *index_data,
5011  D3DFORMAT index_type,
5012  const unsigned char *buffer, size_t stride) {
5013  // As above, we'll hack the case of the buffer crossing the 0x10000
5014  // boundary.
5015  const unsigned char *buffer_start = buffer + stride * min_index;
5016  const unsigned char *buffer_end = buffer + stride * (max_index + 1);
5017 
5018  if (buffer_end - buffer_start > 0x10000) {
5019  // Actually, the buffer doesn't fit within the required limit anyway. Go
5020  // ahead and draw it and hope for the best.
5021  _d3d_device->DrawIndexedPrimitiveUP
5022  (primitive_type, min_index, max_index - min_index + 1, num_primitives,
5023  index_data, index_type, buffer, stride);
5024 
5025  } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) {
5026  // No problem; we can draw the buffer directly.
5027  _d3d_device->DrawIndexedPrimitiveUP
5028  (primitive_type, min_index, max_index - min_index + 1, num_primitives,
5029  index_data, index_type, buffer, stride);
5030 
5031  } else {
5032  // We have a problem--the buffer crosses over a 0x10000 boundary. We have
5033  // to copy the buffer to a temporary buffer that we can draw from.
5034  unsigned char *safe_buffer_start = get_safe_buffer_start();
5035  memcpy(safe_buffer_start, buffer_start, buffer_end - buffer_start);
5036  _d3d_device->DrawIndexedPrimitiveUP
5037  (primitive_type, min_index, max_index - min_index + 1, num_primitives,
5038  index_data, index_type, safe_buffer_start - stride * min_index, stride);
5039  }
5040 }
5041 
5042 /**
5043  * This function is called after the creation of textures, vertex buffers, and
5044  * index buffers to check if DirectX is out of memory. If DirectX is out of
5045  * memory and the LRU is being used, then page out some memory. This function
5046  * is a fail-safe just in case another process allocates video memory, DirectX
5047  * is fragmented, or there are some borderline memory allocation cases, ...
5048  */
5050 check_dx_allocation (HRESULT result, int allocation_size, int attempts)
5051 {
5052  bool retry;
5053 
5054  retry = false;
5055  if (attempts <= 4)
5056  {
5057  switch (result) {
5058  case D3D_OK:
5059  break;
5060 
5061  case D3DERR_OUTOFVIDEOMEMORY:
5062  case E_OUTOFMEMORY:
5063  // increase the page out size as the number of attempts increases
5064  {
5065  size_t current_size = _prepared_objects->_graphics_memory_lru.get_total_size();
5066  size_t target_size = max(current_size - allocation_size * attempts, (size_t) 0);
5067  _prepared_objects->_graphics_memory_lru.evict_to(target_size);
5068  dxgsg9_cat.info()
5069  << "Evicted " << current_size - _prepared_objects->_graphics_memory_lru.get_total_size() << " bytes of texture memory to make room for more.\n";
5070  if (_prepared_objects->_graphics_memory_lru.get_total_size() < current_size) {
5071  retry = true;
5072  }
5073  }
5074  break;
5075 
5076  default:
5077  break;
5078  }
5079  }
5080 
5081  return retry;
5082 }
5083 
5084 // DX stencil code section
5085 static int dx_stencil_comparison_function_array[] = {
5086  D3DCMP_NEVER,
5087  D3DCMP_LESS,
5088  D3DCMP_EQUAL,
5089  D3DCMP_LESSEQUAL,
5090  D3DCMP_GREATER,
5091  D3DCMP_NOTEQUAL,
5092  D3DCMP_GREATEREQUAL,
5093  D3DCMP_ALWAYS,
5094 };
5095 
5096 static int dx_stencil_operation_array[] = {
5097  D3DSTENCILOP_KEEP,
5098  D3DSTENCILOP_ZERO,
5099  D3DSTENCILOP_REPLACE,
5100  D3DSTENCILOP_INCR,
5101  D3DSTENCILOP_DECR,
5102  D3DSTENCILOP_INVERT,
5103 
5104  D3DSTENCILOP_INCRSAT,
5105  D3DSTENCILOP_DECRSAT,
5106 };
5107 
5108 /**
5109  * Set stencil render states.
5110  */
5111 void DXGraphicsStateGuardian9::
5112 do_issue_stencil() {
5113  if (!_supports_stencil) {
5114  return;
5115  }
5116 
5117  const StencilAttrib *stencil = DCAST(StencilAttrib, _target_rs->get_attrib(StencilAttrib::get_class_slot()));
5118 
5119  if (stencil != nullptr) {
5120  // DEBUG
5121  if (false) {
5122  dxgsg9_cat.debug() << "STENCIL STATE CHANGE\n";
5123  dxgsg9_cat.debug() << "\n"
5124  << "SRS_front_comparison_function " << stencil->get_render_state(StencilAttrib::SRS_front_comparison_function) << "\n"
5125  << "SRS_front_stencil_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_front_stencil_fail_operation) << "\n"
5126  << "SRS_front_stencil_pass_z_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_fail_operation) << "\n"
5127  << "SRS_front_stencil_pass_z_pass_operation " << stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_pass_operation) << "\n"
5128  << "SRS_reference " << stencil->get_render_state(StencilAttrib::SRS_reference) << "\n"
5129  << "SRS_read_mask " << stencil->get_render_state(StencilAttrib::SRS_read_mask) << "\n"
5130  << "SRS_write_mask " << stencil->get_render_state(StencilAttrib::SRS_write_mask) << "\n"
5131  << "SRS_back_comparison_function " << stencil->get_render_state(StencilAttrib::SRS_back_comparison_function) << "\n"
5132  << "SRS_back_stencil_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_back_stencil_fail_operation) << "\n"
5133  << "SRS_back_stencil_pass_z_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_fail_operation) << "\n"
5134  << "SRS_back_stencil_pass_z_pass_operation " << stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation) << "\n";
5135  }
5136 
5137  bool on = false;
5138 
5139  unsigned int front_compare;
5140  front_compare = stencil->get_render_state(StencilAttrib::SRS_front_comparison_function);
5141 
5142  if (front_compare != RenderAttrib::M_none) {
5143  set_render_state(D3DRS_STENCILENABLE, TRUE);
5144  set_render_state(D3DRS_STENCILFUNC, dx_stencil_comparison_function_array[front_compare]);
5145  set_render_state(D3DRS_STENCILFAIL, dx_stencil_operation_array[
5146  stencil->get_render_state(StencilAttrib::SRS_front_stencil_fail_operation)]);
5147  set_render_state(D3DRS_STENCILZFAIL, dx_stencil_operation_array[
5148  stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_fail_operation)]);
5149  set_render_state(D3DRS_STENCILPASS, dx_stencil_operation_array[
5150  stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_pass_operation)]);
5151  on = true;
5152  } else {
5153  set_render_state(D3DRS_STENCILENABLE, FALSE);
5154  }
5155 
5156  if (_supports_two_sided_stencil) {
5157  unsigned int back_compare;
5158  back_compare = stencil->get_render_state(StencilAttrib::SRS_back_comparison_function);
5159 
5160  if (back_compare != RenderAttrib::M_none) {
5161  set_render_state(D3DRS_TWOSIDEDSTENCILMODE, TRUE);
5162  set_render_state(D3DRS_CCW_STENCILFUNC, dx_stencil_comparison_function_array[back_compare]);
5163  set_render_state(D3DRS_CCW_STENCILFAIL, dx_stencil_operation_array[
5164  stencil->get_render_state(StencilAttrib::SRS_back_stencil_fail_operation)]);
5165  set_render_state(D3DRS_CCW_STENCILZFAIL, dx_stencil_operation_array[
5166  stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_fail_operation)]);
5167  set_render_state(D3DRS_CCW_STENCILPASS, dx_stencil_operation_array[
5168  stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation)]);
5169  on = true;
5170  } else {
5171  set_render_state(D3DRS_TWOSIDEDSTENCILMODE, FALSE);
5172  }
5173  }
5174 
5175  if (on) {
5176  set_render_state(D3DRS_STENCILREF, stencil->get_render_state(StencilAttrib::SRS_reference));
5177  set_render_state(D3DRS_STENCILMASK, stencil->get_render_state(StencilAttrib::SRS_read_mask));
5178  set_render_state(D3DRS_STENCILWRITEMASK, stencil->get_render_state(StencilAttrib::SRS_write_mask));
5179  }
5180 
5181  if (stencil->get_render_state(StencilAttrib::SRS_clear)) {
5182  _d3d_device->Clear(0, nullptr, D3DCLEAR_STENCIL, 0, 0.0f, stencil->get_render_state(StencilAttrib::SRS_clear_value));
5183  }
5184 
5185  } else {
5186  // DEBUG
5187  if (false) {
5188  dxgsg9_cat.debug() << "STENCIL STATE CHANGE TO OFF\n";
5189  }
5190 
5191  set_render_state(D3DRS_STENCILENABLE, FALSE);
5192  if (_supports_two_sided_stencil) {
5193  set_render_state(D3DRS_TWOSIDEDSTENCILMODE, FALSE);
5194  }
5195  }
5196 }
5197 
5198 /**
5199  *
5200  */
5201 void DXGraphicsStateGuardian9::
5202 do_issue_scissor() {
5203  const ScissorAttrib *target_scissor = DCAST(ScissorAttrib, _target_rs->get_attrib_def(ScissorAttrib::get_class_slot()));
5204  const LVecBase4 &frame = target_scissor->get_frame();
5205 
5206  RECT r;
5207  r.left = _current_viewport.X + _current_viewport.Width * frame[0];
5208  r.top = _current_viewport.Y + _current_viewport.Height * (1.0f - frame[3]);
5209  r.right = _current_viewport.X + _current_viewport.Width * frame[1];
5210  r.bottom = _current_viewport.Y + _current_viewport.Height * (1.0f - frame[2]);
5211  _d3d_device->SetScissorRect(&r);
5212  set_render_state(D3DRS_SCISSORTESTENABLE, TRUE);
5213 }
5214 
5215 /**
5216  * Convert DirectX framebuffer format ids into a FrameBufferProperties
5217  * structure.
5218  */
5220 calc_fb_properties(DWORD cformat, DWORD dformat,
5221  DWORD multisampletype, DWORD multisamplequality) {
5222  FrameBufferProperties props;
5223  int index=0;
5224  int r=0, g=0, b=0, a=0;
5225  switch (cformat) {
5226  case D3DFMT_R8G8B8: r=8; g=8; b=8; a=0; break;
5227  case D3DFMT_A8R8G8B8: r=8; g=8; b=8; a=8; break;
5228  case D3DFMT_X8R8G8B8: r=8; g=8; b=8; a=0; break;
5229  case D3DFMT_R5G6B5: r=5; g=6; b=5; a=0; break;
5230  case D3DFMT_X1R5G5B5: r=5; g=5; b=5; a=0; break;
5231  case D3DFMT_A1R5G5B5: r=5; g=5; b=5; a=1; break;
5232  case D3DFMT_A4R4G4B4: r=4; g=4; b=4; a=4; break;
5233  case D3DFMT_R3G3B2: r=3; g=3; b=2; a=0; break;
5234  case D3DFMT_A8R3G3B2: r=3; g=3; b=2; a=8; break;
5235  case D3DFMT_X4R4G4B4: r=4; g=4; b=4; a=0; break;
5236  case D3DFMT_A2B10G10R10: r=10;g=10;b=10;a=2; break;
5237  case D3DFMT_A8P8: index=8; a=8; break;
5238  case D3DFMT_P8: index=8; a=0; break;
5239  default: break;
5240  }
5241  if (index > 0) {
5242  props.set_rgb_color(0);
5243  props.set_indexed_color(1);
5244  props.set_color_bits(index);
5245  } else if (r + g + b > 0) {
5246  props.set_rgb_color(1);
5247  props.set_indexed_color(0);
5248  props.set_rgba_bits(r, g, b, a);
5249  }
5250  props.set_alpha_bits(a);
5251 
5252  int depth=0;
5253  int stencil=0;
5254  switch (dformat) {
5255  case D3DFMT_D32: depth=32; stencil=0; break;
5256  case D3DFMT_D15S1: depth=15; stencil=1; break;
5257  case D3DFMT_D24S8: depth=24; stencil=8; break;
5258  case D3DFMT_D16: depth=16; stencil=0; break;
5259  case D3DFMT_D24X8: depth=24; stencil=0; break;
5260  case D3DFMT_D24X4S4: depth=24; stencil=4; break;
5261  default: break;
5262  }
5263  props.set_depth_bits(depth);
5264  props.set_stencil_bits(stencil);
5265  if (multisampletype == D3DMULTISAMPLE_NONMASKABLE) {
5266  props.set_multisamples(2);
5267  } else {
5268  props.set_multisamples(multisampletype);
5269  }
5270  return props;
5271 }
5272 
5273 
5274 #define GAMMA_1 (255.0 * 256.0)
5275 
5276 static bool _gamma_table_initialized = false;
5277 static bool _gamma_changed = false;
5278 static unsigned short _original_gamma_table [256 * 3];
5279 
5280 void _create_gamma_table_dx9 (PN_stdfloat gamma, unsigned short *original_red_table, unsigned short *original_green_table, unsigned short *original_blue_table, unsigned short *red_table, unsigned short *green_table, unsigned short *blue_table) {
5281  int i;
5282  double gamma_correction;
5283 
5284  if (gamma <= 0.0) {
5285  // avoid divide by zero and negative exponents
5286  gamma = 1.0;
5287  }
5288  gamma_correction = 1.0 / (double) gamma;
5289 
5290  for (i = 0; i < 256; i++) {
5291  double r;
5292  double g;
5293  double b;
5294 
5295  if (original_red_table) {
5296  r = (double) original_red_table [i] / GAMMA_1;
5297  g = (double) original_green_table [i] / GAMMA_1;
5298  b = (double) original_blue_table [i] / GAMMA_1;
5299  }
5300  else {
5301  r = ((double) i / 255.0);
5302  g = r;
5303  b = r;
5304  }
5305 
5306  r = pow (r, gamma_correction);
5307  g = pow (g, gamma_correction);
5308  b = pow (b, gamma_correction);
5309 
5310  if (r > 1.00) {
5311  r = 1.0;
5312  }
5313  if (g > 1.00) {
5314  g = 1.0;
5315  }
5316  if (b > 1.00) {
5317  b = 1.0;
5318  }
5319 
5320  r = r * GAMMA_1;
5321  g = g * GAMMA_1;
5322  b = b * GAMMA_1;
5323 
5324  red_table [i] = r;
5325  green_table [i] = g;
5326  blue_table [i] = b;
5327  }
5328 }
5329 
5330 /**
5331  * Static function for getting the original gamma.
5332  */
5334 get_gamma_table(void) {
5335  bool get;
5336 
5337  get = false;
5338  if (_gamma_table_initialized == false) {
5339  HDC hdc = GetDC(nullptr);
5340 
5341  if (hdc) {
5342  if (GetDeviceGammaRamp (hdc, (LPVOID) _original_gamma_table)) {
5343  _gamma_table_initialized = true;
5344  get = true;
5345  }
5346 
5347  ReleaseDC (nullptr, hdc);
5348  }
5349  }
5350 
5351  return get;
5352 }
5353 
5354 /**
5355  * Static function for setting gamma which is needed for atexit.
5356  */
5358 static_set_gamma(bool restore, PN_stdfloat gamma) {
5359  bool set;
5360  HDC hdc = GetDC(nullptr);
5361 
5362  set = false;
5363  if (hdc) {
5364  unsigned short ramp [256 * 3];
5365 
5366  if (restore && _gamma_table_initialized) {
5367  _create_gamma_table_dx9 (gamma, &_original_gamma_table [0], &_original_gamma_table [256], &_original_gamma_table [512], &ramp [0], &ramp [256], &ramp [512]);
5368  }
5369  else {
5370  _create_gamma_table_dx9 (gamma, 0, 0, 0, &ramp [0], &ramp [256], &ramp [512]);
5371  }
5372 
5373  if (SetDeviceGammaRamp (hdc, ramp)) {
5374  set = true;
5375  _gamma_changed = !restore;
5376  }
5377 
5378  ReleaseDC (nullptr, hdc);
5379  }
5380 
5381  return set;
5382 }
5383 
5384 /**
5385  * Non static version of setting gamma. Returns true on success.
5386  */
5388 set_gamma(PN_stdfloat gamma) {
5389  bool set;
5390 
5391  set = static_set_gamma(false, gamma);
5392  if (set) {
5393  _gamma = gamma;
5394  }
5395 
5396  return set;
5397 }
5398 
5399 /**
5400  * Restore original gamma.
5401  */
5403 restore_gamma() {
5404  static_set_gamma(true, 1.0f);
5405 }
5406 
5407 /**
5408  * This function is passed to the atexit function.
5409  */
5411 atexit_function(void) {
5412  set_cg_device(nullptr);
5413  if (_gamma_changed) {
5414  static_set_gamma(true, 1.0f);
5415  }
5416 }
5417 
5418 /**
5419  * Returns true if this particular GSG supports the specified Cg Shader
5420  * Profile.
5421  */
5423 get_supports_cg_profile(const std::string &name) const {
5424 #ifndef HAVE_CG
5425  return false;
5426 #else
5427  CGprofile profile = cgGetProfile(name.c_str());
5428 
5429  if (profile == CG_PROFILE_UNKNOWN) {
5430  dxgsg9_cat.error() << name <<", unknown Cg-profile\n";
5431  return false;
5432  }
5433  return cgD3D9IsProfileSupported(cgGetProfile(name.c_str())) != 0;
5434 #endif // HAVE_CG
5435 }
5436 
5437 /**
5438  * Sets the global Cg device pointer. TODO: make this thread-safe somehow.
5439  * Maybe Cg is inherently not thread-safe.
5440  */
5442 set_cg_device(LPDIRECT3DDEVICE9 cg_device) {
5443 #ifdef HAVE_CG
5444  if (_cg_device != cg_device) {
5445  cgD3D9SetDevice(cg_device);
5446  _cg_device = cg_device;
5447  }
5448 #endif // HAVE_CG
5449 }
5450 
5451 /**
5452  * Returns a vertex buffer containing only a full-white color.
5453  */
5454 LPDIRECT3DVERTEXBUFFER9 DXGraphicsStateGuardian9::
5456  if (_white_vbuffer != nullptr) {
5457  return _white_vbuffer;
5458  }
5459 
5460  LPDIRECT3DVERTEXBUFFER9 vbuffer;
5461  HRESULT hr;
5462  hr = _screen->_d3d_device->CreateVertexBuffer(sizeof(D3DCOLOR), D3DUSAGE_WRITEONLY, D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &vbuffer, nullptr);
5463 
5464  if (FAILED(hr)) {
5465  dxgsg9_cat.error()
5466  << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
5467  return nullptr;
5468  }
5469 
5470  D3DCOLOR *local_pointer;
5471  hr = vbuffer->Lock(0, sizeof(D3DCOLOR), (void **) &local_pointer, D3DLOCK_DISCARD);
5472  if (FAILED(hr)) {
5473  dxgsg9_cat.error()
5474  << "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
5475  return false;
5476  }
5477 
5478  *local_pointer = D3DCOLOR_ARGB(255, 255, 255, 255);
5479 
5480  vbuffer->Unlock();
5481  _white_vbuffer = vbuffer;
5482  return vbuffer;
5483 }
5484 
5485 typedef std::string KEY;
5486 
5487 typedef struct _KEY_ELEMENT
5488 {
5489  KEY key;
5490  int count;
5491  int secondary_count;
5492 
5493  struct _KEY_ELEMENT *next;
5494 }
5495 KEY_ELEMENT;
5496 
5497 typedef struct _KEY_LIST
5498 {
5499  int total_key_elements;
5500  KEY_ELEMENT *key_element;
5501 }
5502 KEY_LIST;
5503 
5504 KEY_ELEMENT *new_key_element (KEY key, KEY_LIST *key_list)
5505 {
5506  KEY_ELEMENT *key_element;
5507 
5508  key_element = new KEY_ELEMENT;
5509  key_element -> key = key;
5510  key_element -> count = 1;
5511  key_element -> secondary_count = 0;
5512  key_element -> next = 0;
5513 
5514  key_list -> total_key_elements++;
5515 
5516  return key_element;
5517 }
5518 
5519 KEY_ELEMENT *first_key_element (KEY_LIST *key_list)
5520 {
5521  return key_list -> key_element;
5522 }
5523 
5524 KEY_ELEMENT *next_key_element (KEY_ELEMENT *key_element)
5525 {
5526  return key_element -> next;
5527 }
5528 
5529 void delete_key_list (KEY_LIST *key_list)
5530 {
5531  if (key_list)
5532  {
5533  KEY_ELEMENT *key_element;
5534  KEY_ELEMENT *key_element_next;
5535 
5536  key_element = first_key_element (key_list);
5537  while (key_element)
5538  {
5539  key_element_next = next_key_element (key_element);
5540  delete key_element;
5541  key_element = key_element_next;
5542  }
5543 
5544  delete key_list;
5545  }
5546 }
5547 
5548 KEY_LIST *new_key_list (void)
5549 {
5550  KEY_LIST *key_list;
5551 
5552  key_list = new KEY_LIST;
5553  memset (key_list, 0, sizeof (KEY_LIST));
5554 
5555  return key_list;
5556 }
5557 
5558 KEY_ELEMENT *add_to_key_list (KEY key, KEY_LIST *key_list)
5559 {
5560  KEY_ELEMENT *key_element;
5561  KEY_ELEMENT *last_key_element;
5562  KEY_ELEMENT *current_key_element;
5563 
5564  key_element = 0;
5565  last_key_element = 0;
5566  current_key_element = key_list -> key_element;
5567  if (current_key_element == 0)
5568  {
5569  key_element = new_key_element (key, key_list);
5570  key_list -> key_element = key_element;
5571  }
5572  else
5573  {
5574  while (current_key_element)
5575  {
5576  if (key < current_key_element -> key)
5577  {
5578  key_element = new_key_element (key, key_list);
5579  key_element -> next = current_key_element;
5580 
5581  if (last_key_element == 0)
5582  {
5583  key_list -> key_element = key_element;
5584  }
5585  else
5586  {
5587  last_key_element -> next = key_element;
5588  }
5589  break;
5590  }
5591  else
5592  {
5593  if (key > current_key_element -> key)
5594  {
5595  if (current_key_element -> next == 0)
5596  {
5597  key_element = new_key_element (key, key_list);
5598  current_key_element -> next = key_element;
5599  break;
5600  }
5601  else
5602  {
5603 
5604  }
5605  }
5606  else
5607  {
5608  current_key_element -> count++;
5609  break;
5610  }
5611  }
5612 
5613  last_key_element = current_key_element;
5614  current_key_element = current_key_element -> next;
5615  }
5616  }
5617 
5618  return key_element;
5619 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void enqueue_lru(AdaptiveLru *lru)
Adds the page to the LRU for the first time, or marks it recently-accessed if it has already been add...
Enables or disables writing of pixel to framebuffer based on its alpha value relative to a reference ...
get_reference_alpha
Returns the alpha reference value.
get_mode
Returns the alpha write mode.
void set_active(bool flag)
Changes the active flag associated with this object.
Definition: bufferContext.I:65
This specifies how colors are blended into the frame buffer, for special effects.
get_operand_a
Returns the RGB multiplier for the first component.
get_alpha_operand_b
Returns the alpha multiplier for the second component.
get_alpha_mode
Returns the blending mode for the alpha channel.
get_operand_b
Returns the RGB multiplier for the second component.
get_mode
Returns the blending mode for the RGB channels.
get_alpha_operand_a
Returns the alpha multiplier for the first component.
Enables or disables writing to the color buffer.
get_channels
Returns the mask of color channels that are enabled by this attrib.
Similar to PointerToArray, except that its contents may not be modified.
Indicates which faces should be culled based on their vertex ordering.
get_effective_mode
Returns the effective culling mode.
This specialization on GeomMunger finesses vertices for DirectX rendering.
Definition: dxGeomMunger9.h:29
virtual void end_draw_primitives()
Called after a sequence of draw_primitive() functions are called, this should do whatever cleanup is ...
virtual bool begin_frame(Thread *current_thread)
Called before each frame is rendered, to allow the GSG a chance to do any internal cleanup before beg...
virtual void release_texture(TextureContext *tc)
Frees the GL resources previously allocated for the texture.
bool setup_array_data(DXVertexBufferContext9 *&vbc, const GeomVertexArrayDataHandle *data, bool force)
Internal function to bind a buffer object for the indicated data array, if appropriate,...
virtual VertexBufferContext * prepare_vertex_buffer(GeomVertexArrayData *data)
Creates a new retained-mode representation of the given data, and returns a newly-allocated VertexBuf...
virtual bool prepare_lens()
Makes the current lens (whichever lens was most recently specified with set_scene()) active,...
virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, const GeomVertexDataPipelineReader *data_reader, bool force)
Called before a sequence of draw_primitive() functions are called, this should prepare the vertex dat...
virtual bool get_supports_cg_profile(const std::string &name) const
Returns true if this particular GSG supports the specified Cg Shader Profile.
virtual bool framebuffer_copy_to_ram(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)
Copy the pixels within the indicated display region from the framebuffer into system memory,...
static void atexit_function(void)
This function is passed to the atexit function.
virtual bool draw_points(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected points.
bool do_framebuffer_copy_to_ram(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb, bool inverted)
This is the implementation of framebuffer_copy_to_ram(); it adds one additional parameter,...
virtual void reset()
Resets all internal state as if the gsg were newly created.
static bool static_set_gamma(bool restore, PN_stdfloat gamma)
Static function for setting gamma which is needed for atexit.
static DWORD LColor_to_D3DCOLOR(const LColor &cLColor)
Converts Panda's floating-point LColor structure to DirectX's D3DCOLOR packed structure.
virtual bool framebuffer_copy_to_texture(Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb)
Copy the pixels within the indicated display region from the framebuffer into texture memory.
void release_shader(ShaderContext *sc)
Releases the resources allocated by prepare_shader.
virtual bool draw_triangles(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected triangles.
virtual bool update_texture(TextureContext *tc, bool force)
Ensures that the current Texture data is refreshed onto the GSG.
void apply_texture(int i, TextureContext *tc, const SamplerState &sampler)
Makes the texture the currently available texture for rendering on the ith stage.
static bool get_gamma_table(void)
Static function for getting the original gamma.
virtual bool draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of line strips.
virtual bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a "scene" ...
HRESULT set_render_state(D3DRENDERSTATETYPE state, DWORD value)
This function creates a common layer between DX and Panda for SetRenderState.
virtual void clear(DrawableRegion *clearable)
Clears all of the indicated buffers to their assigned colors.
bool apply_vertex_buffer(VertexBufferContext *vbc, const GeomVertexArrayDataHandle *reader, bool force)
Updates the vertex buffer with the current data, and makes it the current vertex buffer for rendering...
LPDIRECT3DVERTEXBUFFER9 get_white_vbuffer()
Returns a vertex buffer containing only a full-white color.
void restore_gamma()
Restore original gamma.
virtual void release_index_buffer(IndexBufferContext *ibc)
Frees the GL resources previously allocated for the data.
bool check_dx_allocation(HRESULT result, int allocation_size, int attempts)
This function is called after the creation of textures, vertex buffers, and index buffers to check if...
virtual bool extract_texture_data(Texture *tex)
This method should only be called by the GraphicsEngine.
FrameBufferProperties calc_fb_properties(DWORD cformat, DWORD dformat, DWORD multisampletype, DWORD multisamplequality)
Convert DirectX framebuffer format ids into a FrameBufferProperties structure.
virtual bool draw_tristrips(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of triangle strips.
virtual bool draw_lines(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of disconnected line segments.
virtual bool draw_trifans(const GeomPrimitivePipelineReader *reader, bool force)
Draws a series of triangle fans.
virtual void set_state_and_transform(const RenderState *state, const TransformState *transform)
Simultaneously resets the render state and the transform state.
virtual TextureContext * prepare_texture(Texture *tex, int view)
Creates a new retained-mode representation of the given texture, and returns a newly-allocated Textur...
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Prepare a display region for rendering (set up scissor region and viewport)
virtual IndexBufferContext * prepare_index_buffer(GeomPrimitive *data)
Creates a new retained-mode representation of the given data, and returns a newly-allocated IndexBuff...
virtual void begin_occlusion_query()
Begins a new occlusion query.
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a "scene" (usual...
static void set_cg_device(LPDIRECT3DDEVICE9 cg_device)
Sets the global Cg device pointer.
bool apply_index_buffer(IndexBufferContext *ibc, const GeomPrimitivePipelineReader *reader, bool force)
Updates the index buffer with the current data, and makes it the current index buffer for rendering.
virtual void end_frame(Thread *current_thread)
Called after each frame is rendered, to allow the GSG a chance to do any internal cleanup after rende...
HRESULT set_sampler_state(DWORD sampler, D3DSAMPLERSTATETYPE type, DWORD value)
This function creates a common layer between DX and Panda.
ShaderContext * prepare_shader(Shader *se)
Compile a vertex/fragment shader body.
virtual void bind_light(PointLight *light_obj, const NodePath &light, int light_id)
Called the first time a particular light has been bound to a given id within a frame,...
virtual void release_vertex_buffer(VertexBufferContext *vbc)
Frees the GL resources previously allocated for the data.
bool upload_texture(DXTextureContext9 *dtc, bool force)
Creates a texture surface on the graphics card and fills it with its pixel data.
static D3DFORMAT get_index_type(Geom::NumericType numeric_type)
Maps from the Geom's internal numeric type symbols to DirectX's.
HRESULT set_texture_stage_state(DWORD stage, D3DTEXTURESTAGESTATETYPE type, DWORD value)
This function creates a common layer between DX and Panda.
Caches a GeomPrimitive in the DirectX device as an index buffer.
bool upload_data(const GeomPrimitivePipelineReader *reader, bool force)
Copies the latest data from the client store to DirectX.
void create_ibuffer(DXScreenData &scrn, const GeomPrimitivePipelineReader *reader)
Creates a new index buffer (but does not upload data to it).
bool update_shader_vertex_arrays(DXShaderContext9 *prev, GSG *gsg, bool force)
Disables all vertex arrays used by the previous shader, then enables all the vertex arrays needed by ...
void disable_shader_vertex_arrays(GSG *gsg)
Disable all the vertex arrays used by this shader.
void unbind(GSG *gsg)
This function disables a currently-bound shader.
void disable_shader_texture_bindings(GSG *gsg)
Disable all the texture bindings used by this shader.
bool bind(GSG *gsg)
This function is to be called to enable a new shader.
void update_shader_texture_bindings(DXShaderContext9 *prev, GSG *gsg)
Disables all texture bindings used by the previous shader, then enables all the texture bindings need...
void issue_parameters(GSG *gsg, int altered)
This function gets called whenever the RenderState or TransformState has changed, but the Shader itse...
bool create_texture(DXScreenData &scrn)
Use panda texture's pixelbuffer to create a texture for the specified device.
IDirect3DTexture9 * get_d3d_2d_texture() const
Returns the Direct3D object that represents the texture, in the case of a 1-d or 2-d texture.
IDirect3DBaseTexture9 * get_d3d_texture() const
Returns the Direct3D object that represents the texture, whatever kind of texture it is.
bool extract_texture_data(DXScreenData &scrn)
This method will be called in the draw thread to download the texture memory's image into its ram_ima...
void delete_texture()
Release the surface used to store the texture.
static HRESULT d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface9 *d3d_surface, bool inverted, Texture *result, int view, int z)
copies source_rect in pD3DSurf to upper left of texture
bool has_mipmaps() const
Returns true if the texture was created with mipmaps, false otherwise.
Caches a GeomVertexArrayData in the DirectX device as a vertex buffer.
This is a special kind of attribute that instructs the graphics driver to apply an offset or bias to ...
get_offset
Returns the depth offset represented by this attrib.
Enables or disables writing to the depth buffer.
get_mode
Returns the depth write mode.
Enables or disables writing to the depth buffer.
get_mode
Returns the depth write mode.
A light shining from infinitely far away in a particular direction, like sunlight.
get_direction
Returns the direction in which the light is aimed.
get_specular_color
Returns the color of specular highlights generated by the light.
Encapsulates the data from a DisplayRegion, pre-fetched for one stage of the pipeline.
void get_region_pixels_i(int &xo, int &yo, int &w, int &h) const
Similar to get_region_pixels(), but returns the upper left corner, and the pixel numbers are numbered...
A rectangular subregion within a window for rendering into.
Definition: displayRegion.h:57
void get_region_pixels_i(int &xo, int &yo, int &w, int &h) const
Similar to get_region_pixels(), but returns the upper left corner, and the pixel numbers are numbered...
This is a base class for GraphicsWindow (actually, GraphicsOutput) and DisplayRegion,...
get_clear_color
Returns the current clear color value.
get_clear_stencil
Returns the current clear stencil value.
bool get_clear_depth_active() const
Returns the current setting of the flag that indicates whether the depth buffer should be cleared eve...
get_clear_depth
Returns the current clear depth value.
bool get_clear_stencil_active() const
Returns the current setting of the flag that indicates whether the color buffer should be cleared eve...
bool get_clear_color_active() const
Returns the current setting of the flag that indicates whether the color buffer should be cleared eve...
Applies a Fog to the geometry at and below this node.
Definition: fogAttrib.h:25
get_fog
If the FogAttrib is not an 'off' FogAttrib, returns the fog that is associated.
Definition: fogAttrib.h:38
bool is_off() const
Returns true if the FogAttrib is an 'off' FogAttrib, indicating that it should disable fog.
Definition: fogAttrib.I:26
Specifies how atmospheric fog effects are applied to geometry.
Definition: fog.h:41
get_color
Returns the color of the fog.
Definition: fog.h:68
get_exp_density
Returns the density of the fog for exponential calculations.
Definition: fog.h:86
void get_linear_range(PN_stdfloat &onset, PN_stdfloat &opaque)
Retrieves the current onset and offset ranges.
Definition: fog.cxx:173
A container for the various kinds of properties we might ask to have on a graphics frameBuffer before...
set_color_bits
Sets the number of requested color bits as a single number that represents the sum of the individual ...
void set_rgba_bits(int r, int g, int b, int a)
Convenience method for setting the red, green, blue and alpha bits in one go.
Objects of this class are used to convert vertex data from a Geom into a format suitable for passing ...
Definition: geomMunger.h:50
Encapsulates the data from a Geom, pre-fetched for one stage of the pipeline.
Definition: geom.h:405
Encapsulates the data from a GeomPrimitive, pre-fetched for one stage of the pipeline.
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
This object describes how the vertex animation, if any, represented in a GeomVertexData is encoded.
get_animation_type
Returns the type of animation represented by this spec.
get_indexed_transforms
This is only meaningful for animation_type AT_hardware.
get_num_transforms
This is only meaningful for animation_type AT_hardware.
This data object is returned by GeomVertexArrayData::get_handle() or modify_handle().
const unsigned char * get_read_pointer(bool force) const
Returns a readable pointer to the beginning of the actual data stream, or NULL if the data is not cur...
This is the data for one array of a GeomVertexData structure.
This describes the structure of a single array within a Geom data.
get_stride
Returns the total number of bytes reserved in the array for each vertex.
Encapsulates the data from a GeomVertexData, pre-fetched for one stage of the pipeline.
This class defines the physical layout of the vertex data stored within a Geom.
get_animation
Returns the GeomVertexAnimationSpec that indicates how this format's vertices are set up for animatio...
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
int get_data1i()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
This class is the main interface to controlling the render process.
void reset_all_windows(bool swapchain)
Resets the framebuffer of the current window.
An object to create GraphicsOutputs that share a particular 3-D API.
Definition: graphicsPipe.h:52
static void add_gsg(GraphicsStateGuardianBase *gsg)
Called by a GSG after it has been initialized, to add a new GSG to the available list.
Encapsulates all the communication with a particular instance of a given rendering backend.
void release_all()
Releases all prepared objects.
virtual bool begin_draw_primitives(const GeomPipelineReader *geom_reader, const GeomVertexDataPipelineReader *data_reader, bool force)
Called before a sequence of draw_primitive() functions are called, this should prepare the vertex dat...
GraphicsEngine * get_engine() const
Returns the graphics engine that created this GSG.
virtual bool get_supports_compressed_texture_format(int compression_mode) const
Returns true if this GSG can accept textures pre-compressed in the indicated format.
virtual void prepare_display_region(DisplayRegionPipelineReader *dr)
Makes the specified DisplayRegion current.
get_prepared_objects
Returns the set of texture and geom objects that have been prepared with this GSG (and possibly other...
void mark_new()
Marks the GSG as "new", so that the next call to reset_if_new() will be effective.
virtual void do_issue_light()
This implementation of do_issue_light() assumes we have a limited number of hardware lights available...
void do_issue_color()
This method is defined in the base class because it is likely that this functionality will be used fo...
virtual void end_frame(Thread *current_thread)
Called after each frame is rendered, to allow the GSG a chance to do any internal cleanup after rende...
virtual void end_scene()
Called between begin_frame() and end_frame() to mark the end of drawing commands for a "scene" (usual...
virtual void reset()
Resets all internal state as if the gsg were newly created.
int release_all_vertex_buffers()
Frees the resources for all vertex buffers associated with this GSG.
virtual bool begin_frame(Thread *current_thread)
Called before each frame is rendered, to allow the GSG a chance to do any internal cleanup before beg...
virtual void end_draw_primitives()
Called after a sequence of draw_primitive() functions are called, this should do whatever cleanup is ...
int release_all_index_buffers()
Frees the resources for all index buffers associated with this GSG.
virtual bool begin_scene()
Called between begin_frame() and end_frame() to mark the beginning of drawing commands for a "scene" ...
This is a special class object that holds all the information returned by a particular GSG to indicat...
bool changed_size(const GeomPrimitivePipelineReader *reader) const
Returns true if the data has changed size since the last time mark_loaded() was called.
void mark_loaded(const GeomPrimitivePipelineReader *reader)
Should be called after the IndexBufferContext has been loaded into graphics memory,...
bool was_modified(const GeomPrimitivePipelineReader *reader) const
Returns true if the data has been modified since the last time mark_loaded() was called.
Lens * get_lens(int index=0) const
Returns a pointer to the particular Lens associated with this LensNode, or NULL if there is not yet a...
Definition: lensNode.I:47
A base class for any number of different kinds of lenses, linear and otherwise.
Definition: lens.h:41
virtual bool is_linear() const
Returns true if the lens represents a linear projection (e.g.
Definition: lens.cxx:540
PN_stdfloat get_hfov() const
Returns the horizontal component of fov only.
Definition: lens.I:344
get_nodal_point
Returns the center point of the lens: the point from which the lens is viewing.
Definition: lens.h:129
const LVector3 & get_view_vector() const
Returns the axis along which the lens is facing.
Definition: lens.cxx:214
const LMatrix4 & get_projection_mat(StereoChannel channel=SC_mono) const
Returns the complete transformation matrix from a 3-d point in space to a point on the film,...
Definition: lens.I:563
The abstract interface to all kinds of lights.
Definition: light.h:38
get_color
Returns the basic color of the light.
Definition: light.h:49
Indicates which, if any, material should be applied to geometry.
bool is_off() const
Returns true if the MaterialAttrib is an 'off' MaterialAttrib, indicating that it should disable the ...
get_material
If the MaterialAttrib is not an 'off' MaterialAttrib, returns the material that is associated.
Defines the way an object appears in the presence of lighting.
Definition: material.h:43
get_ambient
Returns the ambient color setting, if it has been set.
Definition: material.h:114
has_specular
Returns true if the specular color has been explicitly set for this material, false otherwise.
Definition: material.h:118
get_emission
Returns the emission color setting, if it has been set.
Definition: material.h:120
has_diffuse
Returns true if the diffuse color has been explicitly set for this material, false otherwise.
Definition: material.h:116
has_base_color
Returns true if the base color has been explicitly set for this material, false otherwise.
Definition: material.h:112
get_specular
Returns the specular color setting, if it has been set.
Definition: material.h:118
has_ambient
Returns true if the ambient color has been explicitly set for this material, false otherwise.
Definition: material.h:114
get_shininess
Returns the shininess exponent of the material.
Definition: material.h:122
get_diffuse
Returns the diffuse color setting, if it has been set.
Definition: material.h:116
get_local
Returns the local viewer flag.
Definition: material.h:128
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:795
Returned from a GSG in response to begin_occlusion_query() .
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:30
A node that contains a plane.
Definition: planeNode.h:36
const LPlane & get_plane() const
Returns the plane represented by the PlaneNode.
Definition: planeNode.I:54
A light originating from a single point in space, and shining in all directions.
Definition: pointLight.h:25
get_specular_color
Returns the color of specular highlights generated by the light.
Definition: pointLight.h:45
get_point
Returns the point in space at which the light is located.
Definition: pointLight.h:57
get_attenuation
Returns the terms of the attenuation equation for the light.
Definition: pointLight.h:49
A RenderBuffer is an arbitrary subset of the various layers (depth buffer, color buffer,...
Definition: renderBuffer.h:27
Specifies how polygons are to be drawn.
get_perspective
Returns the perspective flag.
get_thickness
Returns the line width or point thickness.
get_mode
Returns the render mode.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
Specifies how polygons are to be drawn.
get_mode
Returns the render mode.
Represents a set of settings that indicate how a texture is sampled.
Definition: samplerState.h:36
get_wrap_v
Returns the wrap mode of the texture in the V direction.
Definition: samplerState.h:113
get_wrap_w
Returns the wrap mode of the texture in the W direction.
Definition: samplerState.h:114
get_effective_magfilter
Returns the filter mode of the texture for magnification, with special treatment for FT_default.
Definition: samplerState.h:118
get_lod_bias
Returns the bias that will be added to the texture level of detail when sampling this texture.
Definition: samplerState.h:124
get_effective_minfilter
Returns the filter mode of the texture for minification, with special treatment for FT_default.
Definition: samplerState.h:117
get_effective_anisotropic_degree
Returns the degree of anisotropic filtering that should be applied to the texture.
Definition: samplerState.h:120
get_wrap_u
Returns the wrap mode of the texture in the U direction.
Definition: samplerState.h:112
get_border_color
Returns the solid color of the texture's border.
Definition: samplerState.h:121
This restricts rendering to within a rectangular region of the scene, without otherwise affecting the...
Definition: scissorAttrib.h:36
get_frame
Returns the left, right, bottom, top coordinates of the scissor frame.
Definition: scissorAttrib.h:51
Specifies whether flat shading (per-polygon) or smooth shading (per-vertex) is in effect.
get_mode
Returns the shade mode.
The ShaderContext is meant to contain the compiled version of a shader string.
Definition: shaderContext.h:31
Definition: shader.h:49
ShaderContext * prepare_now(PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the shader on the particular GSG, if it does not already exist.
Definition: shader.cxx:3711
ShaderLanguage get_language() const
Returns the shader language in which this shader was written.
Definition: shader.I:132
A light originating from a single point in space, and shining in a particular direction,...
Definition: spotlight.h:32
get_specular_color
Returns the color of specular highlights generated by the light.
Definition: spotlight.h:56
get_exponent
Returns the exponent that controls the amount of light falloff from the center of the spotlight.
Definition: spotlight.h:51
get_attenuation
Returns the terms of the attenuation equation for the light.
Definition: spotlight.h:60
A StencilAttrib is a collection of all stencil render states.
Definition: stencilAttrib.h:28
unsigned int get_render_state(StencilRenderState render_state_identifier) const
Returns render state.
Definition: stencilAttrib.I:18
Applies a transform matrix to UV's before they are rendered.
This is a special class object that holds all the information returned by a particular GSG to indicat...
bool was_simple_image_modified() const
Returns true if the texture's "simple" image has been modified since the last time mark_simple_loaded...
void update_data_size_bytes(size_t new_data_size_bytes)
Should be called (usually by a derived class) when the on-card size of this object has changed.
Texture * get_texture() const
Returns the pointer to the associated Texture object.
bool was_modified() const
Returns true if the texture properties or image have been modified since the last time mark_loaded() ...
void mark_unloaded()
Should be called after the texture has been forced out of texture memory.
void mark_loaded()
Should be called after the texture has been loaded into graphics memory, this updates the internal fl...
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:35
CombineMode get_combine_alpha_mode() const
Get combine_alpha_mode.
Definition: textureStage.I:534
get_saved_result
Returns the current setting of the saved_result flag.
Definition: textureStage.h:203
CombineOperand get_combine_rgb_operand2() const
Get operand2 of combine_rgb_mode.
Definition: textureStage.I:453
CombineSource get_combine_alpha_source1() const
Get source1 of combine_alpha_mode.
Definition: textureStage.I:567
CombineMode get_combine_rgb_mode() const
Get the combine_rgb_mode.
Definition: textureStage.I:396
CombineSource get_combine_rgb_source1() const
Get source1 of combine_rgb_mode.
Definition: textureStage.I:429
bool involves_color_scale() const
Returns true if the TextureStage is affected by the setting of the current ColorScaleAttrib,...
Definition: textureStage.I:600
CombineOperand get_combine_alpha_operand0() const
Get operand0 of combine_alpha_mode.
Definition: textureStage.I:559
CombineOperand get_combine_rgb_operand1() const
Get operand1 of combine_rgb_mode.
Definition: textureStage.I:437
CombineSource get_combine_alpha_source0() const
Get source0 of combine_alpha_mode.
Definition: textureStage.I:551
get_rgb_scale
See set_rgb_scale().
Definition: textureStage.h:201
get_alpha_scale
See set_alpha_scale().
Definition: textureStage.h:202
int get_num_combine_rgb_operands() const
Returns the number of meaningful operands that may be retrieved via get_combine_rgb_sourceN() and get...
Definition: textureStage.I:405
CombineOperand get_combine_alpha_operand1() const
Get operand1 of combine_alpha_mode.
Definition: textureStage.I:575
get_color
return the color for this stage
Definition: textureStage.h:200
get_tex_view_offset
Returns the current setting of the tex_view_offset.
Definition: textureStage.h:205
CombineSource get_combine_rgb_source0() const
Get source0 of combine_rgb_mode.
Definition: textureStage.I:413
CombineSource get_combine_rgb_source2() const
Get source2 of combine_rgb_mode.
Definition: textureStage.I:445
get_mode
Return the mode of this stage.
Definition: textureStage.h:198
bool uses_color() const
Returns true if the TextureStage makes use of whatever color is specified in set_color(),...
Definition: textureStage.I:609
int get_num_combine_alpha_operands() const
Returns the number of meaningful operands that may be retrieved via get_combine_alpha_sourceN() and g...
Definition: textureStage.I:543
CombineOperand get_combine_rgb_operand0() const
Get operand0 of combine_rgb_mode.
Definition: textureStage.I:421
CombineSource get_combine_alpha_source2() const
Get source2 of combine_alpha_mode.
Definition: textureStage.I:583
CombineOperand get_combine_alpha_operand2() const
Get operand2 of combine_alpha_mode.
Definition: textureStage.I:591
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition: texture.h:370
TextureContext * prepare_now(int view, PreparedGraphicsObjects *prepared_objects, GraphicsStateGuardianBase *gsg)
Creates a context for the texture on the particular GSG, if it does not already exist.
Definition: texture.cxx:1981
get_num_views
Returns the number of "views" in the texture.
Definition: texture.h:354
bool has_ram_image() const
Returns true if the Texture has its image contents available in main RAM, false if it exists only in ...
Definition: texture.I:1242
get_texture_type
Returns the overall interpretation of the texture.
Definition: texture.h:365
static bool is_srgb(Format format)
Returns true if the indicated format is in the sRGB color space, false otherwise.
Definition: texture.cxx:2678
get_ram_image_compression
Returns the compression mode in which the ram image is already stored pre- compressed.
Definition: texture.h:471
void setup_texture(TextureType texture_type, int x_size, int y_size, int z_size, ComponentType component_type, Format format)
Sets the texture to the indicated type and dimensions, presumably in preparation for calling read() o...
Definition: texture.I:52
set_render_to_texture
Sets a flag on the texture that indicates whether the texture is intended to be used as a direct-rend...
Definition: texture.h:417
get_y_size
Returns the height of the texture image in texels.
Definition: texture.h:346
get_z_size
Returns the depth of the texture image in texels.
Definition: texture.h:350
bool might_have_ram_image() const
Returns true if the texture's image contents are currently available in main RAM, or there is reason ...
Definition: texture.I:1264
has_simple_ram_image
Returns true if the Texture has a "simple" image available in main RAM.
Definition: texture.h:517
get_x_size
Returns the width of the texture image in texels.
Definition: texture.h:342
bool has_uncompressed_ram_image() const
Returns true if the Texture has its image contents available in main RAM and is uncompressed,...
Definition: texture.I:1252
void set_size_padded(int x=1, int y=1, int z=1)
Changes the size of the texture, padding if necessary, and setting the pad region as well.
Definition: texture.cxx:1932
get_component_type
Returns the numeric interpretation of each component of the texture.
Definition: texture.h:374
A thread; that is, a lightweight process.
Definition: thread.h:46
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition: thread.h:109
Indicates a coordinate-system transform on vertices.
get_mat
Returns the matrix that describes the transform.
Stores the total set of VertexTransforms that the vertices in a particular GeomVertexData object migh...
get_transform
Returns the nth transform in the table.
get_num_transforms
Returns the number of transforms in the table.
This controls the enabling of transparency.
get_mode
Returns the transparency mode.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
This is a special class object that holds all the information returned by a particular GSG to indicat...
void update_data_size_bytes(size_t new_data_size_bytes)
Should be called (usually by a derived class) when the on-card size of this object has changed.
bool was_modified(const GeomVertexArrayDataHandle *reader) const
Returns true if the data has been modified since the last time mark_loaded() was called.
bool changed_size(const GeomVertexArrayDataHandle *reader) const
Returns true if the data has changed size since the last time mark_loaded() was called.
void mark_loaded(const GeomVertexArrayDataHandle *reader)
Should be called after the VertexBufferContext has been loaded into graphics memory,...
An offscreen render buffer.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(OcclusionQueryContext) DXGraphicsStateGuardian9
Ends a previous call to begin_occlusion_query().
CPT(TransformState) DXGraphicsStateGuardian9
Given a lens, calculates the appropriate projection matrix for use with this gsg.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.