Panda3D
Loading...
Searching...
No Matches
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"
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
80using std::endl;
81using std::max;
82using std::min;
83
84TypeHandle DXGraphicsStateGuardian9::_type_handle;
85
86D3DMATRIX DXGraphicsStateGuardian9::_d3d_ident_mat;
87
88unsigned char *DXGraphicsStateGuardian9::_temp_buffer = nullptr;
89unsigned char *DXGraphicsStateGuardian9::_safe_buffer_start = nullptr;
90
91#ifdef HAVE_CG
92LPDIRECT3DDEVICE9 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 */
102DXGraphicsStateGuardian9::
103DXGraphicsStateGuardian9(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 */
168DXGraphicsStateGuardian9::
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 */
192prepare_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 */
209apply_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 */
313update_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 */
338upload_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();
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 */
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) {
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 */
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 */
776PT(OcclusionQueryContext) DXGraphicsStateGuardian9::
777end_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 */
801PT(GeomMunger) DXGraphicsStateGuardian9::
802make_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 */
811clear(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 */
940CPT(TransformState) DXGraphicsStateGuardian9::
941calc_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 */
982prepare_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 */
999begin_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 */
1049begin_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 */
1086end_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 */
1134end_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 */
1179begin_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 */
1304bool DXGraphicsStateGuardian9::
1305update_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.
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 */
1352void DXGraphicsStateGuardian9::
1353disable_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 */
1365draw_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 */
1432draw_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 */
1600draw_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 */
1706draw_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 */
1772draw_linestrips(const GeomPrimitivePipelineReader *reader, bool force) {
1773 return false;
1774}
1775
1776/**
1777 * Draws a series of disconnected points.
1778 */
1780draw_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 */
1840framebuffer_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 */
1989framebuffer_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 */
2000do_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 // We can't directly call GetRenderTargetData on a multisampled buffer.
2074 // Instead, blit it into a temporary target.
2075 if (surface_description.MultiSampleType != D3DMULTISAMPLE_NONE) {
2076 IDirect3DSurface9 *resolved;
2077 hr = _d3d_device->CreateRenderTarget(surface_description.Width,
2078 surface_description.Height,
2079 surface_description.Format,
2080 D3DMULTISAMPLE_NONE, 0, FALSE,
2081 &resolved, nullptr);
2082 if (FAILED(hr)) {
2083 dxgsg9_cat.error()
2084 << "CreateRenderTarget failed" << D3DERRORSTRING(hr) << "\n";
2085 backbuffer->Release();
2086 return false;
2087 }
2088
2089 _d3d_device->StretchRect(backbuffer, nullptr, resolved, nullptr, D3DTEXF_NONE);
2090 if (FAILED(hr)) {
2091 dxgsg9_cat.error()
2092 << "StretchRect failed" << D3DERRORSTRING(hr) << "\n";
2093 backbuffer->Release();
2094 resolved->Release();
2095 return false;
2096 }
2097
2098 backbuffer->Release();
2099 backbuffer = resolved;
2100 }
2101
2102 pool = D3DPOOL_SYSTEMMEM;
2103 hr = _d3d_device->CreateOffscreenPlainSurface(
2104 surface_description.Width,
2105 surface_description.Height,
2106 surface_description.Format,
2107 pool,
2108 &temp_surface,
2109 nullptr);
2110 if (FAILED(hr)) {
2111 dxgsg9_cat.error()
2112 << "CreateImageSurface failed in copy_pixel_buffer()"
2113 << D3DERRORSTRING(hr);
2114 backbuffer->Release();
2115 return false;
2116 }
2117
2118 // Now we must copy from the backbuffer to our temporary surface.
2119 hr = _d3d_device -> GetRenderTargetData (backbuffer, temp_surface);
2120 if (FAILED(hr)) {
2121 dxgsg9_cat.error() << "GetRenderTargetData failed" << D3DERRORSTRING(hr);
2122 temp_surface->Release();
2123 backbuffer->Release();
2124 return false;
2125 }
2126
2127 copy_inverted = true;
2128
2129 RELEASE(backbuffer, dxgsg9, "backbuffer", RELEASE_ONCE);
2130
2131 } else if (_cur_read_pixel_buffer & RenderBuffer::T_front) {
2132
2133 if (_screen->_presentation_params.Windowed) {
2134 // GetFrontBuffer() retrieves the entire desktop for a monitor, so we
2135 // need to reserve space for that.
2136
2137 // We have to use GetMonitorInfo(), since this GSG may not be for the
2138 // primary monitor.
2139 MONITORINFO minfo;
2140 minfo.cbSize = sizeof(MONITORINFO);
2141 GetMonitorInfo(_screen->_monitor, &minfo);
2142
2143 w = RECT_XSIZE(minfo.rcMonitor);
2144 h = RECT_YSIZE(minfo.rcMonitor);
2145
2146 // set rect to client area of window in scrn coords
2147 ClientToScreen(_screen->_window, (POINT*)&rect.left);
2148 ClientToScreen(_screen->_window, (POINT*)&rect.right);
2149 }
2150
2151 // For GetFrontBuffer(), we need a temporary surface of type A8R8G8B8.
2152 // Unlike GetBackBuffer(), GetFrontBuffer() implicitly performs a copy.
2153 hr = _d3d_device->CreateOffscreenPlainSurface(w, h, D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &temp_surface, nullptr);
2154 if (FAILED(hr)) {
2155 dxgsg9_cat.error()
2156 << "CreateImageSurface failed in copy_pixel_buffer()"
2157 << D3DERRORSTRING(hr);
2158 return false;
2159 }
2160
2161 UINT swap_chain;
2162
2163 swap_chain = 0;
2164 hr = _d3d_device->GetFrontBufferData(swap_chain,temp_surface);
2165
2166 if (hr == D3DERR_DEVICELOST) {
2167 dxgsg9_cat.error()
2168 << "copy_pixel_buffer failed: device lost\n";
2169 temp_surface->Release();
2170 return false;
2171 }
2172
2173 copy_inverted = true;
2174
2175 } else {
2176 dxgsg9_cat.error()
2177 << "copy_pixel_buffer: unhandled current_read_pixel_buffer type\n";
2178 temp_surface->Release();
2179 return false;
2180 }
2181
2182 if (inverted) {
2183 copy_inverted = !copy_inverted;
2184 }
2186 copy_inverted, tex, view, z);
2187
2188 RELEASE(temp_surface, dxgsg9, "temp_surface", RELEASE_ONCE);
2189
2190 nassertr(tex->has_ram_image(), false);
2191 return true;
2192}
2193
2194void DXGraphicsStateGuardian9::reset_render_states (void)
2195{
2196 int index;
2197 int maximum_texture_stages;
2198
2199 maximum_texture_stages = D3D_MAXTEXTURESTAGES;
2200
2201 // set to invalid values so that the state will always be set the first time
2202 memset (_render_state_array, -1, sizeof (_render_state_array));
2203 memset (_texture_stage_states_array, -1, sizeof (_texture_stage_states_array));
2204
2205 // states that may be set intially to -1 by the user, so set it to D3D's
2206 // default value
2207 _render_state_array [D3DRS_FOGCOLOR] = 0;
2208 _render_state_array [D3DRS_AMBIENT] = 0;
2209
2210 // set to D3D default values or invalid values so that the state will always
2211 // be set the first time
2212 memset (_texture_render_states_array, 0, sizeof (_texture_render_states_array));
2213
2214 // states that may be set intially to 0 by the user, so set it to D3D's
2215 // default value
2216 for (index = 0; index < MAXIMUM_TEXTURES; index++) {
2217 TextureRenderStates *texture_render_states;
2218
2219 texture_render_states = &_texture_render_states_array [index];
2220 texture_render_states -> state_array [D3DSAMP_MAGFILTER] = D3DTEXF_POINT;
2221 texture_render_states -> state_array [D3DSAMP_MINFILTER] = D3DTEXF_POINT;
2222 texture_render_states -> state_array [D3DSAMP_MAXANISOTROPY] = 1;
2223 }
2224 _num_active_texture_stages = 0;
2225
2226 set_render_state(D3DRS_NORMALIZENORMALS, false);
2227
2228 _last_fvf = 0;
2229}
2230
2231/**
2232 * Resets all internal state as if the gsg were newly created. The
2233 * GraphicsWindow pointer represents a typical window that might be used for
2234 * this context; it may be required to set up the frame buffer properly the
2235 * first time.
2236 */
2238reset() {
2240
2241 // Build _inv_state_mask as a mask of 1's where we don't care, and 0's where
2242 // we do care, about the state.
2243 _inv_state_mask.clear_bit(ShaderAttrib::get_class_slot());
2244 _inv_state_mask.clear_bit(AlphaTestAttrib::get_class_slot());
2245 _inv_state_mask.clear_bit(ClipPlaneAttrib::get_class_slot());
2246 _inv_state_mask.clear_bit(ColorAttrib::get_class_slot());
2247 _inv_state_mask.clear_bit(ColorScaleAttrib::get_class_slot());
2248 _inv_state_mask.clear_bit(CullFaceAttrib::get_class_slot());
2249 _inv_state_mask.clear_bit(DepthOffsetAttrib::get_class_slot());
2250 _inv_state_mask.clear_bit(DepthTestAttrib::get_class_slot());
2251 _inv_state_mask.clear_bit(DepthWriteAttrib::get_class_slot());
2252 _inv_state_mask.clear_bit(RenderModeAttrib::get_class_slot());
2253 _inv_state_mask.clear_bit(RescaleNormalAttrib::get_class_slot());
2254 _inv_state_mask.clear_bit(ShadeModelAttrib::get_class_slot());
2255 _inv_state_mask.clear_bit(TransparencyAttrib::get_class_slot());
2256 _inv_state_mask.clear_bit(ColorWriteAttrib::get_class_slot());
2257 _inv_state_mask.clear_bit(ColorBlendAttrib::get_class_slot());
2258 _inv_state_mask.clear_bit(TextureAttrib::get_class_slot());
2259 _inv_state_mask.clear_bit(TexGenAttrib::get_class_slot());
2260 _inv_state_mask.clear_bit(TexMatrixAttrib::get_class_slot());
2261 _inv_state_mask.clear_bit(MaterialAttrib::get_class_slot());
2262 _inv_state_mask.clear_bit(LightAttrib::get_class_slot());
2263 _inv_state_mask.clear_bit(StencilAttrib::get_class_slot());
2264 _inv_state_mask.clear_bit(FogAttrib::get_class_slot());
2265 _inv_state_mask.clear_bit(ScissorAttrib::get_class_slot());
2266
2267 // D3DRS_POINTSPRITEENABLE doesn't seem to support remapping the texture
2268 // coordinates via a texture matrix, so we don't advertise
2269 // GR_point_sprite_tex_matrix.
2270 _supported_geom_rendering =
2271 Geom::GR_point | Geom::GR_point_uniform_size |
2272 Geom::GR_point_perspective | Geom::GR_point_sprite |
2273 Geom::GR_indexed_other |
2274 Geom::GR_triangle_strip | Geom::GR_triangle_fan |
2275 Geom::GR_flat_first_vertex |
2276 Geom::GR_render_mode_wireframe | Geom::GR_render_mode_point;
2277
2278 // overwrite gsg defaults with these values
2279
2280 HRESULT hr;
2281
2282 // make sure gsg passes all current state down to us
2283 // set_state_and_transform(RenderState::make_empty(),
2284 // TransformState::make_identity()); want gsg to pass all state settings
2285 // down so any non-matching defaults we set here get overwritten
2286
2287 nassertv(_screen->_d3d9 != nullptr);
2288
2289 if (_d3d_device == nullptr) {
2290 return;
2291 }
2292
2293 D3DCAPS9 d3d_caps;
2294 _d3d_device->GetDeviceCaps(&d3d_caps);
2295
2296 _vertex_shader_version_major = D3DSHADER_VERSION_MAJOR (d3d_caps.VertexShaderVersion);
2297 _vertex_shader_version_minor = D3DSHADER_VERSION_MINOR (d3d_caps.VertexShaderVersion);
2298 _pixel_shader_version_major = D3DSHADER_VERSION_MAJOR (d3d_caps.PixelShaderVersion);
2299 _pixel_shader_version_minor = D3DSHADER_VERSION_MINOR (d3d_caps.PixelShaderVersion);
2300
2301 _supports_hlsl = (_pixel_shader_version_major != 0);
2302
2303 _vertex_shader_profile = (char *) D3DXGetVertexShaderProfile (_d3d_device);
2304 _pixel_shader_profile = (char *) D3DXGetPixelShaderProfile (_d3d_device);
2305
2306 _vertex_shader_maximum_constants = d3d_caps.MaxVertexShaderConst;
2307
2308 switch (_pixel_shader_version_major)
2309 {
2310 case 0:
2311 _shader_model = SM_00;
2312 break;
2313 case 1:
2314 _shader_model = SM_11;
2315 break;
2316 case 2:
2317 // minimim specification for pixel shader 2.0 is 96 instruction slots
2318 _shader_model = SM_20;
2319 if (d3d_caps.PS20Caps.NumInstructionSlots >= 512) {
2320 _shader_model = SM_2X;
2321 }
2322 break;
2323 case 3:
2324 _shader_model = SM_30;
2325 break;
2326 case 4:
2327 _shader_model = SM_40;
2328 break;
2329 case 5:
2330 default:
2331 _shader_model = SM_50;
2332 break;
2333 }
2334
2335 _auto_detect_shader_model = _shader_model;
2336
2337#ifdef HAVE_CG
2338 set_cg_device(_d3d_device);
2339
2340 _cg_context = cgCreateContext();
2341
2342 if (cgD3D9IsProfileSupported(CG_PROFILE_PS_2_0) &&
2343 cgD3D9IsProfileSupported(CG_PROFILE_VS_2_0)) {
2344 _supports_basic_shaders = true;
2345 _shader_caps._active_vprofile = (int)cgD3D9GetLatestVertexProfile();
2346 _shader_caps._active_fprofile = (int)cgD3D9GetLatestPixelProfile();
2347 _shader_caps._ultimate_vprofile = (int)CG_PROFILE_VS_3_0;
2348 _shader_caps._ultimate_fprofile = (int)CG_PROFILE_PS_3_0;
2349/*
2350 _shader_caps._active_vprofile = (int)CG_PROFILE_VS_2_0;
2351 _shader_caps._active_fprofile = (int)CG_PROFILE_PS_2_0;
2352 _shader_caps._ultimate_vprofile = (int)CG_PROFILE_VS_2_0;
2353 _shader_caps._ultimate_fprofile = (int)CG_PROFILE_PS_2_0;
2354*/
2355 }
2356
2357 if (dxgsg9_cat.is_debug()) {
2358 CGprofile vertex_profile;
2359 CGprofile pixel_profile;
2360
2361 vertex_profile = cgD3D9GetLatestVertexProfile();
2362 pixel_profile = cgD3D9GetLatestPixelProfile();
2363
2364 const char *vertex_profile_str =
2365 cgGetProfileString(vertex_profile);
2366 const char *pixel_profile_str =
2367 cgGetProfileString(pixel_profile);
2368
2369 if (vertex_profile_str == nullptr) {
2370 vertex_profile_str = "(null)";
2371 }
2372 if (pixel_profile_str == nullptr) {
2373 pixel_profile_str = "(null)";
2374 }
2375
2376 dxgsg9_cat.debug()
2377 << "\nCg latest vertex profile = " << vertex_profile_str << " id = " << vertex_profile
2378 << "\nCg latest pixel profile = " << pixel_profile_str << " id = " << pixel_profile
2379 << "\nshader model = " << _shader_model
2380 << "\n";
2381 }
2382#endif
2383
2384 _supports_stream_offset = (d3d_caps.DevCaps2 & D3DDEVCAPS2_STREAMOFFSET) != 0;
2385 _screen->_supports_dynamic_textures = ((d3d_caps.Caps2 & D3DCAPS2_DYNAMICTEXTURES) != 0);
2386 _screen->_supports_automatic_mipmap_generation = ((d3d_caps.Caps2 & D3DCAPS2_CANAUTOGENMIPMAP) != 0);
2387
2388 if (support_stencil) {
2389 int min_stencil = D3DSTENCILCAPS_ZERO | D3DSTENCILCAPS_REPLACE | D3DSTENCILCAPS_INCR | D3DSTENCILCAPS_DECR;
2390 if ((d3d_caps.StencilCaps & min_stencil) == min_stencil) {
2391 if (dxgsg9_cat.is_debug()) {
2392 dxgsg9_cat.debug()
2393 << "Checking for stencil; mode = "
2394 << D3DFormatStr(_screen->_presentation_params.AutoDepthStencilFormat)
2395 << "\n";
2396 }
2397 switch (_screen->_presentation_params.AutoDepthStencilFormat) {
2398 // These are the only formats that support stencil.
2399 case D3DFMT_D15S1:
2400 case D3DFMT_D24S8:
2401 case D3DFMT_D24X4S4:
2402 _supports_stencil = true;
2403 if (dxgsg9_cat.is_debug()) {
2404 dxgsg9_cat.debug()
2405 << "Stencils supported.\n";
2406 }
2407 break;
2408
2409 default:
2410 if (dxgsg9_cat.is_debug()) {
2411 dxgsg9_cat.debug()
2412 << "Stencils NOT supported.\n";
2413 }
2414 }
2415 }
2416 }
2417
2418 _supports_stencil_wrap = (d3d_caps.StencilCaps & D3DSTENCILCAPS_INCR) && (d3d_caps.StencilCaps & D3DSTENCILCAPS_DECR);
2419 _supports_two_sided_stencil = ((d3d_caps.StencilCaps & D3DSTENCILCAPS_TWOSIDED) != 0);
2420
2421 _max_color_targets = d3d_caps.NumSimultaneousRTs;
2422
2423 _supports_depth_bias = ((d3d_caps.RasterCaps & (D3DPRASTERCAPS_DEPTHBIAS | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS)) == (D3DPRASTERCAPS_DEPTHBIAS | D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS));
2424
2425 _supports_gamma_calibration = ((d3d_caps.Caps2 & D3DCAPS2_CANCALIBRATEGAMMA) != 0);
2426
2427 // Test for occlusion query support
2428 hr = _d3d_device->CreateQuery(D3DQUERYTYPE_OCCLUSION, nullptr);
2429 _supports_occlusion_query = !FAILED(hr);
2430
2431 if (dxgsg9_cat.is_error()) {
2432 dxgsg9_cat.debug()
2433 << "\nHwTransformAndLight = " << ((d3d_caps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) != 0)
2434 << "\nMaxTextureWidth = " << d3d_caps.MaxTextureWidth
2435 << "\nMaxTextureHeight = " << d3d_caps.MaxTextureHeight
2436 << "\nMaxVolumeExtent = " << d3d_caps.MaxVolumeExtent
2437 << "\nMaxTextureAspectRatio = " << d3d_caps.MaxTextureAspectRatio
2438 << "\nTexCoordCount = " << (d3d_caps.FVFCaps & D3DFVFCAPS_TEXCOORDCOUNTMASK)
2439 << "\nMaxTextureBlendStages = " << d3d_caps.MaxTextureBlendStages
2440 << "\nMaxSimultaneousTextures = " << d3d_caps.MaxSimultaneousTextures
2441 << "\nMaxActiveLights = " << d3d_caps.MaxActiveLights
2442 << "\nMaxUserClipPlanes = " << d3d_caps.MaxUserClipPlanes
2443 << "\nMaxVertexBlendMatrices = " << d3d_caps.MaxVertexBlendMatrices
2444 << "\nMaxVertexBlendMatrixIndex = " << d3d_caps.MaxVertexBlendMatrixIndex
2445 << "\nMaxPointSize = " << d3d_caps.MaxPointSize
2446 << "\nMaxPrimitiveCount = " << d3d_caps.MaxPrimitiveCount
2447 << "\nMaxVertexIndex = " << d3d_caps.MaxVertexIndex
2448 << "\nMaxStreams = " << d3d_caps.MaxStreams
2449 << "\nMaxStreamStride = " << d3d_caps.MaxStreamStride
2450 << "\nD3DTEXOPCAPS_MULTIPLYADD = " << ((d3d_caps.TextureOpCaps & D3DTEXOPCAPS_MULTIPLYADD) != 0)
2451 << "\nD3DTEXOPCAPS_LERP = " << ((d3d_caps.TextureOpCaps & D3DTEXOPCAPS_LERP) != 0)
2452 << "\nD3DPMISCCAPS_TSSARGTEMP = " << ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP) != 0)
2453 << "\nD3DPRASTERCAPS_DEPTHBIAS = " << ((d3d_caps.RasterCaps & D3DPRASTERCAPS_DEPTHBIAS) != 0)
2454 << "\nD3DPRASTERCAPS_SLOPESCALEDEPTHBIAS = " << ((d3d_caps.RasterCaps & D3DPRASTERCAPS_SLOPESCALEDEPTHBIAS) != 0)
2455 << "\nVertexShaderVersion = " << _vertex_shader_version_major << "." << _vertex_shader_version_minor
2456 << "\nPixelShaderVersion = " << _pixel_shader_version_major << "." << _pixel_shader_version_minor
2457 << "\nMaxVertexShaderConst = " << _vertex_shader_maximum_constants
2458 << "\nsupports_stream_offset = " << _supports_stream_offset
2459 << "\nsupports_dynamic_textures = " << _screen->_supports_dynamic_textures
2460 << "\nsupports_automatic_mipmap_generation = " << _screen->_supports_automatic_mipmap_generation
2461 << "\nsupports_stencil_wrap = " << _supports_stencil_wrap
2462 << "\nsupports_two_sided_stencil = " << _supports_two_sided_stencil
2463 << "\nsupports_occlusion_query = " << _supports_occlusion_query
2464 << "\nsupports_gamma_calibration = " << _supports_gamma_calibration
2465 << "\nMaxAnisotropy = " << d3d_caps.MaxAnisotropy
2466 << "\nNumSimultaneousRTs = " << d3d_caps.NumSimultaneousRTs
2467 << "\nD3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING = " << ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_MRTPOSTPIXELSHADERBLENDING) != 0)
2468 << "\nDirectX SDK version " DIRECTX_SDK_VERSION
2469 << "\n";
2470 }
2471
2472 // OVERRIDE SUPPORT SINCE IT DOES NOT WORK WELL
2473 _screen->_supports_automatic_mipmap_generation = false;
2474
2475 reset_render_states();
2476
2477 _max_vertices_per_array = d3d_caps.MaxVertexIndex;
2478 _max_vertices_per_primitive = d3d_caps.MaxPrimitiveCount;
2479
2480 _max_texture_stages = d3d_caps.MaxSimultaneousTextures;
2481
2482 _max_texture_dimension = min(d3d_caps.MaxTextureWidth, d3d_caps.MaxTextureHeight);
2483
2484 _supports_tex_non_pow2 = !(d3d_caps.TextureCaps & D3DPTEXTURECAPS_POW2);
2485
2486 _supports_texture_combine = ((d3d_caps.TextureOpCaps & D3DTEXOPCAPS_LERP) != 0);
2487 _supports_texture_saved_result = ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_TSSARGTEMP) != 0);
2488 _supports_texture_constant_color = ((d3d_caps.PrimitiveMiscCaps & D3DPMISCCAPS_PERSTAGECONSTANT) != 0);
2489 _supports_texture_dot3 = true;
2490
2491 if (_supports_texture_constant_color) {
2492 _constant_color_operand = D3DTA_CONSTANT;
2493 } else {
2494 _constant_color_operand = D3DTA_TFACTOR;
2495 }
2496
2497 _screen->_managed_textures = _gsg_managed_textures;
2498 _screen->_managed_vertex_buffers = _gsg_managed_vertex_buffers;
2499 _screen->_managed_index_buffers = _gsg_managed_index_buffers;
2500
2501 UINT available_texture_memory;
2502
2503 available_texture_memory = _d3d_device->GetAvailableTextureMem ( );
2504 if (dxgsg9_cat.is_debug()) {
2505 dxgsg9_cat.debug() << "*** GetAvailableTextureMem = " << available_texture_memory << "\n";
2506 }
2507 _available_texture_memory = available_texture_memory;
2508
2509 // check for render to texture support
2510 D3DDEVICE_CREATION_PARAMETERS creation_parameters;
2511
2512 _supports_render_texture = false;
2513 _screen->_render_to_texture_d3d_format = D3DFMT_UNKNOWN;
2514 _screen->_framebuffer_d3d_format = D3DFMT_UNKNOWN;
2515
2516 #define TOTAL_RENDER_TO_TEXTURE_FORMATS 3
2517
2518 D3DFORMAT render_to_texture_formats [TOTAL_RENDER_TO_TEXTURE_FORMATS] =
2519 {
2520 D3DFMT_A8R8G8B8, // check for this format first
2521 D3DFMT_X8R8G8B8,
2522 D3DFMT_UNKNOWN, // place holder for _screen->_display_mode.Format
2523 };
2524
2525 render_to_texture_formats [TOTAL_RENDER_TO_TEXTURE_FORMATS - 1] = _screen->_display_mode.Format;
2526
2527 hr = _d3d_device->GetCreationParameters (&creation_parameters);
2528 if (SUCCEEDED (hr)) {
2529 _screen->_framebuffer_d3d_format = _screen->_display_mode.Format;
2530
2531 int index;
2532 for (index = 0; index < TOTAL_RENDER_TO_TEXTURE_FORMATS; index++) {
2533 hr = _screen->_d3d9->CheckDeviceFormat (
2534 creation_parameters.AdapterOrdinal,
2535 creation_parameters.DeviceType,
2536 _screen->_display_mode.Format,
2537 D3DUSAGE_RENDERTARGET,
2538 D3DRTYPE_TEXTURE,
2539 render_to_texture_formats [index]);
2540 if (SUCCEEDED (hr)) {
2541 _screen->_render_to_texture_d3d_format = render_to_texture_formats [index];
2542 _supports_render_texture = true;
2543 }
2544 if (_supports_render_texture) {
2545 break;
2546 }
2547 }
2548 }
2549 if (dxgsg9_cat.is_debug()) {
2550 dxgsg9_cat.debug() << "Render to Texture Support = " << _supports_render_texture << "\n";
2551 }
2552
2553 // override default config setting since it is really supported or not ???
2554 // support_render_texture = _supports_render_texture;
2555
2556 _supports_3d_texture = ((d3d_caps.TextureCaps & D3DPTEXTURECAPS_VOLUMEMAP) != 0);
2557 if (_supports_3d_texture) {
2558 _max_3d_texture_dimension = d3d_caps.MaxVolumeExtent;
2559 }
2560 _supports_cube_map = ((d3d_caps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP) != 0);
2561 if (_supports_cube_map) {
2562 _max_cube_map_dimension = _max_texture_dimension;
2563 }
2564
2565 _max_lights = (int)d3d_caps.MaxActiveLights;
2566 _max_clip_planes = (int)d3d_caps.MaxUserClipPlanes;
2567 _max_vertex_transforms = d3d_caps.MaxVertexBlendMatrices;
2568 _max_vertex_transform_indices = d3d_caps.MaxVertexBlendMatrixIndex;
2569
2570 set_render_state(D3DRS_AMBIENT, 0x0);
2571
2572 _clip_plane_bits = 0;
2573 set_render_state(D3DRS_CLIPPLANEENABLE , 0x0);
2574
2575 set_render_state(D3DRS_CLIPPING, true);
2576
2577 set_render_state(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
2578
2579 set_render_state(D3DRS_ZWRITEENABLE, TRUE);
2580
2581/* ***** DX9 ??? D3DRS_EDGEANTIALIAS NOT IN DX9 */
2582// set_render_state(D3DRS_EDGEANTIALIAS, false);
2583
2584 set_render_state(D3DRS_ZENABLE, D3DZB_FALSE);
2585
2586 set_render_state(D3DRS_ALPHABLENDENABLE, FALSE);
2587
2588 set_render_state(D3DRS_FOGENABLE, FALSE);
2589
2590 _has_scene_graph_color = false;
2591
2592 _last_testcooplevel_result = D3D_OK;
2593
2594 if (dxgsg9_cat.is_debug()) {
2595 dxgsg9_cat.debug() << "Supported texture formats:\n";
2596 }
2597
2598 for(int i = 0; i < MAX_POSSIBLE_TEXFMTS; i++) {
2599 // look for all possible DX9 texture fmts
2600 D3DFORMAT_FLAG fmtflag = D3DFORMAT_FLAG(1 << i);
2601 hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format,
2602 0x0, D3DRTYPE_TEXTURE, g_D3DFORMATmap[fmtflag]);
2603 if (SUCCEEDED(hr)) {
2604 if (dxgsg9_cat.is_debug()) {
2605 dxgsg9_cat.debug() << " " << D3DFormatStr(g_D3DFORMATmap[fmtflag]) << "\n";
2606 }
2607 _screen->_supported_tex_formats_mask |= fmtflag;
2608 }
2609 }
2610
2611 _supports_depth_stencil = ((_screen->_supported_tex_formats_mask & D24S8_FLAG) != 0);
2612 _supports_depth_texture = _supports_depth_stencil ||
2613 (_screen->_supported_tex_formats_mask & D16_FLAG) != 0 ||
2614 (_screen->_supported_tex_formats_mask & D32_FLAG) != 0 ||
2615 (_screen->_supported_tex_formats_mask & D24X8_FLAG) != 0;
2616
2617 // In DirectX 9, all built-in depth formats use shadow map filtering. Some
2618 // drivers (GeForce 8000+, Radeon HD 4000+, Intel G45+) expose a FourCC
2619 // format called "INTZ" that allows access to the actual depth. We don't
2620 // have a flag to indicate this, though.
2621 _supports_shadow_filter = _supports_depth_texture;
2622
2623 // check if compressed textures are supported
2624 #define CHECK_COMPRESSED_FMT(mode, fmt) \
2625 if (_screen->_supported_tex_formats_mask & fmt##_FLAG) {\
2626 if (dxgsg9_cat.is_debug()) {\
2627 dxgsg9_cat.debug() << "Compressed texture format " << #fmt << " supported\n";\
2628 }\
2629 _supports_compressed_texture = true;\
2630 _compressed_texture_formats.set_bit(Texture::mode);\
2631 }
2632
2633 if (_screen->_intel_compressed_texture_bug) {
2634 dxgsg9_cat.info()
2635 << "Buggy Intel driver detected; disabling compressed textures.\n";
2636 _screen->_supported_tex_formats_mask &=
2637 ~(DXT1_FLAG | DXT2_FLAG | DXT3_FLAG | DXT4_FLAG | DXT5_FLAG);
2638
2639 } else {
2640 // Check for available compressed formats normally.
2641 CHECK_COMPRESSED_FMT(CM_dxt1, DXT1);
2642 CHECK_COMPRESSED_FMT(CM_dxt2, DXT2);
2643 CHECK_COMPRESSED_FMT(CM_dxt3, DXT3);
2644 CHECK_COMPRESSED_FMT(CM_dxt4, DXT4);
2645 CHECK_COMPRESSED_FMT(CM_dxt5, DXT5);
2646 CHECK_COMPRESSED_FMT(CM_rgtc, ATI1);
2647 CHECK_COMPRESSED_FMT(CM_rgtc, ATI2);
2648 }
2649
2650 #undef CHECK_COMPRESSED_FMT
2651
2652 _screen->_supports_rgba16f_texture_format = false;
2653 hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format, 0x0, D3DRTYPE_TEXTURE, D3DFMT_A16B16G16R16F);
2654 if (SUCCEEDED(hr)){
2655 _screen->_supports_rgba16f_texture_format = true;
2656 }
2657 _screen->_supports_rgba32_texture_format = false;
2658 hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format, 0x0, D3DRTYPE_TEXTURE, D3DFMT_A32B32G32R32F);
2659 if (SUCCEEDED(hr)){
2660 _screen->_supports_rgba32_texture_format = true;
2661 }
2662
2663 // s3 virge drivers sometimes give crap values for these
2664 if (_screen->_d3dcaps.MaxTextureWidth == 0)
2665 _screen->_d3dcaps.MaxTextureWidth = 256;
2666
2667 if (_screen->_d3dcaps.MaxTextureHeight == 0)
2668 _screen->_d3dcaps.MaxTextureHeight = 256;
2669
2670 if (_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGTABLE) {
2671 // Watch out for drivers that emulate per-pixel fog with per-vertex fog
2672 // (Riva128, Matrox Millen G200). Some of these require gouraud-shading
2673 // to be set to work, as if you were using vertex fog
2674 _do_fog_type = PerPixelFog;
2675 } else {
2676 // every card is going to have vertex fog, since it's implemented in d3d
2677 // runtime.
2678 nassertv((_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGVERTEX) != 0);
2679
2680 // vertex fog may look crappy if you have large polygons in the foreground
2681 // and they get clipped, so you may want to disable it
2682
2683 if (dx_no_vertex_fog) {
2684 _do_fog_type = None;
2685 } else {
2686 _do_fog_type = PerVertexFog;
2687
2688 // range-based fog only works with vertex fog in dx78
2689 if (dx_use_rangebased_fog && (_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_FOGRANGE)) {
2690 set_render_state(D3DRS_RANGEFOGENABLE, true);
2691 }
2692 }
2693 }
2694
2695 _screen->_can_direct_disable_color_writes = ((_screen->_d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_COLORWRITEENABLE) != 0);
2696
2697 // Lighting, let's turn it off initially.
2698 set_render_state(D3DRS_LIGHTING, false);
2699
2700 // turn on dithering if the rendertarget is < 8bitscolor channel
2701 bool dither_enabled = ((!dx_no_dithering) && IS_16BPP_DISPLAY_FORMAT(_screen->_presentation_params.BackBufferFormat)
2702 && (_screen->_d3dcaps.RasterCaps & D3DPRASTERCAPS_DITHER));
2703 set_render_state(D3DRS_DITHERENABLE, dither_enabled);
2704
2705 set_render_state(D3DRS_CLIPPING, true);
2706
2707 // Stencil test is off by default
2708 set_render_state(D3DRS_STENCILENABLE, FALSE);
2709 if (_supports_two_sided_stencil) {
2710 set_render_state(D3DRS_TWOSIDEDSTENCILMODE, FALSE);
2711 }
2712
2713 // Antialiasing.
2714/* ***** DX9 ??? D3DRS_EDGEANTIALIAS NOT IN DX9 */
2715// set_render_state(D3DRS_EDGEANTIALIAS, FALSE);
2716
2717 _current_fill_mode = RenderModeAttrib::M_filled;
2718 set_render_state(D3DRS_FILLMODE, D3DFILL_SOLID);
2719
2720 // must do SetTSS here because redundant states are filtered out by our code
2721 // based on current values above, so initial conditions must be correct
2722 set_texture_stage_state(0, D3DTSS_COLOROP, D3DTOP_DISABLE); // disables texturing
2723
2724 _cull_face_mode = CullFaceAttrib::M_cull_none;
2725 set_render_state(D3DRS_CULLMODE, D3DCULL_NONE);
2726
2727 set_render_state(D3DRS_ALPHAFUNC, D3DCMP_ALWAYS);
2728 set_render_state(D3DRS_ALPHAREF, 255);
2729 set_render_state(D3DRS_ALPHATESTENABLE, FALSE);
2730
2731 // this is a new DX8 state that lets you do additional operations other than
2732 // ADD (e.g. subtractmaxmin) must check
2733 // (_screen->_d3dcaps.PrimitiveMiscCaps & D3DPMISCCAPS_BLENDOP) (yes on
2734 // GF2Radeon8500, no on TNT)
2735 set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD);
2736
2737 _current_shader = nullptr;
2738 _current_shader_context = nullptr;
2739 _vertex_array_shader = nullptr;
2740 _vertex_array_shader_context = nullptr;
2741 _texture_binding_shader = nullptr;
2742 _texture_binding_shader_context = nullptr;
2743
2744 PRINT_REFCNT(dxgsg9, _d3d_device);
2745
2746 // Now that the GSG has been initialized, make it available for
2747 // optimizations.
2748 add_gsg(this);
2749}
2750
2751/**
2752 *
2753 */
2754void DXGraphicsStateGuardian9::
2755apply_fog(Fog *fog) {
2756 if (_do_fog_type == None)
2757 return;
2758
2759 Fog::Mode panda_fogmode = fog->get_mode();
2760 D3DFOGMODE d3dfogmode = get_fog_mode_type(panda_fogmode);
2761
2762 set_render_state((D3DRENDERSTATETYPE)_do_fog_type, d3dfogmode);
2763
2764 const LColor &fog_colr = fog->get_color();
2765 set_render_state(D3DRS_FOGCOLOR,
2766 MY_D3DRGBA(fog_colr[0], fog_colr[1], fog_colr[2], 0.0f)); // Alpha bits are not used
2767
2768 // do we need to adjust fog startend values based on
2769 // D3DPRASTERCAPS_WFOGD3DPRASTERCAPS_ZFOG ? if not WFOG, then docs say we
2770 // need to adjust values to range [0, 1]
2771
2772 switch (panda_fogmode) {
2773 case Fog::M_linear:
2774 {
2775 PN_stdfloat onset, opaque;
2776 fog->get_linear_range(onset, opaque);
2777
2778 set_render_state(D3DRS_FOGSTART,
2779 *((LPDWORD) (&onset)));
2780 set_render_state(D3DRS_FOGEND,
2781 *((LPDWORD) (&opaque)));
2782 }
2783 break;
2784 case Fog::M_exponential:
2785 case Fog::M_exponential_squared:
2786 {
2787 // Exponential fog is always camera-relative.
2788 PN_stdfloat fog_density = fog->get_exp_density();
2789 set_render_state(D3DRS_FOGDENSITY,
2790 *((LPDWORD) (&fog_density)));
2791 }
2792 break;
2793 }
2794}
2795
2796/**
2797 * Sends the indicated transform matrix to the graphics API to be applied to
2798 * future vertices.
2799 *
2800 * This transform is the internal_transform, already converted into the GSG's
2801 * internal coordinate system.
2802 */
2803void DXGraphicsStateGuardian9::
2804do_issue_transform() {
2805 const TransformState *transform = _internal_transform;
2806 DO_PSTATS_STUFF(_transform_state_pcollector.add_level(1));
2807
2808 if (_current_shader_context) {
2809// _current_shader_context->issue_transform(this);
2810 _current_shader_context->issue_parameters(this, Shader::SSD_transform);
2811
2812// ??? NO NEED TO SET THE D3D TRANSFORM VIA SetTransform SINCE THE TRANSFORM
2813// IS ONLY USED IN THE SHADER
2814 LMatrix4f mat = LCAST(float, transform->get_mat());
2815 const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data();
2816 _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
2817
2818 }
2819 else {
2820 LMatrix4f mat = LCAST(float, transform->get_mat());
2821 const D3DMATRIX *d3d_mat = (const D3DMATRIX *)mat.get_data();
2822 _d3d_device->SetTransform(D3DTS_WORLD, d3d_mat);
2823
2824// DEBUG PRINT
2825/*
2826 const PN_stdfloat *data;
2827 data = &d3d_mat -> _11;
2828 dxgsg9_cat.debug ( ) << "do_issue_transform\n" <<
2829 data[ 0] << " " << data[ 1] << " " << data[ 2] << " " << data[ 3] << "\n" <<
2830 data[ 4] << " " << data[ 5] << " " << data[ 6] << " " << data[ 7] << "\n" <<
2831 data[ 8] << " " << data[ 9] << " " << data[10] << " " << data[11] << "\n" <<
2832 data[12] << " " << data[13] << " " << data[14] << " " << data[15] << "\n";
2833*/
2834
2835 }
2836
2837 _transform_stale = false;
2838}
2839
2840/**
2841 *
2842 */
2843void DXGraphicsStateGuardian9::
2844do_issue_alpha_test() {
2845 if (_target_shader->get_flag(ShaderAttrib::F_subsume_alpha_test)) {
2846 set_render_state(D3DRS_ALPHATESTENABLE, FALSE);
2847 } else {
2848 const AlphaTestAttrib *target_alpha_test = DCAST(AlphaTestAttrib, _target_rs->get_attrib_def(AlphaTestAttrib::get_class_slot()));
2849 AlphaTestAttrib::PandaCompareFunc mode = target_alpha_test->get_mode();
2850 if (mode == AlphaTestAttrib::M_none) {
2851 set_render_state(D3DRS_ALPHATESTENABLE, FALSE);
2852 } else {
2853 // AlphaTestAttrib::PandaCompareFunc === D3DCMPFUNC
2854 set_render_state(D3DRS_ALPHAFUNC, (D3DCMPFUNC)mode);
2855 set_render_state(D3DRS_ALPHAREF, (UINT) (target_alpha_test->get_reference_alpha()*255.0f)); //d3d uses 0x0-0xFF, not a float
2856 set_render_state(D3DRS_ALPHATESTENABLE, TRUE);
2857 }
2858 }
2859}
2860
2861/**
2862 *
2863 */
2864void DXGraphicsStateGuardian9::
2865do_issue_shader() {
2866
2867 DXShaderContext9 *context = 0;
2868 Shader *shader = 0;
2869 if (_target_shader) {
2870 shader = (Shader *)(_target_shader->get_shader());
2871 }
2872 if (shader) {
2873 context = (DXShaderContext9 *)(shader->prepare_now(get_prepared_objects(), this));
2874 }
2875
2876 if (context == 0 || (context && context -> valid (this) == false)) {
2877 if (_current_shader_context != 0) {
2878 _current_shader_context->unbind(this);
2879 _current_shader = 0;
2880 _current_shader_context = 0;
2881 disable_standard_texture_bindings();
2882 }
2883 return;
2884 }
2885
2886 if (context != _current_shader_context) {
2887 // Use a completely different shader than before. Unbind old shader, bind
2888 // the new one.
2889 if (_current_shader_context != 0) {
2890 _current_shader_context->unbind(this);
2891 _current_shader_context = 0;
2892 _current_shader = 0;
2893 disable_standard_texture_bindings();
2894 }
2895 if (context != 0) {
2896 context->bind(this);
2897 _current_shader = shader;
2898 _current_shader_context = context;
2899 }
2900 } else {
2901 // Use the same shader as before, but with new input arguments.
2902 context->issue_parameters(this, Shader::SSD_shaderinputs);
2903 }
2904}
2905
2906/**
2907 *
2908 */
2909void DXGraphicsStateGuardian9::
2910do_issue_render_mode() {
2911 const RenderModeAttrib *target_render_mode;
2912 _target_rs->get_attrib_def(target_render_mode);
2913 RenderModeAttrib::Mode mode = target_render_mode->get_mode();
2914
2915 switch (mode) {
2916 case RenderModeAttrib::M_unchanged:
2917 case RenderModeAttrib::M_filled:
2918 case RenderModeAttrib::M_filled_flat:
2919 set_render_state(D3DRS_FILLMODE, D3DFILL_SOLID);
2920 break;
2921
2922 case RenderModeAttrib::M_wireframe:
2923 set_render_state(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
2924 break;
2925
2926 case RenderModeAttrib::M_point:
2927 set_render_state(D3DRS_FILLMODE, D3DFILL_POINT);
2928 break;
2929
2930 default:
2931 dxgsg9_cat.error()
2932 << "Unknown render mode " << (int)mode << endl;
2933 }
2934
2935 // This might also specify the point size.
2936 PN_stdfloat point_size = target_render_mode->get_thickness();
2937 set_render_state(D3DRS_POINTSIZE, *((DWORD*)&point_size));
2938
2939 if (target_render_mode->get_perspective()) {
2940 set_render_state(D3DRS_POINTSCALEENABLE, TRUE);
2941
2942 LVector3 height(0.0f, point_size, 1.0f);
2943 height = height * _projection_mat->get_mat();
2944 PN_stdfloat s = height[1] / point_size;
2945
2946 PN_stdfloat zero = 0.0f;
2947 PN_stdfloat one_over_s2 = 1.0f / (s * s);
2948 set_render_state(D3DRS_POINTSCALE_A, *((DWORD*)&zero));
2949 set_render_state(D3DRS_POINTSCALE_B, *((DWORD*)&zero));
2950 set_render_state(D3DRS_POINTSCALE_C, *((DWORD*)&one_over_s2));
2951
2952 } else {
2953 set_render_state(D3DRS_POINTSCALEENABLE, FALSE);
2954 }
2955
2956 _current_fill_mode = mode;
2957}
2958
2959/**
2960 *
2961 */
2962void DXGraphicsStateGuardian9::
2963do_issue_rescale_normal() {
2964 RescaleNormalAttrib::Mode mode = RescaleNormalAttrib::M_none;
2965
2966 const RescaleNormalAttrib *target_rescale_normal;
2967 if (_target_rs->get_attrib(target_rescale_normal)) {
2968 mode = target_rescale_normal->get_mode();
2969 }
2970
2971 switch (mode) {
2972 case RescaleNormalAttrib::M_none:
2973 set_render_state(D3DRS_NORMALIZENORMALS, false);
2974 break;
2975
2976 case RescaleNormalAttrib::M_rescale:
2977 case RescaleNormalAttrib::M_normalize:
2978 set_render_state(D3DRS_NORMALIZENORMALS, true);
2979 break;
2980
2981 default:
2982 dxgsg9_cat.error()
2983 << "Unknown rescale_normal mode " << (int)mode << endl;
2984 }
2985}
2986
2987/**
2988 *
2989 */
2990void DXGraphicsStateGuardian9::
2991do_issue_depth_test() {
2992 const DepthTestAttrib *target_depth_test = DCAST(DepthTestAttrib, _target_rs->get_attrib_def(DepthTestAttrib::get_class_slot()));
2993 DepthTestAttrib::PandaCompareFunc mode = target_depth_test->get_mode();
2994 if (mode == DepthTestAttrib::M_none) {
2995 set_render_state(D3DRS_ZENABLE, D3DZB_FALSE);
2996 } else {
2997 set_render_state(D3DRS_ZENABLE, D3DZB_TRUE);
2998 set_render_state(D3DRS_ZFUNC, (D3DCMPFUNC) mode);
2999 }
3000}
3001
3002/**
3003 *
3004 */
3005void DXGraphicsStateGuardian9::
3006do_issue_depth_write() {
3007 const DepthWriteAttrib *target_depth_write = DCAST(DepthWriteAttrib, _target_rs->get_attrib_def(DepthWriteAttrib::get_class_slot()));
3008 DepthWriteAttrib::Mode mode = target_depth_write->get_mode();
3009 if (mode == DepthWriteAttrib::M_on) {
3010 set_render_state(D3DRS_ZWRITEENABLE, TRUE);
3011 } else {
3012 set_render_state(D3DRS_ZWRITEENABLE, FALSE);
3013 }
3014}
3015
3016/**
3017 *
3018 */
3019void DXGraphicsStateGuardian9::
3020do_issue_cull_face() {
3021 const CullFaceAttrib *target_cull_face = DCAST(CullFaceAttrib, _target_rs->get_attrib_def(CullFaceAttrib::get_class_slot()));
3022 _cull_face_mode = target_cull_face->get_effective_mode();
3023
3024 switch (_cull_face_mode) {
3025 case CullFaceAttrib::M_cull_none:
3026 set_render_state(D3DRS_CULLMODE, D3DCULL_NONE);
3027
3028// printf ("------------------- D3DCULL_NONE\n");
3029
3030 break;
3031 case CullFaceAttrib::M_cull_clockwise:
3032 set_render_state(D3DRS_CULLMODE, D3DCULL_CW);
3033
3034// printf ("------------------- D3DCULL_CW -- CLOCKWISE \n");
3035
3036 break;
3037 case CullFaceAttrib::M_cull_counter_clockwise:
3038 set_render_state(D3DRS_CULLMODE, D3DCULL_CCW);
3039
3040// printf ("------------------- D3DCULL_CCW\n");
3041
3042 break;
3043 default:
3044 dxgsg9_cat.error()
3045 << "invalid cull face mode " << (int)_cull_face_mode << endl;
3046 break;
3047 }
3048}
3049
3050/**
3051 *
3052 */
3053void DXGraphicsStateGuardian9::
3054do_issue_fog() {
3055 const FogAttrib *target_fog = DCAST(FogAttrib, _target_rs->get_attrib_def(FogAttrib::get_class_slot()));
3056 if (!target_fog->is_off()) {
3057 set_render_state(D3DRS_FOGENABLE, TRUE);
3058 Fog *fog = target_fog->get_fog();
3059 nassertv(fog != nullptr);
3060 apply_fog(fog);
3061 } else {
3062 set_render_state(D3DRS_FOGENABLE, FALSE);
3063 }
3064}
3065
3066/**
3067 *
3068 */
3069void DXGraphicsStateGuardian9::
3070do_issue_depth_offset() {
3071 const DepthOffsetAttrib *target_depth_offset = DCAST(DepthOffsetAttrib, _target_rs->get_attrib_def(DepthOffsetAttrib::get_class_slot()));
3072 int offset = target_depth_offset->get_offset();
3073
3074 if (_supports_depth_bias && !dx_broken_depth_bias) {
3075 set_render_state(D3DRS_DEPTHBIAS, offset);
3076 set_render_state(D3DRS_SLOPESCALEDEPTHBIAS, offset);
3077
3078 } else {
3079 // DirectX depth bias isn't directly supported by the driver. Cheese a
3080 // depth bias effect by sliding the viewport backward a bit.
3081 static const PN_stdfloat bias_scale = dx_depth_bias_scale;
3082 D3DVIEWPORT9 vp = _current_viewport;
3083 vp.MinZ -= bias_scale * offset;
3084 vp.MaxZ -= bias_scale * offset;
3085 _d3d_device->SetViewport(&vp);
3086 }
3087}
3088
3089/**
3090 *
3091 */
3092void DXGraphicsStateGuardian9::
3093do_issue_shade_model() {
3094 const ShadeModelAttrib *target_shade_model = DCAST(ShadeModelAttrib, _target_rs->get_attrib_def(ShadeModelAttrib::get_class_slot()));
3095 switch (target_shade_model->get_mode()) {
3096 case ShadeModelAttrib::M_smooth:
3097 set_render_state(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
3098 break;
3099
3100 case ShadeModelAttrib::M_flat:
3101 set_render_state(D3DRS_SHADEMODE, D3DSHADE_FLAT);
3102 break;
3103 }
3104}
3105
3106/**
3107 * Simultaneously resets the render state and the transform state.
3108 *
3109 * This transform specified is the "internal" net transform, already converted
3110 * into the GSG's internal coordinate space by composing it to
3111 * get_cs_transform(). (Previously, this used to be the "external" net
3112 * transform, with the assumption that that GSG would convert it internally,
3113 * but that is no longer the case.)
3114 *
3115 * Special case: if (state==NULL), then the target state is already stored in
3116 * _target.
3117 */
3120 const TransformState *transform) {
3121#ifndef NDEBUG
3122 if (gsg_cat.is_spam()) {
3123 gsg_cat.spam() << "Setting GSG state to " << (void *)target << ":\n";
3124 target->write(gsg_cat.spam(false), 2);
3125 }
3126#endif
3127 _state_pcollector.add_level(1);
3128 PStatTimer timer1(_draw_set_state_pcollector);
3129
3130 if (transform != _internal_transform) {
3131 // PStatTimer timer(_draw_set_state_transform_pcollector);
3132 _state_pcollector.add_level(1);
3133 _internal_transform = transform;
3134 do_issue_transform();
3135 }
3136
3137 if (target == _state_rs && (_state_mask | _inv_state_mask).is_all_on()) {
3138 return;
3139 }
3140 _target_rs = target;
3141
3142 int shader_deps = 0;
3143 determine_target_shader();
3144
3145 int alpha_test_slot = AlphaTestAttrib::get_class_slot();
3146 if (_target_rs->get_attrib(alpha_test_slot) != _state_rs->get_attrib(alpha_test_slot) ||
3147 !_state_mask.get_bit(alpha_test_slot)) {
3148 // PStatTimer timer(_draw_set_state_alpha_test_pcollector);
3149 do_issue_alpha_test();
3150 _state_mask.set_bit(alpha_test_slot);
3151 }
3152
3153 int clip_plane_slot = ClipPlaneAttrib::get_class_slot();
3154 if (_target_rs->get_attrib(clip_plane_slot) != _state_rs->get_attrib(clip_plane_slot) ||
3155 !_state_mask.get_bit(clip_plane_slot)) {
3156 // PStatTimer timer(_draw_set_state_clip_plane_pcollector);
3157 do_issue_clip_plane();
3158 _state_mask.set_bit(clip_plane_slot);
3159 }
3160
3161 int color_slot = ColorAttrib::get_class_slot();
3162 int color_scale_slot = ColorScaleAttrib::get_class_slot();
3163 if (_target_rs->get_attrib(color_slot) != _state_rs->get_attrib(color_slot) ||
3164 _target_rs->get_attrib(color_scale_slot) != _state_rs->get_attrib(color_scale_slot) ||
3165 !_state_mask.get_bit(color_slot) ||
3166 !_state_mask.get_bit(color_scale_slot)) {
3167 // PStatTimer timer(_draw_set_state_color_pcollector);
3169 do_issue_color_scale();
3170 _state_mask.set_bit(color_slot);
3171 _state_mask.set_bit(color_scale_slot);
3172 shader_deps |= Shader::SSD_color | Shader::SSD_colorscale;
3173 }
3174
3175 int cull_face_slot = CullFaceAttrib::get_class_slot();
3176 if (_target_rs->get_attrib(cull_face_slot) != _state_rs->get_attrib(cull_face_slot) ||
3177 !_state_mask.get_bit(cull_face_slot)) {
3178 // PStatTimer timer(_draw_set_state_cull_face_pcollector);
3179 do_issue_cull_face();
3180 _state_mask.set_bit(cull_face_slot);
3181 }
3182
3183 int depth_offset_slot = DepthOffsetAttrib::get_class_slot();
3184 if (_target_rs->get_attrib(depth_offset_slot) != _state_rs->get_attrib(depth_offset_slot) ||
3185 !_state_mask.get_bit(depth_offset_slot)) {
3186 // PStatTimer timer(_draw_set_state_depth_offset_pcollector);
3187 do_issue_depth_offset();
3188 _state_mask.set_bit(depth_offset_slot);
3189 }
3190
3191 int depth_test_slot = DepthTestAttrib::get_class_slot();
3192 if (_target_rs->get_attrib(depth_test_slot) != _state_rs->get_attrib(depth_test_slot) ||
3193 !_state_mask.get_bit(depth_test_slot)) {
3194 // PStatTimer timer(_draw_set_state_depth_test_pcollector);
3195 do_issue_depth_test();
3196 _state_mask.set_bit(depth_test_slot);
3197 }
3198
3199 int depth_write_slot = DepthWriteAttrib::get_class_slot();
3200 if (_target_rs->get_attrib(depth_write_slot) != _state_rs->get_attrib(depth_write_slot) ||
3201 !_state_mask.get_bit(depth_write_slot)) {
3202 // PStatTimer timer(_draw_set_state_depth_write_pcollector);
3203 do_issue_depth_write();
3204 _state_mask.set_bit(depth_write_slot);
3205 }
3206
3207 int render_mode_slot = RenderModeAttrib::get_class_slot();
3208 if (_target_rs->get_attrib(render_mode_slot) != _state_rs->get_attrib(render_mode_slot) ||
3209 !_state_mask.get_bit(render_mode_slot)) {
3210 // PStatTimer timer(_draw_set_state_render_mode_pcollector);
3211 do_issue_render_mode();
3212 _state_mask.set_bit(render_mode_slot);
3213 shader_deps |= Shader::SSD_render_mode;
3214 }
3215
3216 int rescale_normal_slot = RescaleNormalAttrib::get_class_slot();
3217 if (_target_rs->get_attrib(rescale_normal_slot) != _state_rs->get_attrib(rescale_normal_slot) ||
3218 !_state_mask.get_bit(rescale_normal_slot)) {
3219 // PStatTimer timer(_draw_set_state_rescale_normal_pcollector);
3220 do_issue_rescale_normal();
3221 _state_mask.set_bit(rescale_normal_slot);
3222 }
3223
3224 int shade_model_slot = ShadeModelAttrib::get_class_slot();
3225 if (_target_rs->get_attrib(shade_model_slot) != _state_rs->get_attrib(shade_model_slot) ||
3226 !_state_mask.get_bit(shade_model_slot)) {
3227 // PStatTimer timer(_draw_set_state_shade_model_pcollector);
3228 do_issue_shade_model();
3229 _state_mask.set_bit(shade_model_slot);
3230 }
3231
3232 int transparency_slot = TransparencyAttrib::get_class_slot();
3233 int color_write_slot = ColorWriteAttrib::get_class_slot();
3234 int color_blend_slot = ColorBlendAttrib::get_class_slot();
3235 if (_target_rs->get_attrib(transparency_slot) != _state_rs->get_attrib(transparency_slot) ||
3236 _target_rs->get_attrib(color_write_slot) != _state_rs->get_attrib(color_write_slot) ||
3237 _target_rs->get_attrib(color_blend_slot) != _state_rs->get_attrib(color_blend_slot) ||
3238 !_state_mask.get_bit(transparency_slot) ||
3239 !_state_mask.get_bit(color_write_slot) ||
3240 !_state_mask.get_bit(color_blend_slot) ||
3241 (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write) !=
3242 _state_shader->get_flag(ShaderAttrib::F_disable_alpha_write))) {
3243 // PStatTimer timer(_draw_set_state_blending_pcollector);
3244 do_issue_blending();
3245 _state_mask.set_bit(transparency_slot);
3246 _state_mask.set_bit(color_write_slot);
3247 _state_mask.set_bit(color_blend_slot);
3248 }
3249
3250 if (_target_shader != _state_shader) {
3251 // PStatTimer timer(_draw_set_state_shader_pcollector);
3252 do_issue_shader();
3253 _state_shader = _target_shader;
3254 _state_mask.clear_bit(TextureAttrib::get_class_slot());
3255 }
3256
3257 int texture_slot = TextureAttrib::get_class_slot();
3258 int tex_matrix_slot = TexMatrixAttrib::get_class_slot();
3259 int tex_gen_slot = TexGenAttrib::get_class_slot();
3260 if (_target_rs->get_attrib(texture_slot) != _state_rs->get_attrib(texture_slot) ||
3261 _target_rs->get_attrib(tex_matrix_slot) != _state_rs->get_attrib(tex_matrix_slot) ||
3262 _target_rs->get_attrib(tex_gen_slot) != _state_rs->get_attrib(tex_gen_slot) ||
3263 !_state_mask.get_bit(texture_slot) ||
3264 !_state_mask.get_bit(tex_matrix_slot) ||
3265 !_state_mask.get_bit(tex_gen_slot)) {
3266 // PStatTimer timer(_draw_set_state_texture_pcollector);
3267 determine_target_texture();
3268 do_issue_texture();
3269
3270 _state_texture = _target_texture;
3271 _state_mask.set_bit(texture_slot);
3272 _state_mask.set_bit(tex_matrix_slot);
3273 _state_mask.set_bit(tex_gen_slot);
3274 shader_deps |= Shader::SSD_tex_matrix | Shader::SSD_tex_gen;
3275 }
3276
3277 int material_slot = MaterialAttrib::get_class_slot();
3278 if (_target_rs->get_attrib(material_slot) != _state_rs->get_attrib(material_slot) ||
3279 !_state_mask.get_bit(material_slot)) {
3280 // PStatTimer timer(_draw_set_state_material_pcollector);
3281 do_issue_material();
3282 _state_mask.set_bit(material_slot);
3283 shader_deps |= Shader::SSD_material;
3284 }
3285
3286 int light_slot = LightAttrib::get_class_slot();
3287 if (_target_rs->get_attrib(light_slot) != _state_rs->get_attrib(light_slot) ||
3288 !_state_mask.get_bit(light_slot)) {
3289 // PStatTimer timer(_draw_set_state_light_pcollector);
3291 _state_mask.set_bit(light_slot);
3292 }
3293
3294 int stencil_slot = StencilAttrib::get_class_slot();
3295 if (_target_rs->get_attrib(stencil_slot) != _state_rs->get_attrib(stencil_slot) ||
3296 !_state_mask.get_bit(stencil_slot)) {
3297 // PStatTimer timer(_draw_set_state_stencil_pcollector);
3298 do_issue_stencil();
3299 _state_mask.set_bit(stencil_slot);
3300 }
3301
3302 int fog_slot = FogAttrib::get_class_slot();
3303 if (_target_rs->get_attrib(fog_slot) != _state_rs->get_attrib(fog_slot) ||
3304 !_state_mask.get_bit(fog_slot)) {
3305 // PStatTimer timer(_draw_set_state_fog_pcollector);
3306 do_issue_fog();
3307 _state_mask.set_bit(fog_slot);
3308 shader_deps |= Shader::SSD_fog;
3309 }
3310
3311 int scissor_slot = ScissorAttrib::get_class_slot();
3312 if (_target_rs->get_attrib(scissor_slot) != _state_rs->get_attrib(scissor_slot) ||
3313 !_state_mask.get_bit(scissor_slot)) {
3314 // PStatTimer timer(_draw_set_state_scissor_pcollector);
3315 do_issue_scissor();
3316 _state_mask.set_bit(scissor_slot);
3317 }
3318
3319 if (_current_shader_context != nullptr && shader_deps != 0) {
3320 _current_shader_context->issue_parameters(this, shader_deps);
3321 }
3322
3323 _state_rs = _target_rs;
3324}
3325
3326/**
3327 * Called the first time a particular light has been bound to a given id
3328 * within a frame, this should set up the associated hardware light with the
3329 * light's properties.
3330 */
3332bind_light(PointLight *light_obj, const NodePath &light, int light_id) {
3333 // Get the light in "world coordinates" (actually, view coordinates). This
3334 // means the light in the coordinate space of the camera, converted to DX's
3335 // coordinate system.
3336 CPT(TransformState) transform = light.get_transform(_scene_setup->get_camera_path());
3337 const LMatrix4 &light_mat = transform->get_mat();
3338 LMatrix4 rel_mat = light_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
3339 LPoint3f pos = LCAST(float, light_obj->get_point() * rel_mat);
3340
3341 D3DCOLORVALUE black;
3342 black.r = black.g = black.b = black.a = 0.0f;
3343 D3DLIGHT9 alight;
3344 alight.Type = D3DLIGHT_POINT;
3345 alight.Diffuse = get_light_color(light_obj);
3346 alight.Ambient = black ;
3347 LColorf color = LCAST(float, light_obj->get_specular_color());
3348 alight.Specular = *(D3DCOLORVALUE *)(color.get_data());
3349
3350 // Position needs to specify x, y, z, and w w == 1 implies non-infinite
3351 // position
3352 alight.Position = *(D3DVECTOR *)pos.get_data();
3353
3354 alight.Range = __D3DLIGHT_RANGE_MAX;
3355 alight.Falloff = 1.0f;
3356
3357 const LVecBase3 &att = light_obj->get_attenuation();
3358 alight.Attenuation0 = att[0];
3359 alight.Attenuation1 = att[1];
3360 alight.Attenuation2 = att[2];
3361
3362 HRESULT hr = _d3d_device->SetLight(light_id, &alight);
3363 if (FAILED(hr)) {
3364 wdxdisplay9_cat.warning()
3365 << "Could not set light properties for " << light
3366 << " to id " << light_id << ": " << D3DERRORSTRING(hr) << "\n";
3367 }
3368}
3369
3370/**
3371 * Called the first time a particular light has been bound to a given id
3372 * within a frame, this should set up the associated hardware light with the
3373 * light's properties.
3374 */
3376bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
3377 static PStatCollector _draw_set_state_light_bind_directional_pcollector("Draw:Set State:Light:Bind:Directional");
3378 // PStatTimer timer(_draw_set_state_light_bind_directional_pcollector);
3379
3380 std::pair<DirectionalLights::iterator, bool> lookup = _dlights.insert(DirectionalLights::value_type(light, D3DLIGHT9()));
3381 D3DLIGHT9 &fdata = (*lookup.first).second;
3382 if (lookup.second) {
3383 // Get the light in "world coordinates" (actually, view coordinates).
3384 // This means the light in the coordinate space of the camera, converted
3385 // to DX's coordinate system.
3386 CPT(TransformState) transform = light.get_transform(_scene_setup->get_camera_path());
3387 const LMatrix4 &light_mat = transform->get_mat();
3388 LMatrix4 rel_mat = light_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
3389 LVector3f dir = LCAST(float, light_obj->get_direction() * rel_mat);
3390
3391 D3DCOLORVALUE black;
3392 black.r = black.g = black.b = black.a = 0.0f;
3393
3394 ZeroMemory(&fdata, sizeof(D3DLIGHT9));
3395
3396 fdata.Type = D3DLIGHT_DIRECTIONAL;
3397 fdata.Ambient = black ;
3398 LColorf color = LCAST(float, light_obj->get_specular_color());
3399 fdata.Specular = *(D3DCOLORVALUE *)(color.get_data());
3400
3401 fdata.Direction = *(D3DVECTOR *)dir.get_data();
3402
3403 fdata.Range = __D3DLIGHT_RANGE_MAX;
3404 fdata.Falloff = 1.0f;
3405
3406 fdata.Attenuation0 = 1.0f; // constant
3407 fdata.Attenuation1 = 0.0f; // linear
3408 fdata.Attenuation2 = 0.0f; // quadratic
3409 }
3410
3411 // We have to reset the Diffuse color at each call, because it might have
3412 // changed independently of the light object itself (due to
3413 // color_scale_via_lighting being in effect).
3414 fdata.Diffuse = get_light_color(light_obj);
3415
3416 HRESULT hr = _d3d_device->SetLight(light_id, &fdata);
3417 if (FAILED(hr)) {
3418 wdxdisplay9_cat.warning()
3419 << "Could not set light properties for " << light
3420 << " to id " << light_id << ": " << D3DERRORSTRING(hr) << "\n";
3421 }
3422}
3423
3424/**
3425 * Called the first time a particular light has been bound to a given id
3426 * within a frame, this should set up the associated hardware light with the
3427 * light's properties.
3428 */
3430bind_light(Spotlight *light_obj, const NodePath &light, int light_id) {
3431 Lens *lens = light_obj->get_lens();
3432 nassertv(lens != nullptr);
3433
3434 // Get the light in "world coordinates" (actually, view coordinates). This
3435 // means the light in the coordinate space of the camera, converted to DX's
3436 // coordinate system.
3437 CPT(TransformState) transform = light.get_transform(_scene_setup->get_camera_path());
3438 const LMatrix4 &light_mat = transform->get_mat();
3439 LMatrix4 rel_mat = light_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
3440 LPoint3f pos = LCAST(float, lens->get_nodal_point() * rel_mat);
3441 LVector3f dir = LCAST(float, lens->get_view_vector() * rel_mat);
3442
3443 D3DCOLORVALUE black;
3444 black.r = black.g = black.b = black.a = 0.0f;
3445
3446 D3DLIGHT9 alight;
3447 ZeroMemory(&alight, sizeof(D3DLIGHT9));
3448
3449 alight.Type = D3DLIGHT_SPOT;
3450 alight.Ambient = black ;
3451 alight.Diffuse = get_light_color(light_obj);
3452 LColorf color = LCAST(float, light_obj->get_specular_color());
3453 alight.Specular = *(D3DCOLORVALUE *)(color.get_data());
3454
3455 alight.Position = *(D3DVECTOR *)pos.get_data();
3456
3457 alight.Direction = *(D3DVECTOR *)dir.get_data();
3458
3459 alight.Range = __D3DLIGHT_RANGE_MAX;
3460
3461 // I determined this formular empirically. It seems to mostly approximate
3462 // the OpenGL spotlight equation, for a reasonable range of values for FOV.
3463 PN_stdfloat fov = lens->get_hfov();
3464 alight.Falloff = light_obj->get_exponent() * (fov * fov * fov) / 1620000.0f;
3465
3466 alight.Theta = 0.0f;
3467 alight.Phi = deg_2_rad(fov);
3468
3469 const LVecBase3 &att = light_obj->get_attenuation();
3470 alight.Attenuation0 = att[0];
3471 alight.Attenuation1 = att[1];
3472 alight.Attenuation2 = att[2];
3473
3474 HRESULT hr = _d3d_device->SetLight(light_id, &alight);
3475 if (FAILED(hr)) {
3476 wdxdisplay9_cat.warning()
3477 << "Could not set light properties for " << light
3478 << " to id " << light_id << ": " << D3DERRORSTRING(hr) << "\n";
3479 }
3480}
3481
3482/**
3483 * Maps from the Geom's internal numeric type symbols to DirectX's.
3484 */
3486get_index_type(Geom::NumericType numeric_type) {
3487 switch (numeric_type) {
3488 // NT_uint8 is automatically promoted to uint16.
3489 case Geom::NT_uint8:
3490 case Geom::NT_uint16:
3491 return D3DFMT_INDEX16;
3492
3493 case Geom::NT_uint32:
3494 return D3DFMT_INDEX32;
3495 }
3496
3497 dxgsg9_cat.error()
3498 << "Invalid index NumericType value (" << (int)numeric_type << ")\n";
3499 return D3DFMT_INDEX16;
3500}
3501
3502/**
3503 *
3504 */
3505void DXGraphicsStateGuardian9::
3506do_issue_material() {
3507 static Material empty;
3508 const Material *material;
3509 const MaterialAttrib *target_material = DCAST(MaterialAttrib, _target_rs->get_attrib_def(MaterialAttrib::get_class_slot()));
3510 if (target_material->is_off()) {
3511 material = &empty;
3512 } else {
3513 material = target_material->get_material();
3514 }
3515
3516 D3DMATERIAL9 cur_material;
3517 LColorf color = LCAST(float, material->get_diffuse());
3518 cur_material.Diffuse = *(D3DCOLORVALUE *)(color.get_data());
3519 color = LCAST(float, material->get_ambient());
3520 cur_material.Ambient = *(D3DCOLORVALUE *)(color.get_data());
3521 color = LCAST(float, material->get_specular());
3522 cur_material.Specular = *(D3DCOLORVALUE *)(color.get_data());
3523 color = LCAST(float, material->get_emission());
3524 cur_material.Emissive = *(D3DCOLORVALUE *)(color.get_data());
3525 cur_material.Power = material->get_shininess();
3526
3527 if (material->has_diffuse() || material->has_base_color()) {
3528 // If the material specifies an diffuse color, use it.
3529 set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
3530 } else {
3531 // Otherwise, the diffuse color comes from the object color.
3532 if (_has_material_force_color) {
3533 color = LCAST(float, _material_force_color);
3534 cur_material.Diffuse = *(D3DCOLORVALUE *)color.get_data();
3535 set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
3536 } else {
3537 set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
3538 }
3539 }
3540 if (material->has_ambient() || material->has_base_color()) {
3541 // If the material specifies an ambient color, use it.
3542 set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
3543 } else {
3544 // Otherwise, the ambient color comes from the object color.
3545 if (_has_material_force_color) {
3546 color = LCAST(float, _material_force_color);
3547 cur_material.Ambient = *(D3DCOLORVALUE *)color.get_data();
3548 set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
3549 } else {
3550 set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_COLOR1);
3551 }
3552 }
3553
3554 if (material->has_specular() || material->has_base_color()) {
3555 set_render_state(D3DRS_SPECULARENABLE, TRUE);
3556 } else {
3557 set_render_state(D3DRS_SPECULARENABLE, FALSE);
3558 }
3559
3560 if (material->get_local()) {
3561 set_render_state(D3DRS_LOCALVIEWER, TRUE);
3562 } else {
3563 set_render_state(D3DRS_LOCALVIEWER, FALSE);
3564 }
3565
3566 _d3d_device->SetMaterial(&cur_material);
3567}
3568
3569/**
3570 *
3571 */
3572void DXGraphicsStateGuardian9::
3573do_issue_texture() {
3574 DO_PSTATS_STUFF(_texture_state_pcollector.add_level(1));
3575
3576 if (_texture_binding_shader_context==0) {
3577 if (_current_shader_context==0) {
3578 update_standard_texture_bindings();
3579 } else {
3580 disable_standard_texture_bindings();
3581 _current_shader_context->update_shader_texture_bindings(nullptr,this);
3582 }
3583 } else {
3584 if (_current_shader_context==0) {
3585 _texture_binding_shader_context->disable_shader_texture_bindings(this);
3586 update_standard_texture_bindings();
3587 } else {
3588 _current_shader_context->
3589 update_shader_texture_bindings(_texture_binding_shader_context,this);
3590 }
3591 }
3592 _texture_binding_shader = _current_shader;
3593 _texture_binding_shader_context = _current_shader_context;
3594}
3595
3596/**
3597 *
3598 */
3599void DXGraphicsStateGuardian9::
3600disable_standard_texture_bindings() {
3601 // Disable the texture stages that are no longer used.
3602 for (int i = 0; i < _num_active_texture_stages; i++) {
3603 HRESULT hr;
3604
3605 hr = _d3d_device -> SetTexture (i, nullptr);
3606 if (FAILED (hr)) {
3607 dxgsg9_cat.error()
3608 << "SetTexture ("
3609 << i
3610 << ", NULL) failed "
3611 << D3DERRORSTRING(hr);
3612 }
3613 set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
3614 }
3615
3616 _num_active_texture_stages = 0;
3617}
3618
3619/**
3620 *
3621 */
3622void DXGraphicsStateGuardian9::
3623update_standard_texture_bindings() {
3624 DO_PSTATS_STUFF(_texture_state_pcollector.add_level(1));
3625
3626 int num_stages = _target_texture->get_num_on_ff_stages();
3627 int num_old_stages = _max_texture_stages;
3628 if (_state_texture != nullptr) {
3629 num_old_stages = _state_texture->get_num_on_ff_stages();
3630 }
3631
3632 nassertv(num_stages <= _max_texture_stages &&
3633 _num_active_texture_stages <= _max_texture_stages);
3634
3635 _texture_involves_color_scale = false;
3636
3637 // We have to match up the texcoord stage index to the order written out by
3638 // the DXGeomMunger. This means the texcoord names are written in the order
3639 // indicated by the TextureAttrib.
3640
3641 int si;
3642 for (si = 0; si < num_stages; si++) {
3643 TextureStage *stage = _target_texture->get_on_ff_stage(si);
3644 int texcoord_index = _target_texture->get_ff_tc_index(si);
3645
3646 Texture *texture = _target_texture->get_on_texture(stage);
3647 nassertv(texture != nullptr);
3648 const SamplerState &sampler = _target_texture->get_on_sampler(stage);
3649
3650 // We always reissue every stage in DX, just in case the texcoord index or
3651 // texgen mode or some other property has changed.
3652 int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
3653 TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
3654 apply_texture(si, tc, sampler);
3655 set_texture_blend_mode(si, stage);
3656
3657 int texcoord_dimensions = 2;
3658
3659 CPT(TransformState) tex_mat = TransformState::make_identity();
3660 const TexMatrixAttrib *target_tex_matrix = DCAST(TexMatrixAttrib, _target_rs->get_attrib_def(TexMatrixAttrib::get_class_slot()));
3661 if (target_tex_matrix->has_stage(stage)) {
3662 tex_mat = target_tex_matrix->get_transform(stage);
3663 }
3664
3665 // Issue the texgen mode.
3666 TexGenAttrib::Mode mode = _target_tex_gen->get_mode(stage);
3667 bool any_point_sprite = false;
3668
3669 switch (mode) {
3670 case TexGenAttrib::M_off:
3671 case TexGenAttrib::M_unused2:
3672 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX, texcoord_index);
3673 break;
3674
3675 case TexGenAttrib::M_eye_sphere_map:
3676 {
3677 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3678 texcoord_index | D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);
3679 // This texture matrix, applied on top of the texcoord computed by
3680 // D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR, approximates the effect
3681 // produced by OpenGL's GL_SPHERE_MAP.
3682 static CPT(TransformState) sphere_map =
3683 TransformState::make_mat(LMatrix4(0.33, 0.0f, 0.0f, 0.0f,
3684 0.0f, 0.33, 0.0f, 0.0f,
3685 0.0f, 0.0f, 1.0f, 0.0f,
3686 0.5f, 0.5f, 0.0f, 1.0f));
3687 tex_mat = tex_mat->compose(sphere_map);
3688 texcoord_dimensions = 3;
3689 }
3690 break;
3691
3692 case TexGenAttrib::M_world_cube_map:
3693 // To achieve world reflection vector, we must transform camera
3694 // coordinates to world coordinates; i.e. apply the camera transform.
3695 // In the case of a vector, we should not apply the pos component of the
3696 // transform.
3697 {
3698 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3699 texcoord_index | D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);
3700 texcoord_dimensions = 3;
3701 CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
3702 tex_mat = tex_mat->compose(camera_transform->set_pos(LVecBase3::zero()));
3703 }
3704 break;
3705
3706 case TexGenAttrib::M_eye_cube_map:
3707 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3708 texcoord_index | D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);
3709 tex_mat = tex_mat->compose(_inv_cs_transform);
3710 texcoord_dimensions = 3;
3711 break;
3712
3713 case TexGenAttrib::M_world_normal:
3714 // To achieve world normal, we must transform camera coordinates to
3715 // world coordinates; i.e. apply the camera transform. In the case of
3716 // a normal, we should not apply the pos component of the transform.
3717 {
3718 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3719 texcoord_index | D3DTSS_TCI_CAMERASPACENORMAL);
3720 texcoord_dimensions = 3;
3721 CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
3722 tex_mat = tex_mat->compose(camera_transform->set_pos(LVecBase3::zero()));
3723 }
3724 break;
3725
3726 case TexGenAttrib::M_eye_normal:
3727 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3728 texcoord_index | D3DTSS_TCI_CAMERASPACENORMAL);
3729 texcoord_dimensions = 3;
3730 tex_mat = tex_mat->compose(_inv_cs_transform);
3731 break;
3732
3733 case TexGenAttrib::M_world_position:
3734 // To achieve world position, we must transform camera coordinates to
3735 // world coordinates; i.e. apply the camera transform.
3736 {
3737 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3738 texcoord_index | D3DTSS_TCI_CAMERASPACEPOSITION);
3739 texcoord_dimensions = 3;
3740 CPT(TransformState) camera_transform = _scene_setup->get_camera_transform()->compose(_inv_cs_transform);
3741 tex_mat = tex_mat->compose(camera_transform);
3742 }
3743 break;
3744
3745 case TexGenAttrib::M_eye_position:
3746 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3747 texcoord_index | D3DTSS_TCI_CAMERASPACEPOSITION);
3748 texcoord_dimensions = 3;
3749 tex_mat = tex_mat->compose(_inv_cs_transform);
3750 break;
3751
3752 case TexGenAttrib::M_point_sprite:
3753 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX, texcoord_index);
3754 any_point_sprite = true;
3755 break;
3756
3757 case TexGenAttrib::M_constant:
3758 // To generate a constant UV(w) coordinate everywhere, we use
3759 // CAMERASPACEPOSITION coordinates, but we construct a special matrix
3760 // that flattens the existing values to zero and then adds our desired
3761 // value.
3762
3763 // The only reason we need to specify CAMERASPACEPOSITION at all,
3764 // instead of using whatever texture coordinates (if any) happen to be
3765 // on the vertices, is because we need to guarantee that there are 3-d
3766 // texture coordinates, because of the 3-component texture coordinate in
3767 // get_constant_value().
3768 {
3769 set_texture_stage_state(si, D3DTSS_TEXCOORDINDEX,
3770 texcoord_index | D3DTSS_TCI_CAMERASPACEPOSITION);
3771 texcoord_dimensions = 3;
3772
3773 const LTexCoord3 &v = _target_tex_gen->get_constant_value(stage);
3774 CPT(TransformState) squash =
3775 TransformState::make_pos_hpr_scale(v, LVecBase3::zero(),
3776 LVecBase3::zero());
3777 tex_mat = tex_mat->compose(squash);
3778 }
3779 break;
3780 }
3781
3782 set_render_state(D3DRS_POINTSPRITEENABLE, any_point_sprite);
3783
3784 if (!tex_mat->is_identity()) {
3785 if (/*tex_mat->is_2d() &&*/ texcoord_dimensions <= 2) {
3786 // For 2-d texture coordinates, we have to reorder the matrix.
3787 LMatrix4 m = tex_mat->get_mat();
3788 LMatrix4f mf;
3789 mf.set(m(0, 0), m(0, 1), m(0, 3), 0.0f,
3790 m(1, 0), m(1, 1), m(1, 3), 0.0f,
3791 m(3, 0), m(3, 1), m(3, 3), 0.0f,
3792 0.0f, 0.0f, 0.0f, 1.0f);
3793 _d3d_device->SetTransform(get_tex_mat_sym(si), (D3DMATRIX *)mf.get_data());
3794 set_texture_stage_state(si, D3DTSS_TEXTURETRANSFORMFLAGS,
3795 D3DTTFF_COUNT2);
3796 } else {
3797 LMatrix4f mf = LCAST(float, tex_mat->get_mat());
3798 _d3d_device->SetTransform(get_tex_mat_sym(si), (D3DMATRIX *)mf.get_data());
3799 DWORD transform_flags = texcoord_dimensions;
3800 if (mf.get_col(3) != LVecBase4f(0.0f, 0.0f, 0.0f, 1.0f)) {
3801 // If we have a projected texture matrix, we also need to set
3802 // D3DTTFF_COUNT4.
3803 transform_flags = D3DTTFF_COUNT4 | D3DTTFF_PROJECTED;
3804 }
3805 set_texture_stage_state(si, D3DTSS_TEXTURETRANSFORMFLAGS,
3806 transform_flags);
3807 }
3808
3809 } else {
3810 set_texture_stage_state(si, D3DTSS_TEXTURETRANSFORMFLAGS,
3811 D3DTTFF_DISABLE);
3812 // For some reason, "disabling" texture coordinate transforms doesn't
3813 // seem to be sufficient. We'll load an identity matrix to underscore
3814 // the point.
3815 _d3d_device->SetTransform(get_tex_mat_sym(si), &_d3d_ident_mat);
3816 }
3817 }
3818
3819 // Disable the texture stages that are no longer used.
3820 for (si = num_stages; si < _num_active_texture_stages; si++) {
3821 set_texture_stage_state(si, D3DTSS_COLOROP, D3DTOP_DISABLE);
3822 _d3d_device->SetTexture(si, nullptr);
3823 }
3824
3825 // Save the count of texture stages for next time.
3826 _num_active_texture_stages = num_stages;
3827}
3828
3829/**
3830 * Called after any of the things that might change blending state have
3831 * changed, this function is responsible for setting the appropriate color
3832 * blending mode based on the current properties.
3833 */
3834void DXGraphicsStateGuardian9::
3835do_issue_blending() {
3836 // Handle the color_write attrib. If color_write is off, then all the other
3837 // blending-related stuff doesn't matter. If the device doesn't support
3838 // color-write, we use blending tricks to effectively disable color write.
3839 const ColorWriteAttrib *target_color_write = DCAST(ColorWriteAttrib, _target_rs->get_attrib_def(ColorWriteAttrib::get_class_slot()));
3840 unsigned int color_channels =
3841 target_color_write->get_channels() & _color_write_mask;
3842 if (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write)) {
3843 color_channels &= ~(ColorWriteAttrib::C_alpha);
3844 }
3845 if (color_channels == ColorWriteAttrib::C_off) {
3846 if (_screen->_can_direct_disable_color_writes) {
3847 set_render_state(D3DRS_ALPHABLENDENABLE, FALSE);
3848 set_render_state(D3DRS_COLORWRITEENABLE, (DWORD)0x0);
3849 } else {
3850 set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3851 set_render_state(D3DRS_SRCBLEND, D3DBLEND_ZERO);
3852 set_render_state(D3DRS_DESTBLEND, D3DBLEND_ONE);
3853 }
3854 return;
3855 } else {
3856 if (_screen->_can_direct_disable_color_writes) {
3857 set_render_state(D3DRS_COLORWRITEENABLE, color_channels);
3858 }
3859 }
3860
3861 const ColorBlendAttrib *color_blend;
3862 _target_rs->get_attrib_def(color_blend);
3863 ColorBlendAttrib::Mode color_blend_mode = color_blend->get_mode();
3864
3865 const TransparencyAttrib *target_transparency;
3866 _target_rs->get_attrib_def(target_transparency);
3867 TransparencyAttrib::Mode transparency_mode = target_transparency->get_mode();
3868
3869 // Is there a color blend set?
3870 if (color_blend_mode != ColorBlendAttrib::M_none) {
3871 set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3872 set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
3873 set_render_state(D3DRS_BLENDOP, get_blend_mode(color_blend_mode));
3874 set_render_state(D3DRS_BLENDOPALPHA, get_blend_mode(color_blend->get_alpha_mode()));
3875 set_render_state(D3DRS_SRCBLEND, get_blend_func(color_blend->get_operand_a()));
3876 set_render_state(D3DRS_DESTBLEND, get_blend_func(color_blend->get_operand_b()));
3877 set_render_state(D3DRS_SRCBLENDALPHA, get_blend_func(color_blend->get_alpha_operand_a()));
3878 set_render_state(D3DRS_DESTBLENDALPHA, get_blend_func(color_blend->get_alpha_operand_b()));
3879 return;
3880 }
3881
3882 // No color blend; is there a transparency set?
3883 switch (transparency_mode) {
3884 case TransparencyAttrib::M_none:
3885 case TransparencyAttrib::M_binary:
3886 break;
3887
3888 case TransparencyAttrib::M_alpha:
3889 case TransparencyAttrib::M_multisample:
3890 case TransparencyAttrib::M_multisample_mask:
3891 case TransparencyAttrib::M_dual:
3892 set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3893 if (old_alpha_blend) {
3894 set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
3895 } else {
3896 set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
3897 set_render_state(D3DRS_BLENDOPALPHA, D3DBLENDOP_ADD);
3898 set_render_state(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
3899 set_render_state(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
3900 }
3901 set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD);
3902 set_render_state(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
3903 set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
3904 return;
3905
3906 case TransparencyAttrib::M_premultiplied_alpha:
3907 set_render_state(D3DRS_ALPHABLENDENABLE, TRUE);
3908 set_render_state(D3DRS_SEPARATEALPHABLENDENABLE, FALSE);
3909 set_render_state(D3DRS_BLENDOP, D3DBLENDOP_ADD);
3910 set_render_state(D3DRS_SRCBLEND, D3DBLEND_ONE);
3911 set_render_state(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
3912 return;
3913
3914 default:
3915 dxgsg9_cat.error()
3916 << "invalid transparency mode " << (int)transparency_mode << endl;
3917 break;
3918 }
3919
3920 // Nothing's set, so disable blending.
3921 set_render_state(D3DRS_ALPHABLENDENABLE, FALSE);
3922}
3923
3924/**
3925 * Called by clear_state_and_transform() to ensure that the current modelview
3926 * and projection matrices are properly loaded in the graphics state, after a
3927 * callback might have mucked them up.
3928 */
3929void DXGraphicsStateGuardian9::
3930reissue_transforms() {
3931 prepare_lens();
3932 do_issue_transform();
3933}
3934
3935/**
3936 * Intended to be overridden by a derived class to enable or disable the use
3937 * of lighting overall. This is called by issue_light() according to whether
3938 * any lights are in use or not.
3939 */
3940void DXGraphicsStateGuardian9::
3941enable_lighting(bool enable) {
3942 set_render_state(D3DRS_LIGHTING, (DWORD)enable);
3943}
3944
3945/**
3946 * Intended to be overridden by a derived class to indicate the color of the
3947 * ambient light that should be in effect. This is called by issue_light()
3948 * after all other lights have been enabled or disabled.
3949 */
3950void DXGraphicsStateGuardian9::
3951set_ambient_light(const LColor &color) {
3952 LColor c = color;
3953 c.set(c[0] * _light_color_scale[0],
3954 c[1] * _light_color_scale[1],
3955 c[2] * _light_color_scale[2],
3956 c[3] * _light_color_scale[3]);
3957
3958 set_render_state(D3DRS_AMBIENT, LColor_to_D3DCOLOR(c));
3959}
3960
3961/**
3962 * Intended to be overridden by a derived class to enable the indicated light
3963 * id. A specific Light will already have been bound to this id via
3964 * bind_light().
3965 */
3966void DXGraphicsStateGuardian9::
3967enable_light(int light_id, bool enable) {
3968 HRESULT hr = _d3d_device->LightEnable(light_id, enable);
3969
3970 if (FAILED(hr)) {
3971 wdxdisplay9_cat.warning()
3972 << "Could not enable light " << light_id << ": "
3973 << D3DERRORSTRING(hr) << "\n";
3974 }
3975}
3976
3977/**
3978 * Intended to be overridden by a derived class to enable the indicated
3979 * clip_plane id. A specific PlaneNode will already have been bound to this
3980 * id via bind_clip_plane().
3981 */
3982void DXGraphicsStateGuardian9::
3983enable_clip_plane(int plane_id, bool enable) {
3984 if (enable) {
3985 _clip_plane_bits |= ((DWORD)1 << plane_id);
3986 } else {
3987 _clip_plane_bits &= ~((DWORD)1 << plane_id);
3988 }
3989 set_render_state(D3DRS_CLIPPLANEENABLE, _clip_plane_bits);
3990}
3991
3992/**
3993 * Called the first time a particular clip_plane has been bound to a given id
3994 * within a frame, this should set up the associated hardware clip_plane with
3995 * the clip_plane's properties.
3996 */
3997void DXGraphicsStateGuardian9::
3998bind_clip_plane(const NodePath &plane, int plane_id) {
3999 // Get the plane in "world coordinates" (actually, view coordinates). This
4000 // means the plane in the coordinate space of the camera, converted to DX's
4001 // coordinate system.
4002 CPT(TransformState) transform = plane.get_transform(_scene_setup->get_camera_path());
4003 const LMatrix4 &plane_mat = transform->get_mat();
4004 LMatrix4 rel_mat = plane_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
4005 const PlaneNode *plane_node;
4006 DCAST_INTO_V(plane_node, plane.node());
4007 LPlanef world_plane = LCAST(float, plane_node->get_plane() * rel_mat);
4008
4009 HRESULT hr = _d3d_device->SetClipPlane(plane_id, world_plane.get_data());
4010 if (FAILED(hr)) {
4011 wdxdisplay9_cat.warning()
4012 << "Could not set clip plane for " << plane
4013 << " to id " << plane_id << ": " << D3DERRORSTRING(hr) << "\n";
4014 }
4015}
4016
4017/**
4018 * This is called by the associated GraphicsWindow when close_window() is
4019 * called. It should null out the _win pointer and possibly free any open
4020 * resources associated with the GSG.
4021 */
4022void DXGraphicsStateGuardian9::
4023close_gsg() {
4024 GraphicsStateGuardian::close_gsg();
4025
4026 if (_prepared_objects.is_null()) {
4027 return;
4028 }
4029
4030 if (dxgsg9_cat.is_debug()) {
4031 dxgsg9_cat.debug()
4032 << "Closing GSG, prepared_objects count = "
4033 << _prepared_objects->get_ref_count() << "\n";
4034 }
4035
4036 // Unlike in OpenGL, in DX9 it is safe to try to explicitly release any
4037 // textures here. And it may even be a good idea.
4038 if (_prepared_objects->get_ref_count() == 1) {
4039 release_all();
4040
4041 // Now we need to actually delete all of the objects we just released.
4042 Thread *current_thread = Thread::get_current_thread();
4043 _prepared_objects->begin_frame(this, current_thread);
4044 _prepared_objects->end_frame(current_thread);
4045 }
4046}
4047
4048/**
4049 * Frees some memory that was explicitly allocated within the dxgsg.
4050 */
4051void DXGraphicsStateGuardian9::
4052free_nondx_resources() {
4053#ifdef HAVE_CG
4054 if (_cg_context) {
4055 cgDestroyContext(_cg_context);
4056 _cg_context = 0;
4057 }
4058#endif
4059}
4060
4061/**
4062 * setup for re-calling dx_init(), this is not the final exit cleanup routine
4063 * (see dx_cleanup)
4064 */
4065void DXGraphicsStateGuardian9::
4066free_d3d_device() {
4067 // dont want a full reset of gsg, just a state clear
4068 _state_rs = RenderState::make_empty();
4069 _state_mask.clear();
4070
4071 // want gsg to pass all state settings through
4072
4073 _dx_is_ready = false;
4074
4075 if (_d3d_device != nullptr) {
4076 for(int i = 0; i < D3D_MAXTEXTURESTAGES; i++) {
4077 // d3d should release this stuff internally anyway, but whatever
4078 _d3d_device->SetTexture(i, nullptr);
4079 }
4080 }
4081
4082 release_all();
4083
4084 if (_d3d_device != nullptr) {
4085 RELEASE(_d3d_device, dxgsg9, "d3dDevice", RELEASE_DOWN_TO_ZERO);
4086 }
4087
4088 free_nondx_resources();
4089
4090 // obviously we dont release ID3D9, just ID3DDevice9
4091}
4092
4093/**
4094 * Sets up the glDrawBuffer to render into the buffer indicated by the
4095 * RenderBuffer object. This only sets up the color bits; it does not affect
4096 * the depth, stencil, accum layers.
4097 */
4098void DXGraphicsStateGuardian9::
4099set_draw_buffer(const RenderBuffer &rb) {
4100 dxgsg9_cat.fatal() << "DX set_draw_buffer unimplemented!!!";
4101 return;
4102}
4103
4104/**
4105 * Vestigial analog of glReadBuffer
4106 */
4107void DXGraphicsStateGuardian9::
4108set_read_buffer(const RenderBuffer &rb) {
4109 if (rb._buffer_type & RenderBuffer::T_front) {
4110 _cur_read_pixel_buffer = RenderBuffer::T_front;
4111 } else if (rb._buffer_type & RenderBuffer::T_back) {
4112 _cur_read_pixel_buffer = RenderBuffer::T_back;
4113 } else if (rb._buffer_type & RenderBuffer::T_aux_rgba_ALL) {
4114 _cur_read_pixel_buffer = RenderBuffer::T_back;
4115 } else {
4116 dxgsg9_cat.error() << "Invalid or unimplemented Argument to set_read_buffer!\n";
4117 }
4118 return;
4119}
4120
4121/**
4122 * Returns the array of four floats that should be issued as the light's
4123 * color, as scaled by the current value of _light_color_scale, in the case of
4124 * color_scale_via_lighting.
4125 */
4126const D3DCOLORVALUE &DXGraphicsStateGuardian9::
4127get_light_color(Light *light) const {
4128 LColor c = light->get_color();
4129 static LColorf cf;
4130 cf.set(c[0] * _light_color_scale[0],
4131 c[1] * _light_color_scale[1],
4132 c[2] * _light_color_scale[2],
4133 c[3] * _light_color_scale[3]);
4134 return *(D3DCOLORVALUE *)cf.get_data();
4135}
4136
4137/**
4138 * Maps from ColorBlendAttrib::Mode to D3DBLENDOP vaule.
4139 */
4140D3DBLENDOP DXGraphicsStateGuardian9::
4141get_blend_mode(ColorBlendAttrib::Mode mode) {
4142 switch (mode) {
4143 case ColorBlendAttrib::M_add:
4144 return D3DBLENDOP_ADD;
4145
4146 case ColorBlendAttrib::M_subtract:
4147 return D3DBLENDOP_SUBTRACT;
4148
4149 case ColorBlendAttrib::M_inv_subtract:
4150 return D3DBLENDOP_REVSUBTRACT;
4151
4152 case ColorBlendAttrib::M_min:
4153 return D3DBLENDOP_MIN;
4154
4155 case ColorBlendAttrib::M_max:
4156 return D3DBLENDOP_MAX;
4157 }
4158
4159 dxgsg9_cat.error()
4160 << "Unknown color blend mode " << (int)mode << endl;
4161 return D3DBLENDOP_ADD;
4162}
4163
4164/**
4165 * Maps from ColorBlendAttrib::Operand to D3DBLEND value.
4166 */
4167D3DBLEND DXGraphicsStateGuardian9::
4168get_blend_func(ColorBlendAttrib::Operand operand) {
4169 switch (operand) {
4170 case ColorBlendAttrib::O_zero:
4171 return D3DBLEND_ZERO;
4172
4173 case ColorBlendAttrib::O_one:
4174 return D3DBLEND_ONE;
4175
4176 case ColorBlendAttrib::O_incoming_color:
4177 return D3DBLEND_SRCCOLOR;
4178
4179 case ColorBlendAttrib::O_one_minus_incoming_color:
4180 return D3DBLEND_INVSRCCOLOR;
4181
4182 case ColorBlendAttrib::O_fbuffer_color:
4183 return D3DBLEND_DESTCOLOR;
4184
4185 case ColorBlendAttrib::O_one_minus_fbuffer_color:
4186 return D3DBLEND_INVDESTCOLOR;
4187
4188 case ColorBlendAttrib::O_incoming_alpha:
4189 return D3DBLEND_SRCALPHA;
4190
4191 case ColorBlendAttrib::O_one_minus_incoming_alpha:
4192 return D3DBLEND_INVSRCALPHA;
4193
4194 case ColorBlendAttrib::O_fbuffer_alpha:
4195 return D3DBLEND_DESTALPHA;
4196
4197 case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
4198 return D3DBLEND_INVDESTALPHA;
4199
4200 case ColorBlendAttrib::O_constant_color:
4201 // Not supported by DX.
4202 return D3DBLEND_SRCCOLOR;
4203
4204 case ColorBlendAttrib::O_one_minus_constant_color:
4205 // Not supported by DX.
4206 return D3DBLEND_INVSRCCOLOR;
4207
4208 case ColorBlendAttrib::O_constant_alpha:
4209 // Not supported by DX.
4210 return D3DBLEND_SRCALPHA;
4211
4212 case ColorBlendAttrib::O_one_minus_constant_alpha:
4213 // Not supported by DX.
4214 return D3DBLEND_INVSRCALPHA;
4215
4216 case ColorBlendAttrib::O_incoming_color_saturate:
4217 return D3DBLEND_SRCALPHASAT;
4218
4219 case ColorBlendAttrib::O_incoming1_color:
4220 return (D3DBLEND)16; //D3DBLEND_SRCCOLOR2;
4221
4222 case ColorBlendAttrib::O_one_minus_incoming1_color:
4223 return (D3DBLEND)17; //D3DBLEND_INVSRCCOLOR2;
4224
4225 case ColorBlendAttrib::O_incoming1_alpha:
4226 // Not supported by DX9.
4227 return (D3DBLEND)18;
4228
4229 case ColorBlendAttrib::O_one_minus_incoming1_alpha:
4230 // Not supported by DX9.
4231 return (D3DBLEND)19;
4232 }
4233
4234 dxgsg9_cat.error()
4235 << "Unknown color blend operand " << (int)operand << endl;
4236 return D3DBLEND_ZERO;
4237}
4238
4239/**
4240 * Reports the DX texture manager's activity to PStats.
4241 */
4242void DXGraphicsStateGuardian9::
4243report_texmgr_stats() {
4244
4245#ifdef DO_PSTATS
4246 HRESULT hr;
4247 hr = 0;
4248
4249#ifdef TEXMGRSTATS_USES_GETAVAILVIDMEM
4250 DWORD dwTexTotal, dwTexFree, dwVidTotal, dwVidFree;
4251
4252 if (_total_texmem_pcollector.is_active()) {
4253 DDSCAPS2 ddsCaps;
4254
4255 ZeroMemory(&ddsCaps, sizeof(ddsCaps));
4256
4257 ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_PRIMARYSURFACE | DDSCAPS_3DDEVICE;
4258 if (FAILED( hr = _d3d_device->GetAvailableVidMem(&ddsCaps, &dwVidTotal, &dwVidFree))) {
4259 dxgsg9_cat.fatal() << "report_texmgr GetAvailableVidMem for VIDMEM failed : result = " << D3DERRORSTRING(hr);
4260 throw_event("panda3d-render-error");
4261 return;
4262 }
4263
4264 ddsCaps.dwCaps = DDSCAPS_TEXTURE;
4265 if (FAILED( hr = _d3d_device->GetAvailableVidMem(&ddsCaps, &dwTexTotal, &dwTexFree))) {
4266 dxgsg9_cat.fatal() << "report_texmgr GetAvailableVidMem for TEXTURE failed : result = " << D3DERRORSTRING(hr);
4267 throw_event("panda3d-render-error");
4268 return;
4269 }
4270 }
4271#endif // TEXMGRSTATS_USES_GETAVAILVIDMEM
4272
4273 D3DDEVINFO_RESOURCEMANAGER all_resource_stats;
4274 ZeroMemory(&all_resource_stats, sizeof(D3DDEVINFO_RESOURCEMANAGER));
4275
4276/* ***** DX9, GetInfo ( ) NOT IN DX9 */
4277/*
4278 if (!_tex_stats_retrieval_impossible) {
4279 hr = _d3d_device->GetInfo(D3DDEVINFOID_RESOURCEMANAGER, &all_resource_stats, sizeof(D3DDEVINFO_RESOURCEMANAGER));
4280 if (hr != D3D_OK) {
4281 if (hr == S_FALSE) {
4282 static int PrintedMsg = 2;
4283 if (PrintedMsg>0) {
4284 if (dxgsg9_cat.is_debug()) {
4285 dxgsg9_cat.debug()
4286 << "texstats GetInfo() requires debug DX DLLs to be installed!!\n";
4287 }
4288 ZeroMemory(&all_resource_stats, sizeof(D3DDEVINFO_RESOURCEMANAGER));
4289 _tex_stats_retrieval_impossible = true;
4290 }
4291 } else {
4292 dxgsg9_cat.error() << "GetInfo(RESOURCEMANAGER) failed to get tex stats: result = " << D3DERRORSTRING(hr);
4293 return;
4294 }
4295 }
4296 }
4297*/
4298
4299 // Tell PStats about the state of the texture memory.
4300
4301 if (_texmgrmem_total_pcollector.is_active()) {
4302 // report zero if no debug dlls, to signal this info is invalid
4303 _texmgrmem_total_pcollector.set_level(all_resource_stats.stats[D3DRTYPE_TEXTURE].TotalBytes);
4304 _texmgrmem_resident_pcollector.set_level(all_resource_stats.stats[D3DRTYPE_TEXTURE].WorkingSetBytes);
4305 }
4306#ifdef TEXMGRSTATS_USES_GETAVAILVIDMEM
4307 if (_total_texmem_pcollector.is_active()) {
4308 _total_texmem_pcollector.set_level(dwTexTotal);
4309 _used_texmem_pcollector.set_level(dwTexTotal - dwTexFree);
4310 }
4311#endif // TEXMGRSTATS_USES_GETAVAILVIDMEM
4312#endif // DO_PSTATS
4313}
4314
4315/**
4316 *
4317 */
4318void DXGraphicsStateGuardian9::
4319set_context(DXScreenData *new_context) {
4320 nassertv(new_context != nullptr);
4321 _screen = new_context;
4322 _d3d_device = _screen->_d3d_device; //copy this one field for speed of deref
4323 _swap_chain = _screen->_swap_chain; //copy this one field for speed of deref
4324
4325 _screen->_dxgsg9 = this;
4326 set_cg_device(_d3d_device);
4327}
4328
4329/**
4330 * Set render target to the backbuffer of current swap chain.
4331 */
4332void DXGraphicsStateGuardian9::
4333set_render_target() {
4334 if (_d3d_device == nullptr) {
4335 return;
4336 }
4337
4338 LPDIRECT3DSURFACE9 back = nullptr, stencil = nullptr;
4339
4340 UINT swap_chain;
4341
4342 /* ***** DX9 swap_chain ??? */
4343 swap_chain = 0;
4344
4345 if (!_swap_chain) //maybe fullscreen mode or main/single window
4346 _d3d_device->GetBackBuffer(swap_chain, 0, D3DBACKBUFFER_TYPE_MONO, &back);
4347 else
4348 _swap_chain->GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO, &back);
4349
4350 // wdxdisplay9_cat.debug() << "swapchain is " << _swap_chain << "\n";
4351 // wdxdisplay9_cat.debug() << "back buffer is " << back << "\n";
4352
4353 _d3d_device->GetDepthStencilSurface(&stencil);
4354
4355// _d3d_device->SetRenderTarget(back, stencil);
4356 DWORD render_target_index;
4357 render_target_index = 0;
4358 _d3d_device->SetRenderTarget(render_target_index, back);
4359
4360 if (back) {
4361 back->Release();
4362 }
4363 if (stencil) {
4364 stencil->Release();
4365 }
4366}
4367
4368/**
4369 *
4370 */
4371void DXGraphicsStateGuardian9::
4372set_texture_blend_mode(int i, const TextureStage *stage) {
4373 switch (stage->get_mode()) {
4374 case TextureStage::M_modulate:
4375 case TextureStage::M_modulate_glow:
4376 case TextureStage::M_modulate_gloss:
4377 // emulates GL_MODULATE glTexEnv mode
4378 set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_MODULATE);
4379 set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4380 set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4381 set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
4382 set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4383 set_texture_stage_state(i, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
4384 break;
4385
4386 case TextureStage::M_decal:
4387 // emulates GL_DECAL glTexEnv mode
4388 set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA);
4389 set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4390 set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4391
4392 set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
4393 set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_CURRENT);
4394 break;
4395
4396 case TextureStage::M_replace:
4397 set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
4398 set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4399
4400 set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1);
4401 set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4402 break;
4403
4404 case TextureStage::M_add:
4405 set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_ADD);
4406 set_texture_stage_state(i, D3DTSS_COLORARG1, D3DTA_TEXTURE);
4407 set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4408
4409 set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
4410 set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4411 set_texture_stage_state(i, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
4412 break;
4413
4414 case TextureStage::M_blend:
4415 case TextureStage::M_blend_color_scale:
4416 {
4417 set_texture_stage_state(i, D3DTSS_COLOROP, D3DTOP_LERP);
4418 set_texture_stage_state(i, D3DTSS_COLORARG0, D3DTA_TEXTURE);
4419 set_texture_stage_state(i, D3DTSS_COLORARG2, D3DTA_CURRENT);
4420 set_texture_stage_state(i, D3DTSS_COLORARG1, _constant_color_operand);
4421
4422 set_texture_stage_state(i, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
4423 set_texture_stage_state(i, D3DTSS_ALPHAARG1, D3DTA_TEXTURE);
4424 set_texture_stage_state(i, D3DTSS_ALPHAARG2, D3DTA_CURRENT);
4425 }
4426 break;
4427
4428 case TextureStage::M_combine:
4429 // M_combine mode begins a collection of more sophisticated modes, which
4430 // match up more closely with DirectX's built-in modes.
4432 (i, D3DTSS_COLOROP,
4433 get_texture_operation(stage->get_combine_rgb_mode(),
4434 stage->get_rgb_scale()));
4435
4436 switch (stage->get_num_combine_rgb_operands()) {
4437 case 3:
4439 (i, D3DTSS_COLORARG0,
4440 get_texture_argument(stage->get_combine_rgb_source2(),
4441 stage->get_combine_rgb_operand2()));
4442 // fall through
4443
4444 case 2:
4446 (i, D3DTSS_COLORARG2,
4447 get_texture_argument(stage->get_combine_rgb_source1(),
4448 stage->get_combine_rgb_operand1()));
4449 // fall through
4450
4451 case 1:
4453 (i, D3DTSS_COLORARG1,
4454 get_texture_argument(stage->get_combine_rgb_source0(),
4455 stage->get_combine_rgb_operand0()));
4456 // fall through
4457
4458 default:
4459 break;
4460 }
4461
4463 (i, D3DTSS_ALPHAOP,
4464 get_texture_operation(stage->get_combine_alpha_mode(),
4465 stage->get_alpha_scale()));
4466
4467 switch (stage->get_num_combine_alpha_operands()) {
4468 case 3:
4470 (i, D3DTSS_ALPHAARG0,
4471 get_texture_argument(stage->get_combine_alpha_source2(),
4472 stage->get_combine_alpha_operand2()));
4473 // fall through
4474
4475 case 2:
4477 (i, D3DTSS_ALPHAARG2,
4478 get_texture_argument(stage->get_combine_alpha_source1(),
4479 stage->get_combine_alpha_operand1()));
4480 // fall through
4481
4482 case 1:
4484 (i, D3DTSS_ALPHAARG1,
4485 get_texture_argument(stage->get_combine_alpha_source0(),
4486 stage->get_combine_alpha_operand0()));
4487 // fall through
4488
4489 default:
4490 break;
4491 }
4492 break;
4493
4494 default:
4495 dxgsg9_cat.error()
4496 << "Unknown texture mode " << (int)stage->get_mode() << endl;
4497 break;
4498 }
4499
4500 if (stage->get_saved_result()) {
4501 set_texture_stage_state(i, D3DTSS_RESULTARG, D3DTA_TEMP);
4502 } else {
4503 set_texture_stage_state(i, D3DTSS_RESULTARG, D3DTA_CURRENT);
4504 }
4505
4506 if (stage->uses_color() || stage->involves_color_scale()) {
4507 // Set up the constant color for this stage.
4508
4509 D3DCOLOR constant_color;
4510 if (stage->involves_color_scale() && _color_scale_enabled) {
4511 LColor color = stage->get_color();
4512 color.set(color[0] * _current_color_scale[0],
4513 color[1] * _current_color_scale[1],
4514 color[2] * _current_color_scale[2],
4515 color[3] * _current_color_scale[3]);
4516 _texture_involves_color_scale = true;
4517 constant_color = LColor_to_D3DCOLOR(color);
4518 } else {
4519 constant_color = LColor_to_D3DCOLOR(stage->get_color());
4520 }
4521 if (_supports_texture_constant_color) {
4522 set_texture_stage_state(i, D3DTSS_CONSTANT, constant_color);
4523 } else {
4524 // This device doesn't supoprt a per-stage constant color, so we have to
4525 // fall back to a single constant color for the overall texture
4526 // pipeline.
4527 set_render_state(D3DRS_TEXTUREFACTOR, constant_color);
4528 }
4529 }
4530}
4531
4532/**
4533 * Clean up the DirectX environment, accounting for exit()
4534 */
4535void DXGraphicsStateGuardian9::
4536dx_cleanup() {
4537 if (!_d3d_device) {
4538 return;
4539 }
4540
4541 free_nondx_resources();
4542 PRINT_REFCNT(dxgsg9, _d3d_device);
4543
4544 // Do a safe check for releasing the D3DDEVICE. RefCount should be zero. if
4545 // we're called from exit(), _d3d_device may already have been released
4546 RELEASE(_d3d_device, dxgsg9, "d3dDevice", RELEASE_DOWN_TO_ZERO);
4547 _screen->_d3d_device = nullptr;
4548
4549 // Releasing pD3D is now the responsibility of the GraphicsPipe destructor
4550}
4551
4552/**
4553 * This function checks current device's framebuffer dimension against passed
4554 * p_presentation_params backbuffer dimension to determine a device reset if
4555 * there is only one window or it is the main window or fullscreen mode then,
4556 * it resets the device. Finally it returns the new DXScreenData through
4557 * parameter screen
4558 */
4559HRESULT DXGraphicsStateGuardian9::
4560reset_d3d_device(D3DPRESENT_PARAMETERS *presentation_params,
4561 DXScreenData **screen) {
4562 HRESULT hr;
4563
4564 nassertr(IS_VALID_PTR(presentation_params), E_FAIL);
4565 nassertr(IS_VALID_PTR(_screen->_d3d9), E_FAIL);
4566 nassertr(IS_VALID_PTR(_d3d_device), E_FAIL);
4567
4568 // for windowed mode make sure our format matches the desktop fmt, in case
4569 // the desktop mode has been changed
4570 _screen->_d3d9->GetAdapterDisplayMode(_screen->_card_id, &_screen->_display_mode);
4571 presentation_params->BackBufferFormat = _screen->_display_mode.Format;
4572
4573 // here we have to look at the _presentation_reset frame buffer dimension if
4574 // current window's dimension is bigger than _presentation_reset we have to
4575 // reset the device before creating new swapchain. inorder to reset
4576 // properly, we need to release all swapchains
4577
4578 if (true || !(_screen->_swap_chain)
4579 || (_presentation_reset.BackBufferWidth < presentation_params->BackBufferWidth)
4580 || (_presentation_reset.BackBufferHeight < presentation_params->BackBufferHeight)) {
4581 if (wdxdisplay9_cat.is_debug()) {
4582 wdxdisplay9_cat.debug()
4583 << "swap_chain = " << _screen->_swap_chain << " _presentation_reset = "
4584 << _presentation_reset.BackBufferWidth << "x" << _presentation_reset.BackBufferHeight
4585 << " presentation_params = "
4586 << presentation_params->BackBufferWidth << "x" << presentation_params->BackBufferHeight << "\n";
4587 }
4588
4589 get_engine()->reset_all_windows(false);// reset old swapchain by releasing
4590
4591 if (_screen->_swap_chain) { //other windows might be using bigger buffers
4592 _presentation_reset.BackBufferWidth = max(_presentation_reset.BackBufferWidth, presentation_params->BackBufferWidth);
4593 _presentation_reset.BackBufferHeight = max(_presentation_reset.BackBufferHeight, presentation_params->BackBufferHeight);
4594
4595 } else { // single window, must reset to the new presentation_params dimension
4596 _presentation_reset.BackBufferWidth = presentation_params->BackBufferWidth;
4597 _presentation_reset.BackBufferHeight = presentation_params->BackBufferHeight;
4598 }
4599
4600 // Calling this forces all of the textures and vbuffers to be regenerated,
4601 // a prerequisite to calling Reset().
4602 release_all();
4603
4604 // Just to be extra-conservative for now, we'll go ahead and release the
4605 // vbuffers and ibuffers at least; they're relatively cheap to replace.
4608
4609 if (_white_vbuffer != nullptr) {
4610 _white_vbuffer->Release();
4611 _white_vbuffer = nullptr;
4612 }
4613
4614 // must be called before reset
4615 Thread *current_thread = Thread::get_current_thread();
4616 _prepared_objects->begin_frame(this, current_thread);
4617
4618 // release graphics buffer surfaces
4619 {
4620 wdxGraphicsBuffer9 *graphics_buffer;
4621 std::list <wdxGraphicsBuffer9 **>::iterator graphics_buffer_iterator;
4622
4623 for (graphics_buffer_iterator = _graphics_buffer_list.begin( ); graphics_buffer_iterator != _graphics_buffer_list.end( ); graphics_buffer_iterator++)
4624 {
4625 graphics_buffer = **graphics_buffer_iterator;
4626 if (graphics_buffer -> _color_backing_store)
4627 {
4628 graphics_buffer -> _color_backing_store -> Release ( );
4629 graphics_buffer -> _color_backing_store = 0;
4630 }
4631 if (graphics_buffer -> _depth_backing_store)
4632 {
4633 graphics_buffer -> _depth_backing_store -> Release ( );
4634 graphics_buffer -> _depth_backing_store = 0;
4635 }
4636 }
4637 }
4638
4639 mark_new();
4640 hr = _d3d_device->Reset(&_presentation_reset);
4641 if (FAILED(hr) && hr != D3DERR_DEVICELOST) {
4642 return hr;
4643 }
4644
4645 get_engine()->reset_all_windows(true);// reset with new swapchains by creating
4646 if (screen) {
4647 *screen = nullptr;
4648 }
4649
4650 if (presentation_params != &_screen->_presentation_params) {
4651 memcpy(&_screen->_presentation_params, presentation_params, sizeof(D3DPRESENT_PARAMETERS));
4652 }
4653
4654 return hr;
4655 }
4656
4657 // release the old swapchain and create a new one
4658 if (_screen && _screen->_swap_chain) {
4659 _screen->_swap_chain->Release();
4660 wdxdisplay9_cat.debug()
4661 << "swap chain " << _screen->_swap_chain << " is released\n";
4662 _screen->_swap_chain = nullptr;
4663 hr = _d3d_device->CreateAdditionalSwapChain(presentation_params, &_screen->_swap_chain);
4664 }
4665 if (SUCCEEDED(hr)) {
4666 if (presentation_params != &_screen->_presentation_params) {
4667 memcpy(&_screen->_presentation_params, presentation_params, sizeof(D3DPRESENT_PARAMETERS));
4668 }
4669 if (screen) {
4670 *screen = _screen;
4671 }
4672 }
4673 return hr;
4674}
4675
4676/**
4677 *
4678 */
4679bool DXGraphicsStateGuardian9::
4680check_cooperative_level() {
4681 bool bDoReactivateWindow = false;
4682 if (_d3d_device == nullptr) {
4683 return false;
4684 }
4685
4686 HRESULT hr = _d3d_device->TestCooperativeLevel();
4687
4688 if (SUCCEEDED(hr)) {
4689 nassertr(SUCCEEDED(_last_testcooplevel_result), false);
4690 return true;
4691 }
4692
4693 switch (hr) {
4694 case D3DERR_DEVICENOTRESET:
4695 _dx_is_ready = false;
4696
4697 // call this just in case
4698 _prepared_objects->begin_frame(this, Thread::get_current_thread());
4699
4700 hr = reset_d3d_device(&_screen->_presentation_params);
4701 if (FAILED(hr)) {
4702 // I think this shouldnt fail unless I've screwed up the
4703 // _presentation_params from the original working ones somehow
4704 dxgsg9_cat.error()
4705 << "check_cooperative_level Reset() failed, hr = " << D3DERRORSTRING(hr);
4706 }
4707
4708 hr = _d3d_device->TestCooperativeLevel();
4709 if (FAILED(hr)) {
4710 // internal chk, shouldnt fail
4711 dxgsg9_cat.error()
4712 << "TestCooperativeLevel following Reset() failed, hr = " << D3DERRORSTRING(hr);
4713
4714 }
4715
4716 _dx_is_ready = TRUE;
4717 break;
4718
4719 case D3DERR_DEVICELOST:
4720 // sleep while the device is lost to free up the CPU
4721 Sleep (10);
4722
4723 if (SUCCEEDED(_last_testcooplevel_result)) {
4724 if (_dx_is_ready) {
4725 _dx_is_ready = false;
4726 if (dxgsg9_cat.is_debug()) {
4727 dxgsg9_cat.debug() << "D3D Device was Lost, waiting...\n";
4728 }
4729 }
4730 }
4731 }
4732
4733 _last_testcooplevel_result = hr;
4734 return SUCCEEDED(hr);
4735}
4736
4737/**
4738 * redraw primary buffer
4739 */
4740void DXGraphicsStateGuardian9::
4741show_frame() {
4742 if (_d3d_device == nullptr) {
4743 return;
4744 }
4745
4746 HRESULT hr;
4747
4748 if (_swap_chain) {
4749 DWORD flags;
4750 flags = 0;
4751
4752 hr = _swap_chain->Present(nullptr, nullptr, (HWND)nullptr, nullptr, flags);
4753 } else {
4754 hr = _d3d_device->Present(nullptr, nullptr, (HWND)nullptr, nullptr);
4755 }
4756
4757 if (FAILED(hr)) {
4758 if (hr == D3DERR_DEVICELOST) {
4759 check_cooperative_level();
4760 } else {
4761 dxgsg9_cat.error()
4762 << "show_frame() - Present() failed" << D3DERRORSTRING(hr);
4763 throw_event("panda3d-render-error");
4764 }
4765 }
4766}
4767
4768/**
4769 *
4770 */
4771bool DXGraphicsStateGuardian9::
4772create_swap_chain(DXScreenData *new_context) {
4773 // Instead of creating a device and rendering as d3ddevice->present() we
4774 // should render using SwapChain->present(). This is done to support
4775 // multiple windows rendering. For that purpose, we need to set additional
4776 // swap chains here.
4777
4778 HRESULT hr;
4779 hr = new_context->_d3d_device->CreateAdditionalSwapChain(&new_context->_presentation_params, &new_context->_swap_chain);
4780 if (FAILED(hr)) {
4781 wdxdisplay9_cat.debug() << "Swapchain creation failed :"<<D3DERRORSTRING(hr)<<"\n";
4782 return false;
4783 }
4784 return true;
4785}
4786
4787/**
4788 * Release the swap chain on this DXScreenData
4789 */
4790bool DXGraphicsStateGuardian9::
4791release_swap_chain(DXScreenData *new_context) {
4792 HRESULT hr;
4793 if (new_context->_swap_chain) {
4794 hr = new_context->_swap_chain->Release();
4795 if (FAILED(hr)) {
4796 wdxdisplay9_cat.debug() << "Swapchain release failed:" << D3DERRORSTRING(hr) << "\n";
4797 return false;
4798 }
4799 if (new_context->_swap_chain == _swap_chain) {
4800 _swap_chain = nullptr;
4801 }
4802 }
4803 return true;
4804}
4805
4806/**
4807 * copies the PresReset from passed DXScreenData
4808 */
4809void DXGraphicsStateGuardian9::
4810copy_pres_reset(DXScreenData *screen) {
4811 memcpy(&_presentation_reset, &_screen->_presentation_params, sizeof(D3DPRESENT_PARAMETERS));
4812}
4813
4814/**
4815 *
4816 */
4817D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian9::
4818get_d3d_min_type(SamplerState::FilterType filter_type) {
4819 switch (filter_type) {
4820 case SamplerState::FT_nearest:
4821 return D3DTEXF_POINT;
4822
4823 case SamplerState::FT_linear:
4824 return D3DTEXF_LINEAR;
4825
4826 case SamplerState::FT_nearest_mipmap_nearest:
4827 return D3DTEXF_POINT;
4828
4829 case SamplerState::FT_linear_mipmap_nearest:
4830 return D3DTEXF_LINEAR;
4831
4832 case SamplerState::FT_nearest_mipmap_linear:
4833 return D3DTEXF_POINT;
4834
4835 case SamplerState::FT_linear_mipmap_linear:
4836 return D3DTEXF_LINEAR;
4837
4838 case SamplerState::FT_shadow:
4839 case SamplerState::FT_default:
4840 return D3DTEXF_LINEAR;
4841 }
4842
4843 dxgsg9_cat.error()
4844 << "Invalid FilterType value (" << (int)filter_type << ")\n";
4845 return D3DTEXF_POINT;
4846}
4847
4848/**
4849 *
4850 */
4851D3DTEXTUREFILTERTYPE DXGraphicsStateGuardian9::
4852get_d3d_mip_type(SamplerState::FilterType filter_type) {
4853 switch (filter_type) {
4854 case SamplerState::FT_nearest:
4855 return D3DTEXF_NONE;
4856
4857 case SamplerState::FT_linear:
4858 return D3DTEXF_NONE;
4859
4860 case SamplerState::FT_nearest_mipmap_nearest:
4861 return D3DTEXF_POINT;
4862
4863 case SamplerState::FT_linear_mipmap_nearest:
4864 return D3DTEXF_POINT;
4865
4866 case SamplerState::FT_nearest_mipmap_linear:
4867 return D3DTEXF_LINEAR;
4868
4869 case SamplerState::FT_linear_mipmap_linear:
4870 return D3DTEXF_LINEAR;
4871
4872 case SamplerState::FT_shadow:
4873 case SamplerState::FT_default:
4874 return D3DTEXF_NONE;
4875 }
4876
4877 dxgsg9_cat.error()
4878 << "Invalid FilterType value (" << (int)filter_type << ")\n";
4879 return D3DTEXF_NONE;
4880}
4881
4882/**
4883 * Returns the D3DTEXTUREOP value corresponding to the indicated
4884 * TextureStage::CombineMode enumerated type.
4885 */
4886D3DTEXTUREOP DXGraphicsStateGuardian9::
4887get_texture_operation(TextureStage::CombineMode mode, int scale) {
4888 switch (mode) {
4889 case TextureStage::CM_undefined:
4890 case TextureStage::CM_replace:
4891 return D3DTOP_SELECTARG1;
4892
4893 case TextureStage::CM_modulate:
4894 if (scale < 2) {
4895 return D3DTOP_MODULATE;
4896 } else if (scale < 4) {
4897 return D3DTOP_MODULATE2X;
4898 } else {
4899 return D3DTOP_MODULATE4X;
4900 }
4901
4902 case TextureStage::CM_add:
4903 return D3DTOP_ADD;
4904
4905 case TextureStage::CM_add_signed:
4906 if (scale < 2) {
4907 return D3DTOP_ADDSIGNED;
4908 } else {
4909 return D3DTOP_ADDSIGNED2X;
4910 }
4911
4912 case TextureStage::CM_interpolate:
4913 return D3DTOP_LERP;
4914
4915 case TextureStage::CM_subtract:
4916 return D3DTOP_SUBTRACT;
4917
4918 case TextureStage::CM_dot3_rgb:
4919 case TextureStage::CM_dot3_rgba:
4920 return D3DTOP_DOTPRODUCT3;
4921 }
4922
4923 dxgsg9_cat.error()
4924 << "Invalid TextureStage::CombineMode value (" << (int)mode << ")\n";
4925 return D3DTOP_DISABLE;
4926}
4927
4928/**
4929 * Returns the D3DTA value corresponding to the indicated
4930 * TextureStage::CombineSource and TextureStage::CombineOperand enumerated
4931 * types.
4932 */
4933DWORD DXGraphicsStateGuardian9::
4934get_texture_argument(TextureStage::CombineSource source,
4935 TextureStage::CombineOperand operand) const {
4936 switch (source) {
4937 case TextureStage::CS_undefined:
4938 case TextureStage::CS_texture:
4939 return D3DTA_TEXTURE | get_texture_argument_modifier(operand);
4940
4941 case TextureStage::CS_constant:
4942 case TextureStage::CS_constant_color_scale:
4943 return _constant_color_operand | get_texture_argument_modifier(operand);
4944
4945 case TextureStage::CS_primary_color:
4946 return D3DTA_DIFFUSE | get_texture_argument_modifier(operand);
4947
4948 case TextureStage::CS_previous:
4949 return D3DTA_CURRENT | get_texture_argument_modifier(operand);
4950
4951 case TextureStage::CS_last_saved_result:
4952 return D3DTA_TEMP | get_texture_argument_modifier(operand);
4953 }
4954 dxgsg9_cat.error()
4955 << "Invalid TextureStage::CombineSource value (" << (int)source << ")\n";
4956 return D3DTA_CURRENT;
4957}
4958
4959/**
4960 * Returns the extra bits that modify the D3DTA argument, according to the
4961 * indicated TextureStage::CombineOperand enumerated type.
4962 */
4963DWORD DXGraphicsStateGuardian9::
4964get_texture_argument_modifier(TextureStage::CombineOperand operand) {
4965 switch (operand) {
4966 case TextureStage::CO_src_color:
4967 return 0;
4968
4969 case TextureStage::CO_one_minus_src_color:
4970 return D3DTA_COMPLEMENT;
4971
4972 case TextureStage::CO_src_alpha:
4973 return D3DTA_ALPHAREPLICATE;
4974
4975 case TextureStage::CO_one_minus_src_alpha:
4976 return D3DTA_ALPHAREPLICATE | D3DTA_COMPLEMENT;
4977
4978 case TextureStage::CO_undefined:
4979 break;
4980 }
4981 dxgsg9_cat.error()
4982 << "Invalid TextureStage::CombineOperand value (" << (int)operand << ")\n";
4983 return 0;
4984}
4985
4986/**
4987 * Issues the DrawPrimitiveUP call to draw the indicated primitive_type from
4988 * the given buffer. We add the num_vertices parameter, so we can determine
4989 * the size of the buffer.
4990 */
4991void DXGraphicsStateGuardian9::
4992draw_primitive_up(D3DPRIMITIVETYPE primitive_type,
4993 unsigned int primitive_count,
4994 unsigned int first_vertex,
4995 unsigned int num_vertices,
4996 const unsigned char *buffer, size_t stride) {
4997
4998 // It appears that the common ATI driver seems to fail to draw anything in
4999 // the DrawPrimitiveUP() call if the address range of the buffer supplied
5000 // crosses over a multiple of 0x10000. That's incredibly broken, yet it
5001 // undeniably appears to be true. We'll have to hack around it.
5002
5003 const unsigned char *buffer_start = buffer + stride * first_vertex;
5004 const unsigned char *buffer_end = buffer_start + stride * num_vertices;
5005
5006 if (buffer_end - buffer_start > 0x10000) {
5007 // Actually, the buffer doesn't fit within the required limit anyway. Go
5008 // ahead and draw it and hope for the best.
5009 _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count,
5010 buffer_start, stride);
5011
5012 } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) {
5013 // No problem; we can draw the buffer directly.
5014 _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count,
5015 buffer_start, stride);
5016
5017 } else {
5018 // We have a problem--the buffer crosses over a 0x10000 boundary. We have
5019 // to copy the buffer to a temporary buffer that we can draw from.
5020 unsigned char *safe_buffer_start = get_safe_buffer_start();
5021 memcpy(safe_buffer_start, buffer_start, buffer_end - buffer_start);
5022 _d3d_device->DrawPrimitiveUP(primitive_type, primitive_count,
5023 safe_buffer_start, stride);
5024
5025 }
5026}
5027
5028/**
5029 * Issues the DrawIndexedPrimitiveUP call to draw the indicated primitive_type
5030 * from the given buffer. As in draw_primitive_up(), above, the parameter
5031 * list is not exactly one-for-one with the DrawIndexedPrimitiveUP() call, but
5032 * it's similar (in particular, we pass max_index instead of NumVertices,
5033 * which always seemed ambiguous to me).
5034 */
5035void DXGraphicsStateGuardian9::
5036draw_indexed_primitive_up(D3DPRIMITIVETYPE primitive_type,
5037 unsigned int min_index, unsigned int max_index,
5038 unsigned int num_primitives,
5039 const unsigned char *index_data,
5040 D3DFORMAT index_type,
5041 const unsigned char *buffer, size_t stride) {
5042 // As above, we'll hack the case of the buffer crossing the 0x10000
5043 // boundary.
5044 const unsigned char *buffer_start = buffer + stride * min_index;
5045 const unsigned char *buffer_end = buffer + stride * (max_index + 1);
5046
5047 if (buffer_end - buffer_start > 0x10000) {
5048 // Actually, the buffer doesn't fit within the required limit anyway. Go
5049 // ahead and draw it and hope for the best.
5050 _d3d_device->DrawIndexedPrimitiveUP
5051 (primitive_type, min_index, max_index - min_index + 1, num_primitives,
5052 index_data, index_type, buffer, stride);
5053
5054 } else if ((((uintptr_t)buffer_end ^ (uintptr_t)buffer_start) & ~0xffff) == 0) {
5055 // No problem; we can draw the buffer directly.
5056 _d3d_device->DrawIndexedPrimitiveUP
5057 (primitive_type, min_index, max_index - min_index + 1, num_primitives,
5058 index_data, index_type, buffer, stride);
5059
5060 } else {
5061 // We have a problem--the buffer crosses over a 0x10000 boundary. We have
5062 // to copy the buffer to a temporary buffer that we can draw from.
5063 unsigned char *safe_buffer_start = get_safe_buffer_start();
5064 memcpy(safe_buffer_start, buffer_start, buffer_end - buffer_start);
5065 _d3d_device->DrawIndexedPrimitiveUP
5066 (primitive_type, min_index, max_index - min_index + 1, num_primitives,
5067 index_data, index_type, safe_buffer_start - stride * min_index, stride);
5068 }
5069}
5070
5071/**
5072 * This function is called after the creation of textures, vertex buffers, and
5073 * index buffers to check if DirectX is out of memory. If DirectX is out of
5074 * memory and the LRU is being used, then page out some memory. This function
5075 * is a fail-safe just in case another process allocates video memory, DirectX
5076 * is fragmented, or there are some borderline memory allocation cases, ...
5077 */
5079check_dx_allocation (HRESULT result, int allocation_size, int attempts)
5080{
5081 bool retry;
5082
5083 retry = false;
5084 if (attempts <= 4)
5085 {
5086 switch (result) {
5087 case D3D_OK:
5088 break;
5089
5090 case D3DERR_OUTOFVIDEOMEMORY:
5091 case E_OUTOFMEMORY:
5092 // increase the page out size as the number of attempts increases
5093 {
5094 size_t current_size = _prepared_objects->_graphics_memory_lru.get_total_size();
5095 size_t target_size = max(current_size - allocation_size * attempts, (size_t) 0);
5096 _prepared_objects->_graphics_memory_lru.evict_to(target_size);
5097 dxgsg9_cat.info()
5098 << "Evicted " << current_size - _prepared_objects->_graphics_memory_lru.get_total_size() << " bytes of texture memory to make room for more.\n";
5099 if (_prepared_objects->_graphics_memory_lru.get_total_size() < current_size) {
5100 retry = true;
5101 }
5102 }
5103 break;
5104
5105 default:
5106 break;
5107 }
5108 }
5109
5110 return retry;
5111}
5112
5113// DX stencil code section
5114static int dx_stencil_comparison_function_array[] = {
5115 D3DCMP_NEVER,
5116 D3DCMP_LESS,
5117 D3DCMP_EQUAL,
5118 D3DCMP_LESSEQUAL,
5119 D3DCMP_GREATER,
5120 D3DCMP_NOTEQUAL,
5121 D3DCMP_GREATEREQUAL,
5122 D3DCMP_ALWAYS,
5123};
5124
5125static int dx_stencil_operation_array[] = {
5126 D3DSTENCILOP_KEEP,
5127 D3DSTENCILOP_ZERO,
5128 D3DSTENCILOP_REPLACE,
5129 D3DSTENCILOP_INCR,
5130 D3DSTENCILOP_DECR,
5131 D3DSTENCILOP_INVERT,
5132
5133 D3DSTENCILOP_INCRSAT,
5134 D3DSTENCILOP_DECRSAT,
5135};
5136
5137/**
5138 * Set stencil render states.
5139 */
5140void DXGraphicsStateGuardian9::
5141do_issue_stencil() {
5142 if (!_supports_stencil) {
5143 return;
5144 }
5145
5146 const StencilAttrib *stencil = DCAST(StencilAttrib, _target_rs->get_attrib(StencilAttrib::get_class_slot()));
5147
5148 if (stencil != nullptr) {
5149 // DEBUG
5150 if (false) {
5151 dxgsg9_cat.debug() << "STENCIL STATE CHANGE\n";
5152 dxgsg9_cat.debug() << "\n"
5153 << "SRS_front_comparison_function " << stencil->get_render_state(StencilAttrib::SRS_front_comparison_function) << "\n"
5154 << "SRS_front_stencil_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_front_stencil_fail_operation) << "\n"
5155 << "SRS_front_stencil_pass_z_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_fail_operation) << "\n"
5156 << "SRS_front_stencil_pass_z_pass_operation " << stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_pass_operation) << "\n"
5157 << "SRS_reference " << stencil->get_render_state(StencilAttrib::SRS_reference) << "\n"
5158 << "SRS_read_mask " << stencil->get_render_state(StencilAttrib::SRS_read_mask) << "\n"
5159 << "SRS_write_mask " << stencil->get_render_state(StencilAttrib::SRS_write_mask) << "\n"
5160 << "SRS_back_comparison_function " << stencil->get_render_state(StencilAttrib::SRS_back_comparison_function) << "\n"
5161 << "SRS_back_stencil_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_back_stencil_fail_operation) << "\n"
5162 << "SRS_back_stencil_pass_z_fail_operation " << stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_fail_operation) << "\n"
5163 << "SRS_back_stencil_pass_z_pass_operation " << stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation) << "\n";
5164 }
5165
5166 bool on = false;
5167
5168 unsigned int front_compare;
5169 front_compare = stencil->get_render_state(StencilAttrib::SRS_front_comparison_function);
5170
5171 if (front_compare != RenderAttrib::M_none) {
5172 set_render_state(D3DRS_STENCILENABLE, TRUE);
5173 set_render_state(D3DRS_STENCILFUNC, dx_stencil_comparison_function_array[front_compare]);
5174 set_render_state(D3DRS_STENCILFAIL, dx_stencil_operation_array[
5175 stencil->get_render_state(StencilAttrib::SRS_front_stencil_fail_operation)]);
5176 set_render_state(D3DRS_STENCILZFAIL, dx_stencil_operation_array[
5177 stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_fail_operation)]);
5178 set_render_state(D3DRS_STENCILPASS, dx_stencil_operation_array[
5179 stencil->get_render_state(StencilAttrib::SRS_front_stencil_pass_z_pass_operation)]);
5180 on = true;
5181 } else {
5182 set_render_state(D3DRS_STENCILENABLE, FALSE);
5183 }
5184
5185 if (_supports_two_sided_stencil) {
5186 unsigned int back_compare;
5187 back_compare = stencil->get_render_state(StencilAttrib::SRS_back_comparison_function);
5188
5189 if (back_compare != RenderAttrib::M_none) {
5190 set_render_state(D3DRS_TWOSIDEDSTENCILMODE, TRUE);
5191 set_render_state(D3DRS_CCW_STENCILFUNC, dx_stencil_comparison_function_array[back_compare]);
5192 set_render_state(D3DRS_CCW_STENCILFAIL, dx_stencil_operation_array[
5193 stencil->get_render_state(StencilAttrib::SRS_back_stencil_fail_operation)]);
5194 set_render_state(D3DRS_CCW_STENCILZFAIL, dx_stencil_operation_array[
5195 stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_fail_operation)]);
5196 set_render_state(D3DRS_CCW_STENCILPASS, dx_stencil_operation_array[
5197 stencil->get_render_state(StencilAttrib::SRS_back_stencil_pass_z_pass_operation)]);
5198 on = true;
5199 } else {
5200 set_render_state(D3DRS_TWOSIDEDSTENCILMODE, FALSE);
5201 }
5202 }
5203
5204 if (on) {
5205 set_render_state(D3DRS_STENCILREF, stencil->get_render_state(StencilAttrib::SRS_reference));
5206 set_render_state(D3DRS_STENCILMASK, stencil->get_render_state(StencilAttrib::SRS_read_mask));
5207 set_render_state(D3DRS_STENCILWRITEMASK, stencil->get_render_state(StencilAttrib::SRS_write_mask));
5208 }
5209
5210 if (stencil->get_render_state(StencilAttrib::SRS_clear)) {
5211 _d3d_device->Clear(0, nullptr, D3DCLEAR_STENCIL, 0, 0.0f, stencil->get_render_state(StencilAttrib::SRS_clear_value));
5212 }
5213
5214 } else {
5215 // DEBUG
5216 if (false) {
5217 dxgsg9_cat.debug() << "STENCIL STATE CHANGE TO OFF\n";
5218 }
5219
5220 set_render_state(D3DRS_STENCILENABLE, FALSE);
5221 if (_supports_two_sided_stencil) {
5222 set_render_state(D3DRS_TWOSIDEDSTENCILMODE, FALSE);
5223 }
5224 }
5225}
5226
5227/**
5228 *
5229 */
5230void DXGraphicsStateGuardian9::
5231do_issue_scissor() {
5232 const ScissorAttrib *target_scissor = DCAST(ScissorAttrib, _target_rs->get_attrib_def(ScissorAttrib::get_class_slot()));
5233 const LVecBase4 &frame = target_scissor->get_frame();
5234
5235 RECT r;
5236 r.left = _current_viewport.X + _current_viewport.Width * frame[0];
5237 r.top = _current_viewport.Y + _current_viewport.Height * (1.0f - frame[3]);
5238 r.right = _current_viewport.X + _current_viewport.Width * frame[1];
5239 r.bottom = _current_viewport.Y + _current_viewport.Height * (1.0f - frame[2]);
5240 _d3d_device->SetScissorRect(&r);
5241 set_render_state(D3DRS_SCISSORTESTENABLE, TRUE);
5242}
5243
5244/**
5245 * Convert DirectX framebuffer format ids into a FrameBufferProperties
5246 * structure.
5247 */
5249calc_fb_properties(DWORD cformat, DWORD dformat,
5250 DWORD multisampletype, DWORD multisamplequality) {
5252 int index=0;
5253 int r=0, g=0, b=0, a=0;
5254 switch (cformat) {
5255 case D3DFMT_R8G8B8: r=8; g=8; b=8; a=0; break;
5256 case D3DFMT_A8R8G8B8: r=8; g=8; b=8; a=8; break;
5257 case D3DFMT_X8R8G8B8: r=8; g=8; b=8; a=0; break;
5258 case D3DFMT_R5G6B5: r=5; g=6; b=5; a=0; break;
5259 case D3DFMT_X1R5G5B5: r=5; g=5; b=5; a=0; break;
5260 case D3DFMT_A1R5G5B5: r=5; g=5; b=5; a=1; break;
5261 case D3DFMT_A4R4G4B4: r=4; g=4; b=4; a=4; break;
5262 case D3DFMT_R3G3B2: r=3; g=3; b=2; a=0; break;
5263 case D3DFMT_A8R3G3B2: r=3; g=3; b=2; a=8; break;
5264 case D3DFMT_X4R4G4B4: r=4; g=4; b=4; a=0; break;
5265 case D3DFMT_A2B10G10R10: r=10;g=10;b=10;a=2; break;
5266 case D3DFMT_A8P8: index=8; a=8; break;
5267 case D3DFMT_P8: index=8; a=0; break;
5268 default: break;
5269 }
5270 if (index > 0) {
5271 props.set_rgb_color(0);
5272 props.set_indexed_color(1);
5273 props.set_color_bits(index);
5274 } else if (r + g + b > 0) {
5275 props.set_rgb_color(1);
5276 props.set_indexed_color(0);
5277 props.set_rgba_bits(r, g, b, a);
5278 }
5279 props.set_alpha_bits(a);
5280
5281 int depth=0;
5282 int stencil=0;
5283 switch (dformat) {
5284 case D3DFMT_D32: depth=32; stencil=0; break;
5285 case D3DFMT_D15S1: depth=15; stencil=1; break;
5286 case D3DFMT_D24S8: depth=24; stencil=8; break;
5287 case D3DFMT_D16: depth=16; stencil=0; break;
5288 case D3DFMT_D24X8: depth=24; stencil=0; break;
5289 case D3DFMT_D24X4S4: depth=24; stencil=4; break;
5290 default: break;
5291 }
5292 props.set_depth_bits(depth);
5293 props.set_stencil_bits(stencil);
5294 if (multisampletype == D3DMULTISAMPLE_NONMASKABLE) {
5295 props.set_multisamples(2);
5296 } else {
5297 props.set_multisamples(multisampletype);
5298 }
5299 return props;
5300}
5301
5302
5303#define GAMMA_1 (255.0 * 256.0)
5304
5305static bool _gamma_table_initialized = false;
5306static bool _gamma_changed = false;
5307static unsigned short _original_gamma_table [256 * 3];
5308
5309void _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) {
5310 int i;
5311 double gamma_correction;
5312
5313 if (gamma <= 0.0) {
5314 // avoid divide by zero and negative exponents
5315 gamma = 1.0;
5316 }
5317 gamma_correction = 1.0 / (double) gamma;
5318
5319 for (i = 0; i < 256; i++) {
5320 double r;
5321 double g;
5322 double b;
5323
5324 if (original_red_table) {
5325 r = (double) original_red_table [i] / GAMMA_1;
5326 g = (double) original_green_table [i] / GAMMA_1;
5327 b = (double) original_blue_table [i] / GAMMA_1;
5328 }
5329 else {
5330 r = ((double) i / 255.0);
5331 g = r;
5332 b = r;
5333 }
5334
5335 r = pow (r, gamma_correction);
5336 g = pow (g, gamma_correction);
5337 b = pow (b, gamma_correction);
5338
5339 if (r > 1.00) {
5340 r = 1.0;
5341 }
5342 if (g > 1.00) {
5343 g = 1.0;
5344 }
5345 if (b > 1.00) {
5346 b = 1.0;
5347 }
5348
5349 r = r * GAMMA_1;
5350 g = g * GAMMA_1;
5351 b = b * GAMMA_1;
5352
5353 red_table [i] = r;
5354 green_table [i] = g;
5355 blue_table [i] = b;
5356 }
5357}
5358
5359/**
5360 * Static function for getting the original gamma.
5361 */
5363get_gamma_table(void) {
5364 bool get;
5365
5366 get = false;
5367 if (_gamma_table_initialized == false) {
5368 HDC hdc = GetDC(nullptr);
5369
5370 if (hdc) {
5371 if (GetDeviceGammaRamp (hdc, (LPVOID) _original_gamma_table)) {
5372 _gamma_table_initialized = true;
5373 get = true;
5374 }
5375
5376 ReleaseDC (nullptr, hdc);
5377 }
5378 }
5379
5380 return get;
5381}
5382
5383/**
5384 * Static function for setting gamma which is needed for atexit.
5385 */
5387static_set_gamma(bool restore, PN_stdfloat gamma) {
5388 bool set;
5389 HDC hdc = GetDC(nullptr);
5390
5391 set = false;
5392 if (hdc) {
5393 unsigned short ramp [256 * 3];
5394
5395 if (restore && _gamma_table_initialized) {
5396 _create_gamma_table_dx9 (gamma, &_original_gamma_table [0], &_original_gamma_table [256], &_original_gamma_table [512], &ramp [0], &ramp [256], &ramp [512]);
5397 }
5398 else {
5399 _create_gamma_table_dx9 (gamma, 0, 0, 0, &ramp [0], &ramp [256], &ramp [512]);
5400 }
5401
5402 if (SetDeviceGammaRamp (hdc, ramp)) {
5403 set = true;
5404 _gamma_changed = !restore;
5405 }
5406
5407 ReleaseDC (nullptr, hdc);
5408 }
5409
5410 return set;
5411}
5412
5413/**
5414 * Non static version of setting gamma. Returns true on success.
5415 */
5417set_gamma(PN_stdfloat gamma) {
5418 bool set;
5419
5420 set = static_set_gamma(false, gamma);
5421 if (set) {
5422 _gamma = gamma;
5423 }
5424
5425 return set;
5426}
5427
5428/**
5429 * Restore original gamma.
5430 */
5432restore_gamma() {
5433 static_set_gamma(true, 1.0f);
5434}
5435
5436/**
5437 * This function is passed to the atexit function.
5438 */
5440atexit_function(void) {
5441 set_cg_device(nullptr);
5442 if (_gamma_changed) {
5443 static_set_gamma(true, 1.0f);
5444 }
5445}
5446
5447/**
5448 * Returns true if this particular GSG supports the specified Cg Shader
5449 * Profile.
5450 */
5452get_supports_cg_profile(const std::string &name) const {
5453#ifndef HAVE_CG
5454 return false;
5455#else
5456 CGprofile profile = cgGetProfile(name.c_str());
5457
5458 if (profile == CG_PROFILE_UNKNOWN) {
5459 dxgsg9_cat.error() << name <<", unknown Cg-profile\n";
5460 return false;
5461 }
5462 return cgD3D9IsProfileSupported(cgGetProfile(name.c_str())) != 0;
5463#endif // HAVE_CG
5464}
5465
5466/**
5467 * Sets the global Cg device pointer. TODO: make this thread-safe somehow.
5468 * Maybe Cg is inherently not thread-safe.
5469 */
5471set_cg_device(LPDIRECT3DDEVICE9 cg_device) {
5472#ifdef HAVE_CG
5473 if (_cg_device != cg_device) {
5474 cgD3D9SetDevice(cg_device);
5475 _cg_device = cg_device;
5476 }
5477#endif // HAVE_CG
5478}
5479
5480/**
5481 * Returns a vertex buffer containing only a full-white color.
5482 */
5483LPDIRECT3DVERTEXBUFFER9 DXGraphicsStateGuardian9::
5485 if (_white_vbuffer != nullptr) {
5486 return _white_vbuffer;
5487 }
5488
5489 LPDIRECT3DVERTEXBUFFER9 vbuffer;
5490 HRESULT hr;
5491 hr = _screen->_d3d_device->CreateVertexBuffer(sizeof(D3DCOLOR), D3DUSAGE_WRITEONLY, D3DFVF_DIFFUSE, D3DPOOL_DEFAULT, &vbuffer, nullptr);
5492
5493 if (FAILED(hr)) {
5494 dxgsg9_cat.error()
5495 << "CreateVertexBuffer failed" << D3DERRORSTRING(hr);
5496 return nullptr;
5497 }
5498
5499 D3DCOLOR *local_pointer;
5500 hr = vbuffer->Lock(0, sizeof(D3DCOLOR), (void **) &local_pointer, D3DLOCK_DISCARD);
5501 if (FAILED(hr)) {
5502 dxgsg9_cat.error()
5503 << "VertexBuffer::Lock failed" << D3DERRORSTRING(hr);
5504 return false;
5505 }
5506
5507 *local_pointer = D3DCOLOR_ARGB(255, 255, 255, 255);
5508
5509 vbuffer->Unlock();
5510 _white_vbuffer = vbuffer;
5511 return vbuffer;
5512}
5513
5514typedef std::string KEY;
5515
5516typedef struct _KEY_ELEMENT
5517{
5518 KEY key;
5519 int count;
5520 int secondary_count;
5521
5522 struct _KEY_ELEMENT *next;
5523}
5524KEY_ELEMENT;
5525
5526typedef struct _KEY_LIST
5527{
5528 int total_key_elements;
5529 KEY_ELEMENT *key_element;
5530}
5531KEY_LIST;
5532
5533KEY_ELEMENT *new_key_element (KEY key, KEY_LIST *key_list)
5534{
5535 KEY_ELEMENT *key_element;
5536
5537 key_element = new KEY_ELEMENT;
5538 key_element -> key = key;
5539 key_element -> count = 1;
5540 key_element -> secondary_count = 0;
5541 key_element -> next = 0;
5542
5543 key_list -> total_key_elements++;
5544
5545 return key_element;
5546}
5547
5548KEY_ELEMENT *first_key_element (KEY_LIST *key_list)
5549{
5550 return key_list -> key_element;
5551}
5552
5553KEY_ELEMENT *next_key_element (KEY_ELEMENT *key_element)
5554{
5555 return key_element -> next;
5556}
5557
5558void delete_key_list (KEY_LIST *key_list)
5559{
5560 if (key_list)
5561 {
5562 KEY_ELEMENT *key_element;
5563 KEY_ELEMENT *key_element_next;
5564
5565 key_element = first_key_element (key_list);
5566 while (key_element)
5567 {
5568 key_element_next = next_key_element (key_element);
5569 delete key_element;
5570 key_element = key_element_next;
5571 }
5572
5573 delete key_list;
5574 }
5575}
5576
5577KEY_LIST *new_key_list (void)
5578{
5579 KEY_LIST *key_list;
5580
5581 key_list = new KEY_LIST;
5582 memset (key_list, 0, sizeof (KEY_LIST));
5583
5584 return key_list;
5585}
5586
5587KEY_ELEMENT *add_to_key_list (KEY key, KEY_LIST *key_list)
5588{
5589 KEY_ELEMENT *key_element;
5590 KEY_ELEMENT *last_key_element;
5591 KEY_ELEMENT *current_key_element;
5592
5593 key_element = 0;
5594 last_key_element = 0;
5595 current_key_element = key_list -> key_element;
5596 if (current_key_element == 0)
5597 {
5598 key_element = new_key_element (key, key_list);
5599 key_list -> key_element = key_element;
5600 }
5601 else
5602 {
5603 while (current_key_element)
5604 {
5605 if (key < current_key_element -> key)
5606 {
5607 key_element = new_key_element (key, key_list);
5608 key_element -> next = current_key_element;
5609
5610 if (last_key_element == 0)
5611 {
5612 key_list -> key_element = key_element;
5613 }
5614 else
5615 {
5616 last_key_element -> next = key_element;
5617 }
5618 break;
5619 }
5620 else
5621 {
5622 if (key > current_key_element -> key)
5623 {
5624 if (current_key_element -> next == 0)
5625 {
5626 key_element = new_key_element (key, key_list);
5627 current_key_element -> next = key_element;
5628 break;
5629 }
5630 else
5631 {
5632
5633 }
5634 }
5635 else
5636 {
5637 current_key_element -> count++;
5638 break;
5639 }
5640 }
5641
5642 last_key_element = current_key_element;
5643 current_key_element = current_key_element -> next;
5644 }
5645 }
5646
5647 return key_element;
5648}
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.
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.
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.
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...
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.
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:794
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
get_plane
Returns the plane represented by the PlaneNode.
Definition planeNode.h:68
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,...
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.
get_wrap_v
Returns the wrap mode of the texture in the V direction.
get_wrap_w
Returns the wrap mode of the texture in the W direction.
get_effective_magfilter
Returns the filter mode of the texture for magnification, with special treatment for FT_default.
get_lod_bias
Returns the bias that will be added to the texture level of detail when sampling this texture.
get_effective_minfilter
Returns the filter mode of the texture for minification, with special treatment for FT_default.
get_effective_anisotropic_degree
Returns the degree of anisotropic filtering that should be applied to the texture.
get_wrap_u
Returns the wrap mode of the texture in the U direction.
get_border_color
Returns the solid color of the texture's border.
This restricts rendering to within a rectangular region of the scene, without otherwise affecting the...
get_frame
Returns the left, right, bottom, top coordinates of the scissor frame.
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.
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:3762
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.
unsigned int get_render_state(StencilRenderState render_state_identifier) const
Returns render state.
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.
CombineMode get_combine_alpha_mode() const
Get combine_alpha_mode.
get_saved_result
Returns the current setting of the saved_result flag.
CombineOperand get_combine_rgb_operand2() const
Get operand2 of combine_rgb_mode.
CombineSource get_combine_alpha_source1() const
Get source1 of combine_alpha_mode.
CombineMode get_combine_rgb_mode() const
Get the combine_rgb_mode.
CombineSource get_combine_rgb_source1() const
Get source1 of combine_rgb_mode.
bool involves_color_scale() const
Returns true if the TextureStage is affected by the setting of the current ColorScaleAttrib,...
CombineOperand get_combine_alpha_operand0() const
Get operand0 of combine_alpha_mode.
CombineOperand get_combine_rgb_operand1() const
Get operand1 of combine_rgb_mode.
CombineSource get_combine_alpha_source0() const
Get source0 of combine_alpha_mode.
get_rgb_scale
See set_rgb_scale().
get_alpha_scale
See set_alpha_scale().
int get_num_combine_rgb_operands() const
Returns the number of meaningful operands that may be retrieved via get_combine_rgb_sourceN() and get...
CombineOperand get_combine_alpha_operand1() const
Get operand1 of combine_alpha_mode.
get_color
return the color for this stage
get_tex_view_offset
Returns the current setting of the tex_view_offset.
CombineSource get_combine_rgb_source0() const
Get source0 of combine_rgb_mode.
CombineSource get_combine_rgb_source2() const
Get source2 of combine_rgb_mode.
get_mode
Return the mode of this stage.
bool uses_color() const
Returns true if the TextureStage makes use of whatever color is specified in set_color(),...
int get_num_combine_alpha_operands() const
Returns the number of meaningful operands that may be retrieved via get_combine_alpha_sourceN() and g...
CombineOperand get_combine_rgb_operand0() const
Get operand0 of combine_rgb_mode.
CombineSource get_combine_alpha_source2() const
Get source2 of combine_alpha_mode.
CombineOperand get_combine_alpha_operand2() const
Get operand2 of combine_alpha_mode.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition texture.h:72
get_format
Returns the format of the texture, which represents both the semantic meaning of the texels and,...
Definition texture.h:371
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:355
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:1243
get_texture_type
Returns the overall interpretation of the texture.
Definition texture.h:366
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:472
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:53
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:418
get_y_size
Returns the height of the texture image in texels.
Definition texture.h:347
get_z_size
Returns the depth of the texture image in texels.
Definition texture.h:351
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:1265
has_simple_ram_image
Returns true if the Texture has a "simple" image available in main RAM.
Definition texture.h:520
get_x_size
Returns the width of the texture image in texels.
Definition texture.h:343
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:1253
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:375
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.
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.