ddnet/src/engine/client/backend_sdl.h

313 lines
8.2 KiB
C++

#include "SDL.h"
#include "graphics_threaded.h"
// platform dependent implementations for transfering render context from the main thread to the graphics thread
// TODO: when SDL 1.3 comes, this can be removed
#if defined(CONF_FAMILY_WINDOWS)
struct SGLContext
{
HDC m_hDC;
HGLRC m_hGLRC;
};
static SGLContext GL_GetCurrentContext()
{
SGLContext Context;
Context.m_hDC = wglGetCurrentDC();
Context.m_hGLRC = wglGetCurrentContext();
return Context;
}
static void GL_MakeCurrent(const SGLContext &Context) { wglMakeCurrent(Context.m_hDC, Context.m_hGLRC); }
static void GL_ReleaseContext(const SGLContext &Context) { wglMakeCurrent(NULL, NULL); }
static void GL_SwapBuffers(const SGLContext &Context) { SwapBuffers(Context.m_hDC); }
#elif defined(CONF_PLATFORM_MACOSX)
#include <objc/objc-runtime.h>
class semaphore
{
SDL_sem *sem;
public:
semaphore() { sem = SDL_CreateSemaphore(0); }
~semaphore() { SDL_DestroySemaphore(sem); }
void wait() { SDL_SemWait(sem); }
void signal() { SDL_SemPost(sem); }
};
struct SGLContext
{
id m_Context;
};
static SGLContext GL_GetCurrentContext()
{
SGLContext Context;
Class NSOpenGLContextClass = (Class) objc_getClass("NSOpenGLContext");
SEL selector = sel_registerName("currentContext");
Context.m_Context = objc_msgSend((objc_object*) NSOpenGLContextClass, selector);
return Context;
}
static void GL_MakeCurrent(const SGLContext &Context)
{
SEL selector = sel_registerName("makeCurrentContext");
objc_msgSend(Context.m_Context, selector);
}
static void GL_ReleaseContext(const SGLContext &Context)
{
Class NSOpenGLContextClass = (Class) objc_getClass("NSOpenGLContext");
SEL selector = sel_registerName("clearCurrentContext");
objc_msgSend((objc_object*) NSOpenGLContextClass, selector);
}
static void GL_SwapBuffers(const SGLContext &Context)
{
SEL selector = sel_registerName("flushBuffer");
objc_msgSend(Context.m_Context, selector);
}
class CAutoreleasePool
{
private:
id m_Pool;
public:
CAutoreleasePool()
{
Class NSAutoreleasePoolClass = (Class) objc_getClass("NSAutoreleasePool");
m_Pool = class_createInstance(NSAutoreleasePoolClass, 0);
SEL selector = sel_registerName("init");
objc_msgSend(m_Pool, selector);
}
~CAutoreleasePool()
{
SEL selector = sel_registerName("drain");
objc_msgSend(m_Pool, selector);
}
};
#elif defined(CONF_FAMILY_UNIX)
#if defined(__ANDROID__)
#include <EGL/egl.h>
struct SGLContext
{
EGLDisplay m_Display;
EGLSurface m_SurfaceDraw;
EGLSurface m_SurfaceRead;
EGLContext m_Context;
};
static SGLContext GL_GetCurrentContext()
{
SGLContext Context;
Context.m_Display = eglGetCurrentDisplay();
Context.m_SurfaceDraw = eglGetCurrentSurface(EGL_DRAW);
Context.m_SurfaceRead = eglGetCurrentSurface(EGL_READ);
Context.m_Context = eglGetCurrentContext();
return Context;
}
static void GL_MakeCurrent(const SGLContext &Context)
{
eglMakeCurrent(Context.m_Display, Context.m_SurfaceDraw, Context.m_SurfaceRead, Context.m_Context);
}
static void GL_ReleaseContext(const SGLContext &Context)
{
eglMakeCurrent(Context.m_Display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
static void GL_SwapBuffers(const SGLContext &Context)
{
// eglSwapBuffers(Context.m_Display, Context.m_SurfaceDraw);
SDL_GL_SwapBuffers(); // This will also draw on-screen keyboard
}
#else
#include <GL/glx.h>
struct SGLContext
{
Display *m_pDisplay;
GLXDrawable m_Drawable;
GLXContext m_Context;
};
static SGLContext GL_GetCurrentContext()
{
SGLContext Context;
Context.m_pDisplay = glXGetCurrentDisplay();
Context.m_Drawable = glXGetCurrentDrawable();
Context.m_Context = glXGetCurrentContext();
return Context;
}
static void GL_MakeCurrent(const SGLContext &Context) { glXMakeCurrent(Context.m_pDisplay, Context.m_Drawable, Context.m_Context); }
static void GL_ReleaseContext(const SGLContext &Context) { glXMakeCurrent(Context.m_pDisplay, None, 0x0); }
static void GL_SwapBuffers(const SGLContext &Context) { glXSwapBuffers(Context.m_pDisplay, Context.m_Drawable); }
#endif
#else
#error missing implementation
#endif
// basic threaded backend, abstract, missing init and shutdown functions
class CGraphicsBackend_Threaded : public IGraphicsBackend
{
public:
// constructed on the main thread, the rest of the functions is runned on the render thread
class ICommandProcessor
{
public:
virtual ~ICommandProcessor() {}
virtual void RunBuffer(CCommandBuffer *pBuffer) = 0;
};
CGraphicsBackend_Threaded();
virtual void RunBuffer(CCommandBuffer *pBuffer);
virtual bool IsIdle() const;
virtual void WaitForIdle();
protected:
void StartProcessor(ICommandProcessor *pProcessor);
void StopProcessor();
private:
ICommandProcessor *m_pProcessor;
CCommandBuffer * volatile m_pBuffer;
volatile bool m_Shutdown;
semaphore m_Activity;
semaphore m_BufferDone;
void *m_pThread;
static void ThreadFunc(void *pUser);
};
// takes care of implementation independent operations
class CCommandProcessorFragment_General
{
void Cmd_Nop();
void Cmd_Signal(const CCommandBuffer::SCommand_Signal *pCommand);
public:
bool RunCommand(const CCommandBuffer::SCommand * pBaseCommand);
};
// takes care of opengl related rendering
class CCommandProcessorFragment_OpenGL
{
struct CTexture
{
GLuint m_Tex;
int m_MemSize;
};
CTexture m_aTextures[CCommandBuffer::MAX_TEXTURES];
volatile int *m_pTextureMemoryUsage;
public:
enum
{
CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_OPENGL,
};
struct SCommand_Init : public CCommandBuffer::SCommand
{
SCommand_Init() : SCommand(CMD_INIT) {}
volatile int *m_pTextureMemoryUsage;
};
private:
static int TexFormatToOpenGLFormat(int TexFormat);
static unsigned char Sample(int w, int h, const unsigned char *pData, int u, int v, int Offset, int ScaleW, int ScaleH, int Bpp);
static void *Rescale(int Width, int Height, int NewWidth, int NewHeight, int Format, const unsigned char *pData);
void SetState(const CCommandBuffer::SState &State);
void Cmd_Init(const SCommand_Init *pCommand);
void Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand);
void Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand);
void Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand);
void Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand);
void Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand);
void Cmd_Screenshot(const CCommandBuffer::SCommand_Screenshot *pCommand);
public:
CCommandProcessorFragment_OpenGL();
bool RunCommand(const CCommandBuffer::SCommand * pBaseCommand);
};
// takes care of sdl related commands
class CCommandProcessorFragment_SDL
{
// SDL stuff
SGLContext m_GLContext;
public:
enum
{
CMD_INIT = CCommandBuffer::CMDGROUP_PLATFORM_SDL,
CMD_SHUTDOWN,
};
struct SCommand_Init : public CCommandBuffer::SCommand
{
SCommand_Init() : SCommand(CMD_INIT) {}
SGLContext m_Context;
};
struct SCommand_Shutdown : public CCommandBuffer::SCommand
{
SCommand_Shutdown() : SCommand(CMD_SHUTDOWN) {}
};
private:
void Cmd_Init(const SCommand_Init *pCommand);
void Cmd_Shutdown(const SCommand_Shutdown *pCommand);
void Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand);
void Cmd_VideoModes(const CCommandBuffer::SCommand_VideoModes *pCommand);
public:
CCommandProcessorFragment_SDL();
bool RunCommand(const CCommandBuffer::SCommand *pBaseCommand);
};
// command processor impelementation, uses the fragments to combine into one processor
class CCommandProcessor_SDL_OpenGL : public CGraphicsBackend_Threaded::ICommandProcessor
{
CCommandProcessorFragment_OpenGL m_OpenGL;
CCommandProcessorFragment_SDL m_SDL;
CCommandProcessorFragment_General m_General;
public:
virtual void RunBuffer(CCommandBuffer *pBuffer);
};
// graphics backend implemented with SDL and OpenGL
class CGraphicsBackend_SDL_OpenGL : public CGraphicsBackend_Threaded
{
SDL_Surface *m_pScreenSurface;
ICommandProcessor *m_pProcessor;
SGLContext m_GLContext;
volatile int m_TextureMemoryUsage;
public:
virtual int Init(const char *pName, int *Width, int *Height, int FsaaSamples, int Flags);
virtual int Shutdown();
virtual int MemoryUsage() const;
virtual void Minimize();
virtual void Maximize();
virtual int WindowActive();
virtual int WindowOpen();
virtual void NotifyWindow();
};