Browse Source

Merge branch 'shaderpipeline' into vulkan

rdb 2 years ago
parent
commit
15bde34f1a
83 changed files with 1846 additions and 1342 deletions
  1. 3 3
      .github/workflows/ci.yml
  2. 8 0
      CMakeLists.txt
  3. 3 3
      contrib/src/panda3dtoolsgui/Panda3DToolsGUI.py
  4. 16 0
      direct/src/dist/FreezeTool.py
  5. 16 1
      direct/src/dist/commands.py
  6. 1 1
      direct/src/showbase/PythonUtil.py
  7. 16 1
      dtool/CompilerFlags.cmake
  8. 3 1
      dtool/Package.cmake
  9. 17 8
      dtool/src/dtoolutil/filename.cxx
  10. 5 0
      dtool/src/dtoolutil/filename.h
  11. 15 5
      dtool/src/interrogate/functionRemap.cxx
  12. 1 0
      dtool/src/interrogate/functionRemap.h
  13. 8 1
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  14. 1 1
      makepanda/makepackage.py
  15. 7 1
      panda/src/audiotraits/CMakeLists.txt
  16. 4 0
      panda/src/cocoadisplay/cocoaGraphicsPipe.mm
  17. 5 7
      panda/src/display/graphicsOutput.cxx
  18. 1 1
      panda/src/display/graphicsStateGuardian.cxx
  19. 1 1
      panda/src/display/graphicsStateGuardian.h
  20. 15 24
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  21. 2 2
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  22. 2 2
      panda/src/dxgsg9/dxShaderContext9.cxx
  23. 9 8
      panda/src/dxgsg9/dxTextureContext9.I
  24. 394 338
      panda/src/dxgsg9/dxTextureContext9.cxx
  25. 10 9
      panda/src/dxgsg9/dxTextureContext9.h
  26. 12 12
      panda/src/dxgsg9/wdxGraphicsBuffer9.cxx
  27. 28 3
      panda/src/egldisplay/eglGraphicsBuffer.cxx
  28. 2 0
      panda/src/egldisplay/eglGraphicsBuffer.h
  29. 0 1
      panda/src/egldisplay/eglGraphicsPipe.cxx
  30. 1 1
      panda/src/express/nodeReferenceCount.I
  31. 1 1
      panda/src/express/referenceCount.I
  32. 6 5
      panda/src/express/virtualFileSystem.I
  33. 17 0
      panda/src/gles2gsg/gles2gsg.h
  34. 1 1
      panda/src/glstuff/glGeomMunger_src.cxx
  35. 42 45
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  36. 1 1
      panda/src/glstuff/glGraphicsBuffer_src.h
  37. 437 302
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  38. 14 8
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  39. 41 106
      panda/src/glstuff/glShaderContext_src.cxx
  40. 0 3
      panda/src/glstuff/glShaderContext_src.h
  41. 29 9
      panda/src/glstuff/glTextureContext_src.I
  42. 68 56
      panda/src/glstuff/glTextureContext_src.cxx
  43. 15 14
      panda/src/glstuff/glTextureContext_src.h
  44. 0 10
      panda/src/glstuff/glmisc_src.cxx
  45. 0 1
      panda/src/glstuff/glmisc_src.h
  46. 9 11
      panda/src/gobj/preparedGraphicsObjects.cxx
  47. 1 1
      panda/src/gobj/preparedGraphicsObjects.h
  48. 106 131
      panda/src/gobj/texture.cxx
  49. 6 4
      panda/src/gobj/texture.h
  50. 13 7
      panda/src/gobj/textureContext.I
  51. 2 2
      panda/src/gobj/textureContext.h
  52. 1 1
      panda/src/gsgbase/graphicsStateGuardianBase.h
  53. 3 3
      panda/src/iphone/ipfreeze.py
  54. 1 1
      panda/src/iphone/provision.py
  55. 2 0
      panda/src/pgraph/CMakeLists.txt
  56. 4 0
      panda/src/pgraph/bamFile.h
  57. 28 0
      panda/src/pgraph/bamFile_ext.cxx
  58. 37 0
      panda/src/pgraph/bamFile_ext.h
  59. 1 0
      panda/src/pgraph/p3pgraph_ext_composite.cxx
  60. 16 3
      panda/src/pstatclient/pStatClient_ext.cxx
  61. 2 0
      panda/src/putil/CMakeLists.txt
  62. 1 1
      panda/src/putil/bamReader.cxx
  63. 4 0
      panda/src/putil/bamWriter.h
  64. 28 0
      panda/src/putil/bamWriter_ext.cxx
  65. 37 0
      panda/src/putil/bamWriter_ext.h
  66. 2 2
      panda/src/putil/copyOnWriteObject.I
  67. 16 0
      panda/src/putil/datagramOutputFile.cxx
  68. 1 0
      panda/src/putil/datagramOutputFile.h
  69. 1 0
      panda/src/putil/p3putil_ext_composite.cxx
  70. 6 6
      panda/src/testbed/test_native_net1.py
  71. 3 3
      panda/src/testbed/test_native_net2.py
  72. 2 2
      panda/src/testbed/test_native_net3.py
  73. 1 0
      panda/src/tinydisplay/tinyCocoaGraphicsWindow.mm
  74. 148 125
      panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx
  75. 9 9
      panda/src/tinydisplay/tinyGraphicsStateGuardian.h
  76. 4 2
      panda/src/tinydisplay/tinyTextureContext.I
  77. 8 0
      panda/src/tinydisplay/tinyTextureContext.cxx
  78. 1 1
      panda/src/tinydisplay/tinyTextureContext.h
  79. 1 1
      panda/src/tinydisplay/zbuffer.h
  80. 6 1
      panda/src/tinydisplay/zgl.h
  81. 2 2
      panda/src/wgldisplay/wglGraphicsBuffer.cxx
  82. 53 26
      panda/src/windisplay/winGraphicsWindow.cxx
  83. 2 0
      panda/src/windisplay/winGraphicsWindow.h

+ 3 - 3
.github/workflows/ci.yml

@@ -40,7 +40,7 @@ jobs:
           eigen: NO
 
         - profile: macos-eigen-coverage-unity-xcode
-          os: macOS-10.15
+          os: macOS-11
           config: Coverage
           unity: YES
           generator: Xcode
@@ -50,7 +50,7 @@ jobs:
           eigen: YES
 
         - profile: macos-nometa-standard-makefile
-          os: macOS-10.15
+          os: macOS-11
           config: Standard
           unity: NO
           generator: Unix Makefiles
@@ -87,7 +87,7 @@ jobs:
         fetch-depth: 10
 
     - name: Self-destruct makepanda
-      run: python makepanda/selfdestruct.py --yes
+      run: python3 makepanda/selfdestruct.py --yes
 
     - name: Install dependencies (macOS)
       if: runner.os == 'macOS'

+ 8 - 0
CMakeLists.txt

@@ -24,6 +24,9 @@ endif()
 
 # Set defaults for macOS, must be before project().
 if(APPLE)
+  # Needed for enable_language(OBJCXX)
+  cmake_minimum_required(VERSION 3.16)
+
   set(CMAKE_OSX_DEPLOYMENT_TARGET "10.9" CACHE STRING "Minimum macOS version to target")
   set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++")
 
@@ -46,6 +49,11 @@ project(Panda3D VERSION ${_version})
 unset(_version)
 unset(_s)
 
+if(APPLE)
+  # Allows separating out C++ flags from ObjC++ flags
+  enable_language(OBJCXX)
+endif()
+
 # Determine the possible build types.  Must be *after* calling project().
 set(_configs Standard Release RelWithDebInfo Debug MinSizeRel)
 if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang|GCC)")

+ 3 - 3
contrib/src/panda3dtoolsgui/Panda3DToolsGUI.py

@@ -1905,7 +1905,7 @@ class main(wx.Frame):
                             self.txaExtraLines.append(line)
                 txafile.close()
             except:
-                print "Error opening .txa file!"
+                print("Error opening .txa file!")
             self.palettize_saveTxaTxt.SetValue(os.path.join(dirname + os.sep , filename))
         dlg.Destroy()
 
@@ -2705,7 +2705,7 @@ class main(wx.Frame):
             selectedItemIndex = int(self.batchTree.GetItemText(selectedItemId).split()[0])-1
             batchItem = self.batchList[selectedItemIndex]
 
-            print '\n'+self.BuildCommand(batchItem)
+            print('\n'+self.BuildCommand(batchItem))
 
             if (batchItem['cmd'].count('maya2egg')):
                 # Display Maya2Egg Tool Panel
@@ -2840,7 +2840,7 @@ class main(wx.Frame):
                                 self.txaExtraLines.append(line)
                     txafile.close()
                 except:
-                    print "Error opening .txa file!"
+                    print("Error opening .txa file!")
 
             self.batchItemNameTxt.SetValue(batchItem['label'])
 

+ 16 - 0
direct/src/dist/FreezeTool.py

@@ -90,6 +90,7 @@ defaultHiddenImports = {
     'scipy.special._ufuncs': ['scipy.special._ufuncs_cxx'],
     'scipy.stats._stats': ['scipy.special.cython_special'],
     'setuptools.monkey': ['setuptools.msvc'],
+    'shapely._geometry_helpers': ['shapely._geos'],
 }
 
 
@@ -2583,6 +2584,21 @@ class PandaModuleFinder(modulefinder.ModuleFinder):
             else:
                 code = fp.read()
 
+            # Strip out delvewheel patch (see GitHub issue #1492)
+            if isinstance(code, bytes):
+                # Don't look for \n at the end, it may also be \r\n
+                start_marker = b'# start delvewheel patch'
+                end_marker = b'# end delvewheel patch'
+            else:
+                start_marker = '# start delvewheel patch'
+                end_marker = '# end delvewheel patch'
+
+            start = code.find(start_marker)
+            while start >= 0:
+                end = code.find(end_marker, start) + len(end_marker)
+                code = code[:start] + code[end:]
+                start = code.find(start_marker)
+
             code += b'\n' if isinstance(code, bytes) else '\n'
             co = compile(code, pathname, 'exec', optimize=self.optimize)
         elif type == imp.PY_COMPILED:

+ 16 - 1
direct/src/dist/commands.py

@@ -634,7 +634,20 @@ class build_apps(setuptools.Command):
         for index in self.pypi_extra_indexes:
             pip_args += ['--extra-index-url', index]
 
