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