diff --git a/stepmania/src/RageDisplay_OGL_Helpers.h b/stepmania/src/RageDisplay_OGL_Helpers.h index 7f8d686576..ddbaf140c4 100644 --- a/stepmania/src/RageDisplay_OGL_Helpers.h +++ b/stepmania/src/RageDisplay_OGL_Helpers.h @@ -33,6 +33,23 @@ namespace RageDisplay_OGL_Helpers RString GLToString( GLenum e ); }; +class RenderTarget +{ +public: + virtual ~RenderTarget() { } + + virtual unsigned GetTexture() const = 0; + + /* Render to this RenderTarget. */ + virtual void StartRenderingTo() = 0; + + /* Stop rendering to this RenderTarget. Update the texture, if necessary, and + * make it available. */ + virtual void FinishRenderingTo() = 0; + + virtual bool InvertY() const { return false; } +}; + class LowLevelWindow; struct GLExt_t { diff --git a/stepmania/src/arch/LowLevelWindow/LowLevelWindow.h b/stepmania/src/arch/LowLevelWindow/LowLevelWindow.h index b8715fb365..562a4bc88d 100644 --- a/stepmania/src/arch/LowLevelWindow/LowLevelWindow.h +++ b/stepmania/src/arch/LowLevelWindow/LowLevelWindow.h @@ -8,6 +8,8 @@ class DisplayResolution; typedef set DisplayResolutions; class VideoModeParams; +class RenderTarget; +struct RenderTargetParam; class LowLevelWindow { @@ -30,6 +32,9 @@ public: virtual const VideoModeParams &GetActualVideoModeParams() const = 0; + virtual bool SupportsRenderToTexture() const { return false; } + virtual RenderTarget *CreateRenderTarget( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ) { return NULL; } + virtual bool SupportsThreadedRendering() { return false; } virtual void BeginConcurrentRenderingMainThread() { } virtual void EndConcurrentRenderingMainThread() { } diff --git a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp index c145e6d6ce..eb197364b4 100644 --- a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp +++ b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.cpp @@ -8,6 +8,9 @@ #include "DisplayResolutions.h" #include "LocalizedString.h" +#include "RageDisplay_OGL_Helpers.h" +using namespace RageDisplay_OGL_Helpers; + #include #include // ceil() #define GLX_GLXEXT_PROTOTYPES @@ -312,6 +315,174 @@ bool LowLevelWindow_X11::SupportsThreadedRendering() return g_pBackgroundContext != NULL; } +class RenderTarget_X11: public RenderTarget +{ +public: + RenderTarget_X11( LowLevelWindow_X11 *pWind ); + ~RenderTarget_X11(); + + void Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ); + unsigned GetTexture() const { return m_iTexHandle; } + void StartRenderingTo(); + void FinishRenderingTo(); + + /* Copying from the Pbuffer to the texture flips Y. */ + virtual bool InvertY() const { return true; } + +private: + int m_iWidth, m_iHeight; + LowLevelWindow_X11 *m_pWind; + GLXPbuffer m_iPbuffer; + GLXContext m_pPbufferContext; + unsigned int m_iTexHandle; + + GLXContext m_pOldContext; + GLXDrawable m_pOldDrawable; +}; + +RenderTarget_X11::RenderTarget_X11( LowLevelWindow_X11 *pWind ) +{ + m_pWind = pWind; + m_iPbuffer = 0; + m_pPbufferContext = NULL; + m_iTexHandle = 0; + m_pOldContext = NULL; + m_pOldDrawable = 0; +} + +RenderTarget_X11::~RenderTarget_X11() +{ + if( m_pPbufferContext ) + glXDestroyContext( X11Helper::Dpy, m_pPbufferContext ); + if( m_iPbuffer ) + glXDestroyPbuffer( X11Helper::Dpy, m_iPbuffer ); + if( m_iTexHandle ) + glDeleteTextures( 1, reinterpret_cast(&m_iTexHandle) ); +} + +/* Note that although the texture size may need to be a power of 2, the Pbuffer + * does not. */ +void RenderTarget_X11::Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ) +{ + //ASSERT( param.iWidth == power_of_two(param.iWidth) && param.iHeight == power_of_two(param.iHeight) ); + + m_iWidth = param.iWidth; + m_iHeight = param.iHeight; + + int pConfigAttribs[] = + { + GLX_DRAWABLE_TYPE, GLX_PBUFFER_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, param.bWithAlpha? 8:GLX_DONT_CARE, + + GLX_DOUBLEBUFFER, False, + GLX_DEPTH_SIZE, param.bWithDepthBuffer? 16:GLX_DONT_CARE, + None + }; + int iConfigs; + GLXFBConfig *pConfigs = glXChooseFBConfig( X11Helper::Dpy, DefaultScreen(X11Helper::Dpy), pConfigAttribs, &iConfigs ); + ASSERT( pConfigs ); + + const int pPbufferAttribs[] = + { + GLX_PBUFFER_WIDTH, param.iWidth, + GLX_PBUFFER_HEIGHT, param.iHeight, + None + }; + + for( int i = 0; i < iConfigs; ++i ) + { + m_iPbuffer = glXCreatePbuffer( X11Helper::Dpy, pConfigs[i], pPbufferAttribs ); + if( m_iPbuffer == 0 ) + continue; + + XVisualInfo *pVisual = glXGetVisualFromFBConfig( X11Helper::Dpy, pConfigs[i] ); + m_pPbufferContext = glXCreateContext( X11Helper::Dpy, pVisual, g_pContext, True ); + ASSERT( m_pPbufferContext ); + XFree( pVisual ); + break; + } + + ASSERT( m_iPbuffer ); + + // allocate OpenGL texture resource + glGenTextures( 1, reinterpret_cast(&m_iTexHandle) ); + glBindTexture( GL_TEXTURE_2D, m_iTexHandle ); + + LOG->Trace( "n %i, %ix%i", m_iTexHandle, param.iWidth, param.iHeight ); + while( glGetError() != GL_NO_ERROR ) + ; + + int iTextureWidth = power_of_two( param.iWidth ); + int iTextureHeight = power_of_two( param.iHeight ); + iTextureWidthOut = iTextureWidth; + iTextureHeightOut = iTextureHeight; + + glTexImage2D( GL_TEXTURE_2D, 0, param.bWithAlpha? GL_RGBA8:GL_RGB8, + iTextureWidth, iTextureHeight, 0, param.bWithAlpha? GL_RGBA:GL_RGB, GL_UNSIGNED_BYTE, NULL ); + GLenum error = glGetError(); + ASSERT_M( error == GL_NO_ERROR, GLToString(error) ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); +} + +void RenderTarget_X11::StartRenderingTo() +{ + m_pOldContext = glXGetCurrentContext(); + m_pOldDrawable = glXGetCurrentDrawable(); + glXMakeCurrent( X11Helper::Dpy, m_iPbuffer, m_pPbufferContext ); + + glViewport( 0, 0, m_iWidth, m_iHeight ); +} + +void RenderTarget_X11::FinishRenderingTo() +{ + glFlush(); + + glBindTexture( GL_TEXTURE_2D, m_iTexHandle ); + + while( glGetError() != GL_NO_ERROR ) + ; + + glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 0, 0, m_iWidth, m_iHeight ); + + GLenum error = glGetError(); + ASSERT_M( error == GL_NO_ERROR, GLToString(error) ); + + glBindTexture( GL_TEXTURE_2D, 0 ); + + glXMakeCurrent( X11Helper::Dpy, m_pOldDrawable, m_pOldContext ); + m_pOldContext = NULL; + m_pOldDrawable = 0; + +} + +bool LowLevelWindow_X11::SupportsRenderToTexture() const +{ + /* Server must support pbuffers: */ + const int iScreen = DefaultScreen( X11Helper::Dpy ); + float fVersion = strtof( glXQueryServerString(X11Helper::Dpy, iScreen, GLX_VERSION), NULL ); + if( fVersion < 1.3f ) + return false; + + return true; +} + +RenderTarget *LowLevelWindow_X11::CreateRenderTarget( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ) +{ + RenderTarget_X11 *pTarget = new RenderTarget_X11( this ); + pTarget->Create( param, iTextureWidthOut, iTextureHeightOut ); + return pTarget; +} + void LowLevelWindow_X11::BeginConcurrentRenderingMainThread() { /* Move the main thread, which is going to be loading textures, etc. but diff --git a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h index 18b809ae99..b5948ffeee 100644 --- a/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h +++ b/stepmania/src/arch/LowLevelWindow/LowLevelWindow_X11.h @@ -22,6 +22,9 @@ public: void GetDisplayResolutions( DisplayResolutions &out ) const; float GetMonitorAspectRatio() const; + bool SupportsRenderToTexture() const; + RenderTarget *CreateRenderTarget( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut ); + bool SupportsThreadedRendering(); void BeginConcurrentRenderingMainThread(); void EndConcurrentRenderingMainThread();