2016-02-08 19:53:16 +05:00
|
|
|
/* $Id: toglAGL.c,v 1.7 2009/10/22 00:06:41 gregcouch Exp $ */
|
|
|
|
|
|
|
|
/* vi:set sw=4 expandtab: */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Togl - a Tk OpenGL widget
|
|
|
|
*
|
|
|
|
* Copyright (C) 1996-2002 Brian Paul and Ben Bederson
|
|
|
|
* Copyright (C) 2005-2009 Greg Couch
|
|
|
|
* See the LICENSE file for copyright details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct FBInfo
|
|
|
|
{
|
|
|
|
GLint acceleration;
|
|
|
|
GLint colors;
|
|
|
|
GLint depth;
|
|
|
|
GLint samples;
|
|
|
|
AGLPixelFormat pix;
|
|
|
|
};
|
|
|
|
typedef struct FBInfo FBInfo;
|
|
|
|
|
|
|
|
static int
|
|
|
|
FBInfoCmp(const void *a, const void *b)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 1. full acceleration is better
|
|
|
|
* 2. greater color bits is better
|
|
|
|
* 3. greater depth bits is better
|
|
|
|
* 4. more multisampling is better
|
|
|
|
*/
|
|
|
|
const FBInfo *x = (const FBInfo *) a;
|
|
|
|
const FBInfo *y = (const FBInfo *) b;
|
|
|
|
|
|
|
|
if (x->acceleration != y->acceleration)
|
|
|
|
return y->acceleration - x->acceleration;
|
|
|
|
if (x->colors != y->colors)
|
|
|
|
return y->colors - x->colors;
|
|
|
|
if (x->depth != y->depth)
|
|
|
|
return y->depth - x->depth;
|
|
|
|
if (x->samples != y->samples)
|
|
|
|
return y->samples - x->samples;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static AGLPixelFormat
|
|
|
|
togl_pixelFormat(Togl *togl)
|
|
|
|
{
|
|
|
|
GLint attribs[32];
|
|
|
|
int na = 0;
|
|
|
|
AGLPixelFormat pix;
|
|
|
|
GDHandle display = NULL;
|
|
|
|
FBInfo *info = NULL;
|
|
|
|
int count;
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (togl->MultisampleFlag && !hasMultisampling) {
|
|
|
|
Tcl_SetResult(togl->Interp,
|
|
|
|
TCL_STUPID "multisampling not supported", TCL_STATIC);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (togl->PbufferFlag && !togl->RgbaFlag) {
|
|
|
|
Tcl_SetResult(togl->Interp,
|
|
|
|
TCL_STUPID "puffer must be RGB[A]", TCL_STATIC);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
attribs[na++] = AGL_MINIMUM_POLICY;
|
|
|
|
/* ask for hardware-accelerated onscreen */
|
|
|
|
attribs[na++] = AGL_ACCELERATED;
|
|
|
|
attribs[na++] = AGL_NO_RECOVERY;
|
|
|
|
if (togl->RgbaFlag) {
|
|
|
|
/* RGB[A] mode */
|
|
|
|
attribs[na++] = AGL_RGBA;
|
|
|
|
attribs[na++] = AGL_RED_SIZE;
|
|
|
|
attribs[na++] = togl->RgbaRed;
|
|
|
|
attribs[na++] = AGL_GREEN_SIZE;
|
|
|
|
attribs[na++] = togl->RgbaGreen;
|
|
|
|
attribs[na++] = AGL_BLUE_SIZE;
|
|
|
|
attribs[na++] = togl->RgbaBlue;
|
|
|
|
if (togl->AlphaFlag) {
|
|
|
|
attribs[na++] = AGL_ALPHA_SIZE;
|
|
|
|
attribs[na++] = togl->AlphaSize;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Color index mode */
|
|
|
|
attribs[na++] = AGL_BUFFER_SIZE;
|
|
|
|
attribs[na++] = 8;
|
|
|
|
}
|
|
|
|
if (togl->DepthFlag) {
|
|
|
|
attribs[na++] = AGL_DEPTH_SIZE;
|
|
|
|
attribs[na++] = togl->DepthSize;
|
|
|
|
}
|
|
|
|
if (togl->DoubleFlag) {
|
|
|
|
attribs[na++] = AGL_DOUBLEBUFFER;
|
|
|
|
}
|
|
|
|
if (togl->StencilFlag) {
|
|
|
|
attribs[na++] = AGL_STENCIL_SIZE;
|
|
|
|
attribs[na++] = togl->StencilSize;
|
|
|
|
}
|
|
|
|
if (togl->AccumFlag) {
|
|
|
|
attribs[na++] = AGL_ACCUM_RED_SIZE;
|
|
|
|
attribs[na++] = togl->AccumRed;
|
|
|
|
attribs[na++] = AGL_ACCUM_GREEN_SIZE;
|
|
|
|
attribs[na++] = togl->AccumGreen;
|
|
|
|
attribs[na++] = AGL_ACCUM_BLUE_SIZE;
|
|
|
|
attribs[na++] = togl->AccumBlue;
|
|
|
|
if (togl->AlphaFlag) {
|
|
|
|
attribs[na++] = AGL_ACCUM_ALPHA_SIZE;
|
|
|
|
attribs[na++] = togl->AccumAlpha;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (togl->MultisampleFlag) {
|
|
|
|
attribs[na++] = AGL_MULTISAMPLE;
|
|
|
|
#ifdef AGL_SAMPLES_ARB
|
|
|
|
/* OS X 10.2 and later */
|
|
|
|
attribs[na++] = AGL_SAMPLE_BUFFERS_ARB;
|
|
|
|
attribs[na++] = 1;
|
|
|
|
attribs[na++] = AGL_SAMPLES_ARB;
|
|
|
|
attribs[na++] = 2;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if (togl->AuxNumber != 0) {
|
|
|
|
attribs[na++] = AGL_AUX_BUFFERS;
|
|
|
|
attribs[na++] = togl->AuxNumber;
|
|
|
|
}
|
|
|
|
if (togl->Stereo == TOGL_STEREO_NATIVE) {
|
|
|
|
attribs[na++] = AGL_STEREO;
|
|
|
|
}
|
|
|
|
if (togl->FullscreenFlag) {
|
|
|
|
attribs[na++] = AGL_FULLSCREEN;
|
|
|
|
/* TODO: convert Tk screen to display device */
|
|
|
|
display = GetMainDevice();
|
|
|
|
}
|
|
|
|
attribs[na++] = AGL_NONE;
|
|
|
|
|
|
|
|
if ((pix = aglChoosePixelFormat(&display, togl->FullscreenFlag ? 1 : 0,
|
|
|
|
attribs)) == NULL) {
|
|
|
|
Tcl_SetResult(togl->Interp, TCL_STUPID "couldn't choose pixel format",
|
|
|
|
TCL_STATIC);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: since we aglDestroyPixelFormat elsewhere, this code may leak
|
2017-07-26 08:29:41 +05:00
|
|
|
* memory if the pixel format chosen is not the original (because
|
2016-02-08 19:53:16 +05:00
|
|
|
* aglDestroyPixelFormat will give an error). */
|
|
|
|
count = 0;
|
|
|
|
do {
|
|
|
|
info = (FBInfo *) realloc(info, (count + 1) * sizeof (FBInfo));
|
|
|
|
info[count].pix = pix;
|
|
|
|
aglDescribePixelFormat(pix, AGL_ACCELERATED, &info[count].acceleration);
|
|
|
|
aglDescribePixelFormat(pix, AGL_BUFFER_SIZE, &info[count].colors);
|
|
|
|
aglDescribePixelFormat(pix, AGL_DEPTH_SIZE, &info[count].depth);
|
|
|
|
#ifdef AGL_SAMPLES_ARB
|
|
|
|
aglDescribePixelFormat(pix, AGL_SAMPLES_ARB, &info[count].samples);
|
|
|
|
#else
|
|
|
|
info[count].samples = 0;
|
|
|
|
#endif
|
|
|
|
++count;
|
|
|
|
} while (pix = aglNextPixelFormat(pix));
|
|
|
|
qsort(info, count, sizeof info[0], FBInfoCmp);
|
|
|
|
pix = info[0].pix;
|
|
|
|
free(info);
|
|
|
|
return pix;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
togl_describePixelFormat(Togl *togl)
|
|
|
|
{
|
|
|
|
AGLPixelFormat pixelformat;
|
|
|
|
|
|
|
|
/* fill in RgbaFlag, DoubleFlag, and Stereo */
|
|
|
|
pixelformat = (AGLPixelFormat) togl->PixelFormat;
|
|
|
|
GLint has_rgba, has_doublebuf, has_depth, has_accum, has_alpha,
|
|
|
|
has_stencil, has_stereo, has_multisample;
|
|
|
|
|
|
|
|
if (aglDescribePixelFormat(pixelformat, AGL_RGBA, &has_rgba)
|
|
|
|
&& aglDescribePixelFormat(pixelformat, AGL_DOUBLEBUFFER,
|
|
|
|
&has_doublebuf)
|
|
|
|
&& aglDescribePixelFormat(pixelformat, AGL_DEPTH_SIZE, &has_depth)
|
|
|
|
&& aglDescribePixelFormat(pixelformat, AGL_ACCUM_RED_SIZE,
|
|
|
|
&has_accum)
|
|
|
|
&& aglDescribePixelFormat(pixelformat, AGL_ALPHA_SIZE, &has_alpha)
|
|
|
|
&& aglDescribePixelFormat(pixelformat, AGL_STENCIL_SIZE,
|
|
|
|
&has_stencil)
|
|
|
|
&& aglDescribePixelFormat(pixelformat, AGL_STEREO, &has_stereo)
|
|
|
|
#ifdef AGL_SAMPLES_ARB
|
|
|
|
&& aglDescribePixelFormat(pixelformat, AGL_SAMPLES_ARB,
|
|
|
|
&has_multisample)
|
|
|
|
#endif
|
|
|
|
) {
|
|
|
|
togl->RgbaFlag = (has_rgba != 0);
|
|
|
|
togl->DoubleFlag = (has_doublebuf != 0);
|
|
|
|
togl->DepthFlag = (has_depth != 0);
|
|
|
|
togl->AccumFlag = (has_accum != 0);
|
|
|
|
togl->AlphaFlag = (has_alpha != 0);
|
|
|
|
togl->StencilFlag = (has_stencil != 0);
|
|
|
|
togl->Stereo = (has_stereo ? TOGL_STEREO_NATIVE : TOGL_STEREO_NONE);
|
|
|
|
#ifdef AGL_SAMPLES_ARB
|
|
|
|
togl->MultisampleFlag = (has_multisample != 0);
|
|
|
|
#else
|
|
|
|
togl->MultisampleFlag = False;
|
|
|
|
#endif
|
|
|
|
return True;
|
|
|
|
} else {
|
|
|
|
Tcl_SetResult(togl->Interp,
|
|
|
|
TCL_STUPID "failed querying pixel format attributes",
|
|
|
|
TCL_STATIC);
|
|
|
|
return False;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#define isPow2(x) (((x) & ((x) - 1)) == 0)
|
|
|
|
|
|
|
|
static AGLPbuffer
|
|
|
|
togl_createPbuffer(Togl *togl)
|
|
|
|
{
|
|
|
|
GLint min_size[2], max_size[2];
|
|
|
|
Bool hasPbuffer;
|
|
|
|
const char *extensions;
|
|
|
|
GLboolean good;
|
|
|
|
GLint target;
|
|
|
|
GLint virtualScreen;
|
|
|
|
AGLPbuffer pbuf;
|
|
|
|
|
|
|
|
extensions = (const char *) glGetString(GL_EXTENSIONS);
|
|
|
|
hasPbuffer = (strstr(extensions, "GL_APPLE_pixel_buffer") != NULL);
|
|
|
|
if (!hasPbuffer) {
|
|
|
|
Tcl_SetResult(togl->Interp,
|
|
|
|
TCL_STUPID "pbuffers are not supported", TCL_STATIC);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
glGetIntegerv(GL_MIN_PBUFFER_VIEWPORT_DIMS_APPLE, min_size);
|
|
|
|
glGetIntegerv(GL_MAX_VIEWPORT_DIMS, max_size);
|
|
|
|
virtualScreen = aglGetVirtualScreen(togl->Ctx);
|
|
|
|
for (;;) {
|
|
|
|
/* make sure we don't exceed the maximum size because if we do,
|
|
|
|
* aglCreatePbuffer may succeed and later uses of the pbuffer fail */
|
|
|
|
if (togl->Width < min_size[0])
|
|
|
|
togl->Width = min_size[0];
|
|
|
|
else if (togl->Width > max_size[0]) {
|
|
|
|
if (togl->LargestPbufferFlag)
|
|
|
|
togl->Width = max_size[0];
|
|
|
|
else {
|
|
|
|
Tcl_SetResult(togl->Interp,
|
|
|
|
TCL_STUPID "pbuffer too large", TCL_STATIC);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (togl->Height < min_size[1])
|
|
|
|
togl->Height = min_size[1];
|
|
|
|
else if (togl->Height > max_size[1]) {
|
|
|
|
if (togl->LargestPbufferFlag)
|
|
|
|
togl->Height = max_size[1];
|
|
|
|
else {
|
|
|
|
Tcl_SetResult(togl->Interp,
|
|
|
|
TCL_STUPID "pbuffer too large", TCL_STATIC);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPow2(togl->Width) && isPow2(togl->Height))
|
|
|
|
target = GL_TEXTURE_2D;
|
|
|
|
else
|
|
|
|
target = GL_TEXTURE_RECTANGLE_ARB;
|
|
|
|
|
|
|
|
good = aglCreatePBuffer(togl->Width, togl->Height, target,
|
|
|
|
togl->AlphaFlag ? GL_RGBA : GL_RGB, 0, &pbuf);
|
|
|
|
if (good) {
|
|
|
|
/* aglSetPbuffer allocates the framebuffer space */
|
|
|
|
if (aglSetPBuffer(togl->Ctx, pbuf, 0, 0, virtualScreen)) {
|
|
|
|
return pbuf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!togl->LargestPbufferFlag
|
|
|
|
|| togl->Width == min_size[0] || togl->Height == min_size[1]) {
|
|
|
|
Tcl_SetResult(togl->Interp,
|
|
|
|
TCL_STUPID "unable to create pbuffer", TCL_STATIC);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* largest unavailable, try something smaller */
|
|
|
|
togl->Width = togl->Width / 2 + togl->Width % 2;
|
|
|
|
togl->Height = togl->Width / 2 + togl->Height % 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
togl_destroyPbuffer(Togl *togl)
|
|
|
|
{
|
|
|
|
aglDestroyPBuffer(togl->pbuf);
|
|
|
|
}
|