singe/ldp-out/ldp-vldp-gl.cpp
2019-11-11 14:53:02 -06:00

780 lines
22 KiB
C++

#ifdef USE_OPENGL
#ifdef MAC_OSX
#include <glew.h>
#else
#include <GL/glew.h>
#endif
#include <string.h> // for memcpy
#include "../vldp2/vldp/vldp.h" // to get the vldp structs
#include "ldp-vldp.h"
#include "ldp-vldp-gl.h"
#include "ldp.h"
#include "../io/mpo_mem.h"
#include "../io/conout.h"
#include "../game/game.h"
#include "../daphne.h"
#include "../video/video.h"
#include "../video/blend.h"
#include "../video/palette.h"
#ifdef DEBUG
#include <assert.h>
#endif
enum
{
TEX_Y, // holds Y buffer
TEX_U, // U buffer
TEX_V, // V buffer
NUM_YUV_TEXTURES,
};
enum
{
TEX_VID_OVERLAY, // video overlay
TEX_VID_PALETTE, // color palette look-up texture
TEX_SCANLINES, // scanlines texture (if enabled by user)
NUM_TEXTURES // this must be last!
};
// for speed.. I am in a hurry and don't want to mess w/ functions :(
extern Uint32 g_uRGBAPalette[];
extern unsigned int g_filter_type;
// for speed
extern unsigned g_uCPUMsBehind;
// for speed
extern bool g_take_screenshot; // if true, a screenshot will be taken at the next available opportunity
// buffers to hold video frame texture
// There are two sets of each buffer to hold copies of the YUV buffers
GLuint g_textureYUVIDs[2][NUM_YUV_TEXTURES];
// buffers to hold non-YUV textures
GLuint g_textureIDs[NUM_TEXTURES];
// g_textureYUVIDs[g_uCurTextureArray] is the currently active texture array :)
unsigned int g_uCurTextureArray = 0;
// dimensions of YUV frame (in pixels)
unsigned int g_uTexWidth = 0, g_uTexHeight = 0;
// dimensions of output window (in pixels)
unsigned int g_uVidWidth = 0, g_uVidHeight = 0;
// the height of the video overlay (pre-calculate for speed)
// The video overlay's height often goes off the screen
unsigned int g_uOverlayHeight = 0;
// the same as g_game->get_video_visible_lines()
unsigned int g_uVisibleLines = 240;
bool g_bShaderInitialized = false;
bool g_bOpenGLInitialized = false;
GLuint g_FSHandle = 0, g_PHandle = 0;
GLuint g_hndF8Bit = 0, g_prgF8Bit = 0;
// how many frames have been prepared
unsigned int g_uFramePrepReq = 0;
// last frame index to be requested to be displayed, and last frame index that was displayed
// (index corresponds to g_uFramePrepReq)
unsigned int g_uFrameDispReq = 0;
unsigned int g_uFrameDispAck = 0;
// blank requested means the next frame we display should be blank, with overlay drawn on top
bool g_bBlankRequested = false;
// what gamevid was last time the think() function was called
SDL_Surface *g_gamevid_old = NULL;
// so we know when a new vblank has occurred
unsigned int g_uVblankCountOld = 0;
#define Y_IDX 0
#define U_IDX 1
#define V_IDX 2
// This number logically only needs to be 2 but performs much better as 4 :)
#define YUV_BUF_COUNT 4
unsigned char *g_ptr[YUV_BUF_COUNT][3];
// Macros to lock and unlock the mutex to make sure two threads aren't accessing shared vars at the same time
#define VLDP_GL_LOCK SDL_mutexP(g_vldp_gl_mutex)
#define VLDP_GL_UNLOCK SDL_mutexV(g_vldp_gl_mutex)
// mutex to protect shared data
SDL_mutex *g_vldp_gl_mutex = NULL;
// Thanks to Peter Bengtsson for the reference code to help me figure out
// how to convert YUV to RGB using a fragment shader.
const GLchar *g_FProgram=
"uniform sampler2D Ytex;\n"
"uniform sampler2D Utex,Vtex;\n"
"void main(void) {\n"
" float r,g,b,y,u,v;\n"
" y=texture2D(Ytex,gl_TexCoord[0].st).r;\n"
" u=texture2D(Utex,gl_TexCoord[0].st).r;\n"
" v=texture2D(Vtex,gl_TexCoord[0].st).r;\n"
" y=1.1643*(y-0.0625);\n"
" u=u-0.5;\n"
" v=v-0.5;\n"
" r=y+1.5958*v;\n"
" g=y-0.39173*u-0.81290*v;\n"
" b=y+2.017*u;\n"
" gl_FragColor=vec4(r,g,b,1.0);\n"
"}\n";
// 8-bit texture fragment program (by Matt Ownby, my first fragment shader! woohoo!)
const GLchar *g_F8Bit =
"uniform sampler1D smpColorPalette;\n"
"uniform sampler2D smpPixels;\n"
"void main(void) {\n"
" float idx;\n"
// idx gets a value between 0.0 and 1.0 which corresponds to 0-255 on our color palette
" idx=texture2D(smpPixels, gl_TexCoord[0].st).r;\n"
// 255/256 (0.99609375) converts 0.0-1.0 to the left border of the color that we want in the palette texture
// Adding 1/512 (0.001953125) centers us inside the color that we want so that there can be no ambiguity
" gl_FragColor=texture1D(smpColorPalette, (idx * 0.99609375) + 0.001953125);\n"
"}\n";
void ldp_vldp_gl_blend_y(Uint8 *g_ptrY)
{
// NOTE : this function is unoptimized, because it is low-priority.
// I just coded it up quickly to make sure it is supported to save myself any questions
// from people who are wondering why it's not working.
g_blend_line1 = g_ptrY;
g_blend_line2 = g_ptrY + g_uTexWidth;
g_blend_dest = MPO_MALLOC(g_uTexWidth);
g_blend_iterations = g_uTexWidth;
if (g_blend_dest)
{
// two rows at a time ...
for (unsigned int uRow = 0; uRow < g_uTexHeight; uRow += 2)
{
g_blend_func();
// overwrite two source lines with blended line
memcpy(g_blend_line1, g_blend_dest, g_uTexWidth);
memcpy(g_blend_line2, g_blend_dest, g_uTexWidth);
g_blend_line1 += (g_uTexWidth << 1);
g_blend_line2 += (g_uTexWidth << 1);
}
MPO_FREE(g_blend_dest);
}
}
void draw_prepared_frame(SDL_Surface *gamevid)
{
int w_half = g_uVidWidth >> 1, h_half = g_uVidHeight >> 1;
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// if this isn't supposed to be a blank frame
if (!g_bBlankRequested)
{
glUseProgram(g_PHandle); // start using the program
// ASSUMPTION: YUV textures have already been prepared
// bind the YUV textures for the current frame
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,g_textureYUVIDs[g_uCurTextureArray][TEX_U]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D,g_textureYUVIDs[g_uCurTextureArray][TEX_V]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,g_textureYUVIDs[g_uCurTextureArray][TEX_Y]);
// draw the textured rectangle
glBegin(GL_QUADS);
glTexCoord2f(0, 1); glVertex3i(-w_half, -h_half, 0);
glTexCoord2f(1, 1); glVertex3i(w_half, -h_half, 0);
glTexCoord2f(1, 0); glVertex3i(w_half, h_half, 0);
glTexCoord2f(0, 0); glVertex3i(-w_half, h_half, 0);
glEnd();
glUseProgram(0); // stop using fragment shader
} // end if it's not supposed to be a blank frame
// else the frame is supposed to be blank, so we don't draw the VLDP frame
else
{
// we've done our duty and blanked...
g_bBlankRequested = false;
}
// STEP 2: Display 8-bit video overlay (if it exists)
// if there is game video that needs to be displayed
if (gamevid)
{
g_uOverlayHeight = gamevid->h;
// start using 8-bit texture fragment program
glUseProgram(g_prgF8Bit);
// bind color palette to texture unit 1
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_1D, g_textureIDs[TEX_VID_PALETTE]);
// The palette texture has already been copied over in on_palette_change_gl()
// update 8-bit pixels to texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, g_textureIDs[TEX_VID_OVERLAY]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, gamevid->w, gamevid->h,
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, gamevid->pixels);
glPushMatrix();
// take vertical offset into account
// The bottom of the rectangle must be the bottom of the overlay,
// relative to the size of the mpeg video.
// (many overlays are too big vertically to fit on the screen)
// We have to rescale the vertical coordinates in case g_uVisibleLines
// is not the same as the screen's height.
GLfloat fScaleFactor = (GLfloat) g_uVidHeight / g_uVisibleLines;
glScalef(1.0, fScaleFactor, 1.0);
// We have to do the translation after we do the scale, because
// the video row offset must be scaled too.
glTranslatef(0, (GLfloat) -g_game->get_video_row_offset(), 0);
// iY is initialized as the bottom row
GLint iY = ((g_uVisibleLines / 2) - g_uOverlayHeight);
// draw the textured rectangle
glBegin(GL_QUADS);
glTexCoord2f(0, 1); glVertex3i(-w_half, iY, 1); // bottom left
glTexCoord2f(1, 1); glVertex3i(w_half, iY, 1); // bottom right
iY += g_uOverlayHeight;
glTexCoord2f(1, 0); glVertex3i(w_half, iY, 1); // top right
glTexCoord2f(0, 0); glVertex3i(-w_half, iY, 1); // top left
glEnd();
glPopMatrix();
// stop using fragment program
glUseProgram(0);
#if 0
glDisable(GL_TEXTURE_2D);
glPushMatrix();
fScaleFactor = (GLfloat) g_uVidHeight / g_uVisibleLines;
glScalef(1.0, fScaleFactor, 1.0);
glTranslatef(0, (GLfloat) -g_game->get_video_row_offset(), 0);
iY = ((g_uVisibleLines / 2) - g_uOverlayHeight);
glColor4f(1.0, 1.0, 1.0, 0.35f); // draw border so we can see where overlay's border is
glBegin(GL_QUADS);
glVertex3i(-w_half, iY, 3); // bottom left
glVertex3i(w_half, iY, 3); // bottom right
iY += g_uOverlayHeight;
glVertex3i(w_half, iY, 3); // top right
glVertex3i(-w_half, iY, 3); // top left
glEnd();
glPopMatrix();
glColor4f(1.0, 0.0, 0.0, 0.25);
glBegin(GL_QUADS);
glVertex3i(-w_half, -h_half, 4); // bottom left
glVertex3i(w_half, -h_half, 4); // bottom right
glVertex3i(w_half, h_half, 4); // top right
glVertex3i(-w_half, h_half, 4); // top left
glEnd();
glEnable(GL_TEXTURE_2D);
#endif
} // end if game video exists
// if scanlines are enabled
if (g_filter_type & FILTER_SCANLINES)
{
glBindTexture(GL_TEXTURE_2D, g_textureIDs[TEX_SCANLINES]);
glBegin(GL_QUADS);
glTexCoord2i(0, g_uVidHeight >> 1); glVertex3i(-w_half, -h_half, 2);
glTexCoord2i(1, g_uVidHeight >> 1); glVertex3i(w_half, -h_half, 2);
glTexCoord2i(1, 0); glVertex3i(w_half, h_half, 2);
glTexCoord2i(0, 0); glVertex3i(-w_half, h_half, 2);
glEnd();
}
}
void ldp_vldp_gl_think(unsigned int uVblankCount)
{
// this is used by multiple branches so we define it here
SDL_Surface *gamevid = g_game->get_finished_video_overlay();
bool bOkToDraw = false;
VLDP_GL_LOCK;
// if we have at least 1 new frame requested to be displayed
if (g_uFrameDispReq > g_uFrameDispAck)
{
#ifdef DEBUG
assert(g_ldp->is_blitting_allowed() == false);
#endif
// STEP 1: copy frame into video card texture memory
// Use the other texture array so that we can buffer our next YUV image and
// still allow access to the current one in case the video overlay changes before
// VLDP is ready to display the next YUV image
unsigned int uNextTextureArray = g_uCurTextureArray ^ 1;
// acknowledge that we've drawn (or will draw very soon) this new frame
++g_uFrameDispAck;
// this allows us to display frames that are slightly behind but buffered
unsigned int uIdx = g_uFrameDispAck % YUV_BUF_COUNT;
// lock while we access g_ptr* and g_bFrameQueued
// copy YUV frame into openGL texture buffer
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,g_textureYUVIDs[uNextTextureArray][TEX_U]);
glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE, g_uTexWidth >> 1, g_uTexHeight >> 1,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,
g_ptr[uIdx][U_IDX]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D,g_textureYUVIDs[uNextTextureArray][TEX_V]);
glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,g_uTexWidth >> 1,g_uTexHeight >> 1,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,
g_ptr[uIdx][V_IDX]);
// if blending is requested, then blend the Y plane now before sending it to the texture
if (g_filter_type & FILTER_BLEND)
{
ldp_vldp_gl_blend_y(g_ptr[uIdx][Y_IDX]);
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,g_textureYUVIDs[uNextTextureArray][TEX_Y]);
glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE,g_uTexWidth,g_uTexHeight,0,GL_LUMINANCE,GL_UNSIGNED_BYTE,
g_ptr[uIdx][Y_IDX]);
g_uCurTextureArray ^= 1; // flip texture array so we're using our new VLDP frame when we draw
bOkToDraw = true; // it's ok to draw the frame now ...
}
// if our ROM-generated video overlay has changed
// (THIS SHOULD NOT BE AN 'ELSE IF' BECAUSE WE WANT TO UPDATE BOTH AT THE SAME FRAME IF NEEDED DUE TO VSYNC DELAY)
if (gamevid != g_gamevid_old)
{
g_gamevid_old = gamevid;
bOkToDraw = true;
}
// else do nothing ...
VLDP_GL_UNLOCK;
// If it's time to draw a new frame and if we have a new frame to draw
// (We don't want to draw every time bOkToDraw is true because if the laserdisc video
// changes at 30 FPS and the overlay changes at 60 FPS, we will have updates at 90 FPS
// which is too fast for most monitor refresh rates, although it does work fine with
// vsync disabled)
if ((uVblankCount != g_uVblankCountOld) && (bOkToDraw))
{
// if we're not too far behind, then display the frame
// I chose 33 because it is approximately 2 frames on a 60 Hz monitor, and 1 NTSC frame
if (g_uCPUMsBehind < 33)
{
// draw it! (this should go really fast since the textures are already set up)
draw_prepared_frame(gamevid);
SDL_GL_SwapBuffers();
// if we've been instructed to take a screenshot, do so now that the image has been displayed
if (g_take_screenshot)
{
printline("Screenshot requested!");
g_take_screenshot = false;
take_screenshot_GL();
}
}
// else we're too far behind and SDL_GL_SwapBuffers can take a long time if vsync is enabled,
// so drop this frame to try to catch up.
// TODO : maybe we should show every 4th frame even if we are dropping just to avoid
// having 'dead' air
g_uVblankCountOld = uVblankCount;
}
}
extern unsigned int g_draw_width, g_draw_height;
bool check_shader(GLuint uFHandle, GLuint &uPHandle)
{
char s[32768];
bool bResult = false;
GLint i = 0;
GLsizei iCharsWritten = 0;
// see if compile succeeded
glGetShaderiv(uFHandle,GL_COMPILE_STATUS,&i);
if (i == GL_TRUE)
{
// create the program
uPHandle = glCreateProgram();
/* Create a complete program object. */
glAttachShader(uPHandle,uFHandle);
glLinkProgram(uPHandle);
// see if link succeeded
glGetProgramiv(uPHandle, GL_LINK_STATUS, &i);
// if link succeeded
if (i == GL_TRUE)
{
bResult = true;
}
else
{
glGetProgramiv(uPHandle, GL_INFO_LOG_LENGTH, &i);
glGetProgramInfoLog(uPHandle,i,&iCharsWritten,s);
outstr("OpenGL Shader Link Failed: ");
printline(s);
}
}
// else compile failed
else
{
glGetShaderiv(uFHandle, GL_INFO_LOG_LENGTH, &i);
glGetShaderInfoLog(uFHandle,i,&iCharsWritten,s);
outstr("OpenGL Shader Compile Failed: ");
printline(s);
}
return bResult;
}
bool init_shader()
{
bool bResult = false;
glewInit();
if (glewIsSupported("GL_VERSION_2_0"))
{
printline("OpenGL v2.0 is supported.");
/* Set up program objects. */
g_FSHandle=glCreateShader(GL_FRAGMENT_SHADER);
g_hndF8Bit=glCreateShader(GL_FRAGMENT_SHADER);
/* Compile the shader. */
glShaderSource(g_FSHandle,1,&g_FProgram,NULL);
glCompileShader(g_FSHandle);
glShaderSource(g_hndF8Bit, 1, &g_F8Bit, NULL);
glCompileShader(g_hndF8Bit);
// check to see if compilation succeeded
if (check_shader(g_FSHandle, g_PHandle))
{
if (check_shader(g_hndF8Bit, g_prgF8Bit))
{
// setup our shader variables
int i = 0;
// setup variables for YUV->RGB program
glUseProgram(g_PHandle);
i=glGetUniformLocation(g_PHandle,"Utex");
glUniform1i(i,1);
i=glGetUniformLocation(g_PHandle,"Vtex");
glUniform1i(i,2);
i=glGetUniformLocation(g_PHandle,"Ytex");
glUniform1i(i,0);
// setup variables for 8-bit texture program
glUseProgram(g_prgF8Bit);
i=glGetUniformLocation(g_prgF8Bit,"smpColorPalette");
glUniform1i(i,1);
// pixels on texture unit 0
i=glGetUniformLocation(g_prgF8Bit,"smpPixels");
glUniform1i(i,0);
glUseProgram(0);
bResult = true;
}
}
}
else
{
printline("OpenGL v2.0 is required for VLDP OpenGL mode.");
}
return bResult;
}
bool init_vldp_opengl()
{
bool bResult = false;
int i = 0;
// if we've never initialized
if (!g_bOpenGLInitialized)
{
g_uVidWidth = g_draw_width;
g_uVidHeight = g_draw_height;
// find out how many visible lines the video overlay supports
g_uVisibleLines = g_game->get_video_visible_lines();
glGenTextures(NUM_TEXTURES, g_textureIDs); // generate texture buffers
glGenTextures(NUM_YUV_TEXTURES, g_textureYUVIDs[0]); // " " "
glGenTextures(NUM_YUV_TEXTURES, g_textureYUVIDs[1]); // " " "
// setup texture parameters
for (i = 0; i < NUM_TEXTURES; i++)
{
GLuint uType = GL_LINEAR;
GLuint uParam = GL_CLAMP_TO_EDGE; // no reason to ever use GL_CLAMP based on what I've now learned :)
GLenum uTarget = GL_TEXTURE_2D;
switch (i)
{
// the color palette texture is 1-Dimensional
// We _must_ use GL_NEAREST for TEX_VID_PALETTE because it is a lookup table.
case TEX_VID_PALETTE:
uTarget = GL_TEXTURE_1D;
uType = GL_NEAREST;
break;
// the scanlines texture must repeat due to its design
case TEX_SCANLINES:
uParam = GL_REPEAT;
break;
// IMPORTANT: For TEX_VID_OVERLAY, we _must_ use GL_NEAREST for our shader to work properly.
case TEX_VID_OVERLAY:
uType = GL_NEAREST;
break;
}
glBindTexture(uTarget, g_textureIDs[i]);
glTexParameteri(uTarget, GL_TEXTURE_WRAP_S, uParam);
glTexParameteri(uTarget, GL_TEXTURE_WRAP_T, uParam);
glTexParameteri(uTarget, GL_TEXTURE_MAG_FILTER, uType);
glTexParameteri(uTarget, GL_TEXTURE_MIN_FILTER, uType);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
// setup YUV texture parameters
for (i = 0; i < NUM_YUV_TEXTURES; i++)
{
GLenum uTarget = GL_TEXTURE_2D;
GLuint uParam = GL_CLAMP_TO_EDGE; // this solves the 'green border' problem (GL_CLAMP was causing it)
GLuint uType = GL_LINEAR;
for (int j = 0; j < 2; ++j)
{
glBindTexture(uTarget, g_textureYUVIDs[j][i]);
glTexParameteri(uTarget, GL_TEXTURE_WRAP_S, uParam);
glTexParameteri(uTarget, GL_TEXTURE_WRAP_T, uParam);
glTexParameteri(uTarget, GL_TEXTURE_MAG_FILTER, uType);
glTexParameteri(uTarget, GL_TEXTURE_MIN_FILTER, uType);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
}
// NOW CREATE SCANLINES TEXTURE (it is so small it's ok to create it even if we don't use it)
glBindTexture(GL_TEXTURE_2D, g_textureIDs[TEX_SCANLINES]);
Uint32 uScanLineTex[4] =
{
0xFF000000, 0xFF000000, // first line (black)
0, 0 // second line (totally transparent)
};
// apply the texture
glTexImage2D(GL_TEXTURE_2D,
0, // level is 0
GL_RGBA,
2, // 2 wide
2, // 2 tall
0, // border is 0
GL_RGBA, GL_UNSIGNED_BYTE, uScanLineTex);
// if shaders don't init properly, then shutdown
if (init_shader())
{
// we need a mutex to handle thread syncing
g_vldp_gl_mutex = SDL_CreateMutex();
// if mutex creates successfully
if (g_vldp_gl_mutex)
{
// IMPORTANT: this must be set to true before on_palette_change_gl is called!!!
g_bOpenGLInitialized = true;
// This is important for us to do here, because the palette may have been finalized
// before we initialized VLDP GL, and it may never change hereafter. Therefore,
// we need to make sure that it is set at least once.
on_palette_change_gl();
bResult = true;
}
else
{
printline("g_vldp_gl_mutex could not be created");
}
}
}
else bResult = true;
return bResult;
}
void free_gl_resources()
{
// if we successfully created a mutex previously, then destroy it now
if (g_vldp_gl_mutex)
{
SDL_DestroyMutex(g_vldp_gl_mutex);
g_vldp_gl_mutex = NULL;
}
for (unsigned int u = 0; u < YUV_BUF_COUNT; ++u)
{
MPO_FREE(g_ptr[u][Y_IDX]);
MPO_FREE(g_ptr[u][U_IDX]);
MPO_FREE(g_ptr[u][V_IDX]);
}
}
// gets called when the color palette changes
void on_palette_change_gl()
{
// this function may get called before we've initialized our GL stuff, in which
// case, we will have to ignore it.
if (g_bOpenGLInitialized)
{
// These shouldn't be necessary to do here, but I've left them here, commented out,
// in case problems are discovered on other hardware and they are needed.
// glActiveTexture(GL_TEXTURE1);
// glBindTexture(GL_TEXTURE_1D, g_textureIDs[TEX_VID_PALETTE]);
// apply modified palette to palette texture
glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE,
g_uRGBAPalette);
}
// else we're not initialized, so there's nothing we can do ...
}
//////////////////////////////
// threaded stuff here
// opengl version of the callback
int prepare_frame_GL_callback(struct yuv_buf *src)
{
int result = VLDP_TRUE;
VLDP_GL_LOCK;
/*
// used to test super don overflow problem, can probably be removed once we're
// sure the problem is really gone :)
if (*((unsigned int *) src->Y) == 0xbbbec1c2)
{
int i = 0;
}
*/
// NOTE : we have multiple YUV buffers because, due to threading, it's possible
// for these callbacks to get called many times before the gl_think function
// is called.
// One scenario: prepare callback, display callback, followed by another prepare
// callback. Now we have 2 frames prepared, 1 frame requested to be displayed,
// and our gl_think hasn't even run yet! We need a way to keep track of all
// of this stuff without throwing anything away (hopefully).
// if we're out of buffer room, we'll have to kill an old frame and
// not display it
while ((g_uFrameDispReq - g_uFrameDispAck) >= YUV_BUF_COUNT)
{
// this effectively will ensure that the frame is dropped
// (this should never happen but it's here as a safety)
++g_uFrameDispAck;
}
// If a prepared frame hasn't been displayed, then we should overwrite it instead of keeping it.
g_uFramePrepReq = g_uFrameDispReq + 1;
// this handles auto-wraparound for us nicely
unsigned int uIdx = g_uFramePrepReq % YUV_BUF_COUNT;
// store decoded frame for displaying later
memcpy(g_ptr[uIdx][Y_IDX], src->Y, src->Y_size);
memcpy(g_ptr[uIdx][U_IDX], src->U, src->UV_size);
memcpy(g_ptr[uIdx][V_IDX], src->V, src->UV_size);
VLDP_GL_UNLOCK;
return result;
}
void display_frame_GL_callback(struct yuv_buf *buf)
{
VLDP_GL_LOCK;
// set the request display frame to the last prepared frame
g_uFrameDispReq = g_uFramePrepReq;
VLDP_GL_UNLOCK;
}
void report_mpeg_dimensions_GL_callback(int width, int height)
{
VLDP_GL_LOCK;
// blitting is not allowed once we create the YUV overlay ...
g_ldp->set_blitting_allowed(false);
if (((unsigned) width != g_uTexWidth) || ((unsigned) height != g_uTexHeight))
{
for (unsigned int u = 0; u < YUV_BUF_COUNT; ++u)
{
MPO_FREE(g_ptr[u][Y_IDX]);
MPO_FREE(g_ptr[u][U_IDX]);
MPO_FREE(g_ptr[u][V_IDX]);
g_ptr[u][Y_IDX] = MPO_MALLOC(width * height);
g_ptr[u][U_IDX] = MPO_MALLOC((width * height) >> 2);
g_ptr[u][V_IDX] = MPO_MALLOC((width * height) >> 2);
}
}
g_uTexWidth = width;
g_uTexHeight = height;
VLDP_GL_UNLOCK;
}
void render_blank_frame_GL_callback()
{
VLDP_GL_LOCK;
g_bBlankRequested = true;
// force a frame update to ensure blanking happens
// (we don't need to populate the YUV buffer because the frame will be blank)
++g_uFramePrepReq;
++g_uFrameDispReq;
VLDP_GL_UNLOCK;
}
#endif // USE_OPENGL