1 /*****************************************************************************\
2      Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
3                 This file is licensed under the Snes9x License.
4    For further information, consult the LICENSE file in the root directory.
5 \*****************************************************************************/
6 
7 #include "COpenGL.h"
8 #include "win32_display.h"
9 #include "../snes9x.h"
10 #include "../gfx.h"
11 #include "../display.h"
12 #include "wsnes9x.h"
13 #include <msxml2.h>
14 
15 #include "../filter/hq2x.h"
16 #include "../filter/2xsai.h"
17 
18 
COpenGL(void)19 COpenGL::COpenGL(void)
20 {
21 	hDC = NULL;
22 	hRC = NULL;
23 	hWnd = NULL;
24 	drawTexture = 0;
25 	initDone = false;
26 	afterRenderWidth = 0;
27 	afterRenderHeight = 0;
28     outTextureWidth = 0;
29     outTextureHeight = 0;
30 	fullscreen = false;
31 	shaderFunctionsLoaded = false;
32 	shader_type = OGL_SHADER_NONE;
33 	pboFunctionsLoaded = false;
34 	shaderProgram = 0;
35     vertexShader = 0;
36     fragmentShader = 0;
37 	cgContext = NULL;
38 	cgVertexProgram = cgFragmentProgram = NULL;
39 	cgAvailable = false;
40 	frameCount = 0;
41 	cgShader = NULL;
42     glslShader = NULL;
43 	*currentShaderFile = _T('\0');
44 }
45 
~COpenGL(void)46 COpenGL::~COpenGL(void)
47 {
48 	DeInitialize();
49 }
50 
Initialize(HWND hWnd)51 bool COpenGL::Initialize(HWND hWnd)
52 {
53 	int pfdIndex;
54 	RECT windowRect;
55 
56 	this->hWnd = hWnd;
57 	this->hDC = GetDC(hWnd);
58 
59 	PIXELFORMATDESCRIPTOR pfd=
60 	{
61 		sizeof(PIXELFORMATDESCRIPTOR),					// Size Of This Pixel Format Descriptor
62 		1,												// Version Number
63 		PFD_DRAW_TO_WINDOW |							// Format Must Support Window
64 		PFD_SUPPORT_OPENGL |							// Format Must Support OpenGL
65 		PFD_DOUBLEBUFFER,								// Must Support Double Buffering
66 		PFD_TYPE_RGBA,									// Request An RGBA Format
67 		16,												// Select Our Color Depth
68 		0, 0, 0, 0, 0, 0,								// Color Bits Ignored
69 		0,												// No Alpha Buffer
70 		0,												// Shift Bit Ignored
71 		0,												// No Accumulation Buffer
72 		0, 0, 0, 0,										// Accumulation Bits Ignored
73 		16,												// 16Bit Z-Buffer (Depth Buffer)
74 		0,												// No Stencil Buffer
75 		0,												// No Auxiliary Buffer
76 		PFD_MAIN_PLANE,									// Main Drawing Layer
77 		0,												// Reserved
78 		0, 0, 0											// Layer Masks Ignored
79 	};
80 	PIXELFORMATDESCRIPTOR pfdSel;
81 
82 	if(!(pfdIndex=ChoosePixelFormat(hDC,&pfd))) {
83 		DeInitialize();
84 		return false;
85 	}
86 	if(!SetPixelFormat(hDC,pfdIndex,&pfd)) {
87 		DeInitialize();
88 		return false;
89 	}
90 	if(!(hRC=wglCreateContext(hDC))) {
91 		DeInitialize();
92 		return false;
93 	}
94 	if(!wglMakeCurrent(hDC,hRC)) {
95 		DeInitialize();
96 		return false;
97 	}
98 
99     ogl_LoadFunctions();
100 
101 	LoadPBOFunctions();
102 
103 	wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress( "wglSwapIntervalEXT" );
104 
105 	glEnableClientState(GL_VERTEX_ARRAY);
106     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
107 	glEnable(GL_BLEND);
108 	glEnable(GL_TEXTURE_2D);
109 
110 	glMatrixMode (GL_PROJECTION);
111     glLoadIdentity ();
112     glOrtho (0.0, 1.0, 0.0, 1.0, -1, 1);
113 
114 	glVertexPointer(2, GL_FLOAT, 0, vertices);
115 	glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
116 
117 	cgAvailable = loadCgFunctions();
118 	if(cgAvailable) {
119 		cgContext = cgCreateContext();
120 		cgShader = new CGLCG(cgContext);
121 	}
122 
123     if (ShaderAvailable() && NPOTAvailable()) {
124         glslShader = new GLSLShader();
125     }
126 
127 	ApplyDisplayChanges();
128 
129 	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
130 	glClear(GL_COLOR_BUFFER_BIT);
131 	SwapBuffers(hDC);
132 
133 	initDone = true;
134 	return true;
135 }
136 
DeInitialize()137 void COpenGL::DeInitialize()
138 {
139 	initDone = false;
140 	SetShaders(NULL);
141 	DestroyDrawSurface();
142 	wglMakeCurrent(NULL,NULL);
143 	if(hRC) {
144 		wglDeleteContext(hRC);
145 		hRC = NULL;
146 	}
147 	if(hDC) {
148 		ReleaseDC(hWnd,hDC);
149 		hDC = NULL;
150 	}
151 	hWnd = NULL;
152 	afterRenderWidth = 0;
153 	afterRenderHeight = 0;
154     outTextureWidth = 0;
155     outTextureHeight = 0;
156 	shaderFunctionsLoaded = false;
157 	shader_type = OGL_SHADER_NONE;
158     if (glslShader) {
159         delete glslShader;
160         glslShader = NULL;
161     }
162 	if(cgShader) {
163 		delete cgShader;
164 		cgShader = NULL;
165 	}
166 	if(cgAvailable)
167 		unloadCgLibrary();
168 	cgAvailable = false;
169 }
170 
CreateDrawSurface(unsigned int width,unsigned int height)171 void COpenGL::CreateDrawSurface(unsigned int width, unsigned int height)
172 {
173 	HRESULT hr;
174 
175 	if (!NPOTAvailable()) {
176 		unsigned int neededSize = max(width, height);
177 		//we need at least 512 pixels (SNES_WIDTH * 2) so we can start with that value
178 		unsigned int quadTextureSize = 512;
179 		while (quadTextureSize < neededSize)
180 			quadTextureSize *= 2;
181 		width = height = quadTextureSize;
182 	}
183 
184 	if(!drawTexture) {
185 		outTextureWidth = width;
186 		outTextureHeight = height;
187 		glGenTextures(1,&drawTexture);
188 		glBindTexture(GL_TEXTURE_2D,drawTexture);
189 		glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA, outTextureWidth, outTextureHeight,0,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,NULL);
190 		if(pboFunctionsLoaded) {
191 			glGenBuffers(1,&drawBuffer);
192 			glBindBuffer(GL_PIXEL_UNPACK_BUFFER,drawBuffer);
193 			glBufferData(GL_PIXEL_UNPACK_BUFFER, outTextureWidth*outTextureHeight *2,NULL,GL_STREAM_DRAW);
194 			glBindBuffer(GL_PIXEL_UNPACK_BUFFER,0);
195 		} else {
196 			noPboBuffer = new BYTE[outTextureWidth*outTextureHeight *2];
197 		}
198 
199 		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
200 		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
201 	}
202 }
203 
DestroyDrawSurface()204 void COpenGL::DestroyDrawSurface()
205 {
206 	if(drawTexture) {
207 		glDeleteTextures(1,&drawTexture);
208 		drawTexture = NULL;
209 	}
210 	if(drawBuffer) {
211 		glDeleteBuffers(1,&drawBuffer);
212 		drawBuffer = NULL;
213 	}
214 	if(noPboBuffer) {
215 		delete [] noPboBuffer;
216 		noPboBuffer = NULL;
217 	}
218 }
219 
ChangeDrawSurfaceSize(unsigned int width,unsigned int height)220 bool COpenGL::ChangeDrawSurfaceSize(unsigned int width, unsigned int height)
221 {
222 	DestroyDrawSurface();
223 	CreateDrawSurface(width, height);
224 	SetupVertices();
225 	return true;
226 }
227 
SetupVertices()228 void COpenGL::SetupVertices()
229 {
230 	vertices[0] = 0.0f;
231     vertices[1] = 0.0f;
232 	vertices[2] = 1.0f;
233     vertices[3] = 0.0f;
234 	vertices[4] = 1.0f;
235 	vertices[5] = 1.0f;
236     vertices[6] = 0.0f;
237 	vertices[7] = 1.0f;
238 
239 	float tX = (float)afterRenderWidth / (float)outTextureWidth;
240 	float tY = (float)afterRenderHeight / (float)outTextureHeight;
241 
242 	texcoords[0] = 0.0f;
243     texcoords[1] = tY;
244     texcoords[2] = tX;
245     texcoords[3] = tY;
246     texcoords[4] = tX;
247     texcoords[5] = 0.0f;
248     texcoords[6] = 0.0f;
249     texcoords[7] = 0.0f;
250 	glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
251 }
252 
wOGLViewportCallback(int source_width,int source_height,int viewport_x,int viewport_y,int viewport_width,int viewport_height,int * out_dst_x,int * out_dst_y,int * out_dst_width,int * out_dst_height)253 void wOGLViewportCallback(int source_width, int source_height,
254 	int viewport_x, int viewport_y,
255 	int viewport_width, int viewport_height,
256 	int *out_dst_x, int *out_dst_y,
257 	int *out_dst_width, int *out_dst_height)
258 {
259 	/* get window size here instead of using viewport passed in - we limited the viewport before the glsl render
260 	   call already, this is simply to position smaller outputs correctly in the actual viewport
261 	 */
262 	RECT windowSize;
263 	GetClientRect(GUI.hWnd, &windowSize);
264 	RECT displayRect = CalculateDisplayRect(source_width, source_height, windowSize.right, windowSize.bottom);
265 	*out_dst_x = displayRect.left;
266 	*out_dst_y = displayRect.top;
267 	*out_dst_width = displayRect.right - displayRect.left;
268 	*out_dst_height = displayRect.bottom - displayRect.top;
269 }
270 
Render(SSurface Src)271 void COpenGL::Render(SSurface Src)
272 {
273 	SSurface Dst;
274 	RECT dstRect;
275 	unsigned int newFilterScale;
276 	GLenum error;
277 
278 	if(!initDone) return;
279 
280 	//create a new draw surface if the filter scale changes
281 	dstRect = GetFilterOutputSize(Src);
282 	if(outTextureWidth != dstRect.right || outTextureHeight != dstRect.bottom)
283 		ChangeDrawSurfaceSize(dstRect.right, dstRect.bottom);
284 
285 	if(pboFunctionsLoaded) {
286 		glBindBuffer(GL_PIXEL_UNPACK_BUFFER, drawBuffer);
287 		Dst.Surface = (unsigned char *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
288 	} else {
289 		Dst.Surface = noPboBuffer;
290 	}
291 	Dst.Height = outTextureHeight;
292 	Dst.Width = outTextureWidth;
293 	Dst.Pitch = outTextureWidth * 2;
294 
295 	RenderMethod (Src, Dst, &dstRect);
296 	if(!Settings.AutoDisplayMessages) {
297 		WinSetCustomDisplaySurface((void *)Dst.Surface, Dst.Pitch/2, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, GetFilterScale(CurrentScale));
298 		S9xDisplayMessages ((uint16*)Dst.Surface, Dst.Pitch/2, dstRect.right-dstRect.left, dstRect.bottom-dstRect.top, GetFilterScale(CurrentScale));
299 	}
300 
301 	if(pboFunctionsLoaded)
302 		glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
303 
304 	if(afterRenderHeight != dstRect.bottom || afterRenderWidth != dstRect.right) {
305 		afterRenderHeight = dstRect.bottom;
306 		afterRenderWidth = dstRect.right;
307 
308 		ChangeRenderSize(0,0);
309 	}
310 
311 	glBindTexture(GL_TEXTURE_2D,drawTexture);
312 	glPixelStorei(GL_UNPACK_ROW_LENGTH, outTextureWidth);
313 	glTexSubImage2D (GL_TEXTURE_2D,0,0,0,dstRect.right-dstRect.left,dstRect.bottom-dstRect.top,GL_RGB,GL_UNSIGNED_SHORT_5_6_5,pboFunctionsLoaded?0:noPboBuffer);
314 
315 	if(pboFunctionsLoaded)
316 		glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
317 
318 	RECT windowSize, displayRect;
319 	GetClientRect(hWnd, &windowSize);
320 	//Get maximum rect respecting AR setting
321 	displayRect = CalculateDisplayRect(afterRenderWidth, afterRenderHeight, windowSize.right, windowSize.bottom);
322 
323 	// GLSL class does all the rendering, no output needed
324 	if (shader_type == OGL_SHADER_GLSL) {
325 		glslShader->render(drawTexture, afterRenderWidth, afterRenderHeight, displayRect.left, displayRect.top, displayRect.right - displayRect.left, displayRect.bottom - displayRect.top, wOGLViewportCallback);
326 	}
327 	else { // for CG shaders and old style .shader files the last pass is done here, same as no shader
328 		if(shader_type == OGL_SHADER_CG) {
329 			xySize inputSize = { (float)afterRenderWidth, (float)afterRenderHeight };
330 			xySize xywindowSize = { (double)windowSize.right, (double)windowSize.bottom };
331 			xySize viewportSize = { (double)(displayRect.right - displayRect.left),
332 				                    (double)(displayRect.bottom - displayRect.top) };
333 			xySize textureSize = { (double)outTextureWidth, (double)outTextureHeight };
334 			cgShader->Render(drawTexture, textureSize, inputSize, viewportSize, xywindowSize);
335 		}
336 		else if (shader_type == OGL_SHADER_GLSL_OLD) {
337 			GLint location;
338 
339 			float inputSize[2] = { (float)afterRenderWidth, (float)afterRenderHeight };
340 			float outputSize[2] = { (float)(GUI.Stretch ? windowSize.right : afterRenderWidth),
341 				(float)(GUI.Stretch ? windowSize.bottom : afterRenderHeight) };
342 			float textureSize[2] = { (float)outTextureWidth, (float)outTextureHeight };
343 			float frameCnt = (float)++frameCount;
344 			location = glGetUniformLocation(shaderProgram, "rubyInputSize");
345 			glUniform2fv(location, 1, inputSize);
346 
347 			location = glGetUniformLocation(shaderProgram, "rubyOutputSize");
348 			glUniform2fv(location, 1, outputSize);
349 
350 			location = glGetUniformLocation(shaderProgram, "rubyTextureSize");
351 			glUniform2fv(location, 1, textureSize);
352 		}
353 		if (Settings.BilinearFilter) {
354 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
355 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
356 		}
357 		else {
358 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
359 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
360 		}
361 
362 		glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
363 		glClear(GL_COLOR_BUFFER_BIT);
364 		glDrawArrays(GL_QUADS, 0, 4);
365     }
366 
367 	glFlush();
368 	SwapBuffers(hDC);
369 	if (GUI.ReduceInputLag)
370 		glFinish();
371 }
372 
ChangeRenderSize(unsigned int newWidth,unsigned int newHeight)373 bool COpenGL::ChangeRenderSize(unsigned int newWidth, unsigned int newHeight)
374 {
375 	RECT displayRect, windowSize;
376 	if(newWidth==0||newHeight==0) {
377 		GetClientRect(hWnd,&windowSize);
378 		newWidth = windowSize.right;
379 		newHeight = windowSize.bottom;
380 	}
381 	displayRect=CalculateDisplayRect(afterRenderWidth,afterRenderHeight,newWidth,newHeight);
382 	glViewport(displayRect.left,newHeight-displayRect.bottom,displayRect.right-displayRect.left,displayRect.bottom-displayRect.top);
383 	SetupVertices();
384 	return true;
385 }
386 
SetSwapInterval(int frames)387 void COpenGL::SetSwapInterval(int frames)
388 {
389     if (wglSwapIntervalEXT)
390         wglSwapIntervalEXT(frames);
391 }
392 
ApplyDisplayChanges(void)393 bool COpenGL::ApplyDisplayChanges(void)
394 {
395 	if(wglSwapIntervalEXT) {
396 		wglSwapIntervalEXT(GUI.Vsync?1:0);
397 	}
398 	if(GUI.shaderEnabled && GUI.OGLshaderFileName)
399 		SetShaders(GUI.OGLshaderFileName);
400 	else
401 		SetShaders(NULL);
402 
403 	ChangeRenderSize(0,0);
404 	return true;
405 }
406 
SetFullscreen(bool fullscreen)407 bool COpenGL::SetFullscreen(bool fullscreen)
408 {
409 	if(!initDone)
410 		return false;
411 
412 	if(this->fullscreen==fullscreen)
413 		return true;
414 
415 	this->fullscreen = fullscreen;
416 
417 	if(fullscreen) {
418 		DEVMODE dmScreenSettings={0};
419 		dmScreenSettings.dmSize=sizeof(dmScreenSettings);
420 		dmScreenSettings.dmPelsWidth	= GUI.FullscreenMode.width;
421 		dmScreenSettings.dmPelsHeight	= GUI.FullscreenMode.height;
422 		dmScreenSettings.dmBitsPerPel	= GUI.FullscreenMode.depth;
423 		dmScreenSettings.dmDisplayFrequency = GUI.FullscreenMode.rate;
424 		dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT|DM_DISPLAYFREQUENCY;
425 		if(ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) {
426 			this->fullscreen = false;
427 			return false;
428 		}
429 		ChangeRenderSize(GUI.FullscreenMode.width,GUI.FullscreenMode.height);
430 	} else {
431 		ChangeDisplaySettings(NULL,0);
432 	}
433 
434 
435 
436 	return true;
437 }
438 
SetSnes9xColorFormat()439 void COpenGL::SetSnes9xColorFormat()
440 {
441 	GUI.ScreenDepth = 16;
442 	GUI.BlueShift = 0;
443 	GUI.GreenShift = 6;
444 	GUI.RedShift = 11;
445 	S9xBlit2xSaIFilterInit();
446 	S9xBlitHQ2xFilterInit();
447 	GUI.NeedDepthConvert = FALSE;
448 	GUI.DepthConverted = TRUE;
449 	return;
450 }
451 
EnumModes(std::vector<dMode> * modeVector)452 void COpenGL::EnumModes(std::vector<dMode> *modeVector)
453 {
454 	DISPLAY_DEVICE dd;
455 	dd.cb = sizeof(dd);
456 	DWORD dev = 0;
457 	int iMode = 0;
458 	dMode mode;
459 
460 	while (EnumDisplayDevices(0, dev, &dd, 0))
461 	{
462 		if (!(dd.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER) && (dd.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE))
463 		{
464 			DEVMODE dm;
465 			memset(&dm, 0, sizeof(dm));
466 			dm.dmSize = sizeof(dm);
467 			iMode = 0;
468 			while(EnumDisplaySettings(dd.DeviceName,iMode,&dm)) {
469 				if(dm.dmBitsPerPel>=16) {
470 					mode.width = dm.dmPelsWidth;
471 					mode.height = dm.dmPelsHeight;
472 					mode.rate = dm.dmDisplayFrequency;
473 					mode.depth = dm.dmBitsPerPel;
474 					modeVector->push_back(mode);
475 				}
476 				iMode++;
477 			}
478 		}
479 		dev++;
480 	}
481 }
482 
LoadPBOFunctions()483 bool COpenGL::LoadPBOFunctions()
484 {
485 	if(GUI.OGLdisablePBOs)
486 		return false;
487 
488 	if(pboFunctionsLoaded)
489 		return true;
490 
491 	const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
492 
493 	if(extensions && strstr(extensions, "pixel_buffer_object")) {
494 
495 		if(glGenBuffers && glBindBuffer && glBufferData && glDeleteBuffers && glMapBuffer) {
496 			pboFunctionsLoaded = true;
497 		}
498 
499 	}
500 	return pboFunctionsLoaded;
501 }
502 
LoadShaderFunctions()503 bool COpenGL::LoadShaderFunctions()
504 {
505 	if(shaderFunctionsLoaded)
506 		return true;
507 
508 	const char *extensions = (const char *) glGetString(GL_EXTENSIONS);
509 
510     if(extensions && strstr(extensions, "fragment_program")) {
511 
512 		if(glCreateProgram      &&
513 		   glCreateShader       &&
514 		   glCompileShader      &&
515 		   glDeleteShader       &&
516 		   glDeleteProgram      &&
517 		   glAttachShader       &&
518 		   glDetachShader       &&
519 		   glLinkProgram        &&
520 		   glUseProgram         &&
521 		   glShaderSource       &&
522 		   glGetUniformLocation &&
523 		   glUniform2fv) {
524 			   shaderFunctionsLoaded = true;
525 		}
526 	}
527 	return shaderFunctionsLoaded;
528 }
529 
SetShaders(const TCHAR * file)530 bool COpenGL::SetShaders(const TCHAR *file)
531 {
532 	if (file && lstrcmp(file, currentShaderFile) == 0)
533 		return true;
534 
535 	SetShadersCG(NULL);
536 	SetShadersGLSL(NULL);
537 	SetShadersGLSL_OLD(NULL);
538 	shader_type = OGL_SHADER_NONE;
539 
540 	if (file) {
541 		lstrcpy(currentShaderFile, file);
542 		if ((lstrlen(file) > 3 && _tcsncicmp(&file[lstrlen(file) - 3], TEXT(".cg"), 3) == 0) ||
543 			(lstrlen(file) > 4 && _tcsncicmp(&file[lstrlen(file) - 4], TEXT(".cgp"), 4) == 0)) {
544 			return SetShadersCG(file);
545 		}
546 		else if ((lstrlen(file) > 7 && _tcsncicmp(&file[lstrlen(file) - 7], TEXT(".shader"), 7) == 0)) {
547 			return SetShadersGLSL_OLD(file);
548 		}
549 		else {
550 			return SetShadersGLSL(file);
551 		}
552 	}
553 
554 	*currentShaderFile = _T('\0');
555 
556 	return true;
557 }
558 
checkForCgError(const char * situation)559 void COpenGL::checkForCgError(const char *situation)
560 {
561 	char buffer[4096];
562 	CGerror error = cgGetError();
563 	const char *string = cgGetErrorString(error);
564 
565 	if (error != CG_NO_ERROR) {
566 		sprintf(buffer,
567 			  "Situation: %s\n"
568 			  "Error: %s\n\n"
569 			  "Cg compiler output...\n", situation, string);
570 		MessageBoxA(0, buffer,
571 				  "Cg error", MB_OK|MB_ICONEXCLAMATION);
572 		if (error == CG_COMPILER_ERROR) {
573 			MessageBoxA(0, cgGetLastListing(cgContext),
574 					  "Cg compilation error", MB_OK|MB_ICONEXCLAMATION);
575 		}
576 	}
577 }
578 
SetShadersCG(const TCHAR * file)579 bool COpenGL::SetShadersCG(const TCHAR *file)
580 {
581 	if(!cgAvailable) {
582 		if(file)
583 			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"),
584 				MB_OK|MB_ICONEXCLAMATION);
585         return false;
586     }
587 
588 	if(!cgShader->LoadShader(file))
589 		return false;
590 
591 	shader_type = OGL_SHADER_CG;
592 
593 	return true;
594 }
595 
SetShadersGLSL(const TCHAR * glslFileName)596 bool COpenGL::SetShadersGLSL(const TCHAR *glslFileName)
597 {
598 	if (!glslShader)
599 		return false;
600 
601 	glslShader->destroy();
602 
603     if (!glslFileName)
604         return false;
605 
606     if(!glslShader->load_shader(_tToChar(glslFileName))) {
607         return false;
608     }
609 
610 	shader_type = OGL_SHADER_GLSL;
611 
612     return true;
613 }
614 
SetShadersGLSL_OLD(const TCHAR * glslFileName)615 bool COpenGL::SetShadersGLSL_OLD(const TCHAR *glslFileName)
616 {
617 	char *fragment = NULL, *vertex = NULL;
618 	IXMLDOMDocument * pXMLDoc = NULL;
619 	IXMLDOMElement * pXDE = NULL;
620 	IXMLDOMNode * pXDN = NULL;
621 	HRESULT hr;
622 	BSTR queryString, nodeContent;
623 
624 	TCHAR errorMsg[MAX_PATH + 50];
625 
626 	if (fragmentShader) {
627 		glDetachShader(shaderProgram, fragmentShader);
628 		glDeleteShader(fragmentShader);
629 		fragmentShader = 0;
630 	}
631 	if (vertexShader) {
632 		glDetachShader(shaderProgram, vertexShader);
633 		glDeleteShader(vertexShader);
634 		vertexShader = 0;
635 	}
636 	if (shaderProgram) {
637 		glUseProgram(0);
638 		glDeleteProgram(shaderProgram);
639 		shaderProgram = 0;
640 	}
641 
642 	if (glslFileName == NULL || *glslFileName == TEXT('\0'))
643 		return true;
644 
645 	if (!LoadShaderFunctions()) {
646 		MessageBox(NULL, TEXT("Unable to load OpenGL shader functions"), TEXT("Shader Loading Error"),
647 			MB_OK | MB_ICONEXCLAMATION);
648 		return false;
649 	}
650 
651 	hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pXMLDoc));
652 
653 	if (FAILED(hr)) {
654 		MessageBox(NULL, TEXT("Error creating XML Parser"), TEXT("Shader Loading Error"),
655 			MB_OK | MB_ICONEXCLAMATION);
656 		return false;
657 	}
658 
659 	VARIANT fileName;
660 	VARIANT_BOOL ret;
661 	fileName.vt = VT_BSTR;
662 #ifdef UNICODE
663 	fileName.bstrVal = SysAllocString(glslFileName);
664 #else
665 	wchar_t tempfilename[MAX_PATH];
666 	MultiByteToWideChar(CP_UTF8, 0, glslFileName, -1, tempfilename, MAX_PATH);
667 	fileName.bstrVal = SysAllocString(tempfilename);
668 #endif
669 	hr = pXMLDoc->load(fileName, &ret);
670 	SysFreeString(fileName.bstrVal);
671 
672 	if (FAILED(hr) || hr == S_FALSE) {
673 		_stprintf(errorMsg, TEXT("Error loading GLSL shader file:\n%s"), glslFileName);
674 		MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
675 		pXMLDoc->Release();
676 		return false;
677 	}
678 
679 	VARIANT attributeValue;
680 	BSTR attributeName;
681 
682 	hr = pXMLDoc->get_documentElement(&pXDE);
683 	if (FAILED(hr) || hr == S_FALSE) {
684 		_stprintf(errorMsg, TEXT("Error loading root element from file:\n%s"), glslFileName);
685 		MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
686 		pXMLDoc->Release();
687 		return false;
688 	}
689 
690 	attributeName = SysAllocString(L"language");
691 	pXDE->getAttribute(attributeName, &attributeValue);
692 	SysFreeString(attributeName);
693 	pXDE->Release();
694 
695 	if (attributeValue.vt != VT_BSTR || lstrcmpiW(attributeValue.bstrVal, L"glsl")) {
696 		_stprintf(errorMsg, TEXT("Shader language is <%s>, expected <GLSL> in file:\n%s"), attributeValue.bstrVal, glslFileName);
697 		MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
698 		if (attributeValue.vt == VT_BSTR) SysFreeString(attributeValue.bstrVal);
699 		pXMLDoc->Release();
700 		return false;
701 	}
702 	if (attributeValue.vt == VT_BSTR) SysFreeString(attributeValue.bstrVal);
703 
704 	queryString = SysAllocString(L"/shader/fragment");
705 	hr = pXMLDoc->selectSingleNode(queryString, &pXDN);
706 	SysFreeString(queryString);
707 
708 	if (hr == S_OK) {
709 		hr = pXDN->get_text(&nodeContent);
710 		if (hr == S_OK) {
711 			int requiredChars = WideCharToMultiByte(CP_ACP, 0, nodeContent, -1, fragment, 0, NULL, NULL);
712 			fragment = new char[requiredChars];
713 			WideCharToMultiByte(CP_UTF8, 0, nodeContent, -1, fragment, requiredChars, NULL, NULL);
714 		}
715 		SysFreeString(nodeContent);
716 		pXDN->Release();
717 		pXDN = NULL;
718 	}
719 
720 	queryString = SysAllocString(L"/shader/vertex");
721 	hr = pXMLDoc->selectSingleNode(queryString, &pXDN);
722 	SysFreeString(queryString);
723 
724 	if (hr == S_OK) {
725 		hr = pXDN->get_text(&nodeContent);
726 		if (hr == S_OK) {
727 			int requiredChars = WideCharToMultiByte(CP_ACP, 0, nodeContent, -1, vertex, 0, NULL, NULL);
728 			vertex = new char[requiredChars];
729 			WideCharToMultiByte(CP_UTF8, 0, nodeContent, -1, vertex, requiredChars, NULL, NULL);
730 		}
731 		SysFreeString(nodeContent);
732 		pXDN->Release();
733 		pXDN = NULL;
734 	}
735 
736 	pXMLDoc->Release();
737 
738 	if (!fragment && !vertex) {
739 		_stprintf(errorMsg, TEXT("No vertex or fragment program in file:\n%s"), glslFileName);
740 		MessageBox(NULL, errorMsg, TEXT("Shader Loading Error"), MB_OK | MB_ICONEXCLAMATION);
741 		return false;
742 	}
743 
744 	shaderProgram = glCreateProgram();
745 	if (vertex) {
746 		vertexShader = glCreateShader(GL_VERTEX_SHADER);
747 		glShaderSource(vertexShader, 1, (const GLchar **)&vertex, NULL);
748 		glCompileShader(vertexShader);
749 		glAttachShader(shaderProgram, vertexShader);
750 		delete[] vertex;
751 	}
752 	if (fragment) {
753 		fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
754 		glShaderSource(fragmentShader, 1, (const GLchar **)&fragment, NULL);
755 		glCompileShader(fragmentShader);
756 		glAttachShader(shaderProgram, fragmentShader);
757 		delete[] fragment;
758 	}
759 
760 	glLinkProgram(shaderProgram);
761 	glUseProgram(shaderProgram);
762 
763 	shader_type = OGL_SHADER_GLSL_OLD;
764 
765 	return true;
766 }
767 
ShaderAvailable()768 bool COpenGL::ShaderAvailable()
769 {
770     const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
771 
772     if (!extensions)
773         return false;
774 
775     if (strstr(extensions, "fragment_program") ||
776         strstr(extensions, "fragment_shader"))
777     {
778         return true;
779     }
780 
781     return false;
782 }
783 
NPOTAvailable()784 bool COpenGL::NPOTAvailable()
785 {
786     const char *extensions = (const char *)glGetString(GL_EXTENSIONS);
787     const char *version = (const char *)glGetString(GL_VERSION);
788 
789     if (!extensions)
790         return false;
791 
792     int glVersionMajor = 0;
793     glVersionMajor = atoi (version);
794 
795     if (glVersionMajor >= 2)
796         return true;
797 
798     if (strstr(extensions, "non_power_of_two") ||
799         strstr(extensions, "npot"))
800     {
801         return true;
802     }
803 
804     return false;
805 }
806