From: Thomas Debesse Date: Thu, 11 Mar 2021 04:59:47 +0000 (+0100) Subject: multicontext: optional per-viewport texture bind X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=51fa51ccb2eb99ad47e63ca2b44d44395e064040;p=xonotic%2Fnetradiant.git multicontext: optional per-viewport texture bind --- diff --git a/CMakeLists.txt b/CMakeLists.txt index b2fa296e..760c5a8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,7 +231,7 @@ if (BUILD_BINARIES) if (APPLE) add_definitions(-DGL_SILENCE_DEPRECATION=1) - endif() + endif () set(CMAKE_POSITION_INDEPENDENT_CODE 1) endif () @@ -249,12 +249,26 @@ if (BUILD_RADIANT) endif () if (APPLE) + set(UNSUPPORTED_GL_SHARED_CONTEXT ON CACHE INTERNAL "...") + if (GTK_TARGET EQUAL 2) add_definitions(-DWORKAROUND_MACOS_GTK2_DESTROY=1) add_definitions(-DWORKAROUND_MACOS_GTK2_GLWIDGET=1) add_definitions(-DWORKAROUND_MACOS_GTK2_LAGGYPOINTER=1) endif () endif () + + if (UNSUPPORTED_GL_SHARED_CONTEXT) + set(DEFAULT_GL_SHARED_CONTEXT ON) + else () + set(DEFAULT_GL_SHARED_CONTEXT OFF) + endif () + + option(USE_GL_UNSHARED_CONTEXT "Do not share GL Contexts" ${DEFAULT_GL_SHARED_CONTEXT}) + + if (USE_GL_UNSHARED_CONTEXT) + add_definitions(-DGL_UNSHARED_CONTEXT=1) + endif () endif () #----------------------------------------------------------------------- diff --git a/contrib/bobtoolz/shapes.cpp b/contrib/bobtoolz/shapes.cpp index 2e47035d..a2a98c63 100644 --- a/contrib/bobtoolz/shapes.cpp +++ b/contrib/bobtoolz/shapes.cpp @@ -101,7 +101,8 @@ void AddFaceWithTextureScaled( scene::Node& brush, vec3_t va, vec3_t vb, vec3_t // If a texture doesn't have a shader script, a default shader object is used. // The IShader object was leaking also // collect texture info: sizes, etc - IShader* i = GlobalShaderSystem().getShaderForName( texture ); + IShader* i = QERApp_Shader_ForName( texture ); + pqtTexInfo = i->getTexture(); // shader width/height doesn't come out properly if ( pqtTexInfo ) { diff --git a/include/iglrender.h b/include/iglrender.h index 04b4a807..7128543f 100644 --- a/include/iglrender.h +++ b/include/iglrender.h @@ -23,6 +23,7 @@ #define INCLUDED_IGLRENDER_H #include "igl.h" +#include "itextures.h" #include "generic/vector.h" class AABB; class Matrix4; @@ -74,23 +75,38 @@ enum ESort unsigned int m_state; std::size_t m_sort; -GLint m_texture; -GLint m_texture1; -GLint m_texture2; -GLint m_texture3; -GLint m_texture4; -GLint m_texture5; -GLint m_texture6; -GLint m_texture7; + +qtexture_t *m_qtexture; +qtexture_t *m_qtexture1; +qtexture_t *m_qtexture2; +qtexture_t *m_qtexture3; +qtexture_t *m_qtexture4; +qtexture_t *m_qtexture5; +qtexture_t *m_qtexture6; +qtexture_t *m_qtexture7; + +GLuint m_texture; +GLuint m_texture1; +GLuint m_texture2; +GLuint m_texture3; +GLuint m_texture4; +GLuint m_texture5; +GLuint m_texture6; +GLuint m_texture7; + Vector4 m_colour; + GLenum m_blend_src, m_blend_dst; GLenum m_depthfunc; GLenum m_alphafunc; + GLfloat m_alpharef; GLfloat m_linewidth; GLfloat m_pointsize; + GLint m_linestipple_factor; GLushort m_linestipple_pattern; + OpenGLFogState m_fog; GLProgram* m_program; diff --git a/include/irender.h b/include/irender.h index 169d881a..32272bda 100644 --- a/include/irender.h +++ b/include/irender.h @@ -25,6 +25,7 @@ #include "generic/constant.h" #include "generic/callback.h" +#include "viewport/viewport.h" // Rendering states to sort by. // Higher bits have the most effect - slowest state changes should be highest. @@ -139,7 +140,7 @@ STRING_CONSTANT( Name, "renderstate" ); virtual Shader* capture( const char* name ) = 0; virtual void release( const char* name ) = 0; /*! Render all Shader objects. */ -virtual void render( RenderStateFlags globalstate, const Matrix4& modelview, const Matrix4& projection, const Vector3& viewer = Vector3( 0, 0, 0 ) ) = 0; +virtual void render( const enum ViewportId, RenderStateFlags globalstate, const Matrix4& modelview, const Matrix4& projection, const Vector3& viewer = Vector3( 0, 0, 0 ) ) = 0; virtual void realise() = 0; virtual void unrealise() = 0; diff --git a/include/ishaders.h b/include/ishaders.h index b580e2c4..ed788906 100644 --- a/include/ishaders.h +++ b/include/ishaders.h @@ -147,6 +147,9 @@ STRING_CONSTANT( Name, "shaders" ); virtual void realise() = 0; virtual void unrealise() = 0; virtual void refresh() = 0; +virtual bool refreshed() = 0; +virtual void triggerRefresh() = 0; +virtual bool triggeredRefresh() = 0; // activate the shader for a given name and return it // will return the default shader if name is not found virtual IShader* getShaderForName( const char* name ) = 0; diff --git a/include/itextures.h b/include/itextures.h index 6fa4b02c..5b9d9eec 100644 --- a/include/itextures.h +++ b/include/itextures.h @@ -23,10 +23,17 @@ #define INCLUDED_ITEXTURES_H #include "iimage.h" +#include "igl.h" #include "generic/constant.h" +#include "viewport/viewport.h" + struct qtexture_t; +void LoadTextureRGBA( const enum ViewportId v, qtexture_t* q, Image* image ); +GLuint GetBindTextureNumber( const enum ViewportId v, const qtexture_t* q ); +GLuint RequestBindTextureNumber( const enum ViewportId v, qtexture_t* q ); + class LoadImageCallback { typedef Image* ( *LoadFunc )( void* environment, const char* name ); diff --git a/libs/CMakeLists.txt b/libs/CMakeLists.txt index a306996a..807e8813 100644 --- a/libs/CMakeLists.txt +++ b/libs/CMakeLists.txt @@ -14,6 +14,7 @@ add_subdirectory(generic) if (BUILD_RADIANT) add_subdirectory(gtkutil) add_subdirectory(uilib) + add_subdirectory(viewport) endif () add_subdirectory(l_net) diff --git a/libs/gtkutil/glwidget.cpp b/libs/gtkutil/glwidget.cpp index 8581f7a4..6c4291c5 100644 --- a/libs/gtkutil/glwidget.cpp +++ b/libs/gtkutil/glwidget.cpp @@ -261,7 +261,12 @@ static bool glwidget_enable_gl(ui::GLArea self, ui::Widget root, gpointer data) GdkGLConfig *glconfig = zbuffer ? glconfig_new_with_depth() : glconfig_new(); ASSERT_MESSAGE(glconfig, "failed to create OpenGL config"); +#if defined(GL_UNSHARED_CONTEXT) + const auto share_list = nullptr; +#else // !GL_UNSHARED_CONTEXT const auto share_list = g_shared ? gtk_widget_get_gl_context(g_shared) : nullptr; +#endif // !GL_UNSHARED_CONTEXT + gtk_widget_set_gl_capability(self, glconfig, share_list, true, GDK_GL_RGBA_TYPE); gtk_widget_realize(self); diff --git a/libs/modulesystem/singletonmodule.h b/libs/modulesystem/singletonmodule.h index 8dcd88de..be855611 100644 --- a/libs/modulesystem/singletonmodule.h +++ b/libs/modulesystem/singletonmodule.h @@ -111,6 +111,7 @@ void capture(){ else { globalOutputStream() << "Module Dependencies Failed: '" << typename Type::Name() << "' '" << APIConstructor::getName() << "'\n"; + ASSERT_MESSAGE( m_dependencyCheck, "module dependencies failed" ); } m_cycleCheck = true; } diff --git a/libs/texturelib.h b/libs/texturelib.h index d9da1e89..1e4db259 100644 --- a/libs/texturelib.h +++ b/libs/texturelib.h @@ -22,7 +22,12 @@ #if !defined ( INCLUDED_TEXTURELIB_H ) #define INCLUDED_TEXTURELIB_H +#include "iimage.h" + #include "generic/vector.h" + +#include "viewport/viewport.h" + typedef Vector3 Colour3; typedef unsigned int GLuint; class LoadImageCallback; @@ -35,9 +40,10 @@ struct qtexture_t const LoadImageCallback& load; const char* name; std::size_t width, height; - GLuint texture_number; // gl bind number + GLuint texture_number[ VP_MAX ]; // GL texture bind number Colour3 color; // for flat shade mode int surfaceFlags, contentFlags, value; + bool loaded; // shader plugin has no knowledge about viewport and can't rely on texture_number }; #endif diff --git a/libs/viewport/CMakeLists.txt b/libs/viewport/CMakeLists.txt new file mode 100644 index 00000000..0ddc0d3e --- /dev/null +++ b/libs/viewport/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(viewport STATIC + viewport.cpp viewport.h + ) diff --git a/libs/viewport/viewport.cpp b/libs/viewport/viewport.cpp new file mode 100644 index 00000000..def73c59 --- /dev/null +++ b/libs/viewport/viewport.cpp @@ -0,0 +1,3 @@ +#include "viewport.h" + +ViewportId g_viewport_id = VP_NONE; diff --git a/libs/viewport/viewport.h b/libs/viewport/viewport.h new file mode 100644 index 00000000..cbf746dc --- /dev/null +++ b/libs/viewport/viewport.h @@ -0,0 +1,19 @@ +#if !defined ( INCLUDED_VIEWPORT_H ) +#define INCLUDED_VIEWPORT_H + +enum ViewportId { + VP_NONE, +#if defined(GL_UNSHARED_CONTEXT) + VP_CAMWINDOW, + VP_TEXWINDOW, + VP_XYWINDOW, +#else // !GL_UNSHARED_CONTEXT + VP_SHARED, + VP_CAMWINDOW = VP_SHARED, + VP_TEXWINDOW = VP_SHARED, + VP_XYWINDOW = VP_SHARED, +#endif // !GL_UNSHARED_CONTEXT + VP_MAX +}; + +#endif // INCLUDED_VIEWPORT_H diff --git a/plugins/shaders/shaders.cpp b/plugins/shaders/shaders.cpp index 58c52325..65f4c3ac 100644 --- a/plugins/shaders/shaders.cpp +++ b/plugins/shaders/shaders.cpp @@ -968,7 +968,7 @@ const char* getShaderFileName() const { void realise(){ m_pTexture = evaluateTexture( m_template.m_textureName, m_template.m_params, m_args ); - if ( m_pTexture->texture_number == 0 ) { + if ( !m_pTexture->loaded ) { m_notfound = m_pTexture; { @@ -1740,6 +1740,9 @@ void Shaders_Refresh(){ class Quake3ShaderSystem : public ShaderSystem, public ModuleObserver { +bool m_toBeRefreshed = false; +bool m_refreshed = false; + public: void realise(){ Shaders_Realise(); @@ -1750,7 +1753,25 @@ void unrealise(){ } void refresh(){ - Shaders_Refresh(); + if ( m_toBeRefreshed ) + { + Shaders_Refresh(); + m_toBeRefreshed = false; + m_refreshed = true; + } +} + +bool refreshed(){ + return m_refreshed; +} + +void triggerRefresh(){ + m_toBeRefreshed = true; + m_refreshed = false; +} + +bool triggeredRefresh(){ + return m_toBeRefreshed; } IShader* getShaderForName( const char* name ){ diff --git a/plugins/spritemodel/spritemodel.cpp b/plugins/spritemodel/spritemodel.cpp index 8308416f..af00f514 100644 --- a/plugins/spritemodel/spritemodel.cpp +++ b/plugins/spritemodel/spritemodel.cpp @@ -26,6 +26,9 @@ // Based on MD3Model source code by SPoG // +// This code looks to be dead and unused. +// It is not ported to RequestBindTextureNumber(). + #include "spritemodel.h" void LoadSpriteModel( entity_interfaces_t *interfaces, const char *name ){ diff --git a/plugins/textool/TexTool.cpp b/plugins/textool/TexTool.cpp index e328bdb0..c430d30f 100644 --- a/plugins/textool/TexTool.cpp +++ b/plugins/textool/TexTool.cpp @@ -26,6 +26,10 @@ // texturing tools for Radiant // +// This code looks to be dead and unused. +// It calls unknown symbols like m_pfnGetTextureNumber(). +// It is not ported to RequestBindTextureNumber(). + #include "StdAfx.h" static void dialog_button_callback( GtkWidget *widget, gpointer data ){ diff --git a/radiant/CMakeLists.txt b/radiant/CMakeLists.txt index 12ee1dfa..362cebac 100644 --- a/radiant/CMakeLists.txt +++ b/radiant/CMakeLists.txt @@ -124,6 +124,7 @@ target_link_libraries(${RADIANT_BASENAME} stream string uilib + viewport xmllib ) diff --git a/radiant/camwindow.cpp b/radiant/camwindow.cpp index 27ae274e..233262b5 100644 --- a/radiant/camwindow.cpp +++ b/radiant/camwindow.cpp @@ -26,6 +26,7 @@ // #include "camwindow.h" +#include "texwindow.h" // TextureBrowser_ShowStartupShaders #include #include @@ -33,9 +34,11 @@ #include "debugging/debugging.h" #include "iscenegraph.h" +#include "ishaders.h" #include "irender.h" #include "igl.h" #include "icamera.h" +#include "textures.h" #include "cullable.h" #include "renderable.h" #include "preferencesystem.h" @@ -725,6 +728,8 @@ CameraView& getCameraView(){ } private: +void Cam_Clear(); +void Cam_Init_World(); void Cam_Draw(); }; @@ -774,8 +779,6 @@ void CamWnd_Update( CamWnd& camwnd ){ camwnd.queue_draw(); } - - camwindow_globals_t g_camwindow_globals; const Vector3& Camera_getOrigin( CamWnd& camwnd ){ @@ -1384,7 +1387,10 @@ void addRenderable( const OpenGLRenderable& renderable, const Matrix4& world ){ } void render( const Matrix4& modelview, const Matrix4& projection ){ - GlobalShaderCache().render( m_globalstate, modelview, projection, m_viewer ); + if( GlobalShaderSystem().refreshed() ) + { + GlobalShaderCache().render( VP_CAMWINDOW, m_globalstate, modelview, projection, m_viewer ); + } } }; @@ -1406,6 +1412,52 @@ FreeCaller&), ShowStatsExport> g_show_stats_call Callback &)> g_show_stats_callback( g_show_stats_caller ); ToggleItem g_show_stats( g_show_stats_callback ); +void CamWnd::Cam_Clear(){ + Vector3 clearColour( 0, 0, 0 ); + if ( m_Camera.draw_mode != cd_lighting ) { + clearColour = g_camwindow_globals.color_cameraback; + } + + glClearColor( clearColour[0], clearColour[1], clearColour[2], 0 ); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); +} + +// g_deferredStartupMap is set in main.cpp +extern const char* g_deferredStartupMap; + +// g_loadedMap and g_deferredStartupShaders are set in map.cpp +extern bool g_deferredStartupShaders; + +void CamWnd::Cam_Init_World(){ + // Only run and run once when Textures_TriggerRealise() is called before. + if ( Textures_TriggeredRealise() ) + { + Textures_Realise(); + } + + // Only run and run once when GlobalShaderSystem().TriggerRefresh() is called before. + + if ( GlobalShaderSystem().triggeredRefresh() ) + { + GlobalShaderSystem().refresh(); + } + + if ( g_deferredStartupShaders ) + { + TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() ); + g_deferredStartupShaders = false; + } + + // Load the “to be loaded at startup” map when GL viewport + // is cleared and builtin shaders are realised. + if ( g_deferredStartupMap != nullptr ) + { + Map_Free(); + Map_LoadFile( g_deferredStartupMap ); + g_deferredStartupMap = nullptr; + } +} + void CamWnd::Cam_Draw(){ glViewport( 0, 0, m_Camera.width, m_Camera.height ); #if 0 @@ -1417,13 +1469,7 @@ void CamWnd::Cam_Draw(){ glDepthMask( GL_TRUE ); glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - Vector3 clearColour( 0, 0, 0 ); - if ( m_Camera.draw_mode != cd_lighting ) { - clearColour = g_camwindow_globals.color_cameraback; - } - - glClearColor( clearColour[0], clearColour[1], clearColour[2], 0 ); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + Cam_Clear(); extern void Renderer_ResetStats(); Renderer_ResetStats(); @@ -1566,6 +1612,23 @@ void CamWnd::Cam_Draw(){ void CamWnd::draw(){ m_drawing = true; + if ( Textures_TriggeredRealise() || GlobalShaderSystem().triggeredRefresh() || g_deferredStartupShaders ) + { + // Draw something to not display garbage while iterating textures. + if ( glwidget_make_current( m_gl_widget ) != FALSE ) { + if ( ScreenUpdates_Enabled() ) { + GlobalOpenGL_debugAssertNoErrors(); + glViewport( 0, 0, m_Camera.width, m_Camera.height ); + Cam_Clear(); + GlobalOpenGL_debugAssertNoErrors(); + } + + Cam_Init_World(); + + glwidget_swap_buffers( m_gl_widget ); + } + } + //globalOutputStream() << "draw...\n"; if ( glwidget_make_current( m_gl_widget ) != FALSE ) { if ( Map_Valid( g_map ) && ScreenUpdates_Enabled() ) { @@ -1700,6 +1763,10 @@ void GlobalCamera_Update(){ CamWnd_Update( camwnd ); } +void GlobalCamera_SetVisible(){ + g_camera_shown.set( true ); +} + camera_draw_mode CamWnd_GetMode(){ return camera_t::draw_mode; } diff --git a/radiant/camwindow.h b/radiant/camwindow.h index 9ed6d242..9eb0bba7 100644 --- a/radiant/camwindow.h +++ b/radiant/camwindow.h @@ -44,6 +44,7 @@ void CamWnd_constructToolbar( ui::Toolbar toolbar ); void CamWnd_registerShortcuts(); void GlobalCamera_Benchmark(); +void GlobalCamera_SetVisible(); const Vector3& Camera_getOrigin( CamWnd& camwnd ); void Camera_setOrigin( CamWnd& camwnd, const Vector3& origin ); diff --git a/radiant/main.cpp b/radiant/main.cpp index 6c8ffd03..8dbe88c2 100644 --- a/radiant/main.cpp +++ b/radiant/main.cpp @@ -502,6 +502,8 @@ void user_shortcuts_save(){ SaveCommandMap( path.c_str() ); } +const char *g_deferredStartupMap = nullptr; + /* HACK: If ui::main is not called yet, gtk_main_quit will not quit, so tell main to not call ui::main. This happens when a @@ -617,7 +619,6 @@ int main( int argc, char* argv[] ){ g_GamesDialog.m_bForceLogConsole = false; } - Radiant_Initialise(); user_shortcuts_init(); @@ -627,21 +628,20 @@ int main( int argc, char* argv[] ){ hide_splash(); + // Always load an empty map at startup. + Map_New(); + + // Defer the map loading to the time GL viewports are cleared, + // prevent garbage to be rendered while loading map and related assets. + // The deferred map is now loaded by the camwindow istelf and map related + // textures and GL states are now loaded and set by camwindow, so the + // camwindow must always be visible at map loading time. if ( mapname != NULL ) { - Map_LoadFile( mapname ); + g_deferredStartupMap = mapname; } else if ( g_bLoadLastMap && !g_strLastMap.empty() ) { - Map_LoadFile( g_strLastMap.c_str() ); + g_deferredStartupMap = g_strLastMap.c_str(); } - else - { - Map_New(); - } - - // load up shaders now that we have the map loaded - // eviltypeguy - TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() ); - remove_local_pid(); diff --git a/radiant/mainframe.cpp b/radiant/mainframe.cpp index 17e53efc..8accc5ef 100644 --- a/radiant/mainframe.cpp +++ b/radiant/mainframe.cpp @@ -159,6 +159,13 @@ void VFS_Shutdown(){ void VFS_Refresh(){ if ( !g_vfsInitialized ) return; + // Always set camwindow visible when reloading VFS + // especially when loading maps, because GL states loaded + // by the camwindow are also used by the xywindow. + // Loading map at startup (not deferred) may also requires this + // to make sure GL states are ready at map loading time + // since camwindow is now the place to initialize GL stuff. + GlobalCamera_SetVisible(); GlobalFileSystem().clear(); QE_InitVFS(); GlobalFileSystem().refresh(); @@ -1994,8 +2001,8 @@ void XY_UpdateAllWindows(){ } void UpdateAllWindows(){ - GlobalCamera_UpdateWindow(); XY_UpdateAllWindows(); + GlobalCamera_UpdateWindow(); } @@ -2004,8 +2011,8 @@ void ModeChangeNotify(){ } void ClipperChangeNotify(){ - GlobalCamera_UpdateWindow(); XY_UpdateAllWindows(); + GlobalCamera_UpdateWindow(); } @@ -3359,7 +3366,7 @@ void GlobalGL_sharedContextCreated(){ ShaderCache_extensionsInitialised(); GlobalShaderCache().realise(); - Textures_Realise(); + Textures_TriggerRealise(); #if GDEF_OS_WINDOWS /* win32 is dodgy here, just use courier new then */ diff --git a/radiant/map.cpp b/radiant/map.cpp index a5ac53f8..c15a2f63 100644 --- a/radiant/map.cpp +++ b/radiant/map.cpp @@ -88,6 +88,7 @@ MapModules& ReferenceAPI_getMapModules(); #include "brush.h" bool g_writeMapComments = true; +bool g_deferredStartupShaders = false; class NameObserver { @@ -1016,6 +1017,9 @@ void Map_LoadFile( const char *filename ){ g_currentMap = &g_map; + // Load up shaders now that we have the map loaded. + g_deferredStartupShaders = true; + Brush_switchFormat( switch_format ); } @@ -1285,6 +1289,9 @@ void Map_New(){ // restart VFS to apply new pak filtering based on mapname // needed for daemon DPK VFS VFS_Restart(); + + // Load up shaders now that we have the map loaded. + g_deferredStartupShaders = true; } extern void ConstructRegionBrushes( scene::Node * brushes[6], const Vector3 ®ion_mins, const Vector3 ®ion_maxs ); diff --git a/radiant/renderstate.cpp b/radiant/renderstate.cpp index f714fc7e..6b30a19c 100644 --- a/radiant/renderstate.cpp +++ b/radiant/renderstate.cpp @@ -53,7 +53,6 @@ #include "xywindow.h" - #define DEBUG_RENDER 0 inline void debug_string( const char* string ){ @@ -585,7 +584,7 @@ qtexture_t* g_specular_lookup = 0; qtexture_t* g_attenuation_xy = 0; qtexture_t* g_attenuation_z = 0; -void createVertexProgram(){ +void createVertexProgram( const enum ViewportId v ){ { glGenProgramsNV( 1, &m_vertex_program ); glBindProgramNV( GL_VERTEX_PROGRAM_NV, m_vertex_program ); @@ -605,13 +604,13 @@ void createVertexProgram(){ g_attenuation_xy = GlobalTexturesCache().capture( "lights/squarelight1" ); glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, g_attenuation_xy->texture_number ); + glBindTexture( GL_TEXTURE_2D, requestTextureBind( v, g_attenuation_xy ) ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); g_attenuation_z = GlobalTexturesCache().capture( "lights/squarelight1a" ); glActiveTexture( GL_TEXTURE0 ); - glBindTexture( GL_TEXTURE_2D, g_attenuation_z->texture_number ); + glBindTexture( GL_TEXTURE_2D, requestTextureBind( v, g_attenuation_z ) ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); @@ -772,12 +771,24 @@ bool g_normalArray_enabled = false; bool g_texcoordArray_enabled = false; bool g_colorArray_enabled = false; +GLuint requestTextureBind( const enum ViewportId v, qtexture_t *q ){ + if ( q != nullptr ) + { + return RequestBindTextureNumber( v, q ); + } + + return 0; +} + inline bool OpenGLState_less( const OpenGLState& self, const OpenGLState& other ){ //! Sort by sort-order override. if ( self.m_sort != other.m_sort ) { return self.m_sort < other.m_sort; } + //! Sort by texture handle. + // HACK: The same data it reused whatever the viewport, + // but it is expected to produce the same result anyway. if ( self.m_texture != other.m_texture ) { return self.m_texture < other.m_texture; } @@ -802,10 +813,12 @@ inline bool OpenGLState_less( const OpenGLState& self, const OpenGLState& other if ( self.m_texture7 != other.m_texture7 ) { return self.m_texture7 < other.m_texture7; } + //! Sort by state bit-vector. if ( self.m_state != other.m_state ) { return self.m_state < other.m_state; } + //! Comparing address makes sure states are never equal. return &self < &other; } @@ -813,6 +826,15 @@ inline bool OpenGLState_less( const OpenGLState& self, const OpenGLState& other void OpenGLState_constructDefault( OpenGLState& state ){ state.m_state = RENDER_DEFAULT; + state.m_qtexture = nullptr; + state.m_qtexture1 = nullptr; + state.m_qtexture2 = nullptr; + state.m_qtexture3 = nullptr; + state.m_qtexture4 = nullptr; + state.m_qtexture5 = nullptr; + state.m_qtexture6 = nullptr; + state.m_qtexture7 = nullptr; + state.m_texture = 0; state.m_texture1 = 0; state.m_texture2 = 0; @@ -880,7 +902,7 @@ OpenGLState& state(){ return m_state; } -void render( OpenGLState& current, unsigned int globalstate, const Vector3& viewer ); +void render( const enum ViewportId, OpenGLState& current, unsigned int globalstate, const Vector3& viewer ); }; #define LIGHT_SHADER_DEBUG 0 @@ -1217,9 +1239,11 @@ Shader* capture( const char* name ){ || *name == '<' || *name == '(' || strchr( name, '\\' ) == 0, "shader name contains invalid characters: \"" << name << "\"" ); + #if DEBUG_SHADERS globalOutputStream() << "shaders capture: " << makeQuoted( name ) << '\n'; #endif + return m_shaders.capture( name ).get(); } @@ -1229,7 +1253,7 @@ void release( const char *name ){ #endif m_shaders.release( name ); } -void render( RenderStateFlags globalstate, const Matrix4& modelview, const Matrix4& projection, const Vector3& viewer ){ +void render( const enum ViewportId v, RenderStateFlags globalstate, const Matrix4& modelview, const Matrix4& projection, const Vector3& viewer ){ glMatrixMode( GL_PROJECTION ); glLoadMatrixf( reinterpret_cast( &projection ) ); #if 0 @@ -1333,7 +1357,7 @@ void render( RenderStateFlags globalstate, const Matrix4& modelview, const Matri debug_string( "begin rendering" ); for ( OpenGLStates::iterator i = g_state_sorted.begin(); i != g_state_sorted.end(); ++i ) { - ( *i ).second->render( current, globalstate, viewer ); + ( *i ).second->render( v, current, globalstate, viewer ); } debug_string( "end rendering" ); } @@ -1615,7 +1639,7 @@ ShaderCache* GetShaderCache(){ return g_ShaderCache; } -inline void setTextureState( GLint& current, const GLint& texture, GLenum textureUnit ){ +inline void setTextureState( GLuint& current, const GLuint& texture, GLenum textureUnit ){ if ( texture != current ) { glActiveTexture( textureUnit ); glClientActiveTexture( textureUnit ); @@ -1625,7 +1649,7 @@ inline void setTextureState( GLint& current, const GLint& texture, GLenum textur } } -inline void setTextureState( GLint& current, const GLint& texture ){ +inline void setTextureState( GLuint& current, const GLuint& texture ){ if ( texture != current ) { glBindTexture( GL_TEXTURE_2D, texture ); GlobalOpenGL_debugAssertNoErrors(); @@ -1644,9 +1668,10 @@ inline void setState( unsigned int state, unsigned int delta, unsigned int flag, } } -void OpenGLState_apply( const OpenGLState& self, OpenGLState& current, unsigned int globalstate ){ - debug_int( "sort", int(self.m_sort) ); +void OpenGLState_apply( const enum ViewportId v, OpenGLState& self, OpenGLState& current, unsigned int globalstate ){ + self.m_texture = requestTextureBind( v, self.m_qtexture ); debug_int( "texture", self.m_texture ); + debug_int( "sort", int(self.m_sort) ); debug_int( "state", self.m_state ); debug_int( "address", int(std::size_t( &self ) ) ); @@ -1870,26 +1895,35 @@ void OpenGLState_apply( const OpenGLState& self, OpenGLState& current, unsigned current.m_alpharef = self.m_alpharef; } + // Request texture binds. + current.m_texture = requestTextureBind( v, current.m_qtexture ); + current.m_texture1 = requestTextureBind( v, current.m_qtexture1 ); + current.m_texture2 = requestTextureBind( v, current.m_qtexture2 ); + current.m_texture3 = requestTextureBind( v, current.m_qtexture3 ); + current.m_texture4 = requestTextureBind( v, current.m_qtexture4 ); + current.m_texture5 = requestTextureBind( v, current.m_qtexture5 ); + current.m_texture6 = requestTextureBind( v, current.m_qtexture6 ); + current.m_texture7 = requestTextureBind( v, current.m_qtexture7 ); + + // Request texture binds. + self.m_texture = requestTextureBind( v, self.m_qtexture ); + self.m_texture1 = requestTextureBind( v, self.m_qtexture1 ); + self.m_texture2 = requestTextureBind( v, self.m_qtexture2 ); + self.m_texture3 = requestTextureBind( v, self.m_qtexture3 ); + self.m_texture4 = requestTextureBind( v, self.m_qtexture4 ); + self.m_texture5 = requestTextureBind( v, self.m_qtexture5 ); + self.m_texture6 = requestTextureBind( v, self.m_qtexture6 ); + self.m_texture7 = requestTextureBind( v, self.m_qtexture7 ); + { - GLint texture0 = 0; - GLint texture1 = 0; - GLint texture2 = 0; - GLint texture3 = 0; - GLint texture4 = 0; - GLint texture5 = 0; - GLint texture6 = 0; - GLint texture7 = 0; - //if(state & RENDER_TEXTURE) != 0) - { - texture0 = self.m_texture; - texture1 = self.m_texture1; - texture2 = self.m_texture2; - texture3 = self.m_texture3; - texture4 = self.m_texture4; - texture5 = self.m_texture5; - texture6 = self.m_texture6; - texture7 = self.m_texture7; - } + GLuint texture0 = self.m_texture; + GLuint texture1 = self.m_texture1; + GLuint texture2 = self.m_texture2; + GLuint texture3 = self.m_texture3; + GLuint texture4 = self.m_texture4; + GLuint texture5 = self.m_texture5; + GLuint texture6 = self.m_texture6; + GLuint texture7 = self.m_texture7; if ( GlobalOpenGL().GL_1_3() ) { setTextureState( current.m_texture, texture0, GL_TEXTURE0 ); @@ -1907,7 +1941,6 @@ void OpenGLState_apply( const OpenGLState& self, OpenGLState& current, unsigned } } - if ( state & RENDER_TEXTURE && self.m_colour[3] != current.m_colour[3] ) { debug_colour( "setting alpha" ); glColor4f( 1,1,1,self.m_colour[3] ); @@ -1952,8 +1985,9 @@ void OpenGLState_apply( const OpenGLState& self, OpenGLState& current, unsigned GlobalOpenGL_debugAssertNoErrors(); } -void Renderables_flush( OpenGLStateBucket::Renderables& renderables, OpenGLState& current, unsigned int globalstate, const Vector3& viewer ){ +void Renderables_flush( const enum ViewportId v, OpenGLStateBucket::Renderables& renderables, OpenGLState& current, unsigned int globalstate, const Vector3& viewer ){ const Matrix4* transform = 0; + glPushMatrix(); for ( OpenGLStateBucket::Renderables::const_iterator i = renderables.begin(); i != renderables.end(); ++i ) { @@ -1972,10 +2006,12 @@ void Renderables_flush( OpenGLStateBucket::Renderables& renderables, OpenGLState if ( current.m_program != 0 && ( *i ).m_light != 0 ) { const IShader& lightShader = static_cast( ( *i ).m_light->getShader() )->getShader(); if ( lightShader.firstLayer() != 0 ) { - GLuint attenuation_xy = lightShader.firstLayer()->texture()->texture_number; + GLuint attenuation_xy = requestTextureBind( v, lightShader.firstLayer()->texture() ); GLuint attenuation_z = lightShader.lightFalloffImage() != 0 - ? lightShader.lightFalloffImage()->texture_number - : static_cast( g_defaultPointLight )->getShader().lightFalloffImage()->texture_number; + ? requestTextureBind( v, lightShader.lightFalloffImage() ) + : requestTextureBind( v, static_cast( g_defaultPointLight )->getShader().lightFalloffImage() ); + + current.m_texture3 = requestTextureBind( v, current.m_qtexture3 ); setTextureState( current.m_texture3, attenuation_xy, GL_TEXTURE3 ); glActiveTexture( GL_TEXTURE3 ); @@ -1983,13 +2019,14 @@ void Renderables_flush( OpenGLStateBucket::Renderables& renderables, OpenGLState glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER ); + current.m_texture4 = requestTextureBind( v, current.m_qtexture4 ); + setTextureState( current.m_texture4, attenuation_z, GL_TEXTURE4 ); glActiveTexture( GL_TEXTURE4 ); glBindTexture( GL_TEXTURE_2D, attenuation_z ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER ); glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - AABB lightBounds( ( *i ).m_light->aabb() ); Matrix4 world2light( g_matrix4_identity ); @@ -2018,9 +2055,9 @@ void Renderables_flush( OpenGLStateBucket::Renderables& renderables, OpenGLState renderables.clear(); } -void OpenGLStateBucket::render( OpenGLState& current, unsigned int globalstate, const Vector3& viewer ){ +void OpenGLStateBucket::render( const enum ViewportId v, OpenGLState& current, unsigned int globalstate, const Vector3& viewer ){ if ( ( globalstate & m_state.m_state & RENDER_SCREEN ) != 0 ) { - OpenGLState_apply( m_state, current, globalstate ); + OpenGLState_apply( v, m_state, current, globalstate ); debug_colour( "screen fill" ); glMatrixMode( GL_PROJECTION ); @@ -2045,8 +2082,8 @@ void OpenGLStateBucket::render( OpenGLState& current, unsigned int globalstate, glPopMatrix(); } else if ( !m_renderables.empty() ) { - OpenGLState_apply( m_state, current, globalstate ); - Renderables_flush( m_renderables, current, globalstate, viewer ); + OpenGLState_apply( v, m_state, current, globalstate ); + Renderables_flush( v, m_renderables, current, globalstate, viewer ); } } @@ -2123,6 +2160,11 @@ inline GLenum convertBlendFactor( BlendFactor factor ){ /// \todo Define special-case shaders in a data file. void OpenGLShader::construct( const char* name ){ OpenGLState& state = appendDefaultPass(); + + // HACK: It is currently assumed this is only called by camwindow + // and that there is only one camwindow. + ViewportId v = VP_CAMWINDOW; + switch ( name[0] ) { case '(': @@ -2328,7 +2370,8 @@ void OpenGLShader::construct( const char* name ){ // construction from IShader m_shader = QERApp_Shader_ForName( name ); - if ( g_ShaderCache->lightingSupported() && g_ShaderCache->lightingEnabled() && m_shader->getBump() != 0 && m_shader->getBump()->texture_number != 0 ) { // is a bump shader + // FIXME: what if the image loading or upload fail? + if ( g_ShaderCache->lightingSupported() && g_ShaderCache->lightingEnabled() && m_shader->getBump() != 0 && requestTextureBind( v, m_shader->getBump() ) != 0 ) { // is a bump shader state.m_state = RENDER_FILL | RENDER_CULLFACE | RENDER_TEXTURE | RENDER_DEPTHTEST | RENDER_DEPTHWRITE | RENDER_COLOURWRITE | RENDER_PROGRAM; state.m_colour[0] = 0; state.m_colour[1] = 0; @@ -2345,9 +2388,12 @@ void OpenGLShader::construct( const char* name ){ } OpenGLState& bumpPass = appendDefaultPass(); - bumpPass.m_texture = m_shader->getDiffuse()->texture_number; - bumpPass.m_texture1 = m_shader->getBump()->texture_number; - bumpPass.m_texture2 = m_shader->getSpecular()->texture_number; + bumpPass.m_qtexture = m_shader->getDiffuse(); + bumpPass.m_texture = requestTextureBind( v, bumpPass.m_qtexture ); + bumpPass.m_qtexture1 = m_shader->getBump(); + bumpPass.m_texture1 = requestTextureBind( v, bumpPass.m_qtexture1 ); + bumpPass.m_qtexture2 = m_shader->getSpecular(); + bumpPass.m_texture2 = requestTextureBind( v, bumpPass.m_qtexture2 ); bumpPass.m_state = RENDER_BLEND | RENDER_FILL | RENDER_CULLFACE | RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_SMOOTH | RENDER_BUMP | RENDER_PROGRAM; @@ -2367,7 +2413,8 @@ void OpenGLShader::construct( const char* name ){ } else { - state.m_texture = m_shader->getTexture()->texture_number; + state.m_qtexture = m_shader->getTexture(); + state.m_texture = requestTextureBind( v, state.m_qtexture ); state.m_state = RENDER_FILL | RENDER_TEXTURE | RENDER_DEPTHTEST | RENDER_COLOURWRITE | RENDER_LIGHTING | RENDER_SMOOTH; if ( ( m_shader->getFlags() & QER_CULL ) != 0 ) { diff --git a/radiant/surfacedialog.cpp b/radiant/surfacedialog.cpp index 9ae43fd7..0d39a951 100644 --- a/radiant/surfacedialog.cpp +++ b/radiant/surfacedialog.cpp @@ -1496,7 +1496,8 @@ void CopyPointsFromSelectedFace( void ){ } Face & face = g_SelectedFaceInstances.last().getFace(); - textureNum = face.getShader().m_state->getTexture().texture_number; + // FIXME: is it called by CamWindow thread? + textureNum = RequestBindTextureNumber( face.getShader().m_state->getTexture(), VP_CAMWINDOW ); textureSize.x() = face.getShader().m_state->getTexture().width; textureSize.y() = face.getShader().m_state->getTexture().height; //globalOutputStream() << "--> Texture #" << textureNum << ": " << textureSize.x() << " x " << textureSize.y() << "...\n"; diff --git a/radiant/textures.cpp b/radiant/textures.cpp index afa8d4e0..0a484f77 100644 --- a/radiant/textures.cpp +++ b/radiant/textures.cpp @@ -26,6 +26,7 @@ #include "itextures.h" #include "igl.h" + #include "preferencesystem.h" #include "qgl.h" @@ -39,8 +40,6 @@ #include "texmanip.h" #include "preferences.h" - - enum ETexturesMode { eTextures_NEAREST = 0, @@ -170,10 +169,14 @@ LatchedValue g_Textures_textureQuality( 3, "Texture Quality" ); /// \brief This function does the actual processing of raw RGBA data into a GL texture. /// It will also resample to power-of-two dimensions, generate the mipmaps and adjust gamma. -void LoadTextureRGBA( qtexture_t* q, unsigned char* pPixels, int nWidth, int nHeight ){ +void LoadTextureRGBA( const enum ViewportId v, qtexture_t* q, Image* image ){ static float fGamma = -1; float total[3]; byte *outpixels = 0; + + unsigned char* pPixels = image->getRGBAPixels(); + int nWidth = image->getWidth(); + int nHeight = image->getHeight(); int nCount = nWidth * nHeight; if ( fGamma != g_texture_globals.fGamma ) { @@ -201,9 +204,17 @@ void LoadTextureRGBA( qtexture_t* q, unsigned char* pPixels, int nWidth, int nHe q->color[1] = total[1] / ( nCount * 255 ); q->color[2] = total[2] / ( nCount * 255 ); - glGenTextures( 1, &q->texture_number ); + glGenTextures( 1, &q->texture_number[ v ] ); + + ASSERT_MESSAGE( q->name != nullptr, "Undefined texture name" ); + +#ifdef DEBUG_TEXTURES + // It may produce an infinite loop (never reproduced though): + // Log → Redraw → Log → Redraw… + globalOutputStream() << "LoadTextureRGBA: " << v << ", " << q->name << ", " << q->texture_number[ v ] << "\n"; +#endif - glBindTexture( GL_TEXTURE_2D, q->texture_number ); + glBindTexture( GL_TEXTURE_2D, q->texture_number[ v ] ); SetTexParameters( g_texture_mode ); @@ -264,6 +275,50 @@ void LoadTextureRGBA( qtexture_t* q, unsigned char* pPixels, int nWidth, int nHe } } +GLuint GetBindTextureNumber( const enum ViewportId v, const qtexture_t* q ){ + ASSERT_MESSAGE( v >= VP_NONE && v < VP_MAX, "Unknown GL Viewport" ); + ASSERT_MESSAGE( v != VP_NONE, "Unset GL Viewport" ); + + return q->texture_number[ v ]; +} + +GLuint RequestBindTextureNumber( const enum ViewportId v, qtexture_t* q ){ + if ( GetBindTextureNumber( v, q ) == 0 ) + { + ASSERT_MESSAGE( q->name != nullptr, "Undefined texture name" ); + + // It is common to set "" as unknown texture, + // expecting NetRadiant to display the notex fallback. + if ( q->name[0] == '\0' ) + { + q->loaded = false; + return 0; + } + + Image* image = GlobalTexturesCache().loadImage( q->name ); + + if ( image != nullptr ) + { + LoadTextureRGBA( v, q, image ); + q->surfaceFlags = image->getSurfaceFlags(); + q->contentFlags = image->getContentFlags(); + q->value = image->getValue(); + GlobalOpenGL_debugAssertNoErrors(); + globalOutputStream() << "Loaded Texture: \"" << q->name << "\"\n"; + q->loaded = true; + image->release(); + } + else + { + globalErrorStream() << "Texture load failed: \"" << q->name << "\"\n"; + q->texture_number[ v ] = 0; + q->loaded = false; + } + } + + return q->texture_number[ v ]; +} + #if 0 /* ============== @@ -334,29 +389,50 @@ const TestHashtable g_testhashtable; typedef std::pair TextureKey; void qtexture_realise( qtexture_t& texture, const TextureKey& key ){ - texture.texture_number = 0; if ( !string_empty( key.second.c_str() ) ) { - Image* image = key.first.loadImage( key.second.c_str() ); - if ( image != 0 ) { - LoadTextureRGBA( &texture, image->getRGBAPixels(), image->getWidth(), image->getHeight() ); - texture.surfaceFlags = image->getSurfaceFlags(); - texture.contentFlags = image->getContentFlags(); - texture.value = image->getValue(); + for ( int v = VP_NONE; v < VP_MAX; v++ ) + { + texture.texture_number[ v ] = 0; + } + + texture.name = key.second.c_str(); + + ASSERT_MESSAGE( texture.name != nullptr, "Undefined texture name" ); + + // It is common to set "" as unknown texture, + // expecting NetRadiant to display the notex fallback. + if ( texture.name[0] == '\0' ) + { + texture.loaded = false; + return; + } + +#if defined(GL_UNSHARED_CONTEXT) + Image* image = GlobalTexturesCache().loadImage( texture.name ); + if ( image != nullptr ) + { + texture.loaded = true; image->release(); - globalOutputStream() << "Loaded Texture: \"" << key.second.c_str() << "\"\n"; - GlobalOpenGL_debugAssertNoErrors(); } else { - globalErrorStream() << "Texture load failed: \"" << key.second.c_str() << "\"\n"; + globalErrorStream() << "Texture load failed: \"" << texture.name << "\"\n"; + // Can't reuse texture_number because shader plugin has no knowledge about viewport. + texture.loaded = false; } +#else // !GL_UNSHARED_CONTEXT + RequestBindTextureNumber( VP_SHARED, &texture ); +#endif // !GL_UNSHARED_CONTEXT } } void qtexture_unrealise( qtexture_t& texture ){ - if ( GlobalOpenGL().contextValid && texture.texture_number != 0 ) { - glDeleteTextures( 1, &texture.texture_number ); - GlobalOpenGL_debugAssertNoErrors(); + for ( int v = VP_NONE; v < VP_MAX; v++ ) + { + if ( GlobalOpenGL().contextValid && texture.texture_number[ v ] != 0 ) { + glDeleteTextures( 1, &texture.texture_number[ v ] ); + GlobalOpenGL_debugAssertNoErrors(); + } } } @@ -498,7 +574,6 @@ void realise(){ break; } - glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max_tex_size ); if ( max_tex_size == 0 ) { max_tex_size = 1024; @@ -539,9 +614,19 @@ TexturesCache& GetTexturesCache(){ return *g_texturesmap; } +bool texturesToBeRealised = false; void Textures_Realise(){ g_texturesmap->realise(); + texturesToBeRealised = false; +} + +void Textures_TriggerRealise(){ + texturesToBeRealised = true; +} + +bool Textures_TriggeredRealise(){ + return texturesToBeRealised; } void Textures_Unrealise(){ @@ -556,12 +641,13 @@ void Textures_setModeChangedNotify( const Callback& notify ){ } void Textures_ModeChanged(){ + // FIXME: not called by CamWindow thread if ( g_texturesmap->realised() ) { SetTexParameters( g_texture_mode ); for ( TexturesMap::iterator i = g_texturesmap->begin(); i != g_texturesmap->end(); ++i ) { - glBindTexture( GL_TEXTURE_2D, ( *i ).value->texture_number ); + glBindTexture( GL_TEXTURE_2D, ( *i ).value->texture_number[ VP_CAMWINDOW ] ); SetTexParameters( g_texture_mode ); } @@ -582,7 +668,7 @@ void Textures_setTextureComponents( GLint texture_components ){ if ( g_texture_globals.texture_components != texture_components ) { Textures_Unrealise(); g_texture_globals.texture_components = texture_components; - Textures_Realise(); + Textures_TriggerRealise(); } } @@ -662,7 +748,7 @@ struct TextureGamma { if (value != self) { Textures_Unrealise(); self = value; - Textures_Realise(); + Textures_TriggerRealise(); } } }; diff --git a/radiant/textures.h b/radiant/textures.h index 3176ce6d..af300818 100644 --- a/radiant/textures.h +++ b/radiant/textures.h @@ -25,6 +25,8 @@ #include "generic/callback.h" void Textures_Realise(); +void Textures_TriggerRealise(); +bool Textures_TriggeredRealise(); void Textures_Unrealise(); void Textures_sharedContextDestroyed(); diff --git a/radiant/texwindow.cpp b/radiant/texwindow.cpp index f741f0ce..e76528ce 100644 --- a/radiant/texwindow.cpp +++ b/radiant/texwindow.cpp @@ -34,6 +34,7 @@ #include "defaults.h" #include "ifilesystem.h" +#include "itextures.h" #include "iundo.h" #include "igl.h" #include "iarchive.h" @@ -983,6 +984,10 @@ void TextureBrowser_ShowStartupShaders( TextureBrowser& textureBrowser ){ if ( textureBrowser.m_startupShaders == STARTUPSHADERS_COMMON ) { TextureBrowser_ShowDirectory( textureBrowser, TextureBrowser_getCommonShadersDir() ); } + else + { + TextureBrowser_showAll(); + } } @@ -1189,6 +1194,9 @@ void Texture_Draw( TextureBrowser& textureBrowser ){ break; } + ViewportId v = VP_TEXWINDOW; + + GLuint texture_number = RequestBindTextureNumber( v, q ); int nWidth = textureBrowser.getTextureWidth( q ); int nHeight = textureBrowser.getTextureHeight( q ); @@ -1284,7 +1292,14 @@ void Texture_Draw( TextureBrowser& textureBrowser ){ } // Draw the texture - glBindTexture( GL_TEXTURE_2D, q->texture_number ); + +#ifdef DEBUG_TEXTURES + // It is known to produces an infinite loop on macOS with default layout: + // Log → Redraw → Log → Redraw… + globalOutputStream() << "Texture_Draw: " << v << ", " << q->name << ", " << texture_number << "\n"; +#endif + + glBindTexture( GL_TEXTURE_2D, texture_number ); GlobalOpenGL_debugAssertNoErrors(); glColor3f( 1,1,1 ); glBegin( GL_QUADS ); @@ -1317,7 +1332,6 @@ void Texture_Draw( TextureBrowser& textureBrowser ){ //int totalHeight = abs(y) + last_height + TextureBrowser_fontHeight(textureBrowser) + 4; } - // reset the current texture glBindTexture( GL_TEXTURE_2D, 0 ); //qglFinish(); @@ -2611,7 +2625,8 @@ void TextureBrowser_pasteTag(){ void TextureBrowser_RefreshShaders(){ TextureBrowser &textureBrowser = GlobalTextureBrowser(); ScopeDisableScreenUpdates disableScreenUpdates( "Processing...", "Loading Shaders" ); - GlobalShaderSystem().refresh(); + // GlobalShaderSystem is refreshed by camwindow + GlobalShaderSystem().triggerRefresh(); UpdateAllWindows(); auto selection = gtk_tree_view_get_selection(GlobalTextureBrowser().m_treeViewTree); GtkTreeModel* model = NULL; diff --git a/radiant/texwindow.h b/radiant/texwindow.h index f17a980d..4f5a4dc2 100644 --- a/radiant/texwindow.h +++ b/radiant/texwindow.h @@ -39,6 +39,8 @@ void TextureBrowser_destroyWindow(); void TextureBrowser_ShowDirectory( TextureBrowser& textureBrowser, const char* name ); void TextureBrowser_ShowStartupShaders( TextureBrowser& textureBrowser ); +void TextureBrowser_showAll(); + const char* TextureBrowser_GetSelectedShader( TextureBrowser& textureBrower ); void TextureBrowser_Construct(); diff --git a/radiant/xywindow.cpp b/radiant/xywindow.cpp index 35d49170..742b58fb 100644 --- a/radiant/xywindow.cpp +++ b/radiant/xywindow.cpp @@ -36,6 +36,8 @@ #include "ibrush.h" #include "iundo.h" #include "iimage.h" +#include "ishaders.h" +#include "itextures.h" #include "ifilesystem.h" #include "os/path.h" #include "image.h" @@ -65,6 +67,7 @@ #include "brushmanip.h" #include "selection.h" #include "entity.h" +#include "textures.h" #include "camwindow.h" #include "texwindow.h" #include "mainframe.h" @@ -74,11 +77,11 @@ #include "grid.h" #include "windowobservers.h" -void LoadTextureRGBA( qtexture_t* q, unsigned char* pPixels, int nWidth, int nHeight ); - // d1223m extern bool g_brush_always_caulk; +const char *g_xywindow_background_filename = nullptr; + //!\todo Rewrite. class ClipPoint { @@ -530,6 +533,22 @@ void XYWnd_ZoomOut( XYWnd* xy ){ } void XYWnd::Redraw() { + if ( Textures_TriggeredRealise() || GlobalShaderSystem().triggeredRefresh() ) { + if ( glwidget_make_current( m_gl_widget ) != FALSE ) { + // Draw something to not display garbage while camwindow + // is iterating textures. + if ( ScreenUpdates_Enabled() ) { + GlobalOpenGL_debugAssertNoErrors(); + glViewport( 0, 0, m_nWidth, m_nHeight ); + XY_Clear(); + GlobalOpenGL_debugAssertNoErrors(); + } + } + + glwidget_swap_buffers( m_gl_widget ); + return; + } + if ( glwidget_make_current( m_gl_widget ) != FALSE ) { if ( Map_Valid( g_map ) && ScreenUpdates_Enabled() ) { GlobalOpenGL_debugAssertNoErrors(); @@ -538,6 +557,7 @@ void XYWnd::Redraw() { m_XORRectangle.set( rectangle_t() ); } + glwidget_swap_buffers( m_gl_widget ); } } @@ -1436,7 +1456,18 @@ void XYWnd::XY_SnapToGrid( Vector3& point ){ } } -void XYWnd::XY_LoadBackgroundImage( const char *name ){ +void XYWnd::XY_LoadBackgroundImage(){ + if (g_pParentWnd->ActiveXY()->m_backgroundActivated ){ + return; + } + + const char* name = g_xywindow_background_filename; + + if ( name == nullptr ) + { + return; + } + const char* relative = path_make_relative( name, GlobalFileSystem().findRoot( name ) ); if ( relative == name ) { globalOutputStream() << "WARNING: could not extract the relative path, using full path instead\n"; @@ -1447,13 +1478,20 @@ void XYWnd::XY_LoadBackgroundImage( const char *name ){ fileNameWithoutExt[512 - 1] = '\0'; fileNameWithoutExt[strlen( fileNameWithoutExt ) - 4] = '\0'; - Image *image = QERApp_LoadImage( 0, fileNameWithoutExt ); + Image* image = QERApp_LoadImage( 0, fileNameWithoutExt ); + if ( !image ) { globalOutputStream() << "Could not load texture " << fileNameWithoutExt << "\n"; + g_xywindow_background_filename = nullptr; return; } + g_pParentWnd->ActiveXY()->m_tex = (qtexture_t*)malloc( sizeof( qtexture_t ) ); - LoadTextureRGBA( g_pParentWnd->ActiveXY()->XYWnd::m_tex, image->getRGBAPixels(), image->getWidth(), image->getHeight() ); + g_pParentWnd->ActiveXY()->XYWnd::m_tex->name = fileNameWithoutExt; + + // It can be loaded from outside the VFS, don't use RequestBindTextureNumber + LoadTextureRGBA( VP_XYWINDOW, g_pParentWnd->ActiveXY()->XYWnd::m_tex, image ); + globalOutputStream() << "Loaded background texture " << relative << "\n"; g_pParentWnd->ActiveXY()->m_backgroundActivated = true; @@ -1484,6 +1522,7 @@ void XYWnd::XY_LoadBackgroundImage( const char *name ){ void XYWnd::XY_DisableBackground( void ){ g_pParentWnd->ActiveXY()->m_backgroundActivated = false; + g_xywindow_background_filename = nullptr; if ( g_pParentWnd->ActiveXY()->m_tex ) { free( g_pParentWnd->ActiveXY()->m_tex ); } @@ -1501,14 +1540,19 @@ void WXY_BackgroundSelect( void ){ return; } - const char *filename = main_window.file_dialog( TRUE, "Background Image", NULL, NULL ); - g_pParentWnd->ActiveXY()->XY_DisableBackground(); - if ( filename ) { - g_pParentWnd->ActiveXY()->XY_LoadBackgroundImage( filename ); + g_xywindow_background_filename = main_window.file_dialog( TRUE, "Background Image", NULL, NULL ); + + if ( g_xywindow_background_filename != nullptr ) + { + globalOutputStream() << "Selected background texture path: " << g_xywindow_background_filename << "\n"; } + // Request draw immediately, that will also request image loading. + // If something bad happens at image loading time, it will be known + // immediately. + // Draw the background image immediately (do not wait for user input). g_pParentWnd->ActiveXY()->Redraw(); } @@ -1573,6 +1617,22 @@ void XYWnd::XY_DrawAxis( void ){ } void XYWnd::XY_DrawBackground( void ){ + if ( g_xywindow_background_filename == nullptr ) + { + return; + } + + if ( m_tex == nullptr ) + { + XY_LoadBackgroundImage(); + } + + // TODO: activated? + if ( m_tex == nullptr ) + { + return; + } + glPushAttrib( GL_ALL_ATTRIB_BITS ); glEnable( GL_TEXTURE_2D ); @@ -1584,7 +1644,8 @@ void XYWnd::XY_DrawBackground( void ){ glPolygonMode( GL_FRONT, GL_FILL ); - glBindTexture( GL_TEXTURE_2D, m_tex->texture_number ); + // It can be loaded from outside the VFS, don't use RequestBindTextureNumber + glBindTexture( GL_TEXTURE_2D, GetBindTextureNumber( VP_XYWINDOW, m_tex ) ); glBegin( GL_QUADS ); glColor4f( 1.0, 1.0, 1.0, m_alpha ); @@ -2106,7 +2167,11 @@ void addRenderable( const OpenGLRenderable& renderable, const Matrix4& localToWo } void render( const Matrix4& modelview, const Matrix4& projection ){ - GlobalShaderCache().render( m_globalstate, modelview, projection ); + // GlobalShaderSystem is refreshed by camwindow + if ( GlobalShaderSystem().refreshed() ) + { + GlobalShaderCache().render( VP_XYWINDOW, m_globalstate, modelview, projection ); + } } private: std::vector m_state_stack; @@ -2212,17 +2277,20 @@ void XYWnd::updateModelview( bool reconstruct ){ //#define DBG_SCENEDUMP -void XYWnd::XY_Draw(){ - // - // clear - // - glViewport( 0, 0, m_nWidth, m_nHeight ); +void XYWnd::XY_Clear(){ glClearColor( g_xywindow_globals.color_gridback[0], g_xywindow_globals.color_gridback[1], g_xywindow_globals.color_gridback[2],0 ); glClear( GL_COLOR_BUFFER_BIT ); +} +void XYWnd::XY_Draw(){ + // + // clear + // + glViewport( 0, 0, m_nWidth, m_nHeight ); + XY_Clear(); // // set up viewpoint // @@ -2247,9 +2315,7 @@ void XYWnd::XY_Draw(){ glDisable( GL_COLOR_MATERIAL ); glDisable( GL_DEPTH_TEST ); - if ( m_backgroundActivated ) { - XY_DrawBackground(); - } + XY_DrawBackground(); XY_DrawGrid(); if ( g_xywindow_globals_private.show_blocks ) { diff --git a/radiant/xywindow.h b/radiant/xywindow.h index ac059329..8b35b418 100644 --- a/radiant/xywindow.h +++ b/radiant/xywindow.h @@ -69,6 +69,8 @@ guint m_exposeHandler; DeferredDraw m_deferredDraw; DeferredMotion m_deferred_motion; + +void XY_Clear(); public: ui::Window m_parent; XYWnd(); @@ -100,7 +102,7 @@ void XY_DrawBlockGrid(); void XY_DrawAxis(); void XY_DrawGrid(); void XY_DrawBackground(); -void XY_LoadBackgroundImage( const char *name ); +void XY_LoadBackgroundImage(); void XY_DisableBackground(); void XY_MouseUp( int x, int y, unsigned int buttons );