diff options
Diffstat (limited to 'src/engine/client/backend_sdl.cpp')
| -rw-r--r-- | src/engine/client/backend_sdl.cpp | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/src/engine/client/backend_sdl.cpp b/src/engine/client/backend_sdl.cpp new file mode 100644 index 00000000..691d193c --- /dev/null +++ b/src/engine/client/backend_sdl.cpp @@ -0,0 +1,495 @@ + +#include "SDL.h" +#include "SDL_opengl.h" + +#include "graphics_threaded.h" +#include "backend_sdl.h" + +// ------------ CGraphicsBackend_Threaded + +void CGraphicsBackend_Threaded::ThreadFunc(void *pUser) +{ + CGraphicsBackend_Threaded *pThis = (CGraphicsBackend_Threaded *)pUser; + + while(!pThis->m_Shutdown) + { + pThis->m_Activity.wait(); + if(pThis->m_pBuffer) + { + pThis->m_pProcessor->RunBuffer(pThis->m_pBuffer); + sync_barrier(); + pThis->m_pBuffer = 0x0; + pThis->m_BufferDone.signal(); + } + } +} + +CGraphicsBackend_Threaded::CGraphicsBackend_Threaded() +{ + m_pBuffer = 0x0; + m_pProcessor = 0x0; + m_pThread = 0x0; +} + +void CGraphicsBackend_Threaded::StartProcessor(ICommandProcessor *pProcessor) +{ + m_Shutdown = false; + m_pProcessor = pProcessor; + m_pThread = thread_create(ThreadFunc, this); + m_BufferDone.signal(); +} + +void CGraphicsBackend_Threaded::StopProcessor() +{ + m_Shutdown = true; + m_Activity.signal(); + thread_wait(m_pThread); + thread_destroy(m_pThread); +} + +void CGraphicsBackend_Threaded::RunBuffer(CCommandBuffer *pBuffer) +{ + WaitForIdle(); + m_pBuffer = pBuffer; + m_Activity.signal(); +} + +bool CGraphicsBackend_Threaded::IsIdle() const +{ + return m_pBuffer == 0x0; +} + +void CGraphicsBackend_Threaded::WaitForIdle() +{ + while(m_pBuffer != 0x0) + m_BufferDone.wait(); +} + + +// ------------ CCommandProcessorFragment_General + +void CCommandProcessorFragment_General::Cmd_Signal(const CCommandBuffer::SCommand_Signal *pCommand) +{ + pCommand->m_pSemaphore->signal(); +} + +bool CCommandProcessorFragment_General::RunCommand(const CCommandBuffer::SCommand * pBaseCommand) +{ + switch(pBaseCommand->m_Cmd) + { + case CCommandBuffer::CMD_NOP: break; + case CCommandBuffer::CMD_SIGNAL: Cmd_Signal(static_cast<const CCommandBuffer::SCommand_Signal *>(pBaseCommand)); break; + default: return false; + } + + return true; +} + +// ------------ CCommandProcessorFragment_OpenGL + +int CCommandProcessorFragment_OpenGL::TexFormatToOpenGLFormat(int TexFormat) +{ + if(TexFormat == CCommandBuffer::TEXFORMAT_RGB) return GL_RGB; + if(TexFormat == CCommandBuffer::TEXFORMAT_ALPHA) return GL_ALPHA; + if(TexFormat == CCommandBuffer::TEXFORMAT_RGBA) return GL_RGBA; + return GL_RGBA; +} + +void CCommandProcessorFragment_OpenGL::SetState(const CCommandBuffer::SState &State) +{ + // blend + switch(State.m_BlendMode) + { + case CCommandBuffer::BLEND_NONE: + glDisable(GL_BLEND); + break; + case CCommandBuffer::BLEND_ALPHA: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + break; + case CCommandBuffer::BLEND_ADDITIVE: + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + break; + default: + dbg_msg("render", "unknown blendmode %d\n", State.m_BlendMode); + }; + + // clip + if(State.m_ClipEnable) + { + glScissor(State.m_ClipX, State.m_ClipY, State.m_ClipW, State.m_ClipH); + glEnable(GL_SCISSOR_TEST); + } + else + glDisable(GL_SCISSOR_TEST); + + // texture + if(State.m_Texture >= 0 && State.m_Texture < CCommandBuffer::MAX_TEXTURES) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_aTextures[State.m_Texture]); + } + else + glDisable(GL_TEXTURE_2D); + + // screen mapping + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(State.m_ScreenTL.x, State.m_ScreenBR.x, State.m_ScreenBR.y, State.m_ScreenTL.y, 1.0f, 10.f); +} + +void CCommandProcessorFragment_OpenGL::Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand) +{ + glBindTexture(GL_TEXTURE_2D, m_aTextures[pCommand->m_Slot]); + glTexSubImage2D(GL_TEXTURE_2D, 0, pCommand->m_X, pCommand->m_Y, pCommand->m_Width, pCommand->m_Height, + TexFormatToOpenGLFormat(pCommand->m_Format), GL_UNSIGNED_BYTE, pCommand->m_pData); + mem_free(pCommand->m_pData); +} + +void CCommandProcessorFragment_OpenGL::Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand) +{ + glDeleteTextures(1, &m_aTextures[pCommand->m_Slot]); +} + +void CCommandProcessorFragment_OpenGL::Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand) +{ + int Oglformat = TexFormatToOpenGLFormat(pCommand->m_Format); + int StoreOglformat = TexFormatToOpenGLFormat(pCommand->m_StoreFormat); + + glGenTextures(1, &m_aTextures[pCommand->m_Slot]); + glBindTexture(GL_TEXTURE_2D, m_aTextures[pCommand->m_Slot]); + + if(pCommand->m_Flags&CCommandBuffer::TEXFLAG_NOMIPMAPS) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, StoreOglformat, pCommand->m_Width, pCommand->m_Height, 0, Oglformat, GL_UNSIGNED_BYTE, pCommand->m_pData); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + gluBuild2DMipmaps(GL_TEXTURE_2D, StoreOglformat, pCommand->m_Width, pCommand->m_Height, Oglformat, GL_UNSIGNED_BYTE, pCommand->m_pData); + } + + mem_free(pCommand->m_pData); +} + +void CCommandProcessorFragment_OpenGL::Cmd_Clear(const CCommandBuffer::SCommand_Clear *pCommand) +{ + glClearColor(pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void CCommandProcessorFragment_OpenGL::Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand) +{ + SetState(pCommand->m_State); + + glVertexPointer(3, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices); + glTexCoordPointer(2, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, sizeof(CCommandBuffer::SVertex), (char*)pCommand->m_pVertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + switch(pCommand->m_PrimType) + { + case CCommandBuffer::PRIMTYPE_QUADS: + glDrawArrays(GL_QUADS, 0, pCommand->m_PrimCount*4); + break; + case CCommandBuffer::PRIMTYPE_LINES: + glDrawArrays(GL_LINES, 0, pCommand->m_PrimCount*2); + break; + default: + dbg_msg("render", "unknown primtype %d\n", pCommand->m_Cmd); + }; +} + +void CCommandProcessorFragment_OpenGL::Cmd_Screenshot(const CCommandBuffer::SCommand_Screenshot *pCommand) +{ + // fetch image data + GLint aViewport[4] = {0,0,0,0}; + glGetIntegerv(GL_VIEWPORT, aViewport); + + int w = aViewport[2]; + int h = aViewport[3]; + + // we allocate one more row to use when we are flipping the texture + unsigned char *pPixelData = (unsigned char *)mem_alloc(w*(h+1)*3, 1); + unsigned char *pTempRow = pPixelData+w*h*3; + + // fetch the pixels + GLint Alignment; + glGetIntegerv(GL_PACK_ALIGNMENT, &Alignment); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pPixelData); + glPixelStorei(GL_PACK_ALIGNMENT, Alignment); + + // flip the pixel because opengl works from bottom left corner + for(int y = 0; y < h/2; y++) + { + mem_copy(pTempRow, pPixelData+y*w*3, w*3); + mem_copy(pPixelData+y*w*3, pPixelData+(h-y-1)*w*3, w*3); + mem_copy(pPixelData+(h-y-1)*w*3, pTempRow,w*3); + } + + // fill in the information + pCommand->m_pImage->m_Width = w; + pCommand->m_pImage->m_Height = h; + pCommand->m_pImage->m_Format = CImageInfo::FORMAT_RGB; + pCommand->m_pImage->m_pData = pPixelData; +} + +CCommandProcessorFragment_OpenGL::CCommandProcessorFragment_OpenGL() +{ + mem_zero(m_aTextures, sizeof(m_aTextures)); +} + +bool CCommandProcessorFragment_OpenGL::RunCommand(const CCommandBuffer::SCommand * pBaseCommand) +{ + switch(pBaseCommand->m_Cmd) + { + case CCommandBuffer::CMD_TEXTURE_CREATE: Cmd_Texture_Create(static_cast<const CCommandBuffer::SCommand_Texture_Create *>(pBaseCommand)); break; + case CCommandBuffer::CMD_TEXTURE_DESTROY: Cmd_Texture_Destroy(static_cast<const CCommandBuffer::SCommand_Texture_Destroy *>(pBaseCommand)); break; + case CCommandBuffer::CMD_TEXTURE_UPDATE: Cmd_Texture_Update(static_cast<const CCommandBuffer::SCommand_Texture_Update *>(pBaseCommand)); break; + case CCommandBuffer::CMD_CLEAR: Cmd_Clear(static_cast<const CCommandBuffer::SCommand_Clear *>(pBaseCommand)); break; + case CCommandBuffer::CMD_RENDER: Cmd_Render(static_cast<const CCommandBuffer::SCommand_Render *>(pBaseCommand)); break; + case CCommandBuffer::CMD_SCREENSHOT: Cmd_Screenshot(static_cast<const CCommandBuffer::SCommand_Screenshot *>(pBaseCommand)); break; + default: return false; + } + + return true; +} + + +// ------------ CCommandProcessorFragment_SDL + +void CCommandProcessorFragment_SDL::Cmd_Init(const SCommand_Init *pCommand) +{ + m_GLContext = pCommand->m_Context; + GL_MakeCurrent(m_GLContext); + + // set some default settings + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glAlphaFunc(GL_GREATER, 0); + glEnable(GL_ALPHA_TEST); + glDepthMask(0); +} + +void CCommandProcessorFragment_SDL::Cmd_Shutdown(const SCommand_Shutdown *pCommand) +{ + GL_ReleaseContext(m_GLContext); +} + +void CCommandProcessorFragment_SDL::Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand) +{ + GL_SwapBuffers(m_GLContext); +} + +void CCommandProcessorFragment_SDL::Cmd_VideoModes(const CCommandBuffer::SCommand_VideoModes *pCommand) +{ + // TODO: fix this code on osx or windows + SDL_Rect **ppModes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN); + if(ppModes == NULL) + { + // no modes + *pCommand->m_pNumModes = 0; + } + else if(ppModes == (SDL_Rect**)-1) + { + // no modes + *pCommand->m_pNumModes = 0; + } + else + { + int NumModes = 0; + for(int i = 0; ppModes[i]; ++i) + { + if(NumModes == pCommand->m_MaxModes) + break; + pCommand->m_pModes[NumModes].m_Width = ppModes[i]->w; + pCommand->m_pModes[NumModes].m_Height = ppModes[i]->h; + pCommand->m_pModes[NumModes].m_Red = 8; + pCommand->m_pModes[NumModes].m_Green = 8; + pCommand->m_pModes[NumModes].m_Blue = 8; + NumModes++; + } + + *pCommand->m_pNumModes = NumModes; + } +} + +CCommandProcessorFragment_SDL::CCommandProcessorFragment_SDL() +{ +} + +bool CCommandProcessorFragment_SDL::RunCommand(const CCommandBuffer::SCommand *pBaseCommand) +{ + switch(pBaseCommand->m_Cmd) + { + case CCommandBuffer::CMD_SWAP: Cmd_Swap(static_cast<const CCommandBuffer::SCommand_Swap *>(pBaseCommand)); break; + case CCommandBuffer::CMD_VIDEOMODES: Cmd_VideoModes(static_cast<const CCommandBuffer::SCommand_VideoModes *>(pBaseCommand)); break; + case CMD_INIT: Cmd_Init(static_cast<const SCommand_Init *>(pBaseCommand)); break; + case CMD_SHUTDOWN: Cmd_Shutdown(static_cast<const SCommand_Shutdown *>(pBaseCommand)); break; + default: return false; + } + + return true; +} + +// ------------ CCommandProcessor_SDL_OpenGL + +void CCommandProcessor_SDL_OpenGL::RunBuffer(CCommandBuffer *pBuffer) +{ + unsigned CmdIndex = 0; + while(1) + { + const CCommandBuffer::SCommand *pBaseCommand = pBuffer->GetCommand(&CmdIndex); + if(pBaseCommand == 0x0) + break; + + if(m_OpenGL.RunCommand(pBaseCommand)) + continue; + + if(m_SDL.RunCommand(pBaseCommand)) + continue; + + if(m_General.RunCommand(pBaseCommand)) + continue; + + dbg_msg("graphics", "unknown command %d", pBaseCommand->m_Cmd); + } +} + +// ------------ CGraphicsBackend_SDL_OpenGL + +int CGraphicsBackend_SDL_OpenGL::Init(const char *pName, int Width, int Height, int FsaaSamples, int Flags) +{ + if(!SDL_WasInit(SDL_INIT_VIDEO)) + { + if(SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) + { + dbg_msg("gfx", "unable to init SDL video: %s", SDL_GetError()); + return -1; + } + + #ifdef CONF_FAMILY_WINDOWS + if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) // ignore_convention + putenv("SDL_VIDEO_WINDOW_POS=8,27"); // ignore_convention + #endif + } + + const SDL_VideoInfo *pInfo = SDL_GetVideoInfo(); + + // set flags + int SdlFlags = SDL_OPENGL; + if(Flags&IGraphicsBackend::INITFLAG_RESIZABLE) + SdlFlags |= SDL_RESIZABLE; + + if(pInfo->hw_available) // ignore_convention + SdlFlags |= SDL_HWSURFACE; + else + SdlFlags |= SDL_SWSURFACE; + + if(pInfo->blit_hw) // ignore_convention + SdlFlags |= SDL_HWACCEL; + + if(Flags&IGraphicsBackend::INITFLAG_FULLSCREEN) + SdlFlags |= SDL_FULLSCREEN; + + // set gl attributes + if(FsaaSamples) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, FsaaSamples); + } + else + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + } + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, Flags&CCommandBuffer::INITFLAG_VSYNC ? 1 : 0); + + // set caption + SDL_WM_SetCaption(pName, pName); + + // create window + m_pScreenSurface = SDL_SetVideoMode(Width, Height, 0, SdlFlags); + if(!m_pScreenSurface) + { + dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); + //*pCommand->m_pResult = -1; + return -1; + } + + SDL_ShowCursor(0); + + // fetch gl contexts and release the context from this thread + m_GLContext = GL_GetCurrentContext(); + GL_ReleaseContext(m_GLContext); + + // start the command processor + m_pProcessor = new CCommandProcessor_SDL_OpenGL; + StartProcessor(m_pProcessor); + + // issue a init command + CCommandBuffer CmdBuffer(1024, 512); + CCommandProcessorFragment_SDL::SCommand_Init Cmd; + Cmd.m_Context = m_GLContext; + CmdBuffer.AddCommand(Cmd); + RunBuffer(&CmdBuffer); + WaitForIdle(); + + // return + return 0; +} + +int CGraphicsBackend_SDL_OpenGL::Shutdown() +{ + // issue a shutdown command + CCommandBuffer CmdBuffer(1024, 512); + CCommandProcessorFragment_SDL::SCommand_Shutdown Cmd; + CmdBuffer.AddCommand(Cmd); + RunBuffer(&CmdBuffer); + WaitForIdle(); + + // stop and delete the processor + StopProcessor(); + delete m_pProcessor; + m_pProcessor = 0; + + SDL_QuitSubSystem(SDL_INIT_VIDEO); + return 0; +} + +void CGraphicsBackend_SDL_OpenGL::Minimize() +{ + SDL_WM_IconifyWindow(); +} + +void CGraphicsBackend_SDL_OpenGL::Maximize() +{ + // TODO: SDL +} + +int CGraphicsBackend_SDL_OpenGL::WindowActive() +{ + return SDL_GetAppState()&SDL_APPINPUTFOCUS; +} + +int CGraphicsBackend_SDL_OpenGL::WindowOpen() +{ + return SDL_GetAppState()&SDL_APPACTIVE; + +} + + +IGraphicsBackend *CreateGraphicsBackend() { return new CGraphicsBackend_SDL_OpenGL; } |