class QAlphaBuf | .h |
constructor | QAlphaBuf(int _wid,int _hgt,int _buffers) : QDrawable() |
destructor | ~QAlphaBuf() |
GetGLXDrawable | GLXDrawable GetGLXDrawable() |
SetGenlock | void SetGenlock(bool yn) Set genlock mode BEFORE calling Create() |
Create | bool Create() |
Destroy | void Destroy() |
Trigger | void Trigger() |
HandleEvents | void HandleEvents() Waits for a transfercomplete |
Swap | void Swap() Swap to back buffer |
Select | void Select() |
Equalize | void Equalize(QRect *r) Equalizing the alpha buffers is NOT trivial Both pbuf[]'s are held on, and changed during a Swap() This way, we can copy from one pbuf to another BIG HACK!!! |
HoldBuffers | void HoldBuffers(int n) When you have an alpha buffer pool with more than 2 buffers, the usual Swap() will walk along all buffers. This makes regular programming which assumes just 1 front and 1 back buffer go bad. With this function you can hold on to buffers, to leave 2 buffers when normal drawing is in place (as expected by many gfx routines). We need the extra buffers however for movie playback for example; this would be too slow when only 2 buffers are allowed. Perhaps a different heuristic for swapping might help too. Alpha swapping is slow as it is. Note that the total number of buffers held is 'n'; if you specify: HoldBuffers(5); HoldBuffers(3); then the total #buffers held is 3. (the other 2 are freed in fact) |
Flat functions |
QNap | QNap(50); qdbg(" later: free=%d\n",bufPool->GetFreeBuffers()); #endif #endif } |
/*
* QAlphaBuf - alpha double buffer, for use under QBC for example
* 17-00-00: Created!
* 01-03-01: Max buffers can be set; handy for playing movies.
* NOTES:
* - This does nothing much on Win32; video? what? PAL? Huh, friend? ;-)
* (C) MarketGraph/RVG
*/
#include <qlib/app.h>
#include <qlib/gel.h>
#include <qlib/bc.h>
#include <qlib/common/dmedia.h>
#include <qlib/opengl.h>
#include <qlib/info.h>
#include <stdio.h>
#pragma hdrstop
#include <qlib/alphabuf.h>
#include <unistd.h>
#ifdef WIN32
#include <windows.h>
#include <GL/gl.h>
#endif
#ifdef OBS
#include <unistd.h>
#include <limits.h>
#include <X11/Xlib.h>
#include <GL/glx.h>
#endif
#include <qlib/debug.h>
DEBUG_ENABLE
// $DEV; should be in the class
// Hold buffers to avoid skipping across many buffers when swapping
static QDMBuffer *holdBuffer[QAlphaBuf::MAX_BUFFER];
QAlphaBuf::QAlphaBuf(int _wid,int _hgt,int _buffers)
: QDrawable()
{
int i;
wid=_wid; hgt=_hgt;
_buffers=16;
buffers=_buffers;
backBufferIndex=0;
// Initially, all buffers are available for use
maxBufferIndex=buffers;
flags=TRIGGER;
transfersComplete=0;
vout=0;
for(i=0;i<buffers;i++)
{ pbuf[i]=0;
}
bufPool=0;
}
QAlphaBuf::~QAlphaBuf()
{
}
GLXDrawable QAlphaBuf::GetGLXDrawable()
{
if(pbuf[backBufferIndex])
return pbuf[backBufferIndex]->GetGLXPBuffer();
return 0;
}
void QAlphaBuf::SetGenlock(bool yn)
// Set genlock mode BEFORE calling Create()
{
if(yn)flags|=GENLOCK;
else flags&=~GENLOCK;
}
bool QAlphaBuf::Create()
{
#ifdef WIN32
qerr("QAlphaBuf:Create() nyi/win32");
return FALSE;
#else
int w,h,i;
// Create video out path
vout=new QDMVideoOut();
vout->SetLayout(VL_LAYOUT_GRAPHICS);
vout->SetPacking(VL_PACKING_ABGR_8);
vout->SetGenlock(flags&GENLOCK?TRUE:FALSE);
vout->SetSize(wid,hgt);
// No fields
vout->SetCaptureType(VL_CAPTURE_INTERLEAVED);
//vout->SetSize(wid,hgt/div);
vout->GetSize(&w,&h);
qdbg("QAlphaBuf: video size: %dx%d\n",w,h);
// Create pbuffer
for(i=0;i<buffers;i++)
{ pbuf[i]=new QDMPBuffer(w,h);
}
// Bufferpool; not cached, mapped
bufPool=new QDMBPool(buffers,wid*hgt*4,FALSE,TRUE);
bufPool->AddProducer(pbuf[0]);
bufPool->AddConsumer(vout);
if(!bufPool->Create())
{ qerr("QAlphaBuf: can't create pbuffer pool");
return FALSE;
}
vout->RegisterPool(bufPool);
qdbg("bufPool: buffers=%d\n",bufPool->GetFreeBuffers());
#ifdef ND_NO_PREALLOC
// Allocate the buffers now (we won't let go on them)
for(i=0;i<buffers;i++)
{
dmBuf[i]=bufPool->AllocateDMBuffer();
qdbg(" dmBuffer[%d]=%p SGI %p\n",i,dmBuf[i],dmBuf[i]->GetDMbuffer());
}
#endif
qdbg("bufPool: buffers=%d\n",bufPool->GetFreeBuffers());
// Create canvas
cv=new QCanvas(this);
qdbg(" cv=%p\n",cv);
// Get rid of extraneous VL buffer
bufPool->AllocateDMBuffer();
// Get buffers and video installed
Swap();
Swap();
return TRUE;
#endif
}
void QAlphaBuf::Destroy()
{
qerr("QAlphaBuf:Destroy() NYI");
}
void QAlphaBuf::Trigger()
{
#ifdef WIN32
#else
if(!(flags&TRIGGER))return;
qdbg("Trigger vout\n");
vout->Start();
flags&=~TRIGGER;
#endif
}
void QAlphaBuf::HandleEvents()
// Waits for a transfercomplete
{
#ifdef WIN32
// No events here
#else
QVideoServer *vs;
vs=app->GetVideoServer();
if(flags&TRIGGER)
Trigger();
int i,n;
fd_set fdr,fdw;
static int maxFD;
VLEvent vlEvent;
if(!maxFD)
{ // Create an FD for the output path
vout->GetFD();
// Get largest FD for select()
maxFD=getdtablehi();
}
return;
qdbg("HandleEvents: transfersComplete=%d\n",transfersComplete);
do
{
FD_ZERO(&fdr);
FD_ZERO(&fdw);
FD_SET(vout->GetFD(),&fdr);
if(select(maxFD+1,&fdr,&fdw,0,0)==-1)
{ qerr("QAlphaBuf: select"); break;
}
if(FD_ISSET(vout->GetFD(),&fdr))
{
// >=1 events on video out
n=vs->Pending();
qdbg(" %d events\n",n);
for(i=0;i<n;i++)
{
vs->NextEvent(&vlEvent);
qdbg("vout: %s\n",vs->Reason2Str(vlEvent.reason)); fflush(stderr);
if(vlEvent.reason==VLTransferComplete)
{ transfersComplete++;
} else if(vlEvent.reason==VLSequenceLost)
{
qdbg("vout: seqLost %d frames\n",vlEvent.vlsequencelost.count);
}
}
}
break;
} while(transfersComplete<=0);
#endif
}
/***********
* Swapping *
***********/
void QAlphaBuf::Swap()
// Swap to back buffer
{
#ifdef WIN32
// Not supported on Win32
#else
QDMBuffer *db;
// Back buffer
static QDMBuffer *bbuf=0;
static bool pBufInUse[MAX_BUFFER];
//qdbg("QAlphaBuf:Swap() at vtrace %d\n",app->GetVTraceCount());
if(bbuf)
{
// De-associate old buffer
#ifdef OBS
QDMBuffer *dmb;
dmb=new QDMBuffer(0);
#endif
//qdbg(" de-ass old\n");
if(pBufInUse[backBufferIndex^1])
pbuf[backBufferIndex^1]->Associate(0);
//delete dmb;
// Wait for last frame to have been completed
HandleEvents();
//qdbg(" cur to vout\n");
// Send current buffer to video out
if(cv->GetGLContext())
{//qdbg("finish\n");
cv->Select();
//glFinish();
}
vout->Send(bbuf->GetDMbuffer());
//vout->Send(dmBuf[backBufferIndex]->GetDMbuffer());
//qdbg(" free bbuf\n");
bbuf->Free();
delete bbuf;
//qdbg(" evts\n");
//HandleEvents();
transfersComplete--;
backBufferIndex^=1;
//qdbg(" VTB=%d\n",app->GetVTraceCount());
//QNap(50);
}
// Get next buffer
//qdbg(" Get free buffer\n");
//db=dmBuf[backBufferIndex];
while(bufPool->GetFreeBuffers()<=0)
{ //qdbg("waiting for free bufs\n");
QNap(1);
}
bbuf=bufPool->AllocateDMBuffer();
//qdbg("bbuf=%p SGI %p\n",bbuf,bbuf->GetDMbuffer());
// Associate (back) canvas with new DMbuffer
// NOTE: old buffer is attempted to be freed by glXAssociateDMPBufferSGIX
//qdbg("declaring back buffer %p\n",db->GetDMbuffer());
//void *p=dmBufferMapData(bbuf->GetDMbuffer());
//qdbg("declaring back buffer %p @%p\n",bbuf->GetDMbuffer(),p);
#ifdef OBS
db->Attach();
pbuf->Associate(db);
db->Free();
#endif
pbuf[backBufferIndex]->Associate(bbuf);
pbuf[backBufferIndex]->Select();
glViewport(0,0,wid,hgt);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glOrtho(0,wid,0,hgt,-1,1);
cv->SetGLContext(pbuf[backBufferIndex]->GetGLContext());
cv->GetGLContext()->SetDrawable(this);
//qdbg("CV glctx %p\n",cv->GetGLContext());
//qdbg(" pbuf glctx=%p\n",pbuf->GetGLContext());
cv->Select();
pBufInUse[backBufferIndex]=TRUE;
#endif
}
void QAlphaBuf::Select()
{
#ifdef WIN32
// No support
QTRACE("QAlphaBuf:Select() nyi on Win32");
#else
pbuf[backBufferIndex]->Select();
#endif
}
/*************
* Equalizing *
*************/
void QAlphaBuf::Equalize(QRect *r)
// Equalizing the alpha buffers is NOT trivial
// Both pbuf[]'s are held on, and changed during a Swap()
// This way, we can copy from one pbuf to another
// BIG HACK!!!
{
#ifndef WIN32
GLXContext ctxBack;
int dummy;
if(r)
{ // Reverse Y (OpenGL coord system)
r->y=576-r->hgt-r->y;
}
SetCurrentQGLContext(0);
ctxBack=cv->GetGLContext()->GetGLXContext();
glXMakeCurrentReadSGI(XDSP,
pbuf[backBufferIndex]->GetGLXPBuffer(), // Draw
pbuf[backBufferIndex^1]->GetGLXPBuffer(), // Read
ctxBack); // Context
glDisable(GL_BLEND);
glDisable(GL_DITHER);
glRasterPos2i(0,0);
if(r)glBitmap(0,0,0,0,r->x,r->y,(const GLubyte *)&dummy);
glPixelStorei(GL_UNPACK_ROW_LENGTH,0);
glPixelStorei(GL_UNPACK_SKIP_ROWS,0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS,0);
glPixelStorei(GL_PACK_SKIP_ROWS,0);
glPixelStorei(GL_PACK_SKIP_PIXELS,0);
if(!r)glCopyPixels(0,0,wid,hgt,GL_COLOR);
else glCopyPixels(r->x,r->y,r->wid,r->hgt,GL_COLOR);
glEnable(GL_DITHER);
#endif
}
/************************
* Holding on to buffers *
************************/
void QAlphaBuf::HoldBuffers(int n)
// When you have an alpha buffer pool with more than 2 buffers,
// the usual Swap() will walk along all buffers. This makes
// regular programming which assumes just 1 front and 1 back buffer
// go bad.
// With this function you can hold on to buffers, to leave 2 buffers
// when normal drawing is in place (as expected by many gfx routines).
// We need the extra buffers however for movie playback for example;
// this would be too slow when only 2 buffers are allowed. Perhaps
// a different heuristic for swapping might help too. Alpha swapping
// is slow as it is.
// Note that the total number of buffers held is 'n'; if you specify:
// HoldBuffers(5); HoldBuffers(3); then the total #buffers held is 3.
// (the other 2 are freed in fact)
{
#ifdef WIN32
// NYI on Win32
return;
#else
int i,count;
// Check arg
if(n>buffers)
{
qwarn("QAlphaBuf:HoldBuffers(%d); max held buffers is %d\n",n,buffers);
return;
}
qdbg("HoldBuffers(%d)\n",n);
qdbg(" buffers=%d, pre: free=%d\n",buffers,bufPool->GetFreeBuffers());
while(bufPool->GetFreeBuffers()<n)
{
qdbg("Waiting for enough (%d) free buffers (free=%d)\n",
n,bufPool->GetFreeBuffers());
QNap(10);
}
// Count #buffers
// Make sure 'n' buffers are held
for(i=0;i<n;i++)
{
if(!holdBuffer[i])
{
// Allocate it
//qdbg(" allocate %d\n",i);
holdBuffer[i]=bufPool->AllocateDMBuffer();
} // else it's already being held
}
// Free the rest
for(;i<buffers;i++)
{
if(holdBuffer[i])
{
// Free it
//qdbg(" free %d\n",i);
holdBuffer[i]->Free();
delete holdBuffer[i];
holdBuffer[i]=0;
}
}
#ifdef OBS
qdbg(" after: free=%d\n",bufPool->GetFreeBuffers());
QNap(50);
qdbg(" later: free=%d\n",bufPool->GetFreeBuffers());
#endif
#endif
}
}