snes9x/macosx/mac-render.mm

1740 lines
42 KiB
Plaintext

/*****************************************************************************\
Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
This file is licensed under the Snes9x License.
For further information, consult the LICENSE file in the root directory.
\*****************************************************************************/
/***********************************************************************************
SNES9X for Mac OS (c) Copyright John Stiles
Snes9x for Mac OS X
(c) Copyright 2001 - 2011 zones
(c) Copyright 2002 - 2005 107
(c) Copyright 2002 PB1400c
(c) Copyright 2004 Alexander and Sander
(c) Copyright 2004 - 2005 Steven Seeger
(c) Copyright 2005 Ryan Vogt
***********************************************************************************/
#include "snes9x.h"
#include "memmap.h"
#include "apu.h"
#include "display.h"
#include "blit.h"
#include <OpenGL/OpenGL.h>
#include <OpenGL/CGLRenderers.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
#include <AGL/agl.h>
#include <sys/time.h>
#include "mac-prefix.h"
#include "mac-cheatfinder.h"
#include "mac-coreimage.h"
#include "mac-os.h"
#include "mac-screenshot.h"
#include "mac-render.h"
typedef void (* Blitter) (uint8 *, int, uint8 *, int, int, int);
static OSStatus BlitMPGLTask (void *);
static OSStatus PrepareMPBlitGL (void);
static void S9xInitFullScreen (void);
static void S9xDeinitFullScreen (void);
static void S9xInitWindowMode (void);
static void S9xDeinitWindowMode (void);
static void S9xInitOpenGLFullScreen (void);
static void S9xDeinitOpenGLFullScreen (void);
static void S9xInitOpenGLWindowMode (void);
static void S9xDeinitOpenGLWindowMode (void);
static void S9xInitBlitGL (void);
static void S9xDeinitBlitGL (void);
static void S9xInitOpenGLContext (void);
static void S9xDeinitOpenGLContext (void);
static void S9xInitCoreImage (void);
static void S9xDeinitCoreImage (void);
static void S9xPutImageOpenGL (int, int);
static void S9xPutImageBlitGL (int, int);
static void S9xPutImageBlitGL2 (int, int);
static void GLMakeScreenMesh (GLfloat *, int, int);
static void GLMakeTextureMesh (GLfloat *, int, int, float, float);
static void GLPrepareTexture (bool8, int, int, int, int, int, int);
static inline void RenderBlitScreen (Blitter, int, int, int, int, int, uint16 *);
#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
static void SetBestDisplayMode (int, int);
#endif
enum
{
kMPBlitFrame = 1,
kMPBlitDone,
kMPBlitNone
};
enum
{
kGL256256 = 0,
kGL256512,
kGL512256,
kGL512512,
kGLBlit2x,
kGLBlit3x,
kGLBlit4x,
kGLNTS256,
kGLNTS512,
kGLNumTextures
};
enum
{
kSC2xNormal = 0,
kSC2xExtend,
kSC2xNHiRes,
kSC2xEHiRes,
kSC2xNInter,
kSC2xEInter,
kSC3xNormal,
kSC3xExtend,
kSC3xNHiRes,
kSC3xEHiRes,
kSCNTNormal,
kSCNTExtend,
kSCNumTextures
};
enum
{
kSCMeshX = 10,
kSCMeshY = 9
};
typedef struct
{
Blitter blitFn;
int nx;
int srcWidth;
int srcHeight;
int copyWidth;
int copyHeight;
uint16 *gfxBuffer;
} MPData;
typedef struct
{
GLint internal_format;
GLint format;
GLint type;
GLenum target;
GLuint textures[kGLNumTextures];
GLfloat vertex[kGLNumTextures][8];
GLint texW[kGLNumTextures];
GLint texH[kGLNumTextures];
GLboolean rangeExt;
GLint storage_hint;
GLint storage_apple;
GLfloat agp_texturing;
} OpenGLData;
static uint16 *gfxScreen[2],
*snesScreenA,
*snesScreenB;
static uint8 *blitGLBuffer;
static CGDirectDisplayID gGameDisplayID;
static MPTaskID taskID = NULL;
static MPQueueID notificationQueue = NULL,
taskQueue = NULL;
static MPSemaphoreID readySemaphore = NULL;
static MPData *mpBlit = NULL;
static OpenGLData OpenGL;
static CGLContextObj glContext;
static AGLContext agContext;
static CGLPixelFormatObj cglpix;
static AGLPixelFormat aglpix;
static GLint glSwapInterval = 0;
static GLint agSwapInterval = 0;
#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
static CFDictionaryRef oldDisplayMode;
#else
static CGDisplayModeRef oldDisplayModeRef;
#endif
static CGImageRef cgGameImage = NULL,
cgBlitImage = NULL;
static int whichBuf = 0;
static int textureNum = 0;
static int prevBlitWidth, prevBlitHeight;
static int imageWidth[2], imageHeight[2];
static int nx = 2;
static GLfloat *scTexArray[kSCNumTextures];
static GLfloat *scScnArray;
static struct timeval bencht1, bencht2;
static const int ntsc_width = SNES_NTSC_OUT_WIDTH(SNES_WIDTH); // 602
void InitGraphics (void)
{
int safemarginbytes = (520 * 520 - 512 * 512) * 2;
snesScreenA = (uint16 *) calloc( 520 * 520 * 2, 1);
snesScreenB = (uint16 *) calloc( 520 * 520 * 2, 1);
blitGLBuffer = (uint8 *) calloc(1024 * 1024 * 2, 1);
gfxScreen[0] = snesScreenA + (safemarginbytes >> 2);
gfxScreen[1] = snesScreenB + (safemarginbytes >> 2);
GFX.Pitch = 512 * 2;
GFX.Screen = gfxScreen[0];
if (!snesScreenA || !snesScreenB || !blitGLBuffer)
QuitWithFatalError(@"render 01");
if (!S9xBlitFilterInit() |
!S9xBlit2xSaIFilterInit() |
!S9xBlitHQ2xFilterInit() |
!S9xBlitNTSCFilterInit())
QuitWithFatalError(@"render 02");
switch (videoMode)
{
default:
case VIDEOMODE_NTSC_C:
case VIDEOMODE_NTSC_TV_C:
S9xBlitNTSCFilterSet(&snes_ntsc_composite);
break;
case VIDEOMODE_NTSC_S:
case VIDEOMODE_NTSC_TV_S:
S9xBlitNTSCFilterSet(&snes_ntsc_svideo);
break;
case VIDEOMODE_NTSC_R:
case VIDEOMODE_NTSC_TV_R:
S9xBlitNTSCFilterSet(&snes_ntsc_rgb);
break;
case VIDEOMODE_NTSC_M:
case VIDEOMODE_NTSC_TV_M:
S9xBlitNTSCFilterSet(&snes_ntsc_monochrome);
break;
}
}
void DeinitGraphics (void)
{
S9xBlitNTSCFilterDeinit();
S9xBlitHQ2xFilterDeinit();
S9xBlit2xSaIFilterDeinit();
S9xBlitFilterDeinit();
if (snesScreenA)
{
free(snesScreenA);
snesScreenA = NULL;
}
if (snesScreenB)
{
free(snesScreenB);
snesScreenB = NULL;
}
if (blitGLBuffer)
{
free(blitGLBuffer);
blitGLBuffer = NULL;
}
}
void DrawPauseScreen (CGContextRef ctx, HIRect bounds)
{
CGImageRef image;
CGRect rct;
float sh, mh, rofs, ry;
if ((IPPU.RenderedScreenWidth == 0) || (IPPU.RenderedScreenHeight == 0))
return;
sh = (float) ((IPPU.RenderedScreenHeight > 256) ? IPPU.RenderedScreenHeight : IPPU.RenderedScreenHeight * 2);
mh = (float) (SNES_HEIGHT_EXTENDED * 2);
if (drawoverscan)
{
rofs = (mh - sh) / mh;
ry = sh / mh;
}
else
if (windowExtend)
{
rofs = (mh - sh) / mh / 2.0f;
ry = sh / mh;
}
else
{
rofs = 0.0f;
ry = 1.0f;
}
image = CreateGameScreenCGImage();
if (image)
{
CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 1.0f);
CGContextFillRect(ctx, bounds);
rct = CGRectMake(0.0f, bounds.size.height * rofs, bounds.size.width, bounds.size.height * ry);
CGContextDrawImage(ctx, rct, image);
CGContextSetRGBFillColor(ctx, 0.0f, 0.0f, 0.0f, 0.5f);
CGContextFillRect(ctx, bounds);
CGImageRelease(image);
}
}
void DrawFreezeDefrostScreen (uint8 *draw)
{
const int w = SNES_WIDTH << 1, h = kMacWindowHeight;
imageWidth[0] = imageHeight[0] = 0;
imageWidth[1] = imageHeight[1] = 0;
prevBlitWidth = prevBlitHeight = 0;
if ((drawingMethod == kDrawingBlitGL) && multiprocessor)
{
MPWaitOnSemaphore(readySemaphore, kDurationForever);
printf("MP: Send dummy signal.\n");
MPNotifyQueue(taskQueue, (void *) kMPBlitNone, 0, 0);
}
if (nx < 0 && !ciFilterEnable)
{
for (int y = 0; y < h; y++)
memcpy(blitGLBuffer + y * 1024 * 2, draw + y * w * 2, w * 2);
}
else
memcpy(blitGLBuffer, draw, w * h * 2);
S9xPutImageBlitGL2(512, kMacWindowHeight);
}
void ClearGFXScreen (void)
{
memset(gfxScreen[0], 0, 512 * 512 * 2);
memset(gfxScreen[1], 0, 512 * 512 * 2);
memset(blitGLBuffer, 0, 1024 * 1024 * 2);
S9xBlitClearDelta();
imageWidth[0] = imageHeight[0] = 0;
imageWidth[1] = imageHeight[1] = 0;
prevBlitWidth = prevBlitHeight = 0;
if (fullscreen)
{
CGLSetCurrentContext(glContext);
glViewport(0, 0, glScreenW, glScreenH);
}
else
{
aglSetCurrentContext(agContext);
aglUpdateContext(agContext);
glViewport(0, 0, (GLsizei) gWindowRect.size.width, (GLsizei) gWindowRect.size.height);
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
for (int i = 0; i < 2; i++)
{
glClear(GL_COLOR_BUFFER_BIT);
if (fullscreen)
CGLFlushDrawable(glContext);
else
aglSwapBuffers(agContext);
}
}
#ifndef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
static void SetBestDisplayMode (int width, int height)
{
if (autoRes || !gl32bit)
{
CGError err;
CGDisplayModeRef mode;
CFArrayRef array;
CFStringRef pixenc, pix;
CFIndex n, i;
size_t w, h;
bool r;
pixenc = gl32bit ? CFSTR(IO32BitDirectPixels) : CFSTR(IO16BitDirectPixels);
array = CGDisplayCopyAllDisplayModes(gGameDisplayID, NULL);
n = CFArrayGetCount(array);
for (i = 0; i < n; i++)
{
mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(array, i);
w = CGDisplayModeGetWidth(mode);
h = CGDisplayModeGetHeight(mode);
pix = CGDisplayModeCopyPixelEncoding(mode);
r = CFStringCompare(pix, pixenc, 0) == kCFCompareEqualTo;
CFRelease(pix);
if (w == (size_t) width && h == (size_t) height && r)
break;
}
if (i < n)
err = CGDisplaySetDisplayMode(gGameDisplayID, mode, NULL);
CFRelease(array);
}
}
#endif
static void S9xInitFullScreen (void)
{
size_t width, height;
width = autoRes ? 640 : CGDisplayPixelsWide(gGameDisplayID);
height = autoRes ? 480 : CGDisplayPixelsHigh(gGameDisplayID);
#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
CFDictionaryRef mode;
boolean_t exactMatch;
size_t depth = gl32bit ? 32 : 16;
oldDisplayMode = CGDisplayCurrentMode(gGameDisplayID);
mode = CGDisplayBestModeForParameters(gGameDisplayID, depth, width, height, &exactMatch);
CGDisplayCapture(gGameDisplayID);
CGDisplaySwitchToMode(gGameDisplayID, mode);
#else
oldDisplayModeRef = CGDisplayCopyDisplayMode(gGameDisplayID);
CGDisplayCapture(gGameDisplayID);
SetBestDisplayMode(width, height);
#endif
CGDisplayErr cgErr;
CGDisplayCount numDisplays, maxDisplays = 32;
CGDirectDisplayID activeDisplays[32];
cgErr = CGGetActiveDisplayList(maxDisplays, activeDisplays, &numDisplays);
if (cgErr == noErr)
{
if ((macControllerOption == SNES_MOUSE) || (macControllerOption == SNES_MOUSE_SWAPPED) || (numDisplays == 1))
CGDisplayHideCursor(gGameDisplayID);
if ((macControllerOption == SNES_MOUSE) || (macControllerOption == SNES_MOUSE_SWAPPED))
{
CGDisplayMoveCursorToPoint(gGameDisplayID, CGPointMake((float) (width >> 1), (float) (height >> 1)));
CGAssociateMouseAndMouseCursorPosition(false);
}
}
}
static void S9xDeinitFullScreen (void)
{
CGAssociateMouseAndMouseCursorPosition(true);
CGDisplayShowCursor(gGameDisplayID);
#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
CGDisplaySwitchToMode(gGameDisplayID, oldDisplayMode);
#else
CGError err;
err = CGDisplaySetDisplayMode(gGameDisplayID, oldDisplayModeRef, NULL);
CGDisplayModeRelease(oldDisplayModeRef);
#endif
CGDisplayRelease(gGameDisplayID);
}
static void S9xInitWindowMode (void)
{
Rect rct;
size_t width, height;
width = CGDisplayPixelsWide(gGameDisplayID);
height = CGDisplayPixelsHigh(gGameDisplayID);
#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
CFDictionaryRef mode;
boolean_t exactMatch;
size_t depth = gl32bit ? 32 : 16;
oldDisplayMode = CGDisplayCurrentMode(gGameDisplayID);
mode = CGDisplayBestModeForParameters(gGameDisplayID, depth, width, height, &exactMatch);
if (exactMatch)
CGDisplaySwitchToMode(gGameDisplayID, mode);
#else
oldDisplayModeRef = CGDisplayCopyDisplayMode(gGameDisplayID);
SetBestDisplayMode(width, height);
#endif
// InitGameWindow();
// ShowWindow(gWindow);
//
// GetWindowBounds(gWindow, kWindowContentRgn, &rct);
// gWindowRect = CGRectMake((float) rct.left, (float) rct.top, (float) (rct.right - rct.left), (float) (rct.bottom - rct.top));
// UpdateGameWindow();
}
static void S9xDeinitWindowMode (void)
{
#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
CGDisplaySwitchToMode(gGameDisplayID, oldDisplayMode);
#else
CGError err;
err = CGDisplaySetDisplayMode(gGameDisplayID, oldDisplayModeRef, NULL);
CGDisplayModeRelease(oldDisplayModeRef);
#endif
// UpdateGameWindow();
}
static void S9xInitOpenGLFullScreen (void)
{
CGOpenGLDisplayMask displayMask;
GLint numPixelFormats;
displayMask = CGDisplayIDToOpenGLDisplayMask(gGameDisplayID);
CGLPixelFormatAttribute attribs[] = { (CGLPixelFormatAttribute) kCGLPFAFullScreen,
(CGLPixelFormatAttribute) kCGLPFADoubleBuffer,
(CGLPixelFormatAttribute) kCGLPFAAccelerated,
(CGLPixelFormatAttribute) kCGLPFANoRecovery,
(CGLPixelFormatAttribute) kCGLPFAColorSize,
(CGLPixelFormatAttribute) (gl32bit ? 32 : 16),
(CGLPixelFormatAttribute) kCGLPFADisplayMask,
(CGLPixelFormatAttribute) displayMask,
(CGLPixelFormatAttribute) 0 };
CGLChoosePixelFormat(attribs, &cglpix, &numPixelFormats);
CGLCreateContext(cglpix, NULL, &glContext);
glSwapInterval = vsync ? 1 : 0;
if (extraOptions.benchmark)
glSwapInterval = 0;
CGLSetParameter(glContext, kCGLCPSwapInterval, &glSwapInterval);
CGLSetCurrentContext(glContext);
#ifdef MAC_LEOPARD_TIGER_PANTHER_SUPPORT
CGLSetFullScreen(glContext);
#else
CGLSetFullScreenOnDisplay(glContext, CGDisplayIDToOpenGLDisplayMask(gGameDisplayID));
#endif
glScreenW = CGDisplayPixelsWide(gGameDisplayID);
glScreenH = CGDisplayPixelsHigh(gGameDisplayID);
}
static void S9xDeinitOpenGLFullScreen (void)
{
if (glContext)
{
CGLSetCurrentContext(NULL);
CGLClearDrawable(glContext);
CGLDestroyContext(glContext);
CGLDestroyPixelFormat(cglpix);
}
}
static void S9xInitOpenGLWindowMode (void)
{
GLint attribs[] = { AGL_RGBA,
AGL_DOUBLEBUFFER,
AGL_ACCELERATED,
AGL_NO_RECOVERY,
AGL_PIXEL_SIZE, gl32bit ? 32 : 16,
AGL_NONE };
aglpix = aglChoosePixelFormat(NULL, 0, attribs);
agContext = aglCreateContext(aglpix, NULL);
aglSetWindowRef(agContext, gWindow);
agSwapInterval = vsync ? 1 : 0;
if (extraOptions.benchmark)
agSwapInterval = 0;
aglSetInteger(agContext, AGL_SWAP_INTERVAL, &agSwapInterval);
aglSetCurrentContext(agContext);
aglGetCGLPixelFormat(aglpix, (void **) &cglpix);
aglGetCGLContext(agContext, (void **) &glContext);
}
static void S9xDeinitOpenGLWindowMode (void)
{
if (agContext)
{
aglSetWindowRef(agContext, NULL);
aglSetCurrentContext(NULL);
aglDestroyContext(agContext);
aglDestroyPixelFormat(aglpix);
}
}
static void S9xInitBlitGL (void)
{
if (multiprocessor)
{
printf("MP: Creating BlitGL thread.\n");
if (noErr != PrepareMPBlitGL())
multiprocessor = false;
}
}
static void S9xDeinitBlitGL (void)
{
if (multiprocessor)
{
MPNotifyQueue(taskQueue, (void *) kMPBlitDone, 0, 0);
MPWaitOnQueue(notificationQueue, NULL, NULL, NULL, kDurationForever);
MPDeleteQueue(notificationQueue);
notificationQueue = NULL;
printf("MP: Successfully received terminate signal from BlitGL thread.\n");
}
}
static void GLPrepareTexture (bool8 useRange, int texNo, int rangeOnW, int rangeOnH, int rangeOffW, int rangeOffH, int filter)
{
bool8 rangeAvailable = OpenGL.rangeExt & useRange;
OpenGL.texW[texNo] = rangeAvailable ? rangeOnW : rangeOffW;
OpenGL.texH[texNo] = rangeAvailable ? rangeOnH : rangeOffH;
OpenGL.vertex[texNo][0] = 0;
OpenGL.vertex[texNo][1] = 0;
OpenGL.vertex[texNo][2] = rangeAvailable ? rangeOnW : 1;
OpenGL.vertex[texNo][3] = 0;
OpenGL.vertex[texNo][4] = rangeAvailable ? rangeOnW : 1;
OpenGL.vertex[texNo][5] = rangeAvailable ? rangeOnH : 1;
OpenGL.vertex[texNo][6] = 0;
OpenGL.vertex[texNo][7] = rangeAvailable ? rangeOnH : 1;
glBindTexture(OpenGL.target, OpenGL.textures[texNo]);
if (rangeAvailable)
{
glTextureRangeAPPLE(OpenGL.target, OpenGL.texW[texNo] * OpenGL.texH[texNo] * 2, GFX.Screen);
glTexParameteri(OpenGL.target, GL_TEXTURE_STORAGE_HINT_APPLE, OpenGL.storage_hint);
}
glTexParameterf(OpenGL.target, GL_TEXTURE_PRIORITY, OpenGL.agp_texturing);
glTexParameteri(OpenGL.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(OpenGL.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(OpenGL.target, GL_TEXTURE_MAG_FILTER, filter);
glTexParameteri(OpenGL.target, GL_TEXTURE_MIN_FILTER, filter);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexImage2D(OpenGL.target, 0, OpenGL.internal_format, OpenGL.texW[texNo], OpenGL.texH[texNo], 0, OpenGL.format, OpenGL.type, GFX.Screen);
}
static void GLMakeScreenMesh (GLfloat *vertex3D, int meshx, int meshy)
{
GLfloat *v;
float warp;
v = vertex3D;
warp = macCurvatureWarp * 0.001f;
for (int y = 0; y < meshy; y++)
{
for (int x = 0; x <= meshx; x++)
{
float u1, v1, v2;
u1 = -1.0f + 2.0f / (float) meshx * (float) x;
v1 = -1.0f + 2.0f / (float) meshy * (float) y;
v2 = -1.0f + 2.0f / (float) meshy * (float) (y + 1);
*v++ = u1;
*v++ = v2;
*v++ = -1.0f - (u1 * u1 + v2 * v2) * warp;
*v++ = u1;
*v++ = v1;
*v++ = -1.0f - (u1 * u1 + v1 * v1) * warp;
}
}
}
static void GLMakeTextureMesh (GLfloat *vertex2D, int meshx, int meshy, float lx, float ly)
{
GLfloat *v;
v = vertex2D;
for (int y = meshy; y > 0; y--)
{
for (int x = 0; x <= meshx; x++)
{
float u1, v1, v2;
u1 = lx / (float) meshx * (float) x;
v1 = ly / (float) meshy * (float) y;
v2 = ly / (float) meshy * (float) (y - 1);
*v++ = u1;
*v++ = v2;
*v++ = u1;
*v++ = v1;
}
}
}
static void S9xInitOpenGLContext (void)
{
OpenGL.internal_format = GL_RGB5_A1;
OpenGL.format = GL_BGRA;
OpenGL.type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
OpenGL.rangeExt = gluCheckExtension((const GLubyte *) "GL_APPLE_texture_range", glGetString(GL_EXTENSIONS));
OpenGL.target = OpenGL.rangeExt ? GL_TEXTURE_RECTANGLE_EXT : GL_TEXTURE_2D;
OpenGL.storage_apple = extraOptions.glUseClientStrageApple ? 1 : 0;
OpenGL.agp_texturing = extraOptions.glUseTexturePriority ? 0.0f : 1.0f;
switch (extraOptions.glStorageHint)
{
case 1: OpenGL.storage_hint = GL_STORAGE_PRIVATE_APPLE; break;
case 2: OpenGL.storage_hint = GL_STORAGE_CACHED_APPLE; break;
case 3: OpenGL.storage_hint = GL_STORAGE_SHARED_APPLE; break;
}
if (screencurvature || videoMode >= VIDEOMODE_NTSC_C || extraOptions.glForceNoTextureRectangle)
{
OpenGL.rangeExt = false;
OpenGL.target = GL_TEXTURE_2D;
}
printf("TextureRange: %s\n", OpenGL.rangeExt ? "enable" : "disable");
glDisable(GL_BLEND);
glDisable(GL_DITHER);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_FILL);
glCullFace(GL_BACK);
glDisable(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_RECTANGLE_EXT);
glEnable(OpenGL.target);
glGenTextures(kGLNumTextures, OpenGL.textures);
glPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, OpenGL.storage_apple);
glPixelStorei(GL_UNPACK_ALIGNMENT, 8);
int filter = (videoMode == VIDEOMODE_SMOOTH) ? GL_LINEAR : GL_NEAREST;
GLPrepareTexture(true, kGL256256, SNES_WIDTH, SNES_HEIGHT_EXTENDED, 256, 256, filter);
GLPrepareTexture(true, kGL256512, SNES_WIDTH, SNES_HEIGHT_EXTENDED * 2, 256, 512, filter);
GLPrepareTexture(true, kGL512256, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED, 512, 256, filter);
GLPrepareTexture(true, kGL512512, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, 512, 512, filter);
GLPrepareTexture(true, kGLBlit2x, SNES_WIDTH * 2, SNES_HEIGHT_EXTENDED * 2, 512, 512, GL_LINEAR);
GLPrepareTexture(true, kGLBlit3x, SNES_WIDTH * 3, SNES_HEIGHT_EXTENDED * 3, 1024, 1024, GL_LINEAR);
GLPrepareTexture(true, kGLBlit4x, SNES_WIDTH * 4, SNES_HEIGHT_EXTENDED * 4, 1024, 1024, GL_LINEAR);
GLPrepareTexture(false, kGLNTS256, 1024, 256, 1024, 256, GL_LINEAR);
GLPrepareTexture(false, kGLNTS512, 1024, 512, 1024, 512, GL_LINEAR);
if (!screencurvature)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
else
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.0, 1.0, -1.0, 1.0, 0.95, 5.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
int mesh = (kSCMeshX + 1) * 2 * kSCMeshY * 2;
scTexArray[kSC2xNormal] = new GLfloat [mesh];
scTexArray[kSC2xExtend] = new GLfloat [mesh];
scTexArray[kSC2xNHiRes] = new GLfloat [mesh];
scTexArray[kSC2xEHiRes] = new GLfloat [mesh];
scTexArray[kSC2xNInter] = new GLfloat [mesh];
scTexArray[kSC2xEInter] = new GLfloat [mesh];
scTexArray[kSC3xNormal] = new GLfloat [mesh];
scTexArray[kSC3xExtend] = new GLfloat [mesh];
scTexArray[kSC3xNHiRes] = new GLfloat [mesh];
scTexArray[kSC3xEHiRes] = new GLfloat [mesh];
scTexArray[kSCNTNormal] = new GLfloat [mesh];
scTexArray[kSCNTExtend] = new GLfloat [mesh];
GLMakeTextureMesh(scTexArray[kSC2xNormal], kSCMeshX, kSCMeshY, 1.0f, 224.0f / 256.0f);
GLMakeTextureMesh(scTexArray[kSC2xExtend], kSCMeshX, kSCMeshY, 1.0f, 239.0f / 256.0f);
GLMakeTextureMesh(scTexArray[kSC2xNHiRes], kSCMeshX, kSCMeshY, 1.0f, 224.0f / 512.0f);
GLMakeTextureMesh(scTexArray[kSC2xEHiRes], kSCMeshX, kSCMeshY, 1.0f, 239.0f / 512.0f);
GLMakeTextureMesh(scTexArray[kSC2xNInter], kSCMeshX, kSCMeshY, 256.0f / 512.0f, 224.0f / 256.0f);
GLMakeTextureMesh(scTexArray[kSC2xEInter], kSCMeshX, kSCMeshY, 256.0f / 512.0f, 239.0f / 256.0f);
GLMakeTextureMesh(scTexArray[kSC3xNormal], kSCMeshX, kSCMeshY, 768.0f / 1024.0f, 672.0f / 1024.0f);
GLMakeTextureMesh(scTexArray[kSC3xExtend], kSCMeshX, kSCMeshY, 768.0f / 1024.0f, 717.0f / 1024.0f);
GLMakeTextureMesh(scTexArray[kSC3xNHiRes], kSCMeshX, kSCMeshY, 768.0f / 1024.0f, 672.0f / 2048.0f);
GLMakeTextureMesh(scTexArray[kSC3xEHiRes], kSCMeshX, kSCMeshY, 768.0f / 1024.0f, 717.0f / 2048.0f);
GLMakeTextureMesh(scTexArray[kSCNTNormal], kSCMeshX, kSCMeshY, (float) ntsc_width / 1024.0f, 224.0f / 256.0f);
GLMakeTextureMesh(scTexArray[kSCNTExtend], kSCMeshX, kSCMeshY, (float) ntsc_width / 1024.0f, 239.0f / 256.0f);
scScnArray = new GLfloat [(kSCMeshX + 1) * 2 * kSCMeshY * 3];
GLMakeScreenMesh(scScnArray, kSCMeshX, kSCMeshY);
}
if (fullscreen)
{
CGLSetCurrentContext(glContext);
glViewport(0, 0, glScreenW, glScreenH);
}
else
{
aglSetCurrentContext(agContext);
aglUpdateContext(agContext);
glViewport(0, 0, (GLsizei) gWindowRect.size.width, (GLsizei) gWindowRect.size.height);
}
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
for (int i = 0; i < 2; i++)
{
glClear(GL_COLOR_BUFFER_BIT);
if (fullscreen)
CGLFlushDrawable(glContext);
else
aglSwapBuffers(agContext);
}
}
static void S9xDeinitOpenGLContext (void)
{
if (screencurvature)
{
delete [] scTexArray[kSC2xNormal];
delete [] scTexArray[kSC2xExtend];
delete [] scTexArray[kSC2xNHiRes];
delete [] scTexArray[kSC2xEHiRes];
delete [] scTexArray[kSC2xNInter];
delete [] scTexArray[kSC2xEInter];
delete [] scTexArray[kSC3xNormal];
delete [] scTexArray[kSC3xExtend];
delete [] scTexArray[kSC3xNHiRes];
delete [] scTexArray[kSC3xEHiRes];
delete [] scTexArray[kSCNTNormal];
delete [] scTexArray[kSCNTExtend];
delete [] scScnArray;
scTexArray[kSC2xNormal] = NULL;
scTexArray[kSC2xExtend] = NULL;
scTexArray[kSC2xNHiRes] = NULL;
scTexArray[kSC2xEHiRes] = NULL;
scTexArray[kSC2xNInter] = NULL;
scTexArray[kSC2xEInter] = NULL;
scTexArray[kSC3xNormal] = NULL;
scTexArray[kSC3xExtend] = NULL;
scTexArray[kSC3xNHiRes] = NULL;
scTexArray[kSC3xEHiRes] = NULL;
scTexArray[kSCNTNormal] = NULL;
scTexArray[kSCNTExtend] = NULL;
scScnArray = NULL;
}
glDeleteTextures(kGLNumTextures, OpenGL.textures);
}
static void S9xInitCoreImage (void)
{
cgGameImage = NULL;
cgBlitImage = NULL;
InitCoreImageContext(glContext, cglpix);
}
static void S9xDeinitCoreImage (void)
{
DeinitCoreImageContext();
if (cgGameImage)
{
CGImageRelease(cgGameImage);
cgGameImage = NULL;
}
if (cgBlitImage)
{
CGImageRelease(cgBlitImage);
cgBlitImage = NULL;
}
}
void GetGameDisplay (int *w, int *h)
{
CGDisplayErr cgErr;
CGDisplayCount numDisplays, maxDisplays = 32;
CGDirectDisplayID activeDisplays[32];
CGPoint windowAt;
gGameDisplayID = CGMainDisplayID();
windowAt = CGPointMake((float) windowPos[kWindowScreen].h, (float) windowPos[kWindowScreen].v);
cgErr = CGGetDisplaysWithPoint(windowAt, maxDisplays, activeDisplays, &numDisplays);
if ((cgErr == noErr) && (numDisplays > 0))
{
for (unsigned int i = 0; i < numDisplays; i++)
{
if (activeDisplays[i] != CGMainDisplayID())
gGameDisplayID = activeDisplays[i];
}
}
if (w != NULL && h != NULL)
{
*w = CGDisplayPixelsWide(gGameDisplayID);
*h = CGDisplayPixelsHigh(gGameDisplayID);
}
}
void S9xInitDisplay (int argc, char **argv)
{
if (directDisplay)
return;
GetGameDisplay(NULL, NULL);
glScreenBounds = CGDisplayBounds(gGameDisplayID);
unlimitedCursor = CGPointMake(0.0f, 0.0f);
imageWidth[0] = imageHeight[0] = 0;
imageWidth[1] = imageHeight[1] = 0;
prevBlitWidth = prevBlitHeight = 0;
GFX.Screen = gfxScreen[0];
whichBuf = 0;
textureNum = 0;
switch (videoMode)
{
case VIDEOMODE_HQ4X:
nx = 4;
break;
case VIDEOMODE_HQ3X:
nx = 3;
break;
case VIDEOMODE_NTSC_C:
case VIDEOMODE_NTSC_S:
case VIDEOMODE_NTSC_R:
case VIDEOMODE_NTSC_M:
nx = -1;
break;
case VIDEOMODE_NTSC_TV_C:
case VIDEOMODE_NTSC_TV_S:
case VIDEOMODE_NTSC_TV_R:
case VIDEOMODE_NTSC_TV_M:
nx = -2;
break;
default:
nx = 2;
break;
}
if (fullscreen)
{
S9xInitFullScreen();
S9xInitOpenGLFullScreen();
}
else
{
S9xInitWindowMode();
S9xInitOpenGLWindowMode();
}
S9xInitOpenGLContext();
if (ciFilterEnable)
S9xInitCoreImage();
if (drawingMethod == kDrawingBlitGL)
S9xInitBlitGL();
S9xSetSoundMute(false);
Microseconds((UnsignedWide *) &lastFrame);
windowResizeCount = 1;
gettimeofday(&bencht1, NULL);
directDisplay = true;
}
void S9xDeinitDisplay (void)
{
if (!directDisplay)
return;
S9xSetSoundMute(true);
if (drawingMethod == kDrawingBlitGL)
S9xDeinitBlitGL();
if (ciFilterEnable)
S9xDeinitCoreImage();
S9xDeinitOpenGLContext();
if (fullscreen)
{
S9xDeinitOpenGLFullScreen();
S9xDeinitFullScreen();
}
else
{
S9xDeinitOpenGLWindowMode();
S9xDeinitWindowMode();
}
directDisplay = false;
}
bool8 S9xInitUpdate (void)
{
return (true);
}
bool8 S9xDeinitUpdate (int width, int height)
{
if (directDisplay)
S9xPutImage(width, height);
return (true);
}
bool8 S9xContinueUpdate (int width, int height)
{
return (true);
}
static inline void RenderBlitScreen (Blitter Fn, int x, int sW, int sH, int cW, int cH, uint16 *buf)
{
switch (x)
{
case -1:
(Fn) ((uint8 *) buf, sW * 2, blitGLBuffer, 1024 * 2, sW, sH);
break;
case -2:
if (sH > SNES_HEIGHT_EXTENDED)
(Fn) ((uint8 *) buf, sW * 2, blitGLBuffer, 1024 * 2, sW, sH);
else
{
uint8 *tmpBuffer = blitGLBuffer + (1024 * 512 * 2);
int aligned = ((ntsc_width + 2) >> 1) << 1;
(Fn) ((uint8 *) buf, sW * 2, tmpBuffer, 1024 * 2, sW, sH);
S9xBlitPixMixedTV1x2(tmpBuffer, 1024 * 2, blitGLBuffer, 1024 * 2, aligned, cH);
cH *= 2;
}
break;
default:
int dstbytes = (OpenGL.rangeExt ? cW : ((cW > 512) ? 1024 : ((cW > 256) ? 512 : 256))) * 2;
(Fn) ((uint8 *) buf, sW * 2, blitGLBuffer, dstbytes, sW, sH);
break;
}
S9xPutImageBlitGL2(cW, cH);
}
static OSStatus PrepareMPBlitGL (void)
{
OSStatus err;
mpBlit = (MPData *) MPAllocateAligned(sizeof(MPData), kMPAllocateDefaultAligned, kMPAllocateClearMask);
if (!mpBlit)
return (memFullErr);
err = MPCreateQueue(&notificationQueue);
if (err == noErr)
{
err = MPCreateQueue(&taskQueue);
if (err == noErr)
{
err = MPCreateBinarySemaphore(&readySemaphore);
if (err == noErr)
{
MPSignalSemaphore(readySemaphore);
err = MPCreateTask(BlitMPGLTask, NULL, 0, notificationQueue, NULL, NULL, 0, &taskID);
}
}
}
return (err);
}
static OSStatus BlitMPGLTask (void *parameter)
{
OSStatus err = noErr;
int32 theCommand, param1, param2;
printf("MP: Entered BlitGL thread.\n");
for (;;)
{
err = MPWaitOnQueue(taskQueue, (void **) &theCommand, (void **) &param1, (void **) &param2, kDurationForever);
if (err)
break;
if (theCommand == kMPBlitFrame)
{
RenderBlitScreen(mpBlit->blitFn, mpBlit->nx, mpBlit->srcWidth, mpBlit->srcHeight, mpBlit->copyWidth, mpBlit->copyHeight, mpBlit->gfxBuffer);
MPSignalSemaphore(readySemaphore);
}
else
if (theCommand == kMPBlitNone)
MPSignalSemaphore(readySemaphore);
else
if (theCommand == kMPBlitDone)
break;
else
{
err = userCanceledErr;
break;
}
}
MPFree(mpBlit);
MPDeleteSemaphore(readySemaphore);
MPDeleteQueue(taskQueue);
mpBlit = NULL;
readySemaphore = NULL;
taskQueue = NULL;
printf("MP: Exited BlitGL thread.\n");
return (err);
}
void S9xPutImage (int width, int height)
{
static float fps = 0.0f;
static long count = 0;
static char text[32];
if (extraOptions.benchmark)
{
uint16 *basePtr;
long delta;
size_t len;
count++;
gettimeofday(&bencht2, NULL);
delta = 1000000 * (bencht2.tv_sec - bencht1.tv_sec) + (bencht2.tv_usec - bencht1.tv_usec);
if (delta > 1000000)
{
fps = (1000000.0f * (float) count) / (float) delta;
count = 0;
gettimeofday(&bencht1, NULL);
}
sprintf(text, "%.1f", fps);
basePtr = GFX.Screen + 1;
len = strlen(text);
for (unsigned int i = 0; i < len; i++)
{
S9xDisplayChar(basePtr, text[i]);
basePtr += (8 - 1);
}
}
else
{
if (cfIsWatching)
CheatFinderDrawWatchAddr();
if (Settings.DisplayFrameRate)
{
static int drawnFrames[60] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
static int tableIndex = 0;
int frameCalc = 0;
drawnFrames[tableIndex] = skipFrames;
if (Settings.TurboMode)
{
drawnFrames[tableIndex] = (drawnFrames[tableIndex] + (macFastForwardRate / 2)) / macFastForwardRate;
if (drawnFrames[tableIndex] == 0)
drawnFrames[tableIndex] = 1;
}
tableIndex = (tableIndex + 1) % 60;
for (int i = 0; i < 60; i++)
frameCalc += drawnFrames[i];
IPPU.DisplayedRenderedFrameCount = (Memory.ROMFramesPerSecond * 60) / frameCalc;
}
}
switch (drawingMethod)
{
case kDrawingOpenGL:
S9xPutImageOpenGL(width, height);
break;
case kDrawingBlitGL:
S9xPutImageBlitGL(width, height);
break;
}
}
static void S9xPutImageOpenGL (int width, int height)
{
int orig_height = height;
if ((imageWidth[0] != width) || (imageHeight[0] != height))
windowResizeCount += 2;
if (windowResizeCount > 0)
{
if (drawoverscan && (height % SNES_HEIGHT == 0))
{
int pitch = width << 1;
int extbtm = (height > 256) ? (SNES_HEIGHT_EXTENDED << 1) : SNES_HEIGHT_EXTENDED;
uint32 *extarea = (uint32 *) ((uint8 *) GFX.Screen + height * pitch);
for (int i = 0; i < (((extbtm - height) * pitch) >> 2); i++)
extarea[i] = 0;
height = extbtm;
}
int vh = (height > 256) ? height : (height << 1);
if (fullscreen)
{
CGLSetCurrentContext(glContext);
glViewport(0, 0, glScreenW, glScreenH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (glstretch)
{
float fpw = (float) glScreenH / vh * 512.0f;
int pw = (int) (fpw + ((float) glScreenW - fpw) * (float) macAspectRatio / 10000.0);
glViewport((glScreenW - pw) >> 1, 0, pw, glScreenH);
}
else
glViewport((glScreenW - 512) >> 1, (glScreenH - vh) >> 1, 512, vh);
}
else
{
int ww = (int) gWindowRect.size.width,
wh = (int) gWindowRect.size.height;
aglSetCurrentContext(agContext);
aglUpdateContext(agContext);
glViewport(0, 0, ww, wh);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (windowExtend)
glViewport(0, ((kMacWindowHeight - vh) >> 1) * wh / kMacWindowHeight, ww, vh * wh / kMacWindowHeight);
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, width);
if (!ciFilterEnable)
{
textureNum = (width <= 256) ? ((height <= 256) ? kGL256256 : kGL256512) : ((height <= 256) ? kGL512256 : kGL512512);
OpenGL.vertex[textureNum][5] = OpenGL.vertex[textureNum][7] = OpenGL.rangeExt ? height : (vh / 512.0f);
glBindTexture(OpenGL.target, OpenGL.textures[textureNum]);
}
else
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, width, orig_height - height, orig_height, -1, 1);
if (cgGameImage)
CGImageRelease(cgGameImage);
cgGameImage = CreateGameScreenCGImage();
}
imageWidth[0] = width;
imageHeight[0] = height;
windowResizeCount--;
}
else
{
if (drawoverscan)
height = (height > 256) ? (SNES_HEIGHT_EXTENDED << 1) : SNES_HEIGHT_EXTENDED;
}
if (!ciFilterEnable)
{
glTexSubImage2D(OpenGL.target, 0, 0, 0, OpenGL.texW[textureNum], OpenGL.texH[textureNum], OpenGL.format, OpenGL.type, GFX.Screen);
if (!screencurvature)
{
glBegin(GL_QUADS);
glTexCoord2fv(&OpenGL.vertex[textureNum][6]);
glVertex2f(-1.0f, -1.0f);
glTexCoord2fv(&OpenGL.vertex[textureNum][4]);
glVertex2f( 1.0f, -1.0f);
glTexCoord2fv(&OpenGL.vertex[textureNum][2]);
glVertex2f( 1.0f, 1.0f);
glTexCoord2fv(&OpenGL.vertex[textureNum][0]);
glVertex2f(-1.0f, 1.0f);
glEnd();
}
else
{
GLfloat *t, *s;
t = scTexArray[(height % SNES_HEIGHT) ? kSC2xExtend : kSC2xNormal];
s = scScnArray;
for (int i = 0; i < kSCMeshY; i++)
{
glTexCoordPointer(2, GL_FLOAT, 0, t);
glVertexPointer(3, GL_FLOAT, 0, s);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (kSCMeshX + 1) * 2);
t += (kSCMeshX + 1) * 2 * 2;
s += (kSCMeshX + 1) * 2 * 3;
}
}
glFinishObjectAPPLE(GL_TEXTURE, OpenGL.textures[textureNum]);
}
else
{
CGRect src;
src = CGRectMake(0, 0, width, orig_height);
DrawWithCoreImageFilter(src, cgGameImage);
}
if (fullscreen)
CGLFlushDrawable(glContext);
else
aglSwapBuffers(agContext);
}
static void S9xPutImageBlitGL (int width, int height)
{
Blitter blitFn;
int copyWidth, copyHeight;
if ((imageWidth[whichBuf] != width) || (imageHeight[whichBuf] != height))
{
if ((videoMode == VIDEOMODE_TV) && (width <= 256))
S9xBlitClearDelta();
if (drawoverscan && (height % SNES_HEIGHT == 0))
{
memset(blitGLBuffer, 0, 1024 * 1024 * 2);
int pitch = width << 1;
int extbtm = (height > 256) ? (SNES_HEIGHT_EXTENDED << 1) : SNES_HEIGHT_EXTENDED;
uint32 *extarea = (uint32 *) ((uint8 *) GFX.Screen + height * pitch);
for (int i = 0; i < (((extbtm - height) * pitch) >> 2); i++)
extarea[i] = 0;
height = extbtm;
}
}
else
{
if (drawoverscan)
height = (height > 256) ? (SNES_HEIGHT_EXTENDED << 1) : SNES_HEIGHT_EXTENDED;
}
switch (nx)
{
default:
case 2:
if (videoMode == VIDEOMODE_BLEND)
{
if (width <= 256)
{
copyWidth = width * 2;
copyHeight = height;
blitFn = S9xBlitPixBlend2x1;
}
else
{
copyWidth = width;
copyHeight = height;
blitFn = S9xBlitPixBlend1x1;
}
}
else
if (height <= 256)
{
if (width <= 256)
{
copyWidth = width * 2;
copyHeight = height * 2;
switch (videoMode)
{
default:
case VIDEOMODE_TV: blitFn = S9xBlitPixTV2x2; break;
case VIDEOMODE_SUPEREAGLE: blitFn = S9xBlitPixSuperEagle16; break;
case VIDEOMODE_2XSAI: blitFn = S9xBlitPix2xSaI16; break;
case VIDEOMODE_SUPER2XSAI: blitFn = S9xBlitPixSuper2xSaI16; break;
case VIDEOMODE_EPX: blitFn = S9xBlitPixEPX16; break;
case VIDEOMODE_HQ2X: blitFn = S9xBlitPixHQ2x16; break;
}
}
else
{
if (videoMode == VIDEOMODE_TV)
{
copyWidth = width;
copyHeight = height * 2;
blitFn = S9xBlitPixTV1x2;
}
else
{
copyWidth = width;
copyHeight = height;
blitFn = S9xBlitPixSimple1x1;
}
}
}
else
{
copyWidth = width;
copyHeight = height;
blitFn = S9xBlitPixSimple1x1;
}
break;
case 3:
if (width <= 256 && height <= 256)
{
copyWidth = width * 3;
copyHeight = height * 3;
blitFn = S9xBlitPixHQ3x16;
}
else
{
copyWidth = width;
copyHeight = height;
blitFn = S9xBlitPixSimple1x1;
}
break;
case 4:
if (width <= 256 && height <= 256)
{
copyWidth = width * 4;
copyHeight = height * 4;
blitFn = S9xBlitPixHQ4x16;
}
else
if (width > 256 && height > 256)
{
copyWidth = width * 2;
copyHeight = height * 2;
blitFn = S9xBlitPixHQ2x16;
}
else
{
copyWidth = width;
copyHeight = height;
blitFn = S9xBlitPixSimple1x1;
}
break;
case -1:
case -2:
copyWidth = ntsc_width;
copyHeight = height;
if (width <= 256)
blitFn = S9xBlitPixNTSC16;
else
blitFn = S9xBlitPixHiResNTSC16;
break;
}
imageWidth[whichBuf] = width;
imageHeight[whichBuf] = height;
if (multiprocessor)
{
MPWaitOnSemaphore(readySemaphore, kDurationForever);
mpBlit->nx = nx;
mpBlit->blitFn = blitFn;
mpBlit->srcWidth = width;
mpBlit->srcHeight = height;
mpBlit->copyWidth = copyWidth;
mpBlit->copyHeight = copyHeight;
mpBlit->gfxBuffer = GFX.Screen;
MPNotifyQueue(taskQueue, (void *) kMPBlitFrame, 0, 0);
whichBuf = 1 - whichBuf;
GFX.Screen = gfxScreen[whichBuf];
}
else
RenderBlitScreen(blitFn, nx, width, height, copyWidth, copyHeight, GFX.Screen);
}
static void S9xPutImageBlitGL2 (int blit_width, int blit_height)
{
if ((prevBlitWidth != blit_width) || (prevBlitHeight != blit_height))
windowResizeCount += 2;
if (windowResizeCount > 0)
{
if (fullscreen)
{
CGLSetCurrentContext(glContext);
glViewport(0, 0, glScreenW, glScreenH);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (glstretch)
{
int sh = (blit_width < blit_height) ? (blit_height >> 1) : ((blit_width > blit_height * 2) ? (blit_height << 1) : blit_height);
float fpw = (float) glScreenH / (float) sh * (float) blit_width;
int pw = (int) (fpw + ((float) glScreenW - fpw) * (float) macAspectRatio / 10000.0);
glViewport((glScreenW - pw) >> 1, 0, pw, glScreenH);
}
else
{
int sw, sh;
if (nx < 0)
{
sw = ntsc_width;
sh = ((blit_height % SNES_HEIGHT) ? SNES_HEIGHT_EXTENDED : SNES_HEIGHT) * 2;
}
else
{
sw = SNES_WIDTH * nx;
sh = ((blit_height % SNES_HEIGHT) ? SNES_HEIGHT_EXTENDED : SNES_HEIGHT) * nx;
}
glViewport((glScreenW - sw) >> 1, (glScreenH - sh) >> 1, sw, sh);
}
}
else
{
int ww = (int) gWindowRect.size.width,
wh = (int) gWindowRect.size.height;
aglSetCurrentContext(agContext);
aglUpdateContext(agContext);
glViewport(0, 0, ww, wh);
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (windowExtend)
{
int bh = (blit_height % SNES_HEIGHT) ? (SNES_HEIGHT_EXTENDED << 1) : (SNES_HEIGHT << 1);
glViewport(0, ((kMacWindowHeight - bh) >> 1) * wh / kMacWindowHeight, ww, bh * wh / kMacWindowHeight);
}
}
if (!ciFilterEnable)
{
if (nx < 0)
textureNum = (blit_height > 256) ? kGLNTS512 : kGLNTS256;
else
{
switch (blit_width / SNES_WIDTH)
{
default:
case 1:
case 2: textureNum = kGLBlit2x; break;
case 3: textureNum = kGLBlit3x; break;
case 4: textureNum = kGLBlit4x; break;
}
}
if (nx < 0)
{
int sh = (blit_height > 256) ? 512 : 256;
OpenGL.vertex[textureNum][2] = OpenGL.vertex[textureNum][4] = blit_width / 1024.0f;
OpenGL.vertex[textureNum][5] = OpenGL.vertex[textureNum][7] = blit_height / (float) sh;
glPixelStorei(GL_UNPACK_ROW_LENGTH, 1024);
}
else
{
if (OpenGL.rangeExt)
{
OpenGL.vertex[textureNum][2] = OpenGL.vertex[textureNum][4] = blit_width;
OpenGL.vertex[textureNum][5] = OpenGL.vertex[textureNum][7] = blit_height;
glPixelStorei(GL_UNPACK_ROW_LENGTH, blit_width);
}
else
{
int sl = (blit_width > 512) ? 1024 : 512;
int sh = (blit_height > 512) ? 1024 : 512;
OpenGL.vertex[textureNum][2] = OpenGL.vertex[textureNum][4] = blit_width / (float) sl;
OpenGL.vertex[textureNum][5] = OpenGL.vertex[textureNum][7] = blit_height / (float) sh;
glPixelStorei(GL_UNPACK_ROW_LENGTH, (blit_width > 512) ? 1024 : ((blit_width > 256) ? 512 : 256));
}
}
glBindTexture(OpenGL.target, OpenGL.textures[textureNum]);
}
else
{
int sl = OpenGL.rangeExt ? blit_width : ((blit_width > 512) ? 1024 : ((blit_width > 256) ? 512 : 256));
glPixelStorei(GL_UNPACK_ROW_LENGTH, sl);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, blit_width, 0, blit_height, -1, 1);
if (cgBlitImage)
CGImageRelease(cgBlitImage);
cgBlitImage = CreateBlitScreenCGImage(blit_width, blit_height, sl << 1, blitGLBuffer);
}
prevBlitWidth = blit_width;
prevBlitHeight = blit_height;
windowResizeCount--;
}
if (!ciFilterEnable)
{
glTexSubImage2D(OpenGL.target, 0, 0, 0, OpenGL.texW[textureNum], OpenGL.texH[textureNum], OpenGL.format, OpenGL.type, blitGLBuffer);
if (!screencurvature)
{
glBegin(GL_QUADS);
glTexCoord2fv(&OpenGL.vertex[textureNum][6]);
glVertex2f(-1.0f, -1.0f);
glTexCoord2fv(&OpenGL.vertex[textureNum][4]);
glVertex2f( 1.0f, -1.0f);
glTexCoord2fv(&OpenGL.vertex[textureNum][2]);
glVertex2f( 1.0f, 1.0f);
glTexCoord2fv(&OpenGL.vertex[textureNum][0]);
glVertex2f(-1.0f, 1.0f);
glEnd();
}
else
{
GLfloat *t, *s;
int tex;
if (nx < 0)
tex = (blit_height % SNES_HEIGHT) ? kSCNTExtend : kSCNTNormal;
else
if (blit_width > blit_height * 2)
{
if (blit_width / SNES_WIDTH != 3)
tex = (blit_height % SNES_HEIGHT) ? kSC2xEHiRes : kSC2xNHiRes;
else
tex = (blit_height % SNES_HEIGHT) ? kSC3xEHiRes : kSC3xNHiRes;
}
else
if (blit_width > blit_height)
{
if (blit_width / SNES_WIDTH != 3)
tex = (blit_height % SNES_HEIGHT) ? kSC2xExtend : kSC2xNormal;
else
tex = (blit_height % SNES_HEIGHT) ? kSC3xExtend : kSC3xNormal;
}
else
tex = (blit_height % SNES_HEIGHT) ? kSC2xEInter : kSC2xNInter;
t = scTexArray[tex];
s = scScnArray;
for (int i = 0; i < kSCMeshY; i++)
{
glTexCoordPointer(2, GL_FLOAT, 0, t);
glVertexPointer(3, GL_FLOAT, 0, s);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (kSCMeshX + 1) * 2);
t += (kSCMeshX + 1) * 2 * 2;
s += (kSCMeshX + 1) * 2 * 3;
}
}
glFinishObjectAPPLE(GL_TEXTURE, OpenGL.textures[textureNum]);
}
else
{
CGRect src;
src = CGRectMake(0, 0, blit_width, blit_height);
DrawWithCoreImageFilter(src, cgBlitImage);
}
if (fullscreen)
CGLFlushDrawable(glContext);
else
aglSwapBuffers(agContext);
}
void S9xTextMode (void)
{
return;
}
void S9xGraphicsMode (void)
{
return;
}
void S9xSetPalette (void)
{
return;
}