]> git.rm.cloudns.org Git - xonotic/netradiant.git/commitdiff
multicontext: optional per-viewport texture bind
authorThomas Debesse <dev@illwieckz.net>
Thu, 11 Mar 2021 04:59:47 +0000 (05:59 +0100)
committerThomas Debesse <dev@illwieckz.net>
Wed, 29 Sep 2021 21:26:13 +0000 (23:26 +0200)
30 files changed:
CMakeLists.txt
contrib/bobtoolz/shapes.cpp
include/iglrender.h
include/irender.h
include/ishaders.h
include/itextures.h
libs/CMakeLists.txt
libs/gtkutil/glwidget.cpp
libs/modulesystem/singletonmodule.h
libs/texturelib.h
libs/viewport/CMakeLists.txt [new file with mode: 0644]
libs/viewport/viewport.cpp [new file with mode: 0644]
libs/viewport/viewport.h [new file with mode: 0644]
plugins/shaders/shaders.cpp
plugins/spritemodel/spritemodel.cpp
plugins/textool/TexTool.cpp
radiant/CMakeLists.txt
radiant/camwindow.cpp
radiant/camwindow.h
radiant/main.cpp
radiant/mainframe.cpp
radiant/map.cpp
radiant/renderstate.cpp
radiant/surfacedialog.cpp
radiant/textures.cpp
radiant/textures.h
radiant/texwindow.cpp
radiant/texwindow.h
radiant/xywindow.cpp
radiant/xywindow.h

index b2fa296e953cd9c6c2fcfec248b5a3afd8bd3ab1..760c5a8a932aa70b83c064f6be01909be81448ac 100644 (file)
@@ -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 ()
 
 #-----------------------------------------------------------------------
index 2e47035d75b5e0630c611bc8395369198edc32b4..a2a98c63bda56d533a36aa51165976c6fe3d8dfd 100644 (file)
@@ -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 ) {
index 04b4a8078ebd2bec0124dfb708782b54eaf58189..7128543f408fe6af6f8c44465b6eba443a0765a7 100644 (file)
@@ -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;
 
index 169d881a9c59e0c1cadcc8f85d291edb64e5fc16..32272bdae82bbb43de7e765b903ebe4a9eca5a15 100644 (file)
@@ -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;
index b580e2c4c1b2aa33b7998d17d6a364ff6c65aaa2..ed788906d06f7cf53534fe433741b6a7abf2b3bf 100644 (file)
@@ -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;
index 6fa4b02ca51e6c9d7200483cd87248a1c411b33c..5b9d9eecd569dfca4d0de370137f4be90ce1b8f7 100644 (file)
 #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 );
index a306996a019d2c2384435702261d9aa25d74a03d..807e8813819b90a32c39a4bc5f62e9787a15b376 100644 (file)
@@ -14,6 +14,7 @@ add_subdirectory(generic)
 if (BUILD_RADIANT)
     add_subdirectory(gtkutil)
     add_subdirectory(uilib)
+    add_subdirectory(viewport)
 endif ()
 
 add_subdirectory(l_net)
index 8581f7a4a4ef779a0840126e070dd40f21286e12..6c4291c52832e444a27ab69635fc0b72b2cfa98e 100644 (file)
@@ -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);
index 8dcd88de17c708c82161abbd78052e0c1f68874b..be855611751205543132b5445471aa8e695e96c5 100644 (file)
@@ -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;
        }
index d9da1e89dfb4c49d837d9fcbdf4b06013b0d2401..1e4db259352c116ace0e57648e5efe0d499cb8df 100644 (file)
 #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 (file)
