forked from electronicarts/CnC_Generals_Zero_Hour
-
Notifications
You must be signed in to change notification settings - Fork 185
Expand file tree
/
Copy pathdx8wrapper.cpp
More file actions
4450 lines (3854 loc) · 132 KB
/
dx8wrapper.cpp
File metadata and controls
4450 lines (3854 loc) · 132 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
** Command & Conquer Generals Zero Hour(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : WW3D *
* *
* $Archive:: /Commando/Code/ww3d2/dx8wrapper.cpp $*
* *
* Original Author:: Jani Penttinen *
* *
* $Author:: Kenny Mitchell *
* *
* $Modtime:: 08/05/02 1:27p $*
* *
* $Revision:: 170 $*
* *
* 06/26/02 KM Matrix name change to avoid MAX conflicts *
* 06/27/02 KM Render to shadow buffer texture support *
* 06/27/02 KM Shader system updates *
* 08/05/02 KM Texture class redesign
*---------------------------------------------------------------------------------------------*
* Functions: *
* DX8Wrapper::_Update_Texture -- Copies a texture from system memory to video memory *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
//#define CREATE_DX8_MULTI_THREADED
//#define CREATE_DX8_FPU_PRESERVE
#define WW3D_DEVTYPE D3DDEVTYPE_HAL
#if !defined(WINVER) || WINVER < 0x0500
#undef WINVER
#define WINVER 0x0500 // Required to access GetMonitorInfo in VC6.
#endif
#include "dx8wrapper.h"
#include "dx8webbrowser.h"
#include "dx8fvf.h"
#include "dx8vertexbuffer.h"
#include "dx8indexbuffer.h"
#include "dx8renderer.h"
#include "ww3d.h"
#include "camera.h"
#include "wwstring.h"
#include "matrix4.h"
#include "vertmaterial.h"
#include "rddesc.h"
#include "lightenvironment.h"
#include "statistics.h"
#include "registry.h"
#include "boxrobj.h"
#include "pointgr.h"
#include "render2d.h"
#include "sortingrenderer.h"
#include "shattersystem.h"
#include "light.h"
#include "assetmgr.h"
#include "textureloader.h"
#include "missingtexture.h"
#include "thread.h"
#include <d3dx8core.h>
#include "pot.h"
#include "wwprofile.h"
#include "ffactory.h"
#include "dx8caps.h"
#include "formconv.h"
#include "dx8texman.h"
#include "bound.h"
#include "DbgHelpGuard.h"
#include "shdlib.h"
const int DEFAULT_RESOLUTION_WIDTH = 640;
const int DEFAULT_RESOLUTION_HEIGHT = 480;
const int DEFAULT_BIT_DEPTH = 32;
const int DEFAULT_TEXTURE_BIT_DEPTH = 16;
const D3DMULTISAMPLE_TYPE DEFAULT_MSAA = D3DMULTISAMPLE_NONE;
DX8FrameStatistics DX8Wrapper::FrameStatistics;
static DX8FrameStatistics LastFrameStatistics;
bool DX8Wrapper_IsWindowed = true;
// FPU_PRESERVE
int DX8Wrapper_PreserveFPU = 0;
/***********************************************************************************
**
** DX8Wrapper Static Variables
**
***********************************************************************************/
static HWND _Hwnd = nullptr;
bool DX8Wrapper::IsInitted = false;
bool DX8Wrapper::_EnableTriangleDraw = true;
int DX8Wrapper::CurRenderDevice = -1;
int DX8Wrapper::ResolutionWidth = DEFAULT_RESOLUTION_WIDTH;
int DX8Wrapper::ResolutionHeight = DEFAULT_RESOLUTION_HEIGHT;
int DX8Wrapper::BitDepth = DEFAULT_BIT_DEPTH;
int DX8Wrapper::TextureBitDepth = DEFAULT_TEXTURE_BIT_DEPTH;
bool DX8Wrapper::IsWindowed = false;
D3DFORMAT DX8Wrapper::DisplayFormat = D3DFMT_UNKNOWN;
D3DMULTISAMPLE_TYPE DX8Wrapper::MultiSampleAntiAliasing = DEFAULT_MSAA;
// shader system additions KJM v
DWORD DX8Wrapper::Vertex_Shader = 0;
DWORD DX8Wrapper::Pixel_Shader = 0;
Vector4 DX8Wrapper::Vertex_Shader_Constants[MAX_VERTEX_SHADER_CONSTANTS];
Vector4 DX8Wrapper::Pixel_Shader_Constants[MAX_PIXEL_SHADER_CONSTANTS];
LightEnvironmentClass* DX8Wrapper::Light_Environment = nullptr;
DWORD DX8Wrapper::Vertex_Processing_Behavior = 0;
ZTextureClass* DX8Wrapper::Shadow_Map[MAX_SHADOW_MAPS];
Vector3 DX8Wrapper::Ambient_Color;
// shader system additions KJM ^
bool DX8Wrapper::world_identity;
unsigned DX8Wrapper::RenderStates[256];
unsigned DX8Wrapper::TextureStageStates[MAX_TEXTURE_STAGES][32];
IDirect3DBaseTexture8 * DX8Wrapper::Textures[MAX_TEXTURE_STAGES];
RenderStateStruct DX8Wrapper::render_state;
unsigned DX8Wrapper::render_state_changed;
bool DX8Wrapper::FogEnable = false;
D3DCOLOR DX8Wrapper::FogColor = 0;
IDirect3D8 * DX8Wrapper::D3DInterface = nullptr;
IDirect3DDevice8 * DX8Wrapper::D3DDevice = nullptr;
IDirect3DSurface8 * DX8Wrapper::CurrentRenderTarget = nullptr;
IDirect3DSurface8 * DX8Wrapper::CurrentDepthBuffer = nullptr;
IDirect3DSurface8 * DX8Wrapper::DefaultRenderTarget = nullptr;
IDirect3DSurface8 * DX8Wrapper::DefaultDepthBuffer = nullptr;
bool DX8Wrapper::IsRenderToTexture = false;
unsigned DX8Wrapper::_MainThreadID = 0;
bool DX8Wrapper::CurrentDX8LightEnables[4];
bool DX8Wrapper::IsDeviceLost;
int DX8Wrapper::ZBias;
float DX8Wrapper::ZNear;
float DX8Wrapper::ZFar;
D3DMATRIX DX8Wrapper::ProjectionMatrix;
D3DMATRIX DX8Wrapper::DX8Transforms[D3DTS_WORLD+1];
DX8Caps* DX8Wrapper::CurrentCaps = nullptr;
// Hack test... this disables rendering of batches of too few polygons.
unsigned DX8Wrapper::DrawPolygonLowBoundLimit=0;
D3DADAPTER_IDENTIFIER8 DX8Wrapper::CurrentAdapterIdentifier;
unsigned long DX8Wrapper::FrameCount = 0;
bool _DX8SingleThreaded = false;
static D3DPRESENT_PARAMETERS _PresentParameters;
static DynamicVectorClass<StringClass> _RenderDeviceNameTable;
static DynamicVectorClass<StringClass> _RenderDeviceShortNameTable;
static DynamicVectorClass<RenderDeviceDescClass> _RenderDeviceDescriptionTable;
typedef IDirect3D8* (WINAPI *Direct3DCreate8Type) (UINT SDKVersion);
Direct3DCreate8Type Direct3DCreate8Ptr = nullptr;
HINSTANCE D3D8Lib = nullptr;
DX8_CleanupHook *DX8Wrapper::m_pCleanupHook=nullptr;
#ifdef EXTENDED_STATS
DX8_Stats DX8Wrapper::stats;
#endif
/***********************************************************************************
**
** DX8Wrapper Implementation
**
***********************************************************************************/
void Log_DX8_ErrorCode(unsigned res)
{
char tmp[256]="";
HRESULT new_res=D3DXGetErrorStringA(
res,
tmp,
sizeof(tmp));
if (new_res==D3D_OK) {
WWDEBUG_SAY((tmp));
}
WWASSERT(0);
}
void Non_Fatal_Log_DX8_ErrorCode(unsigned res,const char * file,int line)
{
char tmp[256]="";
HRESULT new_res=D3DXGetErrorStringA(
res,
tmp,
sizeof(tmp));
if (new_res==D3D_OK) {
WWDEBUG_SAY(("DX8 Error: %s, File: %s, Line: %d",tmp,file,line));
}
}
// TheSuperHackers @info helmutbuhler 14/04/2025
// Helper function that moves x and y such that the inner rect fits into the outer rect.
// If the inner rect already is in the outer rect, then this does nothing.
// If the inner rect is larger than the outer rect, then the inner rect will be aligned to the top left of the outer rect.
void MoveRectIntoOtherRect(const RECT& inner, const RECT& outer, int* x, int* y)
{
int dx = 0;
if (inner.right > outer.right)
dx = outer.right-inner.right;
if (inner.left < outer.left)
dx = outer.left-inner.left;
int dy = 0;
if (inner.bottom > outer.bottom)
dy = outer.bottom-inner.bottom;
if (inner.top < outer.top)
dy = outer.top-inner.top;
*x += dx;
*y += dy;
}
bool DX8Wrapper::Init(void * hwnd, bool lite)
{
WWASSERT(!IsInitted);
// zero memory
memset(Textures,0,sizeof(IDirect3DBaseTexture8*)*MAX_TEXTURE_STAGES);
memset(RenderStates,0,sizeof(unsigned)*256);
memset(TextureStageStates,0,sizeof(unsigned)*32*MAX_TEXTURE_STAGES);
memset(Vertex_Shader_Constants,0,sizeof(Vector4)*MAX_VERTEX_SHADER_CONSTANTS);
memset(Pixel_Shader_Constants,0,sizeof(Vector4)*MAX_PIXEL_SHADER_CONSTANTS);
memset(&render_state,0,sizeof(RenderStateStruct));
memset(Shadow_Map,0,sizeof(ZTextureClass*)*MAX_SHADOW_MAPS);
/*
** Initialize all variables!
*/
_Hwnd = (HWND)hwnd;
_MainThreadID=ThreadClass::_Get_Current_Thread_ID();
WWDEBUG_SAY(("DX8Wrapper main thread: 0x%x",_MainThreadID));
CurRenderDevice = -1;
ResolutionWidth = DEFAULT_RESOLUTION_WIDTH;
ResolutionHeight = DEFAULT_RESOLUTION_HEIGHT;
// Initialize Render2DClass Screen Resolution
Render2DClass::Set_Screen_Resolution( RectClass( 0, 0, ResolutionWidth, ResolutionHeight ) );
BitDepth = DEFAULT_BIT_DEPTH;
IsWindowed = false;
DX8Wrapper_IsWindowed = false;
for (int light=0;light<4;++light) CurrentDX8LightEnables[light]=false;
//old_vertex_shader; TODO
//old_sr_shader;
//current_shader;
//world_identity;
//CurrentFogColor;
D3DInterface = nullptr;
D3DDevice = nullptr;
WWDEBUG_SAY(("Reset DX8Wrapper statistics"));
Reset_Statistics();
Invalidate_Cached_Render_States();
if (!lite) {
D3D8Lib = LoadLibrary("D3D8.DLL");
if (D3D8Lib == nullptr) return false; // Return false at this point if init failed
Direct3DCreate8Ptr = (Direct3DCreate8Type) GetProcAddress(D3D8Lib, "Direct3DCreate8");
if (Direct3DCreate8Ptr == nullptr) return false;
/*
** Create the D3D interface object
*/
WWDEBUG_SAY(("Create Direct3D8"));
{
// TheSuperHackers @bugfix xezon 13/06/2025 Front load the system dbghelp.dll to prevent
// the graphics driver from potentially loading the old game dbghelp.dll and then crashing the game process.
DbgHelpGuard dbgHelpGuard;
D3DInterface = Direct3DCreate8Ptr(D3D_SDK_VERSION); // TODO: handle failure cases...
}
if (D3DInterface == nullptr) {
return(false);
}
IsInitted = true;
/*
** Enumerate the available devices
*/
WWDEBUG_SAY(("Enumerate devices"));
Enumerate_Devices();
WWDEBUG_SAY(("DX8Wrapper Init completed"));
}
return(true);
}
void DX8Wrapper::Shutdown()
{
if (D3DDevice) {
Set_Render_Target ((IDirect3DSurface8 *)nullptr);
Release_Device();
}
if (D3DInterface) {
D3DInterface->Release();
D3DInterface=nullptr;
}
if (CurrentCaps)
{
int max=CurrentCaps->Get_Max_Textures_Per_Pass();
for (int i = 0; i < max; i++)
{
if (Textures[i])
{
Textures[i]->Release();
Textures[i] = nullptr;
}
}
}
if (D3D8Lib) {
FreeLibrary(D3D8Lib);
D3D8Lib = nullptr;
}
_RenderDeviceNameTable.Clear(); // note - Delete_All() resizes the vector, causing a reallocation. Clear is better. jba.
_RenderDeviceShortNameTable.Clear();
_RenderDeviceDescriptionTable.Clear();
DX8Caps::Shutdown();
IsInitted = false; // 010803 srj
}
void DX8Wrapper::Do_Onetime_Device_Dependent_Inits()
{
/*
** Set Global render states (some of which depend on caps)
*/
Compute_Caps(D3DFormat_To_WW3DFormat(DisplayFormat));
/*
** Initialize any other subsystems inside of WW3D
*/
MissingTexture::_Init();
TextureFilterClass::_Init_Filters((TextureFilterClass::TextureFilterMode)WW3D::Get_Texture_Filter());
TheDX8MeshRenderer.Init();
SHD_INIT;
BoxRenderObjClass::Init();
VertexMaterialClass::Init();
PointGroupClass::_Init(); // This needs the VertexMaterialClass to be initted
ShatterSystem::Init();
TextureLoader::Init();
Set_Default_Global_Render_States();
}
inline DWORD F2DW(float f) { return *((unsigned*)&f); }
void DX8Wrapper::Set_Default_Global_Render_States()
{
DX8_THREAD_ASSERT();
const D3DCAPS8 &caps = Get_Current_Caps()->Get_DX8_Caps();
Set_DX8_Render_State(D3DRS_RANGEFOGENABLE, (caps.RasterCaps & D3DPRASTERCAPS_FOGRANGE) ? TRUE : FALSE);
Set_DX8_Render_State(D3DRS_FOGTABLEMODE, D3DFOG_NONE);
Set_DX8_Render_State(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
Set_DX8_Render_State(D3DRS_SPECULARMATERIALSOURCE, D3DMCS_MATERIAL);
Set_DX8_Render_State(D3DRS_COLORVERTEX, TRUE);
Set_DX8_Render_State(D3DRS_ZBIAS,0);
Set_DX8_Texture_Stage_State(1, D3DTSS_BUMPENVLSCALE, F2DW(1.0f));
Set_DX8_Texture_Stage_State(1, D3DTSS_BUMPENVLOFFSET, F2DW(0.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT00,F2DW(1.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT01,F2DW(0.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT10,F2DW(0.0f));
Set_DX8_Texture_Stage_State(0, D3DTSS_BUMPENVMAT11,F2DW(1.0f));
// Set_DX8_Render_State(D3DRS_CULLMODE, D3DCULL_CW);
// Set dither mode here?
}
void DX8Wrapper::Invalidate_Cached_Render_States()
{
render_state_changed=0;
int a;
for (a=0;a<sizeof(RenderStates)/sizeof(unsigned);++a) {
RenderStates[a]=0x12345678;
}
for (a=0;a<MAX_TEXTURE_STAGES;++a)
{
for (int b=0; b<32;b++)
{
TextureStageStates[a][b]=0x12345678;
}
//Need to explicitly set texture to null, otherwise app will not be able to
//set it to null because of redundant state checker. MW
if (_Get_D3D_Device8())
_Get_D3D_Device8()->SetTexture(a,nullptr);
if (Textures[a] != nullptr) {
Textures[a]->Release();
}
Textures[a]=nullptr;
}
ShaderClass::Invalidate();
//Need to explicitly set render_state texture pointers to null. MW
Release_Render_State();
// (gth) clear the matrix shadows too
memset(&DX8Transforms, 0, sizeof(DX8Transforms));
}
void DX8Wrapper::Do_Onetime_Device_Dependent_Shutdowns()
{
/*
** Shutdown ww3d systems
*/
int i;
for (i=0;i<MAX_VERTEX_STREAMS;++i) {
if (render_state.vertex_buffers[i]) render_state.vertex_buffers[i]->Release_Engine_Ref();
REF_PTR_RELEASE(render_state.vertex_buffers[i]);
}
if (render_state.index_buffer) render_state.index_buffer->Release_Engine_Ref();
REF_PTR_RELEASE(render_state.index_buffer);
REF_PTR_RELEASE(render_state.material);
for (i=0;i<CurrentCaps->Get_Max_Textures_Per_Pass();++i) REF_PTR_RELEASE(render_state.Textures[i]);
TextureLoader::Deinit();
SortingRendererClass::Deinit();
DynamicVBAccessClass::_Deinit();
DynamicIBAccessClass::_Deinit();
ShatterSystem::Shutdown();
PointGroupClass::_Shutdown();
VertexMaterialClass::Shutdown();
BoxRenderObjClass::Shutdown();
SHD_SHUTDOWN;
TheDX8MeshRenderer.Shutdown();
MissingTexture::_Deinit();
delete CurrentCaps;
CurrentCaps=nullptr;
}
bool DX8Wrapper::Create_Device()
{
WWASSERT(D3DDevice==nullptr); // for now, once you've created a device, you're stuck with it!
D3DCAPS8 caps;
if
(
FAILED
(
D3DInterface->GetDeviceCaps
(
CurRenderDevice,
WW3D_DEVTYPE,
&caps
)
)
)
{
return false;
}
::ZeroMemory(&CurrentAdapterIdentifier, sizeof(D3DADAPTER_IDENTIFIER8));
if
(
FAILED
(
D3DInterface->GetAdapterIdentifier
(
CurRenderDevice,
D3DENUM_NO_WHQL_LEVEL,
&CurrentAdapterIdentifier
)
)
)
{
return false;
}
Vertex_Processing_Behavior=(caps.DevCaps&D3DDEVCAPS_HWTRANSFORMANDLIGHT) ?
D3DCREATE_MIXED_VERTEXPROCESSING : D3DCREATE_SOFTWARE_VERTEXPROCESSING;
// enable this when all 'get' dx calls are removed KJM
/*if (caps.DevCaps&D3DDEVCAPS_PUREDEVICE)
{
Vertex_Processing_Behavior|=D3DCREATE_PUREDEVICE;
}*/
#ifdef CREATE_DX8_MULTI_THREADED
Vertex_Processing_Behavior|=D3DCREATE_MULTITHREADED;
_DX8SingleThreaded=false;
#else
_DX8SingleThreaded=true;
#endif
if (DX8Wrapper_PreserveFPU)
Vertex_Processing_Behavior |= D3DCREATE_FPU_PRESERVE;
#ifdef CREATE_DX8_FPU_PRESERVE
Vertex_Processing_Behavior|=D3DCREATE_FPU_PRESERVE;
#endif
// TheSuperHackers @bugfix xezon 13/06/2025 Front load the system dbghelp.dll to prevent
// the graphics driver from potentially loading the old game dbghelp.dll and then crashing the game process.
DbgHelpGuard dbgHelpGuard;
HRESULT hr=D3DInterface->CreateDevice
(
CurRenderDevice,
WW3D_DEVTYPE,
_Hwnd,
Vertex_Processing_Behavior,
&_PresentParameters,
&D3DDevice
);
if (FAILED(hr))
{
// The device selection may fail because the device lied that it supports 32 bit zbuffer with 16 bit
// display. This happens at least on Voodoo2.
if ((_PresentParameters.BackBufferFormat==D3DFMT_R5G6B5 ||
_PresentParameters.BackBufferFormat==D3DFMT_X1R5G5B5 ||
_PresentParameters.BackBufferFormat==D3DFMT_A1R5G5B5) &&
(_PresentParameters.AutoDepthStencilFormat==D3DFMT_D32 ||
_PresentParameters.AutoDepthStencilFormat==D3DFMT_D24S8 ||
_PresentParameters.AutoDepthStencilFormat==D3DFMT_D24X8))
{
_PresentParameters.AutoDepthStencilFormat=D3DFMT_D16;
hr = D3DInterface->CreateDevice
(
CurRenderDevice,
WW3D_DEVTYPE,
_Hwnd,
Vertex_Processing_Behavior,
&_PresentParameters,
&D3DDevice
);
if (FAILED(hr))
{
return false;
}
}
else
{
return false;
}
}
dbgHelpGuard.deactivate();
/*
** Initialize all subsystems
*/
Do_Onetime_Device_Dependent_Inits();
return true;
}
bool DX8Wrapper::Reset_Device(bool reload_assets)
{
WWDEBUG_SAY(("Resetting device."));
DX8_THREAD_ASSERT();
if ((IsInitted) && (D3DDevice != nullptr)) {
// Release all non-MANAGED stuff
WW3D::_Invalidate_Textures();
for (unsigned i=0;i<MAX_VERTEX_STREAMS;++i)
{
Set_Vertex_Buffer (nullptr,i);
}
Set_Index_Buffer (nullptr, 0);
if (m_pCleanupHook) {
m_pCleanupHook->ReleaseResources();
}
DynamicVBAccessClass::_Deinit();
DynamicIBAccessClass::_Deinit();
DX8TextureManagerClass::Release_Textures();
SHD_SHUTDOWN_SHADERS;
// Reset frame count to reflect the flipping chain being reset by Reset()
FrameCount = 0;
memset(Vertex_Shader_Constants,0,sizeof(Vector4)*MAX_VERTEX_SHADER_CONSTANTS);
memset(Pixel_Shader_Constants,0,sizeof(Vector4)*MAX_PIXEL_SHADER_CONSTANTS);
HRESULT hr=_Get_D3D_Device8()->TestCooperativeLevel();
if (hr != D3DERR_DEVICELOST )
{ DX8CALL_HRES(Reset(&_PresentParameters),hr)
if (hr != D3D_OK)
return false; //reset failed.
}
else
return false; //device is lost and can't be reset.
if (reload_assets)
{
DX8TextureManagerClass::Recreate_Textures();
if (m_pCleanupHook) {
m_pCleanupHook->ReAcquireResources();
}
}
Invalidate_Cached_Render_States();
Set_Default_Global_Render_States();
SHD_INIT_SHADERS;
WWDEBUG_SAY(("Device reset completed"));
return true;
}
WWDEBUG_SAY(("Device reset failed"));
return false;
}
void DX8Wrapper::Release_Device()
{
if (D3DDevice) {
for (int a=0;a<MAX_TEXTURE_STAGES;++a)
{ //release references to any textures that were used in last rendering call
DX8CALL(SetTexture(a,nullptr));
}
DX8CALL(SetStreamSource(0, nullptr, 0)); //release reference count on last rendered vertex buffer
DX8CALL(SetIndices(nullptr,0)); //release reference count on last rendered index buffer
/*
** Release the current vertex and index buffers
*/
for (unsigned i=0;i<MAX_VERTEX_STREAMS;++i)
{
if (render_state.vertex_buffers[i]) render_state.vertex_buffers[i]->Release_Engine_Ref();
REF_PTR_RELEASE(render_state.vertex_buffers[i]);
}
if (render_state.index_buffer) render_state.index_buffer->Release_Engine_Ref();
REF_PTR_RELEASE(render_state.index_buffer);
/*
** Shutdown all subsystems
*/
Do_Onetime_Device_Dependent_Shutdowns();
/*
** Release the device
*/
D3DDevice->Release();
D3DDevice=nullptr;
}
}
void DX8Wrapper::Enumerate_Devices()
{
DX8_Assert();
int adapter_count = D3DInterface->GetAdapterCount();
for (int adapter_index=0; adapter_index<adapter_count; adapter_index++) {
D3DADAPTER_IDENTIFIER8 id;
::ZeroMemory(&id, sizeof(D3DADAPTER_IDENTIFIER8));
HRESULT res = D3DInterface->GetAdapterIdentifier(adapter_index,D3DENUM_NO_WHQL_LEVEL,&id);
if (res == D3D_OK) {
/*
** Set up the render device description
** TODO: Fill in more fields of the render device description? (need some lookup tables)
*/
RenderDeviceDescClass desc;
desc.set_device_name(id.Description);
desc.set_driver_name(id.Driver);
char buf[64];
sprintf(buf,"%d.%d.%d.%d", //"%04x.%04x.%04x.%04x",
HIWORD(id.DriverVersion.HighPart),
LOWORD(id.DriverVersion.HighPart),
HIWORD(id.DriverVersion.LowPart),
LOWORD(id.DriverVersion.LowPart));
desc.set_driver_version(buf);
D3DInterface->GetDeviceCaps(adapter_index,WW3D_DEVTYPE,&desc.Caps);
D3DInterface->GetAdapterIdentifier(adapter_index,D3DENUM_NO_WHQL_LEVEL,&desc.AdapterIdentifier);
DX8Caps dx8caps(D3DInterface,desc.Caps,WW3D_FORMAT_UNKNOWN,desc.AdapterIdentifier);
/*
** Enumerate the resolutions
*/
desc.reset_resolution_list();
int mode_count = D3DInterface->GetAdapterModeCount(adapter_index);
for (int mode_index=0; mode_index<mode_count; mode_index++) {
D3DDISPLAYMODE d3dmode;
::ZeroMemory(&d3dmode, sizeof(D3DDISPLAYMODE));
HRESULT res = D3DInterface->EnumAdapterModes(adapter_index,mode_index,&d3dmode);
if (res == D3D_OK) {
int bits = 0;
switch (d3dmode.Format)
{
case D3DFMT_R8G8B8:
case D3DFMT_A8R8G8B8:
case D3DFMT_X8R8G8B8: bits = 32; break;
case D3DFMT_R5G6B5:
case D3DFMT_X1R5G5B5: bits = 16; break;
}
// Some cards fail in certain modes, DX8Caps keeps list of those.
if (!dx8caps.Is_Valid_Display_Format(d3dmode.Width,d3dmode.Height,D3DFormat_To_WW3DFormat(d3dmode.Format))) {
bits=0;
}
/*
** If we recognize the format, add it to the list
** TODO: should we handle more formats? will any cards report more than 24 or 16 bit?
*/
if (bits != 0) {
desc.add_resolution(d3dmode.Width,d3dmode.Height,bits);
}
}
}
// IML: If the device has one or more valid resolutions add it to the device list.
// NOTE: Testing has shown that there are drivers with zero resolutions.
if (desc.Enumerate_Resolutions().Count() > 0) {
/*
** Set up the device name
*/
StringClass device_name(id.Description,true);
_RenderDeviceNameTable.Add(device_name);
_RenderDeviceShortNameTable.Add(device_name); // for now, just add the same name to the "pretty name table"
/*
** Add the render device to our table
*/
_RenderDeviceDescriptionTable.Add(desc);
}
}
}
}
bool DX8Wrapper::Set_Any_Render_Device()
{
// Then fullscreen
int dev_number = 0;
for (; dev_number < _RenderDeviceNameTable.Count(); dev_number++) {
if (Set_Render_Device(dev_number,-1,-1,-1,0,false)) {
return true;
}
}
// Try windowed first
for (dev_number = 0; dev_number < _RenderDeviceNameTable.Count(); dev_number++) {
if (Set_Render_Device(dev_number,-1,-1,-1,1,false)) {
return true;
}
}
return false;
}
bool DX8Wrapper::Set_Render_Device
(
const char * dev_name,
int width,
int height,
int bits,
int windowed,
bool resize_window
)
{
for ( int dev_number = 0; dev_number < _RenderDeviceNameTable.Count(); dev_number++) {
if ( strcmp( dev_name, _RenderDeviceNameTable[dev_number]) == 0) {
return Set_Render_Device( dev_number, width, height, bits, windowed, resize_window );
}
if ( strcmp( dev_name, _RenderDeviceShortNameTable[dev_number]) == 0) {
return Set_Render_Device( dev_number, width, height, bits, windowed, resize_window );
}
}
return false;
}
void DX8Wrapper::Get_Format_Name(unsigned int format, StringClass *tex_format)
{
*tex_format="Unknown";
switch (format) {
case D3DFMT_A8R8G8B8: *tex_format="D3DFMT_A8R8G8B8"; break;
case D3DFMT_R8G8B8: *tex_format="D3DFMT_R8G8B8"; break;
case D3DFMT_A4R4G4B4: *tex_format="D3DFMT_A4R4G4B4"; break;
case D3DFMT_A1R5G5B5: *tex_format="D3DFMT_A1R5G5B5"; break;
case D3DFMT_R5G6B5: *tex_format="D3DFMT_R5G6B5"; break;
case D3DFMT_L8: *tex_format="D3DFMT_L8"; break;
case D3DFMT_A8: *tex_format="D3DFMT_A8"; break;
case D3DFMT_P8: *tex_format="D3DFMT_P8"; break;
case D3DFMT_X8R8G8B8: *tex_format="D3DFMT_X8R8G8B8"; break;
case D3DFMT_X1R5G5B5: *tex_format="D3DFMT_X1R5G5B5"; break;
case D3DFMT_R3G3B2: *tex_format="D3DFMT_R3G3B2"; break;
case D3DFMT_A8R3G3B2: *tex_format="D3DFMT_A8R3G3B2"; break;
case D3DFMT_X4R4G4B4: *tex_format="D3DFMT_X4R4G4B4"; break;
case D3DFMT_A8P8: *tex_format="D3DFMT_A8P8"; break;
case D3DFMT_A8L8: *tex_format="D3DFMT_A8L8"; break;
case D3DFMT_A4L4: *tex_format="D3DFMT_A4L4"; break;
case D3DFMT_V8U8: *tex_format="D3DFMT_V8U8"; break;
case D3DFMT_L6V5U5: *tex_format="D3DFMT_L6V5U5"; break;
case D3DFMT_X8L8V8U8: *tex_format="D3DFMT_X8L8V8U8"; break;
case D3DFMT_Q8W8V8U8: *tex_format="D3DFMT_Q8W8V8U8"; break;
case D3DFMT_V16U16: *tex_format="D3DFMT_V16U16"; break;
case D3DFMT_W11V11U10: *tex_format="D3DFMT_W11V11U10"; break;
case D3DFMT_UYVY: *tex_format="D3DFMT_UYVY"; break;
case D3DFMT_YUY2: *tex_format="D3DFMT_YUY2"; break;
case D3DFMT_DXT1: *tex_format="D3DFMT_DXT1"; break;
case D3DFMT_DXT2: *tex_format="D3DFMT_DXT2"; break;
case D3DFMT_DXT3: *tex_format="D3DFMT_DXT3"; break;
case D3DFMT_DXT4: *tex_format="D3DFMT_DXT4"; break;
case D3DFMT_DXT5: *tex_format="D3DFMT_DXT5"; break;
case D3DFMT_D16_LOCKABLE: *tex_format="D3DFMT_D16_LOCKABLE"; break;
case D3DFMT_D32: *tex_format="D3DFMT_D32"; break;
case D3DFMT_D15S1: *tex_format="D3DFMT_D15S1"; break;
case D3DFMT_D24S8: *tex_format="D3DFMT_D24S8"; break;
case D3DFMT_D16: *tex_format="D3DFMT_D16"; break;
case D3DFMT_D24X8: *tex_format="D3DFMT_D24X8"; break;
case D3DFMT_D24X4S4: *tex_format="D3DFMT_D24X4S4"; break;
default: break;
}
}
void DX8Wrapper::Resize_And_Position_Window()
{
// Get the current dimensions of the 'render area' of the window
RECT rect = { 0 };
::GetClientRect (_Hwnd, &rect);
// Is the window the correct size for this resolution?
if ((rect.right-rect.left) != ResolutionWidth ||
(rect.bottom-rect.top) != ResolutionHeight) {
// Calculate what the main window's bounding rectangle should be to
// accommodate this resolution
rect.left = 0;
rect.top = 0;
rect.right = ResolutionWidth;
rect.bottom = ResolutionHeight;
DWORD dwstyle = ::GetWindowLong (_Hwnd, GWL_STYLE);
AdjustWindowRect (&rect, dwstyle, FALSE);
int width = rect.right-rect.left;
int height = rect.bottom-rect.top;
// Resize the window to fit this resolution
if (!IsWindowed)
{
::SetWindowPos(_Hwnd, HWND_TOPMOST, 0, 0, width, height, 0);
DEBUG_LOG(("Window resized to w:%d h:%d", width, height));
}
else
{
// TheSuperHackers @feature helmutbuhler 14/04/2025
// Center the window in the workarea of the monitor it is on.
MONITORINFO mi = {sizeof(MONITORINFO)};
GetMonitorInfo(MonitorFromWindow(_Hwnd, MONITOR_DEFAULTTOPRIMARY), &mi);
int left = (mi.rcWork.left + mi.rcWork.right - width) / 2;
int top = (mi.rcWork.top + mi.rcWork.bottom - height) / 2;
// TheSuperHackers @feature helmutbuhler 14/04/2025
// Move the window to try fit it into the monitor area, if one of its dimensions is larger than the work area.
// Otherwise align the window to the top left edges, if it is even larger than the monitor area.
RECT rectClient;
rectClient.left = left - rect.left;
rectClient.top = top - rect.top;
rectClient.right = rectClient.left + ResolutionWidth;
rectClient.bottom = rectClient.top + ResolutionHeight;
MoveRectIntoOtherRect(rectClient, mi.rcMonitor, &left, &top);
::SetWindowPos (_Hwnd, nullptr, left, top, width, height, SWP_NOZORDER);
DEBUG_LOG(("Window positioned to x:%d y:%d, resized to w:%d h:%d", left, top, width, height));
}
}
}
bool DX8Wrapper::Set_Render_Device(int dev, int width, int height, int bits, int windowed,
bool resize_window,bool reset_device, bool restore_assets)
{
WWASSERT(IsInitted);
WWASSERT(dev >= -1);
WWASSERT(dev < _RenderDeviceNameTable.Count());
/*
** If user has never selected a render device, start out with device 0
*/
if ((CurRenderDevice == -1) && (dev == -1)) {
CurRenderDevice = 0;
} else if (dev != -1) {
CurRenderDevice = dev;
}
/*
** If user doesn't want to change res, set the res variables to match the
** current resolution
*/
if (width != -1) ResolutionWidth = width;
if (height != -1) ResolutionHeight = height;
if (bits != -1) BitDepth = bits;
if (windowed != -1) IsWindowed = (windowed != 0);
DX8Wrapper_IsWindowed = IsWindowed;
WWDEBUG_SAY(("Attempting Set_Render_Device: name: %s (%s:%s), width: %d, height: %d, windowed: %d",
_RenderDeviceNameTable[CurRenderDevice].str(),_RenderDeviceDescriptionTable[CurRenderDevice].Get_Driver_Name(),
_RenderDeviceDescriptionTable[CurRenderDevice].Get_Driver_Version(),ResolutionWidth,ResolutionHeight,(IsWindowed ? 1 : 0)));
#ifdef _WIN32
// PWG 4/13/2000 - changed so that if you say to resize the window it resizes
// regardless of whether its windowed or not as OpenGL resizes its self around
// the caption and edges of the window type you provide, so its important to
// push the client area to be the size you really want.
// if ( resize_window && windowed ) {
if (resize_window) {
Resize_And_Position_Window();
}
#endif
//must be either resetting existing device or creating a new one.
WWASSERT(reset_device || D3DDevice == nullptr);
/*
** Initialize values for D3DPRESENT_PARAMETERS members.
*/
::ZeroMemory(&_PresentParameters, sizeof(D3DPRESENT_PARAMETERS));
_PresentParameters.BackBufferWidth = ResolutionWidth;
_PresentParameters.BackBufferHeight = ResolutionHeight;
_PresentParameters.BackBufferCount = IsWindowed ? 1 : 2;
//I changed this to discard all the time (even when full-screen) since that the most efficient. 07-16-03 MW:
_PresentParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;//IsWindowed ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_FLIP; // Shouldn't this be D3DSWAPEFFECT_FLIP?
_PresentParameters.hDeviceWindow = _Hwnd;
_PresentParameters.Windowed = IsWindowed;
_PresentParameters.EnableAutoDepthStencil = TRUE; // Driver will attempt to match Z-buffer depth
_PresentParameters.Flags=0; // We're not going to lock the backbuffer
_PresentParameters.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
_PresentParameters.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
/*
** Set up the buffer formats. Several issues here:
** - if in windowed mode, the backbuffer must use the current display format.
** - the depth buffer must use
*/
if (IsWindowed) {
D3DDISPLAYMODE desktop_mode;
::ZeroMemory(&desktop_mode, sizeof(D3DDISPLAYMODE));
D3DInterface->GetAdapterDisplayMode( CurRenderDevice, &desktop_mode );
DisplayFormat=_PresentParameters.BackBufferFormat = desktop_mode.Format;