-        subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
+        try:
+            subprocess.check_call([sys.executable, '-m', 'pip'] + pip_args)
+        except:
+            # Display a more helpful message for these common issues.
+            if platform.startswith('manylinux2010_') and sys.version_info >= (3, 11):
+                new_platform = platform.replace('manylinux2010_', 'manylinux2014_')
+                self.announce('This error likely occurs because {} is not a supported target as of Python 3.11.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
+            elif platform.startswith('manylinux1_') and sys.version_info >= (3, 10):
+                new_platform = platform.replace('manylinux1_', 'manylinux2014_')
+                self.announce('This error likely occurs because {} is not a supported target as of Python 3.10.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
+            elif platform.startswith('macosx_10_6_') and sys.version_info >= (3, 8):
+                new_platform = platform.replace('macosx_10_6_', 'macosx_10_9_')
+                self.announce('This error likely occurs because {} is not a supported target as of Python 3.8.\nChange the target platform to {} instead.'.format(platform, new_platform), distutils.log.ERROR)
+            raise
 
         # Return a list of paths to the downloaded whls
         return [
@@ -1689,6 +1702,8 @@ class bdist_apps(setuptools.Command):
             setattr(self, opt, None)
 
     def finalize_options(self):
+        import pkg_resources
+
         # We need to massage the inputs a bit in case they came from a
         # setup.cfg file.
         self.installers = {

+ 1 - 1
direct/src/showbase/PythonUtil.py

@@ -11,7 +11,7 @@ __all__ = [
     'boolEqual', 'lineupPos', 'formatElapsedSeconds', 'solveQuadratic',
     'findPythonModule', 'mostDerivedLast', 'clampScalar', 'weightedChoice',
     'randFloat', 'normalDistrib', 'weightedRand', 'randUint31', 'randInt32',
-    'SerialNumGen', 'serialNum', 'uniqueName', 'Singleton',
+    'SerialNumGen', 'SerialMaskedGen', 'serialNum', 'uniqueName', 'Singleton',
     'SingletonError', 'printListEnum', 'safeRepr', 'fastRepr',
     'isDefaultValue', 'ScratchPad', 'Sync', 'itype', 'getNumberedTypedString',
     'getNumberedTypedSortedString', 'printNumberedTyped', 'DelayedCall',

+ 16 - 1
dtool/CompilerFlags.cmake

@@ -16,6 +16,7 @@ if(MSVC)
 else()
   set(CMAKE_C_FLAGS_STANDARD "-O3")
   set(CMAKE_CXX_FLAGS_STANDARD "-O3")
+  set(CMAKE_OBJCXX_FLAGS_STANDARD "-O3")
 endif()
 set(CMAKE_SHARED_LINKER_FLAGS_STANDARD "")
 set(CMAKE_MODULE_LINKER_FLAGS_STANDARD "")
@@ -27,6 +28,8 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(AppleClang|Clang)")
     "${CMAKE_C_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
   set(CMAKE_CXX_FLAGS_COVERAGE
     "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
+  set(CMAKE_OBJCXX_FLAGS_COVERAGE
+    "${CMAKE_OBJCXX_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
 
   set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
     "${CMAKE_SHARED_LINKER_FLAGS_DEBUG} -fprofile-instr-generate")
@@ -101,11 +104,14 @@ endif()
 # Set warning levels
 if(MSVC)
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /W3")
-  set(CMAKE_CCXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
+  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3")
 
 else()
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+  if(APPLE)
+    set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} -Wall")
+  endif()
 
 endif()
 
@@ -133,6 +139,14 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
   set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} ${release_flags}")
   set(CMAKE_CXX_FLAGS_STANDARD "${CMAKE_CXX_FLAGS_STANDARD} ${standard_flags}")
 
+  if(APPLE)
+    set(CMAKE_OBJCXX_FLAGS "${CMAKE_OBJCXX_FLAGS} ${global_flags}")
+    set(CMAKE_OBJCXX_FLAGS_RELEASE "${CMAKE_OBJCXX_FLAGS_RELEASE} ${global_flags}")
+    set(CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO "${CMAKE_OBJCXX_FLAGS_RELWITHDEBINFO} ${global_flags}")
+    set(CMAKE_OBJCXX_FLAGS_MINSIZEREL "${CMAKE_OBJCXX_FLAGS_MINSIZEREL} ${global_flags}")
+    set(CMAKE_OBJCXX_FLAGS_STANDARD "${CMAKE_OBJCXX_FLAGS_STANDARD} ${global_flags}")
+  endif()
+
   if(MSVC)
     # Clang behaving as MSVC
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-command-line-argument")
@@ -151,6 +165,7 @@ endif()
 # and stops us from identifying cases where ENABLE_EXPORTS is needed.
 set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
 set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
+set(CMAKE_SHARED_LIBRARY_LINK_OBJCXX_FLAGS "")
 
 # As long as we're figuring out compiler flags, figure out the flags for
 # turning C++ exception support on and off

+ 3 - 1
dtool/Package.cmake

@@ -515,7 +515,9 @@ package_status(OPUS "Opus")
 #
 
 # FMOD Ex
-find_package(FMODEx QUIET)
+if(NOT APPLE)
+  find_package(FMODEx QUIET)
+endif()
 
 package_option(FMODEx
   "This enables support for the FMOD Ex sound library,

+ 17 - 8
dtool/src/dtoolutil/filename.cxx

@@ -1289,9 +1289,12 @@ to_os_long_name() const {
 }
 
 /**
- * Returns true if the filename exists on the disk, false otherwise.  If the
- * type is indicated to be executable, this also tests that the file has
+ * Returns true if the filename exists on the physical disk, false otherwise.
+ * If the type is indicated to be executable, this also tests that the file has
  * execute permission.
+ *
+ * @see VirtualFileSystem::exists() for checking whether the filename exists in
+ * the virtual file system.
  */
 bool Filename::
 exists() const {
@@ -1320,8 +1323,11 @@ exists() const {
 }
 
 /**
- * Returns true if the filename exists and is the name of a regular file (i.e.
- * not a directory or device), false otherwise.
+ * Returns true if the filename exists on the physical disk and is the name of
+ * a regular file (i.e. not a directory or device), false otherwise.
+ *
+ * @see VirtualFileSystem::is_regular_file() for checking whether the filename
+ * exists and is a regular file in the virtual file system.
  */
 bool Filename::
 is_regular_file() const {
@@ -1350,8 +1356,8 @@ is_regular_file() const {
 }
 
 /**
- * Returns true if the filename exists and is either a directory or a regular
- * file that can be written to, or false otherwise.
+ * Returns true if the filename exists on the physical disk and is either a
+ * directory or a regular file that can be written to, or false otherwise.
  */
 bool Filename::
 is_writable() const {
@@ -1382,8 +1388,11 @@ is_writable() const {
 }
 
 /**
- * Returns true if the filename exists and is a directory name, false
- * otherwise.
+ * Returns true if the filename exists on the physical disk and is a directory
+ * name, false otherwise.
+ *
+ * @see VirtualFileSystem::is_directory() for checking whether the filename
+ * exists as a directory in the virtual file system.
  */
 bool Filename::
 is_directory() const {

+ 5 - 0
dtool/src/dtoolutil/filename.h

@@ -36,6 +36,11 @@ class DSearchPath;
  * convention, and it knows how to perform basic OS-specific I/O, like testing
  * for file existence and searching a searchpath, as well as the best way to
  * open an fstream for reading or writing.
+ *
+ * Note that the methods of Filename that interact with the filesystem (such
+ * as exists(), open_read(), etc.) directly interface with the operating system
+ * and are not aware of Panda's virtual file system.  To interact with the VFS,
+ * use the methods on VirtualFileSystem instead.
  */
 class EXPCL_DTOOL_DTOOLUTIL Filename {
 PUBLISHED:

+ 15 - 5
dtool/src/interrogate/functionRemap.cxx

@@ -494,6 +494,10 @@ get_call_str(const string &container, const vector_string &pexprs) const {
       call << separator << "self";
       separator = ", ";
     }
+    if (_flags & F_explicit_cls) {
+      call << separator << "cls";
+      separator = ", ";
+    }
 
     size_t pn;
     size_t num_parameters = pexprs.size();
@@ -781,14 +785,20 @@ setup_properties(const InterrogateFunction &ifunc, InterfaceMaker *interface_mak
     first_param = 1;
   }
 
-  if (_parameters.size() > first_param && _parameters[first_param]._name == "self" &&
+  if (_parameters.size() > first_param &&
       TypeManager::is_pointer_to_PyObject(_parameters[first_param]._remap->get_orig_type())) {
     // Here's a special case.  If the first parameter of a nonstatic method
     // is a PyObject * called "self", then we will automatically fill it in
-    // from the this pointer, and remove it from the generated parameter
-    // list.
-    _parameters.erase(_parameters.begin() + first_param);
-    _flags |= F_explicit_self;
+    // from the this pointer, and remove it from the generated parameter list.
+    // For static methods, we offer "cls" instead, containing the type object.
+    if (_parameters[first_param]._name == "self") {
+      _parameters.erase(_parameters.begin() + first_param);
+      _flags |= F_explicit_self;
+    }
+    else if (!_has_this && _parameters[first_param]._name == "cls") {
+      _parameters.erase(_parameters.begin() + first_param);
+      _flags |= F_explicit_cls;
+    }
   }
 
   if (_parameters.size() == first_param) {

+ 1 - 0
dtool/src/interrogate/functionRemap.h

@@ -101,6 +101,7 @@ public:
     F_divide_integer     = 0x2000,
     F_hash               = 0x4000,
     F_explicit_args      = 0x8000,
+    F_explicit_cls       =0x10000,
   };
 
   typedef std::vector<Parameter> Parameters;

+ 8 - 1
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -1706,7 +1706,11 @@ write_module_class(ostream &out, Object *obj) {
     }
 
     if (!func->_has_this) {
-      flags += " | METH_STATIC";
+      if (func->_flags & FunctionRemap::F_explicit_cls) {
+        flags += " | METH_CLASS";
+      } else {
+        flags += " | METH_STATIC";
+      }
 
       // Skip adding this entry if we also have a property with the same name.
       // In that case, we will use a Dtool_StaticProperty to disambiguate
@@ -3652,6 +3656,9 @@ write_function_for_top(ostream &out, InterfaceMaker::Object *obj, InterfaceMaker
   if (func->_has_this) {
     prototype += "self";
   }
+  else if (func->_flags & FunctionRemap::F_explicit_cls) {
+    prototype += "cls";
+  }
 
   switch (func->_args_type) {
   case AT_keyword_args:

+ 1 - 1
makepanda/makepackage.py

@@ -742,7 +742,7 @@ def MakeInstallerOSX(version, python_versions=[], installdir=None, **kwargs):
     dist.write('</installer-script>\n')
     dist.close()
 
-    oscmd('hdiutil create Panda3D-rw.dmg -volname "Panda3D SDK %s" -srcfolder dstroot/Panda3D' % (version))
+    oscmd('hdiutil create Panda3D-rw.dmg -fs HFS+ -volname "Panda3D SDK %s" -srcfolder dstroot/Panda3D' % (version))
     oscmd('hdiutil convert Panda3D-rw.dmg -format UDBZ -o %s' % (dmg_name))
     oscmd('rm -f Panda3D-rw.dmg')
 

+ 7 - 1
panda/src/audiotraits/CMakeLists.txt

@@ -58,7 +58,13 @@ if(HAVE_OPENAL)
   composite_sources(p3openal_audio P3OPENAL_SOURCES)
   add_library(p3openal_audio ${MODULE_TYPE} ${P3OPENAL_HEADERS} ${P3OPENAL_SOURCES})
   set_target_properties(p3openal_audio PROPERTIES DEFINE_SYMBOL BUILDING_OPENAL_AUDIO)
-  target_link_libraries(p3openal_audio panda PKG::OPENAL)
+  target_link_libraries(p3openal_audio panda)
+
+  if(HAVE_OPENAL_FRAMEWORK)
+    target_link_libraries(p3openal_audio "-framework OpenAL")
+  else()
+    target_link_libraries(p3openal_audio PKG::OPENAL)
+  endif()
 
   if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang)$")
     # When statically linking OpenAL, keep its symbols private to this module.

+ 4 - 0
panda/src/cocoadisplay/cocoaGraphicsPipe.mm

@@ -16,8 +16,12 @@
 #include "displayInformation.h"
 
 #import <Foundation/NSAutoreleasePool.h>
+#import <Foundation/NSArray.h>
+#import <Foundation/NSDictionary.h>
+#import <Foundation/NSThread.h>
 #import <AppKit/NSApplication.h>
 #import <AppKit/NSRunningApplication.h>
+#import <AppKit/NSScreen.h>
 
 #include <mach-o/arch.h>
 

+ 5 - 7
panda/src/display/graphicsOutput.cxx

@@ -1150,8 +1150,9 @@ clear_pipe() {
 
 /**
  * Changes the x_size and y_size, then recalculates structures that depend on
- * size.  The recalculation currently includes: - compute_pixels on all the
- * graphics regions.  - updating the texture card, if one is present.
+ * size.  The recalculation currently includes:
+ *  - compute_pixels on all the graphics regions.
+ *  - updating the texture card, if one is present.
  */
 void GraphicsOutput::
 set_size_and_recalc(int x, int y) {
@@ -1163,11 +1164,8 @@ set_size_and_recalc(int x, int y) {
   int fb_x_size = get_fb_x_size();
   int fb_y_size = get_fb_y_size();
 
-  TotalDisplayRegions::iterator dri;
-  for (dri = _total_display_regions.begin();
-       dri != _total_display_regions.end();
-       ++dri) {
-    (*dri)->compute_pixels_all_stages(fb_x_size, fb_y_size);
+  for (DisplayRegion *dr : _total_display_regions) {
+    dr->compute_pixels_all_stages(fb_x_size, fb_y_size);
   }
 
   if (_texture_card != nullptr && _texture_card->get_num_geoms() > 0) {

+ 1 - 1
panda/src/display/graphicsStateGuardian.cxx

@@ -538,7 +538,7 @@ get_scene() const {
  * call Texture::prepare().
  */
 TextureContext *GraphicsStateGuardian::
-prepare_texture(Texture *, int) {
+prepare_texture(Texture *) {
   return nullptr;
 }
 

+ 1 - 1
panda/src/display/graphicsStateGuardian.h

@@ -291,7 +291,7 @@ PUBLISHED:
   MAKE_PROPERTY(scene, get_scene, set_scene);
 
 public:
-  virtual TextureContext *prepare_texture(Texture *tex, int view);
+  virtual TextureContext *prepare_texture(Texture *tex);
   virtual bool update_texture(TextureContext *tc, bool force);
   virtual void release_texture(TextureContext *tc);
   virtual void release_textures(const pvector<TextureContext *> &contexts);

+ 15 - 24
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -175,8 +175,8 @@ DXGraphicsStateGuardian9::
  * call Texture::prepare().
  */
 TextureContext *DXGraphicsStateGuardian9::
-prepare_texture(Texture *tex, int view) {
-  DXTextureContext9 *dtc = new DXTextureContext9(_prepared_objects, tex, view);
+prepare_texture(Texture *tex) {
+  DXTextureContext9 *dtc = new DXTextureContext9(_prepared_objects, tex);
 
   if (!get_supports_compressed_texture_format(tex->get_ram_image_compression())) {
     dxgsg9_cat.error()
@@ -192,7 +192,7 @@ prepare_texture(Texture *tex, int view) {
  * stage.
  */
 void DXGraphicsStateGuardian9::
-apply_texture(int i, TextureContext *tc, const SamplerState &sampler) {
+apply_texture(int i, TextureContext *tc, int view, const SamplerState &sampler) {
   if (tc == nullptr) {
     // The texture wasn't bound properly or something, so ensure texturing is
     // disabled and just return.
@@ -282,7 +282,7 @@ apply_texture(int i, TextureContext *tc, const SamplerState &sampler) {
   float lod_bias = sampler.get_lod_bias();
   set_sampler_state(i, D3DSAMP_MIPMAPLODBIAS, *(DWORD*)&lod_bias);
 
-  _d3d_device->SetTexture(i, dtc->get_d3d_texture());
+  _d3d_device->SetTexture(i, dtc->get_d3d_texture(view));
 }
 
 /**
@@ -370,20 +370,10 @@ release_texture(TextureContext *tc) {
  */
 bool DXGraphicsStateGuardian9::
 extract_texture_data(Texture *tex) {
-  bool success = true;
-
-  int num_views = tex->get_num_views();
-  for (int view = 0; view < num_views; ++view) {
-    TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
-    nassertr(tc != nullptr, false);
-    DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
-
-    if (!dtc->extract_texture_data(*_screen)) {
-      success = false;
-    }
-  }
-
-  return success;
+  TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
+  nassertr(tc != nullptr, false);
+  DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
+  return dtc->extract_texture_data(*_screen);
 }
 
 /**
@@ -1898,7 +1888,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
   // must use a render target type texture for StretchRect
   tex->set_render_to_texture(true);
 
-  TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
+  TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
   if (tc == nullptr) {
     return false;
   }
@@ -1915,10 +1905,11 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
     // for now.
     return do_framebuffer_copy_to_ram(tex, view, z, dr, rb, true);
   }
-  nassertr(dtc->get_d3d_2d_texture() != nullptr, false);
+  IDirect3DTexture9 *d3d_2d_texture = dtc->get_d3d_2d_texture(view);
+  nassertr(d3d_2d_texture != nullptr, false);
 
   IDirect3DSurface9 *tex_level_0;
-  hr = dtc->get_d3d_2d_texture()->GetSurfaceLevel(0, &tex_level_0);
+  hr = d3d_2d_texture->GetSurfaceLevel(0, &tex_level_0);
   if (FAILED(hr)) {
     dxgsg9_cat.error() << "GetSurfaceLev failed in copy_texture" << D3DERRORSTRING(hr);
     return false;
@@ -1943,7 +1934,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
           << "Unable to re-create texture " << *dtc->get_texture() << endl;
         return false;
       }
-      hr = dtc->get_d3d_2d_texture()->GetSurfaceLevel(0, &tex_level_0);
+      hr = d3d_2d_texture->GetSurfaceLevel(0, &tex_level_0);
       if (FAILED(hr)) {
         dxgsg9_cat.error() << "GetSurfaceLev failed in copy_texture" << D3DERRORSTRING(hr);
         return false;
@@ -3677,8 +3668,8 @@ update_standard_texture_bindings() {
     // We always reissue every stage in DX, just in case the texcoord index or
     // texgen mode or some other property has changed.
     int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
-    TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
-    apply_texture(si, tc, sampler);
+    TextureContext *tc = texture->prepare_now(_prepared_objects, this);
+    apply_texture(si, tc, view, sampler);
     set_texture_blend_mode(si, stage);
 
     int texcoord_dimensions = 2;

+ 2 - 2
panda/src/dxgsg9/dxGraphicsStateGuardian9.h

@@ -63,8 +63,8 @@ public:
     calc_fb_properties(DWORD cformat, DWORD dformat,
                        DWORD multisampletype, DWORD multisamplequality);
 
-  virtual TextureContext *prepare_texture(Texture *tex, int view);
-  void apply_texture(int i, TextureContext *tc, const SamplerState &sampler);
+  virtual TextureContext *prepare_texture(Texture *tex);
+  void apply_texture(int i, TextureContext *tc, int view, const SamplerState &sampler);
   virtual bool update_texture(TextureContext *tc, bool force);
   bool upload_texture(DXTextureContext9 *dtc, bool force);
   virtual void release_texture(TextureContext *tc);

+ 2 - 2
panda/src/dxgsg9/dxShaderContext9.cxx

@@ -1085,12 +1085,12 @@ update_shader_texture_bindings(DXShaderContext9 *prev, GSG *gsg) {
       }
     }
 
-    TextureContext *tc = tex->prepare_now(view, gsg->_prepared_objects, gsg);
+    TextureContext *tc = tex->prepare_now(gsg->_prepared_objects, gsg);
     if (tc == nullptr) {
       continue;
     }
 
-    gsg->apply_texture(texunit, tc, sampler);
+    gsg->apply_texture(texunit, tc, view, sampler);
   }
 }
 

+ 9 - 8
panda/src/dxgsg9/dxTextureContext9.I

@@ -24,8 +24,9 @@ has_mipmaps() const {
  * texture it is.
  */
 INLINE IDirect3DBaseTexture9 *DXTextureContext9::
-get_d3d_texture() const {
-  return _d3d_texture;
+get_d3d_texture(int view) const {
+  view = (std::max)((std::min)(view, (int)_d3d_textures.size() - 1), 0);
+  return _d3d_textures[view];
 }
 
 /**
@@ -33,8 +34,8 @@ get_d3d_texture() const {
  * 1-d or 2-d texture.
  */
 INLINE IDirect3DTexture9 *DXTextureContext9::
-get_d3d_2d_texture() const {
-  return _d3d_2d_texture;
+get_d3d_2d_texture(int view) const {
+  return (IDirect3DTexture9 *)get_d3d_texture(view);
 }
 
 /**
@@ -42,8 +43,8 @@ get_d3d_2d_texture() const {
  * 3-d texture.
  */
 INLINE IDirect3DVolumeTexture9 *DXTextureContext9::
-get_d3d_volume_texture() const {
-  return _d3d_volume_texture;
+get_d3d_volume_texture(int view) const {
+  return (IDirect3DVolumeTexture9 *)get_d3d_texture(view);
 }
 
 /**
@@ -51,6 +52,6 @@ get_d3d_volume_texture() const {
  * cube map texture.
  */
 INLINE IDirect3DCubeTexture9 *DXTextureContext9::
-get_d3d_cube_texture() const {
-  return _d3d_cube_texture;
+get_d3d_cube_texture(int view) const {
+  return (IDirect3DCubeTexture9 *)get_d3d_texture(view);
 }

+ 394 - 338
panda/src/dxgsg9/dxTextureContext9.cxx

@@ -36,18 +36,14 @@ static const DWORD g_LowByteMask = 0x000000FF;
  *
  */
 DXTextureContext9::
-DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex, int view) :
-  TextureContext(pgo, tex, view) {
+DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex) :
+  TextureContext(pgo, tex) {
 
   if (dxgsg9_cat.is_spam()) {
     dxgsg9_cat.spam()
       << "Creating DX texture [" << tex->get_name() << "], minfilter(" << tex->get_minfilter() << "), magfilter(" << tex->get_magfilter() << "), anisodeg(" << tex->get_anisotropic_degree() << ")\n";
   }
 
-  _d3d_texture = nullptr;
-  _d3d_2d_texture = nullptr;
-  _d3d_volume_texture = nullptr;
-  _d3d_cube_texture = nullptr;
   _has_mipmaps = false;
   _is_render_target = false;
   _managed = -1;
@@ -101,7 +97,7 @@ bool DXTextureContext9::
 create_texture(DXScreenData &scrn) {
 
   // check if the texture has already been created
-  if (_d3d_2d_texture || _d3d_cube_texture || _d3d_volume_texture) {
+  if (!_d3d_textures.empty()) {
     // texture already created, no need to create
     return true;
   }
@@ -146,21 +142,22 @@ create_texture(DXScreenData &scrn) {
     }
   }
 
-  switch (tex->get_texture_type()) {
-    case Texture::TT_1d_texture:
-    case Texture::TT_2d_texture:
-    case Texture::TT_cube_map:
-      // no compression for render target textures, or very small textures
-      if (!tex->get_render_to_texture() &&
-          orig_width >= 4 && orig_height >= 4) {
-        if (texture_wants_compressed){
-          compress_texture = true;
-        }
+  Texture::TextureType texture_type = tex->get_texture_type();
+  switch (texture_type) {
+  case Texture::TT_1d_texture:
+  case Texture::TT_2d_texture:
+  case Texture::TT_cube_map:
+    // no compression for render target textures, or very small textures
+    if (!tex->get_render_to_texture() && orig_width >= 4 && orig_height >= 4) {
+      if (texture_wants_compressed){
+        compress_texture = true;
       }
-      break;
-    case Texture::TT_3d_texture:
-      // compression of 3d textures not supported by all video chips
-      break;
+    }
+    break;
+
+  case Texture::TT_3d_texture:
+    // compression of 3d textures not supported by all video chips
+    break;
   }
 
   if (texture_stored_compressed && !compress_texture) {
@@ -259,7 +256,7 @@ create_texture(DXScreenData &scrn) {
 
   DWORD filter_caps;
 
-  switch (tex->get_texture_type()) {
+  switch (texture_type) {
   case Texture::TT_1d_texture:
   case Texture::TT_2d_texture:
     filter_caps = scrn._d3dcaps.TextureFilterCaps;
@@ -353,7 +350,7 @@ create_texture(DXScreenData &scrn) {
 
   if (orig_width != target_width || orig_height != target_height ||
       orig_depth != target_depth) {
-    if (tex->get_texture_type() == Texture::TT_3d_texture) {
+    if (texture_type == Texture::TT_3d_texture) {
       dxgsg9_cat.info()
         << "Reducing size of " << tex->get_name()
         << " from " << orig_width << "x" << orig_height << "x" << orig_depth
@@ -970,17 +967,18 @@ create_texture(DXScreenData &scrn) {
       << "D3D create_texture ( ) unknown texture format\n";
   }
 
-  int data_size;
 
-  data_size = target_width * target_height * target_depth;
+  int num_views = tex->get_num_views();
+
+  size_t data_size = target_width * target_height * target_depth;
   if (_has_mipmaps) {
-    data_size = (int) ((PN_stdfloat) data_size * 1.3333333);
+    data_size += data_size / 3;
   }
-  data_size = (int) ((PN_stdfloat) data_size * bytes_per_texel);
-  if (tex->get_texture_type() == Texture::TT_cube_map) {
+  data_size *= bytes_per_texel;
+  if (texture_type == Texture::TT_cube_map) {
     data_size *= 6;
   }
-  update_data_size_bytes(data_size);
+  update_data_size_bytes((size_t)data_size * (size_t)num_views);
 
   int attempts;
 
@@ -991,14 +989,14 @@ create_texture(DXScreenData &scrn) {
       << " reported available.\n";
     dxgsg9_cat.debug()
       << " size is " << target_width << " w * " << target_height << " h * "
-      << target_depth << " d";
+      << target_depth << " d * " << num_views << " v";
     if (_has_mipmaps) {
       dxgsg9_cat.debug(false)
         << " * 1.3333333 mipmaps";
     }
     dxgsg9_cat.debug(false)
       << " * " << bytes_per_texel << " bpt";
-    if (tex->get_texture_type() == Texture::TT_cube_map) {
+    if (texture_type == Texture::TT_cube_map) {
       dxgsg9_cat.debug(false)
         << " * 6 faces";
     }
@@ -1006,45 +1004,55 @@ create_texture(DXScreenData &scrn) {
       << "\n";
   }
 
-  attempts = 0;
-  do
-  {
-    switch (tex->get_texture_type()) {
-    case Texture::TT_1d_texture:
-    case Texture::TT_2d_texture:
-      hr = scrn._d3d_device->CreateTexture
-        (target_width, target_height, mip_level_count, usage,
-         target_pixel_format, pool, &_d3d_2d_texture, nullptr);
-      _d3d_texture = _d3d_2d_texture;
-      break;
+  for (int view = 0; view < num_views; ++view) {
+    IDirect3DBaseTexture9 *d3d_texture = nullptr;
+    IDirect3DTexture9 *d3d_2d_texture;
+    IDirect3DVolumeTexture9 *d3d_volume_texture;
+    IDirect3DCubeTexture9 *d3d_cube_texture;
+
+    attempts = 0;
+    do {
+      switch (texture_type) {
+      case Texture::TT_1d_texture:
+      case Texture::TT_2d_texture:
+        hr = scrn._d3d_device->CreateTexture
+          (target_width, target_height, mip_level_count, usage,
+           target_pixel_format, pool, &d3d_2d_texture, nullptr);
+        d3d_texture = d3d_2d_texture;
+        break;
 
-    case Texture::TT_3d_texture:
-      hr = scrn._d3d_device->CreateVolumeTexture
-        (target_width, target_height, target_depth, mip_level_count, usage,
-         target_pixel_format, pool, &_d3d_volume_texture, nullptr);
-      _d3d_texture = _d3d_volume_texture;
-      break;
+      case Texture::TT_3d_texture:
+        hr = scrn._d3d_device->CreateVolumeTexture
+          (target_width, target_height, target_depth, mip_level_count, usage,
+           target_pixel_format, pool, &d3d_volume_texture, nullptr);
+        d3d_texture = d3d_volume_texture;
+        break;
 
-    case Texture::TT_cube_map:
-      hr = scrn._d3d_device->CreateCubeTexture
-        (target_width, mip_level_count, usage,
-         target_pixel_format, pool, &_d3d_cube_texture, nullptr);
-      _d3d_texture = _d3d_cube_texture;
+      case Texture::TT_cube_map:
+        hr = scrn._d3d_device->CreateCubeTexture
+          (target_width, mip_level_count, usage,
+           target_pixel_format, pool, &d3d_cube_texture, nullptr);
+        d3d_texture = d3d_cube_texture;
 
-      target_height = target_width;
-      break;
+        target_height = target_width;
+        break;
+      }
+
+      attempts++;
     }
+    while (scrn._dxgsg9->check_dx_allocation(hr, data_size, attempts));
 
-    attempts++;
-  } while (scrn._dxgsg9 -> check_dx_allocation (hr, data_size, attempts));
+    if (FAILED(hr)) {
+      dxgsg9_cat.error()
+        << "D3D create_texture failed!" << D3DERRORSTRING(hr);
+      dxgsg9_cat.error()
+        << "  width = " << target_width << " height = " << target_height
+        << " target_pixel_format = " << target_pixel_format << "\n";
 
-  if (FAILED(hr)) {
-    dxgsg9_cat.error()
-      << "D3D create_texture failed!" << D3DERRORSTRING(hr);
-    dxgsg9_cat.error()
-      << "  width = " << target_width << " height = " << target_height << " target_pixel_format = " << target_pixel_format << "\n";
+      goto error_exit;
+    }
 
-    goto error_exit;
+    _d3d_textures.push_back(d3d_texture);
   }
 
   if (DEBUG_TEXTURES && dxgsg9_cat.is_debug()) {
@@ -1088,12 +1096,11 @@ create_texture(DXScreenData &scrn) {
 
   return true;
 
- error_exit:
-
-  RELEASE(_d3d_texture, dxgsg9, "texture", RELEASE_ONCE);
-  _d3d_2d_texture = nullptr;
-  _d3d_volume_texture = nullptr;
-  _d3d_cube_texture = nullptr;
+error_exit:
+  for (IDirect3DBaseTexture9 *d3d_texture : _d3d_textures) {
+    RELEASE(d3d_texture, dxgsg9, "texture", RELEASE_ONCE);
+  }
+  _d3d_textures.clear();
   return false;
 }
 
@@ -1121,18 +1128,20 @@ create_simple_texture(DXScreenData &scrn) {
 
   int data_size = target_width * target_height * 4;
 
+  IDirect3DTexture9 *d3d_2d_texture = nullptr;
   hr = scrn._d3d_device->CreateTexture
     (target_width, target_height, mip_level_count, usage,
-     target_pixel_format, pool, &_d3d_2d_texture, nullptr);
-  _d3d_texture = _d3d_2d_texture;
+     target_pixel_format, pool, &d3d_2d_texture, nullptr);
   if (FAILED(hr)) {
     dxgsg9_cat.error()
       << "D3D create_simple_texture failed!" << D3DERRORSTRING(hr);
     dxgsg9_cat.error()
-      << "  width = " << target_width << " height = " << target_height << " target_pixel_format = " << target_pixel_format << "\n";
+      << "  width = " << target_width << " height = " << target_height
+      << " target_pixel_format = " << target_pixel_format << "\n";
 
     goto error_exit;
   }
+  _d3d_textures.push_back(d3d_2d_texture);
 
   if (DEBUG_TEXTURES && dxgsg9_cat.is_debug()) {
     dxgsg9_cat.debug()
@@ -1144,10 +1153,10 @@ create_simple_texture(DXScreenData &scrn) {
     CPTA_uchar image = get_texture()->get_simple_ram_image();
 
     hr = -1;
-    // hr = fill_d3d_texture_pixels(scrn);
+    // hr = fill_d3d_texture_pixels(view, scrn);
 
     IDirect3DSurface9 *surface = nullptr;
-    _d3d_2d_texture->GetSurfaceLevel(0, &surface);
+    d3d_2d_texture->GetSurfaceLevel(0, &surface);
 
     RECT source_size;
     source_size.left = source_size.top = 0;
@@ -1176,11 +1185,11 @@ create_simple_texture(DXScreenData &scrn) {
   mark_loaded();
   return true;
 
- error_exit:
-  RELEASE(_d3d_texture, dxgsg9, "texture", RELEASE_ONCE);
-  _d3d_2d_texture = nullptr;
-  _d3d_volume_texture = nullptr;
-  _d3d_cube_texture = nullptr;
+error_exit:
+  for (IDirect3DBaseTexture9 *d3d_texture : _d3d_textures) {
+    RELEASE(d3d_texture, dxgsg9, "texture", RELEASE_ONCE);
+  }
+  _d3d_textures.clear();
   return false;
 }
 
@@ -1189,16 +1198,15 @@ create_simple_texture(DXScreenData &scrn) {
  */
 void DXTextureContext9::
 delete_texture() {
-
-  if (_d3d_texture == nullptr) {
+  if (_d3d_textures.empty()) {
     // don't bother printing the msg below, since we already released it.
     return;
   }
 
-  RELEASE(_d3d_texture, dxgsg9, "texture", RELEASE_ONCE);
-  _d3d_2d_texture = nullptr;
-  _d3d_volume_texture = nullptr;
-  _d3d_cube_texture = nullptr;
+  for (IDirect3DBaseTexture9 *d3d_texture : _d3d_textures) {
+    RELEASE(d3d_texture, dxgsg9, "texture", RELEASE_ONCE);
+  }
+  _d3d_textures.clear();
 }
 
 /**
@@ -1219,10 +1227,13 @@ extract_texture_data(DXScreenData &screen) {
       << "\n";
     return state;
   }
-  nassertr(IS_VALID_PTR(_d3d_2d_texture), false);
+  nassertr(!_d3d_textures.empty(), false);
+
+  //FIXME: support for multiview textures
+  IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(0);
 
   D3DSURFACE_DESC desc;
-  hr = _d3d_2d_texture->GetLevelDesc(0, &desc);
+  hr = d3d_2d_texture->GetLevelDesc(0, &desc);
   if (FAILED(hr)) {
     dxgsg9_cat.error()
       << "Texture::GetLevelDesc() failed!" << D3DERRORSTRING(hr);
@@ -1299,11 +1310,12 @@ extract_texture_data(DXScreenData &screen) {
     return state;
   }
 
-  int num_levels = _d3d_2d_texture->GetLevelCount();
+  int num_levels = d3d_2d_texture->GetLevelCount();
 
   tex->set_x_size(desc.Width);
   tex->set_y_size(desc.Height);
   tex->set_z_size(1);
+  tex->set_num_views(1);
   tex->set_component_type(Texture::T_unsigned_byte);
   tex->set_format(format);
   tex->clear_ram_image();
@@ -1315,7 +1327,7 @@ extract_texture_data(DXScreenData &screen) {
     source_surface = 0;
     destination_surface = 0;
 
-    hr = _d3d_2d_texture -> GetSurfaceLevel (0, &source_surface);
+    hr = d3d_2d_texture->GetSurfaceLevel(0, &source_surface);
     if (hr == D3D_OK) {
 
       D3DPOOL pool;
@@ -1389,7 +1401,7 @@ extract_texture_data(DXScreenData &screen) {
   else {
     for (int n = 0; n < num_levels; ++n) {
       D3DLOCKED_RECT rect;
-      hr = _d3d_2d_texture->LockRect(n, &rect, nullptr, D3DLOCK_READONLY);
+      hr = d3d_2d_texture->LockRect(n, &rect, nullptr, D3DLOCK_READONLY);
       if (FAILED(hr)) {
         dxgsg9_cat.error()
           << "Texture::LockRect() failed!  level = " << n << " " << D3DERRORSTRING(hr);
@@ -1428,7 +1440,7 @@ extract_texture_data(DXScreenData &screen) {
         memcpy(image.p(), rect.pBits, size);
       }
 
-      _d3d_2d_texture->UnlockRect(n);
+      d3d_2d_texture->UnlockRect(n);
       if (n == 0) {
         tex->set_ram_image(image, compression);
       } else {
@@ -1741,62 +1753,49 @@ static UINT calculate_row_byte_length (int width, int num_color_channels, D3DFOR
  * with texture data.  Takes care of all necessary conversions and error
  * handling.
  */
-HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int depth_index, D3DFORMAT source_format)
-{
+HRESULT DXTextureContext9::
+fill_d3d_texture_mipmap_pixels(int mip_level, int depth_index, D3DFORMAT source_format) {
   // This whole function was refactored out of fill_d3d_texture_pixels to make
   // the code more readable and to avoid code duplication.
-  IDirect3DSurface9 *mip_surface = nullptr;
-  bool using_temp_buffer = false;
   HRESULT hr = E_FAIL;
   CPTA_uchar image = get_texture()->get_ram_mipmap_image(mip_level);
-  BYTE *pixels = (BYTE*) image.p();
-  DWORD width  = (DWORD) get_texture()->get_expected_mipmap_x_size(mip_level);
-  DWORD height = (DWORD) get_texture()->get_expected_mipmap_y_size(mip_level);
+  DWORD width  = (DWORD)get_texture()->get_expected_mipmap_x_size(mip_level);
+  DWORD height = (DWORD)get_texture()->get_expected_mipmap_y_size(mip_level);
+  DWORD num_color_channels = get_texture()->get_num_components();
   int component_width = get_texture()->get_component_width();
 
   size_t page_size = get_texture()->get_expected_ram_mipmap_page_size(mip_level);
   size_t view_size;
+  size_t view_stride;
   vector_uchar clear_data;
+  BYTE *image_pixels;
   if (page_size > 0) {
     if (image.is_null()) {
       // Make an image, filled with the texture's clear color.
       image = get_texture()->make_ram_mipmap_image(mip_level);
       nassertr(!image.is_null(), E_FAIL);
-      pixels = (BYTE *)image.p();
     }
-    view_size = image.size();
-    pixels += view_size * get_view();
-    pixels += page_size * depth_index;
+    image_pixels = (BYTE *)image.p();
+    view_size = get_texture()->get_ram_mipmap_view_size(mip_level);
+    view_stride = view_size;
+    image_pixels += page_size * depth_index;
   } else {
     // This is a 0x0 texture, which gets loaded as though it were 1x1.
     width = 1;
     height = 1;
     clear_data = get_texture()->get_clear_data();
-    pixels = clear_data.data();
+    image_pixels = clear_data.data();
     view_size = clear_data.size();
+    view_stride = 0;
   }
-
-  if (get_texture()->get_texture_type() == Texture::TT_cube_map) {
-    nassertr(IS_VALID_PTR(_d3d_cube_texture), E_FAIL);
-    hr = _d3d_cube_texture->GetCubeMapSurface((D3DCUBEMAP_FACES)depth_index, mip_level, &mip_surface);
-  } else {
-    nassertr(IS_VALID_PTR(_d3d_2d_texture), E_FAIL);
-    hr = _d3d_2d_texture->GetSurfaceLevel(mip_level, &mip_surface);
-  }
-
-  if (FAILED(hr)) {
-    dxgsg9_cat.error()
-      << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name()
-      << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr);
-    return E_FAIL;
-  }
+  nassertr(IS_VALID_PTR(image_pixels), E_FAIL);
 
   RECT source_size;
   source_size.left = source_size.top = 0;
   source_size.right = width;
   source_size.bottom = height;
 
-  UINT source_row_byte_length = calculate_row_byte_length(width, get_texture()->get_num_components(), source_format);
+  UINT source_row_byte_length = calculate_row_byte_length(width, num_color_channels, source_format);
 
   DWORD mip_filter;
   // need filtering if size changes, (also if bitdepth reduced (need
@@ -1807,87 +1806,128 @@ HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int dep
     mip_filter |= D3DX_FILTER_SRGB;
   }
 
-  // D3DXLoadSurfaceFromMemory will load black luminance and we want full
-  // white, so convert to explicit luminance-alpha format
-  if (_d3d_format == D3DFMT_A8) {
-    // alloc buffer for explicit D3DFMT_A8L8
-    USHORT *temp_buffer = new USHORT[width * height];
-    if (!IS_VALID_PTR(temp_buffer)) {
-      dxgsg9_cat.error()
-        << "FillDDTextureMipmapPixels couldnt alloc mem for temp pixbuf!\n";
-      goto exit_FillMipmapSurf;
-    }
-    using_temp_buffer = true;
-
-    USHORT *out_pixels = temp_buffer;
-    BYTE *source_pixels = pixels + component_width - 1;
-    for (UINT y = 0; y < height; y++) {
-      for (UINT x = 0; x < width; x++, source_pixels += component_width, out_pixels++) {
-        // add full white, which is our interpretation of alpha-only (similar
-        // to default adding full opaque alpha 0xFF to RGB-only textures)
-        *out_pixels = ((*source_pixels) << 8 ) | 0xFF;
-      }
+  // Preallocate temporary buffer for conversion
+  BYTE *temp_buffer = nullptr;
+  if (source_format == D3DFMT_A8 || component_width != 1) {
+    int num_pixels = width * height;
+    if (source_format == D3DFMT_A8) {
+      num_pixels *= 2;
+      source_format = D3DFMT_A8L8;
+      source_row_byte_length *= 2;
+    } else {
+      num_pixels *= num_color_channels;
     }
-
-    source_format = D3DFMT_A8L8;
-    source_row_byte_length = width * sizeof(USHORT);
-    pixels = (BYTE*)temp_buffer;
-  }
-  else if (component_width != 1) {
-    // Convert from 16-bit per channel (or larger) format down to 8-bit per
-    // channel.  This throws away precision in the original image, but dx8
-    // doesn't support high-precision images anyway.
-
-    int num_components = get_texture()->get_num_components();
-    int num_pixels = width * height * num_components;
-    BYTE *temp_buffer = new BYTE[num_pixels];
+    temp_buffer = new BYTE[num_pixels];
     if (!IS_VALID_PTR(temp_buffer)) {
-      dxgsg9_cat.error() << "FillDDTextureMipmapPixels couldnt alloc mem for temp pixbuf!\n";
-      goto exit_FillMipmapSurf;
-    }
-    using_temp_buffer = true;
-
-    BYTE *source_pixels = pixels + component_width - 1;
-    for (int i = 0; i < num_pixels; i++) {
-      temp_buffer[i] = *source_pixels;
-      source_pixels += component_width;
+      dxgsg9_cat.error()
+        << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
+      return E_FAIL;
     }
-    pixels = (BYTE*)temp_buffer;
   }
 
   // filtering may be done here if texture if targetsize != origsize
+  int num_views = (int)_d3d_textures.size();
+
 #ifdef DO_PSTATS
-  GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_row_byte_length * height);
+  GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_row_byte_length * height * num_views);
 #endif
-  if (source_format == D3DFMT_ATI1 || source_format == D3DFMT_ATI2) {
-    // These formats are not supported by D3DXLoadSurfaceFromMemory.
-    D3DLOCKED_RECT rect;
-    _d3d_2d_texture->LockRect(mip_level, &rect, 0, D3DLOCK_DISCARD);
 
-    unsigned char *dest = (unsigned char *)rect.pBits;
-    memcpy(dest, pixels, view_size);
+  for (int view = 0; view < num_views; ++view) {
+    BYTE *pixels = image_pixels;
+    image_pixels += view_stride;
 
-    _d3d_2d_texture->UnlockRect(mip_level);
+    if (source_format == D3DFMT_ATI1 || source_format == D3DFMT_ATI2) {
+      // These formats are not supported by D3DXLoadSurfaceFromMemory.
+      D3DLOCKED_RECT rect;
 
-  } else {
-    hr = D3DXLoadSurfaceFromMemory
-      (mip_surface, nullptr, nullptr, (LPCVOID)pixels,
-        source_format, source_row_byte_length, nullptr,
-        &source_size, mip_filter, (D3DCOLOR)0x0);
-    if (FAILED(hr)) {
-      dxgsg9_cat.error()
-        << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name()
-        << ", mip_level " << mip_level
-        << ", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr);
+      if (get_texture()->get_texture_type() == Texture::TT_cube_map) {
+        IDirect3DCubeTexture9 *d3d_cube_texture = get_d3d_cube_texture(view);
+        nassertr(IS_VALID_PTR(d3d_cube_texture), E_FAIL);
+        d3d_cube_texture->LockRect((D3DCUBEMAP_FACES)depth_index, mip_level, &rect, 0, D3DLOCK_DISCARD);
+
+        unsigned char *dest = (unsigned char *)rect.pBits;
+        memcpy(dest, pixels, view_size);
+
+        d3d_cube_texture->UnlockRect((D3DCUBEMAP_FACES)depth_index, mip_level);
+      } else {
+        IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(view);
+        nassertr(IS_VALID_PTR(d3d_2d_texture), E_FAIL);
+        d3d_2d_texture->LockRect(mip_level, &rect, 0, D3DLOCK_DISCARD);
+
+        unsigned char *dest = (unsigned char *)rect.pBits;
+        memcpy(dest, pixels, view_size);
+
+        d3d_2d_texture->UnlockRect(mip_level);
+      }
+    }
+    else {
+      if (_d3d_format == D3DFMT_A8 && source_format == D3DFMT_A8L8) {
+        USHORT *out_pixels = (USHORT *)temp_buffer;
+        BYTE *source_pixels = pixels + component_width - 1;
+        for (UINT y = 0; y < height; y++) {
+          for (UINT x = 0; x < width; x++, source_pixels += component_width, out_pixels++) {
+            // add full white, which is our interpretation of alpha-only
+            // (similar to default adding full opaque alpha 0xFF to RGB-only
+            // textures)
+            *out_pixels = ((*source_pixels) << 8) | 0xFF;
+          }
+        }
+        pixels = (BYTE *)temp_buffer;
+      }
+      else if (component_width != 1) {
+        // Convert from 16-bit per channel (or larger) format down to 8-bit per
+        // channel.  This throws away precision in the original image, but dx8
+        // doesn't support high-precision images anyway.
+
+        int num_components = get_texture()->get_num_components();
+        int num_pixels = width * height * num_components;
+
+        BYTE *source_pixels = pixels + component_width - 1;
+        for (int i = 0; i < num_pixels; i++) {
+          temp_buffer[i] = *source_pixels;
+          source_pixels += component_width;
+        }
+        pixels = (BYTE *)temp_buffer;
+      }
+
+      IDirect3DSurface9 *mip_surface = nullptr;
+      if (get_texture()->get_texture_type() == Texture::TT_cube_map) {
+        IDirect3DCubeTexture9 *d3d_cube_texture = get_d3d_cube_texture(view);
+        nassertr(IS_VALID_PTR(d3d_cube_texture), E_FAIL);
+        hr = d3d_cube_texture->GetCubeMapSurface((D3DCUBEMAP_FACES)depth_index, mip_level, &mip_surface);
+      } else {
+        IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(view);
+        nassertr(IS_VALID_PTR(d3d_2d_texture), E_FAIL);
+        hr = d3d_2d_texture->GetSurfaceLevel(mip_level, &mip_surface);
+      }
+
+      if (FAILED(hr)) {
+        dxgsg9_cat.error()
+          << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name()
+          << ", view " << view << ", mip_level " << mip_level
+          << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr);
+        return E_FAIL;
+      }
+
+      hr = D3DXLoadSurfaceFromMemory
+        (mip_surface, nullptr, nullptr, (LPCVOID)pixels,
+          source_format, source_row_byte_length, nullptr,
+          &source_size, mip_filter, (D3DCOLOR)0x0);
+      if (FAILED(hr)) {
+        dxgsg9_cat.error()
+          << "FillDDTextureMipmapPixels failed for " << get_texture()->get_name()
+          << ", view " << view << ", mip_level " << mip_level
+          << ", D3DXLoadSurfFromMem failed" << D3DERRORSTRING(hr);
+      }
+
+      RELEASE(mip_surface, dxgsg9, "FillDDTextureMipmapPixels MipSurface texture ptr", RELEASE_ONCE);
     }
   }
 
-exit_FillMipmapSurf:
-  if (using_temp_buffer) {
-    SAFE_DELETE_ARRAY(pixels);
+  if (temp_buffer != nullptr) {
+    delete[] temp_buffer;
   }
 
-  RELEASE(mip_surface, dxgsg9, "FillDDTextureMipmapPixels MipSurface texture ptr", RELEASE_ONCE);
   return hr;
 }
 
@@ -1899,7 +1939,8 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
   IDirect3DDevice9 *device = scrn._d3d_device;
   Texture *tex = get_texture();
   nassertr(IS_VALID_PTR(tex), E_FAIL);
-  if (tex->get_texture_type() == Texture::TT_3d_texture) {
+  Texture::TextureType texture_type = tex->get_texture_type();
+  if (texture_type == Texture::TT_3d_texture) {
     return fill_d3d_volume_texture_pixels(scrn);
   }
 
@@ -1924,14 +1965,15 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
   if (image.is_null()) {
     // The texture doesn't have an image to load.  That's ok; it might be a
     // texture we've rendered to by frame buffer operations or something.
-    if (tex->get_render_to_texture()) {
+    if (tex->get_render_to_texture() && texture_type != Texture::TT_cube_map) {
       HRESULT result;
 
-      if (_d3d_2d_texture) {
+      for (int view = 0; view < (int)_d3d_textures.size(); ++view) {
+        IDirect3DTexture9 *d3d_2d_texture = get_d3d_2d_texture(view);
+
         // clear render to texture
         IDirect3DSurface9 *surface;
-
-        result = _d3d_2d_texture -> GetSurfaceLevel (0, &surface);
+        result = d3d_2d_texture->GetSurfaceLevel(0, &surface);
         if (result == D3D_OK) {
           D3DSURFACE_DESC surface_description;
 
@@ -1981,7 +2023,6 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
     }
   }
   //nassertr(IS_VALID_PTR((BYTE*)image.p()), E_FAIL);
-  nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL);
 
   PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector);
 
@@ -2017,6 +2058,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
     break;
   }
 
+  int num_views = (int)_d3d_textures.size();
   for (unsigned int di = 0; di < orig_depth; di++) {
 
     // fill top level mipmap
@@ -2028,7 +2070,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
     if (_has_mipmaps) {
       // if we have pre-calculated mipmap levels, use them, otherwise generate
       // on the fly
-      int miplevel_count = _d3d_texture->GetLevelCount();
+      int miplevel_count = _d3d_textures[0]->GetLevelCount();
       if (miplevel_count <= tex->get_num_loadable_ram_mipmap_images()) {
         if (dxgsg9_cat.is_debug()) {
           dxgsg9_cat.debug()
@@ -2041,45 +2083,51 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
             return hr; // error message was already output in fill_d3d_texture_mipmap_pixels
           }
         }
+
+        return D3D_OK;
       }
       else {
         // mipmaps need to be generated, either use autogen or d3dx functions
+        for (int view = 0; view < num_views; ++view) {
+          IDirect3DBaseTexture9 *d3d_texture = get_d3d_texture(view);
+          nassertr(IS_VALID_PTR(d3d_texture), E_FAIL);
+
+          if (_managed == false && scrn._supports_automatic_mipmap_generation) {
+            if (false) {
+              IDirect3DBaseTexture9 *d3d_texture = get_d3d_texture(view);
+              nassertr(IS_VALID_PTR(d3d_texture), E_FAIL);
+              //hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_PYRAMIDALQUAD);
+              //hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_GAUSSIANQUAD);
+              //hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_ANISOTROPIC);
+              hr = d3d_texture->SetAutoGenFilterType(D3DTEXF_LINEAR);
+              if (FAILED(hr)) {
+                dxgsg9_cat.error() << "SetAutoGenFilterType failed " << D3DERRORSTRING(hr);
+              }
 
-        if (_managed == false && scrn._supports_automatic_mipmap_generation) {
-          if (false)
-          {
-            // hr = _d3d_texture -> SetAutoGenFilterType
-            // (D3DTEXF_PYRAMIDALQUAD); hr = _d3d_texture ->
-            // SetAutoGenFilterType (D3DTEXF_GAUSSIANQUAD); hr = _d3d_texture
-            // -> SetAutoGenFilterType (D3DTEXF_ANISOTROPIC);
-            hr = _d3d_texture -> SetAutoGenFilterType (D3DTEXF_LINEAR);
-            if (FAILED(hr)) {
-              dxgsg9_cat.error() << "SetAutoGenFilterType failed " << D3DERRORSTRING(hr);
+              d3d_texture->GenerateMipSubLevels();
             }
-
-            _d3d_texture -> GenerateMipSubLevels ( );
-          }
-        }
-        else {
-          DWORD mip_filter_flags;
-          if (!dx_use_triangle_mipgen_filter) {
-            mip_filter_flags = D3DX_FILTER_BOX;
-          } else {
-            mip_filter_flags = D3DX_FILTER_TRIANGLE;
           }
+          else {
+            DWORD mip_filter_flags;
+            if (!dx_use_triangle_mipgen_filter) {
+              mip_filter_flags = D3DX_FILTER_BOX;
+            } else {
+              mip_filter_flags = D3DX_FILTER_TRIANGLE;
+            }
 
-          if (Texture::is_srgb(tex->get_format())) {
-            mip_filter_flags |= D3DX_FILTER_SRGB;
-          }
+            if (Texture::is_srgb(tex->get_format())) {
+              mip_filter_flags |= D3DX_FILTER_SRGB;
+            }
 
-          // mip_filter_flags |= D3DX_FILTER_DITHER;
-          hr = D3DXFilterTexture(_d3d_texture, nullptr, 0,
-                                mip_filter_flags);
+            // mip_filter_flags |= D3DX_FILTER_DITHER;
+            hr = D3DXFilterTexture(d3d_texture, nullptr, 0, mip_filter_flags);
 
-          if (FAILED(hr)) {
-            dxgsg9_cat.error()
-              << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
-              << ", D3DXFilterTex failed" << D3DERRORSTRING(hr);
+            if (FAILED(hr)) {
+              dxgsg9_cat.error()
+                << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
+                << " view " << view << ", D3DXFilterTex failed"
+                << D3DERRORSTRING(hr);
+            }
           }
         }
       }
@@ -2126,158 +2174,166 @@ fill_d3d_volume_texture_pixels(DXScreenData &scrn) {
 
   PStatTimer timer(GraphicsStateGuardian::_load_texture_pcollector);
 
-  nassertr(IS_VALID_PTR(_d3d_texture), E_FAIL);
+  nassertr(!_d3d_textures.empty(), E_FAIL);
   nassertr(tex->get_texture_type() == Texture::TT_3d_texture, E_FAIL);
 
-  DWORD orig_width  = (DWORD) tex->get_x_size();
-  DWORD orig_height = (DWORD) tex->get_y_size();
-  DWORD orig_depth = (DWORD) tex->get_z_size();
+  DWORD orig_width  = (DWORD)tex->get_x_size();
+  DWORD orig_height = (DWORD)tex->get_y_size();
+  DWORD orig_depth = (DWORD)tex->get_z_size();
   DWORD num_color_channels = tex->get_num_components();
-  D3DFORMAT source_format = _d3d_format;
-  BYTE *image_pixels = (BYTE*)image.p();
   int component_width = tex->get_component_width();
 
+  D3DFORMAT source_format = _d3d_format;
+  UINT source_row_byte_length = orig_width * num_color_channels;
+  UINT source_page_byte_length = orig_height * source_row_byte_length;
+
+  // Preallocate temporary buffer for conversion
+  BYTE *temp_buffer = nullptr;
+  if (_d3d_format == D3DFMT_A8 || component_width != 1) {
+    int num_pixels = orig_width * orig_height * orig_depth;
+    if (_d3d_format == D3DFMT_A8) {
+      num_pixels *= 2;
+      source_format = D3DFMT_A8L8;
+      source_row_byte_length *= 2;
+      source_page_byte_length *= 2;
+    } else {
+      num_pixels *= num_color_channels;
+    }
+    temp_buffer = new BYTE[num_pixels];
+    if (!IS_VALID_PTR(temp_buffer)) {
+      dxgsg9_cat.error()
+        << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
+      return E_FAIL;
+    }
+  }
+
+  BYTE *image_pixels = (BYTE *)image.p();
   nassertr(IS_VALID_PTR(image_pixels), E_FAIL);
 
   size_t view_size = tex->get_ram_mipmap_view_size(0);
-  image_pixels += view_size * get_view();
+  int num_views = (int)_d3d_textures.size();
 
-  IDirect3DVolume9 *mip_level_0 = nullptr;
-  bool using_temp_buffer = false;
-  BYTE *pixels = image_pixels;
+#ifdef DO_PSTATS
+  GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_page_byte_length * orig_depth * num_views);
+#endif
 
-  nassertr(IS_VALID_PTR(_d3d_volume_texture), E_FAIL);
-  hr = _d3d_volume_texture->GetVolumeLevel(0, &mip_level_0);
+  bool success = true;
 
-  if (FAILED(hr)) {
-    dxgsg9_cat.error()
-      << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
-      << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr);
-    return E_FAIL;
-  }
+  for (int view = 0; view < num_views; ++view) {
+    IDirect3DVolumeTexture9 *d3d_volume_texture = get_d3d_volume_texture(view);
+    nassertr(IS_VALID_PTR(d3d_volume_texture), E_FAIL);
 
-  D3DBOX source_size;
-  source_size.Left = source_size.Top = source_size.Front = 0;
-  source_size.Right = orig_width;
-  source_size.Bottom = orig_height;
-  source_size.Back = orig_depth;
+    IDirect3DVolume9 *mip_level_0 = nullptr;
+    BYTE *pixels = image_pixels;
+    image_pixels += view_size;
 
-  UINT source_row_byte_length = orig_width * num_color_channels;
-  UINT source_page_byte_length = orig_height * source_row_byte_length;
+    hr = d3d_volume_texture->GetVolumeLevel(0, &mip_level_0);
+    if (FAILED(hr)) {
+      dxgsg9_cat.error()
+        << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
+        << ", GetSurfaceLevel failed" << D3DERRORSTRING(hr);
+      success = false;
+      continue;
+    }
 
-  DWORD level_0_filter, mip_filter_flags;
-  using_temp_buffer = false;
+    D3DBOX source_size;
+    source_size.Left = source_size.Top = source_size.Front = 0;
+    source_size.Right = orig_width;
+    source_size.Bottom = orig_height;
+    source_size.Back = orig_depth;
 
-  // need filtering if size changes, (also if bitdepth reduced (need
-  // dithering)??)
-  level_0_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER;  //dithering looks ugly on i810 for 4444 textures
+    DWORD level_0_filter, mip_filter_flags;
 
-  if (Texture::is_srgb(tex->get_format())) {
-    level_0_filter |= D3DX_FILTER_SRGB;
-  }
+    // need filtering if size changes, (also if bitdepth reduced (need
+    // dithering)??)
+    level_0_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER;  //dithering looks ugly on i810 for 4444 textures
 
-  // D3DXLoadSurfaceFromMemory will load black luminance and we want full
-  // white, so convert to explicit luminance-alpha format
-  if (_d3d_format == D3DFMT_A8) {
-    // alloc buffer for explicit D3DFMT_A8L8
-    USHORT *temp_buffer = new USHORT[orig_width * orig_height * orig_depth];
-    if (!IS_VALID_PTR(temp_buffer)) {
-      dxgsg9_cat.error()
-        << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
-      goto exit_FillDDSurf;
-    }
-    using_temp_buffer = true;
-
-    USHORT *out_pixels = temp_buffer;
-    BYTE *source_pixels = pixels + component_width - 1;
-    for (UINT z = 0; z < orig_depth; z++) {
-      for (UINT y = 0; y < orig_height; y++) {
-        for (UINT x = 0;
-             x < orig_width;
-             x++, source_pixels += component_width, out_pixels++) {
-          // add full white, which is our interpretation of alpha-only
-          // (similar to default adding full opaque alpha 0xFF to RGB-only
-          // textures)
-          *out_pixels = ((*source_pixels) << 8 ) | 0xFF;
+    if (Texture::is_srgb(tex->get_format())) {
+      level_0_filter |= D3DX_FILTER_SRGB;
+    }
+
+    // D3DXLoadSurfaceFromMemory will load black luminance and we want full
+    // white, so convert to explicit luminance-alpha format
+    if (_d3d_format == D3DFMT_A8 && source_format == D3DFMT_A8L8) {
+      USHORT *out_pixels = (USHORT *)temp_buffer;
+      BYTE *source_pixels = pixels + component_width - 1;
+      for (UINT z = 0; z < orig_depth; z++) {
+        for (UINT y = 0; y < orig_height; y++) {
+          for (UINT x = 0;
+               x < orig_width;
+               x++, source_pixels += component_width, out_pixels++) {
+            // add full white, which is our interpretation of alpha-only
+            // (similar to default adding full opaque alpha 0xFF to RGB-only
+            // textures)
+            *out_pixels = ((*source_pixels) << 8) | 0xFF;
+          }
         }
       }
-    }
 
-    source_format = D3DFMT_A8L8;
-    source_row_byte_length = orig_width * sizeof(USHORT);
-    source_page_byte_length = orig_height * source_row_byte_length;
-    pixels = (BYTE*)temp_buffer;
+      pixels = (BYTE *)temp_buffer;
+    }
+    else if (component_width != 1) {
+      // Convert from 16-bit per channel (or larger) format down to 8-bit per
+      // channel.  This throws away precision in the original image, but dx8
+      // doesn't support high-precision images anyway.
 
-  } else if (component_width != 1) {
-    // Convert from 16-bit per channel (or larger) format down to 8-bit per
-    // channel.  This throws away precision in the original image, but dx8
-    // doesn't support high-precision images anyway.
+      int num_components = tex->get_num_components();
+      int num_pixels = orig_width * orig_height * orig_depth * num_components;
 
-    int num_components = tex->get_num_components();
-    int num_pixels = orig_width * orig_height * orig_depth * num_components;
-    BYTE *temp_buffer = new BYTE[num_pixels];
-    if (!IS_VALID_PTR(temp_buffer)) {
-      dxgsg9_cat.error() << "FillDDSurfaceTexturePixels couldnt alloc mem for temp pixbuf!\n";
-      goto exit_FillDDSurf;
+      BYTE *source_pixels = pixels + component_width - 1;
+      for (int i = 0; i < num_pixels; i++) {
+        temp_buffer[i] = *source_pixels;
+        source_pixels += component_width;
+      }
+      pixels = (BYTE *)temp_buffer;
     }
-    using_temp_buffer = true;
 
-    BYTE *source_pixels = pixels + component_width - 1;
-    for (int i = 0; i < num_pixels; i++) {
-      temp_buffer[i] = *source_pixels;
-      source_pixels += component_width;
+    // filtering may be done here if texture if targetsize != origsize
+    hr = D3DXLoadVolumeFromMemory
+      (mip_level_0, nullptr, nullptr, (LPCVOID)pixels,
+       source_format, source_row_byte_length, source_page_byte_length,
+       nullptr,
+       &source_size, level_0_filter, (D3DCOLOR)0x0);
+    if (FAILED(hr)) {
+      dxgsg9_cat.error()
+        << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
+        << " view " << view << ", D3DXLoadVolumeFromMem failed" << D3DERRORSTRING(hr);
+      success = false;
+      continue;
     }
-    pixels = (BYTE*)temp_buffer;
-  }
 
+    if (_has_mipmaps) {
+      if (!dx_use_triangle_mipgen_filter) {
+        mip_filter_flags = D3DX_FILTER_BOX;
+      } else {
+        mip_filter_flags = D3DX_FILTER_TRIANGLE;
+      }
 
-  // filtering may be done here if texture if targetsize != origsize
-#ifdef DO_PSTATS
-  GraphicsStateGuardian::_data_transferred_pcollector.add_level(source_page_byte_length * orig_depth);
-#endif
-  hr = D3DXLoadVolumeFromMemory
-    (mip_level_0, nullptr, nullptr, (LPCVOID)pixels,
-     source_format, source_row_byte_length, source_page_byte_length,
-     nullptr,
-     &source_size, level_0_filter, (D3DCOLOR)0x0);
-  if (FAILED(hr)) {
-    dxgsg9_cat.error()
-      << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
-      << ", D3DXLoadVolumeFromMem failed" << D3DERRORSTRING(hr);
-    goto exit_FillDDSurf;
-  }
+      if (Texture::is_srgb(tex->get_format())) {
+        mip_filter_flags |= D3DX_FILTER_SRGB;
+      }
 
-  if (_has_mipmaps) {
-    if (!dx_use_triangle_mipgen_filter) {
-      mip_filter_flags = D3DX_FILTER_BOX;
-    } else {
-      mip_filter_flags = D3DX_FILTER_TRIANGLE;
-    }
+      // mip_filter_flags| = D3DX_FILTER_DITHER;
 
-    if (Texture::is_srgb(tex->get_format())) {
-      mip_filter_flags |= D3DX_FILTER_SRGB;
+      hr = D3DXFilterTexture(d3d_volume_texture, nullptr, 0, mip_filter_flags);
+      if (FAILED(hr)) {
+        dxgsg9_cat.error()
+          << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
+          << " view " << view << ", D3DXFilterTex failed" << D3DERRORSTRING(hr);
+        success = false;
+        continue;
+      }
     }
 
-    // mip_filter_flags| = D3DX_FILTER_DITHER;
-
-    hr = D3DXFilterTexture(_d3d_texture, nullptr, 0,
-                           mip_filter_flags);
-    if (FAILED(hr)) {
-      dxgsg9_cat.error()
-        << "FillDDSurfaceTexturePixels failed for " << tex->get_name()
-        << ", D3DXFilterTex failed" << D3DERRORSTRING(hr);
-      goto exit_FillDDSurf;
-    }
+    RELEASE(mip_level_0, dxgsg9, "FillDDSurf MipLev0 texture ptr", RELEASE_ONCE);
   }
 
- exit_FillDDSurf:
-  if (using_temp_buffer) {
-    SAFE_DELETE_ARRAY(pixels);
+  if (temp_buffer != nullptr) {
+    delete[] temp_buffer;
   }
-  RELEASE(mip_level_0, dxgsg9, "FillDDSurf MipLev0 texture ptr", RELEASE_ONCE);
-  return hr;
-}
 
+  return success ? D3D_OK : E_FAIL;
+}
 
 /**
  * Returns the largest power of 2 less than or equal to value.

+ 10 - 9
panda/src/dxgsg9/dxTextureContext9.h

@@ -17,13 +17,14 @@
 #include "dxgsg9base.h"
 #include "texture.h"
 #include "textureContext.h"
+#include "small_vector.h"
 
 /**
  *
  */
 class EXPCL_PANDADX DXTextureContext9 : public TextureContext {
 public:
-  DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex, int view);
+  DXTextureContext9(PreparedGraphicsObjects *pgo, Texture *tex);
   virtual ~DXTextureContext9();
 
   virtual void evict_lru();
@@ -34,10 +35,10 @@ public:
   bool extract_texture_data(DXScreenData &scrn);
 
   INLINE bool has_mipmaps() const;
-  INLINE IDirect3DBaseTexture9 *get_d3d_texture() const;
-  INLINE IDirect3DTexture9 *get_d3d_2d_texture() const;
-  INLINE IDirect3DVolumeTexture9 *get_d3d_volume_texture() const;
-  INLINE IDirect3DCubeTexture9 *get_d3d_cube_texture() const;
+  INLINE IDirect3DBaseTexture9 *get_d3d_texture(int view) const;
+  INLINE IDirect3DTexture9 *get_d3d_2d_texture(int view) const;
+  INLINE IDirect3DVolumeTexture9 *get_d3d_volume_texture(int view) const;
+  INLINE IDirect3DCubeTexture9 *get_d3d_cube_texture(int view) const;
 
   static HRESULT d3d_surface_to_texture(RECT &source_rect,
           IDirect3DSurface9 *d3d_surface,
@@ -54,10 +55,10 @@ private:
 
 private:
   D3DFORMAT _d3d_format;    // the 'D3DFORMAT' the Panda TextureBuffer fmt corresponds to
-  IDirect3DBaseTexture9 *_d3d_texture;
-  IDirect3DTexture9 *_d3d_2d_texture;
-  IDirect3DVolumeTexture9 *_d3d_volume_texture;
-  IDirect3DCubeTexture9 *_d3d_cube_texture;
+  small_vector<IDirect3DBaseTexture9 *> _d3d_textures;
+  //IDirect3DTexture9 *_d3d_2d_texture;
+  //IDirect3DVolumeTexture9 *_d3d_volume_texture;
+  //IDirect3DCubeTexture9 *_d3d_cube_texture;
 
   int _managed;
 

+ 12 - 12
panda/src/dxgsg9/wdxGraphicsBuffer9.cxx

@@ -369,7 +369,7 @@ rebuild_bitplanes() {
 // color_tex->set_format(Texture::F_rgba);
     color_ctx =
       DCAST(DXTextureContext9,
-            color_tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg));
+            color_tex->prepare_now(_gsg->get_prepared_objects(), _gsg));
 
     if (color_ctx) {
       if (!color_ctx->create_texture(*_dxgsg->_screen)) {
@@ -378,7 +378,7 @@ rebuild_bitplanes() {
         return false;
       }
       if (color_tex->get_texture_type() == Texture::TT_2d_texture) {
-        color_d3d_tex = color_ctx->_d3d_2d_texture;
+        color_d3d_tex = color_ctx->get_d3d_2d_texture(0);
         nassertr(color_d3d_tex != 0, false);
         hr = color_d3d_tex -> GetSurfaceLevel(0, &color_surf);
         if (!SUCCEEDED(hr)) {
@@ -386,7 +386,7 @@ rebuild_bitplanes() {
         }
       }
       if (color_tex->get_texture_type() == Texture::TT_cube_map) {
-        color_cube = color_ctx->_d3d_cube_texture;
+        color_cube = color_ctx->get_d3d_cube_texture(0);
         nassertr(color_cube != 0, false);
 
         if (_cube_map_index >= 0 && _cube_map_index < 6) {
@@ -448,7 +448,7 @@ rebuild_bitplanes() {
     depth_tex->set_format(Texture::F_depth_stencil);
     depth_ctx =
       DCAST(DXTextureContext9,
-            depth_tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg));
+            depth_tex->prepare_now(_gsg->get_prepared_objects(), _gsg));
     if (depth_ctx) {
       if (!depth_ctx->create_texture(*_dxgsg->_screen)) {
         dxgsg9_cat.error()
@@ -456,7 +456,7 @@ rebuild_bitplanes() {
         return false;
       }
       if (depth_tex->get_texture_type() == Texture::TT_2d_texture) {
-        depth_d3d_tex = depth_ctx->_d3d_2d_texture;
+        depth_d3d_tex = depth_ctx->get_d3d_2d_texture(0);
         nassertr(depth_d3d_tex != 0, false);
         hr = depth_d3d_tex -> GetSurfaceLevel(0, &depth_surf);
         if (!SUCCEEDED(hr)) {
@@ -464,7 +464,7 @@ rebuild_bitplanes() {
         }
       }
       if (depth_tex->get_texture_type() == Texture::TT_cube_map) {
-        depth_cube = depth_ctx->_d3d_cube_texture;
+        depth_cube = depth_ctx->get_d3d_cube_texture(0);
         nassertr(depth_cube != 0, false);
         hr = depth_cube -> GetCubeMapSurface ((D3DCUBEMAP_FACES) _cube_map_index, 0, &depth_surf);
         if (!SUCCEEDED(hr)) {
@@ -523,7 +523,7 @@ rebuild_bitplanes() {
           IDirect3DSurface9 *color_surf = 0;
           IDirect3DCubeTexture9 *color_cube = 0;
 
-          color_ctx = DCAST(DXTextureContext9, tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg));
+          color_ctx = DCAST(DXTextureContext9, tex->prepare_now(_gsg->get_prepared_objects(), _gsg));
           if (color_ctx) {
             if (!color_ctx->create_texture(*_dxgsg->_screen)) {
               dxgsg9_cat.error()
@@ -531,7 +531,7 @@ rebuild_bitplanes() {
               return false;
             }
             if (tex->get_texture_type() == Texture::TT_2d_texture) {
-              color_d3d_tex = color_ctx->_d3d_2d_texture;
+              color_d3d_tex = color_ctx->get_d3d_2d_texture(0);
               nassertr(color_d3d_tex != 0, false);
 
               hr = color_d3d_tex -> GetSurfaceLevel(0, &color_surf);
@@ -618,8 +618,8 @@ select_target_tex_page(int page) {
   if (color_tex) {
     color_ctx =
       DCAST(DXTextureContext9,
-            color_tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg));
-    color_cube = color_ctx->_d3d_cube_texture;
+            color_tex->prepare_now(_gsg->get_prepared_objects(), _gsg));
+    color_cube = color_ctx->get_d3d_cube_texture(0);
     if (color_cube && _cube_map_index >= 0 && _cube_map_index < 6) {
       hr = color_cube -> GetCubeMapSurface ((D3DCUBEMAP_FACES) _cube_map_index, 0, &color_surf);
       if (!SUCCEEDED(hr)) {
@@ -662,7 +662,7 @@ select_target_tex_page(int page) {
           IDirect3DSurface9 *color_surf = 0;
           IDirect3DCubeTexture9 *color_cube = 0;
 
-          color_ctx = DCAST(DXTextureContext9, tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg));
+          color_ctx = DCAST(DXTextureContext9, tex->prepare_now(_gsg->get_prepared_objects(), _gsg));
           if (color_ctx) {
             if (tex->get_texture_type() == Texture::TT_cube_map) {
 
@@ -670,7 +670,7 @@ select_target_tex_page(int page) {
                 printf ("CUBEMAP i = %d, RenderTexturePlane = %d, _cube_map_index %d \n", i, plane, _cube_map_index);
               }
 
-              color_cube = color_ctx->_d3d_cube_texture;
+              color_cube = color_ctx->get_d3d_cube_texture(0);
               if (color_cube && _cube_map_index >= 0 && _cube_map_index < 6) {
                 hr = color_cube -> GetCubeMapSurface ((D3DCUBEMAP_FACES) _cube_map_index, 0, &color_surf);
                 if (!SUCCEEDED(hr)) {

+ 28 - 3
panda/src/egldisplay/eglGraphicsBuffer.cxx

@@ -38,9 +38,10 @@ eglGraphicsBuffer(GraphicsEngine *engine, GraphicsPipe *pipe,
   DCAST_INTO_V(egl_pipe, _pipe);
   _pbuffer = EGL_NO_SURFACE;
 
-  // Since the pbuffer never gets flipped, we get screenshots from the same
-  // buffer we draw into.
-  _screenshot_buffer_type = _draw_buffer_type;
+  // EGL pbuffers only have a back buffer (see 2.2.2 in spec), and it is never
+  // flipped (eglSwapBuffers is a no-op).
+  _draw_buffer_type = RenderBuffer::T_back;
+  _screenshot_buffer_type = RenderBuffer::T_back;
 }
 
 /**
@@ -124,6 +125,30 @@ end_frame(FrameMode mode, Thread *current_thread) {
   }
 }
 
+/**
+ *
+ */
+void eglGraphicsBuffer::
+set_size(int x, int y) {
+  nassertv_always(_gsg != nullptr);
+
+  if (_size.get_x() != x || _size.get_y() != y) {
+    eglDestroySurface(_egl_display, _pbuffer);
+
+    int attrib_list[] = {
+      EGL_WIDTH, x,
+      EGL_HEIGHT, y,
+      EGL_NONE
+    };
+
+    eglGraphicsStateGuardian *eglgsg;
+    DCAST_INTO_V(eglgsg, _gsg);
+    _pbuffer = eglCreatePbufferSurface(eglgsg->_egl_display, eglgsg->_fbconfig, attrib_list);
+  }
+
+  set_size_and_recalc(x, y);
+}
+
 /**
  * Closes the buffer right now.  Called from the window thread.
  */

+ 2 - 0
panda/src/egldisplay/eglGraphicsBuffer.h

@@ -36,6 +36,8 @@ public:
   virtual bool begin_frame(FrameMode mode, Thread *current_thread);
   virtual void end_frame(FrameMode mode, Thread *current_thread);
 
+  virtual void set_size(int x, int y);
+
 protected:
   virtual void close_buffer();
   virtual bool open_buffer();

+ 0 - 1
panda/src/egldisplay/eglGraphicsPipe.cxx

@@ -323,7 +323,6 @@ make_output(const std::string &name,
   if (retry == 2) {
     if (((flags&BF_require_parasite)!=0)||
         ((flags&BF_require_window)!=0)||
-        ((flags&BF_resizeable)!=0)||
         ((flags&BF_size_track_host)!=0)) {
       return nullptr;
     }

+ 1 - 1
panda/src/express/nodeReferenceCount.I

@@ -213,7 +213,7 @@ NodeRefCountObj(const Base &copy) : Base(copy) {
 template<class Base>
 void NodeRefCountObj<Base>::
 init_type() {
-#if defined(HAVE_RTTI) && !defined(__EDG__)
+#if defined(HAVE_RTTI) && !defined(__EDG__) && (!defined(__GNUC__) || defined(__GXX_RTTI))
   // If we have RTTI, we can determine the name of the base type.
   std::string base_name = typeid(Base).name();
 #else

+ 1 - 1
panda/src/express/referenceCount.I

@@ -441,7 +441,7 @@ RefCountObj(const Base &copy) : Base(copy) {
 template<class Base>
 void RefCountObj<Base>::
 init_type() {
-#if defined(HAVE_RTTI) && !defined(__EDG__)
+#if defined(HAVE_RTTI) && !defined(__EDG__) && (!defined(__GNUC__) || defined(__GXX_RTTI))
   // If we have RTTI, we can determine the name of the base type.
   std::string base_name = typeid(Base).name();
 #else

+ 6 - 5
panda/src/express/virtualFileSystem.I

@@ -12,7 +12,8 @@
  */
 
 /**
- * Convenience function; returns true if the named file exists.
+ * Convenience function; returns true if the named file exists in the virtual
+ * file system hierarchy.
  */
 INLINE bool VirtualFileSystem::
 exists(const Filename &filename) const {
@@ -20,8 +21,8 @@ exists(const Filename &filename) const {
 }
 
 /**
- * Convenience function; returns true if the named file exists and is a
- * directory.
+ * Convenience function; returns true if the named file exists as a directory in
+ * the virtual file system hierarchy.
  */
 INLINE bool VirtualFileSystem::
 is_directory(const Filename &filename) const {
@@ -30,8 +31,8 @@ is_directory(const Filename &filename) const {
 }
 
 /**
- * Convenience function; returns true if the named file exists and is a
- * regular file.
+ * Convenience function; returns true if the named file exists as a regular file
+ * in the virtual file system hierarchy.
  */
 INLINE bool VirtualFileSystem::
 is_regular_file(const Filename &filename) const {

+ 17 - 0
panda/src/gles2gsg/gles2gsg.h

@@ -151,6 +151,8 @@ typedef char GLchar;
 #define GL_COLOR 0x1800
 #define GL_DEPTH 0x1801
 #define GL_STENCIL 0x1802
+#define GL_GREEN 0x1904
+#define GL_BLUE 0x1905
 #define GL_RGB10_A2 0x8059
 #define GL_TEXTURE_WRAP_R 0x8072
 #define GL_TEXTURE_MIN_LOD 0x813A
@@ -203,6 +205,7 @@ typedef char GLchar;
 #define GL_FLOAT_MAT4x3 0x8B6A
 #define GL_TEXTURE_2D_ARRAY 0x8C1A
 #define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D
+#define GL_TEXTURE_BUFFER 0x8C2A
 #define GL_R11F_G11F_B10F 0x8C3A
 #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B
 #define GL_RGB9_E5 0x8C3D
@@ -228,6 +231,7 @@ typedef char GLchar;
 #define GL_RGB_INTEGER 0x8D98
 #define GL_RGBA_INTEGER 0x8D99
 #define GL_SAMPLER_2D_ARRAY 0x8DC1
+#define GL_SAMPLER_BUFFER 0x8DC2
 #define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4
 #define GL_SAMPLER_CUBE_SHADOW 0x8DC5
 #define GL_UNSIGNED_INT_VEC2 0x8DC6
@@ -237,24 +241,37 @@ typedef char GLchar;
 #define GL_INT_SAMPLER_3D 0x8DCB
 #define GL_INT_SAMPLER_CUBE 0x8DCC
 #define GL_INT_SAMPLER_2D_ARRAY 0x8DCF
+#define GL_INT_SAMPLER_BUFFER 0x8DD0
 #define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2
 #define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3
 #define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4
 #define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7
+#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8
 #define GL_MAX_IMAGE_UNITS 0x8F38
 #define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
+#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
+#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
+#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
+#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
+#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
 #define GL_IMAGE_2D 0x904D
 #define GL_IMAGE_3D 0x904E
 #define GL_IMAGE_CUBE 0x9050
+#define GL_IMAGE_BUFFER 0x9051
 #define GL_IMAGE_2D_ARRAY 0x9053
+#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
 #define GL_INT_IMAGE_2D 0x9058
 #define GL_INT_IMAGE_3D 0x9059
 #define GL_INT_IMAGE_CUBE 0x905B
+#define GL_INT_IMAGE_BUFFER 0x905C
 #define GL_INT_IMAGE_2D_ARRAY 0x905E
+#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
 #define GL_UNSIGNED_INT_IMAGE_2D 0x9063
 #define GL_UNSIGNED_INT_IMAGE_3D 0x9064
 #define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066
+#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067
 #define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069
+#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
 #define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117
 #define GL_UNSIGNALED 0x9118
 #define GL_SIGNALED 0x9119

+ 1 - 1
panda/src/glstuff/glGeomMunger_src.cxx

@@ -274,7 +274,7 @@ munge_format_impl(const GeomVertexFormat *orig,
               column_alignment = texcoord_type->get_column_alignment();
               num_values = texcoord_type->get_num_values();
             }
-            if (start + num_values * sizeof(PN_stdfloat) > glgsg->get_max_vertex_attrib_stride()) {
+            if (start + num_values * sizeof(PN_stdfloat) > (size_t)glgsg->get_max_vertex_attrib_stride()) {
               // We are exceeding the limit for stride reported by the driver.
               // Start a new array.
               new_format->insert_array(insert_at++, new_array_format);

+ 42 - 45
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -268,11 +268,8 @@ begin_frame(FrameMode mode, Thread *current_thread) {
     if (gl_enable_memory_barriers && _fbo_multisample == 0) {
       CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p();
 
-      TextureContexts::iterator it;
-      for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) {
-        CLP(TextureContext) *gtc = *it;
-
-        if (gtc != nullptr && gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) {
+      for (CLP(TextureContext) *gtc : _texture_contexts) {
+        if (gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) {
           glgsg->issue_memory_barrier(GL_FRAMEBUFFER_BARRIER_BIT);
           // If we've done it for one, we've done it for all.
           break;
@@ -478,6 +475,12 @@ rebuild_bitplanes() {
 
       // Assign the texture to this slot.
       attach[plane] = tex;
+
+      if (plane == RTP_color && _fb_properties.is_stereo()) {
+        if (tex->get_num_views() < 2) {
+          tex->set_num_views(2);
+        }
+      }
     }
   }
 
@@ -581,7 +584,9 @@ rebuild_bitplanes() {
         // The second tex view has already been initialized, so bind it
         // straight away.
         if (attach[RTP_color] != nullptr) {
-          attach_tex(layer, 1, attach[RTP_color], next++);
+          CLP(TextureContext) *gtc = _texture_contexts.back();
+          nassertv(gtc->get_texture() == attach[RTP_color]);
+          attach_tex(next++, gtc, 1, layer);
         } else {
           // XXX hack: I needed a slot to use, and we don't currently use
           // RTP_stencil and it's treated as a color attachment below, so this
@@ -776,7 +781,15 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
       _fb_properties.setup_color_texture(tex);
     }
 
-    GLenum target = glgsg->get_texture_target(tex->get_texture_type());
+    TextureContext *tc = tex->prepare_now(glgsg->get_prepared_objects(), glgsg);
+    nassertv(tc != nullptr);
+    CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
+
+    glgsg->update_texture(gtc, true);
+    gtc->set_active(true);
+    _texture_contexts.push_back(gtc);
+
+    GLenum target = gtc->_target;
     if (target == GL_TEXTURE_CUBE_MAP) {
       target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
     }
@@ -793,7 +806,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
         _rb[RTP_depth_stencil] = 0;
       }
 
-      attach_tex(layer, 0, tex, GL_DEPTH_ATTACHMENT_EXT);
+      attach_tex(GL_DEPTH_ATTACHMENT_EXT, gtc, 0, layer);
 
 #ifndef OPENGLES
       GLint depth_size = 0;
@@ -806,7 +819,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
           GLCAT.debug() << "Binding texture " << *tex << " to stencil attachment.\n";
         }
 
-        attach_tex(layer, 0, tex, GL_STENCIL_ATTACHMENT_EXT);
+        attach_tex(GL_STENCIL_ATTACHMENT_EXT, gtc, 0, layer);
 
 #ifndef OPENGLES
         GLint stencil_size = 0;
@@ -820,7 +833,7 @@ bind_slot(int layer, bool rb_resize, Texture **attach, RenderTexturePlane slot,
         GLCAT.debug() << "Binding texture " << *tex << " to color attachment.\n";
       }
 
-      attach_tex(layer, 0, tex, attachpoint);
+      attach_tex(attachpoint, gtc, 0, layer);
 
 #ifndef OPENGLES
       if (attachpoint == GL_COLOR_ATTACHMENT0_EXT) {
@@ -1344,30 +1357,22 @@ bind_slot_multisample(bool rb_resize, Texture **attach, RenderTexturePlane slot,
  * This function attaches the given texture to the given attachment point.
  */
 void CLP(GraphicsBuffer)::
-attach_tex(int layer, int view, Texture *attach, GLenum attachpoint) {
-  CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p();
-
-  if (view >= attach->get_num_views()) {
-    attach->set_num_views(view + 1);
-  }
+attach_tex(GLenum attachpoint, CLP(TextureContext) *gtc, int view, int layer) {
+  // We should have created the right number of views ahead of time.
+  nassertv(view < gtc->_num_views);
 
-  // Create the OpenGL texture object.
-  TextureContext *tc = attach->prepare_now(view, glgsg->get_prepared_objects(), glgsg);
-  nassertv(tc != nullptr);
-  CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
-
-  glgsg->update_texture(gtc, true);
-  gtc->set_active(true);
-  _texture_contexts.push_back(gtc);
+  CLP(GraphicsStateGuardian) *glgsg = (CLP(GraphicsStateGuardian) *)_gsg.p();
 
   // It seems that binding the texture is necessary before binding to a
   // framebuffer attachment.
-  glgsg->apply_texture(gtc);
+  glgsg->apply_texture(gtc, view);
+
+  GLuint index = gtc->get_view_index(view);
 
 #if !defined(OPENGLES) && defined(SUPPORT_FIXED_FUNCTION)
   if (glgsg->has_fixed_function_pipeline()) {
     GLclampf priority = 1.0f;
-    glPrioritizeTextures(1, &gtc->_index, &priority);
+    glPrioritizeTextures(1, &index, &priority);
   }
 #endif
 
@@ -1375,31 +1380,29 @@ attach_tex(int layer, int view, Texture *attach, GLenum attachpoint) {
   if (_rb_size_z != 1) {
     // Bind all of the layers of the texture.
     nassertv(glgsg->_glFramebufferTexture != nullptr);
-    glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, attachpoint,
-                                 gtc->_index, 0);
+    glgsg->_glFramebufferTexture(GL_FRAMEBUFFER_EXT, attachpoint, index, 0);
     return;
   }
 #endif
 
-  GLenum target = glgsg->get_texture_target(attach->get_texture_type());
-  if (target == GL_TEXTURE_CUBE_MAP) {
-    target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
-  }
-
+  GLenum target = gtc->_target;
   switch (target) {
 #ifndef OPENGLES_1
   case GL_TEXTURE_3D:
     glgsg->_glFramebufferTexture3D(GL_FRAMEBUFFER_EXT, attachpoint,
-                                   target, gtc->_index, 0, layer);
+                                   target, index, 0, layer);
     break;
   case GL_TEXTURE_2D_ARRAY:
     glgsg->_glFramebufferTextureLayer(GL_FRAMEBUFFER_EXT, attachpoint,
-                                      gtc->_index, 0, layer);
+                                      index, 0, layer);
     break;
 #endif
+  case GL_TEXTURE_CUBE_MAP:
+    target = GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
+    // fall through
   default:
     glgsg->_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachpoint,
-                                   target, gtc->_index, 0);
+                                   target, index, 0);
   }
 }
 
@@ -1419,10 +1422,7 @@ generate_mipmaps() {
 
   // PStatGPUTimer timer(glgsg, _generate_mipmap_pcollector);
 
-  pvector<CLP(TextureContext)*>::iterator it;
-  for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) {
-    CLP(TextureContext) *gtc = *it;
-
+  for (CLP(TextureContext) *gtc : _texture_contexts) {
     if (gtc->_generate_mipmaps) {
       glgsg->generate_mipmaps(gtc);
     }
@@ -1954,11 +1954,8 @@ resolve_multisamples() {
   if (gl_enable_memory_barriers) {
     // Issue memory barriers as necessary to make sure that the texture memory
     // is synchronized before we blit to it.
-    pvector<CLP(TextureContext)*>::iterator it;
-    for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) {
-      CLP(TextureContext) *gtc = *it;
-
-      if (gtc != nullptr && gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) {
+    for (CLP(TextureContext) *gtc : _texture_contexts) {
+      if (gtc->needs_barrier(GL_FRAMEBUFFER_BARRIER_BIT)) {
         glgsg->issue_memory_barrier(GL_FRAMEBUFFER_BARRIER_BIT);
         // If we've done it for one, we've done it for all.
         break;

+ 1 - 1
panda/src/glstuff/glGraphicsBuffer_src.h

@@ -92,7 +92,7 @@ protected:
                  RenderTexturePlane plane, GLenum attachpoint);
   void bind_slot_multisample(bool rb_resize, Texture **attach,
                  RenderTexturePlane plane, GLenum attachpoint);
-  void attach_tex(int layer, int view, Texture *attach, GLenum attachpoint);
+  void attach_tex(GLenum attachpoint, CLP(TextureContext) *gtc, int view, int layer);
   bool check_fbo();
   void generate_mipmaps();
   void rebuild_bitplanes();

File diff suppressed because it is too large
+ 437 - 302
panda/src/glstuff/glGraphicsStateGuardian_src.cxx


+ 14 - 8
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -143,6 +143,7 @@ typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays);
 typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha);
 typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
 typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer);
 
 #ifndef OPENGLES_1
 // GLSL shader functions
@@ -361,7 +362,7 @@ public:
   void issue_memory_barrier(GLbitfield barrier);
 #endif
 
-  virtual TextureContext *prepare_texture(Texture *tex, int view);
+  virtual TextureContext *prepare_texture(Texture *tex);
   virtual bool update_texture(TextureContext *tc, bool force);
   virtual void release_texture(TextureContext *tc);
   virtual void release_textures(const pvector<TextureContext *> &contexts);
@@ -643,12 +644,12 @@ protected:
 #endif  // NDEBUG
 
   bool specify_texture(CLP(TextureContext) *gtc, const SamplerState &sampler);
-  bool apply_texture(CLP(TextureContext) *gtc);
-  bool apply_sampler(GLuint unit, const SamplerState &sampler, CLP(TextureContext) *gtc);
+  bool apply_texture(CLP(TextureContext) *gtc, int view);
+  bool apply_sampler(GLuint unit, const SamplerState &sampler,
+                     CLP(TextureContext) *gtc, int view);
   bool upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps);
-  bool upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
-                            bool uses_mipmaps, int mipmap_bias,
-                            GLenum texture_target,
+  bool upload_texture_image(CLP(TextureContext) *gtc, int view,
+                            bool needs_reload, int mipmap_bias, int num_levels,
                             GLint internal_format, GLint external_format,
                             GLenum component_type,
                             Texture::CompressionMode image_compression);
@@ -657,7 +658,7 @@ protected:
 
   size_t get_texture_memory_size(CLP(TextureContext) *gtc);
   void check_nonresident_texture(BufferContextChain &chain);
-  bool do_extract_texture_data(CLP(TextureContext) *gtc);
+  bool do_extract_texture_data(CLP(TextureContext) *gtc, int view);
   bool extract_texture_image(PTA_uchar &image, size_t &page_size,
            Texture *tex, GLenum target, GLenum page_target,
            Texture::ComponentType type,
@@ -837,7 +838,7 @@ public:
   PFNGLTEXSTORAGE2DPROC _glTexStorage2D;
   PFNGLTEXSTORAGE3DPROC _glTexStorage3D;
 
-#ifndef OPENGLES
+#ifndef OPENGLES_1
   PFNGLTEXBUFFERPROC _glTexBuffer;
 #endif
 
@@ -971,7 +972,12 @@ public:
 
 #ifndef OPENGLES
   bool _supports_dsa;
+  PFNGLCREATETEXTURESPROC _glCreateTextures;
+  PFNGLTEXTURESTORAGE2DPROC _glTextureStorage2D;
+  PFNGLTEXTURESUBIMAGE2DPROC _glTextureSubImage2D;
+  PFNGLTEXTUREPARAMETERIPROC _glTextureParameteri;
   PFNGLGENERATETEXTUREMIPMAPPROC _glGenerateTextureMipmap;
+  PFNGLBINDTEXTUREUNITPROC _glBindTextureUnit;
 #endif
 
 #ifndef OPENGLES_1

+ 41 - 106
panda/src/glstuff/glShaderContext_src.cxx

@@ -1345,6 +1345,15 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
   else if (param_size == 1) {
     // A single uniform (not an array, or an array of size 1).
     switch (param_type) {
+#ifndef OPENGLES
+    case GL_INT_SAMPLER_1D:
+    case GL_INT_SAMPLER_1D_ARRAY:
+    case GL_UNSIGNED_INT_SAMPLER_1D:
+    case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
+    case GL_SAMPLER_1D:
+    case GL_SAMPLER_1D_ARRAY:
+    case GL_SAMPLER_1D_SHADOW:
+#endif
     case GL_INT_SAMPLER_2D:
     case GL_INT_SAMPLER_3D:
     case GL_INT_SAMPLER_2D_ARRAY:
@@ -1356,22 +1365,13 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
     case GL_SAMPLER_CUBE_SHADOW:
     case GL_SAMPLER_2D_ARRAY:
     case GL_SAMPLER_2D_ARRAY_SHADOW:
-#ifndef OPENGLES
-    case GL_INT_SAMPLER_1D:
-    case GL_INT_SAMPLER_1D_ARRAY:
     case GL_INT_SAMPLER_BUFFER:
     case GL_INT_SAMPLER_CUBE_MAP_ARRAY:
-    case GL_UNSIGNED_INT_SAMPLER_1D:
-    case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY:
     case GL_UNSIGNED_INT_SAMPLER_BUFFER:
     case GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY:
-    case GL_SAMPLER_1D:
-    case GL_SAMPLER_1D_ARRAY:
-    case GL_SAMPLER_1D_SHADOW:
     case GL_SAMPLER_BUFFER:
     case GL_SAMPLER_CUBE_MAP_ARRAY:
     case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
-#endif  // !OPENGLES
     case GL_SAMPLER_2D:
     case GL_SAMPLER_2D_SHADOW:
     case GL_SAMPLER_3D:
@@ -1557,6 +1557,11 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
       _shader->_ptr_spec.push_back(bind);
       return;
     }
+#ifndef OPENGLES
+    case GL_IMAGE_1D:
+    case GL_INT_IMAGE_1D:
+    case GL_UNSIGNED_INT_IMAGE_1D:
+#endif
     case GL_IMAGE_2D:
     case GL_IMAGE_3D:
     case GL_IMAGE_CUBE:
@@ -1569,20 +1574,19 @@ reflect_uniform(int i, char *name_buffer, GLsizei name_buflen) {
     case GL_UNSIGNED_INT_IMAGE_3D:
     case GL_UNSIGNED_INT_IMAGE_CUBE:
     case GL_UNSIGNED_INT_IMAGE_2D_ARRAY:
-#ifndef OPENGLES
-    case GL_IMAGE_1D:
     case GL_IMAGE_CUBE_MAP_ARRAY:
     case GL_IMAGE_BUFFER:
-    case GL_INT_IMAGE_1D:
     case GL_INT_IMAGE_CUBE_MAP_ARRAY:
     case GL_INT_IMAGE_BUFFER:
-    case GL_UNSIGNED_INT_IMAGE_1D:
     case GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY:
     case GL_UNSIGNED_INT_IMAGE_BUFFER:
-#endif
       // This won't really change at runtime, so we might as well bind once
       // and then forget about it.
+      // Note that OpenGL ES doesn't support changing this at runtime, so we
+      // rely on the shader using a layout declaration.
+#ifndef OPENGLES
       _glgsg->_glUniform1i(p, _glsl_img_inputs.size());
+#endif
       {
         ImageInput input;
         input._name = param._name;
@@ -1989,7 +1993,6 @@ get_sampler_texture_type(int &out, GLenum param_type) {
       return false;
     }
 
-#ifndef OPENGLES
   case GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW:
     if (!_glgsg->_supports_shadow_filter) {
       GLCAT.error()
@@ -2021,7 +2024,6 @@ get_sampler_texture_type(int &out, GLenum param_type) {
         << "GLSL shader uses buffer texture, which is unsupported by the driver.\n";
       return false;
     }
-#endif  // !OPENGLES
 
   default:
     GLCAT.error()
@@ -2895,56 +2897,25 @@ disable_shader_texture_bindings() {
 
   DO_PSTATS_STUFF(_glgsg->_texture_state_pcollector.add_level(1));
 
-  for (size_t i = 0; i < _shader->_tex_spec.size(); ++i) {
 #ifndef OPENGLES
-    // Check if bindless was used, if so, there's nothing to unbind.
-    if (_glgsg->_supports_bindless_texture) {
-      GLint p = _shader->_tex_spec[i]._id._location;
-
-      if (_glsl_uniform_handles.count(p) > 0) {
-        continue;
-      }
-    }
-
-    if (_glgsg->_supports_multi_bind) {
-      // There are non-bindless textures to unbind, and we're lazy, so let's
-      // go and unbind everything after this point using one multi-bind call,
-      // and then break out of the loop.
-      _glgsg->_glBindTextures(i, _shader->_tex_spec.size() - i, nullptr);
-      break;
+  if (_glgsg->_supports_multi_bind) {
+    _glgsg->_glBindTextures(0, _shader->_tex_spec.size(), nullptr);
+  }
+  else if (_glgsg->_supports_dsa) {
+    for (size_t i = 0; i < _shader->_tex_spec.size(); ++i) {
+      _glgsg->_glBindTextureUnit(i, 0);
     }
+  }
+  else
 #endif
+  {
+    for (size_t i = 0; i < _shader->_tex_spec.size(); ++i) {
+      _glgsg->set_active_texture_stage(i);
 
-    _glgsg->set_active_texture_stage(i);
-
-    switch (_shader->_tex_spec[i]._desired_type) {
-    case Texture::TT_1d_texture:
-#ifndef OPENGLES
-      glBindTexture(GL_TEXTURE_1D, 0);
-#endif
-      break;
-
-    case Texture::TT_2d_texture:
-      glBindTexture(GL_TEXTURE_2D, 0);
-      break;
-
-    case Texture::TT_3d_texture:
-      glBindTexture(GL_TEXTURE_3D, 0);
-      break;
-
-    case Texture::TT_2d_texture_array:
-      glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
-      break;
-
-    case Texture::TT_cube_map:
-      glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
-      break;
-
-    case Texture::TT_buffer_texture:
-#ifndef OPENGLES
-      glBindTexture(GL_TEXTURE_BUFFER, 0);
-#endif
-      break;
+      GLenum target = _glgsg->get_texture_target((Texture::TextureType)_shader->_tex_spec[i]._desired_type);
+      if (target != GL_NONE) {
+        glBindTexture(target, 0);
+      }
     }
   }
 
@@ -3035,14 +3006,14 @@ update_shader_texture_bindings(ShaderContext *prev) {
       CLP(TextureContext) *gtc;
 
       if (tex != nullptr) {
-        int view = _glgsg->get_current_tex_view_offset();
-
-        gtc = DCAST(CLP(TextureContext), tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg));
+        gtc = DCAST(CLP(TextureContext), tex->prepare_now(_glgsg->_prepared_objects, _glgsg));
         if (gtc != nullptr) {
           input._gtc = gtc;
 
           _glgsg->update_texture(gtc, true);
-          gl_tex = gtc->_index;
+
+          int view = _glgsg->get_current_tex_view_offset();
+          gl_tex = gtc->get_view_index(view);
 
 #ifndef OPENGLES
           if (gtc->needs_barrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT)) {
@@ -3177,7 +3148,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
       // enabled.
     }
 
-    CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tex->prepare_now(view, _glgsg->_prepared_objects, _glgsg));
+    CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tex->prepare_now(_glgsg->_prepared_objects, _glgsg));
     if (gtc == nullptr) {
       if (multi_bind) {
         textures[i] = 0;
@@ -3187,49 +3158,13 @@ update_shader_texture_bindings(ShaderContext *prev) {
     }
 
 #ifndef OPENGLES
-    GLint p = spec._id._location;
-
     // If it was recently written to, we will have to issue a memory barrier
     // soon.
     if (gtc->needs_barrier(GL_TEXTURE_FETCH_BARRIER_BIT)) {
       barriers |= GL_TEXTURE_FETCH_BARRIER_BIT;
     }
-
-    // Try bindless texturing first, if supported.
-    if (gl_use_bindless_texture && _glgsg->_supports_bindless_texture) {
-      // We demand the real texture, since we won't be able to change the
-      // texture properties after this point.
-      if (multi_bind) {
-        textures[i] = 0;
-        samplers[i] = 0;
-      }
-      if (!_glgsg->update_texture(gtc, true)) {
-        continue;
-      }
-
-      GLuint64 handle = gtc->get_handle();
-      if (handle != 0) {
-        gtc->make_handle_resident();
-        gtc->set_active(true);
-
-        // Check if we have already specified this texture handle.  If so, no
-        // need to call glUniformHandle again.
-        pmap<GLint, GLuint64>::const_iterator it;
-        it = _glsl_uniform_handles.find(p);
-        if (it != _glsl_uniform_handles.end() && it->second == handle) {
-          // Already specified.
-          continue;
-        } else {
-          _glgsg->_glUniformHandleui64(p, handle);
-          _glsl_uniform_handles[p] = handle;
-        }
-        continue;
-      }
-    }
 #endif
 
-    // Bindless texturing wasn't supported or didn't work, so let's just bind
-    // the texture normally.
     // Note that simple RAM images are always 2-D for now, so to avoid errors,
     // we must load the real texture if this is not for a sampler2D.
     bool force = (spec._desired_type != Texture::TT_2d_texture);
@@ -3240,7 +3175,7 @@ update_shader_texture_bindings(ShaderContext *prev) {
         textures[i] = 0;
       } else {
         gtc->set_active(true);
-        textures[i] = gtc->_index;
+        textures[i] = gtc->get_view_index(view);
       }
 
       SamplerContext *sc = sampler.prepare_now(_glgsg->get_prepared_objects(), _glgsg);
@@ -3259,8 +3194,8 @@ update_shader_texture_bindings(ShaderContext *prev) {
       if (!_glgsg->update_texture(gtc, force)) {
         continue;
       }
-      _glgsg->apply_texture(gtc);
-      _glgsg->apply_sampler(i, sampler, gtc);
+      _glgsg->apply_texture(gtc, view);
+      _glgsg->apply_sampler(i, sampler, gtc, view);
     }
   }
 

+ 0 - 3
panda/src/glstuff/glShaderContext_src.h

@@ -110,9 +110,6 @@ private:
   GLsizei _slider_table_size;
   GLint _frame_number_loc;
   GLint _frame_number;
-#ifndef OPENGLES
-  pmap<GLint, GLuint64> _glsl_uniform_handles;
-#endif
 
 #ifndef OPENGLES
   struct StorageBlock {

+ 29 - 9
panda/src/glstuff/glTextureContext_src.I

@@ -16,26 +16,46 @@
  */
 INLINE CLP(TextureContext)::
 CLP(TextureContext)(CLP(GraphicsStateGuardian) *glgsg,
-  PreparedGraphicsObjects *pgo, Texture *tex, int view) :
-  TextureContext(pgo, tex, view)
+  PreparedGraphicsObjects *pgo, Texture *tex) :
+  TextureContext(pgo, tex),
+  _num_views(0)
 {
   _glgsg = glgsg;
 
-  glGenTextures(1, &_index);
+  _index = 0;
+  _indices = &_index;
+
   _buffer = 0;
+  _buffers = nullptr;
 
-#ifndef OPENGLES
-  _handle = 0;
-  _handle_resident = false;
-#endif
   _has_storage = false;
-  _simple_loaded = false;
   _immutable = false;
-  _uses_mipmaps = false;
+  _may_reload_with_mipmaps = false;
   _generate_mipmaps = false;
   _internal_format = 0;
   _width = 0;
   _height = 0;
   _depth = 0;
+  _num_levels = 0;
   _target = GL_NONE;
 }
+
+/**
+ * Returns the index for the given view of the texture.
+ */
+INLINE GLuint CLP(TextureContext)::
+get_view_index(int view) const {
+  return _indices[std::min(std::max(view, 0), _num_views - 1)];
+}
+
+/**
+ * Returns the buffer index for the given view of the texture.
+ */
+INLINE GLuint CLP(TextureContext)::
+get_view_buffer(int view) const {
+  if (_buffers != nullptr) {
+    return _buffers[std::min(std::max(view, 0), _num_views - 1)];
+  } else {
+    return 0;
+  }
+}

+ 68 - 56
panda/src/glstuff/glTextureContext_src.cxx

@@ -37,53 +37,29 @@ void CLP(TextureContext)::
 evict_lru() {
   dequeue_lru();
 
-#ifndef OPENGLES
-  if (_handle != 0) {
-    if (_handle_resident) {
-      _glgsg->_glMakeTextureHandleNonResident(_handle);
-    }
-    _handle_resident = false;
-  } else
-#endif
-  {
-    reset_data();
-  }
-
+  reset_data(_target);
   update_data_size_bytes(0);
   mark_unloaded();
 }
 
 /**
  * Resets the texture object to a new one so a new GL texture object can be
- * uploaded.
+ * uploaded.  This call also allows the texture target to be changed.
  */
 void CLP(TextureContext)::
-reset_data() {
-#ifndef OPENGLES
-  if (_handle != 0 && _handle_resident) {
-    _glgsg->_glMakeTextureHandleNonResident(_handle);
-  }
-#endif
-
+reset_data(GLenum target, int num_views) {
   // Free the texture resources.
-  glDeleteTextures(1, &_index);
+  set_num_views(0);
 
-  if (_buffer != 0) {
-    _glgsg->_glDeleteBuffers(1, &_buffer);
-    _buffer = 0;
-  }
+  _target = target;
 
   // We still need a valid index number, though, in case we want to re-load
   // the texture later.
-  glGenTextures(1, &_index);
+  set_num_views(num_views);
 
-#ifndef OPENGLES
-  _handle = 0;
-  _handle_resident = false;
-#endif
   _has_storage = false;
-  _simple_loaded = false;
   _immutable = false;
+  _may_reload_with_mipmaps = false;
 
 #ifndef OPENGLES_1
   // Mark the texture as coherent.
@@ -118,41 +94,77 @@ get_native_buffer_id() const {
 }
 
 /**
- *
+ * Changes the number of views in the texture.
  */
-#ifndef OPENGLES
 void CLP(TextureContext)::
-make_handle_resident() {
-  if (_handle != 0) {
-    if (!_handle_resident) {
-      _glgsg->_glMakeTextureHandleResident(_handle);
-      _handle_resident = true;
+set_num_views(int num_views) {
+  if (_num_views > num_views) {
+    glDeleteTextures(_num_views - num_views, _indices + _num_views);
+
+    if (_buffers != nullptr) {
+      _glgsg->_glDeleteBuffers(_num_views - num_views, _buffers + num_views);
     }
-    set_resident(true);
-  }
-}
+
+    if (num_views <= 1) {
+      _index = _indices[0];
+      if (_indices != &_index) {
+        delete[] _indices;
+        _indices = &_index;
+      }
+
+#ifndef OPENGLES_1
+      if (_buffers != nullptr) {
+        _buffer = _buffers[0];
+        if (_buffers != &_buffer) {
+          delete[] _buffers;
+          _buffers = &_buffer;
+        }
+        if (num_views == 0) {
+          _buffers = nullptr;
+        }
+      }
 #endif
+    }
+  }
+  else if (_num_views == 0 && num_views == 1) {
+    glGenTextures(1, &_index);
+    _indices = &_index;
 
-/**
- * Returns a handle for this texture.  Once this has been created, the texture
- * data may still be updated, but its properties may not.
- */
-#ifndef OPENGLES
-INLINE GLuint64 CLP(TextureContext)::
-get_handle() {
-  return 0;
-  if (!_glgsg->_supports_bindless_texture) {
-    return false;
+#ifndef OPENGLES_1
+    if (_target == GL_TEXTURE_BUFFER) {
+      _glgsg->_glGenBuffers(1, &_buffer);
+      _buffers = &_buffer;
+    }
+#endif
   }
+  else if (_num_views < num_views) {
+    GLuint *new_indices = new GLuint[num_views];
+    memcpy(new_indices, _indices, sizeof(GLuint) * _num_views);
+    glGenTextures(num_views - _num_views, new_indices + _num_views);
+    if (_indices != &_index) {
+      delete[] _indices;
+    }
+    _indices = new_indices;
 
-  if (_handle == 0) {
-    _handle = _glgsg->_glGetTextureHandle(_index);
+#ifndef OPENGLES_1
+    if (_target == GL_TEXTURE_BUFFER) {
+      GLuint *new_buffers = new GLuint[num_views];
+      if (_buffers != nullptr) {
+        memcpy(new_buffers, _buffers, sizeof(GLuint) * _num_views);
+        _glgsg->_glGenBuffers(num_views - _num_views, new_buffers + _num_views);
+        if (_buffers != &_buffer) {
+          delete[] _buffers;
+        }
+      } else {
+        _glgsg->_glGenBuffers(num_views, new_buffers);
+      }
+      _buffers = new_buffers;
+    }
+#endif
   }
 
-  _immutable = true;
-  return _handle;
+  _num_views = num_views;
 }
-#endif
 
 #ifndef OPENGLES_1
 /**

+ 15 - 14
panda/src/glstuff/glTextureContext_src.h

@@ -25,21 +25,20 @@ class CLP(SamplerContext);
 class EXPCL_GL CLP(TextureContext) : public TextureContext {
 public:
   INLINE CLP(TextureContext)(CLP(GraphicsStateGuardian) *glgsg,
-                             PreparedGraphicsObjects *pgo,
-                             Texture *tex, int view);
+                             PreparedGraphicsObjects *pgo, Texture *tex);
   ALLOC_DELETED_CHAIN(CLP(TextureContext));
 
   virtual ~CLP(TextureContext)();
   virtual void evict_lru();
-  void reset_data();
+  void reset_data(GLenum target, int num_views = 1);
 
   virtual uint64_t get_native_id() const;
   virtual uint64_t get_native_buffer_id() const;
 
-#ifndef OPENGLES
-  void make_handle_resident();
-  GLuint64 get_handle();
-#endif
+  void set_num_views(int num_views);
+
+  INLINE GLuint get_view_index(int view) const;
+  INLINE GLuint get_view_buffer(int view) const;
 
 #ifdef OPENGLES_1
   static constexpr bool needs_barrier(GLbitfield barrier) { return false; };
@@ -48,30 +47,32 @@ public:
   void mark_incoherent(bool wrote);
 #endif
 
+private:
   // This is the GL "name" of the texture object.
   GLuint _index;
 
   // This is only used for buffer textures.
   GLuint _buffer;
 
-#ifndef OPENGLES
-  // This is the bindless "handle" to the texture object.
-  GLuint64 _handle;
-  bool _handle_resident;
-#endif
+public:
+  // Multiview textures have multiple of the above.  For a single-view texture,
+  // these are simply pointers to the above fields.
+  int _num_views;
+  GLuint *_indices;
+  GLuint *_buffers;
 
   // These are the parameters that we specified with the last glTexImage2D()
   // or glTexStorage2D() call.  If none of these have changed, we can reload
   // the texture image with a glTexSubImage2D().
   bool _has_storage;
-  bool _simple_loaded;
   bool _immutable;
-  bool _uses_mipmaps;
+  bool _may_reload_with_mipmaps;
   bool _generate_mipmaps;
   GLint _internal_format;
   GLsizei _width;
   GLsizei _height;
   GLsizei _depth;
+  int _num_levels;
   GLenum _target;
   SamplerState _active_sampler;
 

+ 0 - 10
panda/src/glstuff/glmisc_src.cxx

@@ -260,16 +260,6 @@ ConfigVariableBool gl_immutable_texture_storage
             "for each texture.  This improves runtime performance, but "
             "changing the size or type of a texture will be slower."));
 
-ConfigVariableBool gl_use_bindless_texture
-  ("gl-use-bindless-texture", false,
-   PRC_DESC("Set this to let Panda use OpenGL's bindless texture "
-            "extension for all textures passed to shaders, for improved "
-            "performance.  This is an experimental feature and comes "
-            "with a few caveats; for one, it requires that all sampler "
-            "uniforms have a layout(bindless_sampler) qualifier, and "
-            "it also requires that the texture properties are not "
-            "modified after the texture handle has been initialized."));
-
 ConfigVariableBool gl_enable_memory_barriers
   ("gl-enable-memory-barriers", true,
    PRC_DESC("If this is set, Panda will make sure that every write "

+ 0 - 1
panda/src/glstuff/glmisc_src.h

@@ -76,7 +76,6 @@ extern ConfigVariableBool gl_cube_map_seamless;
 extern ConfigVariableBool gl_dump_compiled_shaders;
 extern ConfigVariableBool gl_validate_shaders;
 extern ConfigVariableBool gl_immutable_texture_storage;
-extern ConfigVariableBool gl_use_bindless_texture;
 extern ConfigVariableBool gl_enable_memory_barriers;
 extern ConfigVariableBool gl_vertex_array_objects;
 extern ConfigVariableBool gl_fixed_vertex_attrib_locations;

+ 9 - 11
panda/src/gobj/preparedGraphicsObjects.cxx

@@ -242,7 +242,7 @@ void PreparedGraphicsObjects::
 release_texture(TextureContext *tc) {
   ReMutexHolder holder(_lock);
 
-  tc->get_texture()->clear_prepared(tc->get_view(), this);
+  tc->get_texture()->clear_prepared(this);
 
   // We have to set the Texture pointer to NULL at this point, since the
   // Texture itself might destruct at any time after it has been released.
@@ -275,7 +275,7 @@ release_all_textures() {
   int num_textures = (int)_prepared_textures.size() + (int)_enqueued_textures.size();
 
   for (TextureContext *tc : _prepared_textures) {
-    tc->get_texture()->clear_prepared(tc->get_view(), this);
+    tc->get_texture()->clear_prepared(this);
     tc->_object = nullptr;
 
     _released_textures.push_back(tc);
@@ -332,13 +332,13 @@ get_num_prepared_textures() const {
  * the TextureContext will be deleted.
  */
 TextureContext *PreparedGraphicsObjects::
-prepare_texture_now(Texture *tex, int view, GraphicsStateGuardianBase *gsg) {
+prepare_texture_now(Texture *tex, GraphicsStateGuardianBase *gsg) {
   ReMutexHolder holder(_lock);
 
   // Ask the GSG to create a brand new TextureContext.  There might be several
   // GSG's sharing the same set of textures; if so, it doesn't matter which of
   // them creates the context (since they're all shared anyway).
-  TextureContext *tc = gsg->prepare_texture(tex, view);
+  TextureContext *tc = gsg->prepare_texture(tex);
 
   if (tc != nullptr) {
     bool prepared = _prepared_textures.insert(tc).second;
@@ -1474,13 +1474,11 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) {
        qti != _enqueued_textures.end();
        ++qti) {
     Texture *tex = qti->first;
-    for (int view = 0; view < tex->get_num_views(); ++view) {
-      TextureContext *tc = tex->prepare_now(view, this, gsg);
-      if (tc != nullptr) {
-        gsg->update_texture(tc, true);
-        if (view == 0 && qti->second != nullptr) {
-          qti->second->set_result(tc);
-        }
+    TextureContext *tc = tex->prepare_now(this, gsg);
+    if (tc != nullptr) {
+      gsg->update_texture(tc, true);
+      if (qti->second != nullptr) {
+        qti->second->set_result(tc);
       }
     }
   }

+ 1 - 1
panda/src/gobj/preparedGraphicsObjects.h

@@ -83,7 +83,7 @@ PUBLISHED:
   int get_num_queued_textures() const;
   int get_num_prepared_textures() const;
 
-  TextureContext *prepare_texture_now(Texture *tex, int view,
+  TextureContext *prepare_texture_now(Texture *tex,
                                       GraphicsStateGuardianBase *gsg);
 
   void enqueue_sampler(const SamplerState &sampler);

+ 106 - 131
panda/src/gobj/texture.cxx

@@ -1513,6 +1513,39 @@ get_image_modified_pages(UpdateSeq since, int n) const {
     return result;
   }
 
+  if (n > 0 && cdata->_texture_type == Texture::TT_3d_texture) {
+    // Don't bother handling this special case, just consider all mipmap pages
+    // modified.
+    result.set_range(0, do_get_expected_mipmap_num_pages(cdata, n));
+    return result;
+  }
+
+  size_t num_pages = cdata->_z_size * cdata->_num_views;
+  for (const ModifiedPageRange &range : cdata->_modified_pages) {
+    if (range._z_begin >= num_pages) {
+      break;
+    }
+    if (since < range._modified) {
+      result.set_range(range._z_begin, std::min(range._z_end, num_pages) - range._z_begin);
+    }
+  }
+
+  return result;
+}
+
+/**
+ * Like get_image_modified_pages, but returns the result for a particular view.
+ */
+SparseArray Texture::
+get_view_modified_pages(UpdateSeq since, int view, int n) const {
+  CDReader cdata(_cycler);
+
+  SparseArray result;
+  if (since == cdata->_image_modified) {
+    // Early-out since no range is more recent than _image_modified.
+    return result;
+  }
+
   if (n > 0 && cdata->_texture_type == Texture::TT_3d_texture) {
     // Don't bother handling this special case, just consider all mipmap pages
     // modified.
@@ -1520,12 +1553,17 @@ get_image_modified_pages(UpdateSeq since, int n) const {
     return result;
   }
 
+  size_t offset = cdata->_z_size * view;
   for (const ModifiedPageRange &range : cdata->_modified_pages) {
-    if (range._z_begin >= cdata->_z_size) {
+    if (range._z_end <= offset) {
+      continue;
+    }
+    if (range._z_begin >= offset + (size_t)cdata->_z_size) {
       break;
     }
     if (since < range._modified) {
-      result.set_range(range._z_begin, std::min(range._z_end, (size_t)cdata->_z_size) - range._z_begin);
+      size_t begin = std::max(range._z_begin, offset) - offset;
+      result.set_range(begin, std::min(range._z_end - offset, (size_t)cdata->_z_size) - begin);
     }
   }
 
@@ -1553,9 +1591,9 @@ prepare(PreparedGraphicsObjects *prepared_objects) {
 bool Texture::
 is_prepared(PreparedGraphicsObjects *prepared_objects) const {
   MutexHolder holder(_lock);
-  PreparedViews::const_iterator pvi;
-  pvi = _prepared_views.find(prepared_objects);
-  if (pvi != _prepared_views.end()) {
+  Contexts::const_iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
     return true;
   }
   return prepared_objects->is_texture_queued(this);
@@ -1569,24 +1607,11 @@ is_prepared(PreparedGraphicsObjects *prepared_objects) const {
 bool Texture::
 was_image_modified(PreparedGraphicsObjects *prepared_objects) const {
   MutexHolder holder(_lock);
-  CDReader cdata(_cycler);
-
-  PreparedViews::const_iterator pvi;
-  pvi = _prepared_views.find(prepared_objects);
-  if (pvi != _prepared_views.end()) {
-    const Contexts &contexts = (*pvi).second;
-    for (int view = 0; view < cdata->_num_views; ++view) {
-      Contexts::const_iterator ci;
-      ci = contexts.find(view);
-      if (ci == contexts.end()) {
-        return true;
-      }
-      TextureContext *tc = (*ci).second;
-      if (tc->was_image_modified()) {
-        return true;
-      }
-    }
-    return false;
+  Contexts::const_iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    TextureContext *tc = (*ci).second;
+    return tc->was_image_modified();
   }
   return true;
 }
@@ -1601,24 +1626,13 @@ was_image_modified(PreparedGraphicsObjects *prepared_objects) const {
 size_t Texture::
 get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const {
   MutexHolder holder(_lock);
-  CDReader cdata(_cycler);
-
-  PreparedViews::const_iterator pvi;
-  size_t total_size = 0;
-  pvi = _prepared_views.find(prepared_objects);
-  if (pvi != _prepared_views.end()) {
-    const Contexts &contexts = (*pvi).second;
-    for (int view = 0; view < cdata->_num_views; ++view) {
-      Contexts::const_iterator ci;
-      ci = contexts.find(view);
-      if (ci != contexts.end()) {
-        TextureContext *tc = (*ci).second;
-        total_size += tc->get_data_size_bytes();
-      }
-    }
+  Contexts::const_iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    TextureContext *tc = (*ci).second;
+    return tc->get_data_size_bytes();
   }
-
-  return total_size;
+  return 0;
 }
 
 /**
@@ -1628,24 +1642,13 @@ get_data_size_bytes(PreparedGraphicsObjects *prepared_objects) const {
 bool Texture::
 get_active(PreparedGraphicsObjects *prepared_objects) const {
   MutexHolder holder(_lock);
-  CDReader cdata(_cycler);
-
-  PreparedViews::const_iterator pvi;
-  pvi = _prepared_views.find(prepared_objects);
-  if (pvi != _prepared_views.end()) {
-    const Contexts &contexts = (*pvi).second;
-    for (int view = 0; view < cdata->_num_views; ++view) {
-      Contexts::const_iterator ci;
-      ci = contexts.find(view);
-      if (ci != contexts.end()) {
-        TextureContext *tc = (*ci).second;
-        if (tc->get_active()) {
-          return true;
-        }
-      }
-    }
+  Contexts::const_iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    TextureContext *tc = (*ci).second;
+    return tc->get_active();
   }
-  return false;
+  return 0;
 }
 
 /**
@@ -1655,24 +1658,13 @@ get_active(PreparedGraphicsObjects *prepared_objects) const {
 bool Texture::
 get_resident(PreparedGraphicsObjects *prepared_objects) const {
   MutexHolder holder(_lock);
-  CDReader cdata(_cycler);
-
-  PreparedViews::const_iterator pvi;
-  pvi = _prepared_views.find(prepared_objects);
-  if (pvi != _prepared_views.end()) {
-    const Contexts &contexts = (*pvi).second;
-    for (int view = 0; view < cdata->_num_views; ++view) {
-      Contexts::const_iterator ci;
-      ci = contexts.find(view);
-      if (ci != contexts.end()) {
-        TextureContext *tc = (*ci).second;
-        if (tc->get_resident()) {
-          return true;
-        }
-      }
-    }
+  Contexts::const_iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    TextureContext *tc = (*ci).second;
+    return tc->get_resident();
   }
-  return false;
+  return 0;
 }
 
 /**
@@ -1681,20 +1673,15 @@ get_resident(PreparedGraphicsObjects *prepared_objects) const {
  */
 bool Texture::
 release(PreparedGraphicsObjects *prepared_objects) {
-  MutexHolder holder(_lock);
-  PreparedViews::iterator pvi;
-  pvi = _prepared_views.find(prepared_objects);
-  if (pvi != _prepared_views.end()) {
-    Contexts temp;
-    temp.swap((*pvi).second);
-    Contexts::iterator ci;
-    for (ci = temp.begin(); ci != temp.end(); ++ci) {
-      TextureContext *tc = (*ci).second;
-      if (tc != nullptr) {
-        prepared_objects->release_texture(tc);
-      }
+  Contexts::iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    TextureContext *tc = (*ci).second;
+    if (tc != nullptr) {
+      prepared_objects->release_texture(tc);
+    } else {
+      _contexts.erase(ci);
     }
-    _prepared_views.erase(pvi);
   }
 
   // Maybe it wasn't prepared yet, but it's about to be.
@@ -1709,25 +1696,19 @@ int Texture::
 release_all() {
   MutexHolder holder(_lock);
 
-  // We have to traverse a copy of the _prepared_views list, because the
+  // We have to traverse a copy of the _contexts list, because the
   // PreparedGraphicsObjects object will call clear_prepared() in response to
   // each release_texture(), and we don't want to be modifying the
-  // _prepared_views list while we're traversing it.
-  PreparedViews temp;
-  temp.swap(_prepared_views);
-  int num_freed = (int)temp.size();
+  // _contexts list while we're traversing it.
+  Contexts temp = _contexts;
+  int num_freed = (int)_contexts.size();
 
-  PreparedViews::iterator pvi;
-  for (pvi = temp.begin(); pvi != temp.end(); ++pvi) {
-    PreparedGraphicsObjects *prepared_objects = (*pvi).first;
-    Contexts temp;
-    temp.swap((*pvi).second);
-    Contexts::iterator ci;
-    for (ci = temp.begin(); ci != temp.end(); ++ci) {
-      TextureContext *tc = (*ci).second;
-      if (tc != nullptr) {
-        prepared_objects->release_texture(tc);
-      }
+  Contexts::const_iterator ci;
+  for (ci = temp.begin(); ci != temp.end(); ++ci) {
+    PreparedGraphicsObjects *prepared_objects = (*ci).first;
+    TextureContext *tc = (*ci).second;
+    if (tc != nullptr) {
+      prepared_objects->release_texture(tc);
     }
   }
 
@@ -2088,29 +2069,31 @@ set_orig_file_size(int x, int y, int z) {
  * be rendered.
  */
 TextureContext *Texture::
-prepare_now(int view,
-            PreparedGraphicsObjects *prepared_objects,
+prepare_now(PreparedGraphicsObjects *prepared_objects,
             GraphicsStateGuardianBase *gsg) {
   MutexHolder holder(_lock);
-  CDReader cdata(_cycler);
 
-  // Don't exceed the actual number of views.
-  view = max(min(view, cdata->_num_views - 1), 0);
-
-  // Get the list of PreparedGraphicsObjects for this view.
-  Contexts &contexts = _prepared_views[prepared_objects];
-  Contexts::const_iterator pvi;
-  pvi = contexts.find(view);
-  if (pvi != contexts.end()) {
-    return (*pvi).second;
+  Contexts::const_iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    return (*ci).second;
   }
 
-  TextureContext *tc = prepared_objects->prepare_texture_now(this, view, gsg);
-  contexts[view] = tc;
+  TextureContext *tc = prepared_objects->prepare_texture_now(this, gsg);
+  _contexts[prepared_objects] = tc;
 
   return tc;
 }
 
+/**
+ * @deprecated See prepare_now() without a view parameter.
+ */
+TextureContext *Texture::
+prepare_now(int view, PreparedGraphicsObjects *prepared_objects,
+            GraphicsStateGuardianBase *gsg) {
+  return prepare_now(prepared_objects, gsg);
+}
+
 /**
  * Returns the smallest power of 2 greater than or equal to value.
  */
@@ -9277,20 +9260,11 @@ read_dds_level_bc5(Texture *tex, CData *cdata, const DDSHeader &header, int n, i
  * never be called by user code.
  */
 void Texture::
-clear_prepared(int view, PreparedGraphicsObjects *prepared_objects) {
-  PreparedViews::iterator pvi;
-  pvi = _prepared_views.find(prepared_objects);
-  if (pvi != _prepared_views.end()) {
-    Contexts &contexts = (*pvi).second;
-    Contexts::iterator ci;
-    ci = contexts.find(view);
-    if (ci != contexts.end()) {
-      contexts.erase(ci);
-    }
-
-    if (contexts.empty()) {
-      _prepared_views.erase(pvi);
-    }
+clear_prepared(PreparedGraphicsObjects *prepared_objects) {
+  Contexts::iterator ci;
+  ci = _contexts.find(prepared_objects);
+  if (ci != _contexts.end()) {
+    _contexts.erase(ci);
   }
 }
 
@@ -10822,6 +10796,7 @@ Texture::CData::
 CData(const Texture::CData &copy) {
   _num_mipmap_levels_read = 0;
   _render_to_texture = copy._render_to_texture;
+  _post_load_store_cache = copy._post_load_store_cache;
 
   do_assign(&copy);
 

+ 6 - 4
panda/src/gobj/texture.h

@@ -528,6 +528,7 @@ PUBLISHED:
   MAKE_PROPERTY(image_modified, get_image_modified);
 
   SparseArray get_image_modified_pages(UpdateSeq since, int n = 0) const;
+  SparseArray get_view_modified_pages(UpdateSeq since, int view, int n = 0) const;
 
   INLINE bool has_auto_texture_scale() const;
   INLINE AutoTextureScale get_auto_texture_scale() const;
@@ -597,6 +598,8 @@ PUBLISHED:
   MAKE_PROPERTY(post_load_store_cache, get_post_load_store_cache,
                                        set_post_load_store_cache);
 
+  TextureContext *prepare_now(PreparedGraphicsObjects *prepared_objects,
+                              GraphicsStateGuardianBase *gsg);
   TextureContext *prepare_now(int view,
                               PreparedGraphicsObjects *prepared_objects,
                               GraphicsStateGuardianBase *gsg);
@@ -859,7 +862,7 @@ private:
                                       const DDSHeader &header,
                                       int n, std::istream &in);
 
-  void clear_prepared(int view, PreparedGraphicsObjects *prepared_objects);
+  void clear_prepared(PreparedGraphicsObjects *prepared_objects);
 
   static void consider_downgrade(PNMImage &pnmimage, int num_channels, const std::string &name);
 
@@ -1066,9 +1069,8 @@ protected:
   // conversely keeps a list (a set) of all the Textures that have been
   // prepared there.  When either destructs, it removes itself from the
   // other's list.
-  typedef pmap<int, TextureContext *> Contexts;
-  typedef pmap<PreparedGraphicsObjects *, Contexts> PreparedViews;
-  PreparedViews _prepared_views;
+  typedef pmap<PreparedGraphicsObjects *, TextureContext *> Contexts;
+  Contexts _contexts;
 
   // It is common, when using normal maps, specular maps, gloss maps, and
   // such, to use a file naming convention where the filenames of the special

+ 13 - 7
panda/src/gobj/textureContext.I

@@ -15,10 +15,9 @@
  *
  */
 INLINE TextureContext::
-TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) :
+TextureContext(PreparedGraphicsObjects *pgo, Texture *tex) :
   BufferContext(&pgo->_texture_residency, tex),
-  AdaptiveLruPage(0),
-  _view(view)
+  AdaptiveLruPage(0)
 {
 }
 
@@ -31,15 +30,13 @@ get_texture() const {
 }
 
 /**
- * Returns the specific view of a multiview texture this context represents.
- * In the usual case, with a non-multiview texture, this will be 0.
+ * @deprecated since 1.11.0: always returns 0.
  */
 INLINE int TextureContext::
 get_view() const {
-  return _view;
+  return 0;
 }
 
-
 /**
  * Returns true if the texture properties or image have been modified since
  * the last time mark_loaded() was called.
@@ -103,6 +100,15 @@ get_image_modified_pages(int n) const {
   return get_texture()->get_image_modified_pages(_image_modified, n);
 }
 
+/**
+ * Returns a SparseArray indicating which pages of the texture have been
+ * modified since the last call to mark_loaded().
+ */
+INLINE SparseArray TextureContext::
+get_view_modified_pages(int view, int n) const {
+  return get_texture()->get_view_modified_pages(_image_modified, view, n);
+}
+
 /**
  * Should be called (usually by a derived class) when the on-card size of this
  * object has changed.

+ 2 - 2
panda/src/gobj/textureContext.h

@@ -32,7 +32,7 @@
  */
 class EXPCL_PANDA_GOBJ TextureContext : public BufferContext, public AdaptiveLruPage {
 public:
-  INLINE TextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view);
+  INLINE TextureContext(PreparedGraphicsObjects *pgo, Texture *tex);
 
 PUBLISHED:
   INLINE Texture *get_texture() const;
@@ -49,6 +49,7 @@ PUBLISHED:
   INLINE UpdateSeq get_image_modified() const;
 
   INLINE SparseArray get_image_modified_pages(int n = 0) const;
+  INLINE SparseArray get_view_modified_pages(int view, int n = 0) const;
 
 public:
   INLINE void update_data_size_bytes(size_t new_data_size_bytes);
@@ -60,7 +61,6 @@ public:
   virtual void write(std::ostream &out, int indent_level) const;
 
 private:
-  int _view;
   UpdateSeq _properties_modified;
   UpdateSeq _image_modified;
 

+ 1 - 1
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -148,7 +148,7 @@ public:
   virtual PreparedGraphicsObjects *get_prepared_objects()=0;
 #endif
 
-  virtual TextureContext *prepare_texture(Texture *tex, int view)=0;
+  virtual TextureContext *prepare_texture(Texture *tex)=0;
   virtual bool update_texture(TextureContext *tc, bool force)=0;
   virtual void release_texture(TextureContext *tc)=0;
   virtual void release_textures(const pvector<TextureContext *> &contexts)=0;

+ 3 - 3
panda/src/iphone/ipfreeze.py

@@ -17,8 +17,8 @@ import os
 from direct.dist import FreezeTool
 
 def usage(code, msg = ''):
-    print >> sys.stderr, __doc__
-    print >> sys.stderr, msg
+    print(__doc__, file=sys.stderr)
+    print(msg, file=sys.stderr)
     sys.exit(code)
 
 if __name__ == '__main__':
@@ -30,7 +30,7 @@ if __name__ == '__main__':
 
     try:
         opts, args = getopt.getopt(sys.argv[1:], 'h')
-    except getopt.error, msg:
+    except getopt.error as msg:
         usage(1, msg)
 
     for opt, arg in opts:

+ 1 - 1
panda/src/iphone/provision.py

@@ -34,7 +34,7 @@ command = 'env CODESIGN_ALLOCATE="/Developer/Platforms/iPhoneOS.platform/Develop
     'xcent' : xcent,
     }
 
-print command
+print(command)
 result = os.system(command)
 if result != 0:
     raise StandardError

+ 2 - 0
panda/src/pgraph/CMakeLists.txt

@@ -204,6 +204,8 @@ set(P3PGRAPH_SOURCES
 )
 
 set(P3PGRAPH_IGATEEXT
+  bamFile_ext.cxx
+  bamFile_ext.h
   loaderFileTypeRegistry_ext.cxx
   loaderFileTypeRegistry_ext.h
   nodePathCollection_ext.cxx

+ 4 - 0
panda/src/pgraph/bamFile.h

@@ -74,7 +74,11 @@ PUBLISHED:
   BamReader *get_reader();
   BamWriter *get_writer();
 
+public:
+  EXTENSION(PyObject *get_file_version() const);
+
 PUBLISHED:
+  MAKE_PROPERTY(file_version, get_file_version);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
 

+ 28 - 0
panda/src/pgraph/bamFile_ext.cxx

@@ -0,0 +1,28 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file bamFile_ext.cxx
+ * @author rdb
+ * @date 2023-05-03
+ */
+
+#include "bamFile_ext.h"
+#include "bamWriter_ext.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns the version number of the Bam file currently being written.
+ */
+PyObject *Extension<BamFile>::
+get_file_version() const {
+  return Py_BuildValue("(ii)", _this->get_file_major_ver(),
+                               _this->get_file_minor_ver());
+}
+
+#endif

+ 37 - 0
panda/src/pgraph/bamFile_ext.h

@@ -0,0 +1,37 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file bamFile_ext.h
+ * @author rdb
+ * @date 2023-05-03
+ */
+
+#ifndef BAMFILE_EXT_H
+#define BAMFILE_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "bamFile.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for BamFile, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<BamFile> : public ExtensionBase<BamFile> {
+public:
+  PyObject *get_file_version() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // BAMFILE_EXT_H

+ 1 - 0
panda/src/pgraph/p3pgraph_ext_composite.cxx

@@ -1,3 +1,4 @@
+#include "bamFile_ext.cxx"
 #include "loaderFileTypeRegistry_ext.cxx"
 #include "nodePath_ext.cxx"
 #include "nodePathCollection_ext.cxx"

+ 16 - 3
panda/src/pstatclient/pStatClient_ext.cxx

@@ -180,10 +180,23 @@ make_c_function_collector(PyCFunctionObject *meth) {
     } else {
       // If there's no module name, we need to get it from __module__.
       PyObject *py_mod_name = cls->tp_dict ? PyDict_GetItemString(cls->tp_dict, "__module__") : nullptr;
-      const char *mod_name;
+      const char *mod_name = nullptr;
       if (py_mod_name != nullptr) {
-        mod_name = PyUnicode_AsUTF8(py_mod_name);
-      } else {
+        if (PyUnicode_Check(py_mod_name)) {
+          mod_name = PyUnicode_AsUTF8(py_mod_name);
+        } else {
+          // Might be a descriptor.
+          py_mod_name = PyObject_GetAttrString(meth->m_self, "__module__");
+          if (py_mod_name != nullptr) {
+            if (PyUnicode_Check(py_mod_name)) {
+              mod_name = PyUnicode_AsUTF8(py_mod_name);
+            }
+            Py_DECREF(py_mod_name);
+          }
+          else PyErr_Clear();
+        }
+      }
+      if (mod_name == nullptr) {
         // Is it a built-in, like int or dict?
         PyObject *builtins = PyEval_GetBuiltins();
         if (PyDict_GetItemString(builtins, cls->tp_name) == (PyObject *)cls) {

+ 2 - 0
panda/src/putil/CMakeLists.txt

@@ -125,6 +125,8 @@ set(P3PUTIL_SOURCES
 set(P3PUTIL_IGATEEXT
   bamReader_ext.cxx
   bamReader_ext.h
+  bamWriter_ext.cxx
+  bamWriter_ext.h
   bitArray_ext.cxx
   bitArray_ext.h
   bitArray_ext.I

+ 1 - 1
panda/src/putil/bamReader.cxx

@@ -338,7 +338,7 @@ skip_object() {
  * time to call it.
  *
  * This must be called at least once after reading a particular object via
- * get_object() in order to validate that object.
+ * read_object() in order to validate that object.
  *
  * The return value is true if all objects have been resolved, or false if
  * some objects are still outstanding (in which case you will need to call

+ 4 - 0
panda/src/putil/bamWriter.h

@@ -87,9 +87,13 @@ PUBLISHED:
   INLINE TypedWritable *get_root_node() const;
   INLINE void set_root_node(TypedWritable *root_node);
 
+public:
+  EXTENSION(PyObject *get_file_version() const);
+
 PUBLISHED:
   MAKE_PROPERTY(target, get_target, set_target);
   MAKE_PROPERTY(filename, get_filename);
+  MAKE_PROPERTY(file_version, get_file_version);
   MAKE_PROPERTY(file_endian, get_file_endian);
   MAKE_PROPERTY(file_stdfloat_double, get_file_stdfloat_double);
   MAKE_PROPERTY(file_texture_mode, get_file_texture_mode);

+ 28 - 0
panda/src/putil/bamWriter_ext.cxx

@@ -0,0 +1,28 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file bamWriter_ext.cxx
+ * @author rdb
+ * @date 2023-05-03
+ */
+
+#include "bamWriter_ext.h"
+#include "config_putil.h"
+
+#ifdef HAVE_PYTHON
+
+/**
+ * Returns the version number of the Bam file currently being written.
+ */
+PyObject *Extension<BamWriter>::
+get_file_version() const {
+  return Py_BuildValue("(ii)", _this->get_file_major_ver(),
+                               _this->get_file_minor_ver());
+}
+
+#endif

+ 37 - 0
panda/src/putil/bamWriter_ext.h

@@ -0,0 +1,37 @@
+/**
+ * PANDA 3D SOFTWARE
+ * Copyright (c) Carnegie Mellon University.  All rights reserved.
+ *
+ * All use of this software is subject to the terms of the revised BSD
+ * license.  You should have received a copy of this license along
+ * with this source code in a file named "LICENSE."
+ *
+ * @file bamWriter_ext.h
+ * @author rdb
+ * @date 2023-05-01
+ */
+
+#ifndef BAMWRITER_EXT_H
+#define BAMWRITER_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "bamWriter.h"
+#include "py_panda.h"
+
+/**
+ * This class defines the extension methods for BamWriter, which are called
+ * instead of any C++ methods with the same prototype.
+ */
+template<>
+class Extension<BamWriter> : public ExtensionBase<BamWriter> {
+public:
+  PyObject *get_file_version() const;
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // BAMWRITER_EXT_H

+ 2 - 2
panda/src/putil/copyOnWriteObject.I

@@ -128,7 +128,7 @@ make_cow_copy() {
 template<class Base>
 void CopyOnWriteObj<Base>::
 init_type() {
-#if defined(HAVE_RTTI) && !defined(__EDG__)
+#if defined(HAVE_RTTI) && !defined(__EDG__) && (!defined(__GNUC__) || defined(__GXX_RTTI))
   // If we have RTTI, we can determine the name of the base type.
   std::string base_name = typeid(Base).name();
 #else
@@ -188,7 +188,7 @@ make_cow_copy() {
 template<class Base, class Param1>
 void CopyOnWriteObj1<Base, Param1>::
 init_type() {
-#if defined(HAVE_RTTI) && !defined(__EDG__)
+#if defined(HAVE_RTTI) && !defined(__EDG__) && (!defined(__GNUC__) || defined(__GXX_RTTI))
   // If we have RTTI, we can determine the name of the base type.
   std::string base_name = typeid(Base).name();
 #else

+ 16 - 0
panda/src/putil/datagramOutputFile.cxx

@@ -86,6 +86,22 @@ close() {
   _error = false;
 }
 
+/**
+ * Writes a sequence of bytes to the beginning of the datagram file.  This may
+ * be called any number of times after the file has been opened and before the
+ * first datagram is written.  It may not be called once the first datagram is
+ * written.
+ */
+bool DatagramOutputFile::
+write_header(const vector_uchar &header) {
+  nassertr(_out != nullptr, false);
+  nassertr(!_wrote_first_datagram, false);
+
+  _out->write((const char *)&header[0], header.size());
+  thread_consider_yield();
+  return !_out->fail();
+}
+
 /**
  * Writes a sequence of bytes to the beginning of the datagram file.  This may
  * be called any number of times after the file has been opened and before the

+ 1 - 0
panda/src/putil/datagramOutputFile.h

@@ -38,6 +38,7 @@ PUBLISHED:
 
   void close();
 
+  bool write_header(const vector_uchar &header);
   bool write_header(const std::string &header);
   virtual bool put_datagram(const Datagram &data);
   virtual bool copy_datagram(SubfileInfo &result, const Filename &filename);

+ 1 - 0
panda/src/putil/p3putil_ext_composite.cxx

@@ -1,4 +1,5 @@
 #include "bamReader_ext.cxx"
+#include "bamWriter_ext.cxx"
 #include "bitArray_ext.cxx"
 #include "paramPyObject.cxx"
 #include "pythonCallbackObject.cxx"

+ 6 - 6
panda/src/testbed/test_native_net1.py

@@ -11,7 +11,7 @@ SocketIP.InitNetworkDriver();
 
 addr = SocketAddress()
 addr.setHost("127.0.0.1",8080)
-print addr.getIpPort()
+print(addr.getIpPort())
 
 
 inbound = SocketTCPListen()
@@ -23,16 +23,16 @@ while 1 == 1:
     source = SocketAddress()
     if inbound.GetIncomingConnection(newsession,source) :
         #newsession.SetNonBlocking();
-        print source.getIpPort()
+        print(source.getIpPort())
         newsession.SendData("Hello From the Listener\n\r");
 
         s = newsession.RecvData(10);
-        print s
-        print newsession.GetLastError()
+        print(s)
+        print(newsession.GetLastError())
         if newsession.ErrorIsWouldBlocking(newsession.GetLastError()) :
-            print  "Reading Would Block"
+            print("Reading Would Block")
         else:
-            print "Not A Blocking Error"
+            print("Not A Blocking Error")
 
         newsession.SendData("GoodBy From the Listener\n\r");
         newsession.Close();

+ 3 - 3
panda/src/testbed/test_native_net2.py

@@ -15,7 +15,7 @@ SocketIP.InitNetworkDriver();
 
 addr = SocketAddress()
 addr.setHost("127.0.0.1",6666)
-print addr.getIpPort()
+print(addr.getIpPort())
 
 MyConection = BufferedDatagramConnection(0,4096000,4096000,102400);
 #help(BufferedDatagramConnection)
@@ -42,8 +42,8 @@ dg1.addUint16(54321)
 while 1==1:
     for x in range(200000):
         if  not MyConection.SendMessage(dg1):
-            print "Error Sending Message"
+            print("Error Sending Message")
 
     MyConection.Flush();
     time.sleep(1)
-    print "loop"
+    print("loop")

+ 2 - 2
panda/src/testbed/test_native_net3.py

@@ -15,7 +15,7 @@ SocketIP.InitNetworkDriver();
 
 addr = SocketAddress()
 addr.setHost("127.0.0.1",6666)
-print addr.getIpPort()
+print(addr.getIpPort())
 
 MyConection = BufferedDatagramConnection(0,4096000,4096000,102400);
 #help(BufferedDatagramConnection)
@@ -47,4 +47,4 @@ while 1==1:
 
     MyConection.Flush();
     time.sleep(1)
-    print "loop"
+    print("loop")

+ 1 - 0
panda/src/tinydisplay/tinyCocoaGraphicsWindow.mm

@@ -173,6 +173,7 @@ close_window() {
     CFRelease(swap_buffer._data_provider);
     ZB_close(swap_buffer._frame_buffer);
   }
+  _swap_chain.clear();
 
   CocoaGraphicsWindow::close_window();
 }

+ 148 - 125
panda/src/tinydisplay/tinyGraphicsStateGuardian.cxx

@@ -1354,12 +1354,12 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
 
   tex->setup_2d_texture(w, h, Texture::T_unsigned_byte, Texture::F_rgba);
 
-  TextureContext *tc = tex->prepare_now(view, get_prepared_objects(), this);
+  TextureContext *tc = tex->prepare_now(get_prepared_objects(), this);
   nassertr(tc != nullptr, false);
   TinyTextureContext *gtc = DCAST(TinyTextureContext, tc);
 
   GLTexture *gltex = &gtc->_gltex;
-  if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), 1)) {
+  if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), tex->get_num_views(), 1)) {
     return false;
   }
   LColor border_color = tex->get_border_color();
@@ -1369,7 +1369,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z,
   gltex->border_color.v[2] = border_color[2];
   gltex->border_color.v[3] = border_color[3];
 
-  PIXEL *ip = gltex->levels[0].pixmap + gltex->xsize * gltex->ysize;
+  PIXEL *ip = gltex->views[view].levels[0].pixmap + gltex->xsize * gltex->ysize;
   PIXEL *fo = _c->zb->pbuf + xo + yo * _c->zb->linesize / PSZB;
   for (int y = 0; y < gltex->ysize; ++y) {
     ip -= gltex->xsize;
@@ -1602,7 +1602,7 @@ set_state_and_transform(const RenderState *target,
  * call Texture::prepare().
  */
 TextureContext *TinyGraphicsStateGuardian::
-prepare_texture(Texture *tex, int view) {
+prepare_texture(Texture *tex) {
   switch (tex->get_texture_type()) {
   case Texture::TT_1d_texture:
   case Texture::TT_2d_texture:
@@ -1629,7 +1629,7 @@ prepare_texture(Texture *tex, int view) {
   }
   */
 
-  TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex, view);
+  TinyTextureContext *gtc = new TinyTextureContext(_prepared_objects, tex);
 
   return gtc;
 }
@@ -1652,7 +1652,7 @@ update_texture(TextureContext *tc, bool force) {
 
   GLTexture *gltex = &gtc->_gltex;
 
-  if (gtc->was_image_modified() || gltex->num_levels == 0) {
+  if (gtc->was_image_modified() || gltex->num_views == 0 || gltex->num_levels == 0) {
     // If the texture image was modified, reload the texture.
     Texture *tex = gtc->get_texture();
     bool okflag = upload_texture(gtc, force, tex->uses_mipmaps());
@@ -1678,7 +1678,7 @@ update_texture(TextureContext *tc, bool force) {
  * (and if get_incomplete_render() is true).
  */
 bool TinyGraphicsStateGuardian::
-update_texture(TextureContext *tc, bool force, int stage_index, bool uses_mipmaps) {
+update_texture(TextureContext *tc, int view, bool force, int stage_index, bool uses_mipmaps) {
   if (!update_texture(tc, force)) {
     return false;
   }
@@ -1699,8 +1699,15 @@ update_texture(TextureContext *tc, bool force, int stage_index, bool uses_mipmap
 
   _c->current_textures[stage_index] = gltex;
 
+  if (view < 0) {
+    view = 0;
+  }
+  if (view >= gltex->num_views) {
+    view = gltex->num_views - 1;
+  }
+
   ZTextureDef *texture_def = &_c->zb->current_textures[stage_index];
-  texture_def->levels = gltex->levels;
+  texture_def->levels = gltex->views[view].levels;
   texture_def->s_max = gltex->s_max;
   texture_def->t_max = gltex->t_max;
 
@@ -2155,7 +2162,7 @@ do_issue_texture() {
     nassertv(texture != nullptr);
 
     int view = get_current_tex_view_offset() + stage->get_tex_view_offset();
-    TextureContext *tc = texture->prepare_now(view, _prepared_objects, this);
+    TextureContext *tc = texture->prepare_now(_prepared_objects, this);
     if (tc == nullptr) {
       // Something wrong with this texture; skip it.
       return;
@@ -2165,7 +2172,7 @@ do_issue_texture() {
     const SamplerState &sampler = _target_texture->get_on_sampler(stage);
 
     // Then, turn on the current texture mode.
-    if (!update_texture(tc, false, si, sampler.uses_mipmaps())) {
+    if (!update_texture(tc, view, false, si, sampler.uses_mipmaps())) {
       return;
     }
 
@@ -2418,7 +2425,7 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) {
       << num_levels << ", uses_mipmaps = " << uses_mipmaps << "\n";
   }
 
-  if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), num_levels)) {
+  if (!setup_gltex(gltex, tex->get_x_size(), tex->get_y_size(), tex->get_num_views(), num_levels)) {
     return false;
   }
   LColor border_color = tex->get_border_color();
@@ -2432,78 +2439,80 @@ upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps) {
   int xsize = gltex->xsize;
   int ysize = gltex->ysize;
 
-  for (int level = 0; level < gltex->num_levels; ++level) {
-    ZTextureLevel *dest = &gltex->levels[level];
-
-    if (tex->has_ram_mipmap_image(level)) {
-      switch (tex->get_format()) {
-      case Texture::F_rgb:
-      case Texture::F_rgb5:
-      case Texture::F_rgb8:
-      case Texture::F_rgb12:
-      case Texture::F_rgb332:
-        copy_rgb_image(dest, xsize, ysize, gtc, level);
-        break;
-
-      case Texture::F_rgba:
-      case Texture::F_rgbm:
-      case Texture::F_rgba4:
-      case Texture::F_rgba5:
-      case Texture::F_rgba8:
-      case Texture::F_rgba12:
-      case Texture::F_rgba16:
-      case Texture::F_rgba32:
-        copy_rgba_image(dest, xsize, ysize, gtc, level);
-        break;
-
-      case Texture::F_luminance:
-        copy_lum_image(dest, xsize, ysize, gtc, level);
-        break;
-
-      case Texture::F_red:
-        copy_one_channel_image(dest, xsize, ysize, gtc, level, 0);
-        break;
-
-      case Texture::F_green:
-        copy_one_channel_image(dest, xsize, ysize, gtc, level, 1);
-        break;
-
-      case Texture::F_blue:
-        copy_one_channel_image(dest, xsize, ysize, gtc, level, 2);
-        break;
-
-      case Texture::F_alpha:
-        copy_alpha_image(dest, xsize, ysize, gtc, level);
-        break;
-
-      case Texture::F_luminance_alphamask:
-      case Texture::F_luminance_alpha:
-        copy_la_image(dest, xsize, ysize, gtc, level);
-        break;
-
-      default:
-        tinydisplay_cat.error()
-          << "Unsupported texture format "
-          << tex->get_format() << "!\n";
-        return false;
-      }
-    } else {
-      // Fill the mipmap with a solid color.
-      LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero());
-      scaled *= 255;
-      unsigned int clear = RGBA8_TO_PIXEL((int)scaled[0], (int)scaled[1],
-                                          (int)scaled[2], (int)scaled[3]);
-      unsigned int *dpix = (unsigned int *)dest->pixmap;
-      int pixel_count = xsize * ysize;
-      while (pixel_count-- > 0) {
-        *dpix = clear;
-        ++dpix;
+  for (int view = 0; view < gltex->num_views; ++view) {
+    for (int level = 0; level < gltex->num_levels; ++level) {
+      ZTextureLevel *dest = &gltex->views[view].levels[level];
+
+      if (tex->has_ram_mipmap_image(level)) {
+        switch (tex->get_format()) {
+        case Texture::F_rgb:
+        case Texture::F_rgb5:
+        case Texture::F_rgb8:
+        case Texture::F_rgb12:
+        case Texture::F_rgb332:
+          copy_rgb_image(dest, xsize, ysize, gtc, view, level);
+          break;
+
+        case Texture::F_rgba:
+        case Texture::F_rgbm:
+        case Texture::F_rgba4:
+        case Texture::F_rgba5:
+        case Texture::F_rgba8:
+        case Texture::F_rgba12:
+        case Texture::F_rgba16:
+        case Texture::F_rgba32:
+          copy_rgba_image(dest, xsize, ysize, gtc, view, level);
+          break;
+
+        case Texture::F_luminance:
+          copy_lum_image(dest, xsize, ysize, gtc, view, level);
+          break;
+
+        case Texture::F_red:
+          copy_one_channel_image(dest, xsize, ysize, gtc, view, level, 0);
+          break;
+
+        case Texture::F_green:
+          copy_one_channel_image(dest, xsize, ysize, gtc, view, level, 1);
+          break;
+
+        case Texture::F_blue:
+          copy_one_channel_image(dest, xsize, ysize, gtc, view, level, 2);
+          break;
+
+        case Texture::F_alpha:
+          copy_alpha_image(dest, xsize, ysize, gtc, view, level);
+          break;
+
+        case Texture::F_luminance_alphamask:
+        case Texture::F_luminance_alpha:
+          copy_la_image(dest, xsize, ysize, gtc, view, level);
+          break;
+
+        default:
+          tinydisplay_cat.error()
+            << "Unsupported texture format "
+            << tex->get_format() << "!\n";
+          return false;
+        }
+      } else {
+        // Fill the mipmap with a solid color.
+        LColor scaled = tex->get_clear_color().fmin(LColor(1)).fmax(LColor::zero());
+        scaled *= 255;
+        unsigned int clear = RGBA8_TO_PIXEL((int)scaled[0], (int)scaled[1],
+                                            (int)scaled[2], (int)scaled[3]);
+        unsigned int *dpix = (unsigned int *)dest->pixmap;
+        int pixel_count = xsize * ysize;
+        while (pixel_count-- > 0) {
+          *dpix = clear;
+          ++dpix;
+        }
       }
-    }
 
-    bytecount += xsize * ysize * 4;
-    xsize = max(xsize >> 1, 1);
-    ysize = max(ysize >> 1, 1);
+      bytecount += xsize * ysize * 4;
+      xsize = max(xsize >> 1, 1);
+      ysize = max(ysize >> 1, 1);
+    }
   }
 
   gtc->update_data_size_bytes(bytecount);
@@ -2545,7 +2554,7 @@ upload_simple_texture(TinyTextureContext *gtc) {
       << "loading simple image for " << tex->get_name() << "\n";
   }
 
-  if (!setup_gltex(gltex, width, height, 1)) {
+  if (!setup_gltex(gltex, width, height, 1, 1)) {
     return false;
   }
   LColor border_color = tex->get_border_color();
@@ -2555,7 +2564,7 @@ upload_simple_texture(TinyTextureContext *gtc) {
   gltex->border_color.v[2] = border_color[2];
   gltex->border_color.v[3] = border_color[3];
 
-  ZTextureLevel *dest = &gltex->levels[0];
+  ZTextureLevel *dest = &gltex->views[0].levels[0];
   memcpy(dest->pixmap, image_ptr, image_size);
 
   gtc->mark_loaded();
@@ -2569,7 +2578,7 @@ upload_simple_texture(TinyTextureContext *gtc) {
  * texture is a valid size, false otherwise.
  */
 bool TinyGraphicsStateGuardian::
-setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
+setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_views, int num_levels) {
   if (x_size == 0 || y_size == 0) {
     // A texture without pixels gets turned into a 1x1 texture.
     x_size = 1;
@@ -2588,6 +2597,7 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
     return false;
   }
 
+  num_views = max(num_views, 1);
   num_levels = min(num_levels, MAX_MIPMAP_LEVELS);
 
   gltex->xsize = x_size;
@@ -2601,7 +2611,7 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
   // We allocate one big buffer, large enough to include all the mipmap
   // levels, and index into that buffer for each level.  This cuts down on the
   // number of individual alloc calls we have to make for each texture.
-  int total_bytecount = 0;
+  int view_size = 0;
 
   // Count up the total bytes required for all mipmap levels.
   {
@@ -2609,12 +2619,14 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
     int y = y_size;
     for (int level = 0; level < num_levels; ++level) {
       int bytecount = x * y * 4;
-      total_bytecount += bytecount;
+      view_size += bytecount;
       x = max((x >> 1), 1);
       y = max((y >> 1), 1);
     }
   }
 
+  int total_bytecount = view_size * num_views;
+
   if (gltex->total_bytecount != total_bytecount) {
     if (gltex->allocated_buffer != nullptr) {
       TinyTextureContext::get_class_type().deallocate_array(gltex->allocated_buffer);
@@ -2623,37 +2635,48 @@ setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels) {
     gltex->total_bytecount = total_bytecount;
   }
 
-  char *next_buffer = (char *)gltex->allocated_buffer;
-  char *end_of_buffer = next_buffer + total_bytecount;
-
-  int level = 0;
-  ZTextureLevel *dest = nullptr;
-  while (level < num_levels) {
-    dest = &gltex->levels[level];
-    int bytecount = x_size * y_size * 4;
-    dest->pixmap = (PIXEL *)next_buffer;
-    next_buffer += bytecount;
-    nassertr(next_buffer <= end_of_buffer, false);
+  if (num_views != gltex->num_views) {
+    if (gltex->views != nullptr) {
+      TinyTextureContext::get_class_type().deallocate_array(gltex->views);
+    }
 
-    dest->s_mask = ((1 << (s_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
-    dest->t_mask = ((1 << (t_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
-    dest->s_shift = (ZB_POINT_ST_FRAC_BITS + level);
-    dest->t_shift = (ZB_POINT_ST_FRAC_BITS - s_bits + level);
+    gltex->views = (ZTextureView *)TinyTextureContext::get_class_type().allocate_array(sizeof(ZTextureView) * num_views);
+    gltex->num_views = num_views;
+  }
 
-    x_size = max((x_size >> 1), 1);
-    y_size = max((y_size >> 1), 1);
-    s_bits = max(s_bits - 1, 0);
-    t_bits = max(t_bits - 1, 0);
+  char *next_buffer = (char *)gltex->allocated_buffer;
+  char *end_of_buffer = next_buffer + total_bytecount;
 
-    ++level;
-  }
+  for (int view = 0; view < num_views; ++view) {
+    int level = 0;
+    ZTextureLevel *dest = nullptr;
+    while (level < num_levels) {
+      dest = &gltex->views[view].levels[level];
+      int bytecount = x_size * y_size * 4;
+      dest->pixmap = (PIXEL *)next_buffer;
+      next_buffer += bytecount;
+      nassertr(next_buffer <= end_of_buffer, false);
+
+      dest->s_mask = ((1 << (s_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
+      dest->t_mask = ((1 << (t_bits + ZB_POINT_ST_FRAC_BITS)) - (1 << ZB_POINT_ST_FRAC_BITS)) << level;
+      dest->s_shift = (ZB_POINT_ST_FRAC_BITS + level);
+      dest->t_shift = (ZB_POINT_ST_FRAC_BITS - s_bits + level);
+
+      x_size = max((x_size >> 1), 1);
+      y_size = max((y_size >> 1), 1);
+      s_bits = max(s_bits - 1, 0);
+      t_bits = max(t_bits - 1, 0);
+
+      ++level;
+    }
 
-  // Fill out the remaining mipmap arrays with copies of the last level, so we
-  // don't have to be concerned with running off the end of this array while
-  // scanning out triangles.
-  while (level < MAX_MIPMAP_LEVELS) {
-    gltex->levels[level] = *dest;
-    ++level;
+    // Fill out the remaining mipmap arrays with copies of the last level, so we
+    // don't have to be concerned with running off the end of this array while
+    // scanning out triangles.
+    while (level < MAX_MIPMAP_LEVELS) {
+      gltex->views[view].levels[level] = *dest;
+      ++level;
+    }
   }
 
   return true;
@@ -2682,7 +2705,7 @@ get_tex_shift(int orig_size) {
  * indicated ZTexture pixmap.
  */
 void TinyGraphicsStateGuardian::
-copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
+copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) {
   Texture *tex = gtc->get_texture();
   nassertv(tex->get_num_components() == 1);
   nassertv(tex->get_expected_mipmap_x_size(level) == xsize &&
@@ -2692,7 +2715,7 @@ copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt
   nassertv(!src_image.is_null());
   const unsigned char *src = src_image.p();
   size_t view_size = tex->get_ram_mipmap_view_size(level);
-  src += view_size * gtc->get_view();
+  src += view_size * view;
 
   // Component width, and offset to the high-order byte.
   int cw = tex->get_component_width();
@@ -2720,7 +2743,7 @@ copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt
  * indicated ZTexture pixmap.
  */
 void TinyGraphicsStateGuardian::
-copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
+copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) {
   Texture *tex = gtc->get_texture();
   nassertv(tex->get_num_components() == 1);
 
@@ -2728,7 +2751,7 @@ copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *
   nassertv(!src_image.is_null());
   const unsigned char *src = src_image.p();
   size_t view_size = tex->get_ram_mipmap_view_size(level);
-  src += view_size * gtc->get_view();
+  src += view_size * view;
 
   // Component width, and offset to the high-order byte.
   int cw = tex->get_component_width();
@@ -2756,7 +2779,7 @@ copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *
  * green, or blue) from the texture into the indicated ZTexture pixmap.
  */
 void TinyGraphicsStateGuardian::
-copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level, int channel) {
+copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level, int channel) {
   Texture *tex = gtc->get_texture();
   nassertv(tex->get_num_components() == 1);
 
@@ -2764,7 +2787,7 @@ copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureCon
   nassertv(!src_image.is_null());
   const unsigned char *src = src_image.p();
   size_t view_size = tex->get_ram_mipmap_view_size(level);
-  src += view_size * gtc->get_view();
+  src += view_size * view;
 
   // Component width, and offset to the high-order byte.
   int cw = tex->get_component_width();
@@ -2821,7 +2844,7 @@ copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureCon
  * into the indicated ZTexture pixmap.
  */
 void TinyGraphicsStateGuardian::
-copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
+copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) {
   Texture *tex = gtc->get_texture();
   nassertv(tex->get_num_components() == 2);
 
@@ -2829,7 +2852,7 @@ copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc
   nassertv(!src_image.is_null());
   const unsigned char *src = src_image.p();
   size_t view_size = tex->get_ram_mipmap_view_size(level);
-  src += view_size * gtc->get_view();
+  src += view_size * view;
 
   // Component width, and offset to the high-order byte.
   int cw = tex->get_component_width();
@@ -2858,7 +2881,7 @@ copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc
  * indicated ZTexture pixmap.
  */
 void TinyGraphicsStateGuardian::
-copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
+copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) {
   Texture *tex = gtc->get_texture();
   nassertv(tex->get_num_components() == 3);
 
@@ -2866,7 +2889,7 @@ copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt
   nassertv(!src_image.is_null());
   const unsigned char *src = src_image.p();
   size_t view_size = tex->get_ram_mipmap_view_size(level);
-  src += view_size * gtc->get_view();
+  src += view_size * view;
 
   // Component width, and offset to the high-order byte.
   int cw = tex->get_component_width();
@@ -2895,7 +2918,7 @@ copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gt
  * indicated ZTexture pixmap.
  */
 void TinyGraphicsStateGuardian::
-copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level) {
+copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level) {
   Texture *tex = gtc->get_texture();
   nassertv(tex->get_num_components() == 4);
 
@@ -2903,7 +2926,7 @@ copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *g
   nassertv(!src_image.is_null());
   const unsigned char *src = src_image.p();
   size_t view_size = tex->get_ram_mipmap_view_size(level);
-  src += view_size * gtc->get_view();
+  src += view_size * view;
 
   // Component width, and offset to the high-order byte.
   int cw = tex->get_component_width();

+ 9 - 9
panda/src/tinydisplay/tinyGraphicsStateGuardian.h

@@ -84,9 +84,9 @@ public:
   virtual void set_state_and_transform(const RenderState *state,
                                        const TransformState *transform);
 
-  virtual TextureContext *prepare_texture(Texture *tex, int view);
+  virtual TextureContext *prepare_texture(Texture *tex);
   virtual bool update_texture(TextureContext *tc, bool force);
-  bool update_texture(TextureContext *tc, bool force, int stage_index, bool uses_mipmaps);
+  bool update_texture(TextureContext *tc, int view, bool force, int stage_index, bool uses_mipmaps);
   virtual void release_texture(TextureContext *tc);
 
   virtual void do_issue_light();
@@ -112,15 +112,15 @@ private:
   bool apply_texture(TextureContext *tc);
   bool upload_texture(TinyTextureContext *gtc, bool force, bool uses_mipmaps);
   bool upload_simple_texture(TinyTextureContext *gtc);
-  bool setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_levels);
+  bool setup_gltex(GLTexture *gltex, int x_size, int y_size, int num_views, int num_levels);
   int get_tex_shift(int orig_size);
 
-  static void copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level);
-  static void copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level);
-  static void copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level, int channel);
-  static void copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level);
-  static void copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level);
-  static void copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int level);
+  static void copy_lum_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level);
+  static void copy_alpha_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level);
+  static void copy_one_channel_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level, int channel);
+  static void copy_la_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level);
+  static void copy_rgb_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level);
+  static void copy_rgba_image(ZTextureLevel *dest, int xsize, int ysize, TinyTextureContext *gtc, int view, int level);
 
   void setup_material(GLMaterial *gl_material, const Material *material);
   void do_auto_rescale_normal();

+ 4 - 2
panda/src/tinydisplay/tinyTextureContext.I

@@ -15,9 +15,11 @@
  *
  */
 INLINE TinyTextureContext::
-TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view) :
-  TextureContext(pgo, tex, view)
+TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex) :
+  TextureContext(pgo, tex)
 {
+  _gltex.views = nullptr;
+  _gltex.num_views = 0;
   _gltex.num_levels = 0;
   _gltex.allocated_buffer = nullptr;
   _gltex.total_bytecount = 0;

+ 8 - 0
panda/src/tinydisplay/tinyTextureContext.cxx

@@ -31,6 +31,14 @@ TinyTextureContext::
   } else {
     nassertv(gltex->num_levels == 0);
   }
+  if (gltex->views != nullptr) {
+    nassertv(gltex->num_views != 0);
+    get_class_type().deallocate_array(gltex->views);
+    gltex->views = nullptr;
+    gltex->num_views = 0;
+  } else {
+    nassertv(gltex->num_views == 0);
+  }
 }
 
 /**

+ 1 - 1
panda/src/tinydisplay/tinyTextureContext.h

@@ -24,7 +24,7 @@
  */
 class EXPCL_TINYDISPLAY TinyTextureContext : public TextureContext {
 public:
-  INLINE TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex, int view);
+  INLINE TinyTextureContext(PreparedGraphicsObjects *pgo, Texture *tex);
   ALLOC_DELETED_CHAIN(TinyTextureContext);
 
   ~TinyTextureContext();

+ 1 - 1
panda/src/tinydisplay/zbuffer.h

@@ -252,7 +252,7 @@ int texcoord_mirror_once(int coord, int max_coord);
 /* linesize is in BYTES */
 void ZB_copyFrameBuffer(const ZBuffer *zb,void *buf,int linesize);
 void ZB_copyFrameBufferNoAlpha(const ZBuffer *zb,void *buf,int linesize);
-void ZB_zoomFrameBuffer(ZBuffer *dest, int dest_xmin, int dest_ymin, 
+void ZB_zoomFrameBuffer(ZBuffer *dest, int dest_xmin, int dest_ymin,
                         int dest_xsize, int dest_ysize,
                         const ZBuffer *source, int source_xmin, int source_ymin,
                         int source_xsize, int source_ysize);

+ 6 - 1
panda/src/tinydisplay/zgl.h

@@ -119,9 +119,14 @@ typedef struct GLVertex {
 
 /* textures */
 
+typedef struct ZTextureView {
+  ZTextureLevel levels[MAX_MIPMAP_LEVELS];
+} ZTextureView;
+
 /* The combination of all mipmap levels: one complete texture. */
 typedef struct GLTexture {
-  ZTextureLevel levels[MAX_MIPMAP_LEVELS];
+  ZTextureView *views;
+  int num_views;
   int num_levels;
   int xsize, ysize;
   int s_max, t_max;

+ 2 - 2
panda/src/wgldisplay/wglGraphicsBuffer.cxx

@@ -175,7 +175,7 @@ bind_texture_to_pbuffer() {
         tex->set_format(Texture::F_rgb);
       }
     }
-    TextureContext *tc = tex->prepare_now(0, _gsg->get_prepared_objects(), _gsg);
+    TextureContext *tc = tex->prepare_now(_gsg->get_prepared_objects(), _gsg);
     nassertv(tc != nullptr);
     CLP(TextureContext) *gtc = DCAST(CLP(TextureContext), tc);
     GLenum target = wglgsg->get_texture_target(tex->get_texture_type());
@@ -185,7 +185,7 @@ bind_texture_to_pbuffer() {
       cdataw->_textures[tex_index]._rtm_mode = RTM_copy_texture;
       return;
     }
-    GLP(BindTexture)(target, gtc->_index);
+    GLP(BindTexture)(target, gtc->get_view_index(0));
     if (_fb_properties.is_single_buffered()) {
       wglgsg->_wglBindTexImageARB(_pbuffer, WGL_FRONT_LEFT_ARB);
     } else {

+ 53 - 26
panda/src/windisplay/winGraphicsWindow.cxx

@@ -272,15 +272,34 @@ process_events() {
  */
 void WinGraphicsWindow::
 set_properties_now(WindowProperties &properties) {
-  if (properties.has_fullscreen() && !properties.get_fullscreen() &&
-      is_fullscreen()) {
-    if (do_windowed_switch()) {
-      _properties.set_fullscreen(false);
-      properties.clear_fullscreen();
-    } else {
-      windisplay_cat.warning()
-        << "Switching to windowed mode failed!\n";
+  if (properties.has_fullscreen()) {
+    if (!properties.get_fullscreen() && is_fullscreen()) {
+      if (do_windowed_switch()) {
+        _properties.set_fullscreen(false);
+      } else {
+        windisplay_cat.warning()
+          << "Switching to windowed mode failed!\n";
+      }
     }
+    else if (properties.get_fullscreen() && !is_fullscreen()) {
+      int x_size;
+      int y_size;
+      if (properties.has_size()) {
+        x_size = properties.get_x_size();
+        y_size = properties.get_y_size();
+      } else {
+        x_size = _properties.get_x_size();
+        y_size = _properties.get_y_size();
+      }
+      if (do_fullscreen_switch(x_size, y_size)) {
+        _properties.set_fullscreen(true);
+        properties.clear_size();
+      } else {
+        windisplay_cat.warning()
+          << "Switching to fullscreen mode failed!\n";
+      }
+    }
+    properties.clear_fullscreen();
   }
 
   GraphicsWindow::set_properties_now(properties);
@@ -974,8 +993,8 @@ do_fullscreen_resize(int x_size, int y_size) {
  * Called in the set_properties_now function to switch to fullscreen.
  */
 bool WinGraphicsWindow::
-do_fullscreen_switch() {
-  if (!do_fullscreen_enable()) {
+do_fullscreen_switch(int x_size, int y_size) {
+  if (!do_fullscreen_enable(x_size, y_size)) {
     // Couldn't get fullscreen.
     return false;
   }
@@ -985,17 +1004,21 @@ do_fullscreen_switch() {
   DWORD window_style = make_style(props);
   SetWindowLong(_hWnd, GWL_STYLE, window_style);
 
-  WINDOW_METRICS metrics;
-  bool has_origin;
-  if (!calculate_metrics(true, window_style, metrics, has_origin)){
-    return false;
-  }
-
-  SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, metrics.width, metrics.height,
+  SetWindowPos(_hWnd, HWND_NOTOPMOST, 0, 0, x_size, y_size,
     SWP_FRAMECHANGED | SWP_SHOWWINDOW);
+
+  handle_reshape();
   return true;
 }
 
+/**
+ * Called in the set_properties_now function to switch to fullscreen.
+ */
+bool WinGraphicsWindow::
+do_fullscreen_switch() {
+  return do_fullscreen_switch(_properties.get_x_size(), _properties.get_y_size());
+}
+
 /**
  * Called in the set_properties_now function to switch to windowed mode.
  */
@@ -1248,8 +1271,8 @@ open_graphic_window() {
   // somehow, but I need the window's black background to cover up the desktop
   // during the mode change.
 
-  if (fullscreen){
-    if (!do_fullscreen_enable()){
+  if (fullscreen) {
+    if (!do_fullscreen_enable()) {
       return false;
     }
   }
@@ -1262,7 +1285,7 @@ open_graphic_window() {
  * Not to confuse with do_fullscreen_switch().
  */
 bool WinGraphicsWindow::
-do_fullscreen_enable() {
+do_fullscreen_enable(int x_size, int y_size) {
 
   HWND hDesktopWindow = GetDesktopWindow();
   HDC scrnDC = GetDC(hDesktopWindow);
@@ -1272,8 +1295,8 @@ do_fullscreen_enable() {
   // GetDeviceCaps(scrnDC, VERTRES);
   ReleaseDC(hDesktopWindow, scrnDC);
 
-  DWORD dwWidth = _properties.get_x_size();
-  DWORD dwHeight = _properties.get_y_size();
+  DWORD dwWidth = x_size;
+  DWORD dwHeight = y_size;
   DWORD dwFullScreenBitDepth = cur_bitdepth;
 
   DEVMODE dm;
@@ -1301,12 +1324,16 @@ do_fullscreen_enable() {
   }
 
   _fullscreen_display_mode = dm;
-
-  _properties.set_origin(0, 0);
-  _properties.set_size(dwWidth, dwHeight);
-
   return true;
+}
 
+/**
+ * This is a low-level function that just puts Windows in fullscreen mode.
+ * Not to confuse with do_fullscreen_switch().
+ */
+bool WinGraphicsWindow::
+do_fullscreen_enable() {
+  return do_fullscreen_enable(_properties.get_x_size(), _properties.get_y_size());
 }
 
 /**

+ 2 - 0
panda/src/windisplay/winGraphicsWindow.h

@@ -109,8 +109,10 @@ protected:
   virtual void handle_reshape();
   virtual bool do_fullscreen_resize(int x_size, int y_size);
 
+  bool do_fullscreen_switch(int x_size, int y_size);
   virtual bool do_fullscreen_switch();
   virtual bool do_windowed_switch();
+  bool do_fullscreen_enable(int x_size, int y_size);
   virtual bool do_fullscreen_enable();
   virtual bool do_fullscreen_disable();
 

Some files were not shown because too many files changed in this diff