index 0000000..0ddc0d3
--- /dev/null
@@ -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 (file)
index 0000000..def73c5
--- /dev/null
@@ -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 (file)
index 0000000..cbf746d
--- /dev/null
@@ -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
index 58c5232581958eebfd36082a1a86336d7e32b76f..65f4c3acdf2289e2ecb59e86e8d75e8873f1e85e 100644 (file)
@@ -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 ){
index 8308416f63707626c0193124c1b3ab401c758a73..af00f514e5df342ad6b9755c2dff6bfc47bac0ae 100644 (file)
@@ -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 ){
index e328bdb08cc73adaf20dc47a19c8ca2973432500..c430d30f45b9c2938c36c85e99a18a364a3aa2b2 100644 (file)
 // 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 ){
index 12ee1dfa8bd8c7a736328b21aaf4126dd1119888..362cebac65cfceb9cc7eed3545f4af241f41744a 100644 (file)
@@ -124,6 +124,7 @@ target_link_libraries(${RADIANT_BASENAME}
     stream
     string
     uilib
+    viewport
     xmllib
 )
 
index 27ae274e8749d768df3bc4e97a455471d0f5534b..233262b5a6e5ade6370e07015159a5ef872ed02d 100644 (file)
@@ -26,6 +26,7 @@
 //
 
 #include "camwindow.h"
+#include "texwindow.h" // TextureBrowser_ShowStartupShaders
 
 #include <gtk/gtk.h>
 #include <gdk/gdkkeysyms.h>
 #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<void(const Callback<void(bool)>&), ShowStatsExport> g_show_stats_call
 Callback<void(const Callback<void(bool)> &)> 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;
 }
index 9ed6d24260d0013dc149e906ba91473948d7ceea..9eb0bba7ee5f6097abb49797584a9df066e6ebe1 100644 (file)
@@ -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 );
index 6c8ffd037992e5173a564986264232adbd9f2562..8dbe88c2dd897b871a1867b6003fc8387ff3f502 100644 (file)
@@ -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();
 
index 17e53efc7279a87a92f409641c3024c7b5acba43..8accc5ef36134f6f756b6e40d20b53dc50cdab4f 100644 (file)
@@ -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 */
index a5ac53f899e63e2a27e863482a12091851efd4ce..c15a2f63b279b04f86b80d95df2519e87a61a391 100644 (file)
@@ -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 &region_mins, const Vector3 &region_maxs );
index f714fc7eb2dd16712ae6486829ae7d81ea8965c0..6b30a19c4d7fced23411a2e7df0570a195dce965 100644 (file)
@@ -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<const float*>( &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<OpenGLShader*>( ( *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<OpenGLShader*>( g_defaultPointLight )->getShader().lightFalloffImage()->texture_number;
+                                                                          ? requestTextureBind( v, lightShader.lightFalloffImage() )
+                                                                          : requestTextureBind( v, static_cast<OpenGLShader*>( 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 ) {
index 9ae43fd7e130fcbe6beb03047e6bacf29c4c5c72..0d39a951b3cf8740ef75f95b0ad984187bb1d224 100644 (file)
@@ -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";
index afa8d4e05c1d180b5c3f9b2e48d0e365edc60c67..0a484f77f0e6935d1b6bbbd5f92a39d6bc992d0b 100644 (file)
@@ -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<int> 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<LoadImageCallback, CopiedString> 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<void()>& 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();
                }
        }
 };
index 3176ce6db44a3f5779752cf479ec6de55c14d7b5..af3008189dc0a25c9f972399c3723eaf44cb795f 100644 (file)
@@ -25,6 +25,8 @@
 #include "generic/callback.h"
 
 void Textures_Realise();
+void Textures_TriggerRealise();
+bool Textures_TriggeredRealise();
 void Textures_Unrealise();
 void Textures_sharedContextDestroyed();
 
index f741f0ceafafa77bff560b1177843f26b02bdf0f..e76528ce1a237d1a8fbd776f6b891b967bf0eceb 100644 (file)
@@ -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;
index f17a980d73b224aadece0e9e313ad13911456f2f..4f5a4dc28e82f147f737fdbc4b56eef43f45055c 100644 (file)
@@ -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();
index 35d491701aa9a318ee3cfa822aa852a02b302f81..742b58fbaa785163bce80742805246239d5d1899 100644 (file)
@@ -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"
 #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<state_type> 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 ) {
index ac059329518178de2d63c32843579c56352f6be8..8b35b418c8babed94049295df114366e67b92507 100644 (file)
@@ -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 );