1 /***********************************************************************************
2 Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3
4 (c) Copyright 1996 - 2002 Gary Henderson (gary.henderson@ntlworld.com),
5 Jerremy Koot (jkoot@snes9x.com)
6
7 (c) Copyright 2002 - 2004 Matthew Kendora
8
9 (c) Copyright 2002 - 2005 Peter Bortas (peter@bortas.org)
10
11 (c) Copyright 2004 - 2005 Joel Yliluoma (http://iki.fi/bisqwit/)
12
13 (c) Copyright 2001 - 2006 John Weidman (jweidman@slip.net)
14
15 (c) Copyright 2002 - 2006 funkyass (funkyass@spam.shaw.ca),
16 Kris Bleakley (codeviolation@hotmail.com)
17
18 (c) Copyright 2002 - 2010 Brad Jorsch (anomie@users.sourceforge.net),
19 Nach (n-a-c-h@users.sourceforge.net),
20
21 (c) Copyright 2002 - 2011 zones (kasumitokoduck@yahoo.com)
22
23 (c) Copyright 2006 - 2007 nitsuja
24
25 (c) Copyright 2009 - 2016 BearOso,
26 OV2
27
28
29 BS-X C emulator code
30 (c) Copyright 2005 - 2006 Dreamer Nom,
31 zones
32
33 C4 x86 assembler and some C emulation code
34 (c) Copyright 2000 - 2003 _Demo_ (_demo_@zsnes.com),
35 Nach,
36 zsKnight (zsknight@zsnes.com)
37
38 C4 C++ code
39 (c) Copyright 2003 - 2006 Brad Jorsch,
40 Nach
41
42 DSP-1 emulator code
43 (c) Copyright 1998 - 2006 _Demo_,
44 Andreas Naive (andreasnaive@gmail.com),
45 Gary Henderson,
46 Ivar (ivar@snes9x.com),
47 John Weidman,
48 Kris Bleakley,
49 Matthew Kendora,
50 Nach,
51 neviksti (neviksti@hotmail.com)
52
53 DSP-2 emulator code
54 (c) Copyright 2003 John Weidman,
55 Kris Bleakley,
56 Lord Nightmare (lord_nightmare@users.sourceforge.net),
57 Matthew Kendora,
58 neviksti
59
60 DSP-3 emulator code
61 (c) Copyright 2003 - 2006 John Weidman,
62 Kris Bleakley,
63 Lancer,
64 z80 gaiden
65
66 DSP-4 emulator code
67 (c) Copyright 2004 - 2006 Dreamer Nom,
68 John Weidman,
69 Kris Bleakley,
70 Nach,
71 z80 gaiden
72
73 OBC1 emulator code
74 (c) Copyright 2001 - 2004 zsKnight,
75 pagefault (pagefault@zsnes.com),
76 Kris Bleakley
77 Ported from x86 assembler to C by sanmaiwashi
78
79 SPC7110 and RTC C++ emulator code used in 1.39-1.51
80 (c) Copyright 2002 Matthew Kendora with research by
81 zsKnight,
82 John Weidman,
83 Dark Force
84
85 SPC7110 and RTC C++ emulator code used in 1.52+
86 (c) Copyright 2009 byuu,
87 neviksti
88
89 S-DD1 C emulator code
90 (c) Copyright 2003 Brad Jorsch with research by
91 Andreas Naive,
92 John Weidman
93
94 S-RTC C emulator code
95 (c) Copyright 2001 - 2006 byuu,
96 John Weidman
97
98 ST010 C++ emulator code
99 (c) Copyright 2003 Feather,
100 John Weidman,
101 Kris Bleakley,
102 Matthew Kendora
103
104 Super FX x86 assembler emulator code
105 (c) Copyright 1998 - 2003 _Demo_,
106 pagefault,
107 zsKnight
108
109 Super FX C emulator code
110 (c) Copyright 1997 - 1999 Ivar,
111 Gary Henderson,
112 John Weidman
113
114 Sound emulator code used in 1.5-1.51
115 (c) Copyright 1998 - 2003 Brad Martin
116 (c) Copyright 1998 - 2006 Charles Bilyue'
117
118 Sound emulator code used in 1.52+
119 (c) Copyright 2004 - 2007 Shay Green (gblargg@gmail.com)
120
121 S-SMP emulator code used in 1.54+
122 (c) Copyright 2016 byuu
123
124 SH assembler code partly based on x86 assembler code
125 (c) Copyright 2002 - 2004 Marcus Comstedt (marcus@mc.pp.se)
126
127 2xSaI filter
128 (c) Copyright 1999 - 2001 Derek Liauw Kie Fa
129
130 HQ2x, HQ3x, HQ4x filters
131 (c) Copyright 2003 Maxim Stepin (maxim@hiend3d.com)
132
133 NTSC filter
134 (c) Copyright 2006 - 2007 Shay Green
135
136 GTK+ GUI code
137 (c) Copyright 2004 - 2016 BearOso
138
139 Win32 GUI code
140 (c) Copyright 2003 - 2006 blip,
141 funkyass,
142 Matthew Kendora,
143 Nach,
144 nitsuja
145 (c) Copyright 2009 - 2016 OV2
146
147 Mac OS GUI code
148 (c) Copyright 1998 - 2001 John Stiles
149 (c) Copyright 2001 - 2011 zones
150
151
152 Specific ports contains the works of other authors. See headers in
153 individual files.
154
155
156 Snes9x homepage: http://www.snes9x.com/
157
158 Permission to use, copy, modify and/or distribute Snes9x in both binary
159 and source form, for non-commercial purposes, is hereby granted without
160 fee, providing that this license information and copyright notice appear
161 with all copies and any derived work.
162
163 This software is provided 'as-is', without any express or implied
164 warranty. In no event shall the authors be held liable for any damages
165 arising from the use of this software or it's derivatives.
166
167 Snes9x is freeware for PERSONAL USE only. Commercial users should
168 seek permission of the copyright holders first. Commercial use includes,
169 but is not limited to, charging money for Snes9x or software derived from
170 Snes9x, including Snes9x or derivatives in commercial game bundles, and/or
171 using Snes9x as a promotion for your commercial product.
172
173 The copyright holders request that bug fixes and improvements to the code
174 should be forwarded to them so everyone can benefit from the modifications
175 in future versions.
176
177 Super NES and Super Nintendo Entertainment System are trademarks of
178 Nintendo Co., Limited and its subsidiary companies.
179 ***********************************************************************************/
180
181 #pragma comment( lib, "d3d9" )
182 #pragma comment( lib, "d3dx9" )
183
184 #include "cdirect3d.h"
185 #include "win32_display.h"
186 #include "../snes9x.h"
187 #include "../gfx.h"
188 #include "../display.h"
189 #include "wsnes9x.h"
190 #include <Dxerr.h>
191 #include <commctrl.h>
192 #include "CXML.h"
193
194
195
196 #include "../filter/hq2x.h"
197 #include "../filter/2xsai.h"
198
199 #ifndef max
200 #define max(a, b) (((a) > (b)) ? (a) : (b))
201 #endif
202 #ifndef min
203 #define min(a, b) (((a) < (b)) ? (a) : (b))
204 #endif
205
206 const D3DVERTEXELEMENT9 CDirect3D::vertexElems[4] = {
207 {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
208 {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
209 {0, 20, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1},
210 D3DDECL_END()
211 };
212
213 /* CDirect3D::CDirect3D()
214 sets default values for the variables
215 */
CDirect3D()216 CDirect3D::CDirect3D()
217 {
218 init_done = false;
219 pD3D = NULL;
220 pDevice = NULL;
221 drawSurface = NULL;
222 vertexBuffer = NULL;
223 afterRenderWidth = 0;
224 afterRenderHeight = 0;
225 quadTextureSize = 0;
226 fullscreen = false;
227 filterScale = 1;
228 for(int i = 0; i < MAX_SHADER_TEXTURES; i++) {
229 rubyLUT[i] = NULL;
230 }
231 effect=NULL;
232 shader_type = D3D_SHADER_NONE;
233 shaderTimer = 1.0f;
234 shaderTimeStart = 0;
235 shaderTimeElapsed = 0;
236 frameCount = 0;
237 cgContext = NULL;
238 cgAvailable = false;
239 cgShader = NULL;
240 vertexDeclaration = NULL;
241 }
242
243 /* CDirect3D::~CDirect3D()
244 releases allocated objects
245 */
~CDirect3D()246 CDirect3D::~CDirect3D()
247 {
248 DeInitialize();
249 }
250
251
252 /* CDirect3D::Initialize
253 Initializes Direct3D (always in window mode)
254 IN:
255 hWnd - the HWND of the window in which we render/the focus window for fullscreen
256 -----
257 returns true if successful, false otherwise
258 */
Initialize(HWND hWnd)259 bool CDirect3D::Initialize(HWND hWnd)
260 {
261 if(init_done)
262 return true;
263
264 pD3D = Direct3DCreate9(D3D_SDK_VERSION);
265 if(pD3D == NULL) {
266 DXTRACE_ERR_MSGBOX(TEXT("Error creating initial D3D9 object"), 0);
267 return false;
268 }
269
270 memset(&dPresentParams, 0, sizeof(dPresentParams));
271 dPresentParams.hDeviceWindow = hWnd;
272 dPresentParams.Windowed = true;
273 dPresentParams.BackBufferCount = GUI.DoubleBuffered?2:1;
274 dPresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD;
275 dPresentParams.BackBufferFormat = D3DFMT_UNKNOWN;
276
277 HRESULT hr = pD3D->CreateDevice(D3DADAPTER_DEFAULT,
278 D3DDEVTYPE_HAL,
279 hWnd,
280 D3DCREATE_MIXED_VERTEXPROCESSING,
281 &dPresentParams,
282 &pDevice);
283 if(FAILED(hr)) {
284 DXTRACE_ERR_MSGBOX(TEXT("Error creating D3D9 device"), hr);
285 return false;
286 }
287
288 hr = pDevice->CreateVertexBuffer(sizeof(vertexStream),D3DUSAGE_WRITEONLY,0,D3DPOOL_MANAGED,&vertexBuffer,NULL);
289 if(FAILED(hr)) {
290 DXTRACE_ERR_MSGBOX(TEXT("Error creating vertex buffer"), hr);
291 return false;
292 }
293
294 hr = pDevice->CreateVertexDeclaration(vertexElems,&vertexDeclaration);
295 if(FAILED(hr)) {
296 DXTRACE_ERR_MSGBOX(TEXT("Error creating vertex declaration"), hr);
297 return false;
298 }
299
300 cgAvailable = loadCgFunctions();
301
302 if(cgAvailable) {
303 cgContext = cgCreateContext();
304 hr = cgD3D9SetDevice(pDevice);
305 if(FAILED(hr)) {
306 DXTRACE_ERR_MSGBOX(TEXT("Error setting cg device"), hr);
307 }
308 cgShader = new CD3DCG(cgContext,pDevice);
309 }
310
311 pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
312 pDevice->SetRenderState( D3DRS_ZENABLE, FALSE);
313 pDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
314 pDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
315
316 pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
317
318 init_done = true;
319
320 ApplyDisplayChanges();
321
322 return true;
323
324 }
325
DeInitialize()326 void CDirect3D::DeInitialize()
327 {
328 DestroyDrawSurface();
329 SetShader(NULL);
330
331 if(cgShader) {
332 delete cgShader;
333 cgShader = NULL;
334 }
335 if(cgContext) {
336 cgDestroyContext(cgContext);
337 cgContext = NULL;
338 }
339 if(cgAvailable)
340 cgD3D9SetDevice(NULL);
341
342 if(vertexBuffer) {
343 vertexBuffer->Release();
344 vertexBuffer = NULL;
345 }
346
347 if(vertexDeclaration) {
348 vertexDeclaration->Release();
349 vertexDeclaration = NULL;
350 }
351
352 if( pDevice ) {
353 pDevice->Release();
354 pDevice = NULL;
355 }
356
357 if( pD3D ) {
358 pD3D->Release();
359 pD3D = NULL;
360 }
361
362 init_done = false;
363 afterRenderWidth = 0;
364 afterRenderHeight = 0;
365 quadTextureSize = 0;
366 fullscreen = false;
367 filterScale = 0;
368 if(cgAvailable)
369 unloadCgLibrary();
370 cgAvailable = false;
371 }
372
SetShader(const TCHAR * file)373 bool CDirect3D::SetShader(const TCHAR *file)
374 {
375 SetShaderCG(NULL);
376 SetShaderHLSL(NULL);
377 shader_type = D3D_SHADER_NONE;
378 if(file!=NULL &&
379 (lstrlen(file)>3 && _tcsncicmp(&file[lstrlen(file)-3],TEXT(".cg"),3)==0) ||
380 (lstrlen(file)>4 && _tcsncicmp(&file[lstrlen(file)-4],TEXT(".cgp"),4)==0)){
381 return SetShaderCG(file);
382 } else {
383 return SetShaderHLSL(file);
384 }
385 }
386
checkForCgError(const char * situation)387 void CDirect3D::checkForCgError(const char *situation)
388 {
389 char buffer[4096];
390 CGerror error = cgGetError();
391 const char *string = cgGetErrorString(error);
392
393 if (error != CG_NO_ERROR) {
394 sprintf(buffer,
395 "Situation: %s\n"
396 "Error: %s\n\n"
397 "Cg compiler output...\n", situation, string);
398 MessageBoxA(0, buffer,
399 "Cg error", MB_OK|MB_ICONEXCLAMATION);
400 if (error == CG_COMPILER_ERROR) {
401 MessageBoxA(0, cgGetLastListing(cgContext),
402 "Cg compilation error", MB_OK|MB_ICONEXCLAMATION);
403 }
404 }
405 }
406
SetShaderCG(const TCHAR * file)407 bool CDirect3D::SetShaderCG(const TCHAR *file)
408 {
409 if(!cgAvailable) {
410 if(file)
411 MessageBox(NULL, TEXT("The CG runtime is unavailable, CG shaders will not run.\nConsult the snes9x readme for information on how to obtain the runtime."), TEXT("CG Error"),
412 MB_OK|MB_ICONEXCLAMATION);
413 return false;
414 }
415
416 if(!cgShader->LoadShader(file))
417 return false;
418
419 shader_type = D3D_SHADER_CG;
420
421 return true;
422 }
423
SetShaderHLSL(const TCHAR * file)424 bool CDirect3D::SetShaderHLSL(const TCHAR *file)
425 {
426 //MUDLORD: the guts
427 //Compiles a shader from files on disc
428 //Sets LUT textures to texture files in PNG format.
429
430 TCHAR folder[MAX_PATH];
431 TCHAR rubyLUTfileName[MAX_PATH];
432 TCHAR *slash;
433
434 TCHAR errorMsg[MAX_PATH + 50];
435
436 shaderTimer = 1.0f;
437 shaderTimeStart = 0;
438 shaderTimeElapsed = 0;
439
440 if(effect) {
441 effect->Release();
442 effect = NULL;
443 }
444 for(int i = 0; i < MAX_SHADER_TEXTURES; i++) {
445 if (rubyLUT[i] != NULL) {
446 rubyLUT[i]->Release();
447 rubyLUT[i] = NULL;
448 }
449 }
450 if (file == NULL || *file==TEXT('\0'))
451 return true;
452
453 CXML xml;
454
455 if(!xml.loadXmlFile(file))
456 return false;
457
458 TCHAR *lang = xml.getAttribute(TEXT("/shader"),TEXT("language"));
459
460 if(lstrcmpi(lang,TEXT("hlsl"))) {
461 _stprintf(errorMsg,TEXT("Shader language is <%s>, expected <HLSL> in file:\n%s"),lang,file);
462 MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK|MB_ICONEXCLAMATION);
463 return false;
464 }
465
466 TCHAR *shaderText = xml.getNodeContent(TEXT("/shader/source"));
467
468 if(!shaderText) {
469 _stprintf(errorMsg,TEXT("No HLSL shader program in file:\n%s"),file);
470 MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"),
471 MB_OK|MB_ICONEXCLAMATION);
472 return false;
473 }
474
475 LPD3DXBUFFER pBufferErrors = NULL;
476 #ifdef UNICODE
477 HRESULT hr = D3DXCreateEffect( pDevice,WideToCP(shaderText,CP_ACP),strlen(WideToCP(shaderText,CP_ACP)),NULL, NULL,
478 D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, NULL, &effect,
479 &pBufferErrors );
480 #else
481 HRESULT hr = D3DXCreateEffect( pDevice,shaderText,strlen(shaderText),NULL, NULL,
482 D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY, NULL, &effect,
483 &pBufferErrors );
484 #endif
485
486 if( FAILED(hr) ) {
487 _stprintf(errorMsg,TEXT("Error parsing HLSL shader file:\n%s"),file);
488 MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK|MB_ICONEXCLAMATION);
489 if(pBufferErrors) {
490 LPVOID pCompilErrors = pBufferErrors->GetBufferPointer();
491 MessageBox(NULL, (const TCHAR*)pCompilErrors, TEXT("FX Compile Error"),
492 MB_OK|MB_ICONEXCLAMATION);
493 }
494 return false;
495 }
496
497 lstrcpy(folder,file);
498 slash = _tcsrchr(folder,TEXT('\\'));
499 if(slash)
500 *(slash+1)=TEXT('\0');
501 else
502 *folder=TEXT('\0');
503 SetCurrentDirectory(S9xGetDirectoryT(DEFAULT_DIR));
504
505 for(int i = 0; i < MAX_SHADER_TEXTURES; i++) {
506 _stprintf(rubyLUTfileName, TEXT("%srubyLUT%d.png"), folder, i);
507 hr = D3DXCreateTextureFromFile(pDevice,rubyLUTfileName,&rubyLUT[i]);
508 if FAILED(hr){
509 rubyLUT[i] = NULL;
510 }
511 }
512
513 D3DXHANDLE hTech;
514 effect->FindNextValidTechnique(NULL,&hTech);
515 effect->SetTechnique( hTech );
516 shader_type = D3D_SHADER_HLSL;
517 return true;
518 }
519
SetShaderVars(bool setMatrix)520 void CDirect3D::SetShaderVars(bool setMatrix)
521 {
522 if(shader_type == D3D_SHADER_HLSL) {
523 D3DXVECTOR4 rubyTextureSize;
524 D3DXVECTOR4 rubyInputSize;
525 D3DXVECTOR4 rubyOutputSize;
526 D3DXHANDLE rubyTimer;
527
528 int shaderTime = GetTickCount();
529 shaderTimeElapsed += shaderTime - shaderTimeStart;
530 shaderTimeStart = shaderTime;
531 if(shaderTimeElapsed > 100) {
532 shaderTimeElapsed = 0;
533 shaderTimer += 0.01f;
534 }
535 rubyTextureSize.x = rubyTextureSize.y = (float)quadTextureSize;
536 rubyTextureSize.z = rubyTextureSize.w = 1.0 / quadTextureSize;
537 rubyInputSize.x = (float)afterRenderWidth;
538 rubyInputSize.y = (float)afterRenderHeight;
539 rubyInputSize.z = 1.0 / rubyInputSize.y;
540 rubyInputSize.w = 1.0 / rubyInputSize.z;
541 rubyOutputSize.x = GUI.Stretch?(float)dPresentParams.BackBufferWidth:(float)afterRenderWidth;
542 rubyOutputSize.y = GUI.Stretch?(float)dPresentParams.BackBufferHeight:(float)afterRenderHeight;
543 rubyOutputSize.z = 1.0 / rubyOutputSize.y;
544 rubyOutputSize.w = 1.0 / rubyOutputSize.x;
545 rubyTimer = effect->GetParameterByName(0, "rubyTimer");
546
547 effect->SetFloat(rubyTimer, shaderTimer);
548 effect->SetVector("rubyTextureSize", &rubyTextureSize);
549 effect->SetVector("rubyInputSize", &rubyInputSize);
550 effect->SetVector("rubyOutputSize", &rubyOutputSize);
551
552 effect->SetTexture("rubyTexture", drawSurface);
553 for(int i = 0; i < MAX_SHADER_TEXTURES; i++) {
554 char rubyLUTName[256];
555 sprintf(rubyLUTName, "rubyLUT%d", i);
556 if (rubyLUT[i] != NULL) {
557 effect->SetTexture( rubyLUTName, rubyLUT[i] );
558 }
559 }
560 }/* else if(shader_type == D3D_SHADER_CG) {
561
562 D3DXVECTOR2 videoSize;
563 D3DXVECTOR2 textureSize;
564 D3DXVECTOR2 outputSize;
565 float frameCnt;
566 videoSize.x = (float)afterRenderWidth;
567 videoSize.y = (float)afterRenderHeight;
568 textureSize.x = textureSize.y = (float)quadTextureSize;
569 outputSize.x = GUI.Stretch?(float)dPresentParams.BackBufferWidth:(float)afterRenderWidth;
570 outputSize.y = GUI.Stretch?(float)dPresentParams.BackBufferHeight:(float)afterRenderHeight;
571 frameCnt = (float)++frameCount;
572 videoSize = textureSize;
573
574 #define setProgramUniform(program,varname,floats)\
575 {\
576 CGparameter cgp = cgGetNamedParameter(program, varname);\
577 if(cgp)\
578 cgD3D9SetUniform(cgp,floats);\
579 }\
580
581 setProgramUniform(cgFragmentProgram,"IN.video_size",&videoSize);
582 setProgramUniform(cgFragmentProgram,"IN.texture_size",&textureSize);
583 setProgramUniform(cgFragmentProgram,"IN.output_size",&outputSize);
584 setProgramUniform(cgFragmentProgram,"IN.frame_count",&frameCnt);
585
586 setProgramUniform(cgVertexProgram,"IN.video_size",&videoSize);
587 setProgramUniform(cgVertexProgram,"IN.texture_size",&textureSize);
588 setProgramUniform(cgVertexProgram,"IN.output_size",&outputSize);
589 setProgramUniform(cgVertexProgram,"IN.frame_count",&frameCnt);
590
591 if(setMatrix) {
592 D3DXMATRIX matWorld;
593 D3DXMATRIX matView;
594 D3DXMATRIX matProj;
595 D3DXMATRIX mvp;
596
597 pDevice->GetTransform(D3DTS_WORLD,&matWorld);
598 pDevice->GetTransform(D3DTS_VIEW,&matView);
599 pDevice->GetTransform(D3DTS_PROJECTION,&matProj);
600
601 mvp = matWorld * matView * matProj;
602 D3DXMatrixTranspose(&mvp,&mvp);
603
604 CGparameter cgpModelViewProj = cgGetNamedParameter(cgVertexProgram, "modelViewProj");
605
606 if(cgpModelViewProj)
607 cgD3D9SetUniformMatrix(cgpModelViewProj,&mvp);
608 }
609 }*/
610 }
611
612 /* CDirect3D::Render
613 does the actual rendering, changes the draw surface if necessary and recalculates
614 the vertex information if filter output size changes
615 IN:
616 Src - the input surface
617 */
Render(SSurface Src)618 void CDirect3D::Render(SSurface Src)
619 {
620 SSurface Dst;
621 RECT dstRect;
622 unsigned int newFilterScale;
623 D3DLOCKED_RECT lr;
624 D3DLOCKED_RECT lrConv;
625 HRESULT hr;
626
627 if(!init_done) return;
628
629 //create a new draw surface if the filter scale changes
630 //at least factor 2 so we can display unscaled hi-res images
631 newFilterScale = max(2,max(GetFilterScale(GUI.ScaleHiRes),GetFilterScale(GUI.Scale)));
632 if(newFilterScale!=filterScale) {
633 ChangeDrawSurfaceSize(newFilterScale);
634 }
635
636 if(FAILED(hr = pDevice->TestCooperativeLevel())) {
637 switch(hr) {
638 case D3DERR_DEVICELOST: //do no rendering until device is restored
639 return;
640 case D3DERR_DEVICENOTRESET: //we can reset now
641 ResetDevice();
642 return;
643 default:
644 DXTRACE_ERR_MSGBOX( TEXT("Internal driver error"), hr);
645 return;
646 }
647 }
648
649 //BlankTexture(drawSurface);
650 if(FAILED(hr = drawSurface->LockRect(0, &lr, NULL, 0))) {
651 DXTRACE_ERR_MSGBOX( TEXT("Unable to lock texture"), hr);
652 return;
653 } else {
654 Dst.Surface = (unsigned char *)lr.pBits;
655 Dst.Height = quadTextureSize;
656 Dst.Width = quadTextureSize;
657 Dst.Pitch = lr.Pitch;
658
659 RenderMethod (Src, Dst, &dstRect);
660 if(!Settings.AutoDisplayMessages) {
661 WinSetCustomDisplaySurface((void *)Dst.Surface, Dst.Pitch/2, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, GetFilterScale(CurrentScale));
662 S9xDisplayMessages ((uint16*)Dst.Surface, Dst.Pitch/2, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, GetFilterScale(CurrentScale));
663 }
664
665 drawSurface->UnlockRect(0);
666 }
667
668 if(!GUI.Stretch||GUI.AspectRatio)
669 pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
670
671 //if the output size of the render method changes we need to update the viewport
672 if(afterRenderHeight != dstRect.bottom || afterRenderWidth != dstRect.right) {
673 afterRenderHeight = dstRect.bottom;
674 afterRenderWidth = dstRect.right;
675 SetViewport();
676 }
677
678 pDevice->SetTexture(0, drawSurface);
679 pDevice->SetVertexDeclaration(vertexDeclaration);
680 pDevice->SetStreamSource(0,vertexBuffer,0,sizeof(VERTEX));
681
682 if (shader_type == D3D_SHADER_HLSL) {
683 SetShaderVars();
684 SetFiltering();
685
686 UINT passes;
687 pDevice->BeginScene();
688 hr = effect->Begin(&passes, 0);
689 for(UINT pass = 0; pass < passes; pass++ ) {
690 effect->BeginPass(pass);
691 pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);
692 effect->EndPass();
693 }
694 effect->End();
695 pDevice->EndScene();
696
697 } else {
698 if(shader_type == D3D_SHADER_CG) {
699 RECT displayRect;
700 //Get maximum rect respecting AR setting
701 displayRect=CalculateDisplayRect(dPresentParams.BackBufferWidth,dPresentParams.BackBufferHeight,
702 dPresentParams.BackBufferWidth,dPresentParams.BackBufferHeight);
703 cgShader->Render(drawSurface,
704 D3DXVECTOR2((float)quadTextureSize, (float)quadTextureSize),
705 D3DXVECTOR2((float)afterRenderWidth, (float)afterRenderHeight),
706 D3DXVECTOR2((float)(displayRect.right - displayRect.left),
707 (float)(displayRect.bottom - displayRect.top)),
708 D3DXVECTOR2((float)dPresentParams.BackBufferWidth, (float)dPresentParams.BackBufferHeight));
709 }
710
711 SetFiltering();
712
713 pDevice->SetVertexDeclaration(vertexDeclaration);
714
715 pDevice->BeginScene();
716 pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP,0,2);
717 pDevice->EndScene();
718 }
719
720 pDevice->Present(NULL, NULL, NULL, NULL);
721
722 return;
723 }
724
725 /* CDirect3D::CreateDrawSurface
726 calculates the necessary texture size (multiples of 2)
727 and creates a new texture
728 */
CreateDrawSurface()729 void CDirect3D::CreateDrawSurface()
730 {
731 unsigned int neededSize;
732 HRESULT hr;
733
734 //we need at least 512 pixels (SNES_WIDTH * 2) so we can start with that value
735 quadTextureSize = 512;
736 neededSize = SNES_WIDTH * filterScale;
737 while(quadTextureSize < neededSize)
738 quadTextureSize *=2;
739
740 if(!drawSurface) {
741 hr = pDevice->CreateTexture(
742 quadTextureSize, quadTextureSize,
743 1, // 1 level, no mipmaps
744 0, // dynamic textures can be locked
745 D3DFMT_R5G6B5,
746 D3DPOOL_MANAGED,
747 &drawSurface,
748 NULL );
749
750 if(FAILED(hr)) {
751 DXTRACE_ERR_MSGBOX(TEXT("Error while creating texture"), hr);
752 return;
753 }
754 }
755 }
756
757 /* CDirect3D::DestroyDrawSurface
758 releases the old textures (if allocated)
759 */
DestroyDrawSurface()760 void CDirect3D::DestroyDrawSurface()
761 {
762 if(drawSurface) {
763 drawSurface->Release();
764 drawSurface = NULL;
765 }
766 }
767
768 /* CDirect3D::BlankTexture
769 clears a texture (fills it with zeroes)
770 IN:
771 texture - the texture to be blanked
772 -----
773 returns true if successful, false otherwise
774 */
BlankTexture(LPDIRECT3DTEXTURE9 texture)775 bool CDirect3D::BlankTexture(LPDIRECT3DTEXTURE9 texture)
776 {
777 D3DLOCKED_RECT lr;
778 HRESULT hr;
779
780 if(FAILED(hr = texture->LockRect(0, &lr, NULL, 0))) {
781 DXTRACE_ERR_MSGBOX( TEXT("Unable to lock texture"), hr);
782 return false;
783 } else {
784 memset(lr.pBits, 0, lr.Pitch * quadTextureSize);
785 texture->UnlockRect(0);
786 return true;
787 }
788 }
789
790 /* CDirect3D::ChangeDrawSurfaceSize
791 changes the draw surface size: deletes the old textures, creates a new texture
792 and calculate new vertices
793 IN:
794 scale - the scale that has to fit into the textures
795 -----
796 returns true if successful, false otherwise
797 */
ChangeDrawSurfaceSize(unsigned int scale)798 bool CDirect3D::ChangeDrawSurfaceSize(unsigned int scale)
799 {
800 filterScale = scale;
801
802 if(pDevice) {
803 DestroyDrawSurface();
804 CreateDrawSurface();
805 SetupVertices();
806 return true;
807 }
808 return false;
809 }
810
811 /* CDirect3D::SetupVertices
812 calculates the vertex coordinates
813 (respecting the stretch and aspect ratio settings)
814 */
SetupVertices()815 void CDirect3D::SetupVertices()
816 {
817 void *pLockedVertexBuffer;
818
819 float tX = (float)afterRenderWidth / (float)quadTextureSize;
820 float tY = (float)afterRenderHeight / (float)quadTextureSize;
821
822 vertexStream[0] = VERTEX(0.0f,0.0f,0.0f,0.0f,tY,0.0f,0.0f);
823 vertexStream[1] = VERTEX(0.0f,1.0f,0.0f,0.0f,0.0f,0.0f,0.0f);
824 vertexStream[2] = VERTEX(1.0f,0.0f,0.0f,tX,tY,0.0f,0.0f);
825 vertexStream[3] = VERTEX(1.0f,1.0f,0.0f,tX,0.0f,0.0f,0.0f);
826 for(int i=0;i<4;i++) {
827 vertexStream[i].x -= 0.5f / (float)dPresentParams.BackBufferWidth;
828 vertexStream[i].y += 0.5f / (float)dPresentParams.BackBufferHeight;
829 }
830
831
832 HRESULT hr = vertexBuffer->Lock(0,0,&pLockedVertexBuffer,NULL);
833 memcpy(pLockedVertexBuffer,vertexStream,sizeof(vertexStream));
834 vertexBuffer->Unlock();
835 }
836
SetViewport()837 void CDirect3D::SetViewport()
838 {
839 D3DXMATRIX matIdentity;
840 D3DXMATRIX matProjection;
841
842 D3DXMatrixOrthoOffCenterLH(&matProjection,0.0f,1.0f,0.0f,1.0f,0.0f,1.0f);
843 D3DXMatrixIdentity(&matIdentity);
844 pDevice->SetTransform(D3DTS_WORLD,&matIdentity);
845 pDevice->SetTransform(D3DTS_VIEW,&matIdentity);
846 pDevice->SetTransform(D3DTS_PROJECTION,&matProjection);
847
848 SetShaderVars(true);
849
850 RECT drawRect = CalculateDisplayRect(afterRenderWidth,afterRenderHeight,dPresentParams.BackBufferWidth,dPresentParams.BackBufferHeight);
851 D3DVIEWPORT9 viewport;
852 viewport.X = drawRect.left;
853 viewport.Y = drawRect.top;
854 viewport.Height = drawRect.bottom - drawRect.top;
855 viewport.Width = drawRect.right - drawRect.left;
856 viewport.MinZ = 0.0f;
857 viewport.MaxZ = 1.0f;
858 HRESULT hr = pDevice->SetViewport(&viewport);
859
860 SetupVertices();
861 }
862
863 /* CDirect3D::ChangeRenderSize
864 determines if we need to reset the device (if the size changed)
865 called with (0,0) whenever we want new settings to take effect
866 IN:
867 newWidth,newHeight - the new window size
868 -----
869 returns true if successful, false otherwise
870 */
ChangeRenderSize(unsigned int newWidth,unsigned int newHeight)871 bool CDirect3D::ChangeRenderSize(unsigned int newWidth, unsigned int newHeight)
872 {
873 if(!init_done)
874 return false;
875
876 //if we already have the desired size no change is necessary
877 //during fullscreen no changes are allowed
878 if(dPresentParams.BackBufferWidth == newWidth && dPresentParams.BackBufferHeight == newHeight)
879 return true;
880
881 if(!ResetDevice())
882 return false;
883
884 return true;
885 }
886
887 /* CDirect3D::ResetDevice
888 resets the device
889 called if surface was lost or the settings/display size require a device reset
890 -----
891 returns true if successful, false otherwise
892 */
ResetDevice()893 bool CDirect3D::ResetDevice()
894 {
895 if(!init_done) return false;
896
897 HRESULT hr;
898
899 //release prior to reset
900 DestroyDrawSurface();
901
902 if(cgAvailable) {
903 cgShader->OnLostDevice();
904 cgD3D9SetDevice(NULL);
905 }
906
907 if(effect)
908 effect->OnLostDevice();
909
910 //zero or unknown values result in the current window size/display settings
911 dPresentParams.BackBufferWidth = 0;
912 dPresentParams.BackBufferHeight = 0;
913 dPresentParams.BackBufferCount = GUI.DoubleBuffered?2:1;
914 dPresentParams.BackBufferFormat = D3DFMT_UNKNOWN;
915 dPresentParams.FullScreen_RefreshRateInHz = 0;
916 dPresentParams.Windowed = true;
917 dPresentParams.PresentationInterval = GUI.Vsync?D3DPRESENT_INTERVAL_ONE:D3DPRESENT_INTERVAL_IMMEDIATE;
918
919 if(fullscreen) {
920 dPresentParams.BackBufferWidth = GUI.FullscreenMode.width;
921 dPresentParams.BackBufferHeight = GUI.FullscreenMode.height;
922 dPresentParams.BackBufferCount = GUI.DoubleBuffered?2:1;
923 dPresentParams.Windowed = false;
924 if(GUI.FullscreenMode.depth == 32)
925 dPresentParams.BackBufferFormat = D3DFMT_X8R8G8B8;
926 else
927 dPresentParams.BackBufferFormat = D3DFMT_R5G6B5;
928 dPresentParams.FullScreen_RefreshRateInHz = GUI.FullscreenMode.rate;
929 }
930
931 if(FAILED(hr = pDevice->Reset(&dPresentParams))) {
932 DXTRACE_ERR(TEXT("Unable to reset device"), hr);
933 return false;
934 }
935
936 if(effect)
937 effect->OnResetDevice();
938
939 if(cgAvailable) {
940 cgD3D9SetDevice(pDevice);
941 cgShader->OnResetDevice();
942 }
943
944 pDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
945
946 pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
947
948 //recreate the surface
949 CreateDrawSurface();
950
951 SetViewport();
952
953 return true;
954 }
955
956 /* CDirect3D::SetSnes9xColorFormat
957 sets the color format to 16bit (since the texture is always 16bit)
958 no depth conversion is necessary (done by D3D)
959 */
SetSnes9xColorFormat()960 void CDirect3D::SetSnes9xColorFormat()
961 {
962 GUI.ScreenDepth = 16;
963 GUI.BlueShift = 0;
964 GUI.GreenShift = 6;
965 GUI.RedShift = 11;
966 S9xSetRenderPixelFormat (RGB565);
967 S9xBlit2xSaIFilterInit();
968 S9xBlitHQ2xFilterInit();
969 GUI.NeedDepthConvert = FALSE;
970 GUI.DepthConverted = TRUE;
971 return;
972 }
973
974 /* CDirect3D::SetFullscreen
975 enables/disables fullscreen mode
976 IN:
977 fullscreen - determines if fullscreen is enabled/disabled
978 -----
979 returns true if successful, false otherwise
980 */
SetFullscreen(bool fullscreen)981 bool CDirect3D::SetFullscreen(bool fullscreen)
982 {
983 if(!init_done)
984 return false;
985
986 if(this->fullscreen==fullscreen)
987 return true;
988
989 this->fullscreen = fullscreen;
990 if(!ResetDevice())
991 return false;
992
993 //present here to get a fullscreen blank even if no rendering is done
994 pDevice->Present(NULL,NULL,NULL,NULL);
995
996 return true;
997 }
998
999 /* CDirect3D::EnumModes
1000 enumerates possible display modes (only 16 and 32 bit) and fills the vector
1001 IN:
1002 modeVector - pointer to the mode vector
1003 */
EnumModes(std::vector<dMode> * modeVector)1004 void CDirect3D::EnumModes(std::vector<dMode> *modeVector)
1005 {
1006 D3DDISPLAYMODE d3dMode;
1007 int modeCount,index;
1008 dMode mode;
1009
1010 if(!init_done)
1011 return;
1012
1013 //enumerate 32bit modes
1014 modeCount = pD3D->GetAdapterModeCount(D3DADAPTER_DEFAULT,D3DFMT_X8R8G8B8);
1015 for(int i=0;i<modeCount;i++) {
1016 if(pD3D->EnumAdapterModes(D3DADAPTER_DEFAULT,D3DFMT_X8R8G8B8,i,&d3dMode)==D3D_OK) {
1017 mode.width = d3dMode.Width;
1018 mode.height = d3dMode.Height;
1019 mode.rate = d3dMode.RefreshRate;
1020 mode.depth = 32;
1021
1022 modeVector->push_back(mode);
1023 }
1024
1025 }
1026
1027 //enumerate 16bit modes
1028 modeCount = pD3D->GetAdapterModeCount(D3DADAPTER_DEFAULT,D3DFMT_R5G6B5);
1029 for(int i=0;i<modeCount;i++) {
1030 if(pD3D->EnumAdapterModes(D3DADAPTER_DEFAULT,D3DFMT_R5G6B5,i,&d3dMode)==D3D_OK) {
1031 mode.width = d3dMode.Width;
1032 mode.height = d3dMode.Height;
1033 mode.rate = d3dMode.RefreshRate;
1034 mode.depth = 16;
1035
1036 modeVector->push_back(mode);
1037 }
1038
1039 }
1040 }
1041
1042 /* CDirect3D::ApplyDisplayChanges
1043 calls changerendersize to apply new display settings
1044 -----
1045 returns true if successful, false otherwise
1046 */
ApplyDisplayChanges(void)1047 bool CDirect3D::ApplyDisplayChanges(void)
1048 {
1049 if(GUI.shaderEnabled && GUI.D3DshaderFileName)
1050 SetShader(GUI.D3DshaderFileName);
1051 else
1052 SetShader(NULL);
1053
1054 return ChangeRenderSize(0,0);
1055 }
1056
SetFiltering()1057 void CDirect3D::SetFiltering()
1058 {
1059 if(GUI.BilinearFilter) {
1060 pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
1061 pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
1062 } else {
1063 pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT);
1064 pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);
1065 }
1066 }
1067