1 #ifndef NO_OGL
2
3 //OpenGL library
4 #pragma comment( lib, "OpenGL32" )
5
6 // MFC
7 #include "stdafx.h"
8
9 //GUI
10 #include "MainWnd.h"
11 #include "FullscreenSettings.h"
12
13 // Internals
14 #include "../System.h"
15 #include "../gba/GBA.h"
16 #include "../gba/Globals.h"
17 #include "../Util.h"
18 #include "../gb/gbGlobals.h"
19 #include "../common/memgzio.h"
20
21 //Math
22 #include <cmath>
23 #include <sys/stat.h>
24
25 // OpenGL
26 #include <gl/GL.h> // main include file
27 #include <GL/glu.h>
28 #include "glFont.h"
29 #include <gl/glext.h>
30 typedef BOOL (APIENTRY *PFNWGLSWAPINTERVALFARPROC)( int );
31 extern int Init_2xSaI(u32);
32 extern void winlog(const char *,...);
33 extern int systemSpeed;
34
35 #ifdef _DEBUG
36 #define new DEBUG_NEW
37 #undef THIS_FILE
38 static char THIS_FILE[] = __FILE__;
39 #endif
40
41 #ifdef MMX
42 extern "C" bool cpu_mmx;
43 extern bool detectMMX();
44 #endif
45
46
47 class OpenGLDisplay : public IDisplay {
48 private:
49 HDC hDC;
50 HGLRC hRC;
51 GLuint texture;
52 int width,height;
53 float size;
54 u8 *filterData;
55 RECT destRect;
56 bool failed;
57 GLFONT font;
58 int pitch;
59 u8 *data;
60 DWORD currentAdapter;
61
62 void initializeMatrices( int w, int h );
63 bool initializeTexture( int w, int h );
64 void updateFiltering( int value );
65 void setVSync( int interval = 1 );
66 void calculateDestRect( int w, int h );
67 void initializeFont();
68
69 public:
70 OpenGLDisplay();
71 virtual ~OpenGLDisplay();
getType()72 virtual DISPLAY_TYPE getType() { return OPENGL; };
73
74 virtual void EnableOpenGL();
75 virtual void DisableOpenGL();
76 virtual bool initialize();
77 virtual void cleanup();
78 virtual void clear();
79 virtual void render();
80 virtual bool changeRenderSize( int w, int h );
81 virtual void resize( int w, int h );
82 virtual void setOption( const char *, int );
83 virtual bool selectFullScreenMode( VIDEO_MODE &mode );
84 };
85
86 #include "gzglfont.h"
87 //Load GL font
initializeFont()88 void OpenGLDisplay::initializeFont()
89 {
90 int ret;
91 z_stream strm;
92 char *buf = (char *)malloc(GZGLFONT_SIZE);
93
94 /* allocate inflate state */
95 strm.zalloc = Z_NULL;
96 strm.zfree = Z_NULL;
97 strm.opaque = Z_NULL;
98 strm.avail_in = 0;
99 strm.next_in = Z_NULL;
100 ret = inflateInit2(&strm, 16+MAX_WBITS);
101 if (ret != Z_OK)
102 return;
103
104 strm.avail_in = sizeof(gzglfont);
105 strm.next_in = gzglfont;
106 strm.avail_out = GZGLFONT_SIZE;
107 strm.next_out = (Bytef *)buf;
108 ret = inflate(&strm, Z_NO_FLUSH);
109 if (ret==Z_STREAM_END)
110 {
111 glGenTextures( 1, &texture );
112 glFontCreate(&font, (char *)buf, texture);
113 texture=0;
114 }
115 free(buf);
116 (void)inflateEnd(&strm);
117 }
118
119 //OpenGL class constructor
OpenGLDisplay()120 OpenGLDisplay::OpenGLDisplay()
121 {
122 hDC = NULL;
123 hRC = NULL;
124 texture = 0;
125 width = 0;
126 height = 0;
127 size = 0.0f;
128 failed = false;
129 filterData = NULL;
130 currentAdapter = 0;
131 }
132
133 //OpenGL class destroyer
~OpenGLDisplay()134 OpenGLDisplay::~OpenGLDisplay()
135 {
136 cleanup();
137 }
138
139 //Set OpenGL PFD and contexts
EnableOpenGL()140 void OpenGLDisplay::EnableOpenGL()
141 {
142 PIXELFORMATDESCRIPTOR pfd;
143 // get the device context (DC)
144 hDC = GetDC( theApp.m_pMainWnd->GetSafeHwnd() );
145 // set the pixel format for the DC
146 ZeroMemory( &pfd, sizeof( pfd ) );
147 pfd.nSize = sizeof( pfd );
148 pfd.nVersion = 1;
149 pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
150 pfd.iPixelType = PFD_TYPE_RGBA;
151 pfd.cColorBits = 24;
152 pfd.cDepthBits = 16;
153 pfd.iLayerType = PFD_MAIN_PLANE;
154 SetPixelFormat (GetDC (theApp.m_pMainWnd->GetSafeHwnd()), ChoosePixelFormat ( GetDC (theApp.m_pMainWnd->GetSafeHwnd()), &pfd), &pfd);
155 wglMakeCurrent (GetDC (theApp.m_pMainWnd->GetSafeHwnd()), wglCreateContext(GetDC (theApp.m_pMainWnd->GetSafeHwnd()) ) );
156 }
157 //Remove contexts
DisableOpenGL()158 void OpenGLDisplay::DisableOpenGL()
159 {
160 wglMakeCurrent( NULL, NULL );
161 wglDeleteContext( hRC );
162 ReleaseDC( theApp.m_pMainWnd->GetSafeHwnd(), hDC );
163 }
164 //Remove resources used
cleanup()165 void OpenGLDisplay::cleanup()
166 {
167 if(texture != 0) {
168 glDeleteTextures(1, &texture);
169 texture = 0;
170 }
171
172 DisableOpenGL();
173 if(filterData) {
174 free(filterData);
175 filterData = NULL;
176 }
177 width = 0;
178 height = 0;
179 size = 0.0f;
180
181 DISPLAY_DEVICE dev;
182 ZeroMemory( &dev, sizeof(dev) );
183 dev.cb = sizeof(dev);
184 EnumDisplayDevices( NULL, currentAdapter, &dev, 0 );
185 // restore default video mode
186 ChangeDisplaySettingsEx( dev.DeviceName, NULL, NULL, 0, NULL );
187 }
188
189 //init renderer
initialize()190 bool OpenGLDisplay::initialize()
191 {
192 switch( theApp.cartridgeType )
193 {
194 case IMAGE_GBA:
195 sizeX = 240;
196 sizeY = 160;
197 break;
198 case IMAGE_GB:
199 if ( gbBorderOn )
200 {
201 sizeX = 256;
202 sizeY = 224;
203 }
204 else
205 {
206 sizeX = 160;
207 sizeY = 144;
208 }
209 break;
210 }
211
212
213 switch(videoOption)
214 {
215 case VIDEO_1X:
216 surfaceSizeX = sizeX;
217 surfaceSizeY = sizeY;
218 break;
219 case VIDEO_2X:
220 surfaceSizeX = sizeX * 2;
221 surfaceSizeY = sizeY * 2;
222 break;
223 case VIDEO_3X:
224 surfaceSizeX = sizeX * 3;
225 surfaceSizeY = sizeY * 3;
226 break;
227 case VIDEO_4X:
228 surfaceSizeX = sizeX * 4;
229 surfaceSizeY = sizeY * 4;
230 break;
231 case VIDEO_5X:
232 surfaceSizeX = sizeX * 5;
233 surfaceSizeY = sizeY * 5;
234 break;
235 case VIDEO_6X:
236 surfaceSizeX = sizeX * 6;
237 surfaceSizeY = sizeY * 6;
238 break;
239 case VIDEO_320x240:
240 case VIDEO_640x480:
241 case VIDEO_800x600:
242 case VIDEO_1024x768:
243 case VIDEO_1280x1024:
244 case VIDEO_OTHER:
245 {
246 if( fullScreenStretch ) {
247 surfaceSizeX = fsWidth;
248 surfaceSizeY = fsHeight;
249 } else {
250 float scaleX = (float)fsWidth / (float)sizeX;
251 float scaleY = (float)fsHeight / (float)sizeY;
252 float min = ( scaleX < scaleY ) ? scaleX : scaleY;
253 if( maxScale )
254 min = ( min > (float)maxScale ) ? (float)maxScale : min;
255 surfaceSizeX = (int)((float)sizeX * min);
256 surfaceSizeY = (int)((float)sizeY * min);
257 }
258 }
259 break;
260 }
261
262 theApp.rect.left = 0;
263 theApp.rect.top = 0;
264 theApp.rect.right = sizeX;
265 theApp.rect.bottom = sizeY;
266
267 theApp.dest.left = 0;
268 theApp.dest.top = 0;
269 theApp.dest.right = surfaceSizeX;
270 theApp.dest.bottom = surfaceSizeY;
271
272 DWORD style = WS_POPUP | WS_VISIBLE;
273 DWORD styleEx = 0;
274
275 if( videoOption <= VIDEO_6X )
276 style |= WS_OVERLAPPEDWINDOW;
277 else
278 styleEx = 0;
279
280 if( videoOption <= VIDEO_6X )
281 AdjustWindowRectEx( &theApp.dest, style, TRUE, styleEx );
282 else
283 AdjustWindowRectEx( &theApp.dest, style, FALSE, styleEx );
284
285 int winSizeX = theApp.dest.right - theApp.dest.left;
286 int winSizeY = theApp.dest.bottom - theApp.dest.top;
287 int x = 0, y = 0;
288
289 if( videoOption <= VIDEO_6X ) {
290 x = windowPositionX;
291 y = windowPositionY;
292 } else {
293 winSizeX = fsWidth;
294 winSizeY = fsHeight;
295 }
296
297
298
299 theApp.updateMenuBar();
300
301 theApp.adjustDestRect();
302
303 currentAdapter = fsAdapter;
304 DISPLAY_DEVICE dev;
305 ZeroMemory( &dev, sizeof(dev) );
306 dev.cb = sizeof(dev);
307 EnumDisplayDevices( NULL, currentAdapter, &dev, 0 );
308 if( videoOption >= VIDEO_320x240 ) {
309 // enter full screen mode
310 DEVMODE mode;
311 ZeroMemory( &mode, sizeof(mode) );
312 mode.dmSize = sizeof(mode);
313 mode.dmBitsPerPel = fsColorDepth;
314 mode.dmPelsWidth = fsWidth;
315 mode.dmPelsHeight = fsHeight;
316 mode.dmDisplayFrequency = fsFrequency;
317 mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;
318 LONG ret = ChangeDisplaySettingsEx( dev.DeviceName, &mode, NULL, CDS_FULLSCREEN, NULL );
319 if( ret != DISP_CHANGE_SUCCESSFUL ) {
320 systemMessage( 0, "Can not change display mode!" );
321 failed = true;
322 }
323 } else {
324 // restore default mode
325 ChangeDisplaySettingsEx( dev.DeviceName, NULL, NULL, 0, NULL );
326 }
327
328 EnableOpenGL();
329 initializeFont();
330 glPushAttrib( GL_ENABLE_BIT );
331 glDisable( GL_DEPTH_TEST );
332 glDisable( GL_CULL_FACE );
333 glEnable( GL_TEXTURE_2D );
334 glEnable(GL_BLEND);
335 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
336
337 initializeMatrices( surfaceSizeX, surfaceSizeY );
338
339 setVSync( vsync && !gba_joybus_active );
340
341 #ifdef MMX
342 if(!disableMMX)
343 cpu_mmx = theApp.detectMMX();
344 else
345 cpu_mmx = 0;
346 #endif
347
348 systemRedShift = 19;
349 systemGreenShift = 11;
350 systemBlueShift = 3;
351 systemColorDepth = 32;
352 fsColorDepth = 32;
353
354 Init_2xSaI(32);
355
356 utilUpdateSystemColorMaps(theApp.cartridgeType == IMAGE_GBA && gbColorOption == 1);
357 theApp.updateFilter();
358 theApp.updateIFB();
359 pitch = filterWidth * (systemColorDepth>>3) + 4;
360 data = pix + ( sizeX + 1 ) * 4;
361
362 if(failed)
363 return false;
364
365 return true;
366 }
367
368 //clear colour buffer
clear()369 void OpenGLDisplay::clear()
370 {
371 glClearColor(0.0,0.0,0.0,1.0);
372 glClear( GL_COLOR_BUFFER_BIT );
373 }
374
375 //main render func
render()376 void OpenGLDisplay::render()
377 {
378 clear();
379
380 pitch = filterWidth * (systemColorDepth>>3) + 4;
381 data = pix + ( sizeX + 1 ) * 4;
382
383 // apply pixel filter
384 if(theApp.filterFunction) {
385 data = filterData;
386 theApp.filterFunction(
387 pix + pitch,
388 pitch,
389 (u8*)theApp.delta,
390 (u8*)filterData,
391 width * 4 ,
392 filterWidth,
393 filterHeight);
394 }
395
396 // Texturemap complete texture to surface
397 // so we have free scaling and antialiasing
398
399 if( theApp.filterFunction ) {
400 glPixelStorei( GL_UNPACK_ROW_LENGTH, width);
401 } else {
402 glPixelStorei( GL_UNPACK_ROW_LENGTH, sizeX + 1 );
403 }
404 glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,GL_BGRA,GL_UNSIGNED_BYTE,data );
405
406
407 glBegin( GL_QUADS );
408
409 glTexCoord2f( 0.0f, 0.0f );
410 glVertex3i( 0, 0, 0 );
411
412 glTexCoord2f( (float)(width) / size, 0.0f );
413 glVertex3i( surfaceSizeX, 0, 0 );
414
415 glTexCoord2f( (float)(width) / size, (float)(height) / size );
416 glVertex3i( surfaceSizeX, surfaceSizeY, 0 );
417
418 glTexCoord2f( 0.0f, (float)(height) / size );
419 glVertex3i( 0, surfaceSizeY, 0 );
420 glEnd();
421
422
423 if( showSpeed ) { // && ( videoOption > VIDEO_6X ) ) {
424 char buffer[30];
425 if( showSpeed == 1 ) {
426 sprintf( buffer, "%3d%%", systemSpeed );
427 } else {
428 sprintf( buffer, "%3d%%(%d, %d fps)", systemSpeed, systemFrameSkip, showRenderedFrames );
429 }
430 glFontBegin(&font);
431 glPushMatrix();
432 float fontscale = (float)surfaceSizeX / 100.0f;
433 glScalef(fontscale, fontscale, fontscale);
434 glColor4f(1.0f, 0.25f, 0.25f, 1.0f);
435 glFontTextOut(buffer, (surfaceSizeX-(strlen(buffer)*11))/(fontscale*2), (surfaceSizeY-20)/fontscale, 0);
436 glPopMatrix();
437 glFontEnd();
438 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
439 glBindTexture( GL_TEXTURE_2D, texture );
440 }
441 if( screenMessage ) {
442 if( ( ( GetTickCount() - theApp.screenMessageTime ) < 3000 ) && !disableStatusMessages ) {
443 glFontBegin(&font);
444 glPushMatrix();
445
446 float fontscale = (float)surfaceSizeX / 100.0f;
447 glScalef(fontscale, fontscale, fontscale);
448 glColor4f(1.0f, 0.25f, 0.25f, 1.0f);
449 glFontTextOut((char *)((const char *)theApp.screenMessageBuffer), (surfaceSizeX-(theApp.screenMessageBuffer.GetLength()*11))/(fontscale*2), (surfaceSizeY-40)/fontscale, 0);
450 glPopMatrix();
451 glFontEnd();
452 glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
453 glBindTexture( GL_TEXTURE_2D, texture );
454 } else {
455 screenMessage = false;
456 }
457 }
458
459 glFlush();
460 SwapBuffers( hDC );
461 // since OpenGL draws on the back buffer,
462 // we have to swap it to the front buffer to see the content
463
464 }
465
466 //resize screen
resize(int w,int h)467 void OpenGLDisplay::resize( int w, int h )
468 {
469 initializeMatrices( w, h );
470 }
471
472 //update filtering methods
updateFiltering(int value)473 void OpenGLDisplay::updateFiltering( int value )
474 {
475 switch( value )
476 {
477 case 0:
478 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
479 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
480 break;
481 case 1:
482 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
483 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
484 break;
485 }
486
487 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
488 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
489 }
490
491 //init projection matrixes and viewports
initializeMatrices(int w,int h)492 void OpenGLDisplay::initializeMatrices( int w, int h )
493 {
494 if( fullScreenStretch ) {
495 glViewport( 0, 0, w, h );
496 } else {
497 calculateDestRect( w, h );
498 glViewport(
499 destRect.left,
500 destRect.top,
501 destRect.right - destRect.left,
502 destRect.bottom - destRect.top );
503 }
504
505 glMatrixMode( GL_PROJECTION );
506 glLoadIdentity();
507 glOrtho(
508 /* left */ 1.0f,
509 /* right */ (GLdouble)(w - 1),
510 /* bottom */ (GLdouble)(h - 1),
511 /* top */ 1.0f,
512 0.0f,
513 1.0f );
514
515 glMatrixMode(GL_MODELVIEW);
516 glLoadIdentity();
517
518 }
519
520 //init font texture
initializeTexture(int w,int h)521 bool OpenGLDisplay::initializeTexture( int w, int h )
522 {
523 // size = 2^n
524 // w = 24 > size = 256 = 2^8
525 // w = 255 > size = 256 = 2^8
526 // w = 256 > size = 512 = 2^9
527 // w = 300 > size = 512 = 2^9
528 // OpenGL textures have to be square and a power of 2
529 // We could use methods that allow tex's to not be powers of two
530 // but that requires extra OGL extensions
531
532 float n1 = log10( (float)w ) / log10( 2.0f );
533 float n2 = log10( (float)h ) / log10( 2.0f );
534 float n = ( n1 > n2 ) ? n1 : n2;
535
536 if( ((float)((int)n)) != n ) {
537 // round up
538 n = ((float)((int)n)) + 1.0f;
539 }
540
541 size = pow( 2.0f, n );
542
543 glGenTextures( 1, &texture );
544 glBindTexture( GL_TEXTURE_2D, texture );
545 updateFiltering( glFilter );
546
547 glTexImage2D(
548 GL_TEXTURE_2D,
549 0,
550 GL_RGBA,
551 (GLsizei)size,
552 (GLsizei)size,
553 0,
554 GL_RGBA,
555 GL_UNSIGNED_BYTE,
556 NULL );
557
558 width = w;
559 height = h;
560
561 //return ( glGetError() == GL_NO_ERROR) ? true : false;
562 // Workaround: We usually get GL_INVALID_VALUE, but somehow it works nevertheless
563 // In consequence, we must not treat it as an error or else the app behaves as if an error occured.
564 // This in the end results in theApp->input not being created = no input when switching from D3D to OGL
565 return true;
566 }
567
568 //turn vsync on or off
setVSync(int interval)569 void OpenGLDisplay::setVSync( int interval )
570 {
571 const char *extensions = (const char *)glGetString( GL_EXTENSIONS );
572
573 if( strstr( extensions, "WGL_EXT_swap_control" ) == 0 ) {
574 winlog( "Error: WGL_EXT_swap_control extension not supported on your computer.\n" );
575 return;
576 } else {
577 PFNWGLSWAPINTERVALFARPROC wglSwapIntervalEXT = NULL;
578 wglSwapIntervalEXT = (PFNWGLSWAPINTERVALFARPROC)wglGetProcAddress( "wglSwapIntervalEXT" );
579 if( wglSwapIntervalEXT ) {
580 wglSwapIntervalEXT( interval );
581 }
582 }
583 }
584
585 //change render size for fonts and filter data
changeRenderSize(int w,int h)586 bool OpenGLDisplay::changeRenderSize( int w, int h )
587 {
588 if( (width != w) || (height != h) ) {
589 if( texture != 0 ) {
590 glDeleteTextures( 1, &texture );
591 texture = 0;
592 }
593
594 if( !initializeTexture( w, h ) ) {
595 failed = true;
596 return false;
597 }
598 if (filterData)
599 free(filterData);
600 filterData = (u8 *)malloc(4*w*h);
601 }
602
603 return true;
604 }
605
606 //calculate RECTs
calculateDestRect(int w,int h)607 void OpenGLDisplay::calculateDestRect( int w, int h )
608 {
609 float scaleX = (float)w / (float)width;
610 float scaleY = (float)h / (float)height;
611 float min = (scaleX < scaleY) ? scaleX : scaleY;
612 if( maxScale && (min > maxScale) ) {
613 min = (float)maxScale;
614 }
615 destRect.left = 0;
616 destRect.top = 0;
617 destRect.right = (LONG)(width * min);
618 destRect.bottom = (LONG)(height * min);
619 if( destRect.right != w ) {
620 LONG diff = (w - destRect.right) / 2;
621 destRect.left += diff;
622 destRect.right += diff;
623 }
624 if( destRect.bottom != h ) {
625 LONG diff = (h - destRect.bottom) / 2;
626 destRect.top += diff;
627 destRect.bottom += diff;
628 }
629
630 }
631
632 //config options
setOption(const char * option,int value)633 void OpenGLDisplay::setOption( const char *option, int value )
634 {
635 if( !_tcscmp( option, _T("vsync") ) ) {
636 setVSync( value );
637 }
638
639 if( !_tcscmp( option, _T("glFilter") ) ) {
640 updateFiltering( value );
641 }
642
643 if( !_tcscmp( option, _T("maxScale") ) ) {
644 initializeMatrices( theApp.dest.right-theApp.dest.left, theApp.dest.bottom-theApp.dest.top );
645 }
646
647 if( !_tcscmp( option, _T("fullScreenStretch") ) ) {
648 initializeMatrices( theApp.dest.right-theApp.dest.left, theApp.dest.bottom-theApp.dest.top );
649 }
650 }
651
652 //set fullscreen mode
selectFullScreenMode(VIDEO_MODE & mode)653 bool OpenGLDisplay::selectFullScreenMode( VIDEO_MODE &mode )
654 {
655 FullscreenSettings dlg;
656 dlg.setAPI( this->getType() );
657 INT_PTR ret = dlg.DoModal();
658 if( ret == IDOK ) {
659 mode.adapter = dlg.m_device;
660 switch( dlg.m_colorDepth )
661 {
662 case 30:
663 // TODO: support
664 return false;
665 break;
666 case 24:
667 mode.bitDepth = 32;
668 break;
669 case 16:
670 case 15:
671 mode.bitDepth = 16;
672 break;
673 }
674 mode.width = dlg.m_width;
675 mode.height = dlg.m_height;
676 mode.frequency = dlg.m_refreshRate;
677 return true;
678 } else {
679 return false;
680 }
681 }
682
683
newOpenGLDisplay()684 IDisplay *newOpenGLDisplay()
685 {
686 return new OpenGLDisplay();
687 }
688
689 #endif // #ifndef NO_OGL
690