Browse Source

Merge branch 'master' into cmake

Sam Edwards 11 years ago
parent
commit
62d08ec8bf
100 changed files with 3341 additions and 1179 deletions
  1. 1 0
      contrib/src/ai/aiBehaviors.cxx
  2. 4 3
      direct/src/directscripts/Doxyfile.cxx
  3. 3 3
      direct/src/directscripts/Doxyfile.python
  4. 135 101
      direct/src/filter/CommonFilters.py
  5. 7 11
      direct/src/http/baseincomingset.h
  6. 60 60
      direct/src/http/baseincomingset.i
  7. 64 29
      direct/src/p3d/p3dWrapper.c
  8. 12 3
      direct/src/plugin_installer/make_installer.py
  9. 76 21
      direct/src/plugin_installer/p3d_installer.nsi
  10. 0 70
      direct/src/showbase/PythonUtil.py
  11. 2 1
      direct/src/showbase/ShowBase.py
  12. 6 3
      direct/src/showbase/VFSImporter.py
  13. 4 3
      direct/src/showutil/pfreeze.py
  14. 0 8
      dtool/src/dtoolbase/cmath.I
  15. 20 0
      dtool/src/dtoolutil/executionEnvironment.I
  16. 53 13
      dtool/src/dtoolutil/executionEnvironment.cxx
  17. 3 0
      dtool/src/dtoolutil/executionEnvironment.h
  18. 29 6
      dtool/src/dtoolutil/filename.cxx
  19. 5 0
      dtool/src/dtoolutil/filename.h
  20. 34 2
      dtool/src/interrogate/interfaceMakerPythonNative.cxx
  21. 85 1
      dtool/src/interrogate/typeManager.cxx
  22. 4 0
      dtool/src/interrogate/typeManager.h
  23. 6 0
      dtool/src/parser-inc/Python.h
  24. 2 0
      dtool/src/parser-inc/android/asset_manager.h
  25. 39 0
      dtool/src/parser-inc/jni.h
  26. 8 0
      dtool/src/pystub/pystub.cxx
  27. 80 62
      makepanda/makepanda.py
  28. 124 71
      makepanda/makepandacore.py
  29. 30 2
      panda/src/android/android_main.cxx
  30. 11 2
      panda/src/androiddisplay/androidGraphicsWindow.cxx
  31. 2 0
      panda/src/display/Sources.pp
  32. 4 4
      panda/src/display/displayRegion.I
  33. 130 113
      panda/src/display/graphicsEngine.cxx
  34. 52 3
      panda/src/display/graphicsStateGuardian.I
  35. 213 39
      panda/src/display/graphicsStateGuardian.cxx
  36. 41 7
      panda/src/display/graphicsStateGuardian.h
  37. 61 0
      panda/src/display/pStatGPUTimer.I
  38. 67 0
      panda/src/display/pStatGPUTimer.h
  39. 8 2
      panda/src/display/standardMunger.cxx
  40. 1 1
      panda/src/dxgsg8/Sources.pp
  41. 2 2
      panda/src/dxgsg8/wdxGraphicsWindow8.cxx
  42. 1 1
      panda/src/dxgsg9/Sources.pp
  43. 77 57
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  44. 5 5
      panda/src/dxgsg9/dxGraphicsStateGuardian9.h
  45. 87 58
      panda/src/dxgsg9/dxTextureContext9.cxx
  46. 4 4
      panda/src/dxgsg9/wdxGraphicsPipe9.cxx
  47. 14 14
      panda/src/dxgsg9/wdxGraphicsWindow9.cxx
  48. 1 1
      panda/src/express/virtualFileMountAndroidAsset.I
  49. 1 1
      panda/src/express/virtualFileMountAndroidAsset.cxx
  50. 1 0
      panda/src/glesgsg/glesgsg.h
  51. 12 12
      panda/src/glstuff/glCgShaderContext_src.cxx
  52. 34 7
      panda/src/glstuff/glGraphicsBuffer_src.cxx
  53. 14 9
      panda/src/glstuff/glGraphicsBuffer_src.h
  54. 9 5
      panda/src/glstuff/glGraphicsStateGuardian_src.I
  55. 361 107
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  56. 58 33
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  57. 14 0
      panda/src/glstuff/glLatencyQueryContext_src.I
  58. 54 0
      panda/src/glstuff/glLatencyQueryContext_src.cxx
  59. 57 0
      panda/src/glstuff/glLatencyQueryContext_src.h
  60. 28 19
      panda/src/glstuff/glShaderContext_src.cxx
  61. 10 1
      panda/src/glstuff/glTextureContext_src.cxx
  62. 28 0
      panda/src/glstuff/glTimerQueryContext_src.I
  63. 104 0
      panda/src/glstuff/glTimerQueryContext_src.cxx
  64. 68 0
      panda/src/glstuff/glTimerQueryContext_src.h
  65. 11 1
      panda/src/glstuff/glmisc_src.cxx
  66. 1 0
      panda/src/glstuff/glmisc_src.h
  67. 2 0
      panda/src/glstuff/glstuff_src.cxx
  68. 2 0
      panda/src/glstuff/glstuff_src.h
  69. 8 3
      panda/src/glstuff/panda_glext.h
  70. 4 0
      panda/src/gobj/Sources.pp
  71. 5 3
      panda/src/gobj/config_gobj.cxx
  72. 3 1
      panda/src/gobj/geomEnums.h
  73. 51 2
      panda/src/gobj/geomLinestrips.cxx
  74. 4 0
      panda/src/gobj/geomLinestrips.h
  75. 52 1
      panda/src/gobj/geomPrimitive.I
  76. 209 75
      panda/src/gobj/geomPrimitive.cxx
  77. 11 4
      panda/src/gobj/geomPrimitive.h
  78. 16 4
      panda/src/gobj/geomTristrips.cxx
  79. 1 0
      panda/src/gobj/geomTristrips.h
  80. 4 0
      panda/src/gobj/internalName.cxx
  81. 20 4
      panda/src/gobj/internalName.h
  82. 87 0
      panda/src/gobj/internalName_ext.cxx
  83. 44 0
      panda/src/gobj/internalName_ext.h
  84. 3 0
      panda/src/gobj/p3gobj_composite1.cxx
  85. 1 3
      panda/src/gobj/p3gobj_composite2.cxx
  86. 6 6
      panda/src/gobj/shader.I
  87. 2 2
      panda/src/gobj/shader.h
  88. 26 0
      panda/src/gobj/timerQueryContext.I
  89. 35 0
      panda/src/gobj/timerQueryContext.cxx
  90. 60 0
      panda/src/gobj/timerQueryContext.h
  91. 3 6
      panda/src/gsgbase/graphicsStateGuardianBase.h
  92. 7 2
      panda/src/net/connectionManager.cxx
  93. 39 37
      panda/src/pgraph/cullableObject.cxx
  94. 1 1
      panda/src/pgraph/lightRampAttrib.cxx
  95. 1 1
      panda/src/pgraph/transformState.cxx
  96. 3 4
      panda/src/pgraphnodes/shaderGenerator.cxx
  97. 11 0
      panda/src/pstatclient/config_pstats.cxx
  98. 1 0
      panda/src/pstatclient/config_pstats.h
  99. 72 37
      panda/src/pstatclient/pStatClient.cxx
  100. 5 2
      panda/src/pstatclient/pStatClient.h

+ 1 - 0
contrib/src/ai/aiBehaviors.cxx

@@ -1184,6 +1184,7 @@ string AIBehaviors::behavior_status(string ai_type) {
 
       default:
         cout<<"Invalid value!"<<endl;
+        return "invalid";
     }
   }
 

+ 4 - 3
direct/src/directscripts/Doxyfile.cxx

@@ -880,13 +880,13 @@ HTML_STYLESHEET        = $(DOXYGEN_HTML_STYLESHEET)
 # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. 
 # The allowed range is 0 to 359.
 
-HTML_COLORSTYLE_HUE    = 220
+HTML_COLORSTYLE_HUE    = 228
 
 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of 
 # the colors in the HTML output. For a value of 0 the output will use 
 # grayscales only. A value of 255 will produce the most vivid colors.
 
-HTML_COLORSTYLE_SAT    = 100
+HTML_COLORSTYLE_SAT    = 99
 
 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to 
 # the luminance component of the colors in the HTML output. Values below 
@@ -895,7 +895,7 @@ HTML_COLORSTYLE_SAT    = 100
 # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, 
 # and 100 does not change the gamma.
 
-HTML_COLORSTYLE_GAMMA  = 80
+HTML_COLORSTYLE_GAMMA  = 56
 
 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 
 # page will contain the date and time when the page was generated. Setting 
@@ -1451,6 +1451,7 @@ INCLUDE_FILE_PATTERNS  =
 PREDEFINED             = TVOLATILE= \
                          INLINE=inline \
                          PUBLISHED=public \
+                         protected=private \
                          INLINE_LINMATH=inline \
                          INLINE_MATHUTIL=inline \
                          EXTENSION(x)= \

+ 3 - 3
direct/src/directscripts/Doxyfile.python

@@ -863,13 +863,13 @@ HTML_STYLESHEET        = $(DOXYGEN_HTML_STYLESHEET)
 # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. 
 # The allowed range is 0 to 359.
 
-HTML_COLORSTYLE_HUE    = 220
+HTML_COLORSTYLE_HUE    = 228
 
 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of 
 # the colors in the HTML output. For a value of 0 the output will use 
 # grayscales only. A value of 255 will produce the most vivid colors.
 
-HTML_COLORSTYLE_SAT    = 100
+HTML_COLORSTYLE_SAT    = 99
 
 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to 
 # the luminance component of the colors in the HTML output. Values below 
@@ -878,7 +878,7 @@ HTML_COLORSTYLE_SAT    = 100
 # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, 
 # and 100 does not change the gamma.
 
-HTML_COLORSTYLE_GAMMA  = 80
+HTML_COLORSTYLE_GAMMA  = 56
 
 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML 
 # page will contain the date and time when the page was generated. Setting 

+ 135 - 101
direct/src/filter/CommonFilters.py

@@ -20,21 +20,17 @@ from pandac.PandaModules import Point3, Vec3, Vec4, Point2
 from pandac.PandaModules import NodePath, PandaNode
 from pandac.PandaModules import Filename
 from pandac.PandaModules import AuxBitplaneAttrib
-from pandac.PandaModules import RenderState, Texture, Shader
+from pandac.PandaModules import RenderState, Texture, Shader, ATSNone
 import sys,os
 
 CARTOON_BODY="""
 float4 cartoondelta = k_cartoonseparation * texpix_txaux.xwyw;
-float4 cartoon_p0 = l_texcoordN + cartoondelta.xyzw;
-float4 cartoon_c0 = tex2D(k_txaux, cartoon_p0.xy);
-float4 cartoon_p1 = l_texcoordN - cartoondelta.xyzw;
-float4 cartoon_c1 = tex2D(k_txaux, cartoon_p1.xy);
-float4 cartoon_p2 = l_texcoordN + cartoondelta.wzyx;
-float4 cartoon_c2 = tex2D(k_txaux, cartoon_p2.xy);
-float4 cartoon_p3 = l_texcoordN - cartoondelta.wzyx;
-float4 cartoon_c3 = tex2D(k_txaux, cartoon_p3.xy);
-float4 cartoon_mx = max(cartoon_c0,max(cartoon_c1,max(cartoon_c2,cartoon_c3)));
-float4 cartoon_mn = min(cartoon_c0,min(cartoon_c1,min(cartoon_c2,cartoon_c3)));
+float4 cartoon_c0 = tex2D(k_txaux, %(texcoord)s + cartoondelta.xy);
+float4 cartoon_c1 = tex2D(k_txaux, %(texcoord)s - cartoondelta.xy);
+float4 cartoon_c2 = tex2D(k_txaux, %(texcoord)s + cartoondelta.wz);
+float4 cartoon_c3 = tex2D(k_txaux, %(texcoord)s - cartoondelta.wz);
+float4 cartoon_mx = max(cartoon_c0, max(cartoon_c1, max(cartoon_c2, cartoon_c3)));
+float4 cartoon_mn = min(cartoon_c0, min(cartoon_c1, min(cartoon_c2, cartoon_c3)));
 float cartoon_thresh = saturate(dot(cartoon_mx - cartoon_mn, float4(3,3,0,0)) - 0.5);
 o_color = lerp(o_color, k_cartooncolor, cartoon_thresh);
 """
@@ -125,7 +121,6 @@ class CommonFilters:
           self.task = None
 
     def reconfigure(self, fullrebuild, changed):
-
         """ Reconfigure is called whenever any configuration change is made. """
 
         configuration = self.configuration
@@ -138,36 +133,46 @@ class CommonFilters:
                 return
 
             auxbits = 0
-            needtex = {}
-            needtex["color"] = True
+            needtex = set(["color"])
+            needtexcoord = set(["color"])
+
             if ("CartoonInk" in configuration):
-                needtex["aux"] = True
+                needtex.add("aux")
                 auxbits |= AuxBitplaneAttrib.ABOAuxNormal
+                needtexcoord.add("aux")
+
             if ("AmbientOcclusion" in configuration):
-                needtex["depth"] = True
-                needtex["ssao0"] = True
-                needtex["ssao1"] = True
-                needtex["ssao2"] = True
-                needtex["aux"] = True
+                needtex.add("depth")
+                needtex.add("ssao0")
+                needtex.add("ssao1")
+                needtex.add("ssao2")
+                needtex.add("aux")
                 auxbits |= AuxBitplaneAttrib.ABOAuxNormal
+                needtexcoord.add("ssao2")
+
             if ("BlurSharpen" in configuration):
-                needtex["blur0"] = True
-                needtex["blur1"] = True
+                needtex.add("blur0")
+                needtex.add("blur1")
+                needtexcoord.add("blur1")
+
             if ("Bloom" in configuration):
-                needtex["bloom0"] = True
-                needtex["bloom1"] = True
-                needtex["bloom2"] = True
-                needtex["bloom3"] = True
+                needtex.add("bloom0")
+                needtex.add("bloom1")
+                needtex.add("bloom2")
+                needtex.add("bloom3")
                 auxbits |= AuxBitplaneAttrib.ABOGlow
+                needtexcoord.add("bloom3")
+
             if ("ViewGlow" in configuration):
                 auxbits |= AuxBitplaneAttrib.ABOGlow
+
             if ("VolumetricLighting" in configuration):
                 needtex[configuration["VolumetricLighting"].source] = True
+
             for tex in needtex:
-                self.textures[tex] = Texture("scene-"+tex)
+                self.textures[tex] = Texture("scene-" + tex)
                 self.textures[tex].setWrapU(Texture.WMClamp)
                 self.textures[tex].setWrapV(Texture.WMClamp)
-                needtexpix = True
 
             self.finalQuad = self.manager.renderSceneInto(textures = self.textures, auxbits=auxbits)
             if (self.finalQuad == None):
@@ -228,97 +233,112 @@ class CommonFilters:
                 self.bloom[3].setShaderInput("src", bloom2)
                 self.bloom[3].setShader(self.loadShader("filter-bloomy.sha"))
 
+            texcoords = {}
+            texcoordPadding = {}
+
+            for tex in needtexcoord:
+                if self.textures[tex].getAutoTextureScale() != ATSNone or \
+                                           "HalfPixelShift" in configuration:
+                    texcoords[tex] = "l_texcoord_" + tex
+                    texcoordPadding["l_texcoord_" + tex] = tex
+                else:
+                    # Share unpadded texture coordinates.
+                    texcoords[tex] = "l_texcoord"
+                    texcoordPadding["l_texcoord"] = None
+
+            texcoordSets = list(enumerate(texcoordPadding.keys()))
+
             text = "//Cg\n"
             text += "void vshader(float4 vtx_position : POSITION,\n"
-            text += " out float4 l_position : POSITION,\n"
-            text += " uniform float4 texpad_txcolor,\n"
-            text += " uniform float4 texpix_txcolor,\n"
-            text += " out float4 l_texcoordC : TEXCOORD0,\n"
-            if ("CartoonInk" in configuration):
-                text += " uniform float4 texpad_txaux,\n"
-                text += " uniform float4 texpix_txaux,\n"
-                text += " out float4 l_texcoordN : TEXCOORD1,\n"
-            if ("Bloom" in configuration):
-                text += " uniform float4 texpad_txbloom3,\n"
-                text += " out float4 l_texcoordB : TEXCOORD2,\n"
-            if ("BlurSharpen" in configuration):
-                text += " uniform float4 texpad_txblur1,\n"
-                text += " out float4 l_texcoordBS : TEXCOORD3,\n"
-            if ("AmbientOcclusion" in configuration):
-                text += " uniform float4 texpad_txssao2,\n"
-                text += " out float4 l_texcoordAO : TEXCOORD4,\n"
-            text += " uniform float4x4 mat_modelproj)\n"
+            text += "  out float4 l_position : POSITION,\n"
+
+            for texcoord, padTex in texcoordPadding.items():
+                if padTex is not None:
+                    text += "  uniform float4 texpad_tx%s,\n" % (padTex)
+                    if ("HalfPixelShift" in configuration):
+                        text += "  uniform float4 texpix_tx%s,\n" % (padTex)
+
+            for i, name in texcoordSets:
+                text += "  out float2 %s : TEXCOORD%d,\n" % (name, i)
+
+            text += "  uniform float4x4 mat_modelproj)\n"
             text += "{\n"
-            text += " l_position=mul(mat_modelproj, vtx_position);\n"
-            text += " l_texcoordC=(vtx_position.xzxz * texpad_txcolor) + texpad_txcolor;\n"
-            if ("CartoonInk" in configuration):
-                text += " l_texcoordN=(vtx_position.xzxz * texpad_txaux) + texpad_txaux;\n"
-            if ("Bloom" in configuration):
-                text += " l_texcoordB=(vtx_position.xzxz * texpad_txbloom3) + texpad_txbloom3;\n"
-            if ("BlurSharpen" in configuration):
-                text += " l_texcoordBS=(vtx_position.xzxz * texpad_txblur1) + texpad_txblur1;\n"
-            if ("AmbientOcclusion" in configuration):
-                text += " l_texcoordAO=(vtx_position.xzxz * texpad_txssao2) + texpad_txssao2;\n"
-            if ("HalfPixelShift" in configuration):
-                text += " l_texcoordC+=texpix_txcolor*0.5;\n"
-                if ("CartoonInk" in configuration):
-                    text += " l_texcoordN+=texpix_txaux*0.5;\n"
+            text += "  l_position = mul(mat_modelproj, vtx_position);\n"
+
+            for texcoord, padTex in texcoordPadding.items():
+                if padTex is None:
+                    text += "  %s = vtx_position.xz * float2(0.5, 0.5) + float2(0.5, 0.5);\n" % (texcoord)
+                else:
+                    text += "  %s = (vtx_position.xz * texpad_tx%s.xy) + texpad_tx%s.xy;\n" % (texcoord, padTex, padTex)
+
+                    if ("HalfPixelShift" in configuration):
+                        text += "  %s += texpix_tx%s.xy * 0.5;\n" % (texcoord, padTex)
+
             text += "}\n"
 
             text += "void fshader(\n"
-            text += "float4 l_texcoordC : TEXCOORD0,\n"
-            text += "uniform float4 texpix_txcolor,\n"
-            if ("CartoonInk" in configuration):
-                text += "float4 l_texcoordN : TEXCOORD1,\n"
-                text += "uniform float4 texpix_txaux,\n"
-            if ("Bloom" in configuration):
-                text += "float4 l_texcoordB : TEXCOORD2,\n"
-            if ("BlurSharpen" in configuration):
-                text += "float4 l_texcoordBS : TEXCOORD3,\n"
-                text += "uniform float4 k_blurval,\n"
-            if ("AmbientOcclusion" in configuration):
-                text += "float4 l_texcoordAO : TEXCOORD4,\n"
+
+            for i, name in texcoordSets:
+                text += "  float2 %s : TEXCOORD%d,\n" % (name, i)
+
             for key in self.textures:
-                text += "uniform sampler2D k_tx" + key + ",\n"
+                text += "  uniform sampler2D k_tx" + key + ",\n"
+
             if ("CartoonInk" in configuration):
-                text += "uniform float4 k_cartoonseparation,\n"
-                text += "uniform float4 k_cartooncolor,\n"
+                text += "  uniform float4 k_cartoonseparation,\n"
+                text += "  uniform float4 k_cartooncolor,\n"
+                text += "  uniform float4 texpix_txaux,\n"
+
+            if ("BlurSharpen" in configuration):
+                text += "  uniform float4 k_blurval,\n"
+
             if ("VolumetricLighting" in configuration):
-                text += "uniform float4 k_casterpos,\n"
-                text += "uniform float4 k_vlparams,\n"
-            text += "out float4 o_color : COLOR)\n"
+                text += "  uniform float4 k_casterpos,\n"
+                text += "  uniform float4 k_vlparams,\n"
+            text += "  out float4 o_color : COLOR)\n"
             text += "{\n"
-            text += " o_color = tex2D(k_txcolor, l_texcoordC.xy);\n"
+            text += "  o_color = tex2D(k_txcolor, %s);\n" % (texcoords["color"])
             if ("CartoonInk" in configuration):
-                text += CARTOON_BODY
+                text += CARTOON_BODY % {"texcoord" : texcoords["aux"]}
             if ("AmbientOcclusion" in configuration):
-                text += "o_color *= tex2D(k_txssao2, l_texcoordAO.xy).r;\n"
+                text += "  o_color *= tex2D(k_txssao2, %s).r;\n" % (texcoords["ssao2"])
             if ("BlurSharpen" in configuration):
-                text += " o_color = lerp(tex2D(k_txblur1, l_texcoordBS.xy), o_color, k_blurval.x);\n"
+                text += "  o_color = lerp(tex2D(k_txblur1, %s), o_color, k_blurval.x);\n" % (texcoords["blur1"])
             if ("Bloom" in configuration):
-                text += "o_color = saturate(o_color);\n";
-                text += "float4 bloom = 0.5*tex2D(k_txbloom3, l_texcoordB.xy);\n"
-                text += "o_color = 1-((1-bloom)*(1-o_color));\n"
+                text += "  o_color = saturate(o_color);\n";
+                text += "  float4 bloom = 0.5 * tex2D(k_txbloom3, %s);\n" % (texcoords["bloom3"])
+                text += "  o_color = 1-((1-bloom)*(1-o_color));\n"
             if ("ViewGlow" in configuration):
-                text += "o_color.r = o_color.a;\n"
+                text += "  o_color.r = o_color.a;\n"
             if ("VolumetricLighting" in configuration):
-                text += "float decay = 1.0f;\n"
-                text += "float2 curcoord = l_texcoordC.xy;\n"
-                text += "float2 lightdir = curcoord - k_casterpos.xy;\n"
-                text += "lightdir *= k_vlparams.x;\n"
-                text += "half4 sample = tex2D(k_txcolor, curcoord);\n"
-                text += "float3 vlcolor = sample.rgb * sample.a;\n"
-                text += "for (int i = 0; i < %s; i++) {\n" % (int(configuration["VolumetricLighting"].numsamples))
-                text += "  curcoord -= lightdir;\n"
-                text += "  sample = tex2D(k_tx%s, curcoord);\n" % (configuration["VolumetricLighting"].source)
-                text += "  sample *= sample.a * decay;//*weight\n"
-                text += "  vlcolor += sample.rgb;\n"
-                text += "  decay *= k_vlparams.y;\n"
-                text += "}\n"
-                text += "o_color += float4(vlcolor * k_vlparams.z, 1);\n"
+                text += "  float decay = 1.0f;\n"
+                text += "  float2 curcoord = %s;\n" % (texcoords["color"])
+                text += "  float2 lightdir = curcoord - k_casterpos.xy;\n"
+                text += "  lightdir *= k_vlparams.x;\n"
+                text += "  half4 sample = tex2D(k_txcolor, curcoord);\n"
+                text += "  float3 vlcolor = sample.rgb * sample.a;\n"
+                text += "  for (int i = 0; i < %s; i++) {\n" % (int(configuration["VolumetricLighting"].numsamples))
+                text += "    curcoord -= lightdir;\n"
+                text += "    sample = tex2D(k_tx%s, curcoord);\n" % (configuration["VolumetricLighting"].source)
+                text += "    sample *= sample.a * decay;//*weight\n"
+                text += "    vlcolor += sample.rgb;\n"
+                text += "    decay *= k_vlparams.y;\n"
+                text += "  }\n"
+                text += "  o_color += float4(vlcolor * k_vlparams.z, 1);\n"
+
+            if ("GammaAdjust" in configuration):
+                gamma = configuration["GammaAdjust"]
+                if gamma == 0.5:
+                    text += "  o_color.rgb = sqrt(o_color.rgb);\n"
+                elif gamma == 2.0:
+                    text += "  o_color.rgb *= o_color.rgb;\n"
+                elif gamma != 1.0:
+                    text += "  o_color.rgb = pow(o_color.rgb, %ff);\n" % (gamma)
+
             if ("Inverted" in configuration):
-                text += "o_color = float4(1, 1, 1, 1) - o_color;\n"
+                text += "  o_color = float4(1, 1, 1, 1) - o_color;\n"
             text += "}\n"
+            print text
             
             self.finalQuad.setShader(Shader.make(text))
             for tex in self.textures:
@@ -504,3 +524,17 @@ class CommonFilters:
             return self.reconfigure(True, "AmbientOcclusion")
         return True
 
+    def setGammaAdjust(self, gamma):
+        """ Applies additional gamma correction to the image.  1.0 = no correction. """
+        old_gamma = self.configuration.get("GammaAdjust", 1.0)
+        if old_gamma != gamma:
+            self.configuration["GammaAdjust"] = gamma
+            return self.reconfigure(True, "GammaAdjust")
+        return True
+
+    def delGammaAdjust(self):
+        if ("GammaAdjust" in self.configuration):
+            old_gamma = self.configuration["GammaAdjust"]
+            del self.configuration["GammaAdjust"]
+            return self.reconfigure((old_gamma != 1.0), "GammaAdjust")
+        return True

+ 7 - 11
direct/src/http/baseincomingset.h

@@ -4,10 +4,10 @@
 #include <list>
 #include "socket_base.h"
 
-enum CloseState 
+enum CloseState
 {
-        ConnectionDoNotClose,
-        ConnectionDoClose
+    ConnectionDoNotClose,
+    ConnectionDoClose
 };
 // RHH
 ////////////////////////////////////////////////////////////////////
@@ -20,15 +20,15 @@ enum CloseState
 //
 //  The general operation if get connection..
 //          do you have a message
-//          process message 
+//          process message
 //          go back to do you have a message or close connection
 //
 //
 ////////////////////////////////////////////////////////////////////
 template < class _INCLASS1,class _IN_LISTEN, class MESSAGE_READER_BUF, class MESSAGE_READER_UPPASS> class BaseIncomingSet : public  std::list<_INCLASS1 *>
 {
-        typedef std::list<_INCLASS1 *> BaseClass;
-        typedef TYPENAME BaseClass::iterator iterator;
+    typedef std::list<_INCLASS1 *> BaseClass;
+    typedef TYPENAME BaseClass::iterator iterator;
     _IN_LISTEN                  _Listener;
 
     inline void AddFromListener(void);
@@ -39,7 +39,7 @@ public:
 
 //  typedef typename BaseIncomingSet<_INCLASS1, _IN_LISTEN, MESSAGE_READER_BUF, MESSAGE_READER_UPPASS>::LinkNode LinkNode;
 
-//  typedef SentDblLinkListNode_Gm   SentDblLinkListNode_Gm; 
+//  typedef SentDblLinkListNode_Gm   SentDblLinkListNode_Gm;
     inline BaseIncomingSet(void);
     inline BaseIncomingSet(BaseIncomingSet &in);
     virtual ~BaseIncomingSet();
@@ -50,10 +50,6 @@ public:
     virtual CloseState ProcessNewConnection(SOCKET  socket);
     inline  void AddToFDSet(Socket_fdset &set);
 
-    
-
-
-
 //  inline  LinkNode *          GetRoot(void) {  return &this->sentenal; };
     BaseIncomingSet &operator=( BaseIncomingSet &inval);
     void Reset();

+ 60 - 60
direct/src/http/baseincomingset.i

@@ -1,19 +1,19 @@
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::AddFromListener
 // Description      : Read incoming connections off the listener
-//  
-// Return type      : inline void 
+//
+// Return type      : inline void
 // Argument         : void
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::AddFromListener(void)
 {
     Socket_Address  Addr1;
     SOCKET          newsck;
-    
-    
+
+
     while(_Listener.GetIncomingConnection(newsck,Addr1) == true)
-    {   
+    {
         CloseState cl= ProcessNewConnection(newsck);
         if(cl == ConnectionDoNotClose)
         {
@@ -22,52 +22,52 @@ inline void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READ
         }
         else
             DO_CLOSE(newsck);
-    }       
-        
+    }
+
 }
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::PumpReader
 // Description      : Tries to read a record off the incoming socket
-//  
-// Return type      : inline void 
+//
+// Return type      : inline void
 // Argument         : Time_Clock  currentTime
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline int BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::PumpReader(Time_Clock  &currentTime)
-{       
+{
     MESSAGE_READER_BUF      message;
-    
+
     iterator lpNext, lp;
     for (lpNext  = lp = BaseClass::begin(); lp != BaseClass::end() ; lp = lpNext)
     {
-        lpNext++;   
-        
+        lpNext++;
+
         int ans = (*lp)->ReadIt(message, sizeof(message),currentTime);
         if(ans < 0)
         {
             delete *lp;
-            erase(lp);
-        }               
+            this->erase(lp);
+        }
         if(ans > 0)
         {
             CloseState cs = (*lp)->ProcessMessage(message,currentTime);
             if( cs == ConnectionDoClose)
             {
                 delete *lp;
-                erase(lp);
+                this->erase(lp);
             }
         }
-    }   
-    return 0;           
+    }
+    return 0;
 }
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::AddAConection
 // Description      : Adds a member to the base container
-//  
-// Return type      : inline void 
+//
+// Return type      : inline void
 // Argument         : _INCLASS1 * newt
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::AddAConection(_INCLASS1 * newt)
 {
     push_back(newt);
@@ -75,17 +75,17 @@ inline void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READ
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::BaseIncomingSet
 // Description      :  core constructor
-//  
-// Return type      : inline 
+//
+// Return type      : inline
 // Argument         : void
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::BaseIncomingSet(void)
 {
-    
+
 }
 
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::BaseIncomingSet(BaseIncomingSet &in)
 {
 
@@ -93,10 +93,10 @@ BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::~BaseIncomingSet
 // Description      : The Destructot .. will delete all members.. ??
-//  
-// Return type      : 
+//
+// Return type      :
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::~BaseIncomingSet()
 {
     for(iterator ii = BaseClass::begin(); ii != BaseClass::end(); ii++)
@@ -105,76 +105,76 @@ BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::
 ////////////////////////////////////////////////////////////////////
 // Function name    : & BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::GetListener
 // Description      : Retyurns a pointer to the listener in this class
-//  
-// Return type      : inline _IN_LISTEN 
+//
+// Return type      : inline _IN_LISTEN
 // Argument         : void
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
-inline _IN_LISTEN & BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::GetListener(void) 
-{ 
-    return this->Listener; 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
+inline _IN_LISTEN & BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::GetListener(void)
+{
+    return this->Listener;
 };
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::init
 // Description      : the second part of the 2 phase power up.. Opends the listener
-//  
-// Return type      : inline bool 
+//
+// Return type      : inline bool
 // Argument         : Socket_Address &WhereFrom
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline bool BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::init(Socket_Address &WhereFrom)
 {
     if(_Listener.OpenForListen(WhereFrom,true) != true)
-        return false;       
+        return false;
     _Listener.SetNonBlocking();
     return true;
 }
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::PumpAll
 // Description      : THis is the polled interface to this class
-//  
-// Return type      : inline void 
+//
+// Return type      : inline void
 // Argument         : Time_Clock  &currentTime
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::PumpAll(Time_Clock  &currentTime)
 {
     PumpReader(currentTime); // I MOVED ORDER TO FINE TUNE THE READ OPERATIONS
-    AddFromListener();       
+    AddFromListener();
 }
 
 ////////////////////////////////////////////////////////////////////
 // Function name    : BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::ProcessNewConnection
-// Description      :  this is the vertual function call when a new connection is created 
+// Description      :  this is the vertual function call when a new connection is created
 //                      manly here for loging if needed...
-//  
-// Return type      : CloseState 
+//
+// Return type      : CloseState
 // Argument         : SOCKET  socket
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 CloseState BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::ProcessNewConnection(SOCKET  socket)
 {
     return ConnectionDoNotClose;
 }
 ////////////////////////////////////////////////////////////////////
 // Function name    : void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::AddToFDSet
-// Description      : Add LIstener and Client to the FD set for polled reading 
-//  
-// Return type      : inline  
+// Description      : Add LIstener and Client to the FD set for polled reading
+//
+// Return type      : inline
 // Argument         : Socket_Selector &set
 ////////////////////////////////////////////////////////////////////
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline  void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::AddToFDSet(Socket_fdset &set1)
-{   
+{
     if(_Listener.Active())
         set1.setForSocket(_Listener);
     iterator lp;
-    
+
     for (lp = BaseClass::begin(); lp != BaseClass::end(); lp = lp++)
         set1.setForSocket((*lp)->val);
 }
 
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS> &BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::operator=
     (BaseIncomingSet &inval)
 {
@@ -184,18 +184,18 @@ inline BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UP
 }
 
 
-template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS> 
+template <class _INCLASS1,class _IN_LISTEN,typename  MESSAGE_READER_BUF, typename  MESSAGE_READER_UPPASS>
 inline void BaseIncomingSet<_INCLASS1,_IN_LISTEN,MESSAGE_READER_BUF,MESSAGE_READER_UPPASS>::Reset()
 {
     _Listener.Close();
     iterator lpNext, lp;
     for (lpNext  = lp = BaseClass::begin(); lp != BaseClass::end() ; lp = lpNext)
     {
-        lpNext++;   
+        lpNext++;
         (*lp)->Reset();
         delete *lp;
-        erase(lp);
-    }               
+        this->erase(lp);
+    }
 }
 
 

+ 64 - 29
direct/src/p3d/p3dWrapper.c

@@ -23,12 +23,21 @@
 #include <windows.h>
 #include <process.h>
 #include <assert.h>
+#include <malloc.h>
 
 #define BUFFER_SIZE 1024
 
+/* It makes sense to use "App Paths\panda3d.exe".  However, Microsoft
+   decided in their infinite wisdom to disable Redirection for that
+   key from Windows 7 onward, so we can't rely on it producing a
+   result appropriate to the right architecture when both the 32-bit
+   and 64-bit versions of the runtime are installed.  Beh. */
+
+#define UNINST_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Panda3D Game Engine"
+
 int main (int argc, char* argv[]) {
   int i;
-  char buffer [BUFFER_SIZE];
+  char buffer[BUFFER_SIZE];
   char* p3dfile;
   char* runtime = NULL;
   DWORD size;
@@ -37,56 +46,82 @@ int main (int argc, char* argv[]) {
   char *cmd;
   char *newcmd;
   HKEY hKey = 0;
-  char buf [1024] = {0};
-  DWORD dwType = 0;
+  char buf[1024] = {0};
+  DWORD dwType = REG_SZ;
   DWORD dwBufSize = sizeof(buf);
-  size = GetModuleFileName (NULL, buffer, BUFFER_SIZE);
+  size = GetModuleFileName(NULL, buffer, BUFFER_SIZE);
   assert (size > 0);
 
   /* Chop off the .exe and replace it by .p3d. */
-  p3dfile = (char*) malloc (size + 1);
-  memcpy (p3dfile, buffer, size);
-  p3dfile [size] = 0;
-  memcpy (p3dfile + size - 3, "p3d", 3);
+  p3dfile = (char*) _alloca(size + 1);
+  memcpy(p3dfile, buffer, size);
+  p3dfile[size] = 0;
+  memcpy(p3dfile + size - 3, "p3d", 3);
 
-  /* Find the Panda3D applet\DefaultIcon key and extract the path to the runtime from there. */
-  if (RegOpenKey (HKEY_CLASSES_ROOT, "Panda3D applet\\DefaultIcon", &hKey) == ERROR_SUCCESS) {
-    dwType = REG_SZ;
-    if (RegQueryValueEx(hKey, 0, 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
-      for (i = dwBufSize - 1; i >= 0; --i) {
-        if (buf [i] == '/' || buf [i] == '\\') {
-          runtime = (char*) malloc (i + 13);
-          memcpy (runtime, buf, i);
-          runtime [i] = 0;         
-          strcat (runtime, "\\panda3d.exe");      
-          break;
+  /* Find the location of panda3d.exe using the registry path. */
+#ifdef _WIN64
+  /* If we're on 64-bit Windows, try the 64-bit registry first. */
+  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey) == ERROR_SUCCESS) {
+    if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
+      char *slash = strrchr(buf, '\\');
+      if (slash != NULL) {
+        strcpy(slash, "\\panda3d.exe");
+        runtime = buf;
+      }
+    }
+    RegCloseKey(hKey);
+  }
+#endif
+
+  /* On 32-bit Windows, or no 64-bit Runtime installed.  Try 32-bit registry. */
+  if (runtime == NULL) {
+    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, UNINST_KEY, 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &hKey) == ERROR_SUCCESS) {
+      if (RegQueryValueEx(hKey, "DisplayIcon", 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
+        char *slash = strrchr(buf, '\\');
+        if (slash != NULL) {
+          strcpy(slash, "\\panda3d.exe");
+          runtime = buf;
         }
       }
+      RegCloseKey(hKey);
+    }
+  }
+
+  /* Backward compatibility: Runtime 1.0.4 and below looked for the below key, even though the
+     above keys should work fine, but let's just be certain.
+     Find the Panda3D applet\DefaultIcon key and extract the path to the runtime from there.  */
+  if (runtime == NULL) {
+    if (RegOpenKey(HKEY_CLASSES_ROOT, "Panda3D applet\\DefaultIcon", &hKey) == ERROR_SUCCESS) {
+      if (RegQueryValueEx(hKey, 0, 0, &dwType, (BYTE*) buf, &dwBufSize) == ERROR_SUCCESS) {
+        char *slash = strrchr(buf, '\\');
+        if (slash != NULL) {
+          strcpy(slash, "\\panda3d.exe");
+          runtime = buf;
+        }
+      } else {
+        fprintf(stderr, "Failed to read registry key. Try reinstalling the Panda3D Runtime.\n");
+        return 1;
+      }
+      RegCloseKey(hKey);
     } else {
-      fprintf (stderr, "Failed to read registry key. Try reinstalling the Panda3D Runtime.\n");
+      fprintf(stderr, "The Panda3D Runtime does not appear to be installed!\n");
       return 1;
     }
-    RegCloseKey(hKey);
-  } else {
-    fprintf (stderr, "The Panda3D Runtime does not appear to be installed!\n");
-    return 1;
   }
 
   if (runtime == NULL) {
-    fprintf (stderr, "Failed to find panda3d.exe in registry. Try reinstalling the Panda3D Runtime.\n");
+    fprintf(stderr, "Failed to find panda3d.exe in registry. Try reinstalling the Panda3D Runtime.\n");
     return 1;
   }
 
   /* Build the command-line and run panda3d.exe. */
   cmd = GetCommandLine();
-  newcmd = (char*) malloc (strlen(runtime) + strlen(p3dfile) + strlen (cmd) - strlen (argv[0]) + 7);
-  sprintf (newcmd, "\"%s\" \"%s\" %s", runtime, p3dfile, cmd + strlen (argv[0]));
+  newcmd = (char*) _alloca(strlen(runtime) + strlen(p3dfile) + strlen(cmd) - strlen (argv[0]) + 7);
+  sprintf(newcmd, "\"%s\" \"%s\" %s", runtime, p3dfile, cmd + strlen(argv[0]));
   memset(&si, 0, sizeof(si));
   si.cb = sizeof(STARTUPINFO);
   if (CreateProcess(runtime, newcmd, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) {
     WaitForSingleObject(pi.hProcess, INFINITE);
   }
-  free (newcmd);
   return 0;
 }
-

+ 12 - 3
direct/src/plugin_installer/make_installer.py

@@ -40,7 +40,7 @@ parser.add_option('-l', '--license', dest = 'license',
                   default = None)
 parser.add_option('-w', '--website', dest = 'website',
                   help = 'The product website',
-                  default = 'http://www.panda3d.org')
+                  default = 'https://www.panda3d.org')
 parser.add_option('', '--start', dest = 'start',
                   help = 'Specify this option to add a start menu',
                   action = 'store_true', default = False)
@@ -65,6 +65,9 @@ parser.add_option('', '--pvk', dest = 'pvk',
 parser.add_option('', '--mssdk', dest = 'mssdk',
                   help = 'The path to the MS Platform SDK directory (Windows only).  mssdk/bin should contain cabarc.exe and signcode.exe.',
                   default = None)
+parser.add_option('', '--regview', dest = 'regview',
+                  help = 'Which registry view to use, 64 or 32.',
+                  default = None)
 
 (options, args) = parser.parse_args()
 
@@ -221,7 +224,7 @@ def parseDependenciesUnix(tempFile):
         filenames.append(l.strip().split(' ', 1)[0])
     return filenames
 
-def addDependencies(path, pathname, file, pluginDependencies, dependentFiles):
+def addDependencies(path, pathname, file, pluginDependencies, dependentFiles, required=True):
     """ Checks the named file for DLL dependencies, and adds any
     appropriate dependencies found into pluginDependencies and
     dependentFiles. """
@@ -271,7 +274,10 @@ def addDependencies(path, pathname, file, pluginDependencies, dependentFiles):
                         break
                     pathname = None
                 if not pathname:
-                    sys.exit("Couldn't find %s." % (dfile))
+                    if required:
+                        sys.exit("Couldn't find %s." % (dfile))
+                    sys.stderr.write("Warning: couldn't find %s." % (dfile))
+                    continue
                 pathname = os.path.abspath(pathname)
                 dependentFiles[dfilelower] = pathname
 
@@ -553,6 +559,9 @@ def makeInstaller():
         CMD += '/DPANDA3DW="' + panda3dw + '" '
         CMD += '/DPANDA3DW_PATH="' + pluginFiles[panda3dw] + '" '
 
+        if options.regview:
+            CMD += '/DREGVIEW=%s ' % (options.regview)
+
         dependencies = dependentFiles.items()
         for i in range(len(dependencies)):
             CMD += '/DDEP%s="%s" ' % (i, dependencies[i][0])

+ 76 - 21
direct/src/plugin_installer/p3d_installer.nsi

@@ -1,6 +1,5 @@
 !include "MUI.nsh"
 !include LogicLib.nsh
-!include FileAssociation.nsh
 
 ; Several variables are assumed to be pre-defined by the caller.  See
 ; make_installer.py in this directory.
@@ -9,18 +8,18 @@
 !define UNINSTALL_CONFIRM "Are you sure you want to completely remove $(^Name) and all of its components?"
 !define UNINSTALL_LINK_NAME "Uninstall"
 !define WEBSITE_LINK_NAME "Website"
-!define PLID "@panda3d.org/Panda3D Runtime,version=0.0"
+!define PLID "@panda3d.org/Panda3D Runtime"
 
 ; HM NIS Edit Wizard helper defines
 !define APP_INTERNAL_NAME "Panda3D"
 
-!define PRODUCT_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\${OCX}"
+!define PRODUCT_DIR_REGKEY_PANDA3D "Software\Microsoft\Windows\CurrentVersion\App Paths\${PANDA3D}"
+!define PRODUCT_DIR_REGKEY_PANDA3DW "Software\Microsoft\Windows\CurrentVersion\App Paths\${PANDA3DW}"
+!define PRODUCT_DIR_REGKEY_OCX "Software\Microsoft\Windows\CurrentVersion\App Paths\${OCX}"
 !define PRODUCT_UNINST_KEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_NAME}"
 !define PRODUCT_UNINST_ROOT_KEY "HKLM"
 !define PROG_GROUPNAME "${PRODUCT_NAME}"
 
-!define FIREFOX_DIR_REGKEY "Software\Microsoft\Windows\CurrentVersion\App Paths\firefox.exe"
-
 SetCompressor lzma
 
 ; MUI Settings
@@ -60,16 +59,32 @@ InstallDir "${INSTALL_DIR}"
   UninstallIcon "${INSTALL_ICON}"
 !endif
 WindowIcon on
-InstallDirRegKey HKLM "${PRODUCT_DIR_REGKEY}" ""
+
 ShowInstDetails show
 ShowUnInstDetails show
 
+Function .onInit
+!ifdef REGVIEW
+  SetRegView ${REGVIEW}
+!endif
+
+  ClearErrors
+
+  ReadRegStr $0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "InstallLocation"
+
+  IfErrors +2 0
+  StrCpy $INSTDIR $0
+
+FunctionEnd
+
 Section "MainSection" SEC01
   SetShellVarContext all
   SetOutPath "$INSTDIR"
   SetOverwrite ifdiff
 
+!ifdef OCX_PATH
   File "${OCX_PATH}"
+!endif
   File "${NPAPI_PATH}"
   File "${PANDA3D_PATH}"
   File "${PANDA3DW_PATH}"
@@ -100,8 +115,6 @@ Section "MainSection" SEC01
 !ifdef DEP7P
   File "${DEP7P}"
 !endif
-
-  ${registerExtension} "$INSTDIR\${PANDA3DW}" ".p3d" "Panda3D applet"
  
 !ifdef ADD_START_MENU
 ; Start->Programs links
@@ -127,14 +140,41 @@ Section -AdditionalIcons
 SectionEnd
 
 Section -Post
+!ifdef REGVIEW
+  SetRegView ${REGVIEW}
+!endif
+
   WriteUninstaller "$INSTDIR\uninst.exe"
-  WriteRegStr HKLM "${PRODUCT_DIR_REGKEY}" "" "$INSTDIR\${OCX}"
+
+  WriteRegStr HKCR ".p3d" "" "Panda3D applet"
+  WriteRegStr HKCR ".p3d" "Content Type" "application/x-panda3d"
+  WriteRegStr HKCR ".p3d" "PerceivedType" "application"
+  WriteRegStr HKCR "Panda3D applet" "" "Panda3D applet"
+  WriteRegStr HKCR "Panda3D applet\DefaultIcon" "" "$INSTDIR\${PANDA3DW}"
+  WriteRegStr HKCR "Panda3D applet\shell" "" "open"
+  WriteRegStr HKCR "Panda3D applet\shell\open\command" "" '"$INSTDIR\${PANDA3DW}" "%1"'
+  WriteRegExpandStr HKCR "Panda3D applet\shell\open2" "" "Open &with Command Prompt"
+  ;WriteRegExpandStr HKCR "Panda3D applet\shell\open2" "MUIVerb" "@%SystemRoot%\System32\wshext.dll,-4511"
+  WriteRegExpandStr HKCR "Panda3D applet\shell\open2\command" "" '"$INSTDIR\${PANDA3D}" "%1"'
+
+  WriteRegStr HKLM "${PRODUCT_DIR_REGKEY_PANDA3D}" "" "$INSTDIR\${PANDA3D}"
+  WriteRegStr HKLM "${PRODUCT_DIR_REGKEY_PANDA3DW}" "" "$INSTDIR\${PANDA3DW}"
   WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
   WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString" "$INSTDIR\uninst.exe"
-  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${OCX}"
+  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayIcon" "$INSTDIR\${PANDA3D}"
   WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "DisplayVersion" "${PRODUCT_VERSION}"
   WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "URLInfoAbout" "${PRODUCT_WEB_SITE}"
   WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "Publisher" "${PRODUCT_PUBLISHER}"
+  WriteRegStr ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "InstallLocation" "$INSTDIR"
+  WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoModify" 1
+  WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "NoRepair" 1
+
+  SectionGetSize SEC01 $0
+  WriteRegDWORD ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "EstimatedSize" $0
+
+  # Delete keys we used in older versions
+  DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_OCX}"
+  DeleteRegKey HKCR "Panda3D applet\shell\edit"
 SectionEnd
 
 
@@ -149,16 +189,23 @@ Function un.onInit
 FunctionEnd
 
 Function .onInstSuccess
- # Register ActiveX
- ExecWait 'regsvr32 /s "$INSTDIR/${OCX}"'
+  # Register ActiveX
+  ExecWait 'regsvr32 /s "$INSTDIR/${OCX}"'
 
- # Register Mozilla Plugin
- WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Description" "Runs 3-D games and interactive applets"
- WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Path" "$INSTDIR\${NPAPI}"
- WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "ProductName" "${PRODUCT_NAME_SHORT}"
- WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Vendor" "${PRODUCT_PUBLISHER}"
- WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Version" "${PRODUCT_VERSION}"
- WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}\MimeTypes" "application/x-panda3d" ""
+!ifdef REGVIEW
+  SetRegView ${REGVIEW}
+!endif
+
+  # Register Mozilla Plugin
+  WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Description" "Runs 3-D games and interactive applets"
+  WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Path" "$INSTDIR\${NPAPI}"
+  WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "ProductName" "${PRODUCT_NAME_SHORT}"
+  WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Vendor" "${PRODUCT_PUBLISHER}"
+  WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}" "Version" "${PRODUCT_VERSION}"
+  WriteRegStr HKLM "SOFTWARE\MozillaPlugins\${PLID}\MimeTypes\application/x-panda3d" "Description" "Panda3D applet"
+
+  # Remove old stuff
+  DeleteRegKey HKLM "SOFTWARE\MozillaPlugins\${PLID},version=0.0"
 
 FunctionEnd
 
@@ -196,6 +243,10 @@ Section Uninstall
   Delete "$INSTDIR\${DEP7}"
 !endif
 
+!ifdef REGVIEW
+  SetRegView ${REGVIEW}
+!endif
+
 # The following loop uninstalls the plugin where it may have been
 # copied into one of the Mozilla Extensions dirs.  Older versions of
 # the installer would have done this, but now we just update the
@@ -215,8 +266,10 @@ Mozilla-Uninstall-Loop:
 Mozilla-Uninstall-End:
 
   DeleteRegKey HKLM "SOFTWARE\MozillaPlugins\${PLID}"
+  DeleteRegKey HKLM "SOFTWARE\MozillaPlugins\${PLID},version=0.0"
 
-  ${unregisterExtension} ".p3d" "Panda3D applet"
+  DeleteRegKey HKCR ".p3d"
+  DeleteRegKey HKCR "Panda3D applet"
 
   # Remove the user's "Panda3D" directory, where all of the downloaded
   # contents are installed.  Too bad we can't do this for every system
@@ -239,7 +292,9 @@ Mozilla-Uninstall-End:
   RMDir "$INSTDIR"
 
   DeleteRegKey ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}"
-  DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY}"
+  DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_PANDA3D}"
+  DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_PANDA3DW}"
+  DeleteRegKey HKLM "${PRODUCT_DIR_REGKEY_OCX}"
   SetAutoClose true
 SectionEnd
 

+ 0 - 70
direct/src/showbase/PythonUtil.py

@@ -52,7 +52,6 @@ import __builtin__
 from StringIO import StringIO
 import marshal
 import ElementTree as ET
-from HTMLParser import HTMLParser
 import BpDb
 import unicodedata
 import bisect
@@ -4201,75 +4200,6 @@ if __debug__ and __name__ == '__main__':
     assert unescapeHtmlString('as%32df') == 'as2df'
     assert unescapeHtmlString('asdf%32') == 'asdf2'
 
-class HTMLStringToElements(HTMLParser):
-    def __init__(self, str, *a, **kw):
-        self._elements = []
-        self._elementStack = Stack()
-        HTMLParser.__init__(self, *a, **kw)
-        self.feed(str)
-        self.close()
-
-    def getElements(self):
-        return self._elements
-        
-    def _handleNewElement(self, element):
-        if len(self._elementStack):
-            self._elementStack.top().append(element)
-        else:
-            self._elements.append(element)
-        self._elementStack.push(element)
-
-    def handle_starttag(self, tag, attrs):
-        kwArgs = {}
-        for name, value in attrs:
-            kwArgs[name] = value
-        el = ET.Element(tag, **kwArgs)
-        self._handleNewElement(el)
-
-    def handle_data(self, data):
-        # this ignores text outside of a tag
-        if len(self._elementStack):
-            self._elementStack.top().text = data
-
-    def handle_endtag(self, tag):
-        top = self._elementStack.top()
-        if len(top.getchildren()) == 0:
-            # insert a comment to prevent ElementTree from using <... /> convention
-            # force it to create a tag closer a la </tag>
-            # prevents problems in certain browsers
-            if top.tag == 'script' and top.get('type') == 'text/javascript':
-                if top.text == None:
-                    top.text = '// force tag closer'
-            else:
-                self.handle_comment('force tag closer')
-                self._elementStack.pop()
-        self._elementStack.pop()
-
-    def handle_comment(self, data):
-        comment = ET.Comment(data)
-        self._handleNewElement(comment)
-
-def str2elements(str):
-    return HTMLStringToElements(str).getElements()
-
-if __debug__ and __name__ == '__main__':
-    s = ScratchPad()
-    assert len(str2elements('')) == 0
-    s.br = str2elements('<br>')
-    assert len(s.br) == 1
-    assert s.br[0].tag == 'br'
-    s.b = str2elements('<b><br></b>')
-    assert len(s.b) == 1
-    assert len(s.b[0].getchildren()) == 1
-    s.a = str2elements('<a href=\'/\'>test</a>')
-    assert len(s.a) == 1
-    assert s.a[0].get('href') == '/'
-    assert s.a[0].text == 'test'
-    s.c = str2elements('<!--testComment-->')
-    assert len(s.c) == 1
-    assert s.c[0].text == 'testComment'
-    del s
-
 def repeatableRepr(obj):
     if type(obj) is types.DictType:
         keys = obj.keys()

+ 2 - 1
direct/src/showbase/ShowBase.py

@@ -7,7 +7,8 @@ __all__ = ['ShowBase', 'WindowControls']
 # Annoying and very noisy, but sometimes useful
 #import VerboseImport
 
-from pandac.PandaModules import *
+from panda3d.core import *
+from panda3d.direct import *
 
 # This needs to be available early for DirectGUI imports
 import __builtin__

+ 6 - 3
direct/src/showbase/VFSImporter.py

@@ -3,7 +3,6 @@ import sys
 import os
 import marshal
 import imp
-import struct
 import types
 import __builtin__
 
@@ -286,7 +285,8 @@ class VFSLoader:
         code = None
         data = vfile.readFile(True)
         if data[:4] == imp.get_magic():
-            t = struct.unpack('<I', data[4:8])[0]
+            t = ord(data[4]) + (ord(data[5]) << 8) + \
+               (ord(data[6]) << 16) + (ord(data[7]) << 24)
             if not timestamp or t == timestamp:
                 code = marshal.loads(data[8:])
             else:
@@ -314,7 +314,10 @@ class VFSLoader:
             pass
         else:
             f.write('\0\0\0\0')
-            f.write(struct.pack('<I', self.timestamp))
+            f.write(chr(self.timestamp & 0xff) +
+                    chr((self.timestamp >> 8) & 0xff) +
+                    chr((self.timestamp >> 16) & 0xff) +
+                    chr((self.timestamp >> 24) & 0xff))
             f.write(marshal.dumps(code))
             f.flush()
             f.seek(0, 0)

+ 4 - 3
direct/src/showutil/pfreeze.py

@@ -102,15 +102,16 @@ elif bl.endswith('.exe'):
     basename = os.path.splitext(basename)[0]
 
 startfile = args[0]
+startmod = startfile
 if startfile.endswith('.py') or startfile.endswith('.pyw') or \
    startfile.endswith('.pyc') or startfile.endswith('.pyo'):
-    startfile = os.path.splitext(startfile)[0]
+    startmod = os.path.splitext(startfile)[0]
 
 compileToExe = False
 if outputType == 'dll':
-    freezer.addModule(startfile)
+    freezer.addModule(startmod, filename = startfile)
 else:
-    freezer.addModule(startfile, newName = '__main__')
+    freezer.addModule('__main__', filename = startfile)
     compileToExe = True
 
 freezer.done(compileToExe = compileToExe)

+ 0 - 8
dtool/src/dtoolbase/cmath.I

@@ -390,11 +390,7 @@ cinf(double v) {
 ////////////////////////////////////////////////////////////////////
 INLINE float
 make_nan(float) {
-#ifndef _WIN32
-  return nanf("");
-#else
   return std::numeric_limits<float>::quiet_NaN();
-#endif
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -403,11 +399,7 @@ make_nan(float) {
 ////////////////////////////////////////////////////////////////////
 INLINE double
 make_nan(double) {
-#ifndef _WIN32
-  return nan("");
-#else
   return std::numeric_limits<double>::quiet_NaN();
-#endif
 }
 
 ////////////////////////////////////////////////////////////////////

+ 20 - 0
dtool/src/dtoolutil/executionEnvironment.I

@@ -119,3 +119,23 @@ INLINE string ExecutionEnvironment::
 get_dtool_name() {
   return get_ptr()->ns_get_dtool_name();
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: ExecutionEnvironment::set_binary_name
+//       Access: Public, Static
+//  Description: Do not use.
+////////////////////////////////////////////////////////////////////
+INLINE void ExecutionEnvironment::
+set_binary_name(const string &name) {
+  get_ptr()->_binary_name = name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ExecutionEnvironment::set_dtool_name
+//       Access: Public, Static
+//  Description: Do not use.
+////////////////////////////////////////////////////////////////////
+INLINE void ExecutionEnvironment::
+set_dtool_name(const string &name) {
+  get_ptr()->_dtool_name = name;
+}

+ 53 - 13
dtool/src/dtoolutil/executionEnvironment.cxx

@@ -31,6 +31,9 @@
 // And this is for GetModuleFileName().
 #include <windows.h>
 
+// And this is for CommandLineToArgvW.
+#include <shellapi.h>
+
 // SHGetSpecialFolderPath()
 #include <shlobj.h>
 #endif
@@ -74,12 +77,11 @@ extern char **environ;
 #define PREREAD_ENVIRONMENT
 #endif
 
-
 // We define the symbol HAVE_GLOBAL_ARGV if we have global variables
 // named GLOBAL_ARGC/GLOBAL_ARGV that we can read at static init time
 // to determine our command-line arguments.
 
-#if defined(HAVE_GLOBAL_ARGV) && defined(PROTOTYPE_GLOBAL_ARGV)
+#if !defined(WIN32_VC) && defined(HAVE_GLOBAL_ARGV) && defined(PROTOTYPE_GLOBAL_ARGV)
 extern char **GLOBAL_ARGV;
 extern int GLOBAL_ARGC;
 #endif
@@ -298,7 +300,7 @@ ns_get_environment_variable(const string &var) const {
 #endif
         }
       }
-      
+
       PyGILState_Release(state);
 
       if (main_dir.empty()) {
@@ -613,7 +615,7 @@ read_environment_variables() {
 ////////////////////////////////////////////////////////////////////
 void ExecutionEnvironment::
 read_args() {
-
+#ifndef ANDROID
   // First, we need to fill in _dtool_name.  This contains
   // the full path to the p3dtool library.
 
@@ -762,7 +764,39 @@ read_args() {
   // Next we need to fill in _args, which is a vector containing
   // the command-line arguments that the executable was invoked with.
 
-#if defined(IS_FREEBSD)
+#if defined(WIN32_VC)
+
+  // We cannot rely on __argv when Python is linked in Unicode mode.
+  // Instead, let's use GetCommandLine.
+
+  LPWSTR cmdline = GetCommandLineW();
+  int argc = 0;
+  LPWSTR *wargv = CommandLineToArgvW(cmdline, &argc);
+
+  if (wargv == NULL) {
+    cerr << "CommandLineToArgvW failed; command-line arguments unavailable to config.\n";
+
+  } else {
+    TextEncoder encoder;
+    encoder.set_encoding(Filename::get_filesystem_encoding());
+
+    for (int i = 0; i < argc; ++i) {
+      wstring wtext(wargv[i]);
+      encoder.set_wtext(wtext);
+
+      if (i == 0) {
+        if (_binary_name.empty()) {
+          _binary_name = encoder.get_text();
+        }
+      } else {
+        _args.push_back(encoder.get_text());
+      }
+    }
+
+    LocalFree(wargv);
+  }
+
+#elif defined(IS_FREEBSD)
   // In FreeBSD, we can use sysctl to determine the command-line arguments.
 
   size_t bufsize = 4096;
@@ -786,13 +820,17 @@ read_args() {
 #elif defined(HAVE_GLOBAL_ARGV)
   int argc = GLOBAL_ARGC;
 
-  if (_binary_name.empty() && argc > 0) {
-    _binary_name = GLOBAL_ARGV[0];
-    // This really needs to be resolved against PATH.
-  }
+  // On Windows, __argv can be NULL when the main entry point is
+  // compiled in Unicode mode (as is the case with Python 3)
+  if (GLOBAL_ARGV != NULL) {
+    if (_binary_name.empty() && argc > 0) {
+      _binary_name = GLOBAL_ARGV[0];
+      // This really needs to be resolved against PATH.
+    }
 
-  for (int i = 1; i < argc; i++) {
-    _args.push_back(GLOBAL_ARGV[i]);
+    for (int i = 1; i < argc; i++) {
+      _args.push_back(GLOBAL_ARGV[i]);
+    }
   }
 
 #elif defined(HAVE_PROC_SELF_CMDLINE) || defined(HAVE_PROC_CURPROC_CMDLINE)
@@ -835,7 +873,7 @@ read_args() {
   }
 #endif
 
-#ifndef WIN32
+#ifndef _WIN32
   // Try to use realpath to get cleaner paths.
 
   if (!_binary_name.empty()) {
@@ -851,7 +889,9 @@ read_args() {
       _dtool_name = newpath;
     }
   }
-#endif
+#endif  // _WIN32
+
+#endif  // ANDROID
 
   if (_dtool_name.empty()) {
     _dtool_name = _binary_name;

+ 3 - 0
dtool/src/dtoolutil/executionEnvironment.h

@@ -50,6 +50,9 @@ PUBLISHED:
   INLINE static string get_binary_name();
   INLINE static string get_dtool_name();
 
+  INLINE static void set_binary_name(const string &name);
+  INLINE static void set_dtool_name(const string &name);
+
   static Filename get_cwd();
 
 private:

+ 29 - 6
dtool/src/dtoolutil/filename.cxx

@@ -61,6 +61,10 @@ TVOLATILE AtomicAdjust::Pointer Filename::_user_appdata_directory;
 TVOLATILE AtomicAdjust::Pointer Filename::_common_appdata_directory;
 TypeHandle Filename::_type_handle;
 
+#ifdef ANDROID
+string Filename::_internal_data_dir;
+#endif
+
 #ifdef WIN32
 /* begin Win32-specific code */
 
@@ -463,7 +467,7 @@ Filename Filename::
 temporary(const string &dirname, const string &prefix, const string &suffix,
           Type type) {
   Filename fdirname = dirname;
-#ifdef WIN32
+#if defined(_WIN32) || defined(ANDROID)
   // The Windows tempnam() function doesn't do a good job of choosing
   // a temporary directory.  Choose one ourselves.
   if (fdirname.empty()) {
@@ -527,7 +531,7 @@ get_home_directory() {
     if (home_directory.empty()) {
 #ifdef WIN32
       wchar_t buffer[MAX_PATH];
-      
+
       // On Windows, fall back to the "My Documents" folder.
       if (SHGetSpecialFolderPathW(NULL, buffer, CSIDL_PERSONAL, true)) {
         Filename dirname = from_os_specific_w(buffer);
@@ -538,15 +542,22 @@ get_home_directory() {
         }
       }
 
+#elif defined(ANDROID)
+      // Temporary hack.
+      home_directory = "/data/data/org.panda3d.sdk";
+
 #elif defined(IS_OSX)
       home_directory = get_osx_home_directory();
-      
+
+#elif defined(ANDROID)
+      home_directory = _internal_data_dir;
+
 #else
       // Posix case: check /etc/passwd?
-      
+
 #endif  // WIN32
     }
-      
+
     if (home_directory.empty()) {
       // Fallback case.
       home_directory = ExecutionEnvironment::get_cwd();
@@ -559,7 +570,7 @@ get_home_directory() {
       delete newdir;
     }
   }
-  
+
   return (*(Filename *)_home_directory);
 }
 
@@ -589,6 +600,10 @@ get_temp_directory() {
 #elif defined(IS_OSX)
     temp_directory = get_osx_temp_directory();
 
+#elif defined(ANDROID)
+    temp_directory.set_dirname(_internal_data_dir);
+    temp_directory.set_basename("cache");
+
 #else
     // Posix case.
     temp_directory = "/tmp";
@@ -638,6 +653,10 @@ get_user_appdata_directory() {
 #elif defined(IS_OSX)
     user_appdata_directory = get_osx_user_appdata_directory();
 
+#elif defined(ANDROID)
+    user_appdata_directory.set_dirname(_internal_data_dir);
+    user_appdata_directory.set_basename("files");
+
 #else
     // Posix case.
     user_appdata_directory = get_home_directory();
@@ -687,6 +706,10 @@ get_common_appdata_directory() {
 #elif defined(IS_OSX)
     common_appdata_directory = get_osx_common_appdata_directory();
 
+#elif defined(ANDROID)
+    common_appdata_directory.set_dirname(_internal_data_dir);
+    common_appdata_directory.set_basename("files");
+
 #else
     // Posix case.
     common_appdata_directory = "/var";

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

@@ -263,6 +263,11 @@ protected:
   static TVOLATILE AtomicAdjust::Pointer _user_appdata_directory;
   static TVOLATILE AtomicAdjust::Pointer _common_appdata_directory;
 
+#ifdef ANDROID
+public:
+  static string _internal_data_dir;
+#endif
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 34 - 2
dtool/src/interrogate/interfaceMakerPythonNative.cxx

@@ -2743,7 +2743,9 @@ int GetParnetDepth(CPPType *type) {
   } else if (TypeManager::is_wchar_pointer(type)) {
   } else if (TypeManager::is_pointer_to_PyObject(type)) {
   } else if (TypeManager::is_pointer_to_Py_buffer(type)) {
-  } else if (TypeManager::is_pointer(type) || TypeManager::is_reference(type) || TypeManager::is_struct(type)) {
+  } else if (TypeManager::is_pointer(type) ||
+             TypeManager::is_reference(type) ||
+             TypeManager::is_struct(type)) {
     ++answer;
     int deepest = 0;
     TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(type)), false);
@@ -3276,6 +3278,36 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
       only_pyobjects = false;
       ++num_params;
 
+    } else if (TypeManager::is_pointer_to_PyStringObject(type)) {
+      if (args_type == AT_single_arg) {
+        // This is a single-arg function, so there's no need
+        // to convert anything.
+        param_name = "arg";
+        extra_param_check += " && PyString_Check(arg)";
+      } else {
+        indent(out, indent_level) << "PyStringObject *" << param_name << ";\n";
+        format_specifiers += "S";
+        parameter_list += ", &" + param_name;
+      }
+      pexpr_string = param_name;
+      expected_params += "string";
+      ++num_params;
+
+    } else if (TypeManager::is_pointer_to_PyUnicodeObject(type)) {
+      if (args_type == AT_single_arg) {
+        // This is a single-arg function, so there's no need
+        // to convert anything.
+        param_name = "arg";
+        extra_param_check += " && PyUnicode_Check(arg)";
+      } else {
+        indent(out, indent_level) << "PyUnicodeObject *" << param_name << ";\n";
+        format_specifiers += "U";
+        parameter_list += ", &" + param_name;
+      }
+      pexpr_string = param_name;
+      expected_params += "unicode";
+      ++num_params;
+
     } else if (TypeManager::is_pointer_to_PyObject(type)) {
       if (args_type == AT_single_arg) {
         // This is a single-arg function, so there's no need
@@ -3605,7 +3637,7 @@ write_function_instance(ostream &out, InterfaceMaker::Object *obj,
   if (true) {
     indent(out, indent_level)
       << "if (PyErr_Occurred()) {\n";
-    delete_return_value(out, indent_level, remap, return_expr);
+    delete_return_value(out, indent_level + 2, remap, return_expr);
     indent(out, indent_level)
       << "  if (PyErr_ExceptionMatches(PyExc_TypeError)) {\n";
     indent(out, indent_level)

+ 85 - 1
dtool/src/interrogate/typeManager.cxx

@@ -1199,8 +1199,91 @@ is_PyObject(CPPType *type) {
     return is_PyObject(type->as_const_type()->_wrapped_around);
 
   case CPPDeclaration::ST_extension:
+  case CPPDeclaration::ST_struct:
     return (type->get_local_name(&parser) == "PyObject" ||
-            type->get_local_name(&parser) == "_object");
+            type->get_local_name(&parser) == "PyTypeObject" ||
+            type->get_local_name(&parser) == "PyStringObject" ||
+            type->get_local_name(&parser) == "PyUnicodeObject" ||
+            type->get_local_name(&parser) == "_object" ||
+            type->get_local_name(&parser) == "_typeobject");
+
+  default:
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypeManager::is_pointer_to_PyStringObject
+//       Access: Public, Static
+//  Description: Returns true if the indicated type is PyStringObject *.
+////////////////////////////////////////////////////////////////////
+bool TypeManager::
+is_pointer_to_PyStringObject(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return is_pointer_to_PyStringObject(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_pointer:
+    return is_PyStringObject(type->as_pointer_type()->_pointing_at);
+
+  default:
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypeManager::is_PyStringObject
+//       Access: Public, Static
+//  Description: Returns true if the indicated type is PyStringObject.
+////////////////////////////////////////////////////////////////////
+bool TypeManager::
+is_PyStringObject(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return is_PyStringObject(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_extension:
+  case CPPDeclaration::ST_struct:
+    return (type->get_local_name(&parser) == "PyStringObject");
+
+  default:
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypeManager::is_pointer_to_PyUnicodeObject
+//       Access: Public, Static
+//  Description: Returns true if the indicated type is PyStringObject *.
+////////////////////////////////////////////////////////////////////
+bool TypeManager::
+is_pointer_to_PyUnicodeObject(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return is_pointer_to_PyUnicodeObject(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_pointer:
+    return is_PyUnicodeObject(type->as_pointer_type()->_pointing_at);
+
+  default:
+    return false;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TypeManager::is_PyUnicodeObject
+//       Access: Public, Static
+//  Description: Returns true if the indicated type is PyUnicodeObject.
+////////////////////////////////////////////////////////////////////
+bool TypeManager::
+is_PyUnicodeObject(CPPType *type) {
+  switch (type->get_subtype()) {
+  case CPPDeclaration::ST_const:
+    return is_PyUnicodeObject(type->as_const_type()->_wrapped_around);
+
+  case CPPDeclaration::ST_extension:
+  case CPPDeclaration::ST_struct:
+    return (type->get_local_name(&parser) == "PyUnicodeObject");
 
   default:
     return false;
@@ -1238,6 +1321,7 @@ is_Py_buffer(CPPType *type) {
     return is_Py_buffer(type->as_const_type()->_wrapped_around);
 
   case CPPDeclaration::ST_extension:
+  case CPPDeclaration::ST_struct:
     return (type->get_local_name(&parser) == "Py_buffer");
 
   default:

+ 4 - 0
dtool/src/interrogate/typeManager.h

@@ -94,6 +94,10 @@ public:
   static bool is_const_ref_to_pointer_to_base(CPPType *type);
   static bool is_pointer_to_PyObject(CPPType *type);
   static bool is_PyObject(CPPType *type);
+  static bool is_pointer_to_PyStringObject(CPPType *type);
+  static bool is_PyStringObject(CPPType *type);
+  static bool is_pointer_to_PyUnicodeObject(CPPType *type);
+  static bool is_PyUnicodeObject(CPPType *type);
   static bool is_pointer_to_Py_buffer(CPPType *type);
   static bool is_Py_buffer(CPPType *type);
   static bool involves_unpublished(CPPType *type);

+ 6 - 0
dtool/src/parser-inc/Python.h

@@ -23,6 +23,12 @@
 struct _object;
 typedef _object PyObject;
 
+struct _typeobject;
+typedef _typeobject PyTypeObject;
+
+struct PyStringObject;
+struct PyUnicodeObject;
+
 class PyThreadState;
 typedef int Py_ssize_t;
 struct Py_buffer;

+ 2 - 0
dtool/src/parser-inc/android/asset_manager.h

@@ -0,0 +1,2 @@
+struct AAssetManager;
+struct AAsset;

+ 39 - 0
dtool/src/parser-inc/jni.h

@@ -0,0 +1,39 @@
+typedef unsigned char   jboolean;       /* unsigned 8 bits */
+typedef signed char     jbyte;          /* signed 8 bits */
+typedef unsigned short  jchar;          /* unsigned 16 bits */
+typedef short           jshort;         /* signed 16 bits */
+typedef int             jint;           /* signed 32 bits */
+typedef long long       jlong;          /* signed 64 bits */
+typedef float           jfloat;         /* 32-bit IEEE 754 */
+typedef double          jdouble;        /* 64-bit IEEE 754 */
+
+typedef jint jsize;
+
+typedef void*           jobject;
+typedef jobject         jclass;
+typedef jobject         jstring;
+typedef jobject         jarray;
+typedef jarray          jobjectArray;
+typedef jarray          jbooleanArray;
+typedef jarray          jbyteArray;
+typedef jarray          jcharArray;
+typedef jarray          jshortArray;
+typedef jarray          jintArray;
+typedef jarray          jlongArray;
+typedef jarray          jfloatArray;
+typedef jarray          jdoubleArray;
+typedef jobject         jthrowable;
+typedef jobject         jweak;
+
+struct _jfieldID;                       /* opaque structure */
+typedef struct _jfieldID* jfieldID;     /* field IDs */
+
+struct _jmethodID;                      /* opaque structure */
+typedef struct _jmethodID* jmethodID;   /* method IDs */
+
+struct JNIInvokeInterface;
+
+struct _JNIEnv;
+struct _JavaVM;
+typedef _JNIEnv JNIEnv;
+typedef _JavaVM JavaVM;

+ 8 - 0
dtool/src/pystub/pystub.cxx

@@ -121,6 +121,8 @@ extern "C" {
   EXPCL_PYSTUB int PyString_AsStringAndSize(...);
   EXPCL_PYSTUB int PyString_FromString(...);
   EXPCL_PYSTUB int PyString_FromStringAndSize(...);
+  EXPCL_PYSTUB int PyString_InternFromString(...);
+  EXPCL_PYSTUB int PyString_InternInPlace(...);
   EXPCL_PYSTUB int PyString_Size(...);
   EXPCL_PYSTUB int PyString_Type(...);
   EXPCL_PYSTUB int PySys_GetObject(...);
@@ -152,6 +154,8 @@ extern "C" {
   EXPCL_PYSTUB int PyUnicode_FromStringAndSize(...);
   EXPCL_PYSTUB int PyUnicode_FromWideChar(...);
   EXPCL_PYSTUB int PyUnicode_GetSize(...);
+  EXPCL_PYSTUB int PyUnicode_InternFromString(...);
+  EXPCL_PYSTUB int PyUnicode_InternInPlace(...);
   EXPCL_PYSTUB int PyUnicode_Type(...);
   EXPCL_PYSTUB int Py_BuildValue(...);
   EXPCL_PYSTUB int Py_InitModule4(...);
@@ -296,6 +300,8 @@ int PyString_AsString(...) { return 0; }
 int PyString_AsStringAndSize(...) { return 0; }
 int PyString_FromString(...) { return 0; }
 int PyString_FromStringAndSize(...) { return 0; }
+int PyString_InternFromString(...) { return 0; }
+int PyString_InternInPlace(...) { return 0; }
 int PyString_Size(...) { return 0; }
 int PyString_Type(...) { return 0; }
 int PySys_GetObject(...) { return 0; }
@@ -327,6 +333,8 @@ int PyUnicode_FromString(...) { return 0; }
 int PyUnicode_FromStringAndSize(...) { return 0; }
 int PyUnicode_FromWideChar(...) { return 0; }
 int PyUnicode_GetSize(...) { return 0; }
+int PyUnicode_InternFromString(...) { return 0; }
+int PyUnicode_InternInPlace(...) { return 0; }
 int PyUnicode_Type(...) { return 0; }
 int Py_BuildValue(...) { return 0; }
 int Py_InitModule4(...) { return 0; }

+ 80 - 62
makepanda/makepanda.py

@@ -30,8 +30,8 @@ import time
 import os
 import sys
 
-## jGenPyCode tries to get the directory for Direct from the sys.path. This only works if you 
-## have installed the sdk using a installer. This would not work if the installer was 
+## jGenPyCode tries to get the directory for Direct from the sys.path. This only works if you
+## have installed the sdk using a installer. This would not work if the installer was
 ## never used and everything was grabbed into a virgin environment using cvs.
 sys.path.append(os.getcwd())
 
@@ -88,14 +88,14 @@ PkgListSet(["PYTHON", "DIRECT",                        # Python support
   "VRPN", "OPENSSL",                                   # Transport
   "FFTW",                                              # Algorithm helpers
   "ARTOOLKIT", "OPENCV", "DIRECTCAM", "VISION",        # Augmented Reality
-  "NPAPI", "AWESOMIUM",                                # Browser embedding
-  "GTK2", "WX", "FLTK",                                # Toolkit support
-  "ROCKET",                                            # GUI libraries
+  "GTK2",                                              # GTK2 is used for PStats on Unix
+  "NPAPI", "MFC", "WX", "FLTK",                        # Used for web plug-in only
+  "ROCKET", "AWESOMIUM",                               # GUI libraries
   "CARBON", "COCOA",                                   # Mac OS X toolkits
   "X11", "XF86DGA", "XRANDR", "XCURSOR",               # Unix platform support
   "PANDATOOL", "PVIEW", "DEPLOYTOOLS",                 # Toolchain
   "SKEL",                                              # Example SKEL project
-  "PANDAFX",                                           # Some distortion special lenses 
+  "PANDAFX",                                           # Some distortion special lenses
   "PANDAPARTICLESYSTEM",                               # Built in particle system
   "CONTRIB",                                           # Experimental
   "SSE2", "NEON",                                      # Compiler features
@@ -216,7 +216,7 @@ def parseopts(args):
                 if STRDXSDKVERSION == '':
                     print("No DirectX SDK version specified. Using 'default' DirectX SDK search")
                     STRDXSDKVERSION = 'default'
-            elif (option=="--platform-sdk"): 
+            elif (option=="--platform-sdk"):
                 STRMSPLATFORMVERSION = value.strip().lower()
                 if STRMSPLATFORMVERSION == '':
                     print("No MS Platform SDK version specified. Using 'default' MS Platform SDK search")
@@ -234,7 +234,7 @@ def parseopts(args):
             if  (option=="--everything" or option.startswith("--use-")
               or option=="--nothing" or option.startswith("--no-")):
               anything = 1
-    except: 
+    except:
         usage(0)
         print("Exception while parsing commandline:", sys.exc_info()[0])
     if (anything==0): usage(0)
@@ -492,7 +492,7 @@ if (COMPILER == "MSVC"):
                 LibName(pkg, 'dxerr.lib')
             else:
                 LibName(pkg, 'dxerrVNUM.lib'.replace("VNUM", vnum))
-            LibName(pkg, 'ddraw.lib')
+            #LibName(pkg, 'ddraw.lib')
             LibName(pkg, 'dxguid.lib')
     IncDirectory("ALWAYS", GetThirdpartyDir() + "extras/include")
     LibName("WINSOCK", "wsock32.lib")
@@ -536,7 +536,6 @@ if (COMPILER == "MSVC"):
     if (PkgSkip("FFTW")==0):     LibName("FFTW",     GetThirdpartyDir() + "fftw/lib/fftw.lib")
     if (PkgSkip("ARTOOLKIT")==0):LibName("ARTOOLKIT",GetThirdpartyDir() + "artoolkit/lib/libAR.lib")
     if (PkgSkip("FCOLLADA")==0): LibName("FCOLLADA", GetThirdpartyDir() + "fcollada/lib/FCollada.lib")
-    if (PkgSkip("SQUISH")==0):   LibName("SQUISH",   GetThirdpartyDir() + "squish/lib/squish.lib")
     if (PkgSkip("OPENCV")==0):   LibName("OPENCV",   GetThirdpartyDir() + "opencv/lib/cv.lib")
     if (PkgSkip("OPENCV")==0):   LibName("OPENCV",   GetThirdpartyDir() + "opencv/lib/highgui.lib")
     if (PkgSkip("OPENCV")==0):   LibName("OPENCV",   GetThirdpartyDir() + "opencv/lib/cvaux.lib")
@@ -548,6 +547,11 @@ if (COMPILER == "MSVC"):
     if (PkgSkip("FFMPEG")==0):   LibName("FFMPEG",   GetThirdpartyDir() + "ffmpeg/lib/avutil.lib")
     if (PkgSkip("SWSCALE")==0):  LibName("SWSCALE",  GetThirdpartyDir() + "ffmpeg/lib/swscale.lib")
     if (PkgSkip("SWRESAMPLE")==0):LibName("SWRESAMPLE",GetThirdpartyDir() + "ffmpeg/lib/swresample.lib")
+    if (PkgSkip("SQUISH")==0):
+        if GetOptimize() <= 2:
+            LibName("SQUISH",   GetThirdpartyDir() + "squish/lib/squishd.lib")
+        else:
+            LibName("SQUISH",   GetThirdpartyDir() + "squish/lib/squish.lib")
     if (PkgSkip("ROCKET")==0):
         LibName("ROCKET", GetThirdpartyDir() + "rocket/lib/RocketCore.lib")
         LibName("ROCKET", GetThirdpartyDir() + "rocket/lib/RocketControls.lib")
@@ -663,21 +667,6 @@ if (COMPILER=="GCC"):
         IncDirectory("ALWAYS", "/usr/local/include")
         LibDirectory("ALWAYS", "/usr/local/lib")
 
-    if GetHost() != "darwin":
-        # Workaround for an issue where pkg-config does not include this path
-        if GetTargetArch() in ("x86_64", "amd64"):
-            if (os.path.isdir("/usr/lib64/glib-2.0/include")):
-                IncDirectory("GTK2", "/usr/lib64/glib-2.0/include")
-            if (os.path.isdir("/usr/lib64/gtk-2.0/include")):
-                IncDirectory("GTK2", "/usr/lib64/gtk-2.0/include")
-
-            if (os.path.isdir("/usr/X11R6/lib64")):
-                LibDirectory("ALWAYS", "/usr/X11R6/lib64")
-            else:
-                LibDirectory("ALWAYS", "/usr/X11R6/lib")
-        else:
-            LibDirectory("ALWAYS", "/usr/X11R6/lib")
-
     fcollada_libs = ("FColladaD", "FColladaSD", "FColladaS")
     # WARNING! The order of the ffmpeg libraries matters!
     ffmpeg_libs = ("libavformat", "libavcodec", "libavutil")
@@ -752,6 +741,22 @@ if (COMPILER=="GCC"):
             SmartPkgEnable("XF86DGA", "xxf86dga", "Xxf86dga", "X11/extensions/xf86dga.h")
             SmartPkgEnable("XCURSOR", "xcursor", "Xcursor", "X11/Xcursor/Xcursor.h")
 
+    if GetHost() != "darwin":
+        # Workaround for an issue where pkg-config does not include this path
+        if GetTargetArch() in ("x86_64", "amd64"):
+            if (os.path.isdir("/usr/lib64/glib-2.0/include")):
+                IncDirectory("GTK2", "/usr/lib64/glib-2.0/include")
+            if (os.path.isdir("/usr/lib64/gtk-2.0/include")):
+                IncDirectory("GTK2", "/usr/lib64/gtk-2.0/include")
+
+            if not PkgSkip("X11"):
+                if (os.path.isdir("/usr/X11R6/lib64")):
+                    LibDirectory("ALWAYS", "/usr/X11R6/lib64")
+                else:
+                    LibDirectory("ALWAYS", "/usr/X11R6/lib")
+        elif not PkgSkip("X11"):
+            LibDirectory("ALWAYS", "/usr/X11R6/lib")
+
     if (RUNTIME):
         # For the runtime, all packages are required
         for pkg in ["OPENSSL", "ZLIB", "NPAPI", "JPEG", "PNG"]:
@@ -924,7 +929,7 @@ def CompileCxx(obj,src,opts):
                 cmd += "/DWINVER=0x601 "
             cmd += "/Fo" + obj + " /nologo /c"
             if (GetTargetArch() != 'x64' and PkgSkip("SSE2") == 0):
-                cmd += " /arch:SSE2"            
+                cmd += " /arch:SSE2"
             for x in ipath: cmd += " /I" + x
             for (opt,dir) in INCDIRECTORIES:
                 if (opt=="ALWAYS") or (opt in opts): cmd += " /I" + BracketNameWithQuotes(dir)
@@ -936,7 +941,7 @@ def CompileCxx(obj,src,opts):
             if (optlevel==1): cmd += " /MDd /Zi /RTCs /GS"
             if (optlevel==2): cmd += " /MDd /Zi"
             if (optlevel==3): cmd += " /MD /Zi /O2 /Ob2 /Oi /Ot /fp:fast /DFORCE_INLINING"
-            if (optlevel==4): 
+            if (optlevel==4):
                cmd += " /MD /Zi /Ox /Ob2 /Oi /Ot /fp:fast /DFORCE_INLINING /DNDEBUG /GL"
                cmd += " /Oy /Zp16"      # jean-claude add /Zp16 insures correct static alignment for SSEx
 
@@ -982,11 +987,11 @@ def CompileCxx(obj,src,opts):
             if (optlevel==3):
                 cmd += " /MD /Zi /O2 /Oi /Ot /arch:SSE3"
                 cmd += " /Ob0"
-                cmd += " /Qipo-"                            # beware of IPO !!!  
+                cmd += " /Qipo-"                            # beware of IPO !!!
             ##      Lesson learned: Don't use /GL flag -> end result is MESSY
             ## ----------------------------------------------------------------
             if (optlevel==4):
-                cmd += " /MD /Zi /O3 /Oi /Ot /Ob0 /Yc /DNDEBUG"  # /Ob0 a ete rajoute en cours de route a 47%                
+                cmd += " /MD /Zi /O3 /Oi /Ot /Ob0 /Yc /DNDEBUG"  # /Ob0 a ete rajoute en cours de route a 47%
                 cmd += " /Qipo"                              # optimization multi file
 
             # for 3 & 4 optimization levels
@@ -998,7 +1003,7 @@ def CompileCxx(obj,src,opts):
                 cmd += " /Qopt-matmul"                        # needs /O2 or /O3
                 cmd += " /Qprec-div-"
                 cmd += " /Qsimd"
-                
+
                 cmd += " /QxHost"                            # compile for target host; Compiling for distribs should probably strictly enforce /arch:..
                 cmd += " /Quse-intel-optimized-headers"        # use intel optimized headers
                 cmd += " /Qparallel"                        # enable parallelization
@@ -1006,7 +1011,7 @@ def CompileCxx(obj,src,opts):
 
             ## PCH files coexistence: the /Qpchi option causes the Intel C++ Compiler to name its
             ## PCH files with a .pchi filename suffix and reduce build time.
-            ## The /Qpchi option is on by default but interferes with Microsoft libs; so use /Qpchi- to turn it off. 
+            ## The /Qpchi option is on by default but interferes with Microsoft libs; so use /Qpchi- to turn it off.
             ## I need to have a deeper look at this since the compile time is quite influenced by this setting !!!
             cmd += " /Qpchi-"                                 # keep it this way!
 
@@ -1024,7 +1029,7 @@ def CompileCxx(obj,src,opts):
             ## Use this option if you always define a class before you declare a pointer to a member of the class.
             ## The compiler will issue an error if it encounters a pointer declaration before the class is defined.
             ## Alternate: #pragma pointers_to_members
-      
+
             cmd += " /Fd" + os.path.splitext(obj)[0] + ".pdb"
             building = GetValueOption(opts, "BUILDING:")
             if (building): cmd += " /DBUILDING_" + building
@@ -1032,10 +1037,10 @@ def CompileCxx(obj,src,opts):
                 cmd += " /bigobj"
 
             # level of warnings and optimization reports
-            if GetVerbose(): 
+            if GetVerbose():
                 cmd += " /W3 " # or /W4 or /Wall
                 cmd += " /Qopt-report:2 /Qopt-report-phase:hlo /Qopt-report-phase:hpo"    # some optimization reports
-            else:            
+            else:
                 cmd += " /W1 "
             cmd += " /EHa /Zm300 /DWIN32_VC /DWIN32"
             if GetTargetArch() == 'x64':
@@ -1043,10 +1048,10 @@ def CompileCxx(obj,src,opts):
             cmd += " " + BracketNameWithQuotes(src)
 
             oscmd(cmd)
-            
+
     if (COMPILER=="GCC"):
         if (src.endswith(".c")): cmd = GetCC() +' -fPIC -c -o ' + obj
-        else:                    cmd = GetCXX()+' -ftemplate-depth-30 -fPIC -c -o ' + obj
+        else:                    cmd = GetCXX()+' -ftemplate-depth-50 -fPIC -c -o ' + obj
         for (opt, dir) in INCDIRECTORIES:
             if (opt=="ALWAYS") or (opt in opts): cmd += ' -I' + BracketNameWithQuotes(dir)
         for (opt,var,val) in DEFSYMBOLS:
@@ -1070,9 +1075,12 @@ def CompileCxx(obj,src,opts):
             cmd += ' --sysroot=%s -no-canonical-prefixes' % (SDK["SYSROOT"])
 
         # Android-specific flags.
+        arch = GetTargetArch()
+
         if GetTarget() == "android":
+            cmd += ' -I%s/include' % (SDK["ANDROID_STL"])
+            cmd += ' -I%s/libs/%s/include' % (SDK["ANDROID_STL"], SDK["ANDROID_ABI"])
             cmd += ' -ffunction-sections -funwind-tables'
-            arch = GetTargetArch()
             if arch == 'armv7a':
                 cmd += ' -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__'
                 cmd += ' -fstack-protector -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16'
@@ -1111,9 +1119,9 @@ def CompileCxx(obj,src,opts):
         else:
             cmd += " -pthread"
 
-        if PkgSkip("SSE2") == 0:
+        if PkgSkip("SSE2") == 0 and not arch.startswith("arm"):
             cmd += " -msse2"
-        
+
         if (optlevel==1): cmd += " -ggdb -D_DEBUG"
         if (optlevel==2): cmd += " -O1 -D_DEBUG"
         if (optlevel==3): cmd += " -O2"
@@ -1217,7 +1225,7 @@ def CompileIgate(woutd,wsrc,opts):
         # NOTE: this 1600 value is the version number for VC2010.
         cmd += ' -D_MSC_VER=1600 -D"_declspec(param)=" -D_near -D_far -D__near -D__far -D__stdcall'
     if (COMPILER=="GCC"):
-        cmd += ' -DCPPPARSER -D__STDC__=1 -D__cplusplus -D__inline -D__const=const'
+        cmd += ' -DCPPPARSER -D__STDC__=1 -D__cplusplus -D__inline -D__const=const -D__attribute__\(x\)='
         if GetTargetArch() in ("x86_64", "amd64"):
             cmd += ' -D_LP64'
         else:
@@ -1339,7 +1347,7 @@ def CompileLink(dll, obj, opts):
             if ("MFC" not in opts):
                 cmd += " /NOD:MFC90.LIB /NOD:MFC80.LIB /NOD:LIBCMT"
             cmd += " /NOD:LIBCI.LIB /DEBUG"
-            cmd += " /nod:libc /nod:libcmtd /nod:atlthunk /nod:atls"
+            cmd += " /nod:libc /nod:libcmtd /nod:atlthunk /nod:atls /nod:atlsd"
             if (GetOrigExt(dll) != ".exe"): cmd += " /DLL"
             optlevel = GetOptimizeOption(opts)
             if (optlevel==1): cmd += " /MAP /MAPINFO:EXPORTS /NOD:MSVCRT.LIB /NOD:MSVCPRT.LIB /NOD:MSVCIRT.LIB"
@@ -1390,7 +1398,7 @@ def CompileLink(dll, obj, opts):
             oscmd(cmd)
         else:
             cmd = "xilink"
-            if GetVerbose(): cmd += " /verbose:lib"            
+            if GetVerbose(): cmd += " /verbose:lib"
             if HasTargetArch():
                 cmd += " /MACHINE:" + GetTargetArch().upper()
             if ("MFC" not in opts):
@@ -1495,7 +1503,7 @@ def CompileLink(dll, obj, opts):
 
         # Android-specific flags.
         if GetTarget() == 'android':
-            cmd += " -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
+            cmd += " -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"
             if GetTargetArch() == 'armv7a':
                 cmd += " -march=armv7-a -Wl,--fix-cortex-a8"
             cmd += ' -lc -lm'
@@ -2130,7 +2138,7 @@ def WriteConfigSettings():
 
     if (PkgSkip("TOUCHINPUT") == 0 and GetTarget() == "windows"):
         dtool_config["HAVE_WIN_TOUCHINPUT"] = '1'
-    
+
     if (GetOptimize() <= 3):
         dtool_config["HAVE_ROCKET_DEBUGGER"] = '1'
 
@@ -3168,6 +3176,7 @@ if (not RUNTIME):
   TargetAdd('libp3gobj.in', opts=['IMOD:panda3d.core', 'ILIB:libp3gobj', 'SRCDIR:panda/src/gobj'])
   TargetAdd('libp3gobj_igate.obj', input='libp3gobj.in', opts=["DEPENDENCYONLY"])
   TargetAdd('p3gobj_geomVertexArrayData_ext.obj', opts=OPTS, input='geomVertexArrayData_ext.cxx')
+  TargetAdd('p3gobj_internalName_ext.obj', opts=OPTS, input='internalName_ext.cxx')
 
 #
 # DIRECTORY: panda/src/pgraphnodes/
@@ -3539,6 +3548,7 @@ if (not RUNTIME):
   TargetAdd('libpanda.dll', input='p3putil_typedWritable_ext.obj')
   TargetAdd('libpanda.dll', input='p3pnmimage_pfmFile_ext.obj')
   TargetAdd('libpanda.dll', input='p3gobj_geomVertexArrayData_ext.obj')
+  TargetAdd('libpanda.dll', input='p3gobj_internalName_ext.obj')
   TargetAdd('libpanda.dll', input='p3pgraph_ext_composite.obj')
   TargetAdd('libpanda.dll', input='p3display_graphicsStateGuardian_ext.obj')
 
@@ -3825,16 +3835,15 @@ if (PkgSkip("OPENSSL")==0 and not RTDIST and not RUNTIME and PkgSkip("DEPLOYTOOL
   TargetAdd('build_patch.exe', input=COMMON_PANDA_LIBS_PYSTUB)
   TargetAdd('build_patch.exe', opts=OPTS)
 
-  if not PkgSkip("ZLIB"):
-    TargetAdd('check_adler_check_adler.obj', opts=OPTS, input='check_adler.cxx')
-    TargetAdd('check_adler.exe', input=['check_adler_check_adler.obj'])
-    TargetAdd('check_adler.exe', input=COMMON_PANDA_LIBS_PYSTUB)
-    TargetAdd('check_adler.exe', opts=OPTS)
+  TargetAdd('check_adler_check_adler.obj', opts=OPTS, input='check_adler.cxx')
+  TargetAdd('check_adler.exe', input=['check_adler_check_adler.obj'])
+  TargetAdd('check_adler.exe', input=COMMON_PANDA_LIBS_PYSTUB)
+  TargetAdd('check_adler.exe', opts=OPTS)
 
-    TargetAdd('check_crc_check_crc.obj', opts=OPTS, input='check_crc.cxx')
-    TargetAdd('check_crc.exe', input=['check_crc_check_crc.obj'])
-    TargetAdd('check_crc.exe', input=COMMON_PANDA_LIBS_PYSTUB)
-    TargetAdd('check_crc.exe', opts=OPTS)
+  TargetAdd('check_crc_check_crc.obj', opts=OPTS, input='check_crc.cxx')
+  TargetAdd('check_crc.exe', input=['check_crc_check_crc.obj'])
+  TargetAdd('check_crc.exe', input=COMMON_PANDA_LIBS_PYSTUB)
+  TargetAdd('check_crc.exe', opts=OPTS)
 
   TargetAdd('check_md5_check_md5.obj', opts=OPTS, input='check_md5.cxx')
   TargetAdd('check_md5.exe', input=['check_md5_check_md5.obj'])
@@ -4470,7 +4479,7 @@ if (PkgSkip("DIRECT")==0):
     TargetAdd('packpanda.obj', opts=OPTS+['BUILDING:PACKPANDA'], input='ppython.cxx')
     TargetAdd('packpanda.exe', input='packpanda.obj')
     TargetAdd('packpanda.exe', opts=['PYTHON'])
-  
+
     DefSymbol("BUILDING:EGGCACHER", "IMPORT_MODULE", "direct.directscripts.eggcacher")
     TargetAdd('eggcacher.obj', opts=OPTS+['BUILDING:EGGCACHER'], input='ppython.cxx')
     TargetAdd('eggcacher.exe', input='eggcacher.obj')
@@ -4738,7 +4747,7 @@ if (RUNTIME and PkgSkip("NPAPI")==0):
 # DIRECTORY: direct/src/plugin_activex/
 #
 
-if (RUNTIME and GetTarget() == 'windows'):
+if (RUNTIME and GetTarget() == 'windows' and PkgSkip("MFC")==0):
   OPTS=['DIR:direct/src/plugin_activex', 'RUNTIME', 'ACTIVEX', 'MFC']
   DefSymbol('ACTIVEX', '_USRDLL', '')
   DefSymbol('ACTIVEX', '_WINDLL', '')
@@ -5805,7 +5814,7 @@ if (PkgSkip("PYTHON")==0 and not RUNTIME):
     TargetAdd('PandaModules.py', input='fx.pyd')
   if (PkgSkip("DIRECT")==0):
     TargetAdd('PandaModules.py', input='direct.pyd')
-  if (PkgSkip("VISION")==0):  
+  if (PkgSkip("VISION")==0):
     TargetAdd('PandaModules.py', input='vision.pyd')
   if (PkgSkip("SKEL")==0):
     TargetAdd('PandaModules.py', input='skel.pyd')
@@ -6042,11 +6051,25 @@ def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
     elif (os.path.isdir(file)):
         shutil.rmtree(file)
 
+    if GetTargetArch() == 'x64':
+        regview = '64'
+    else:
+        regview = '32'
+
     if (RUNTIME):
         # Invoke the make_installer script.
         AddToPathEnv("PATH", GetOutputDir() + "\\bin")
         AddToPathEnv("PATH", GetOutputDir() + "\\plugins")
-        oscmd(sys.executable + " -B direct\\src\\plugin_installer\\make_installer.py --version %s" % VERSION)
+
+        cmd = sys.executable + " -B -u direct\\src\\plugin_installer\\make_installer.py"
+        cmd += " --version %s --regview %s" % (VERSION, regview)
+
+        if GetTargetArch() == 'x64':
+            cmd += " --install \"$PROGRAMFILES64\\Panda3D\" "
+        else:
+            cmd += " --install \"$PROGRAMFILES32\\Panda3D\" "
+
+        oscmd(cmd)
         shutil.move("direct\\src\\plugin_installer\\p3d-setup.exe", file)
         return
 
@@ -6059,11 +6082,6 @@ def MakeInstallerNSIS(file, fullname, smdirectory, installdir):
     psource = os.path.abspath(".")
     panda = os.path.abspath(GetOutputDir())
 
-    if GetTargetArch() == 'x64':
-        regview = '64'
-    else:
-        regview = '32'
-
     nsis_defs = {
         'COMPRESSOR'  : COMPRESSOR,
         'NAME'        : fullname,

+ 124 - 71
makepanda/makepandacore.py

@@ -37,6 +37,7 @@ HAS_TARGET_ARCH = False
 TOOLCHAIN_PREFIX = ""
 ANDROID_ABI = None
 SYS_LIB_DIRS = []
+DEBUG_DEPENDENCIES = False
 
 # Is the current Python a 32-bit or 64-bit build?  There doesn't
 # appear to be a universal test for this.
@@ -220,7 +221,7 @@ def ProgressOutput(progress, msg, target = None):
             prefix = "%s[%s %d%%%s] " % (GetColor("yellow"), GetColor("cyan"), progress, GetColor("yellow"))
     else:
         global THREADS
-        
+
         ident = thread.get_ident()
         if (ident not in THREADS):
             THREADS[ident] = len(THREADS) + 1
@@ -468,7 +469,7 @@ def LocateBinary(binary):
         p = os.environ["PATH"]
 
     pathList = p.split(os.pathsep)
-    
+
     if GetHost() == 'windows':
         if not binary.endswith('.exe'):
             # Append .exe if necessary
@@ -652,7 +653,10 @@ def NeedsBuild(files, others):
     for file in files:
         dates[file] = GetTimestamp(file)
         if not os.path.exists(file):
+            if DEBUG_DEPENDENCIES:
+                print("rebuilding %s because it does not exist" % (file))
             return True
+
     for file in others:
         dates[file] = GetTimestamp(file)
 
@@ -661,6 +665,16 @@ def NeedsBuild(files, others):
         cached = BUILTFROMCACHE[key]
         if cached == dates:
             return False
+        elif DEBUG_DEPENDENCIES:
+            print("rebuilding %s because:" % (key))
+            for key in frozenset(cached.keys()) | frozenset(dates.keys()):
+                if key not in cached:
+                    print("    new dependency: %s" % (key))
+                elif key not in dates:
+                    print("    removed dependency: %s" % (key))
+                elif cached[key] != dates[key]:
+                    print("    dependency changed: %s" % (key))
+
         if VERBOSE and frozenset(cached) != frozenset(dates):
             print("%sWARNING:%s file dependencies changed: %s%s%s" % (GetColor("red"), GetColor(), GetColor("green"), files, GetColor()))
 
@@ -857,7 +871,7 @@ def CxxCalcDependencies(srcfile, ipath, ignore):
 ########################################################################
 
 if sys.platform == "win32":
-    # Note: not supported on cygwin. 
+    # Note: not supported on cygwin.
     if sys.version_info >= (3, 0):
         import winreg
     else:
@@ -1118,7 +1132,7 @@ def GetThirdpartyBase():
     THIRDPARTYBASE = "thirdparty"
     if "MAKEPANDA_THIRDPARTY" in os.environ:
         THIRDPARTYBASE = os.environ["MAKEPANDA_THIRDPARTY"]
-    
+
     return THIRDPARTYBASE
 
 def GetThirdpartyDir():
@@ -1181,40 +1195,44 @@ def GetThirdpartyDir():
 ########################################################################
 
 def GetOutputDir():
-  return OUTPUTDIR
+    return OUTPUTDIR
 
 def IsCustomOutputDir():
-  return CUSTOM_OUTPUTDIR
+    return CUSTOM_OUTPUTDIR
 
 def SetOutputDir(outputdir):
-  global OUTPUTDIR, CUSTOM_OUTPUTDIR
-  OUTPUTDIR=outputdir
-  CUSTOM_OUTPUTDIR=True
+    global OUTPUTDIR, CUSTOM_OUTPUTDIR
+    OUTPUTDIR = outputdir
+    CUSTOM_OUTPUTDIR = True
 
 def GetOptimize():
-  return int(OPTIMIZE)
+    return int(OPTIMIZE)
 
 def SetOptimize(optimize):
-  global OPTIMIZE
-  OPTIMIZE=optimize
+    global OPTIMIZE
+    OPTIMIZE = optimize
 
 def GetVerbose():
-  return VERBOSE
+    return VERBOSE
 
 def SetVerbose(verbose):
-  global VERBOSE
-  VERBOSE=verbose
+    global VERBOSE
+    VERBOSE = verbose
+
+def SetDebugDependencies(dd = True):
+    global DEBUG_DEPENDENCIES
+    DEBUG_DEPENDENCIES = dd
 
 def GetLinkAllStatic():
-  return LINK_ALL_STATIC
+    return LINK_ALL_STATIC
 
 def SetLinkAllStatic(val = True):
-  global LINK_ALL_STATIC
-  LINK_ALL_STATIC = val
+    global LINK_ALL_STATIC
+    LINK_ALL_STATIC = val
 
 def UnsetLinkAllStatic():
-  global LINK_ALL_STATIC
-  LINK_ALL_STATIC = False
+    global LINK_ALL_STATIC
+    LINK_ALL_STATIC = False
 
 ########################################################################
 ##
@@ -1292,7 +1310,7 @@ def OverrideValue(parameter, value):
 def PkgConfigHavePkg(pkgname, tool = "pkg-config"):
     """Returns a bool whether the pkg-config package is installed."""
 
-    if (sys.platform == "win32" or not LocateBinary(tool)):
+    if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
         return False
     if (tool == "pkg-config"):
         handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --modversion " + pkgname)
@@ -1307,7 +1325,7 @@ def PkgConfigHavePkg(pkgname, tool = "pkg-config"):
 def PkgConfigGetLibs(pkgname, tool = "pkg-config"):
     """Returns a list of libs for the package, prefixed by -l."""
 
-    if (sys.platform == "win32" or not LocateBinary(tool)):
+    if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
         return []
     if (tool == "pkg-config"):
         handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-l " + pkgname)
@@ -1338,7 +1356,7 @@ def PkgConfigGetLibs(pkgname, tool = "pkg-config"):
 def PkgConfigGetIncDirs(pkgname, tool = "pkg-config"):
     """Returns a list of includes for the package, NOT prefixed by -I."""
 
-    if (sys.platform == "win32" or not LocateBinary(tool)):
+    if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
         return []
     if (tool == "pkg-config"):
         handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags-only-I " + pkgname)
@@ -1359,7 +1377,7 @@ def PkgConfigGetIncDirs(pkgname, tool = "pkg-config"):
 def PkgConfigGetLibDirs(pkgname, tool = "pkg-config"):
     """Returns a list of library paths for the package, NOT prefixed by -L."""
 
-    if (sys.platform == "win32" or not LocateBinary(tool)):
+    if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
         return []
     if (tool == "pkg-config"):
         handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --libs-only-L " + pkgname)
@@ -1379,7 +1397,7 @@ def PkgConfigGetLibDirs(pkgname, tool = "pkg-config"):
 def PkgConfigGetDefSymbols(pkgname, tool = "pkg-config"):
     """Returns a dictionary of preprocessor definitions."""
 
-    if (sys.platform == "win32" or not LocateBinary(tool)):
+    if (sys.platform == "win32" or CrossCompiling() or not LocateBinary(tool)):
         return []
     if (tool == "pkg-config"):
         handle = os.popen(LocateBinary("pkg-config") + " --silence-errors --cflags " + pkgname)
@@ -1411,7 +1429,6 @@ def PkgConfigEnable(opt, pkgname, tool = "pkg-config"):
 
 def LibraryExists(lib, lpath=[]):
     """ Returns True if this library was found in the given search path, False otherwise. """
-
     target = GetTarget()
 
     for dir in lpath:
@@ -1517,6 +1534,7 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
         for d, v in defs.values():
             DefSymbol(target_pkg, d, v)
         return
+
     elif (GetHost() == "darwin" and framework != None):
         if (os.path.isdir("/Library/Frameworks/%s.framework" % framework) or
             os.path.isdir("/System/Library/Frameworks/%s.framework" % framework) or
@@ -1532,6 +1550,7 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
             print("%sERROR:%s Could not locate framework %s, aborting build" % (GetColor("red"), GetColor(), framework))
             exit()
         return
+
     elif (LocateBinary(tool) != None and (tool != "pkg-config" or pkgconfig != None)):
         if (isinstance(pkgconfig, str) or tool != "pkg-config"):
             if (PkgConfigHavePkg(pkgconfig, tool)):
@@ -1553,6 +1572,7 @@ def SmartPkgEnable(pkg, pkgconfig = None, libs = None, incs = None, defs = None,
         else:
             print("%sERROR:%s Could not locate pkg-config package %s, aborting build" % (GetColor("red"), GetColor(), pkgconfig))
             exit()
+
     else:
         # Okay, our pkg-config attempts failed. Let's try locating the libs by ourselves.
         have_pkg = True
@@ -1825,56 +1845,84 @@ def SdkLocateMax():
                             SDK[version+"CS"] = top + subdir
 
 def SdkLocatePython(force_use_sys_executable = False):
-    if (PkgSkip("PYTHON")==0):
-        if GetTarget() != GetHost():
-            exit('Use --no-python when cross-compiling until support has been added')
-
-        if (GetTarget() == 'windows' and not force_use_sys_executable):
-            SDK["PYTHON"] = GetThirdpartyBase()+"/win-python"
-            if (GetOptimize() <= 2):
-                SDK["PYTHON"] += "-dbg"
-            if (GetTargetArch() == 'x64' and os.path.isdir(SDK["PYTHON"] + "-x64")):
-                SDK["PYTHON"] += "-x64"
-
-            SDK["PYTHONEXEC"] = SDK["PYTHON"].replace('/', '\\') + "\\python"
-            if (GetOptimize() <= 2):
-                SDK["PYTHONEXEC"] += "_d.exe"
-            else:
-                SDK["PYTHONEXEC"] += ".exe"
+    if PkgSkip("PYTHON"):
+        SDK["PYTHONEXEC"] = os.path.realpath(sys.executable)
+        return
+
+    if CrossCompiling():
+        force_use_sys_executable = False
+
+    if (GetTarget() == 'windows' and not force_use_sys_executable):
+        SDK["PYTHON"] = GetThirdpartyBase() + "/win-python"
+        if (GetOptimize() <= 2):
+            SDK["PYTHON"] += "-dbg"
+        if (GetTargetArch() == 'x64' and os.path.isdir(SDK["PYTHON"] + "-x64")):
+            SDK["PYTHON"] += "-x64"
+
+        SDK["PYTHONEXEC"] = SDK["PYTHON"].replace('/', '\\') + "\\python"
+        if (GetOptimize() <= 2):
+            SDK["PYTHONEXEC"] += "_d.exe"
+        else:
+            SDK["PYTHONEXEC"] += ".exe"
 
-            if (not os.path.isfile(SDK["PYTHONEXEC"])):
-                exit("Could not find %s!" % SDK["PYTHONEXEC"])
+        if (not os.path.isfile(SDK["PYTHONEXEC"])):
+            exit("Could not find %s!" % SDK["PYTHONEXEC"])
 
-            # Determine which version it is by checking which dll is in the directory.
-            if (GetOptimize() <= 2):
-                py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]_d.dll")
-            else:
-                py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9].dll")
+        # Determine which version it is by checking which dll is in the directory.
+        if (GetOptimize() <= 2):
+            py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9]_d.dll")
+        else:
+            py_dlls = glob.glob(SDK["PYTHON"] + "/python[0-9][0-9].dll")
 
-            if len(py_dlls) == 0:
-                exit("Could not find the Python dll in %s." % (SDK["PYTHON"]))
-            elif len(py_dlls) > 1:
-                exit("Found multiple Python dlls in %s." % (SDK["PYTHON"]))
+        if len(py_dlls) == 0:
+            exit("Could not find the Python dll in %s." % (SDK["PYTHON"]))
+        elif len(py_dlls) > 1:
+            exit("Found multiple Python dlls in %s." % (SDK["PYTHON"]))
 
-            py_dll = os.path.basename(py_dlls[0])
-            SDK["PYTHONVERSION"] = "python" + py_dll[6] + "." + py_dll[7]
+        py_dll = os.path.basename(py_dlls[0])
+        SDK["PYTHONVERSION"] = "python" + py_dll[6] + "." + py_dll[7]
 
-        elif (GetTarget() == 'windows'):
-            SDK["PYTHON"] = os.path.dirname(sysconfig.get_python_inc())
-            SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version()
-            SDK["PYTHONEXEC"] = sys.executable
+        os.environ["PYTHONHOME"] = SDK["PYTHON"]
 
+    elif CrossCompiling():
+        tp_python = os.path.join(GetThirdpartyDir(), "python")
+        SDK["PYTHON"] = tp_python + "/include"
+
+        if GetTarget() == 'darwin':
+            py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].dylib")
         else:
-            SDK["PYTHON"] = sysconfig.get_python_inc()
-            SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version()
-            SDK["PYTHONEXEC"] = os.path.realpath(sys.executable)
+            py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].so")
+
+        if len(py_libs) == 0:
+            py_libs = glob.glob(tp_python + "/lib/libpython[0-9].[0-9].a")
+
+        if len(py_libs) == 0:
+            exit("Could not find the Python library in %s." % (tp_python))
+        elif len(py_libs) > 1:
+            exit("Found multiple Python libraries in %s." % (tp_python))
 
-        if GetVerbose():
-            print("Using Python %s build located at %s" % (SDK["PYTHONVERSION"][6:9], SDK["PYTHON"]))
+        py_lib = os.path.basename(py_libs[0])
+        SDK["PYTHONVERSION"] = "python" + py_lib[9] + "." + py_lib[11]
+
+    elif (GetTarget() == 'windows'):
+        SDK["PYTHON"] = os.path.dirname(sysconfig.get_python_inc())
+        SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version()
+        SDK["PYTHONEXEC"] = sys.executable
 
     else:
+        SDK["PYTHON"] = sysconfig.get_python_inc()
+        SDK["PYTHONVERSION"] = "python" + sysconfig.get_python_version()
         SDK["PYTHONEXEC"] = os.path.realpath(sys.executable)
 
+    if CrossCompiling():
+        SDK["PYTHONEXEC"] = sys.executable
+        host_version = "python" + sysconfig.get_python_version()
+        if SDK["PYTHONVERSION"] != host_version:
+            exit("Host Python version (%s) must be the same as target Python version (%s)!" % (host_version, SDK["PYTHONVERSION"]))
+
+    if GetVerbose():
+        print("Using Python %s build located at %s" % (SDK["PYTHONVERSION"][6:9], SDK["PYTHON"]))
+
 def SdkLocateVisualStudio():
     if (GetHost() != "windows"): return
     vcdir = GetRegistryKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VC7", "10.0")
@@ -2025,9 +2073,9 @@ def SdkLocateAndroid():
     SDK["ANDROID_NDK"] = ndk_root
 
     # Determine the toolchain location.
-    gcc_ver = '4.7'
+    gcc_ver = '4.8'
     arch = GetTargetArch()
-    if arch == 'armv7' or arch == 'arm':
+    if arch == 'armv7a' or arch == 'arm':
         arch = 'arm'
         toolchain = 'arm-linux-androideabi-' + gcc_ver
     elif arch == 'x86':
@@ -2045,8 +2093,10 @@ def SdkLocateAndroid():
     #IncDirectory("ALWAYS", os.path.join(SDK["SYSROOT"], 'usr', 'include'))
 
     stdlibc = os.path.join(ndk_root, 'sources', 'cxx-stl', 'gnu-libstdc++', gcc_ver)
-    IncDirectory("ALWAYS", os.path.join(stdlibc, 'include'))
-    IncDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi, 'include'))
+    SDK["ANDROID_STL"] = stdlibc
+
+    #IncDirectory("ALWAYS", os.path.join(stdlibc, 'include'))
+    #IncDirectory("ALWAYS", os.path.join(stdlibc, 'libs', abi, 'include'))
 
     stl_lib = os.path.join(stdlibc, 'libs', abi, 'libgnustl_shared.so')
     LibName("ALWAYS", stl_lib)
@@ -2231,7 +2281,9 @@ def SetupBuildEnvironment(compiler):
         global SYS_LIB_DIRS
 
         # gcc doesn't add this one, but we do want it:
-        SYS_LIB_DIRS.append('/usr/local/lib')
+        local_lib = SDK.get("SYSROOT", "") + "/usr/local/lib"
+        if os.path.isdir(local_lib):
+            SYS_LIB_DIRS.append(local_lib)
 
         cmd = GetCXX() + " -print-search-dirs"
 
@@ -2253,7 +2305,8 @@ def SetupBuildEnvironment(compiler):
         returnval = handle.close()
         if returnval != None and returnval != 0:
             print("%sWARNING:%s %s failed" % (GetColor("red"), GetColor(), cmd))
-            SYS_LIB_DIRS += ['/usr/lib']
+            SYS_LIB_DIRS += [SDK.get("SYSROOT", "") + "/usr/lib"]
+
         elif GetVerbose():
             print("System library search path: %s" % ':'.join(SYS_LIB_DIRS))
 
@@ -2753,4 +2806,4 @@ def TargetAdd(target, dummy=0, opts=0, input=0, dep=0, ipath=0, winrc=0):
         t.deps[FindLocation("dtool_have_python.dat", [])] = 1
 
     if target.endswith(".pz") and not CrossCompiling():
-        t.deps[FindLocation("pzip.exe", [])] = 1     
+        t.deps[FindLocation("pzip.exe", [])] = 1

+ 30 - 2
panda/src/android/android_main.cxx

@@ -16,6 +16,7 @@
 #include "config_util.h"
 #include "virtualFileMountAndroidAsset.h"
 #include "virtualFileSystem.h"
+#include "filename.h"
 
 #include "config_display.h"
 //#define OPENGLES_1
@@ -45,9 +46,36 @@ void android_main(struct android_app* app) {
     return;
   }
 
+  // Fetch the data directory.
+  jclass activity_class = env->GetObjectClass(activity->clazz);
+  jmethodID get_appinfo = env->GetMethodID(activity_class, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;");
+
+  jobject appinfo = env->CallObjectMethod(activity->clazz, get_appinfo);
+  jclass appinfo_class = env->GetObjectClass(appinfo);
+
+  // Fetch the path to the data directory.
+  jfieldID datadir_field = env->GetFieldID(appinfo_class, "dataDir", "Ljava/lang/String;");
+  jstring datadir = (jstring) env->GetObjectField(appinfo, datadir_field);
+  const char *data_path = env->GetStringUTFChars(datadir, NULL);
+
+  Filename::_internal_data_dir = data_path;
+  android_cat.info() << "Path to data: " << data_path << "\n";
+
+  env->ReleaseStringUTFChars(datadir, data_path);
+
+  // Fetch the path to the library directory.
+  jfieldID libdir_field = env->GetFieldID(appinfo_class, "nativeLibraryDir", "Ljava/lang/String;");
+  jstring libdir = (jstring) env->GetObjectField(appinfo, libdir_field);
+  const char *lib_path = env->GetStringUTFChars(libdir, NULL);
+
+  string dtool_name = string(lib_path) + "/libp3dtool.so";
+  ExecutionEnvironment::set_dtool_name(dtool_name);
+  android_cat.info() << "Path to dtool: " << dtool_name << "\n";
+
+  env->ReleaseStringUTFChars(libdir, lib_path);
+
   // Get the path to the APK.
-  jclass clazz = env->GetObjectClass(activity->clazz);
-  jmethodID methodID = env->GetMethodID(clazz, "getPackageCodePath", "()Ljava/lang/String;");
+  jmethodID methodID = env->GetMethodID(activity_class, "getPackageCodePath", "()Ljava/lang/String;");
   jstring code_path = (jstring) env->CallObjectMethod(activity->clazz, methodID);
 
   const char* apk_path;

+ 11 - 2
panda/src/androiddisplay/androidGraphicsWindow.cxx

@@ -326,7 +326,7 @@ open_window() {
     return false;
   }
 
-  //_fb_properties = androidgsg->get_fb_properties();
+  _fb_properties = androidgsg->get_fb_properties();
 
   androiddisplay_cat.error() << "open_window done\n";
 
@@ -434,7 +434,7 @@ handle_command(struct android_app *app, int32_t command) {
 void AndroidGraphicsWindow::
 ns_handle_command(int32_t command) {
   WindowProperties properties;
-  
+
   switch (command) {
     case APP_CMD_SAVE_STATE:
       // The system has asked us to save our current state.  Do so.
@@ -446,16 +446,25 @@ ns_handle_command(int32_t command) {
       // The window is being shown, get it ready.
       if (_app->window != NULL) {
         create_surface();
+        properties.set_size(ANativeWindow_getWidth(_app->window),
+                            ANativeWindow_getHeight(_app->window));
         properties.set_minimized(false);
         system_changed_properties(properties);
       }
       break;
+    case APP_CMD_CONFIG_CHANGED:
+      properties.set_size(ANativeWindow_getWidth(_app->window),
+                          ANativeWindow_getHeight(_app->window));
+      system_changed_properties(properties);
+      break;
     case APP_CMD_TERM_WINDOW:
       destroy_surface();
       properties.set_minimized(true);
       system_changed_properties(properties);
       break;
     case APP_CMD_WINDOW_RESIZED:
+      properties.set_size(ANativeWindow_getWidth(_app->window),
+                          ANativeWindow_getHeight(_app->window));
       break;
     case APP_CMD_WINDOW_REDRAW_NEEDED:
       break;

+ 2 - 0
panda/src/display/Sources.pp

@@ -38,6 +38,7 @@
     lru.h \
     nativeWindowHandle.I nativeWindowHandle.h \
     parasiteBuffer.I parasiteBuffer.h \
+    pStatGPUTimer.I pStatGPUTimer.h \
     windowHandle.I windowHandle.h \
     windowProperties.I windowProperties.h \
     renderBuffer.h \
@@ -112,6 +113,7 @@
     lru.h \
     nativeWindowHandle.I nativeWindowHandle.h \
     parasiteBuffer.I parasiteBuffer.h \
+    pStatGPUTimer.I pStatGPUTimer.h \
     windowHandle.I windowHandle.h \
     windowProperties.I windowProperties.h \
     renderBuffer.h \

+ 4 - 4
panda/src/display/displayRegion.I

@@ -580,9 +580,9 @@ get_region_pixels_i(int i, int &xo, int &yo, int &w, int &h) const {
   CDReader cdata(_cycler);
   const Region &region = cdata->_regions[i];
   xo = region._pixels_i[0];
-  yo = region._pixels_i[2];
+  yo = region._pixels_i[3];
   w = region._pixels_i[1] - xo;
-  h = region._pixels_i[3] - yo;
+  h = region._pixels_i[2] - yo;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1068,9 +1068,9 @@ INLINE void DisplayRegionPipelineReader::
 get_region_pixels_i(int i, int &xo, int &yo, int &w, int &h) const {
   const DisplayRegion::Region &region = _cdata->_regions[i];
   xo = region._pixels_i[0];
-  yo = region._pixels_i[2];
+  yo = region._pixels_i[3];
   w = region._pixels_i[1] - xo;
-  h = region._pixels_i[3] - yo;
+  h = region._pixels_i[2] - yo;
 }
 
 ////////////////////////////////////////////////////////////////////

+ 130 - 113
panda/src/display/graphicsEngine.cxx

@@ -24,6 +24,7 @@
 #include "cullTraverser.h"
 #include "clockObject.h"
 #include "pStatTimer.h"
+#include "pStatGPUTimer.h"
 #include "pStatClient.h"
 #include "pStatCollector.h"
 #include "mutexHolder.h"
@@ -52,7 +53,7 @@
   #define WINDOWS_LEAN_AND_MEAN
   #include <WinSock2.h>
   #include <wtypes.h>
-  #undef WINDOWS_LEAN_AND_MEAN  
+  #undef WINDOWS_LEAN_AND_MEAN
 #else
   #include <sys/time.h>
 #endif
@@ -140,7 +141,7 @@ GraphicsEngine(Pipeline *pipeline) :
   _windows_sorted = true;
   _window_sort_index = 0;
   _needs_open_windows = false;
-  
+
   set_threading_model(GraphicsThreadingModel(threading_model));
   if (!_threading_model.is_default()) {
     display_cat.info()
@@ -188,7 +189,7 @@ set_threading_model(const GraphicsThreadingModel &threading_model) {
       return;
     }
   }
-    
+
 #ifndef THREADED_PIPELINE
   if (!threading_model.is_single_threaded()) {
     display_cat.warning()
@@ -238,7 +239,7 @@ get_threading_model() const {
 //
 //               If a null pointer is supplied for the gsg, then this
 //               routine will create a new gsg.
-//               
+//
 //               This routine is only called from the app thread.
 ////////////////////////////////////////////////////////////////////
 
@@ -283,7 +284,7 @@ make_output(GraphicsPipe *pipe,
   //  already-initialized gsg.
 
   // Simplify the input parameters.
-  
+
   int x_size = 0, y_size = 0;
   if (win_prop.has_size()) {
     x_size = win_prop.get_x_size();
@@ -359,7 +360,7 @@ make_output(GraphicsPipe *pipe,
     // an unencumbered GSG.
     return NULL;
   }
-  
+
   // Determine if a parasite buffer meets the user's specs.
 
   bool can_use_parasite = false;
@@ -381,7 +382,7 @@ make_output(GraphicsPipe *pipe,
   // Even if prefer-parasite-buffer is set, parasites are not preferred
   // if the host window is too small, or if the host window does not
   // have the requested properties.
-  
+
   if ((prefer_parasite_buffer) &&
       (can_use_parasite) &&
       (x_size <= host->get_x_size())&&
@@ -408,10 +409,10 @@ make_output(GraphicsPipe *pipe,
   }
 
   // Ask the pipe to create a window.
-  
+
   for (int retry=0; retry<10; retry++) {
     bool precertify = false;
-    PT(GraphicsOutput) window = 
+    PT(GraphicsOutput) window =
       pipe->make_output(name, fb_prop, win_prop, flags, this, gsg, host, retry, precertify);
     if (window != (GraphicsOutput *)NULL) {
       window->_sort = sort;
@@ -455,11 +456,11 @@ make_output(GraphicsPipe *pipe,
       nassertr(removed, NULL);
     }
   }
-  
+
   // Parasite buffers were not preferred, but the pipe could not
   // create a window to the user's specs.  Try a parasite as a
   // last hope.
-  
+
   if (can_use_parasite) {
     ParasiteBuffer *buffer = new ParasiteBuffer(host, name, x_size, y_size, flags);
     buffer->_sort = sort;
@@ -613,7 +614,7 @@ remove_all_windows() {
 //       Access: Published
 //  Description: Resets the framebuffer of the current window.  This
 //               is currently used by DirectX 8 only. It calls a
-//               reset_window function on each active window to 
+//               reset_window function on each active window to
 //               release/create old/new framebuffer
 ////////////////////////////////////////////////////////////////////
 void GraphicsEngine::
@@ -712,11 +713,11 @@ render_frame() {
     if (!_windows_sorted) {
       do_resort_windows();
     }
-    
+
     if (sync_flip && _flip_state != FS_flip) {
       do_flip_frame(current_thread);
     }
-    
+
     // Are any of the windows ready to be deleted?
     Windows new_windows;
     new_windows.reserve(_windows.size());
@@ -726,10 +727,10 @@ render_frame() {
       nassertv(win != NULL);
       if (win->get_delete_flag()) {
         do_remove_window(win, current_thread);
-        
+
       } else {
         new_windows.push_back(win);
-        
+
         // Let's calculate each scene's bounding volume here in App,
         // before we cycle the pipeline.  The cull traversal will
         // calculate it anyway, but if we calculate it in App first
@@ -770,11 +771,11 @@ render_frame() {
       }
       _loaded_textures.clear();
     }
-    
+
     // Now it's time to do any drawing from the main frame--after all of
     // the App code has executed, but before we begin the next frame.
     _app.do_frame(this, current_thread);
-    
+
     // Grab each thread's mutex again after all windows have flipped,
     // and wait for the thread to finish.
     {
@@ -783,32 +784,32 @@ render_frame() {
       for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
         RenderThread *thread = (*ti).second;
         thread->_cv_mutex.acquire();
-        
+
         while (thread->_thread_state != TS_wait) {
           thread->_cv_done.wait();
         }
       }
     }
-    
+
 #if defined(THREADED_PIPELINE) && defined(DO_PSTATS)
     _cyclers_pcollector.set_level(_pipeline->get_num_cyclers());
     _dirty_cyclers_pcollector.set_level(_pipeline->get_num_dirty_cyclers());
-    
+
 #ifdef DEBUG_THREADS
     if (PStatClient::is_connected()) {
       _pipeline->iterate_all_cycler_types(pstats_count_cycler_type, this);
       _pipeline->iterate_dirty_cycler_types(pstats_count_dirty_cycler_type, this);
     }
 #endif  // DEBUG_THREADS
-    
+
 #endif  // THREADED_PIPELINE && DO_PSTATS
-    
+
     GeomCacheManager::flush_level();
     CullTraverser::flush_level();
     RenderState::flush_level();
     TransformState::flush_level();
     CullableObject::flush_level();
-    
+
     // Now cycle the pipeline and officially begin the next frame.
 #ifdef THREADED_PIPELINE
     {
@@ -816,15 +817,15 @@ render_frame() {
       _pipeline->cycle();
     }
 #endif  // THREADED_PIPELINE
-    
+
     global_clock->tick(current_thread);
     if (global_clock->check_errors(current_thread)) {
       throw_event("clock_error");
     }
-    
+
 #ifdef DO_PSTATS
     PStatClient::main_tick();
-    
+
     // Reset our pcollectors that track data across the frame.
     CullTraverser::_nodes_pcollector.clear_level();
     CullTraverser::_geom_nodes_pcollector.clear_level();
@@ -833,18 +834,18 @@ render_frame() {
     GeomCacheManager::_geom_cache_record_pcollector.clear_level();
     GeomCacheManager::_geom_cache_erase_pcollector.clear_level();
     GeomCacheManager::_geom_cache_evict_pcollector.clear_level();
-    
+
     GraphicsStateGuardian::init_frame_pstats();
-    
+
     _transform_states_pcollector.set_level(TransformState::get_num_states());
     _render_states_pcollector.set_level(RenderState::get_num_states());
     if (pstats_unused_states) {
       _transform_states_unused_pcollector.set_level(TransformState::get_num_unused_states());
       _render_states_unused_pcollector.set_level(RenderState::get_num_unused_states());
     }
-    
+
     _sw_sprites_pcollector.clear_level();
-    
+
     _cnode_volume_pcollector.clear_level();
     _gnode_volume_pcollector.clear_level();
     _geom_volume_pcollector.clear_level();
@@ -869,18 +870,18 @@ render_frame() {
     _occlusion_passed_pcollector.clear_level();
     _occlusion_failed_pcollector.clear_level();
     _occlusion_tests_pcollector.clear_level();
-    
+
     if (PStatClient::is_connected()) {
       size_t small_buf = GeomVertexArrayData::get_small_lru()->get_total_size();
       size_t independent = GeomVertexArrayData::get_independent_lru()->get_total_size();
       size_t resident = VertexDataPage::get_global_lru(VertexDataPage::RC_resident)->get_total_size();
       size_t compressed = VertexDataPage::get_global_lru(VertexDataPage::RC_compressed)->get_total_size();
       size_t pending = VertexDataPage::get_pending_lru()->get_total_size();
-      
+
       VertexDataSaveFile *save_file = VertexDataPage::get_save_file();
       size_t total_disk = save_file->get_total_file_size();
       size_t used_disk = save_file->get_used_file_size();
-      
+
       _vertex_data_small_pcollector.set_level(small_buf);
       _vertex_data_independent_pcollector.set_level(independent);
       _vertex_data_pending_pcollector.set_level(pending);
@@ -889,11 +890,11 @@ render_frame() {
       _vertex_data_unused_disk_pcollector.set_level(total_disk - used_disk);
       _vertex_data_used_disk_pcollector.set_level(used_disk);
     }
-    
+
 #endif  // DO_PSTATS
-    
+
     GeomVertexArrayData::lru_epoch();
-    
+
     // Now signal all of our threads to begin their next frame.
     Threads::const_iterator ti;
     for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
@@ -904,24 +905,24 @@ render_frame() {
       }
       thread->_cv_mutex.release();
     }
-    
+
     // Some threads may still be drawing, so indicate that we have to
     // wait for those threads before we can flip.
     _flip_state = _auto_flip ? FS_flip : FS_draw;
   }
 
   // Now the lock is released.
-  
+
   if (yield_timeslice) {
     // Nap for a moment to yield the timeslice, to be polite to other
     // running applications.
     PStatTimer timer(_yield_pcollector, current_thread);
     Thread::force_yield();
-  } else if (!Thread::is_true_threads()) { 
+  } else if (!Thread::is_true_threads()) {
     PStatTimer timer(_yield_pcollector, current_thread);
     Thread::consider_yield();
   }
-  
+
   // Anything that happens outside of GraphicsEngine::render_frame()
   // is deemed to be App.
   _app_pcollector.start();
@@ -961,11 +962,11 @@ open_windows() {
     for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
       RenderThread *thread = (*ti).second;
       thread->_cv_mutex.acquire();
-      
+
       while (thread->_thread_state != TS_wait) {
         thread->_cv_done.wait();
       }
-      
+
       thread->_thread_state = TS_do_windows;
       thread->_cv_start.notify();
       thread->_cv_mutex.release();
@@ -1004,7 +1005,7 @@ sync_frame() {
 //               we seems to return once all draw calls have been submitted.
 //               Calling 'flip_frame' after this function should immediately
 //               cause a buffer flip.  This function will only work in
-//               opengl right now, for all other graphics pipelines it will 
+//               opengl right now, for all other graphics pipelines it will
 //               simply return immediately.  In opengl it's a bit of a hack:
 //               it will attempt to read a single pixel from the frame buffer to
 //               force the graphics card to finish drawing before it returns
@@ -1082,7 +1083,7 @@ extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
     WindowRenderer *wr = get_window_renderer(draw_name, 0);
     RenderThread *thread = (RenderThread *)wr;
     MutexHolder holder2(thread->_cv_mutex);
-      
+
     while (thread->_thread_state != TS_wait) {
       thread->_cv_done.wait();
     }
@@ -1131,7 +1132,7 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph
     WindowRenderer *wr = get_window_renderer(draw_name, 0);
     RenderThread *thread = (RenderThread *)wr;
     MutexHolder holder2(thread->_cv_mutex);
-      
+
     while (thread->_thread_state != TS_wait) {
       thread->_cv_done.wait();
     }
@@ -1149,7 +1150,7 @@ dispatch_compute(const LVecBase3i &work_groups, const ShaderAttrib *sattr, Graph
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::get_global_ptr
 //       Access: Published, Static
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 GraphicsEngine *GraphicsEngine::
 get_global_ptr() {
@@ -1217,7 +1218,7 @@ do_cull(CullHandler *cull_handler, SceneSetup *scene_setup,
       local_frustum = DCAST(GeometricBoundingVolume, bv->make_copy());
 
       NodePath scene_parent = scene_setup->get_scene_root().get_parent(current_thread);
-      CPT(TransformState) cull_center_transform = 
+      CPT(TransformState) cull_center_transform =
         scene_setup->get_cull_center().get_transform(scene_parent, current_thread);
       local_frustum->xform(cull_center_transform->get_mat());
 
@@ -1338,7 +1339,7 @@ cull_and_draw_together(const GraphicsEngine::Windows &wlist,
 
       if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
         win->clear(current_thread);
-      
+
         int num_display_regions = win->get_num_active_display_regions();
         for (int i = 0; i < num_display_regions; i++) {
           DisplayRegion *dr = win->get_active_display_region(i);
@@ -1377,7 +1378,7 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr,
   GraphicsStateGuardian *gsg = win->get_gsg();
   nassertv(gsg != (GraphicsStateGuardian *)NULL);
 
-  DisplayRegionPipelineReader *dr_reader = 
+  DisplayRegionPipelineReader *dr_reader =
     new DisplayRegionPipelineReader(dr, current_thread);
 
   win->change_scenes(dr_reader);
@@ -1410,9 +1411,9 @@ cull_and_draw_together(GraphicsOutput *win, DisplayRegion *dr,
         // Issue the cull callback on this DisplayRegion.
         DisplayRegionCullCallbackData cbdata(&cull_handler, scene_setup);
         cbobj->do_callback(&cbdata);
-        
+
         // The callback has taken care of the culling.
-        
+
       } else {
         // Perform the cull normally.
         dr->do_cull(&cull_handler, scene_setup, gsg, current_thread);
@@ -1456,7 +1457,7 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
       for (int i = 0; i < num_display_regions; ++i) {
         DisplayRegion *dr = win->get_active_display_region(i);
         if (dr != (DisplayRegion *)NULL) {
-          DisplayRegionPipelineReader *dr_reader = 
+          DisplayRegionPipelineReader *dr_reader =
             new DisplayRegionPipelineReader(dr, current_thread);
           NodePath camera = dr_reader->get_camera();
           AlreadyCulled::iterator aci = already_culled.insert(AlreadyCulled::value_type(camera, (DisplayRegion *)NULL)).first;
@@ -1467,7 +1468,7 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
             dr_reader = NULL;
             (*aci).second = dr;
             cull_to_bins(win, dr, current_thread);
-            
+
           } else {
             // We have already culled a scene using this camera in
             // this thread, and now we're being asked to cull another
@@ -1481,7 +1482,7 @@ cull_to_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
                                 setup_scene(win->get_gsg(), dr_reader),
                                 current_thread);
           }
-        
+
           if (dr_reader != (DisplayRegionPipelineReader *)NULL) {
             delete dr_reader;
           }
@@ -1539,7 +1540,7 @@ cull_to_bins(GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
     PStatTimer timer(_cull_sort_pcollector, current_thread);
     cull_result->finish_cull(scene_setup, current_thread);
   }
-  
+
   // Save the results for next frame.
   dr->set_cull_result(cull_result, scene_setup, current_thread);
 }
@@ -1560,53 +1561,73 @@ draw_bins(const GraphicsEngine::Windows &wlist, Thread *current_thread) {
   size_t wlist_size = wlist.size();
   for (size_t wi = 0; wi < wlist_size; ++wi) {
     GraphicsOutput *win = wlist[wi];
+
     if (win->is_active()) {
-      if (win->flip_ready()) {
+      GraphicsStateGuardian *gsg = win->get_gsg();
+
+      GraphicsOutput *host = win->get_host();
+      if (host->flip_ready()) {
         {
+          // We can't use a PStatGPUTimer before begin_frame, so when using GPU
+          // timing, it is advisable to set auto-flip to #t.
           PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
-          win->begin_flip();
+          host->begin_flip();
         }
         {
           PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
-          win->end_flip();
+          host->end_flip();
         }
       }
 
-      PStatTimer timer(win->get_draw_window_pcollector(), current_thread);
       if (win->begin_frame(GraphicsOutput::FM_render, current_thread)) {
-        win->clear(current_thread);
+        // We have to place this collector inside begin_frame, because
+        // we need a current context for PStatGPUTimer to work.
+        {
+          PStatGPUTimer timer(win->get_gsg(), win->get_draw_window_pcollector(), current_thread);
+          win->clear(current_thread);
 
-        if (display_cat.is_spam()) {
-          display_cat.spam()
-            << "Drawing window " << win->get_name() << "\n";
-        }
-        int num_display_regions = win->get_num_active_display_regions();
-        for (int i = 0; i < num_display_regions; ++i) {
-          DisplayRegion *dr = win->get_active_display_region(i);
-          if (dr != (DisplayRegion *)NULL) {
-            draw_bins(win, dr, current_thread);
+          if (display_cat.is_spam()) {
+            display_cat.spam()
+              << "Drawing window " << win->get_name() << "\n";
+          }
+          int num_display_regions = win->get_num_active_display_regions();
+          for (int i = 0; i < num_display_regions; ++i) {
+            DisplayRegion *dr = win->get_active_display_region(i);
+            if (dr != (DisplayRegion *)NULL) {
+              draw_bins(win, dr, current_thread);
+            }
           }
         }
         win->end_frame(GraphicsOutput::FM_render, current_thread);
 
         if (_auto_flip) {
+#ifdef DO_PSTATS
+          // This is a good time to perform a latency query.
+          if (win->get_gsg()->get_timer_queries_active()) {
+            win->get_gsg()->issue_timer_query(GraphicsStateGuardian::_command_latency_pcollector.get_index());
+          }
+#endif
+
           if (win->flip_ready()) {
             {
+              // begin_flip doesn't do anything interesting, let's not waste two timer queries on that.
               PStatTimer timer(GraphicsEngine::_flip_begin_pcollector, current_thread);
               win->begin_flip();
             }
             {
-              PStatTimer timer(GraphicsEngine::_flip_end_pcollector, current_thread);
+              PStatGPUTimer timer(win->get_gsg(), GraphicsEngine::_flip_end_pcollector, current_thread);
               win->end_flip();
             }
           }
         }
+
       } else {
         if (display_cat.is_spam()) {
           display_cat.spam()
             << "Not drawing window " << win->get_name() << "\n";
         }
       }
+
     } else {
       if (display_cat.is_spam()) {
         display_cat.spam()
@@ -1723,8 +1744,6 @@ ready_flip_windows(const GraphicsEngine::Windows &wlist, Thread *current_thread)
   }
 }
 
-
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::do_sync_frame
 //       Access: Private
@@ -1752,7 +1771,6 @@ do_sync_frame(Thread *current_thread) {
   _flip_state = FS_sync;
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::do_ready_flip
 //       Access: Private
@@ -1778,8 +1796,6 @@ do_ready_flip(Thread *current_thread) {
   }
   _app.do_ready_flip(this,current_thread);
   _flip_state = FS_sync;
-  
-
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1812,7 +1828,7 @@ do_flip_frame(Thread *current_thread) {
       }
     }
   }
-  
+
   // Now signal all of our threads to flip the windows.
   _app.do_flip(this, current_thread);
 
@@ -1896,7 +1912,7 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
     // There must be a singular transform over the scene.
     if (!_singular_warning_last_frame) {
       display_cat.warning()
-        << "Scene " << scene_root << " has net scale (" 
+        << "Scene " << scene_root << " has net scale ("
         << scene_root.get_scale(NodePath()) << "); cannot render.\n";
       _singular_warning_this_frame = true;
     }
@@ -1907,7 +1923,7 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
     // There must be a singular transform over the camera.
     if (!_singular_warning_last_frame) {
       display_cat.warning()
-        << "Camera " << camera << " has net scale (" 
+        << "Camera " << camera << " has net scale ("
         << camera.get_scale(NodePath()) << "); cannot render.\n";
     }
     _singular_warning_this_frame = true;
@@ -1951,11 +1967,12 @@ setup_scene(GraphicsStateGuardian *gsg, DisplayRegionPipelineReader *dr) {
 void GraphicsEngine::
 do_draw(CullResult *cull_result, SceneSetup *scene_setup,
         GraphicsOutput *win, DisplayRegion *dr, Thread *current_thread) {
-  // Statistics
-  PStatTimer timer(dr->get_draw_region_pcollector(), current_thread);
 
-  GraphicsStateGuardian *gsg = win->get_gsg();
   CallbackObject *cbobj;
+  GraphicsStateGuardian *gsg = win->get_gsg();
+
+  // Statistics
+  PStatGPUTimer timer(gsg, dr->get_draw_region_pcollector(), current_thread);
 
   {
     DisplayRegionPipelineReader dr_reader(dr, current_thread);
@@ -2034,20 +2051,20 @@ do_add_window(GraphicsOutput *window,
   _windows_sorted = false;
   _windows.push_back(window);
 
-  WindowRenderer *cull = 
+  WindowRenderer *cull =
     get_window_renderer(threading_model.get_cull_name(),
                         threading_model.get_cull_stage());
-  WindowRenderer *draw = 
+  WindowRenderer *draw =
     get_window_renderer(threading_model.get_draw_name(),
                         threading_model.get_draw_stage());
-  
+
   if (threading_model.get_cull_sorting()) {
     cull->add_window(cull->_cull, window);
     draw->add_window(draw->_draw, window);
   } else {
     cull->add_window(cull->_cdraw, window);
   }
-  
+
   // Ask the pipe which thread it prefers to run its windowing
   // commands in (the "window thread").  This is the thread that
   // handles the commands to open, resize, etc. the window.  X
@@ -2057,7 +2074,7 @@ do_add_window(GraphicsOutput *window,
   // has been bound in a given thread, it cannot subsequently be bound
   // in any other thread, and we have to bind a context in
   // open_window()).
-  
+
   switch (window->get_pipe()->get_preferred_window_thread()) {
   case GraphicsPipe::PWT_app:
     _app.add_window(_app._window, window);
@@ -2096,8 +2113,8 @@ do_add_gsg(GraphicsStateGuardian *gsg, GraphicsPipe *pipe,
   }
 
   auto_adjust_capabilities(gsg);
-  
-  WindowRenderer *draw = 
+
+  WindowRenderer *draw =
     get_window_renderer(threading_model.get_draw_name(),
                         threading_model.get_draw_stage());
 
@@ -2228,7 +2245,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       << "textures_power_2 to 'up' or 'down'.\n";
     textures_power_2 = ATS_down; // Not a fix.  Just suppresses further error messages.
   }
-  
+
   if (textures_auto_power_2 && !Texture::has_textures_power_2()) {
     if (gsg->get_supports_tex_non_pow2()) {
       Texture::set_textures_power_2(ATS_none);
@@ -2236,13 +2253,13 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       Texture::set_textures_power_2(textures_power_2);
     }
   }
-  
-  if ((Texture::get_textures_power_2() == ATS_none) && 
+
+  if ((Texture::get_textures_power_2() == ATS_none) &&
       (!gsg->get_supports_tex_non_pow2())) {
-    
+
     // Overaggressive configuration detected
-    
-    display_cat.error() 
+
+    display_cat.error()
       << "The 'textures_power_2' configuration is set to 'none', meaning \n"
       << "that non-power-of-two texture support is required, but the video \n"
       << "driver I'm trying to use does not support non-power-of-two textures.\n";
@@ -2251,7 +2268,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       display_cat.error()
         << "The 'none' did not come from the config file.  In other words,\n"
         << "the variable 'textures_power_2' was altered procedurally.\n";
-    
+
       if (textures_auto_power_2) {
         display_cat.error()
           << "It is possible that it was set by panda's automatic mechanisms,\n"
@@ -2264,7 +2281,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       }
     }
   }
-  
+
   if (shader_auto_utilization && (shader_utilization != SUT_none)) {
     display_cat.error()
       << "Invalid panda config file: if you set the config-variable\n"
@@ -2272,7 +2289,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       << "shader_utilization to 'none'.\n";
     shader_utilization = SUT_none; // Not a fix.  Just suppresses further error messages.
   }
-  
+
   if (shader_auto_utilization && !Shader::have_shader_utilization()) {
     if (gsg->get_supports_basic_shaders()) {
       Shader::set_shader_utilization(SUT_basic);
@@ -2280,13 +2297,13 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       Shader::set_shader_utilization(SUT_none);
     }
   }
-  
-  if ((Shader::get_shader_utilization() != SUT_none) && 
+
+  if ((Shader::get_shader_utilization() != SUT_none) &&
       (!gsg->get_supports_basic_shaders())) {
-    
+
     // Overaggressive configuration detected
-    
-    display_cat.error() 
+
+    display_cat.error()
       << "The 'shader_utilization' config variable is set, meaning\n"
       << "that panda may try to generate shaders.  However, the video \n"
       << "driver I'm trying to use does not support shaders.\n";
@@ -2295,7 +2312,7 @@ auto_adjust_capabilities(GraphicsStateGuardian *gsg) {
       display_cat.error()
         << "The 'shader_utilization' setting did not come from the config\n"
         << "file.  In other words, it was altered procedurally.\n";
-    
+
       if (shader_auto_utilization) {
         display_cat.error()
           << "It is possible that it was set by panda's automatic mechanisms,\n"
@@ -2323,7 +2340,7 @@ terminate_threads(Thread *current_thread) {
   // We spend almost our entire time in this method just waiting for
   // threads.  Time it appropriately.
   PStatTimer timer(_wait_pcollector, current_thread);
-  
+
   // First, wait for all the threads to finish their current frame.
   // Grabbing the mutex should achieve that.
   Threads::const_iterator ti;
@@ -2331,7 +2348,7 @@ terminate_threads(Thread *current_thread) {
     RenderThread *thread = (*ti).second;
     thread->_cv_mutex.acquire();
   }
-  
+
   // Now tell them to close their windows and terminate.
   for (ti = _threads.begin(); ti != _threads.end(); ++ti) {
     RenderThread *thread = (*ti).second;
@@ -2345,7 +2362,7 @@ terminate_threads(Thread *current_thread) {
     RenderThread *thread = (*ti).second;
     thread->join();
   }
-  
+
   _threads.clear();
 }
 
@@ -2451,7 +2468,7 @@ get_window_renderer(const string &name, int pipeline_stage) {
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::WindowRenderer::Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 GraphicsEngine::WindowRenderer::
 WindowRenderer(const string &name) :
@@ -2589,7 +2606,7 @@ do_frame(GraphicsEngine *engine, Thread *current_thread) {
         // This one has no outstanding pointers; clean it up.
         GraphicsPipe *pipe = gsg->get_pipe();
         engine->close_gsg(pipe, gsg);
-      } else { 
+      } else {
         // This one is ok; preserve it.
         new_gsgs.insert(gsg);
       }
@@ -2668,12 +2685,12 @@ do_close(GraphicsEngine *engine, Thread *current_thread) {
       // This one has no outstanding pointers; clean it up.
       GraphicsPipe *pipe = gsg->get_pipe();
       engine->close_gsg(pipe, gsg);
-    } else { 
+    } else {
       // This one is ok; preserve it.
       new_gsgs.insert(gsg);
     }
   }
-  
+
   _gsgs.swap(new_gsgs);
 }
 
@@ -2729,10 +2746,10 @@ any_done_gsgs() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::RenderThread::Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 GraphicsEngine::RenderThread::
-RenderThread(const string &name, GraphicsEngine *engine) : 
+RenderThread(const string &name, GraphicsEngine *engine) :
   Thread(name, "Main"),
   WindowRenderer(name),
   _engine(engine),

+ 52 - 3
panda/src/display/graphicsStateGuardian.I

@@ -1,6 +1,6 @@
 // Filename: graphicsStateGuardian.I
 // Created by:  drose (24Sep99)
-// Updated by: fperazzi, PandaSE (29Apr10) (added 
+// Updated by: fperazzi, PandaSE (29Apr10) (added
 // get_max_2d_texture_array_layers and related)
 //
 ////////////////////////////////////////////////////////////////////
@@ -334,10 +334,10 @@ get_max_3d_texture_dimension() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_max_2d_texture_array_layers
 //       Access: Published
-//  Description: Returns the largest possible number of pages, or -1 
+//  Description: Returns the largest possible number of pages, or -1
 //               if there is no particular limit. Returns 0 if 2-d
 //               texture arrays not supported.
-//               
+//
 //               The value returned may not be meaningful until after
 //               the graphics context has been fully created (e.g. the
 //               window has been opened).
@@ -447,6 +447,16 @@ get_supports_tex_non_pow2() const {
   return _supports_tex_non_pow2;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_texture_srgb
+//       Access: Published
+//  Description: Returns true if this GSG can handle sRGB textures.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_supports_texture_srgb() const {
+  return _supports_texture_srgb;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_supports_compressed_texture
 //       Access: Published
@@ -713,6 +723,45 @@ get_supports_geometry_instancing() const {
   return _supports_geometry_instancing;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_occlusion_query
+//       Access: Published
+//  Description: Returns true if this GSG supports an occlusion query.
+//               If this is true, then begin_occlusion_query() and
+//               end_occlusion_query() may be called to bracket a
+//               sequence of draw_triangles() (or whatever) calls to
+//               measure pixels that pass the depth test.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+get_supports_occlusion_query() const {
+  return _supports_occlusion_query;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_timer_query
+//       Access: Published
+//  Description: Returns true if this GSG supports a timer query.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+get_supports_timer_query() const {
+  return _supports_timer_query;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_timer_queries_active
+//       Access: Published
+//  Description: Returns true if timer queries are currently
+//               enabled on this GSG.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+get_timer_queries_active() const {
+#ifdef DO_PSTATS
+  return _timer_queries_active;
+#else
+  return false;
+#endif
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_max_color_targets
 //       Access: Published

+ 213 - 39
panda/src/display/graphicsStateGuardian.cxx

@@ -28,6 +28,7 @@
 #include "throw_event.h"
 #include "clockObject.h"
 #include "pStatTimer.h"
+#include "pStatGPUTimer.h"
 #include "geomTristrips.h"
 #include "geomTrifans.h"
 #include "geomLinestrips.h"
@@ -55,6 +56,7 @@
 #include "colorScaleAttrib.h"
 #include "clipPlaneAttrib.h"
 #include "fogAttrib.h"
+#include "config_pstats.h"
 
 #include <algorithm>
 #include <limits.h>
@@ -87,9 +89,19 @@ PStatCollector GraphicsStateGuardian::_draw_primitive_pcollector("Draw:Primitive
 PStatCollector GraphicsStateGuardian::_draw_set_state_pcollector("Draw:Set State");
 PStatCollector GraphicsStateGuardian::_clear_pcollector("Draw:Clear");
 PStatCollector GraphicsStateGuardian::_flush_pcollector("Draw:Flush");
+PStatCollector GraphicsStateGuardian::_compute_dispatch_pcollector("Draw:Compute dispatch");
 
 PStatCollector GraphicsStateGuardian::_wait_occlusion_pcollector("Wait:Occlusion");
+PStatCollector GraphicsStateGuardian::_wait_timer_pcollector("Wait:Timer Queries");
+PStatCollector GraphicsStateGuardian::_timer_queries_pcollector("Timer queries");
+PStatCollector GraphicsStateGuardian::_command_latency_pcollector("Command latency");
 
+PStatCollector GraphicsStateGuardian::_prepare_pcollector("Draw:Prepare");
+PStatCollector GraphicsStateGuardian::_prepare_texture_pcollector("Draw:Prepare:Texture");
+PStatCollector GraphicsStateGuardian::_prepare_geom_pcollector("Draw:Prepare:Geom");
+PStatCollector GraphicsStateGuardian::_prepare_shader_pcollector("Draw:Prepare:Shader");
+PStatCollector GraphicsStateGuardian::_prepare_vertex_buffer_pcollector("Draw:Prepare:Vertex buffer");
+PStatCollector GraphicsStateGuardian::_prepare_index_buffer_pcollector("Draw:Prepare:Index buffer");
 
 PStatCollector GraphicsStateGuardian::_draw_set_state_transform_pcollector("Draw:Set State:Transform");
 PStatCollector GraphicsStateGuardian::_draw_set_state_alpha_test_pcollector("Draw:Set State:Alpha test");
@@ -115,7 +127,6 @@ PStatCollector GraphicsStateGuardian::_draw_set_state_stencil_pcollector("Draw:S
 PStatCollector GraphicsStateGuardian::_draw_set_state_fog_pcollector("Draw:Set State:Fog");
 PStatCollector GraphicsStateGuardian::_draw_set_state_scissor_pcollector("Draw:Set State:Scissor");
 
-
 PT(TextureStage) GraphicsStateGuardian::_alpha_scale_texture_stage = NULL;
 
 TypeHandle GraphicsStateGuardian::_type_handle;
@@ -184,6 +195,7 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _supports_2d_texture_array = false;
   _supports_cube_map = false;
   _supports_tex_non_pow2 = false;
+  _supports_texture_srgb = false;
   _supports_compressed_texture = false;
   _compressed_texture_formats.clear();
   _compressed_texture_formats.set_bit(Texture::CM_off);
@@ -197,6 +209,16 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system,
   _max_vertex_transform_indices = 0;
 
   _supports_occlusion_query = false;
+  _supports_timer_query = false;
+
+#ifdef DO_PSTATS
+  _timer_queries_active = false;
+  _last_query_frame = 0;
+  _last_num_queried = 0;
+  //_timer_delta = 0.0;
+
+  _pstats_gpu_thread = -1;
+#endif
 
   // Initially, we set this to false; a GSG that knows it has this
   // property should set it to true.
@@ -261,7 +283,7 @@ GraphicsStateGuardian::
     delete _stencil_render_states;
     _stencil_render_states = 0;
   }
-  
+
   if (_shader_generator) {
     delete _shader_generator;
     _shader_generator = 0;
@@ -322,7 +344,7 @@ get_supported_geom_rendering() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_supports_cg_profile
 //       Access: Published, Virtual
-//  Description: Returns true if this particular GSG supports the 
+//  Description: Returns true if this particular GSG supports the
 //               specified Cg Shader Profile.
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
@@ -405,7 +427,7 @@ get_prepared_objects() {
 ////////////////////////////////////////////////////////////////////
 bool GraphicsStateGuardian::
 set_gamma(PN_stdfloat gamma) {
-  _gamma = gamma;  
+  _gamma = gamma;
 
   return false;
 }
@@ -437,7 +459,7 @@ restore_gamma() {
 //               function returns false.
 ////////////////////////////////////////////////////////////////////
 void GraphicsStateGuardian::
-traverse_prepared_textures(GraphicsStateGuardian::TextureCallback *func, 
+traverse_prepared_textures(GraphicsStateGuardian::TextureCallback *func,
                            void *callback_arg) {
   ReMutexHolder holder(_prepared_objects->_lock);
   PreparedGraphicsObjects::Textures::const_iterator ti;
@@ -702,20 +724,6 @@ void GraphicsStateGuardian::
 release_index_buffer(IndexBufferContext *) {
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::get_supports_occlusion_query
-//       Access: Public, Virtual
-//  Description: Returns true if this GSG supports an occlusion query.
-//               If this is true, then begin_occlusion_query() and
-//               end_occlusion_query() may be called to bracket a
-//               sequence of draw_triangles() (or whatever) calls to
-//               measure pixels that pass the depth test.
-////////////////////////////////////////////////////////////////////
-bool GraphicsStateGuardian::
-get_supports_occlusion_query() const {
-  return _supports_occlusion_query;
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::begin_occlusion_query
 //       Access: Public, Virtual
@@ -754,6 +762,17 @@ end_occlusion_query() {
   return result;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::issue_timer_query
+//       Access: Public, Virtual
+//  Description: Adds a timer query to the command stream, associated
+//               with the given PStats collector index.
+////////////////////////////////////////////////////////////////////
+PT(TimerQueryContext) GraphicsStateGuardian::
+issue_timer_query(int pstats_index) {
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::dispatch_compute
 //       Access: Public, Virtual
@@ -888,7 +907,7 @@ compute_distance_to(const LPoint3 &point) const {
 //               the need for a separate routine to fetch these values.
 //
 //               The "altered" bits indicate what parts of the
-//               state_and_transform have changed since the last 
+//               state_and_transform have changed since the last
 //               time this particular ShaderMatSpec was evaluated.
 //               This may allow data to be cached and not reevaluated.
 //
@@ -896,7 +915,7 @@ compute_distance_to(const LPoint3 &point) const {
 const LMatrix4 *GraphicsStateGuardian::
 fetch_specified_value(Shader::ShaderMatSpec &spec, int altered) {
   LVecBase3 v;
-  
+
   if (altered & spec._dep[0]) {
     const LMatrix4 *t = fetch_specified_part(spec._part[0], spec._arg[0], spec._cache[0]);
     if (t != &spec._cache[0]) {
@@ -909,7 +928,7 @@ fetch_specified_value(Shader::ShaderMatSpec &spec, int altered) {
       spec._cache[1] = *t;
     }
   }
-  
+
   switch(spec._func) {
   case Shader::SMF_compose:
     spec._value.multiply(spec._cache[0], spec._cache[1]);
@@ -1470,6 +1489,29 @@ begin_frame(Thread *current_thread) {
   _state_rs = RenderState::make_empty();
   _state_mask.clear();
 
+#ifdef DO_PSTATS
+  // We have to do this here instead of in GraphicsEngine because
+  // we need a current context to issue timer queries.
+  int frame = ClockObject::get_global_clock()->get_frame_count();
+  if (_last_query_frame < frame) {
+    _last_query_frame = frame;
+    _timer_queries_pcollector.clear_level();
+
+    // Now is a good time to flush previous frame's queries.  We
+    // may not actually have all of the previous frame's results
+    // in yet, but that's okay; the GPU data is allowed to lag a
+    // few frames behind.
+    flush_timer_queries();
+
+    if (_timer_queries_active) {
+      // Issue a stop and start event for collector 0, marking the
+      // beginning of the new frame.
+      issue_timer_query(0x8000);
+      issue_timer_query(0x0000);
+    }
+  }
+#endif
+
   return !_needs_reset;
 }
 
@@ -1566,6 +1608,138 @@ end_frame(Thread *current_thread) {
   _prepared_objects->_graphics_memory_lru.begin_epoch();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::flush_timer_queries
+//       Access: Public
+//  Description: Called by the graphics engine on the draw thread
+//               to check the status of the running timer queries
+//               and submit their results to the PStats server.
+////////////////////////////////////////////////////////////////////
+void GraphicsStateGuardian::
+flush_timer_queries() {
+#ifdef DO_PSTATS
+  // This uses the lower-level PStats interfaces for now because
+  // of all the unnecessary overhead that would otherwise be incurred
+  // when adding such a large amount of data at once.
+
+  PStatClient *client = PStatClient::get_global_pstats();
+
+  if (!client->client_is_connected()) {
+    _timer_queries_active = false;
+    return;
+  }
+
+  if (!_timer_queries_active) {
+    if (pstats_gpu_timing && _supports_timer_query) {
+      // Check if timer queries should be enabled.
+      _timer_queries_active = true;
+    } else {
+      return;
+    }
+  }
+
+  // Currently, we use one thread per GSG, for convenience.  In the
+  // future, we may want to try and use one thread per graphics card.
+  if (_pstats_gpu_thread == -1) {
+    _pstats_gpu_thread = client->make_gpu_thread(get_driver_renderer()).get_index();
+  }
+  PStatThread gpu_thread(client, _pstats_gpu_thread);
+
+  // Get the results of all the timer queries.
+  int first = 0;
+  if (!_pending_timer_queries.empty()) {
+    int count = _pending_timer_queries.size();
+    if (count == 0) {
+      return;
+    }
+
+    PStatGPUTimer timer(this, _wait_timer_pcollector);
+
+    if (_last_num_queried > 0) {
+      // We know how many queries were available last frame, and this
+      // usually stays fairly constant, so use this as a starting point.
+      int i = min(_last_num_queried, count) - 1;
+
+      if (_pending_timer_queries[i]->is_answer_ready()) {
+        first = count;
+        while (i < count) {
+          if (!_pending_timer_queries[++i]->is_answer_ready()) {
+            first = i;
+            break;
+          }
+        }
+      } else {
+        first = 0;
+        while (i > 0) {
+          if (_pending_timer_queries[--i]->is_answer_ready()) {
+            first = i + 1;
+            break;
+          }
+        }
+      }
+    } else {
+      // We figure out which tasks the GPU has already finished by doing
+      // a binary search for the first query that does not have an answer
+      // ready.  We know then that everything before that must be ready.
+      while (count > 0) {
+        int step = count / 2;
+        int i = first + step;
+        if (_pending_timer_queries[i]->is_answer_ready()) {
+          first += step + 1;
+          count -= step + 1;
+        } else {
+          count = step;
+        }
+      }
+    }
+
+    if (first <= 0) {
+      return;
+    }
+
+    _last_num_queried = first;
+
+    int frame_index = ClockObject::get_global_clock()->get_frame_count();
+
+    for (int i = 0; i < first; ++i) {
+      CPT(TimerQueryContext) query = _pending_timer_queries[i];
+
+      double time_data = query->get_timestamp(); //  + _timer_delta;
+
+      if (query->_pstats_index == _command_latency_pcollector.get_index()) {
+        // Special case for the latency pcollector.
+        PStatCollectorDef *cdef;
+        cdef = client->get_collector_ptr(query->_pstats_index)->get_def(client, query->_pstats_index);
+        _pstats_gpu_data.add_level(query->_pstats_index, time_data * cdef->_factor);
+
+      } else if (query->_pstats_index & 0x8000) {
+        _pstats_gpu_data.add_stop(query->_pstats_index & 0x7fff, time_data);
+
+      } else {
+        _pstats_gpu_data.add_start(query->_pstats_index & 0x7fff, time_data);
+      }
+
+      // We found an end-frame marker (a stop event for collector 0).
+      // This means that the GPU actually caught up with that frame,
+      // and we can flush the GPU thread's frame data to the pstats server.
+      if (query->_pstats_index == 0x8000) {
+        gpu_thread.add_frame(_pstats_gpu_data);
+        _pstats_gpu_data.clear();
+      }
+    }
+  }
+
+  if (first > 0) {
+    // Do this out of the scope of _wait_timer_pcollector.
+    _pending_timer_queries.erase(
+      _pending_timer_queries.begin(),
+      _pending_timer_queries.begin() + first
+    );
+    _timer_queries_pcollector.add_level_now(first);
+  }
+#endif
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::depth_offset_decals
 //       Access: Public, Virtual
@@ -1819,7 +1993,7 @@ reset() {
     delete _stencil_render_states;
     _stencil_render_states = 0;
   }
-  _stencil_render_states = new StencilRenderStates (this);
+  _stencil_render_states = new StencilRenderStates(this);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1935,12 +2109,12 @@ do_issue_clip_plane() {
           enable_clip_planes(true);
           _clip_planes_enabled = true;
         }
-        
+
         enable_clip_plane(num_enabled, true);
         if (num_enabled == 0) {
           begin_bind_clip_planes();
         }
-        
+
         bind_clip_plane(plane, num_enabled);
         num_enabled++;
       }
@@ -2123,7 +2297,7 @@ do_issue_light() {
           if (num_enabled == 0) {
             begin_bind_lights();
           }
-          
+
           light_obj->bind(this, light, num_enabled);
           num_enabled++;
         }
@@ -2278,24 +2452,24 @@ create_gamma_table (PN_stdfloat gamma, unsigned short *red_table, unsigned short
     // avoid divide by zero and negative exponents
     gamma = 1.0;
   }
-  
+
   for (i = 0; i < 256; i++) {
     double g;
     double x;
     PN_stdfloat gamma_correction;
-    
+
     x = ((double) i / 255.0);
-    gamma_correction = 1.0 / gamma;    
+    gamma_correction = 1.0 / gamma;
     x = pow (x, (double) gamma_correction);
     if (x > 1.00) {
       x = 1.0;
     }
 
-    g = x * 65535.0;    
+    g = x * 65535.0;
     red_table [i] = (int)g;
     green_table [i] = (int)g;
     blue_table [i] = (int)g;
-  }    
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2638,7 +2812,7 @@ async_reload_texture(TextureContext *tc) {
 
   string task_name = string("reload:") + tc->get_texture()->get_name();
   PT(AsyncTaskManager) task_mgr = _loader->get_task_manager();
-  
+
   // See if we are already loading this task.
   AsyncTaskCollection orig_tasks = task_mgr->find_tasks(task_name);
   int num_tasks = orig_tasks.get_num_tasks();
@@ -2655,7 +2829,7 @@ async_reload_texture(TextureContext *tc) {
 
   // This texture has not yet been queued to be reloaded.  Queue it up
   // now.
-  PT(AsyncTask) request = 
+  PT(AsyncTask) request =
     new TextureReloadRequest(task_name,
                              _prepared_objects, tc->get_texture(),
                              _supports_compressed_texture);
@@ -2762,20 +2936,20 @@ make_shadow_buffer(const NodePath &light_np, GraphicsOutputBase *host) {
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_driver_vendor
 //       Access: Public, Virtual
-//  Description: Returns the vendor of the video card driver 
+//  Description: Returns the vendor of the video card driver
 ////////////////////////////////////////////////////////////////////
 string GraphicsStateGuardian::
 get_driver_vendor() {
-  return string("0");
+  return string();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: GraphicsStateGuardian::get_driver_vendor
+//     Function: GraphicsStateGuardian::get_driver_renderer
 //       Access: Public, Virtual
 //  Description: Returns GL_Renderer
 ////////////////////////////////////////////////////////////////////
 string GraphicsStateGuardian::get_driver_renderer() {
-  return string("0");
+  return string();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -2783,12 +2957,12 @@ string GraphicsStateGuardian::get_driver_renderer() {
 //       Access: Public, Virtual
 //  Description: Returns driver version
 //               This has an implementation-defined meaning, and may
-//               be "0" if the particular graphics implementation
+//               be "" if the particular graphics implementation
 //               does not provide a way to query this information.
 ////////////////////////////////////////////////////////////////////
 string GraphicsStateGuardian::
 get_driver_version() {
-  return string("0");
+  return string();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 41 - 7
panda/src/display/graphicsStateGuardian.h

@@ -44,6 +44,7 @@
 #include "bitMask.h"
 #include "texture.h"
 #include "occlusionQueryContext.h"
+#include "timerQueryContext.h"
 #include "stencilRenderStates.h"
 #include "loader.h"
 #include "shaderAttrib.h"
@@ -126,6 +127,7 @@ PUBLISHED:
   INLINE bool get_supports_2d_texture_array() const;
   INLINE bool get_supports_cube_map() const;
   INLINE bool get_supports_tex_non_pow2() const;
+  INLINE bool get_supports_texture_srgb() const;
 
   INLINE bool get_supports_compressed_texture() const;
   virtual INLINE bool get_supports_compressed_texture_format(int compression_mode) const;
@@ -151,6 +153,10 @@ PUBLISHED:
   INLINE bool get_supports_two_sided_stencil() const;
   INLINE bool get_supports_geometry_instancing() const;
 
+  INLINE bool get_supports_occlusion_query() const;
+  INLINE bool get_supports_timer_query() const;
+  INLINE bool get_timer_queries_active() const;
+
   INLINE int get_max_color_targets() const;
   INLINE int get_maximum_simultaneous_render_targets() const;
 
@@ -200,7 +206,7 @@ PUBLISHED:
   virtual int get_driver_version_minor();
   virtual int get_driver_shader_version_major();
   virtual int get_driver_shader_version_minor();
-  
+
   bool set_scene(SceneSetup *scene_setup);
   virtual SceneSetup *get_scene() const;
 
@@ -222,10 +228,11 @@ public:
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data);
   virtual void release_index_buffer(IndexBufferContext *ibc);
 
-  virtual bool get_supports_occlusion_query() const;
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
 
+  virtual PT(TimerQueryContext) issue_timer_query(int pstats_index);
+
   virtual void dispatch_compute(int size_x, int size_y, int size_z);
 
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state,
@@ -239,7 +246,7 @@ public:
   virtual PN_stdfloat compute_distance_to(const LPoint3 &point) const;
 
   virtual void clear(DrawableRegion *clearable);
-  
+
   const LMatrix4 *fetch_specified_value(Shader::ShaderMatSpec &spec, int altered);
   const LMatrix4 *fetch_specified_part(Shader::ShaderMatInput input, InternalName *name, LMatrix4 &t);
   const Shader::ShaderPtrData *fetch_ptr_parameter(const Shader::ShaderPtrSpec& spec);
@@ -260,6 +267,8 @@ PUBLISHED:
 public:
   virtual void end_frame(Thread *current_thread);
 
+  void flush_timer_queries();
+
   void set_current_properties(const FrameBufferProperties *properties);
 
   virtual bool depth_offset_decals();
@@ -375,7 +384,7 @@ protected:
   // This bitmask contains a 1 bit everywhere that _state_rs has a
   // known value.  If a bit is 0, the corresponding state must be
   // re-sent.
-  // 
+  //
   // Derived GSGs should initialize _inv_state_mask in reset() as a mask of
   // 1's where they don't care, and 0's where they do care, about the state.
   RenderState::SlotMask _state_mask;
@@ -406,7 +415,7 @@ protected:
 
   unsigned int _color_write_mask;
 
-  CPT(DisplayRegion) _current_display_region;
+  PT(DisplayRegion) _current_display_region;
   Lens::StereoChannel _current_stereo_channel;
   int _current_tex_view_offset;
   CPT(Lens) _current_lens;
@@ -468,6 +477,7 @@ protected:
   bool _supports_2d_texture_array;
   bool _supports_cube_map;
   bool _supports_tex_non_pow2;
+  bool _supports_texture_srgb;
 
   bool _supports_compressed_texture;
   BitMask32 _compressed_texture_formats;
@@ -481,6 +491,19 @@ protected:
   bool _supports_occlusion_query;
   PT(OcclusionQueryContext) _current_occlusion_query;
 
+  bool _supports_timer_query;
+#ifdef DO_PSTATS
+  int _pstats_gpu_thread;
+  bool _timer_queries_active;
+  PStatFrameData _pstats_gpu_data;
+
+  int _last_query_frame;
+  int _last_num_queried;
+  //double _timer_delta;
+  typedef pdeque<PT(TimerQueryContext)> TimerQueryQueue;
+  TimerQueryQueue _pending_timer_queries;
+#endif
+
   bool _copy_texture_inverted;
   bool _supports_multisample;
   bool _supports_generate_mipmap;
@@ -520,13 +543,13 @@ protected:
 
   PN_stdfloat _gamma;
   Texture::QualityLevel _texture_quality_override;
-  
+
   ShaderGenerator* _shader_generator;
 
 #ifndef NDEBUG
   PT(Texture) _flash_texture;
 #endif
-  
+
 public:
   // Statistics
   static PStatCollector _vertex_buffer_switch_pcollector;
@@ -558,7 +581,18 @@ public:
   static PStatCollector _draw_set_state_pcollector;
   static PStatCollector _clear_pcollector;
   static PStatCollector _flush_pcollector;
+  static PStatCollector _compute_dispatch_pcollector;
   static PStatCollector _wait_occlusion_pcollector;
+  static PStatCollector _wait_timer_pcollector;
+  static PStatCollector _timer_queries_pcollector;
+  static PStatCollector _command_latency_pcollector;
+
+  static PStatCollector _prepare_pcollector;
+  static PStatCollector _prepare_texture_pcollector;
+  static PStatCollector _prepare_geom_pcollector;
+  static PStatCollector _prepare_shader_pcollector;
+  static PStatCollector _prepare_vertex_buffer_pcollector;
+  static PStatCollector _prepare_index_buffer_pcollector;
 
   // A whole slew of collectors to measure the cost of individual
   // state changes.  These are disabled by default.

+ 61 - 0
panda/src/display/pStatGPUTimer.I

@@ -0,0 +1,61 @@
+// Filename: pStatGPUTimer.I
+// Created by:  rdb (21Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+#ifdef DO_PSTATS
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGPUTimer::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PStatGPUTimer::
+PStatGPUTimer(GraphicsStateGuardian *gsg, PStatCollector &collector) :
+  PStatTimer(collector),
+  _gsg(gsg)
+{
+  if (gsg->get_timer_queries_active()) {
+    gsg->issue_timer_query(collector.get_index());
+    //cerr << "issuing " << collector << " active " << collector.is_active() << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGPUTimer::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PStatGPUTimer::
+PStatGPUTimer(GraphicsStateGuardian *gsg, PStatCollector &collector, Thread *current_thread) :
+  PStatTimer(collector, current_thread),
+  _gsg(gsg)
+{
+  if (gsg->get_timer_queries_active()) {
+    gsg->issue_timer_query(collector.get_index());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatGPUTimer::Destructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE PStatGPUTimer::
+~PStatGPUTimer() {
+  if (_gsg->get_timer_queries_active()) {
+    _gsg->issue_timer_query(_collector.get_index() | 0x8000);
+  }
+}
+
+#endif

+ 67 - 0
panda/src/display/pStatGPUTimer.h

@@ -0,0 +1,67 @@
+// Filename: pStatGPUTimer.h
+// Created by:  rdb (21Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef PSTATGPUTIMER_H
+#define PSTATGPUTIMER_H
+
+#include "pandabase.h"
+#include "pStatTimer.h"
+#include "pStatCollector.h"
+#include "config_pstats.h"
+#include "timerQueryContext.h"
+
+class Thread;
+class GraphicsStateGuardian;
+
+////////////////////////////////////////////////////////////////////
+//       Class : PStatGPUTimer
+// Description : This is a special type of PStatTimer that also
+//               uses a timer query on the GSG to measure how long
+//               a task actually takes to execute on the GPU, rather
+//               than how long it took for the API commands to be
+//               queued up.
+//
+//               This class may only be used on the draw thread.
+//
+//               At present, it tracks both the CPU time (like a
+//               regular PStatTimer does) and the GPU time, which
+//               is recorded using a special PStatThread.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_DISPLAY PStatGPUTimer : public PStatTimer {
+public:
+#ifdef DO_PSTATS
+  INLINE PStatGPUTimer(GraphicsStateGuardian *gsg,
+                       PStatCollector &collector);
+  INLINE PStatGPUTimer(GraphicsStateGuardian *gsg,
+                       PStatCollector &collector,
+                       Thread *current_thread);
+  INLINE ~PStatGPUTimer();
+
+  GraphicsStateGuardian *_gsg;
+
+private:
+#else // DO_PSTATS
+
+  INLINE PStatGPUTimer(GraphicsStateGuardian *, PStatCollector &col)
+    : PStatTimer(col) { }
+  INLINE PStatGPUTimer(GraphicsStateGuardian *, PStatCollector &col, Thread *)
+    : PStatTimer(col) { }
+  INLINE ~PStatGPUTimer() { }
+
+#endif  // DO_PSTATS
+};
+
+#include "pStatGPUTimer.I"
+
+#endif

+ 8 - 2
panda/src/display/standardMunger.cxx

@@ -178,7 +178,10 @@ munge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data,
     // Even beyond munging the vertex format, we have to convert the
     // Geom itself into a new primitive type the GSG can render
     // directly.
-    if ((unsupported_bits & Geom::GR_composite_bits) != 0) {
+    // If we don't support a strip cut index, it might be faster to
+    // just decompose it rather than draw them one by one.
+    if ((unsupported_bits & Geom::GR_composite_bits) != 0 ||
+        (unsupported_bits & Geom::GR_strip_cut_index) != 0) {
       // This decomposes everything in the primitive, so that if (for
       // instance) the primitive contained both strips and fans, but
       // the GSG didn't support fans, it would decompose the strips
@@ -224,7 +227,10 @@ premunge_geom_impl(CPT(Geom) &geom, CPT(GeomVertexData) &vertex_data) {
     // Even beyond munging the vertex format, we have to convert the
     // Geom itself into a new primitive type the GSG can render
     // directly.
-    if ((unsupported_bits & Geom::GR_composite_bits) != 0) {
+    // If we don't support a strip cut index, it might be faster to
+    // just decompose it rather than draw them one by one.
+    if ((unsupported_bits & Geom::GR_composite_bits) != 0 ||
+        (unsupported_bits & Geom::GR_strip_cut_index) != 0) {
       // This decomposes everything in the primitive, so that if (for
       // instance) the primitive contained both strips and fans, but
       // the GSG didn't support fans, it would decompose the strips

+ 1 - 1
panda/src/dxgsg8/Sources.pp

@@ -22,7 +22,7 @@
     wdxGraphicsPipe8.I wdxGraphicsPipe8.h \
     wdxGraphicsWindow8.I wdxGraphicsWindow8.h \
     dxgsg8base.h config_dxgsg8.h dxGraphicsStateGuardian8.I dxGraphicsStateGuardian8.h \
-    dxVertexBufferContext8.h dxVertexbufferContext8.I \
+    dxVertexBufferContext8.h dxVertexBufferContext8.I \
     dxIndexBufferContext8.h dxIndexBufferContext8.I \
     dxTextureContext8.h dxTextureContext8.I \
     dxGeomMunger8.h dxGeomMunger8.I \

+ 2 - 2
panda/src/dxgsg8/wdxGraphicsWindow8.cxx

@@ -413,7 +413,7 @@ handle_reshape() {
   GdiFlush();
   WinGraphicsWindow::handle_reshape();
 
-  if (_dxgsg != NULL) {
+  if (_dxgsg != NULL && _dxgsg->_d3d_device != NULL) {
     // create the new resized rendertargets
     WindowProperties props = get_properties();
     int x_size = props.get_x_size();
@@ -422,7 +422,7 @@ handle_reshape() {
     if (_wcontext._presentation_params.BackBufferWidth != x_size ||
         _wcontext._presentation_params.BackBufferHeight != y_size) {
       bool resize_succeeded = reset_device_resize_window(x_size, y_size);
-      
+
       if (wdxdisplay8_cat.is_debug()) {
         if (!resize_succeeded) {
           wdxdisplay8_cat.debug()

+ 1 - 1
panda/src/dxgsg9/Sources.pp

@@ -22,7 +22,7 @@
     wdxGraphicsPipe9.I wdxGraphicsPipe9.h \
     wdxGraphicsWindow9.I wdxGraphicsWindow9.h \
     dxgsg9base.h config_dxgsg9.h dxGraphicsStateGuardian9.I dxGraphicsStateGuardian9.h \
-    dxVertexBufferContext9.h dxVertexbufferContext9.I \
+    dxVertexBufferContext9.h dxVertexBufferContext9.I \
     dxIndexBufferContext9.h dxIndexBufferContext9.I \
     dxTextureContext9.h dxTextureContext9.I \
     dxGeomMunger9.h dxGeomMunger9.I \

+ 77 - 57
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -224,6 +224,13 @@ apply_texture(int i, TextureContext *tc) {
   DXTextureContext9 *dtc = DCAST(DXTextureContext9, tc);
   Texture *tex = tc->get_texture();
 
+  //if (tex->get_color_space() == CS_srgb) {
+  if (Texture::is_srgb(tex->get_format())) {
+    set_sampler_state(i, D3DSAMP_SRGBTEXTURE, TRUE);
+  } else {
+    set_sampler_state(i, D3DSAMP_SRGBTEXTURE, FALSE);
+  }
+
   Texture::WrapMode wrap_u, wrap_v, wrap_w;
 
   DWORD address_u;
@@ -256,7 +263,7 @@ apply_texture(int i, TextureContext *tc) {
 
   int supports_anisotropic_mag_filter;
   D3DTEXTUREFILTERTYPE new_mag_filter;
-  
+
   supports_anisotropic_mag_filter = (_screen -> _d3dcaps.TextureFilterCaps & D3DPTFILTERCAPS_MAGFANISOTROPIC) != 0;
   if (aniso_degree <= 1 || supports_anisotropic_mag_filter == 0) {
     new_mag_filter = ((ft != Texture::FT_nearest) ? D3DTEXF_LINEAR : D3DTEXF_POINT);
@@ -268,8 +275,8 @@ apply_texture(int i, TextureContext *tc) {
   hr = set_sampler_state(i, D3DSAMP_MAGFILTER, new_mag_filter);
   if (hr != D3D_OK) {
     dxgsg9_cat.error()
-      << "ERROR: set_sampler_state (D3DSAMP_MAGFILTER, " 
-      << new_mag_filter << ") failed for texture:" << tex -> get_name() << endl;    
+      << "ERROR: set_sampler_state (D3DSAMP_MAGFILTER, "
+      << new_mag_filter << ") failed for texture:" << tex -> get_name() << endl;
   }
 
   // map Panda composite min+mip filter types to d3d's separate min & mip filter types
@@ -353,7 +360,7 @@ upload_texture(DXTextureContext9 *dtc, bool force) {
   dtc->delete_texture();
   dtc->update_data_size_bytes(0);
   dtc->mark_unloaded();
-  
+
   if (_effective_incomplete_render && !force) {
     bool has_image = _supports_compressed_texture ? tex->has_ram_image() : tex->has_uncompressed_ram_image();
     if (!has_image && tex->might_have_ram_image() &&
@@ -371,7 +378,7 @@ upload_texture(DXTextureContext9 *dtc, bool force) {
       }
     }
   }
-  
+
   return dtc->create_texture(*_screen);
 }
 
@@ -746,7 +753,7 @@ begin_occlusion_query() {
       << "Occlusion query failed.\n";
     return;
   }
-  
+
   PT(DXOcclusionQueryContext9) queryobj = new DXOcclusionQueryContext9(query);
 
   if (dxgsg9_cat.is_debug()) {
@@ -776,7 +783,7 @@ end_occlusion_query() {
   PT(OcclusionQueryContext) result = _current_occlusion_query;
 
   IDirect3DQuery9 *query = DCAST(DXOcclusionQueryContext9, result)->_query;
-    
+
   if (dxgsg9_cat.is_debug()) {
     dxgsg9_cat.debug()
       << "ending occlusion query " << query << "\n";
@@ -857,7 +864,7 @@ clear(DrawableRegion *clearable) {
           aux_flags |=  D3DCLEAR_ZBUFFER;
           HRESULT hr2 = _d3d_device->Clear(0, NULL, D3DCLEAR_ZBUFFER, color_clear_value,
                                            depth_clear_value, stencil_clear_value);
-          if (FAILED(hr2)) {          
+          if (FAILED(hr2)) {
             dxgsg9_cat.error()
               << "Unable to clear depth buffer; removing.\n";
             // This is really hacky code.
@@ -1040,6 +1047,12 @@ begin_frame(Thread *current_thread) {
     return false;
   }
 
+  if (_current_properties->get_srgb_color()) {
+    set_render_state(D3DRS_SRGBWRITEENABLE, TRUE);
+  } else {
+    set_render_state(D3DRS_SRGBWRITEENABLE, FALSE);
+  }
+
   return true;
 }
 
@@ -1343,14 +1356,14 @@ update_standard_vertex_arrays(bool force) {
       dxgsg9_cat.error() << "Unable to get reader for array " << array_index << "\n";
       return false;
     }
-  
+
     // Get the vertex buffer for this array.
     CLP(VertexBufferContext)* dvbc;
     if (!setup_array_data(dvbc, array_reader, force)) {
       dxgsg9_cat.error() << "Unable to setup vertex buffer for array " << array_index << "\n";
       return false;
     }
-  
+
     // Bind this array as the data source for the corresponding stream.
     const GeomVertexArrayFormat* array_format = array_reader->get_array_format();
     hr = _d3d_device->SetStreamSource( array_index, dvbc->_vbuffer, 0, array_format->get_stride() );
@@ -2315,7 +2328,7 @@ reset() {
   // want gsg to pass all state settings down so any non-matching defaults we set here get overwritten
 
   nassertv(_screen->_d3d9 != NULL);
-  
+
   if (_d3d_device == NULL) {
     return;
   }
@@ -2379,27 +2392,27 @@ reset() {
     _shader_caps._ultimate_fprofile = (int)CG_PROFILE_PS_2_0;
 */
   }
-  
+
   if (dxgsg9_cat.is_debug()) {
-    
+
     CGprofile vertex_profile;
     CGprofile pixel_profile;
-    
+
     vertex_profile = cgD3D9GetLatestVertexProfile();
     pixel_profile = cgD3D9GetLatestPixelProfile();
-    
+
     const char *vertex_profile_str =
       cgGetProfileString(vertex_profile);
     const char *pixel_profile_str =
       cgGetProfileString(pixel_profile);
-    
+
     if (vertex_profile_str == NULL) {
       vertex_profile_str = "(null)";
     }
     if (pixel_profile_str == NULL) {
       pixel_profile_str = "(null)";
     }
-    
+
     dxgsg9_cat.debug()
       << "\nCg vertex profile = " << vertex_profile_str << "  id = " << vertex_profile
       << "\nCg pixel profile = " << pixel_profile_str << "  id = " << pixel_profile
@@ -2495,7 +2508,7 @@ reset() {
       << "\nDirectX SDK version " DIRECTX_SDK_VERSION
       << "\n";
   }
-  
+
   // OVERRIDE SUPPORT SINCE IT DOES NOT WORK WELL
   _screen->_supports_automatic_mipmap_generation = false;
 
@@ -2618,12 +2631,19 @@ reset() {
 
   _last_testcooplevel_result = D3D_OK;
 
+  if (dxgsg9_cat.is_debug()) {
+    dxgsg9_cat.debug() << "Supported texture formats:\n";
+  }
+
   for(int i = 0; i < MAX_POSSIBLE_TEXFMTS; i++) {
     // look for all possible DX9 texture fmts
     D3DFORMAT_FLAG fmtflag = D3DFORMAT_FLAG(1 << i);
     hr = _screen->_d3d9->CheckDeviceFormat(_screen->_card_id, D3DDEVTYPE_HAL, _screen->_display_mode.Format,
                                           0x0, D3DRTYPE_TEXTURE, g_D3DFORMATmap[fmtflag]);
-    if (SUCCEEDED(hr)){
+    if (SUCCEEDED(hr)) {
+      if (dxgsg9_cat.is_debug()) {
+        dxgsg9_cat.debug() << "  " << D3DFormatStr(g_D3DFORMATmap[fmtflag]) << "\n";
+      }
       _screen->_supported_tex_formats_mask |= fmtflag;
     }
   }
@@ -2632,7 +2652,7 @@ reset() {
   #define CHECK_FOR_DXTVERSION(num) \
   if (_screen->_supported_tex_formats_mask & DXT##num##_FLAG) {\
     if (dxgsg9_cat.is_debug()) {\
-      dxgsg9_cat.debug() << "Compressed texture format DXT" << #num << " supported \n";\
+      dxgsg9_cat.debug() << "Compressed texture format DXT" << #num << " supported\n";\
     }\
     _supports_compressed_texture = true;\
     _compressed_texture_formats.set_bit(Texture::CM_dxt##num);\
@@ -2641,9 +2661,9 @@ reset() {
   if (_screen->_intel_compressed_texture_bug) {
     dxgsg9_cat.info()
       << "Buggy Intel driver detected; disabling compressed textures.\n";
-    _screen->_supported_tex_formats_mask &= 
+    _screen->_supported_tex_formats_mask &=
       ~(DXT1_FLAG | DXT2_FLAG | DXT3_FLAG | DXT4_FLAG | DXT5_FLAG);
-                                              
+
   } else {
     // Check for available compressed formats normally.
     CHECK_FOR_DXTVERSION(1);
@@ -2652,7 +2672,7 @@ reset() {
     CHECK_FOR_DXTVERSION(4);
     CHECK_FOR_DXTVERSION(5);
   }
-      
+
   #undef CHECK_FOR_DXTVERSION
 
   _screen->_supports_rgba16f_texture_format = false;
@@ -3292,7 +3312,7 @@ set_state_and_transform(const RenderState *target,
       !_state_mask.get_bit(transparency_slot) ||
       !_state_mask.get_bit(color_write_slot) ||
       !_state_mask.get_bit(color_blend_slot) ||
-      (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write) != 
+      (_target_shader->get_flag(ShaderAttrib::F_disable_alpha_write) !=
        _state_shader->get_flag(ShaderAttrib::F_disable_alpha_write))) {
     //PStatTimer timer(_draw_set_state_blending_pcollector);
     do_issue_blending();
@@ -3353,7 +3373,7 @@ set_state_and_transform(const RenderState *target,
     do_issue_stencil();
     _state_mask.set_bit(stencil_slot);
   }
-     
+
   int fog_slot = FogAttrib::get_class_slot();
   if (_target_rs->get_attrib(fog_slot) != _state_rs->get_attrib(fog_slot) ||
       !_state_mask.get_bit(fog_slot)) {
@@ -3446,22 +3466,22 @@ bind_light(DirectionalLight *light_obj, const NodePath &light, int light_id) {
     const LMatrix4 &light_mat = transform->get_mat();
     LMatrix4 rel_mat = light_mat * LMatrix4::convert_mat(CS_yup_left, CS_default);
     LVector3f dir = LCAST(float, light_obj->get_direction() * rel_mat);
-    
+
     D3DCOLORVALUE black;
     black.r = black.g = black.b = black.a = 0.0f;
-    
+
     ZeroMemory(&fdata, sizeof(D3DLIGHT9));
-    
+
     fdata.Type =  D3DLIGHT_DIRECTIONAL;
     fdata.Ambient  =  black ;
     LColorf color = LCAST(float, light_obj->get_specular_color());
     fdata.Specular = *(D3DCOLORVALUE *)(color.get_data());
-    
+
     fdata.Direction = *(D3DVECTOR *)dir.get_data();
-    
+
     fdata.Range =  __D3DLIGHT_RANGE_MAX;
     fdata.Falloff =  1.0f;
-    
+
     fdata.Attenuation0 = 1.0f;       // constant
     fdata.Attenuation1 = 0.0f;       // linear
     fdata.Attenuation2 = 0.0f;       // quadratic
@@ -4125,7 +4145,7 @@ close_gsg() {
 
   if (dxgsg9_cat.is_debug()) {
     dxgsg9_cat.debug()
-      << "Closing GSG, prepared_objects count = " 
+      << "Closing GSG, prepared_objects count = "
       << _prepared_objects->get_ref_count() << "\n";
   }
 
@@ -4210,9 +4230,9 @@ set_read_buffer(const RenderBuffer &rb) {
   if (rb._buffer_type & RenderBuffer::T_front) {
     _cur_read_pixel_buffer = RenderBuffer::T_front;
   } else  if (rb._buffer_type & RenderBuffer::T_back) {
-    _cur_read_pixel_buffer = RenderBuffer::T_back;      
+    _cur_read_pixel_buffer = RenderBuffer::T_back;
   } else  if (rb._buffer_type & RenderBuffer::T_aux_rgba_ALL) {
-    _cur_read_pixel_buffer = RenderBuffer::T_back;      
+    _cur_read_pixel_buffer = RenderBuffer::T_back;
   } else {
     dxgsg9_cat.error() << "Invalid or unimplemented Argument to set_read_buffer!\n";
   }
@@ -4816,7 +4836,7 @@ check_cooperative_level() {
   case D3DERR_DEVICELOST:
     // sleep while the device is lost to free up the CPU
     Sleep (10);
-    
+
     if (SUCCEEDED(_last_testcooplevel_result)) {
       if (_dx_is_ready) {
         _dx_is_ready = false;
@@ -4847,7 +4867,7 @@ show_frame() {
   if (_swap_chain) {
     DWORD flags;
     flags = 0;
-    
+
     hr = _swap_chain->Present((CONST RECT*)NULL, (CONST RECT*)NULL, (HWND)NULL, NULL, flags);
   } else {
     hr = _d3d_device->Present((CONST RECT*)NULL, (CONST RECT*)NULL, (HWND)NULL, NULL);
@@ -5220,7 +5240,7 @@ check_dx_allocation (HRESULT result, int allocation_size, int attempts)
         // increase the page out size as the number of attempts increases
         {
           size_t current_size = _prepared_objects->_graphics_memory_lru.get_total_size();
-          size_t target_size = max(current_size - allocation_size * attempts, 0);
+          size_t target_size = max(current_size - allocation_size * attempts, (size_t) 0);
           _prepared_objects->_graphics_memory_lru.evict_to(target_size);
           dxgsg9_cat.info()
             << "Evicted " << current_size - _prepared_objects->_graphics_memory_lru.get_total_size() << " bytes of texture memory to make room for more.\n";
@@ -5435,7 +5455,7 @@ do_issue_stencil() {
 ////////////////////////////////////////////////////////////////////
 //     Function: dxGraphicsStateGuardian9::do_issue_scissor
 //       Access: Protected
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void DXGraphicsStateGuardian9::
 do_issue_scissor() {
@@ -5522,8 +5542,8 @@ void _create_gamma_table (PN_stdfloat gamma, unsigned short *original_red_table,
     // avoid divide by zero and negative exponents
     gamma = 1.0;
   }
-  gamma_correction = 1.0 / (double) gamma;    
-  
+  gamma_correction = 1.0 / (double) gamma;
+
   for (i = 0; i < 256; i++) {
     double r;
     double g;
@@ -5534,11 +5554,11 @@ void _create_gamma_table (PN_stdfloat gamma, unsigned short *original_red_table,
       g = (double) original_green_table [i] / GAMMA_1;
       b = (double) original_blue_table [i] / GAMMA_1;
     }
-    else {    
+    else {
       r = ((double) i / 255.0);
       g = r;
       b = r;
-    }    
+    }
 
     r = pow (r, gamma_correction);
     g = pow (g, gamma_correction);
@@ -5554,14 +5574,14 @@ void _create_gamma_table (PN_stdfloat gamma, unsigned short *original_red_table,
       b = 1.0;
     }
 
-    r = r * GAMMA_1;    
-    g = g * GAMMA_1;    
-    b = b * GAMMA_1;    
+    r = r * GAMMA_1;
+    g = g * GAMMA_1;
+    b = b * GAMMA_1;
 
     red_table [i] = r;
     green_table [i] = g;
     blue_table [i] = b;
-  }    
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -5571,13 +5591,13 @@ void _create_gamma_table (PN_stdfloat gamma, unsigned short *original_red_table,
 ////////////////////////////////////////////////////////////////////
 bool DXGraphicsStateGuardian9::
 get_gamma_table(void) {
-  bool get;  
+  bool get;
 
   get = false;
   if (_gamma_table_initialized == false) {
     HDC hdc = GetDC(NULL);
 
-    if (hdc) {   
+    if (hdc) {
       if (GetDeviceGammaRamp (hdc, (LPVOID) _orignial_gamma_table)) {
         _gamma_table_initialized = true;
         get = true;
@@ -5586,26 +5606,26 @@ get_gamma_table(void) {
       ReleaseDC (NULL, hdc);
     }
   }
-  
+
   return get;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian9::static_set_gamma
 //       Access: Public, Static
-//  Description: Static function for setting gamma which is needed 
+//  Description: Static function for setting gamma which is needed
 //               for atexit.
 ////////////////////////////////////////////////////////////////////
 bool DXGraphicsStateGuardian9::
 static_set_gamma(bool restore, PN_stdfloat gamma) {
-  bool set;  
+  bool set;
   HDC hdc = GetDC(NULL);
 
   set = false;
-  if (hdc) {   
+  if (hdc) {
     unsigned short ramp [256 * 3];
 
-    if (restore && _gamma_table_initialized) {    
+    if (restore && _gamma_table_initialized) {
       _create_gamma_table (gamma, &_orignial_gamma_table [0], &_orignial_gamma_table [256], &_orignial_gamma_table [512], &ramp [0], &ramp [256], &ramp [512]);
     }
     else {
@@ -5615,7 +5635,7 @@ static_set_gamma(bool restore, PN_stdfloat gamma) {
     if (SetDeviceGammaRamp (hdc, ramp)) {
       set = true;
     }
-    
+
     ReleaseDC (NULL, hdc);
   }
 
@@ -5634,7 +5654,7 @@ set_gamma(PN_stdfloat gamma) {
 
   set = static_set_gamma(false, gamma);
   if (set) {
-    _gamma = gamma;  
+    _gamma = gamma;
   }
 
   return set;
@@ -5664,7 +5684,7 @@ atexit_function(void) {
 ////////////////////////////////////////////////////////////////////
 //     Function: DXGraphicsStateGuardian9::get_supports_cg_profile
 //       Access: Public, Virtual
-//  Description: Returns true if this particular GSG supports the 
+//  Description: Returns true if this particular GSG supports the
 //               specified Cg Shader Profile.
 ////////////////////////////////////////////////////////////////////
 bool DXGraphicsStateGuardian9::
@@ -5673,7 +5693,7 @@ get_supports_cg_profile(const string &name) const {
   return false;
 #else
   CGprofile profile = cgGetProfile(name.c_str());
-  
+
   if (profile == CG_PROFILE_UNKNOWN) {
     dxgsg9_cat.error() << name <<", unknown Cg-profile\n";
     return false;

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

@@ -36,9 +36,7 @@
 #include "vertexElementArray.h"
 #include "dxShaderContext9.h"
 
-
-enum GsgPageType
-{
+enum GsgPageType {
   GPT_Texture,
   GPT_VertexBuffer,
   GPT_IndexBuffer,
@@ -52,6 +50,8 @@ class DXTextureContext9;
 class DXVertexBufferContext9;
 class DXIndexBufferContext9;
 
+class wdxGraphicsBuffer9;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : DXGraphicsStateGuardian9
 // Description : A GraphicsStateGuardian for rendering into DirectX9
@@ -265,7 +265,7 @@ protected:
 
 public:
   DXScreenData *_screen;
-  
+
 protected:
   LPDIRECT3DDEVICE9 _d3d_device;  // same as _screen->_d3d_device, cached for spd
   IDirect3DSwapChain9 *_swap_chain;
@@ -374,7 +374,7 @@ protected:
 
   list <wdxGraphicsBuffer9 **> _graphics_buffer_list;
 
-  int _supports_gamma_calibration;  
+  int _supports_gamma_calibration;
 
   static LPDIRECT3DDEVICE9 _cg_device;
 

+ 87 - 58
panda/src/dxgsg9/dxTextureContext9.cxx

@@ -135,13 +135,13 @@ create_texture(DXScreenData &scrn) {
   bool texture_wants_compressed = false;
   Texture::CompressionMode compression_mode = tex->get_ram_image_compression();
   bool texture_stored_compressed = compression_mode != Texture::CM_off;
-  
+
   if (texture_stored_compressed) {
-    texture_wants_compressed = true;  
+    texture_wants_compressed = true;
   } else {
     if (tex->get_compression() == Texture::CM_off) {
       // no compression
-    } else {    
+    } else {
       if (tex->get_compression() == Texture::CM_default) {
         // default = use "compressed-textures" config setting
         if (compressed_textures) {
@@ -150,9 +150,9 @@ create_texture(DXScreenData &scrn) {
       } else {
         texture_wants_compressed = true;
       }
-    }  
+    }
   }
-    
+
   switch (tex->get_texture_type()) {
     case Texture::TT_1d_texture:
     case Texture::TT_2d_texture:
@@ -197,7 +197,9 @@ create_texture(DXScreenData &scrn) {
 
   if ((tex->get_format() == Texture::F_luminance_alpha)||
       (tex->get_format() == Texture::F_luminance_alphamask) ||
-      (tex->get_format() == Texture::F_luminance)) {
+      (tex->get_format() == Texture::F_luminance) ||
+      (tex->get_format() == Texture::F_sluminance_alpha) ||
+      (tex->get_format() == Texture::F_sluminance)) {
     needs_luminance = true;
   }
 
@@ -232,7 +234,7 @@ create_texture(DXScreenData &scrn) {
     _d3d_format = D3DFMT_A8R8G8B8;
     break;
   }
-  
+
   // make sure we handled all the possible cases
   nassertr(_d3d_format != D3DFMT_UNKNOWN, false);
 
@@ -398,9 +400,9 @@ create_texture(DXScreenData &scrn) {
 
   if (compress_texture) {
     if (num_alpha_bits <= 1) {
-      CHECK_FOR_FMT(DXT1);    
+      CHECK_FOR_FMT(DXT1);
     } else if (num_alpha_bits <= 4) {
-      CHECK_FOR_FMT(DXT3);    
+      CHECK_FOR_FMT(DXT3);
     } else {
       CHECK_FOR_FMT(DXT5);
     }
@@ -425,7 +427,7 @@ create_texture(DXScreenData &scrn) {
     // array (the texture array contains num_color_channels*8bits)
 
   case 128:
-    // check if format is supported    
+    // check if format is supported
     if (scrn._supports_rgba32_texture_format) {
       target_pixel_format = D3DFMT_A32B32G32R32F;
     }
@@ -435,7 +437,7 @@ create_texture(DXScreenData &scrn) {
     goto found_matching_format;
 
   case 64:
-    // check if format is supported 
+    // check if format is supported
     if (scrn._supports_rgba16f_texture_format) {
       target_pixel_format = D3DFMT_A16B16G16R16F;
     }
@@ -443,7 +445,7 @@ create_texture(DXScreenData &scrn) {
       target_pixel_format = scrn._render_to_texture_d3d_format;
     }
     goto found_matching_format;
-    
+
   case 32:
     if (!((num_color_channels == 3) || (num_color_channels == 4)))
       break; //bail
@@ -502,7 +504,7 @@ create_texture(DXScreenData &scrn) {
 
     CHECK_FOR_FMT(X8R8G8B8);
     CHECK_FOR_FMT(A8R8G8B8);
-    
+
     // no 24-bit or 32 fmt.  look for 16 bit fmt (higher res 565 1st)
     CHECK_FOR_FMT(R5G6B5);
     CHECK_FOR_FMT(X1R5G5B5);
@@ -782,13 +784,13 @@ create_texture(DXScreenData &scrn) {
     // REQUIRED PARAMETERS
     _managed = false;
     _is_render_target = true;
-    
+
     pool = D3DPOOL_DEFAULT;
     usage = D3DUSAGE_RENDERTARGET;
     if (target_bpp <= 32 ) {
       target_pixel_format = scrn._render_to_texture_d3d_format;
     }
-    
+
     dxgsg9_cat.debug ()
       << "*** RENDER TO TEXTURE ***: format "
       << D3DFormatStr(target_pixel_format)
@@ -848,7 +850,7 @@ create_texture(DXScreenData &scrn) {
     data_size *= 6;
   }
   update_data_size_bytes(data_size);
-  
+
   int attempts;
 
   if (dxgsg9_cat.is_debug()) {
@@ -882,7 +884,7 @@ create_texture(DXScreenData &scrn) {
       hr = scrn._d3d_device->CreateTexture
         (target_width, target_height, mip_level_count, usage,
          target_pixel_format, pool, &_d3d_2d_texture, NULL);
-      _d3d_texture = _d3d_2d_texture;      
+      _d3d_texture = _d3d_2d_texture;
       break;
 
     case Texture::TT_3d_texture:
@@ -952,7 +954,7 @@ create_texture(DXScreenData &scrn) {
     scrn._dxgsg9->get_engine()->texture_uploaded(tex);
   }
   mark_loaded();
-  
+
   return true;
 
  error_exit:
@@ -967,7 +969,7 @@ create_texture(DXScreenData &scrn) {
 ////////////////////////////////////////////////////////////////////
 //     Function: DXTextureContext9::create_simple_texture
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 bool DXTextureContext9::
 create_simple_texture(DXScreenData &scrn) {
@@ -993,7 +995,7 @@ create_simple_texture(DXScreenData &scrn) {
   hr = scrn._d3d_device->CreateTexture
     (target_width, target_height, mip_level_count, usage,
      target_pixel_format, pool, &_d3d_2d_texture, NULL);
-  _d3d_texture = _d3d_2d_texture;      
+  _d3d_texture = _d3d_2d_texture;
   if (FAILED(hr)) {
     dxgsg9_cat.error()
       << "D3D create_simple_texture failed!" << D3DERRORSTRING(hr);
@@ -1032,13 +1034,13 @@ create_simple_texture(DXScreenData &scrn) {
 
     RELEASE(surface, dxgsg9, "create_simple_texture Surface", RELEASE_ONCE);
   }
-    
+
   if (FAILED(hr)) {
     dxgsg9_cat.debug ()
       << "*** fill_d3d_texture_pixels failed ***: format "
       << target_pixel_format
       << "\n";
-    
+
     goto error_exit;
   }
 
@@ -1084,7 +1086,7 @@ bool DXTextureContext9::
 extract_texture_data(DXScreenData &screen) {
   bool state;
   HRESULT hr;
-  
+
   state = false;
   Texture *tex = get_texture();
   if (tex->get_texture_type() != Texture::TT_2d_texture) {
@@ -1147,7 +1149,7 @@ extract_texture_data(DXScreenData &screen) {
 
   default:
     dxgsg9_cat.error()
-      << "Cannot extract texture data: unhandled surface format " 
+      << "Cannot extract texture data: unhandled surface format "
       << desc.Format << "\n";
     return state;
   }
@@ -1164,13 +1166,13 @@ extract_texture_data(DXScreenData &screen) {
   if (_is_render_target) {
     IDirect3DSurface9* source_surface;
     IDirect3DSurface9* destination_surface;
-  
+
     source_surface = 0;
     destination_surface = 0;
-    
+
     hr = _d3d_2d_texture -> GetSurfaceLevel (0, &source_surface);
-    if (hr == D3D_OK) {    
-       
+    if (hr == D3D_OK) {
+
       D3DPOOL pool;
       D3DSURFACE_DESC surface_description;
 
@@ -1186,7 +1188,7 @@ extract_texture_data(DXScreenData &screen) {
         NULL);
       if (hr == D3D_OK) {
         if (source_surface && destination_surface) {
-          hr = screen._d3d_device -> GetRenderTargetData (source_surface, destination_surface);          
+          hr = screen._d3d_device -> GetRenderTargetData (source_surface, destination_surface);
           if (hr == D3D_OK) {
 
             D3DLOCKED_RECT rect;
@@ -1200,13 +1202,13 @@ extract_texture_data(DXScreenData &screen) {
 
               int surface_bytes_per_line;
               unsigned char *surface_pointer;
-              
+
               bytes_per_line = surface_description.Width * this -> d3d_format_to_bytes_per_pixel (surface_description.Format);
               size = bytes_per_line * surface_description.Height;
 
               surface_bytes_per_line = rect.Pitch;
               surface_pointer = (unsigned char *) rect.pBits;
-              
+
               PTA_uchar image = PTA_uchar::empty_array(size);
 
               int offset;
@@ -1217,29 +1219,29 @@ extract_texture_data(DXScreenData &screen) {
                 memcpy (&image [offset], surface_pointer, bytes_per_line);
 
                 offset += bytes_per_line;
-                surface_pointer += surface_bytes_per_line;  
+                surface_pointer += surface_bytes_per_line;
               }
 
               tex->set_ram_image(image, Texture::CM_off);
 
               state = true;
-              
+
               destination_surface -> UnlockRect();
             }
           }
-        }    
-        
+        }
+
         destination_surface -> Release ( );
       }
       else {
         dxgsg9_cat.error()
           << "CreateImageSurface failed in extract_texture_data()"
-          << D3DERRORSTRING(hr);      
-      }      
+          << D3DERRORSTRING(hr);
+      }
       source_surface -> Release ( );
     }
   }
-  else {  
+  else {
     for (int n = 0; n < num_levels; ++n) {
       D3DLOCKED_RECT rect;
       hr = _d3d_2d_texture->LockRect(n, &rect, NULL, D3DLOCK_READONLY);
@@ -1248,11 +1250,11 @@ extract_texture_data(DXScreenData &screen) {
           << "Texture::LockRect() failed!  level = " << n << " " << D3DERRORSTRING(hr);
         return state;
       }
-    
+
       int x_size = tex->get_expected_mipmap_x_size(n);
       int y_size = tex->get_expected_mipmap_y_size(n);
       PTA_uchar image;
-      
+
       if (compression == Texture::CM_off) {
         // Uncompressed, but we have to respect the pitch.
         int pitch = x_size * tex->get_num_components() * tex->get_component_width();
@@ -1273,7 +1275,7 @@ extract_texture_data(DXScreenData &screen) {
             source += rect.Pitch;
           }
         }
-        
+
       } else {
         // Compressed; just copy the data verbatim.
         int size = rect.Pitch * (y_size / div);
@@ -1288,10 +1290,10 @@ extract_texture_data(DXScreenData &screen) {
         tex->set_ram_mipmap_image(n, image);
       }
     }
-    
+
     state = true;
   }
-  
+
   return state;
 }
 
@@ -1560,7 +1562,7 @@ d3d_surface_to_texture(RECT &source_rect, IDirect3DSurface9 *d3d_surface,
 ////////////////////////////////////////////////////////////////////
 //     Function: calculate_row_byte_length
 //       Access: Private, hidden
-//  Description: local helper function, which calculates the 
+//  Description: local helper function, which calculates the
 //               'row_byte_length' or 'pitch' needed for calling
 //               D3DXLoadSurfaceFromMemory.
 //               Takes compressed formats (DXTn) into account.
@@ -1596,12 +1598,12 @@ static UINT calculate_row_byte_length (int width, int num_color_channels, D3DFOR
 //       Access: Private
 //  Description: Called from fill_d3d_texture_pixels, this function
 //               fills a single mipmap with texture data.
-//               Takes care of all necessery conversions and error
+//               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)
 {
-  // This whole function was refactored out of fill_d3d_texture_pixels to make the code 
+  // 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 = NULL;
   bool using_temp_buffer = false;
@@ -1617,7 +1619,7 @@ HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int dep
   pixels += view_size * get_view();
   size_t page_size = get_texture()->get_expected_ram_mipmap_page_size(mip_level);
   pixels += page_size * depth_index;
-  
+
   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);
@@ -1645,6 +1647,10 @@ HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int dep
   // dithering)??)
   mip_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER;  //dithering looks ugly on i810 for 4444 textures
 
+  if (Texture::is_srgb(get_texture()->get_format())) {
+    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) {
@@ -1671,7 +1677,7 @@ HRESULT DXTextureContext9::fill_d3d_texture_mipmap_pixels(int mip_level, int dep
     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
@@ -1756,7 +1762,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
     // 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()) {
       HRESULT result;
 
       if (_d3d_2d_texture) {
@@ -1777,7 +1783,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
               depth_stencil_surface = 0;
               if (device -> GetDepthStencilSurface (&depth_stencil_surface) == D3D_OK) {
                 if (device -> SetDepthStencilSurface (NULL) == D3D_OK) {
-      
+
                 }
               }
 
@@ -1791,7 +1797,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
                 }
               }
 
-              // restore depth stencil 
+              // restore depth stencil
               if (depth_stencil_surface) {
                 device -> SetDepthStencilSurface (depth_stencil_surface);
                 depth_stencil_surface -> Release();
@@ -1806,7 +1812,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
           surface -> Release();
         }
       }
-      
+
       return S_OK;
     }
     return E_FAIL;
@@ -1842,7 +1848,7 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
   }
 
   for (unsigned int di = 0; di < orig_depth; di++) {
-    
+
     // fill top level mipmap
     hr = fill_d3d_texture_mipmap_pixels(0, di, source_format);
     if (FAILED(hr)) {
@@ -1861,8 +1867,8 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
           if (FAILED(hr)) {
             return hr; // error message was already output in fill_d3d_texture_mipmap_pixels
           }
-        }        
-      } 
+        }
+      }
       else {
         // mipmaps need to be generated, either use autogen or d3dx functions
 
@@ -1888,6 +1894,10 @@ fill_d3d_texture_pixels(DXScreenData &scrn, bool compress_texture) {
             mip_filter_flags = D3DX_FILTER_TRIANGLE;
           }
 
+          if (Texture::is_srgb(tex->get_format())) {
+            mip_filter_flags |= D3DX_FILTER_SRGB;
+          }
+
           // mip_filter_flags |= D3DX_FILTER_DITHER;
           hr = D3DXFilterTexture(_d3d_texture, (PALETTEENTRY*)NULL, 0,
                                 mip_filter_flags);
@@ -1934,7 +1944,7 @@ fill_d3d_volume_texture_pixels(DXScreenData &scrn) {
   if (!scrn._dxgsg9->get_supports_compressed_texture_format(image_compression)) {
     image = tex->get_uncompressed_ram_image();
     image_compression = Texture::CM_off;
-  }    
+  }
 
   if (image.is_null()) {
     // The texture doesn't have an image to load.  That's ok; it
@@ -1991,6 +2001,10 @@ fill_d3d_volume_texture_pixels(DXScreenData &scrn) {
   // dithering)??)
   level_0_filter = D3DX_FILTER_LINEAR ; //| D3DX_FILTER_DITHER;  //dithering looks ugly on i810 for 4444 textures
 
+  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) {
@@ -2070,6 +2084,10 @@ fill_d3d_volume_texture_pixels(DXScreenData &scrn) {
       mip_filter_flags = D3DX_FILTER_TRIANGLE;
     }
 
+    if (Texture::is_srgb(tex->get_format())) {
+      mip_filter_flags |= D3DX_FILTER_SRGB;
+    }
+
     //    mip_filter_flags| = D3DX_FILTER_DITHER;
 
     hr = D3DXFilterTexture(_d3d_texture, (PALETTEENTRY*)NULL, 0,
@@ -2167,6 +2185,17 @@ get_bits_per_pixel(Texture::Format format, int *alphbits) {
   case Texture::F_rgba32:
     *alphbits = 32;
     return 128;
+
+  case Texture::F_srgb:
+    return 24;
+  case Texture::F_srgb_alpha:
+    *alphbits = 8;
+    return 32;
+  case Texture::F_sluminance:
+    return 8;
+  case Texture::F_sluminance_alpha:
+    *alphbits = 8;
+    return 16;
   }
   return 8;
 }
@@ -2180,7 +2209,7 @@ PN_stdfloat DXTextureContext9::
 d3d_format_to_bytes_per_pixel (D3DFORMAT format)
 {
   PN_stdfloat bytes_per_pixel;
-  
+
   bytes_per_pixel = 0.0f;
   switch (format)
   {
@@ -2246,6 +2275,6 @@ d3d_format_to_bytes_per_pixel (D3DFORMAT format)
       bytes_per_pixel = 1.0f;
       break;
   }
-  
+
   return bytes_per_pixel;
 }

+ 4 - 4
panda/src/dxgsg9/wdxGraphicsPipe9.cxx

@@ -139,7 +139,7 @@ make_output(const string &name,
     // Early failure - if we are sure that this buffer WONT
     // meet specs, we can bail out early.
     if ((flags & BF_fb_props_optional) == 0) {
-      if ((fb_prop.get_indexed_color() > 0)||
+      if (fb_prop.get_indexed_color() ||
           (fb_prop.get_back_buffers() > 0)||
           (fb_prop.get_accum_bits() > 0)||
           (fb_prop.get_multisamples() > 0)) {
@@ -649,7 +649,7 @@ search_for_valid_displaymode(DXScreenData &scrn,
         continue;
       }
 
-      // disable refresh rate checking since SLI video cards may use 
+      // disable refresh rate checking since SLI video cards may use
       // refresh rates less than 60
       if (0) {
         if ((dispmode.RefreshRate<60) && (dispmode.RefreshRate>1)) {
@@ -663,7 +663,7 @@ search_for_valid_displaymode(DXScreenData &scrn,
           continue;
         }
       }
-      
+
       // Note no attempt is made to verify if format will work at
       // requested size, so even if this call succeeds, could still get
       // an out-of-video-mem error
@@ -956,7 +956,7 @@ const char *D3DFormatStr(D3DFORMAT fmt) {
     CASESTR(D3DFMT_D24X4S4);
     CASESTR(D3DFMT_VERTEXDATA);
     CASESTR(D3DFMT_INDEX16);
-    CASESTR(D3DFMT_INDEX32);    
+    CASESTR(D3DFMT_INDEX32);
     CASESTR(D3DFMT_A16B16G16R16F);
     CASESTR(D3DFMT_A32B32G32R32F);
   }

+ 14 - 14
panda/src/dxgsg9/wdxGraphicsWindow9.cxx

@@ -252,7 +252,7 @@ verify_window_sizes(int numsizes, int *dimen) {
 void wdxGraphicsWindow9::
 close_window() {
   if (wdxdisplay9_cat.is_debug()) {
-    wdxdisplay9_cat.debug() 
+    wdxdisplay9_cat.debug()
       << "wdxGraphicsWindow9::close_window() " << this << "\n";
   }
 
@@ -300,7 +300,7 @@ open_window() {
   // window.
   {
     WindowProperties resized_props;
-    resized_props.set_size(_wcontext._display_mode.Width, 
+    resized_props.set_size(_wcontext._display_mode.Width,
                            _wcontext._display_mode.Height);
     _properties.add_properties(resized_props);
   }
@@ -420,17 +420,17 @@ void wdxGraphicsWindow9::
 handle_reshape() {
   GdiFlush();
   WinGraphicsWindow::handle_reshape();
-  
-  if (_dxgsg != NULL) {
+
+  if (_dxgsg != NULL && _dxgsg->_d3d_device != NULL) {
     // create the new resized rendertargets
     WindowProperties props = get_properties();
     int x_size = props.get_x_size();
     int y_size = props.get_y_size();
-    
+
     if (_wcontext._presentation_params.BackBufferWidth != x_size ||
         _wcontext._presentation_params.BackBufferHeight != y_size) {
       bool resize_succeeded = reset_device_resize_window(x_size, y_size);
-      
+
       if (wdxdisplay9_cat.is_debug()) {
         if (!resize_succeeded) {
           wdxdisplay9_cat.debug()
@@ -708,7 +708,7 @@ create_screen_buffers_and_device(DXScreenData &display, bool force_16bpp_zbuffer
   if (dx_disable_driver_management_ex) {
     dwBehaviorFlags |= D3DCREATE_DISABLE_DRIVER_MANAGEMENT_EX;
   }
-  
+
   if (is_fullscreen()) {
     // CREATE FULLSCREEN BUFFERS
 
@@ -751,11 +751,11 @@ create_screen_buffers_and_device(DXScreenData &display, bool force_16bpp_zbuffer
     }
 
     //From d3d8caps.h
-    //D3DPRESENT_INTERVAL_DEFAULT  = 0x00000000L 
+    //D3DPRESENT_INTERVAL_DEFAULT  = 0x00000000L
     //#define D3DPRESENT_INTERVAL_ONE         0x00000001L
     //Next line is really sloppy, should either be D3DPRESENT_INTERVAL_DEFAULT or D3DPRESENT_INTERVAL_ONE
     //not a direct number! but I'm not going to touch it because it's working as is. Zhao 12/15/2011
-    presentation_params->PresentationInterval = 0;    
+    presentation_params->PresentationInterval = 0;
 
     //ATI 5450 doesn't like D3DSWAPEFFECT_FLIP
     presentation_params->SwapEffect = D3DSWAPEFFECT_DISCARD;
@@ -803,7 +803,7 @@ create_screen_buffers_and_device(DXScreenData &display, bool force_16bpp_zbuffer
 
   PRINT_REFCNT(wdxdisplay9, _wcontext._d3d_device);
 
-  if (presentation_params->EnableAutoDepthStencil) {    
+  if (presentation_params->EnableAutoDepthStencil) {
     int depth_bits;
     int stencil_bits;
 
@@ -846,7 +846,7 @@ create_screen_buffers_and_device(DXScreenData &display, bool force_16bpp_zbuffer
         wdxdisplay9_cat.error() << "unknown depth stencil format  " << presentation_params->AutoDepthStencilFormat;
         break;
     }
-      
+
     _fb_properties.set_stencil_bits(stencil_bits);
     _fb_properties.set_depth_bits(depth_bits);
   } else {
@@ -1118,16 +1118,16 @@ consider_device(wdxGraphicsPipe9 *dxpipe, DXDeviceInfo *device_info) {
 
   if (is_fullscreen()) {
     bool bCouldntFindValidZBuf;
-    
+
     dxpipe->search_for_valid_displaymode(_wcontext, dwRenderWidth, dwRenderHeight,
                                          bNeedZBuffer, bWantStencil,
                                          &_wcontext._supported_screen_depths_mask,
                                          &bCouldntFindValidZBuf,
                                          &pixFmt, dx_force_16bpp_zbuffer, true);
-    
+
     // note I'm not saving refresh rate, will just use adapter
     // default at given res for now
-    
+
     if (pixFmt == D3DFMT_UNKNOWN) {
       wdxdisplay9_cat.error()
         << (bCouldntFindValidZBuf ? "Couldnt find valid zbuffer format to go with FullScreen mode" : "No supported FullScreen modes")

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

@@ -31,5 +31,5 @@ VirtualFileMountAndroidAsset(AAssetManager *mgr, const string &apk_path) :
 ////////////////////////////////////////////////////////////////////
 INLINE VirtualFileMountAndroidAsset::AssetStream::
 AssetStream(AAsset *asset) :
-  istream(new AssetStreamBuf(asset)) {
+  istream(new VirtualFileMountAndroidAsset::AssetStreamBuf(asset)) {
 }

+ 1 - 1
panda/src/express/virtualFileMountAndroidAsset.cxx

@@ -57,7 +57,7 @@ get_fd(const Filename &file, off_t *start, off_t *length) const {
 ////////////////////////////////////////////////////////////////////
 bool VirtualFileMountAndroidAsset::
 has_file(const Filename &file) const {
-  return (file.empty() || is_directory(file) || is_regular_file(file));
+  return (file.empty() || /*is_directory(file) ||*/ is_regular_file(file));
 }
 
 ////////////////////////////////////////////////////////////////////

+ 1 - 0
panda/src/glesgsg/glesgsg.h

@@ -90,6 +90,7 @@
 #define GL_DEPTH_STENCIL_EXT GL_DEPTH_STENCIL_OES
 #define GL_UNSIGNED_INT_24_8_EXT GL_UNSIGNED_INT_24_8_OES
 #define GL_DEPTH24_STENCIL8_EXT GL_DEPTH24_STENCIL8_OES
+#define GL_DEPTH24_STENCIL8 GL_DEPTH24_STENCIL8_OES
 #define GL_DEPTH_COMPONENT16 GL_DEPTH_COMPONENT16_OES
 #define GL_DEPTH_COMPONENT24 GL_DEPTH_COMPONENT24_OES
 #define GL_DEPTH_COMPONENT32 GL_DEPTH_COMPONENT32_OES

+ 12 - 12
panda/src/glstuff/glCgShaderContext_src.cxx

@@ -18,7 +18,7 @@
 
 #include "Cg/cgGL.h"
 
-#include "pStatTimer.h"
+#include "pStatGPUTimer.h"
 
 TypeHandle CLP(CgShaderContext)::_type_handle;
 
@@ -50,7 +50,7 @@ CLP(CgShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderConte
 
   nassertv(s->get_language() == Shader::SL_Cg);
 
-  // Ask the shader to compile itself for us and 
+  // Ask the shader to compile itself for us and
   // to give us the resulting Cg program objects.
   if (!s->cg_compile_for(_glgsg->_shader_caps,
                          _cg_context,
@@ -223,7 +223,7 @@ unbind() {
 ////////////////////////////////////////////////////////////////////
 void CLP(CgShaderContext)::
 issue_parameters(int altered) {
-  PStatTimer timer(_glgsg->_draw_set_state_shader_parameters_pcollector);
+  //PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector);
 
   if (!valid()) {
     return;
@@ -233,9 +233,9 @@ issue_parameters(int altered) {
   for (int i=0; i<(int)_shader->_ptr_spec.size(); i++) {
     if (altered & (_shader->_ptr_spec[i]._dep[0] | _shader->_ptr_spec[i]._dep[1])) {
       const Shader::ShaderPtrSpec& _ptr = _shader->_ptr_spec[i];
-      Shader::ShaderPtrData* ptr_data = 
+      Shader::ShaderPtrData* ptr_data =
         const_cast< Shader::ShaderPtrData*>(_glgsg->fetch_ptr_parameter(_ptr));
-      
+
       if (ptr_data == NULL){ //the input is not contained in ShaderPtrData
         release_resources();
         return;
@@ -249,14 +249,14 @@ issue_parameters(int altered) {
       int input_size = _ptr._dim[0] * _ptr._dim[1] * _ptr._dim[2];
 
       // dimension is negative only if the parameter had the (deprecated)k_ prefix.
-      if ((input_size > ptr_data->_size) && (_ptr._dim[0] > 0)) { 
-        GLCAT.error() << _ptr._id._name << ": incorrect number of elements, expected " 
+      if ((input_size > ptr_data->_size) && (_ptr._dim[0] > 0)) {
+        GLCAT.error() << _ptr._id._name << ": incorrect number of elements, expected "
                       <<  input_size <<" got " <<  ptr_data->_size << "\n";
         release_resources();
         return;
       }
       CGparameter p = _cg_parameter_map[_ptr._id._seqno];
-      
+
       switch (ptr_data->_type) {
       case Shader::SPT_float:
         switch(_ptr._info._class) {
@@ -271,7 +271,7 @@ issue_parameters(int altered) {
         case Shader::SAC_matrix: cgGLSetMatrixParameterfc(p,(float*)ptr_data->_ptr); continue;
         case Shader::SAC_array: {
           switch(_ptr._info._subclass) {
-          case Shader::SAC_scalar: 
+          case Shader::SAC_scalar:
             cgGLSetParameterArray1f(p,0,_ptr._dim[0],(float*)ptr_data->_ptr); continue;
           case Shader::SAC_vector:
             switch(_ptr._dim[2]) {
@@ -298,7 +298,7 @@ issue_parameters(int altered) {
         case Shader::SAC_matrix: cgGLSetMatrixParameterdc(p,(double*)ptr_data->_ptr); continue;
         case Shader::SAC_array: {
           switch(_ptr._info._subclass) {
-          case Shader::SAC_scalar: 
+          case Shader::SAC_scalar:
             cgGLSetParameterArray1d(p,0,_ptr._dim[0],(double*)ptr_data->_ptr); continue;
           case Shader::SAC_vector:
             switch(_ptr._dim[2]) {
@@ -323,8 +323,8 @@ issue_parameters(int altered) {
           case Shader::SAT_vec4: cgSetParameter4iv(p,(int*)ptr_data->_ptr); continue;
           }
         }
-      default: GLCAT.error() << _ptr._id._name << ":" << "unrecognized parameter type\n"; 
-        release_resources(); 
+      default: GLCAT.error() << _ptr._id._name << ":" << "unrecognized parameter type\n";
+        release_resources();
         return;
       }
     }

+ 34 - 7
panda/src/glstuff/glGraphicsBuffer_src.cxx

@@ -27,7 +27,10 @@ CLP(GraphicsBuffer)(GraphicsEngine *engine, GraphicsPipe *pipe,
                     int flags,
                     GraphicsStateGuardian *gsg,
                     GraphicsOutput *host) :
-  GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host)
+  GraphicsBuffer(engine, pipe, name, fb_prop, win_prop, flags, gsg, host),
+  _bind_texture_pcollector(_draw_window_pcollector, "Bind textures"),
+  _generate_mipmap_pcollector(_draw_window_pcollector, "Generate mipmaps"),
+  _resolve_multisample_pcollector(_draw_window_pcollector, "Resolve multisamples")
 {
   CLP(GraphicsStateGuardian) *glgsg;
 
@@ -171,11 +174,12 @@ begin_frame(FrameMode mode, Thread *current_thread) {
 
     // In case of multisample rendering, we don't need to issue
     // the barrier until we call glBlitFramebuffer.
+#ifndef OPENGLES
     if (gl_enable_memory_barriers && _fbo_multisample == 0) {
       CLP(GraphicsStateGuardian) *glgsg;
       DCAST_INTO_R(glgsg, _gsg, false);
 
-      pvector<CLP(TextureContext)*>::iterator it;
+      TextureContexts::iterator it;
       for (it = _texture_contexts.begin(); it != _texture_contexts.end(); ++it) {
         CLP(TextureContext) *gtc = *it;
 
@@ -186,6 +190,7 @@ begin_frame(FrameMode mode, Thread *current_thread) {
         }
       }
     }
+#endif
   }
 
   _gsg->set_current_properties(&get_fb_properties());
@@ -274,6 +279,8 @@ rebuild_bitplanes() {
     return;
   }
 
+  PStatGPUTimer timer(glgsg, _bind_texture_pcollector);
+
   // Calculate bitplane size.  This can be larger than the buffer.
   if (_creation_flags & GraphicsPipe::BF_size_track_host) {
     if (_host->get_size() != _size) {
@@ -437,6 +444,19 @@ rebuild_bitplanes() {
     // Bind the FBO
     if (_fbo[layer] == 0) {
       glgsg->_glGenFramebuffers(1, &_fbo[layer]);
+
+#ifndef OPENGLES
+      if (glgsg->_use_object_labels) {
+        if (num_fbos > 1) {
+          GLchar name[128];
+          GLsizei len = snprintf(name, 128, "%s[%d]", _name.c_str(), layer);
+          glgsg->_glObjectLabel(GL_FRAMEBUFFER, _fbo[layer], len, name);
+        } else {
+          glgsg->_glObjectLabel(GL_FRAMEBUFFER, _fbo[layer], _name.size(), _name.data());
+        }
+      }
+#endif
+
       if (_fbo[layer] == 0) {
         report_my_gl_errors();
         return;
@@ -469,7 +489,7 @@ rebuild_bitplanes() {
       _have_any_color = true;
     }
 
-#ifndef OPENGLES
+#ifndef OPENGLES_1
     for (int i=0; i<_fb_properties.get_aux_rgba(); i++) {
       bind_slot(layer, rb_resize, attach, (RenderTexturePlane)(RTP_aux_rgba_0+i), next++);
       _have_any_color = true;
@@ -1131,6 +1151,8 @@ generate_mipmaps() {
   CLP(GraphicsStateGuardian) *glgsg;
   DCAST_INTO_V(glgsg, _gsg);
 
+  //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;
@@ -1168,7 +1190,8 @@ end_frame(FrameMode mode, Thread *current_thread) {
     copy_to_textures();
   }
 
-  // Unbind the FBO
+  // Unbind the FBO.  TODO: calling bind_fbo is slow, so we should
+  // probably move this to begin_frame to prevent unnecessary calls.
   CLP(GraphicsStateGuardian) *glgsg;
   DCAST_INTO_V(glgsg, _gsg);
   glgsg->bind_fbo(0);
@@ -1604,11 +1627,14 @@ check_host_valid() {
 ////////////////////////////////////////////////////////////////////
 void CLP(GraphicsBuffer)::
 resolve_multisamples() {
+  nassertv(_fbo.size() > 0);
+
   CLP(GraphicsStateGuardian) *glgsg;
   DCAST_INTO_V(glgsg, _gsg);
 
-  nassertv(_fbo.size() > 0);
+  PStatGPUTimer timer(glgsg, _resolve_multisample_pcollector);
 
+#ifndef OPENGLES
   if (gl_enable_memory_barriers) {
     // Issue memory barriers as necessary to make sure that the
     // texture memory is synchronized before we blit to it.
@@ -1623,6 +1649,7 @@ resolve_multisamples() {
       }
     }
   }
+#endif
 
   glgsg->report_my_gl_errors();
   GLuint fbo = _fbo[0];
@@ -1631,7 +1658,7 @@ resolve_multisamples() {
   }
   glgsg->_glBindFramebuffer(GL_DRAW_FRAMEBUFFER_EXT, fbo);
   glgsg->_glBindFramebuffer(GL_READ_FRAMEBUFFER_EXT, _fbo_multisample);
-  
+
   // If the depth buffer is shared, resolve it only on the last to render FBO.
   bool do_depth_blit = false;
   if (_rbm[RTP_depth_stencil] != 0 || _rbm[RTP_depth] != 0) {
@@ -1671,6 +1698,7 @@ resolve_multisamples() {
                               GL_NEAREST);
   }
   // Now handle the other color buffers.
+#ifndef OPENGLES_1
   int next = GL_COLOR_ATTACHMENT1_EXT;
   if (_fb_properties.is_stereo()) {
     glReadBuffer(next);
@@ -1679,7 +1707,6 @@ resolve_multisamples() {
                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
     next += 1;
   }
-#ifndef OPENGLES
   for (int i = 0; i < _fb_properties.get_aux_rgba(); ++i) {
     glReadBuffer(next);
     glDrawBuffer(next);

+ 14 - 9
panda/src/glstuff/glGraphicsBuffer_src.h

@@ -34,7 +34,7 @@
 //               * Can render onto a texture without clearing it first.
 //               * Supports multisample antialiased rendering.
 //
-//               Some of these deserve a little explanation. 
+//               Some of these deserve a little explanation.
 //               Auxiliary bitplanes are additional bitplanes above
 //               and beyond the normal depth,stencil,color.  One can
 //               use them to render out multiple textures in a single
@@ -43,14 +43,14 @@
 //               buffer will be equal to the texture's previous
 //               contents.  This alo means you can meaningfully
 //               share a bitplane between two buffers by binding
-//               the same texture to both buffers. 
+//               the same texture to both buffers.
 //
 //               If either of the necessary OpenGL extensions is not
 //               available, then the glGraphicsBuffer will not be
 //               available (although it may still be possible to
 //               create a wglGraphicsBuffer or glxGraphicsBuffer).
 //
-//               This class now also uses the extensions 
+//               This class now also uses the extensions
 //               EXT_framebuffer_multisample and EXT_framebuffer_blit
 //               to allow for multisample antialiasing these offscreen
 //               render targets.  If these extensions are unavailable
@@ -87,13 +87,13 @@ public:
 protected:
   virtual void close_buffer();
   virtual bool open_buffer();
-  
+
   void check_host_valid();
-  
+
   void report_my_errors(int line, const char *file);
 
 private:
-  
+
   void bind_slot(int layer, bool rb_resize, Texture **attach,
                  RenderTexturePlane plane, GLenum attachpoint);
   void bind_slot_multisample(bool rb_resize, Texture **attach,
@@ -127,7 +127,8 @@ private:
 
   // List of textures for which we might have to generate mipmaps
   // after rendering one frame.
-  pvector<CLP(TextureContext)*> _texture_contexts;
+  typedef pvector<CLP(TextureContext)*> TextureContexts;
+  TextureContexts _texture_contexts;
 
   // The cube map face we are currently drawing to or have just
   // finished drawing to, or -1 if we are not drawing to a cube map.
@@ -136,10 +137,14 @@ private:
   bool _initial_clear;
   bool _needs_rebuild;
   UpdateSeq _last_textures_seq;
-  
+
   CLP(GraphicsBuffer) *_shared_depth_buffer;
   list <CLP(GraphicsBuffer) *> _shared_depth_buffer_list;
-  
+
+  PStatCollector _bind_texture_pcollector;
+  PStatCollector _generate_mipmap_pcollector;
+  PStatCollector _resolve_multisample_pcollector;
+
 public:
   static TypeHandle get_class_type() {
     return _type_handle;

+ 9 - 5
panda/src/glstuff/glGraphicsStateGuardian_src.I

@@ -27,6 +27,7 @@
 INLINE bool CLP(GraphicsStateGuardian)::
 report_errors(int line, const char *source_file) {
 #ifndef NDEBUG
+  PStatTimer timer(_check_error_pcollector);
   GLenum error_code = glGetError();
   if (error_code != GL_NO_ERROR) {
     int error_count = 0;
@@ -46,6 +47,7 @@ INLINE void CLP(GraphicsStateGuardian)::
 report_my_errors(int line, const char *source_file) {
 #ifndef NDEBUG
   if (_check_errors) {
+    PStatTimer timer(_check_error_pcollector);
     GLenum error_code = glGetError();
     if (error_code != GL_NO_ERROR) {
       if (!report_errors_loop(line, source_file, error_code, _error_count)) {
@@ -69,6 +71,7 @@ report_my_errors(int line, const char *source_file) {
 ////////////////////////////////////////////////////////////////////
 INLINE bool CLP(GraphicsStateGuardian)::
 clear_errors(int line, const char *source_file) {
+  PStatTimer timer(_check_error_pcollector);
   GLenum error_code = glGetError();
   if (error_code != GL_NO_ERROR) {
     int error_count = 0;
@@ -92,6 +95,7 @@ clear_errors(int line, const char *source_file) {
 INLINE void CLP(GraphicsStateGuardian)::
 clear_my_errors(int line, const char *source_file) {
   if (_check_errors) {
+    PStatTimer timer(_check_error_pcollector);
     GLenum error_code = glGetError();
     if (error_code != GL_NO_ERROR) {
       int error_count = 0;
@@ -743,7 +747,7 @@ get_clip_plane_id(int index) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GraphicsStateGuardian)::get_supports_framebuffer_multisample
 //       Access: Public
-//  Description: Returns if this glGsg supports multisample 
+//  Description: Returns if this glGsg supports multisample
 //               antialiasing for framebuffer objects.
 ////////////////////////////////////////////////////////////////////
 INLINE bool CLP(GraphicsStateGuardian)::
@@ -754,7 +758,7 @@ get_supports_framebuffer_multisample() {
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GraphicsStateGuardian)::get_supports_framebuffer_multisample
 //       Access: Public
-//  Description: Returns if this glGsg supports multisample 
+//  Description: Returns if this glGsg supports multisample
 //               antialiasing for framebuffer objects.
 ////////////////////////////////////////////////////////////////////
 INLINE bool CLP(GraphicsStateGuardian)::
@@ -766,7 +770,7 @@ get_supports_framebuffer_multisample_coverage_nv() {
 ////////////////////////////////////////////////////////////////////
 //     Function: CLP(GraphicsStateGuardian)::get_supports_framebuffer_blit
 //       Access: Public
-//  Description: Returns if this glGsg supports multisample 
+//  Description: Returns if this glGsg supports multisample
 //               antialiasing for framebuffer objects.
 ////////////////////////////////////////////////////////////////////
 INLINE bool CLP(GraphicsStateGuardian)::
@@ -778,7 +782,7 @@ get_supports_framebuffer_blit() {
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::UsageTextureKey::Constructor
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE CLP(GraphicsStateGuardian)::UsageTextureKey::
 UsageTextureKey(int x_size, int y_size) :
@@ -792,7 +796,7 @@ UsageTextureKey(int x_size, int y_size) :
 ////////////////////////////////////////////////////////////////////
 //     Function: GLGraphicsStateGuardian::UsageTextureKey::operator <
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 INLINE bool CLP(GraphicsStateGuardian)::UsageTextureKey::
 operator < (const CLP(GraphicsStateGuardian)::UsageTextureKey &other) const {

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


+ 58 - 33
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -1,6 +1,6 @@
 // Filename: glGraphicsStateGuardian_src.h
 // Created by:  drose (02Feb99)
-// Updated by: fperazzi, PandaSE (05May10) (added 
+// Updated by: fperazzi, PandaSE (05May10) (added
 //   get_supports_cg_profile)
 //
 ////////////////////////////////////////////////////////////////////
@@ -37,6 +37,7 @@
 #include "pmap.h"
 #include "geomVertexArrayData.h"
 #include "lightMutex.h"
+#include "pStatGPUTimer.h"
 
 class PlaneNode;
 class Light;
@@ -58,6 +59,7 @@ typedef double GLdouble;
 typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam);
 typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam);
 typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled);
+typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label);
 typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img);
 typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids);
 typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id);
@@ -65,6 +67,8 @@ typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target);
 typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids);
 typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params);
 typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params);
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params);
+typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *params);
 typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params);
 typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices);
 // There is some trivial disagreement between different gl.h headers about this one, so we use our own typename.
@@ -98,32 +102,32 @@ typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data);
 typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data);
 typedef void (APIENTRYP PFNGLACTIVESTENCILFACEEXTPROC) (GLenum face);
-typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); 
-typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count); 
-typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights); 
-typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights); 
-typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer); 
-typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer); 
-typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); 
-typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); 
-typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); 
-typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params); 
-typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); 
-typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer); 
-typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); 
-typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); 
-typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target); 
-typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); 
-typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); 
+typedef void (APIENTRYP PFNGLWEIGHTPOINTERARBPROC) (GLint size, GLenum type, GLsizei stride, const GLvoid *pointer);
+typedef void (APIENTRYP PFNGLVERTEXBLENDARBPROC) (GLint count);
+typedef void (APIENTRYP PFNGLWEIGHTFVARBPROC) (GLint size, const GLfloat *weights);
+typedef void (APIENTRYP PFNGLWEIGHTDVARBPROC) (GLint size, const GLdouble *weights);
+typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFEREXTPROC) (GLuint renderbuffer);
+typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum target, GLuint renderbuffer);
+typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers);
+typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers);
+typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
+typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) (GLenum target, GLenum pname, GLint *params);
+typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer);
+typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum target, GLuint framebuffer);
+typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers);
+typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers);
+typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum target);
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);
 #ifdef OPENGLES_2
 typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DOES) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
 #else
-typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); 
+typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset);
 #endif
 typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREARBPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level);
 typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer);
-typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); 
-typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); 
+typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer);
+typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params);
 typedef void (APIENTRYP PFNGLGENERATEMIPMAPEXTPROC) (GLenum target);
 typedef void (APIENTRYP PFNGLCURRENTPALETTEMATRIXARBPROC) (GLint index);
 typedef void (APIENTRYP PFNGLMATRIXINDEXUIVARBPROC) (GLint size, const GLuint *indices);
@@ -189,7 +193,7 @@ typedef void (APIENTRYP PFNGLBINDIMAGETEXTURESPROC) (GLuint first, GLsizei count
 typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z);
 typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers);
 typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufsize, GLsizei *length, GLenum *binaryFormat, void *binary);
-typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); 
+typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params);
 typedef GLuint64 (APIENTRYP PFNGLGETTEXTUREHANDLEPROC) (GLuint texture);
 typedef GLuint64 (APIENTRYP PFNGLGETTEXTURESAMPLERHANDLEPROC) (GLuint texture, GLuint sampler);
 typedef void (APIENTRYP PFNGLMAKETEXTUREHANDLERESIDENTPROC) (GLuint64 handle);
@@ -228,7 +232,7 @@ public:
   virtual int get_driver_version_minor();
   virtual int get_driver_shader_version_major();
   virtual int get_driver_shader_version_minor();
-  
+
 #ifndef OPENGLES_1
   static void debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, GLvoid *userParam);
 #endif
@@ -302,13 +306,15 @@ public:
   virtual void begin_occlusion_query();
   virtual PT(OcclusionQueryContext) end_occlusion_query();
 
+  virtual PT(TimerQueryContext) issue_timer_query(int pstats_index);
+
   virtual void dispatch_compute(int size_x, int size_y, int size_z);
 
   virtual PT(GeomMunger) make_geom_munger(const RenderState *state,
                                           Thread *current_thread);
 
   virtual void clear(DrawableRegion *region);
-  
+
   virtual bool framebuffer_copy_to_texture
     (Texture *tex, int view, int z, const DisplayRegion *dr, const RenderBuffer &rb);
   virtual bool framebuffer_copy_to_ram
@@ -332,9 +338,9 @@ public:
   void draw_immediate_composite_primitives(const GeomPrimitivePipelineReader *reader, GLenum mode);
 #endif  // SUPPORT_IMMEDIATE_MODE
 
-  INLINE static bool report_errors(int line, const char *source_file);
+  INLINE bool report_errors(int line, const char *source_file);
   INLINE void report_my_errors(int line, const char *source_file);
-  INLINE static bool clear_errors(int line, const char *source_file);
+  INLINE bool clear_errors(int line, const char *source_file);
   INLINE void clear_my_errors(int line, const char *source_file);
 
   INLINE const string &get_gl_vendor() const;
@@ -475,8 +481,8 @@ protected:
   bool upload_texture(CLP(TextureContext) *gtc, bool force);
   bool upload_texture_image(CLP(TextureContext) *gtc, bool needs_reload,
                             bool uses_mipmaps, int mipmap_bias,
-                            GLenum texture_target, GLenum page_target, 
-                            GLint internal_format, GLint external_format, 
+                            GLenum texture_target, GLenum page_target,
+                            GLint internal_format, GLint external_format,
                             GLenum component_type,
                             bool one_page_only, int z,
                             Texture::CompressionMode image_compression);
@@ -590,9 +596,12 @@ protected:
 public:
   bool _supports_point_parameters;
   PFNGLPOINTPARAMETERFVPROC _glPointParameterfv;
-
   bool _supports_point_sprite;
 
+#ifndef OPENGLES
+  PFNGLPRIMITIVERESTARTINDEXPROC _glPrimitiveRestartIndex;
+#endif
+
   bool _supports_vertex_blend;
   PFNGLWEIGHTPOINTERARBPROC _glWeightPointer;
   PFNGLVERTEXBLENDARBPROC _glVertexBlend;
@@ -703,6 +712,14 @@ public:
   PFNGLGETQUERYIVPROC _glGetQueryiv;
   PFNGLGETQUERYOBJECTUIVPROC _glGetQueryObjectuiv;
 
+#ifndef OPENGLES
+  PFNGLQUERYCOUNTERPROC _glQueryCounter;
+  PFNGLGETQUERYOBJECTI64VPROC _glGetQueryObjecti64v;
+  PFNGLGETQUERYOBJECTUI64VPROC _glGetQueryObjectui64v;
+
+  PFNGLGETINTEGER64VPROC _glGetInteger64v;
+#endif
+
   PFNGLACTIVESTENCILFACEEXTPROC _glActiveStencilFaceEXT;
 
 #ifndef OPENGLES_1
@@ -778,9 +795,9 @@ public:
 #endif
 
   LightMutex _lock;
-  typedef pvector<GLuint> DeletedDisplayLists;
-  DeletedDisplayLists _deleted_display_lists;
-  DeletedDisplayLists _deleted_queries;
+  typedef pvector<GLuint> DeletedNames;
+  DeletedNames _deleted_display_lists;
+  DeletedNames _deleted_queries;
 
 #ifndef OPENGLES
   // Stores textures for which memory bariers should be issued.
@@ -797,6 +814,9 @@ public:
   bool _force_flush;
   bool _supports_debug;
 
+  bool _use_object_labels;
+  PFNGLOBJECTLABELPROC _glObjectLabel;
+
 #ifndef NDEBUG
   bool _show_texture_usage;
   int _show_texture_usage_max_size;
@@ -818,7 +838,11 @@ public:
   static PStatCollector _primitive_batches_display_list_pcollector;
   static PStatCollector _vertices_display_list_pcollector;
   static PStatCollector _vertices_immediate_pcollector;
-  static PStatCollector _compute_dispatch_pcollector;
+  static PStatCollector _memory_barrier_pcollector;
+  static PStatCollector _vertex_array_update_pcollector;
+  static PStatCollector _texture_update_pcollector;
+  static PStatCollector _fbo_bind_pcollector;
+  static PStatCollector _check_error_pcollector;
 
 public:
   virtual TypeHandle get_type() const {
@@ -846,6 +870,7 @@ private:
   friend class CLP(CgShaderContext);
   friend class CLP(GraphicsBuffer);
   friend class CLP(OcclusionQueryContext);
+  friend class CLP(TimerQueryContext);
 };
 
 #include "glGraphicsStateGuardian_src.I"

+ 14 - 0
panda/src/glstuff/glLatencyQueryContext_src.I

@@ -0,0 +1,14 @@
+// Filename: glLatencyQueryContext_src.I
+// Created by:  rdb (24Sep14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+

+ 54 - 0
panda/src/glstuff/glLatencyQueryContext_src.cxx

@@ -0,0 +1,54 @@
+// Filename: glLatencyQueryContext_src.cxx
+// Created by:  rdb (24Sep14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef OPENGLES  // Timer queries not supported by OpenGL ES.
+
+TypeHandle CLP(LatencyQueryContext)::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(LatencyQueryContext)::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+CLP(LatencyQueryContext)::
+CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg,
+                         int pstats_index) :
+  CLP(TimerQueryContext)(glgsg, pstats_index),
+  _timestamp(0)
+{
+  glgsg->_glGetInteger64v(GL_TIMESTAMP, &_timestamp);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LatencyQueryContext::get_timestamp
+//       Access: Public, Virtual
+//  Description: Returns the timestamp that is the result of this
+//               timer query.  There's no guarantee about which
+//               clock this uses, the only guarantee is that
+//               subtracting a start time from an end time should
+//               yield a time in seconds.
+//               If is_answer_ready() did not return true, this
+//               function may block before it returns.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+double CLP(LatencyQueryContext)::
+get_timestamp() const {
+  GLint64 time_ns;
+  _glgsg->_glGetQueryObjecti64v(_index, GL_QUERY_RESULT, &time_ns);
+
+  return (time_ns - _timestamp) * 0.000000001;
+}
+
+#endif  // OPENGLES

+ 57 - 0
panda/src/glstuff/glLatencyQueryContext_src.h

@@ -0,0 +1,57 @@
+// Filename: glLatencyQueryContext_src.h
+// Created by:  rdb (24Sep14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+class GraphicsStateGuardian;
+
+#ifndef OPENGLES  // Timer queries not supported by OpenGL ES.
+
+////////////////////////////////////////////////////////////////////
+//       Class : GLLatencyQueryContext
+// Description : This is a special variant of GLTimerQueryContext
+//               that measures the command latency, ie. the time
+//               it takes for the GPU to actually get to the commands
+//               we are issuing right now.
+////////////////////////////////////////////////////////////////////
+class EXPCL_GL CLP(LatencyQueryContext) : public CLP(TimerQueryContext) {
+public:
+  CLP(LatencyQueryContext)(CLP(GraphicsStateGuardian) *glgsg, int pstats_index);
+
+  ALLOC_DELETED_CHAIN(CLP(LatencyQueryContext));
+
+  virtual double get_timestamp() const;
+
+  GLint64 _timestamp;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    CLP(TimerQueryContext)::init_type();
+    register_type(_type_handle, CLASSPREFIX_QUOTED "LatencyQueryContext",
+                  CLP(TimerQueryContext)::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "glLatencyQueryContext_src.I"
+
+#endif  // OPENGLES
+

+ 28 - 19
panda/src/glstuff/glShaderContext_src.cxx

@@ -16,7 +16,7 @@
 
 #ifndef OPENGLES_1
 
-#include "pStatTimer.h"
+#include "pStatGPUTimer.h"
 
 TypeHandle CLP(ShaderContext)::_type_handle;
 
@@ -35,19 +35,19 @@ TypeHandle CLP(ShaderContext)::_type_handle;
 //       Access: Public
 //  Description: The Panda CG shader syntax defines a useful set of shorthand notations for setting nodepath
 //               properties as shaderinputs. For example, float4 mspos_XXX refers to nodepath XXX's position
-//               in model space. This function is a rough attempt to reimplement some of the shorthand 
+//               in model space. This function is a rough attempt to reimplement some of the shorthand
 //               notations for GLSL. The code is ~99% composed of excerpts dealing with matrix shaderinputs
-//               from Shader::compile_parameter.  
-//               
-//               Given a uniform variable name queried from the compiled shader passed in via arg_id, 
+//               from Shader::compile_parameter.
+//
+//               Given a uniform variable name queried from the compiled shader passed in via arg_id,
 //                  1) parse the name
 //                  2a) if the name refers to a Panda shorthand notation
 //                        push the appropriate matrix into shader._mat_spec
 //                        returns True
 //                  2b) If the name doesn't refer to a Panda shorthand notation
 //                        returns False
-//               
-//               The boolean return is used to notify down-river processing whether the shader var/parm was 
+//
+//               The boolean return is used to notify down-river processing whether the shader var/parm was
 //               actually picked up and the appropriate ShaderMatSpec pushed onto _mat_spec.
 ////////////////////////////////////////////////////////////////////
 bool CLP(ShaderContext)::
@@ -700,7 +700,7 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext
         }
       }
     }
-    
+
     // Now we've processed the uniforms, we'll process the attribs.
     _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTES, &param_count);
     _glgsg->_glGetProgramiv(_glsl_program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &param_maxlength);
@@ -836,7 +836,7 @@ release_resources() {
   }
 
   _glsl_shaders.clear();
-  
+
   _glgsg->report_my_gl_errors();
 }
 
@@ -890,7 +890,7 @@ unbind() {
 ////////////////////////////////////////////////////////////////////
 void CLP(ShaderContext)::
 issue_parameters(int altered) {
-  PStatTimer timer(_glgsg->_draw_set_state_shader_parameters_pcollector);
+  //PStatGPUTimer timer(_glgsg, _glgsg->_draw_set_state_shader_parameters_pcollector);
 
   if (!valid()) {
     return;
@@ -1374,12 +1374,11 @@ glsl_report_shader_errors(GLuint shader) {
   _glgsg->_glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
 
   if (length > 1) {
-    info_log = (char *) malloc(length);
+    info_log = (char *) alloca(length);
     _glgsg->_glGetShaderInfoLog(shader, length, &num_chars, info_log);
     if (strcmp(info_log, "Success.\n") != 0 && strcmp(info_log, "No errors.\n") != 0) {
       GLCAT.error(false) << info_log << "\n";
     }
-    free(info_log);
   }
 }
 
@@ -1397,19 +1396,18 @@ glsl_report_program_errors(GLuint program) {
   _glgsg->_glGetProgramiv(program, GL_INFO_LOG_LENGTH, &length);
 
   if (length > 1) {
-    info_log = (char *) malloc(length);
+    info_log = (char *) alloca(length);
     _glgsg->_glGetProgramInfoLog(program, length, &num_chars, info_log);
     if (strcmp(info_log, "Success.\n") != 0 && strcmp(info_log, "No errors.\n") != 0) {
       GLCAT.error(false) << info_log << "\n";
     }
-    free(info_log);
   }
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Shader::glsl_compile_shader
 //       Access: Private
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 bool CLP(ShaderContext)::
 glsl_compile_shader(Shader::ShaderType type) {
@@ -1451,16 +1449,21 @@ glsl_compile_shader(Shader::ShaderType type) {
     return false;
   }
 
+  if (_glgsg->_use_object_labels) {
+    string name = _shader->get_filename(type);
+    _glgsg->_glObjectLabel(GL_SHADER, handle, name.size(), name.data());
+  }
+
   string text_str = _shader->get_text(type);
   const char* text = text_str.c_str();
   _glgsg->_glShaderSource(handle, 1, &text, NULL);
   _glgsg->_glCompileShader(handle);
   GLint status;
   _glgsg->_glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
-  
+
   if (status != GL_TRUE) {
-    GLCAT.error() 
-      << "An error occurred while compiling shader " 
+    GLCAT.error()
+      << "An error occurred while compiling shader "
       << _shader->get_filename(type) << "\n";
     glsl_report_shader_errors(handle);
     _glgsg->_glDeleteShader(handle);
@@ -1485,6 +1488,12 @@ glsl_compile_and_link() {
   if (!_glsl_program) {
     return false;
   }
+
+  if (_glgsg->_use_object_labels) {
+    string name = _shader->get_filename();
+    _glgsg->_glObjectLabel(GL_PROGRAM, _glsl_program, name.size(), name.data());
+  }
+
   bool valid = true;
 
   if (!_shader->get_text(Shader::ST_vertex).empty()) {
@@ -1507,7 +1516,7 @@ glsl_compile_and_link() {
     nassertr(_glgsg->_glProgramParameteri != NULL, false);
     GLint max_vertices;
     glGetIntegerv(GL_MAX_GEOMETRY_OUTPUT_VERTICES, &max_vertices);
-    _glgsg->_glProgramParameteri(_glsl_program, GL_GEOMETRY_VERTICES_OUT_ARB, max_vertices); 
+    _glgsg->_glProgramParameteri(_glsl_program, GL_GEOMETRY_VERTICES_OUT_ARB, max_vertices);
   }
 #endif
 

+ 10 - 1
panda/src/glstuff/glTextureContext_src.cxx

@@ -23,12 +23,14 @@ TypeHandle CLP(TextureContext)::_type_handle;
 ////////////////////////////////////////////////////////////////////
 CLP(TextureContext)::
 ~CLP(TextureContext)() {
+#ifndef OPENGLES
   if (gl_enable_memory_barriers) {
     _glgsg->_textures_needing_fetch_barrier.erase(this);
     _glgsg->_textures_needing_image_access_barrier.erase(this);
     _glgsg->_textures_needing_update_barrier.erase(this);
     _glgsg->_textures_needing_framebuffer_barrier.erase(this);
   }
+#endif
 
   glDeleteTextures(1, &_index);
   _index = 0;
@@ -53,12 +55,15 @@ void CLP(TextureContext)::
 evict_lru() {
   dequeue_lru();
 
+#ifndef OPENGLES
   if (_handle != 0) {
     if (_handle_resident) {
       _glgsg->_glMakeTextureHandleNonResident(_handle);
     }
     _handle_resident = false;
-  } else {
+  } else
+#endif
+  {
     reset_data();
   }
 
@@ -74,9 +79,11 @@ evict_lru() {
 ////////////////////////////////////////////////////////////////////
 void CLP(TextureContext)::
 reset_data() {
+#ifndef OPENGLES
   if (_handle != 0 && _handle_resident) {
     _glgsg->_glMakeTextureHandleNonResident(_handle);
   }
+#endif
 
   // Free the texture resources.
   glDeleteTextures(1, &_index);
@@ -108,6 +115,7 @@ reset_data() {
 ////////////////////////////////////////////////////////////////////
 void CLP(TextureContext)::
 make_handle_resident() {
+#ifndef OPENGLES
   if (_handle != 0) {
     if (!_handle_resident) {
       _glgsg->_glMakeTextureHandleResident(_handle);
@@ -115,6 +123,7 @@ make_handle_resident() {
     }
     set_resident(true);
   }
+#endif
 }
 
 ////////////////////////////////////////////////////////////////////

+ 28 - 0
panda/src/glstuff/glTimerQueryContext_src.I

@@ -0,0 +1,28 @@
+// Filename: glTimerQueryContext_src.I
+// Created by:  rdb (22Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CLP(TimerQueryContext)::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE CLP(TimerQueryContext)::
+CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg,
+                       int pstats_index) :
+  TimerQueryContext(pstats_index),
+  _glgsg(glgsg),
+  _index(0)
+{
+}

+ 104 - 0
panda/src/glstuff/glTimerQueryContext_src.cxx

@@ -0,0 +1,104 @@
+// Filename: glTimerQueryContext_src.cxx
+// Created by:  rdb (22Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pnotify.h"
+#include "dcast.h"
+#include "lightMutexHolder.h"
+#include "pStatTimer.h"
+
+#ifndef OPENGLES  // Timer queries not supported by OpenGL ES.
+
+TypeHandle CLP(TimerQueryContext)::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLTimerQueryContext::Destructor
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+CLP(TimerQueryContext)::
+~CLP(TimerQueryContext)() {
+  if (_index != 0) {
+    // Tell the GSG to recycle this index when it gets around to it.
+    LightMutexHolder holder(_glgsg->_lock);
+    _glgsg->_deleted_queries.push_back(_index);
+    _index = 0;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLTimerQueryContext::is_answer_ready
+//       Access: Public, Virtual
+//  Description: Returns true if the query's answer is ready, false
+//               otherwise.  If this returns false, the application
+//               must continue to poll until it returns true.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+bool CLP(TimerQueryContext)::
+is_answer_ready() const {
+  GLuint result;
+  _glgsg->_glGetQueryObjectuiv(_index, GL_QUERY_RESULT_AVAILABLE, &result);
+
+  return (result != 0);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GLTimerQueryContext::waiting_for_answer
+//       Access: Public, Virtual
+//  Description: Requests the graphics engine to expedite the pending
+//               answer--the application is now waiting until the
+//               answer is ready.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+void CLP(TimerQueryContext)::
+waiting_for_answer() {
+  PStatTimer timer(GraphicsStateGuardian::_wait_timer_pcollector);
+  glFlush();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TimerQueryContext::get_timestamp
+//       Access: Public, Virtual
+//  Description: Returns the timestamp that is the result of this
+//               timer query.  There's no guarantee about which
+//               clock this uses, the only guarantee is that
+//               subtracting a start time from an end time should
+//               yield a time in seconds.
+//               If is_answer_ready() did not return true, this
+//               function may block before it returns.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+double CLP(TimerQueryContext)::
+get_timestamp() const {
+  GLuint64 time_ns;
+
+  /*GLuint available;
+  _glgsg->_glGetQueryObjectuiv(_index[1], GL_QUERY_RESULT_AVAILABLE, &available);
+  if (available) {
+    // The answer is ready now.
+    do_get_timestamps(begin_ns, end_ns);
+  } else {
+    // The answer is not ready; this call will block.
+    PStatTimer timer(GraphicsStateGuardian::_wait_timer_pcollector);
+    do_get_timestamps(begin_ns, end_ns);
+  }*/
+
+  _glgsg->_glGetQueryObjectui64v(_index, GL_QUERY_RESULT, &time_ns);
+
+  return time_ns * 0.000000001;
+}
+
+#endif  // OPENGLES

+ 68 - 0
panda/src/glstuff/glTimerQueryContext_src.h

@@ -0,0 +1,68 @@
+// Filename: glTimerQueryContext_src.h
+// Created by:  rdb (22Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "pandabase.h"
+#include "timerQueryContext.h"
+#include "deletedChain.h"
+#include "clockObject.h"
+
+class GraphicsStateGuardian;
+
+#ifndef OPENGLES  // Timer queries not supported by OpenGL ES.
+
+////////////////////////////////////////////////////////////////////
+//       Class : GLTimerQueryContext
+// Description : This class manages a timer query that can be used
+//               by a PStatGPUTimer to measure the time a task takes
+//               to execute on the GPU.
+//               This records the current timestamp; a pair of these
+//               is usually used to get the elapsed time.
+////////////////////////////////////////////////////////////////////
+class EXPCL_GL CLP(TimerQueryContext) : public TimerQueryContext {
+public:
+  INLINE CLP(TimerQueryContext)(CLP(GraphicsStateGuardian) *glgsg,
+                                int pstats_index);
+  virtual ~CLP(TimerQueryContext)();
+
+  ALLOC_DELETED_CHAIN(CLP(TimerQueryContext));
+
+  virtual bool is_answer_ready() const;
+  virtual void waiting_for_answer();
+  virtual double get_timestamp() const;
+
+  GLuint _index;
+  CLP(GraphicsStateGuardian) *_glgsg;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    TimerQueryContext::init_type();
+    register_type(_type_handle, CLASSPREFIX_QUOTED "TimerQueryContext",
+                  TimerQueryContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+};
+
+#include "glTimerQueryContext_src.I"
+
+#endif  // OPENGLES
+

+ 11 - 1
panda/src/glstuff/glmisc_src.cxx

@@ -151,6 +151,13 @@ ConfigVariableEnum<NotifySeverity> gl_debug_abort_level
             "that triggered the error message.  "
             "This feature is not available when NDEBUG has been defined."));
 
+ConfigVariableBool gl_debug_object_labels
+  ("gl-debug-object-labels", true,
+   PRC_DESC("When gl-debug is set to true, this will tell OpenGL the "
+            "name of textures, shaders, and other objects, so that OpenGL "
+            "can display those in error messages.  There's usually no "
+            "reason to disable this."));
+
 ConfigVariableBool gl_debug_buffers
   ("gl-debug-buffers", false,
    PRC_DESC("Set this true, in addition to enabling debug notify for "
@@ -162,7 +169,8 @@ ConfigVariableBool gl_finish
    PRC_DESC("Set this true to force a call to glFinish() after every major "
             "graphics operation.  This is likely to slow down rendering "
             "performance substantially, but it will make PStats graphs "
-            "more accurately reflect where the graphics bottlenecks are.  "
+            "more accurately reflect where the graphics bottlenecks are, "
+            "although it is better to use timer queries when available. "
             "This variable is enabled only if PStats is compiled in."));
 
 ConfigVariableBool gl_force_depth_stencil
@@ -252,6 +260,8 @@ void CLP(init_classes)() {
 
 #ifndef OPENGLES
   CLP(OcclusionQueryContext)::init_type();
+  CLP(TimerQueryContext)::init_type();
+  CLP(LatencyQueryContext)::init_type();
 #endif
 
   PandaSystem *ps = PandaSystem::get_global_ptr();

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

@@ -58,6 +58,7 @@ extern ConfigVariableEnum<GeomEnums::UsageHint> gl_min_buffer_usage_hint;
 extern ConfigVariableBool gl_debug;
 extern ConfigVariableBool gl_debug_synchronous;
 extern ConfigVariableEnum<NotifySeverity> gl_debug_abort_level;
+extern ConfigVariableBool gl_debug_object_labels;
 extern ConfigVariableBool gl_debug_buffers;
 extern ConfigVariableBool gl_finish;
 extern ConfigVariableBool gl_force_depth_stencil;

+ 2 - 0
panda/src/glstuff/glstuff_src.cxx

@@ -22,6 +22,8 @@
 #include "glVertexBufferContext_src.cxx"
 #include "glIndexBufferContext_src.cxx"
 #include "glOcclusionQueryContext_src.cxx"
+#include "glTimerQueryContext_src.cxx"
+#include "glLatencyQueryContext_src.cxx"
 #include "glGeomContext_src.cxx"
 #include "glGeomMunger_src.cxx"
 #include "glShaderContext_src.cxx"

+ 2 - 0
panda/src/glstuff/glstuff_src.h

@@ -36,6 +36,8 @@
 #include "glVertexBufferContext_src.h"
 #include "glIndexBufferContext_src.h"
 #include "glOcclusionQueryContext_src.h"
+#include "glTimerQueryContext_src.h"
+#include "glLatencyQueryContext_src.h"
 #include "glGeomContext_src.h"
 #include "glGeomMunger_src.h"
 #include "glShaderContext_src.h"

+ 8 - 3
panda/src/glstuff/panda_glext.h

@@ -6,7 +6,7 @@ extern "C" {
 
 /*
 ** Copyright (c) 2007-2012 The Khronos Group Inc.
-** 
+**
 ** Permission is hereby granted, free of charge, to any person obtaining a
 ** copy of this software and/or associated documentation files (the
 ** "Materials"), to deal in the Materials without restriction, including
@@ -14,10 +14,10 @@ extern "C" {
 ** distribute, sublicense, and/or sell copies of the Materials, and to
 ** permit persons to whom the Materials are furnished to do so, subject to
 ** the following conditions:
-** 
+**
 ** The above copyright notice and this permission notice shall be included
 ** in all copies or substantial portions of the Materials.
-** 
+**
 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
@@ -1163,6 +1163,11 @@ extern "C" {
 /* reuse GL_TEXTURE_IMMUTABLE_FORMAT */
 #endif
 
+#ifndef GL_VERSION_4_3
+#define GL_VERSION_4_3 1
+#define GL_PRIMITIVE_RESTART_FIXED_INDEX  0x8D69
+#endif
+
 #ifndef GL_ARB_multitexture
 #define GL_TEXTURE0_ARB                   0x84C0
 #define GL_TEXTURE1_ARB                   0x84C1

+ 4 - 0
panda/src/gobj/Sources.pp

@@ -43,6 +43,7 @@
     geomVertexWriter.h geomVertexWriter.I \
     indexBufferContext.I indexBufferContext.h \
     internalName.I internalName.h \
+    internalName_ext.h internalName_ext.cxx \
     lens.h lens.I \
     material.I material.h materialPool.I materialPool.h  \
     matrixLens.I matrixLens.h \
@@ -66,6 +67,7 @@
     textureReloadRequest.I textureReloadRequest.h \
     textureStage.I textureStage.h \
     textureStagePool.I textureStagePool.h \
+    timerQueryContext.I timerQueryContext.h \
     transformBlend.I transformBlend.h \
     transformBlendTable.I transformBlendTable.h \
     transformTable.I transformTable.h \
@@ -136,6 +138,7 @@
     textureReloadRequest.cxx \
     textureStage.cxx \
     textureStagePool.cxx \
+    timerQueryContext.cxx \
     transformBlend.cxx \
     transformBlendTable.cxx \
     transformTable.cxx \
@@ -208,6 +211,7 @@
     textureReloadRequest.I textureReloadRequest.h \
     textureStage.I textureStage.h \
     textureStagePool.I textureStagePool.h \
+    timerQueryContext.I timerQueryContext.h \
     transformBlend.I transformBlend.h \
     transformBlendTable.I transformBlendTable.h \
     transformTable.I transformTable.h \

+ 5 - 3
panda/src/gobj/config_gobj.cxx

@@ -44,6 +44,7 @@
 #include "textureReloadRequest.h"
 #include "textureStage.h"
 #include "textureContext.h"
+#include "timerQueryContext.h"
 #include "shader.h"
 #include "shaderContext.h"
 #include "transformBlend.h"
@@ -92,7 +93,7 @@ ConfigVariableInt texture_scale_limit
           "to both X and Y."));
 
 ConfigVariableList exclude_texture_scale
-("exclude-texture-scale", 
+("exclude-texture-scale",
  PRC_DESC("This is a list of glob patterns for texture filenames "
           "(excluding the directory part of the filename, but including "
           "the extension); for instance, 'digits_*.png'.  Any texture "
@@ -287,7 +288,7 @@ ConfigVariableInt vertex_column_alignment
           "this alignment for the vertex animation columns only."));
 
 ConfigVariableBool vertex_animation_align_16
-("vertex-animation-align-16", 
+("vertex-animation-align-16",
 #ifdef LINMATH_ALIGN
  true,
 #else
@@ -343,7 +344,7 @@ ConfigVariableInt simple_image_size
 
 ConfigVariableDouble simple_image_threshold
 ("simple-image-threshold", 0.1,
- PRC_DESC("This is a value that indicates how closely a texture's " 
+ PRC_DESC("This is a value that indicates how closely a texture's "
           "generated simple "
           "image should approximate the original image.  The smaller the "
           "number, the closer the match; small numbers will result in "
@@ -568,6 +569,7 @@ ConfigureFn(config_gobj) {
   TexturePoolFilter::init_type();
   TextureReloadRequest::init_type();
   TextureStage::init_type();
+  TimerQueryContext::init_type();
   TransformBlend::init_type();
   TransformBlendTable::init_type();
   TransformTable::init_type();

+ 3 - 1
panda/src/gobj/geomEnums.h

@@ -125,6 +125,9 @@ PUBLISHED:
     // The union of all of the above composite types.
     GR_composite_bits       = 0x01c00,
 
+    // If strip-cut indices are used to restart a composite primitive.
+    GR_strip_cut_index      = 0x20000,
+
     // If the shade model requires a particular vertex for flat shading.
     GR_flat_first_vertex    = 0x02000,
     GR_flat_last_vertex     = 0x04000,
@@ -216,4 +219,3 @@ EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::NumericType nume
 EXPCL_PANDA_GOBJ ostream &operator << (ostream &out, GeomEnums::Contents contents);
 
 #endif
-

+ 51 - 2
panda/src/gobj/geomLinestrips.cxx

@@ -89,7 +89,11 @@ get_primitive_type() const {
 int GeomLinestrips::
 get_geom_rendering() const {
   if (is_indexed()) {
-    return GR_line_strip | GR_indexed_other;
+    if (get_num_primitives() > 1) {
+      return GR_line_strip | GR_indexed_other | GR_strip_cut_index; 
+    } else {
+      return GR_line_strip | GR_indexed_other;
+    }
   } else {
     return GR_line_strip;
   }
@@ -106,6 +110,20 @@ get_min_num_vertices_per_primitive() const {
   return 2;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomLinestrips::get_num_unused_vertices_per_primitive
+//       Access: Public, Virtual
+//  Description: Returns the number of vertices that are added between
+//               primitives that aren't, strictly speaking, part of
+//               the primitives themselves.  This is used, for
+//               instance, to define degenerate triangles to connect
+//               otherwise disconnected triangle strips.
+////////////////////////////////////////////////////////////////////
+int GeomLinestrips::
+get_num_unused_vertices_per_primitive() const {
+  return 1;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomLinestrips::draw
 //       Access: Public, Virtual
@@ -138,9 +156,13 @@ decompose_impl() const {
   lines->set_shade_model(get_shade_model());
   CPTA_int ends = get_ends();
 
-  int vi = 0;
+  int num_unused = get_num_unused_vertices_per_primitive();
+
+  int vi = -num_unused;
   int li = 0;
   while (li < (int)ends.size()) {
+    // Skip unused vertices between tristrips.
+    vi += num_unused;
     int end = ends[li];
     nassertr(vi + 1 <= end, lines.p());
     int v0 = get_vertex(vi);
@@ -210,6 +232,33 @@ rotate_impl() const {
   return new_vertices;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomLinestrips::requires_unused_vertices
+//       Access: Protected, Virtual
+//  Description: Should be redefined to return true in any primitive
+//               that implements append_unused_vertices().
+////////////////////////////////////////////////////////////////////
+bool GeomLinestrips::
+requires_unused_vertices() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GeomLinestrips::append_unused_vertices
+//       Access: Protected, Virtual
+//  Description: Called when a new primitive is begun (other than the
+//               first primitive), this should add some degenerate
+//               vertices between primitives, if the primitive type
+//               requires that.  The second parameter is the first
+//               vertex that begins the new primitive.
+////////////////////////////////////////////////////////////////////
+void GeomLinestrips::
+append_unused_vertices(GeomVertexArrayData *vertices, int vertex) {
+  GeomVertexWriter to(vertices, 0);
+  to.set_row_unsafe(vertices->get_num_rows());
+  to.add_data1i(get_strip_cut_index());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomLinestrips::register_with_read_factory
 //       Access: Public, Static

+ 4 - 0
panda/src/gobj/geomLinestrips.h

@@ -34,6 +34,7 @@ public:
   virtual PrimitiveType get_primitive_type() const;
   virtual int get_geom_rendering() const;
   virtual int get_min_num_vertices_per_primitive() const;
+  virtual int get_num_unused_vertices_per_primitive() const;
 
 public:
   virtual bool draw(GraphicsStateGuardianBase *gsg,
@@ -43,6 +44,9 @@ public:
 protected:
   virtual CPT(GeomPrimitive) decompose_impl() const;
   virtual CPT(GeomVertexArrayData) rotate_impl() const;
+  virtual bool requires_unused_vertices() const;
+  virtual void append_unused_vertices(GeomVertexArrayData *vertices, 
+                                      int vertex);
 
 public:
   static void register_with_read_factory();

+ 52 - 1
panda/src/gobj/geomPrimitive.I

@@ -129,7 +129,7 @@ get_first_vertex() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::get_num_vertices
 //       Access: Published
-//  Description: Returns the number of vertices used by all the
+//  Description: Returns the number of indices used by all the
 //               primitives in this object.
 ////////////////////////////////////////////////////////////////////
 INLINE int GeomPrimitive::
@@ -310,6 +310,20 @@ get_index_stride() const {
   return reader.get_index_stride();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::get_strip_cut_index
+//       Access: Published
+//  Description: If relevant, returns the index value that may be
+//               used in some cases to signify the end of a
+//               primitive.  This is typically the highest value
+//               that the numeric type can store.
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitive::
+get_strip_cut_index() const {
+  CDReader cdata(_cycler);
+  return get_strip_cut_index(cdata->_index_type);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::get_ends
 //       Access: Published
@@ -415,6 +429,17 @@ add_vertices(int v1, int v2, int v3, int v4) {
   add_vertex(v4);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::get_index_format
+//       Access: Public
+//  Description: Returns a registered format appropriate for using to
+//               store the index table.
+////////////////////////////////////////////////////////////////////
+INLINE const GeomVertexArrayFormat *GeomPrimitive::
+get_index_format() const {
+  return get_index_format(get_index_type());
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::make_index_data
 //       Access: Public
@@ -425,6 +450,22 @@ make_index_data() const {
   return new GeomVertexArrayData(get_index_format(), get_usage_hint());
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::make_index_format
+//       Access: Private, Static
+//  Description: Returns a registered format appropriate for using to
+//               store the index table.
+////////////////////////////////////////////////////////////////////
+INLINE CPT(GeomVertexArrayFormat) GeomPrimitive::
+make_index_format(NumericType index_type) {
+  PT(GeomVertexArrayFormat) format = new GeomVertexArrayFormat;
+  // It's important that the index format *not* respect the global
+  // setting of vertex-column-alignment.  It needs to be tightly
+  // packed, so we specify an explict column_alignment of 1.
+  format->add_column(InternalName::get_index(), 1, index_type, C_index, 0, 1);
+  return GeomVertexArrayFormat::register_format(format);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::CData::Constructor
 //       Access: Public
@@ -679,6 +720,16 @@ get_read_pointer(bool force) const {
   return _vertices_reader->get_read_pointer(force);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitivePipelineReader::get_strip_cut_index
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE int GeomPrimitivePipelineReader::
+get_strip_cut_index() const {
+  return GeomPrimitive::get_strip_cut_index(_cdata->_index_type);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitivePipelineReader::get_ends
 //       Access: Public

+ 209 - 75
panda/src/gobj/geomPrimitive.cxx

@@ -62,18 +62,18 @@ make_cow_copy() {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 GeomPrimitive::
 GeomPrimitive(GeomPrimitive::UsageHint usage_hint) {
   CDWriter cdata(_cycler, true);
   cdata->_usage_hint = usage_hint;
 }
- 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::Copy Constructor
 //       Access: Published
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 GeomPrimitive::
 GeomPrimitive(const GeomPrimitive &copy) :
@@ -81,7 +81,7 @@ GeomPrimitive(const GeomPrimitive &copy) :
   _cycler(copy._cycler)
 {
 }
-  
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::Copy Assignment Operator
 //       Access: Published
@@ -99,7 +99,7 @@ operator = (const GeomPrimitive &copy) {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::Destructor
 //       Access: Published, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 GeomPrimitive::
 ~GeomPrimitive() {
@@ -198,7 +198,7 @@ add_vertex(int vertex) {
 
   int num_primitives = get_num_primitives();
   if (num_primitives > 0 &&
-      requires_unused_vertices() && 
+      requires_unused_vertices() &&
       get_num_vertices() == get_primitive_end(num_primitives - 1)) {
     // If we are beginning a new primitive, give the derived class a
     // chance to insert some degenerate vertices.
@@ -225,7 +225,7 @@ add_vertex(int vertex) {
       cdata->_got_minmax = false;
       return;
     }
-    
+
     // Otherwise, we need to suddenly become an indexed primitive.
     do_make_indexed(cdata);
   }
@@ -289,7 +289,7 @@ add_consecutive_vertices(int start, int num_vertices) {
       cdata->_got_minmax = false;
       return;
     }
-    
+
     // Otherwise, we need to suddenly become an indexed primitive.
     do_make_indexed(cdata);
   }
@@ -469,12 +469,18 @@ offset_vertices(int offset) {
       recompute_minmax(cdata);
       nassertv(cdata->_got_minmax);
     }
-    
+
     consider_elevate_index_type(cdata, cdata->_max_vertex + offset);
 
+    int strip_cut_index = get_strip_cut_index(cdata->_index_type);
+
     GeomVertexRewriter index(do_modify_vertices(cdata), 0);
     while (!index.is_at_end()) {
-      index.set_data1i(index.get_data1i() + offset);
+      int vertex = index.get_data1i();
+
+      if (vertex != strip_cut_index) {
+        index.set_data1i(vertex + offset);
+      }
     }
 
   } else {
@@ -484,7 +490,7 @@ offset_vertices(int offset) {
     cdata->_modified = Geom::get_next_modified();
     cdata->_got_minmax = false;
 
-    consider_elevate_index_type(cdata, 
+    consider_elevate_index_type(cdata,
                                 cdata->_first_vertex + cdata->_num_vertices - 1);
   }
 }
@@ -517,14 +523,19 @@ offset_vertices(int offset, int begin_row, int end_row) {
 
   if (is_indexed()) {
     CDWriter cdata(_cycler, true);
-    
+
+    int strip_cut_index = get_strip_cut_index(cdata->_index_type);
+
     // Calculate the maximum vertex over our range.
     int max_vertex = 0;
     {
       GeomVertexReader index_r(cdata->_vertices.get_read_pointer(), 0);
       index_r.set_row_unsafe(begin_row);
       for (int j = begin_row; j < end_row; ++j) {
-        max_vertex = max(max_vertex, index_r.get_data1i());
+        int vertex = index_r.get_data1i();
+        if (vertex != strip_cut_index) {
+          max_vertex = max(max_vertex, vertex);
+        }
       }
     }
 
@@ -533,7 +544,10 @@ offset_vertices(int offset, int begin_row, int end_row) {
     GeomVertexRewriter index(do_modify_vertices(cdata), 0);
     index.set_row_unsafe(begin_row);
     for (int j = begin_row; j < end_row; ++j) {
-      index.set_data1i(index.get_data1i() + offset);
+      int vertex = index.get_data1i();
+      if (vertex != strip_cut_index) {
+        index.set_data1i(vertex + offset);
+      }
     }
 
   } else {
@@ -545,7 +559,7 @@ offset_vertices(int offset, int begin_row, int end_row) {
     cdata->_modified = Geom::get_next_modified();
     cdata->_got_minmax = false;
 
-    consider_elevate_index_type(cdata, 
+    consider_elevate_index_type(cdata,
                                 cdata->_first_vertex + cdata->_num_vertices - 1);
   }
 }
@@ -555,17 +569,20 @@ offset_vertices(int offset, int begin_row, int end_row) {
 //       Access: Published
 //  Description: Converts the primitive from indexed to nonindexed by
 //               duplicating vertices as necessary into the indicated
-//               dest GeomVertexData.
+//               dest GeomVertexData.  Note: does not support
+//               primitives with strip cut indices.
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 make_nonindexed(GeomVertexData *dest, const GeomVertexData *source) {
   Thread *current_thread = Thread::get_current_thread();
   int num_vertices = get_num_vertices();
   int dest_start = dest->get_num_rows();
+  int strip_cut_index = get_strip_cut_index();
 
   dest->set_num_rows(dest_start + num_vertices);
   for (int i = 0; i < num_vertices; ++i) {
     int v = get_vertex(i);
+    nassertd(v != strip_cut_index) continue;
     dest->copy_row_from(dest_start + i, source, v, current_thread);
   }
 
@@ -597,14 +614,18 @@ pack_vertices(GeomVertexData *dest, const GeomVertexData *source) {
 
     int num_vertices = get_num_vertices();
     int dest_start = dest->get_num_rows();
+    int strip_cut_index = get_strip_cut_index();
 
     for (int i = 0; i < num_vertices; ++i) {
       int v = get_vertex(i);
+      if (v == strip_cut_index) {
+        continue;
+      }
 
       // Try to add the relation { v : size() }.  If that succeeds,
       // great; if it doesn't, look up whatever we previously added
       // for v.
-      pair<CopiedIndices::iterator, bool> result = 
+      pair<CopiedIndices::iterator, bool> result =
         copied_indices.insert(CopiedIndices::value_type(v, (int)copied_indices.size()));
       int v2 = (*result.first).second + dest_start;
       index.add_data1i(v2);
@@ -614,7 +635,7 @@ pack_vertices(GeomVertexData *dest, const GeomVertexData *source) {
         dest->copy_row_from(v2, source, v, current_thread);
       }
     }
-    
+
     set_vertices(new_vertices);
   }
 }
@@ -644,7 +665,7 @@ make_indexed() {
 //     Function: GeomPrimitive::get_primitive_start
 //       Access: Published
 //  Description: Returns the element within the _vertices list at which
-//               the nth primitive starts.  
+//               the nth primitive starts.
 //
 //               If i is one more than the highest valid primitive
 //               vertex, the return value will be one more than the
@@ -724,7 +745,7 @@ get_primitive_num_vertices(int n) const {
     } else {
       int num_unused_vertices_per_primitive = get_num_unused_vertices_per_primitive();
       return cdata->_ends[n] - cdata->_ends[n - 1] - num_unused_vertices_per_primitive;
-    }      
+    }
 
   } else {
     // This is a simple primitive type like a triangle: each primitive
@@ -733,6 +754,28 @@ get_primitive_num_vertices(int n) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::get_num_used_vertices
+//       Access: Published
+//  Description: Returns the number of vertices used by all of the
+//               primitives.  This is the same as summing
+//               get_primitive_num_vertices(n) for n in
+//               get_num_primitives().  It is like get_num_vertices
+//               except that it excludes all of the degenerate
+//               vertices and strip-cut indices.
+////////////////////////////////////////////////////////////////////
+int GeomPrimitive::
+get_num_used_vertices() const {
+  int num_primitives = get_num_primitives();
+
+  if (num_primitives > 0) {
+    return get_num_vertices() - ((num_primitives - 1) *
+           get_num_unused_vertices_per_primitive());
+  } else {
+    return 0;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::get_primitive_min_vertex
 //       Access: Published
@@ -960,10 +1003,14 @@ make_points() const {
   int num_vertices = get_num_vertices();
   if (is_indexed()) {
     CPT(GeomVertexArrayData) vertices = get_vertices();
+    int strip_cut_index = get_strip_cut_index();
     GeomVertexReader index(vertices, 0);
     for (int vi = 0; vi < num_vertices; ++vi) {
       nassertr(!index.is_at_end(), NULL);
-      bits.set_bit(index.get_data1i());
+      int vertex = index.get_data1i();
+      if (vertex != strip_cut_index) {
+        bits.set_bit(vertex);
+      }
     }
   } else {
     int first_vertex = get_first_vertex();
@@ -997,14 +1044,13 @@ make_points() const {
 //       Access: Published
 //  Description: Decomposes a complex primitive type into a simpler
 //               primitive type, for instance triangle strips to
-//               triangles, puts these in a new GeomPatches objectand returns a pointer to the new primitive
+//               triangles, puts these in a new GeomPatches object
+//               and returns a pointer to the new primitive
 //               definition.  If the decomposition cannot be
 //               performed, this might return the original object.
 //
 //               This method is useful for application code that wants
-//               to iterate through the set of triangles on the
-//               primitive without having to write handlers for each
-//               possible kind of primitive type.
+//               to use tesselation shaders on arbitrary geometry.
 ////////////////////////////////////////////////////////////////////
 CPT(GeomPrimitive) GeomPrimitive::
 make_patches() const {
@@ -1080,7 +1126,7 @@ request_resident() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::output
 //       Access: Published, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 output(ostream &out) const {
@@ -1091,7 +1137,7 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::write
 //       Access: Published, Virtual
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 write(ostream &out, int indent_level) const {
@@ -1188,6 +1234,11 @@ set_vertices(const GeomVertexArrayData *vertices, int num_vertices) {
   cdata->_vertices = (GeomVertexArrayData *)vertices;
   cdata->_num_vertices = num_vertices;
 
+  // Validate the format and make sure to copy its numeric type.
+  const GeomVertexArrayFormat *format = vertices->get_array_format();
+  nassertv(format->get_num_columns() == 1);
+  cdata->_index_type = format->get_column(0)->get_numeric_type();
+
   cdata->_modified = Geom::get_next_modified();
   cdata->_got_minmax = false;
 }
@@ -1449,7 +1500,7 @@ is_prepared(PreparedGraphicsObjects *prepared_objects) const {
 //               rendered.
 ////////////////////////////////////////////////////////////////////
 IndexBufferContext *GeomPrimitive::
-prepare_now(PreparedGraphicsObjects *prepared_objects, 
+prepare_now(PreparedGraphicsObjects *prepared_objects,
             GraphicsStateGuardianBase *gsg) {
   nassertr(is_indexed(), NULL);
 
@@ -1519,18 +1570,41 @@ release_all() {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::get_index_format
-//       Access: Public
-//  Description: Returns a registered format appropriate for using to
-//               store the index table.
+//       Access: Public, Static
+//  Description: Returns a registered GeomVertexArrayFormat of the
+//               indicated unsigned integer numeric type for storing
+//               index values.
 ////////////////////////////////////////////////////////////////////
-CPT(GeomVertexArrayFormat) GeomPrimitive::
-get_index_format() const {
-  PT(GeomVertexArrayFormat) format = new GeomVertexArrayFormat;
-  // It's important that the index format *not* respect the global
-  // setting of vertex-column-alignment.  It needs to be tightly
-  // packed, so we specify an explict column_alignment of 1.
-  format->add_column(InternalName::get_index(), 1, get_index_type(), C_index, 0, 1);
-  return GeomVertexArrayFormat::register_format(format);
+const GeomVertexArrayFormat *GeomPrimitive::
+get_index_format(NumericType index_type) {
+  switch (index_type) {
+  case NT_uint8:
+    {
+      static CPT(GeomVertexArrayFormat) cformat = NULL;
+      if (cformat == NULL) {
+        cformat = make_index_format(NT_uint8);
+      }
+      return cformat;
+    }
+  case NT_uint16:
+    {
+      static CPT(GeomVertexArrayFormat) cformat = NULL;
+      if (cformat == NULL) {
+        cformat = make_index_format(NT_uint16);
+      }
+      return cformat;
+    }
+  case NT_uint32:
+    {
+      static CPT(GeomVertexArrayFormat) cformat = NULL;
+      if (cformat == NULL) {
+        cformat = make_index_format(NT_uint32);
+      }
+      return cformat;
+    }
+  }
+
+  return NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1559,28 +1633,57 @@ clear_prepared(PreparedGraphicsObjects *prepared_objects) {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::get_highest_index_value
 //       Access: Private, Static
-//  Description: Returns the largest index value that can be stored in
-//               an index of the indicated type.
+//  Description: Returns the largest index value that can be stored
+//               in an index of the indicated type, minus one (to
+//               leave room for a potential strip cut index)
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitive::
 get_highest_index_value(NumericType index_type) {
+  // Reserve the highest possible index because implementations use
+  // this as a strip-cut index.
   switch (index_type) {
   case NT_uint8:
-    return 0xff;
+    return 0xff - 1;
 
   case NT_uint16:
-    return 0xffff;
+    return 0xffff - 1;
 
   case NT_uint32:
     // We don't actually allow use of the sign bit, since all of our
     // functions receive an "int" instead of an "unsigned int".
-    return 0x7fffffff;
+    return 0x7fffffff - 1;
 
   default:
     return 0;
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomPrimitive::get_strip_cut_index
+//       Access: Private, Static
+//  Description: Returns the index of the indicated type that is
+//               reserved for use as a strip cut index, if enabled
+//               for the primitive.  When the renderer encounters
+//               this index, it will restart the primitive.  This
+//               is guaranteed not to point to an actual vertex.
+////////////////////////////////////////////////////////////////////
+int GeomPrimitive::
+get_strip_cut_index(NumericType index_type) {
+  // Reserve the highest possible index because implementations use
+  // this as a strip-cut index.
+  switch (index_type) {
+  case NT_uint8:
+    return 0xff;
+
+  case NT_uint16:
+    return 0xffff;
+
+  case NT_uint32:
+  default:
+    return -1;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitive::calc_tight_bounds
 //       Access: Public, Virtual
@@ -1594,7 +1697,7 @@ get_highest_index_value(NumericType index_type) {
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
-                  bool &found_any, 
+                  bool &found_any,
                   const GeomVertexData *vertex_data,
                   bool got_mat, const LMatrix4 &mat,
                   const InternalName *column_name,
@@ -1614,7 +1717,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
       for (int i = 0; i < cdata->_num_vertices; i++) {
         reader.set_row_unsafe(cdata->_first_vertex + i);
         LPoint3 vertex = mat.xform_point(reader.get_data3());
-        
+
         if (found_any) {
           min_point.set(min(min_point[0], vertex[0]),
                         min(min_point[1], vertex[1]),
@@ -1632,7 +1735,7 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
       for (int i = 0; i < cdata->_num_vertices; i++) {
         reader.set_row_unsafe(cdata->_first_vertex + i);
         const LVecBase3 &vertex = reader.get_data3();
-        
+
         if (found_any) {
           min_point.set(min(min_point[0], vertex[0]),
                         min(min_point[1], vertex[1]),
@@ -1651,13 +1754,17 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
   } else {
     // Indexed case.
     GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0, current_thread);
+    int strip_cut_index = get_strip_cut_index(cdata->_index_type);
 
     if (got_mat) {
       while (!index.is_at_end()) {
         int ii = index.get_data1i();
+        if (ii == strip_cut_index) {
+          continue;
+        }
         reader.set_row_unsafe(ii);
         LPoint3 vertex = mat.xform_point(reader.get_data3());
-        
+
         if (found_any) {
           min_point.set(min(min_point[0], vertex[0]),
                         min(min_point[1], vertex[1]),
@@ -1674,9 +1781,12 @@ calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
     } else {
       while (!index.is_at_end()) {
         int ii = index.get_data1i();
+        if (ii == strip_cut_index) {
+          continue;
+        }
         reader.set_row_unsafe(ii);
         const LVecBase3 &vertex = reader.get_data3();
-        
+
         if (found_any) {
           min_point.set(min(min_point[0], vertex[0]),
                         min(min_point[1], vertex[1]),
@@ -1798,63 +1908,77 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
       cdata->_max_vertex = 0;
       cdata->_mins.clear();
       cdata->_maxs.clear();
-      
+
     } else if (get_num_vertices_per_primitive() == 0) {
       // This is a complex primitive type like a triangle strip; compute
       // the minmax of each primitive (as well as the overall minmax).
       GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
-      
+
       cdata->_mins = make_index_data();
       cdata->_maxs = make_index_data();
-      
+
       GeomVertexWriter mins(cdata->_mins.get_write_pointer(), 0);
       mins.reserve_num_rows(cdata->_ends.size());
       GeomVertexWriter maxs(cdata->_maxs.get_write_pointer(), 0);
       maxs.reserve_num_rows(cdata->_ends.size());
-      
+
       int pi = 0;
-      
+
       unsigned int vertex = index.get_data1i();
       cdata->_min_vertex = vertex;
       cdata->_max_vertex = vertex;
       unsigned int min_prim = vertex;
       unsigned int max_prim = vertex;
-      
+
+      int num_unused_vertices = get_num_unused_vertices_per_primitive();
+
       for (int vi = 1; vi < num_vertices; ++vi) {
         nassertv(!index.is_at_end());
-        unsigned int vertex = index.get_data1i();
-        cdata->_min_vertex = min(cdata->_min_vertex, vertex);
-        cdata->_max_vertex = max(cdata->_max_vertex, vertex);
-
         nassertv(pi < (int)cdata->_ends.size());
+
+        unsigned int vertex;
+
         if (vi == cdata->_ends[pi]) {
+          // Skip unused vertices, since they won't be very relevant and
+          // may contain a strip-cut index, which would distort the result.
+          if (num_unused_vertices > 0) {
+            vi += num_unused_vertices;
+            index.set_row_unsafe(vi);
+          }
+          vertex = index.get_data1i();
+
           mins.add_data1i(min_prim);
           maxs.add_data1i(max_prim);
           min_prim = vertex;
           max_prim = vertex;
           ++pi;
-          
+
         } else {
+          vertex = index.get_data1i();
           min_prim = min(min_prim, vertex);
           max_prim = max(max_prim, vertex);
         }
+
+        cdata->_min_vertex = min(cdata->_min_vertex, vertex);
+        cdata->_max_vertex = max(cdata->_max_vertex, vertex);
       }
+
       mins.add_data1i(min_prim);
       maxs.add_data1i(max_prim);
       nassertv(mins.get_array_data()->get_num_rows() == (int)cdata->_ends.size());
-      
+
     } else {
       // This is a simple primitive type like a triangle; just compute
       // the overall minmax.
       GeomVertexReader index(cdata->_vertices.get_read_pointer(), 0);
-      
+
       cdata->_mins.clear();
       cdata->_maxs.clear();
-      
+
       unsigned int vertex = index.get_data1i();
       cdata->_min_vertex = vertex;
       cdata->_max_vertex = vertex;
-      
+
       for (int vi = 1; vi < num_vertices; ++vi) {
         nassertv(!index.is_at_end());
         unsigned int vertex = index.get_data1i();
@@ -1863,7 +1987,7 @@ recompute_minmax(GeomPrimitive::CData *cdata) {
       }
     }
   }
-    
+
   cdata->_got_minmax = true;
 }
 
@@ -1900,22 +2024,25 @@ do_make_indexed(CData *cdata) {
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 consider_elevate_index_type(CData *cdata, int vertex) {
+  // Note that we reserve the highest possible index of a particular
+  // index type (ie. -1) because this is commonly used as a strip-cut
+  // (also known as primitive restart) index.
   switch (cdata->_index_type) {
   case NT_uint8:
-    if (vertex > 0xff) {
+    if (vertex >= 0xff) {
       do_set_index_type(cdata, NT_uint16);
     }
     break;
 
   case NT_uint16:
-    if (vertex > 0xffff) {
+    if (vertex >= 0xffff) {
       do_set_index_type(cdata, NT_uint32);
     }
     break;
 
   case NT_uint32:
     // Not much we can do here.
-    nassertv(vertex <= 0x7fffffff);
+    nassertv(vertex < 0x7fffffff);
     break;
 
   default:
@@ -1930,6 +2057,9 @@ consider_elevate_index_type(CData *cdata, int vertex) {
 ////////////////////////////////////////////////////////////////////
 void GeomPrimitive::
 do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
+  int old_strip_cut_index = get_strip_cut_index(cdata->_index_type);
+  int new_strip_cut_index = get_strip_cut_index(index_type);
+
   cdata->_index_type = index_type;
 
   if (gobj_cat.is_debug()) {
@@ -1939,7 +2069,7 @@ do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
 
   if (!cdata->_vertices.is_null()) {
     CPT(GeomVertexArrayFormat) new_format = get_index_format();
-    
+
     CPT(GeomVertexArrayData) array_obj = cdata->_vertices.get_read_pointer();
     if (array_obj->get_array_format() != new_format) {
       PT(GeomVertexArrayData) new_vertices = make_index_data();
@@ -1947,9 +2077,13 @@ do_set_index_type(CData *cdata, GeomPrimitive::NumericType index_type) {
 
       GeomVertexReader from(array_obj, 0);
       GeomVertexWriter to(new_vertices, 0);
-      
+
       while (!from.is_at_end()) {
-        to.set_data1i(from.get_data1i());
+        int index = from.get_data1i();
+        if (index == old_strip_cut_index) {
+          index = new_strip_cut_index;
+        }
+        to.set_data1i(index);
       }
       cdata->_vertices = new_vertices;
       cdata->_got_minmax = false;
@@ -2057,7 +2191,7 @@ int GeomPrimitive::CData::
 complete_pointers(TypedWritable **p_list, BamReader *manager) {
   int pi = CycleData::complete_pointers(p_list, manager);
 
-  _vertices = DCAST(GeomVertexArrayData, p_list[pi++]);    
+  _vertices = DCAST(GeomVertexArrayData, p_list[pi++]);
 
   if (manager->get_file_minor_ver() < 6 && !_vertices.is_null()) {
     // Older bam files might have a meaningless number in
@@ -2107,7 +2241,7 @@ check_minmax() const {
 #ifdef DO_PIPELINING
       unref_delete((CycleData *)_cdata);
 #endif
-      GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler, 
+      GeomPrimitive::CDWriter fresh_cdata(((GeomPrimitive *)_object.p())->_cycler,
                                           false, _current_thread);
       ((GeomPrimitivePipelineReader *)this)->_cdata = fresh_cdata;
 #ifdef DO_PIPELINING
@@ -2133,7 +2267,7 @@ check_minmax() const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitivePipelineReader::get_first_vertex
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitivePipelineReader::
 get_first_vertex() const {
@@ -2171,7 +2305,7 @@ get_vertex(int i) const {
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomPrimitivePipelineReader::get_num_primitives
 //       Access: Public
-//  Description: 
+//  Description:
 ////////////////////////////////////////////////////////////////////
 int GeomPrimitivePipelineReader::
 get_num_primitives() const {

+ 11 - 4
panda/src/gobj/geomPrimitive.h

@@ -118,6 +118,7 @@ PUBLISHED:
   int get_primitive_start(int n) const;
   int get_primitive_end(int n) const;
   int get_primitive_num_vertices(int n) const;
+  int get_num_used_vertices() const;
 
   INLINE int get_num_faces() const;
   INLINE int get_primitive_num_faces(int n) const;
@@ -163,6 +164,7 @@ PUBLISHED:
   void set_nonindexed_vertices(int first_vertex, int num_vertices);
 
   INLINE int get_index_stride() const;
+  INLINE int get_strip_cut_index() const;
 
   INLINE CPTA_int get_ends() const;
   PTA_int modify_ends();
@@ -183,17 +185,21 @@ public:
   void prepare(PreparedGraphicsObjects *prepared_objects);
   bool is_prepared(PreparedGraphicsObjects *prepared_objects) const;
 
-  IndexBufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects, 
+  IndexBufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects,
                                   GraphicsStateGuardianBase *gsg);
   bool release(PreparedGraphicsObjects *prepared_objects);
   int release_all();
 
-  CPT(GeomVertexArrayFormat) get_index_format() const;
+  static const GeomVertexArrayFormat *get_index_format(NumericType index_type);
+  INLINE const GeomVertexArrayFormat *get_index_format() const;
   INLINE PT(GeomVertexArrayData) make_index_data() const;
 
 private:
+  static CPT(GeomVertexArrayFormat) make_index_format(NumericType index_type);
+
   void clear_prepared(PreparedGraphicsObjects *prepared_objects);
   static int get_highest_index_value(NumericType index_type);
+  static int get_strip_cut_index(NumericType index_type);
 
 public:
   virtual bool draw(GraphicsStateGuardianBase *gsg,
@@ -201,7 +207,7 @@ public:
                     bool force) const=0;
 
   void calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point,
-                         bool &found_any, 
+                         bool &found_any,
                          const GeomVertexData *vertex_data,
                          bool got_mat, const LMatrix4 &mat,
                          const InternalName *column_name,
@@ -213,7 +219,7 @@ protected:
   virtual CPT(GeomPrimitive) doubleside_impl() const;
   virtual CPT(GeomPrimitive) reverse_impl() const;
   virtual bool requires_unused_vertices() const;
-  virtual void append_unused_vertices(GeomVertexArrayData *vertices, 
+  virtual void append_unused_vertices(GeomVertexArrayData *vertices,
                                       int vertex);
 
 private:
@@ -358,6 +364,7 @@ public:
   INLINE int get_index_stride() const;
   INLINE const GeomVertexArrayDataHandle *get_vertices_reader() const;
   INLINE const unsigned char *get_read_pointer(bool force) const;
+  INLINE int get_strip_cut_index() const;
   INLINE CPTA_int get_ends() const;
   INLINE CPT(GeomVertexArrayData) get_mins() const;
   INLINE CPT(GeomVertexArrayData) get_maxs() const;

+ 16 - 4
panda/src/gobj/geomTristrips.cxx

@@ -95,6 +95,17 @@ get_geom_rendering() const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomTristrips::get_min_num_vertices_per_primitive
+//       Access: Public, Virtual
+//  Description: Returns the minimum number of vertices that must be
+//               added before close_primitive() may legally be called.
+////////////////////////////////////////////////////////////////////
+int GeomTristrips::
+get_min_num_vertices_per_primitive() const {
+  return 3;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomTristrips::get_num_unused_vertices_per_primitive
 //       Access: Public, Virtual
@@ -142,6 +153,7 @@ decompose_impl() const {
   CPTA_int ends = get_ends();
 
   int num_vertices = get_num_vertices();
+  int num_unused = get_num_unused_vertices_per_primitive();
 
   // We need a slightly different algorithm for SM_flat_first_vertex
   // than for SM_flat_last_vertex, to preserve the key vertex in the
@@ -150,11 +162,11 @@ decompose_impl() const {
   if (get_shade_model() == SM_flat_first_vertex) {
     // Preserve the first vertex of each component triangle as the
     // first vertex of each generated triangle.
-    int vi = -2;
+    int vi = -num_unused;
     int li = 0;
     while (li < (int)ends.size()) {
       // Skip unused vertices between tristrips.
-      vi += 2;
+      vi += num_unused;
       int end = ends[li];
       nassertr(vi + 2 <= end, NULL);
       int v0 = get_vertex(vi);
@@ -192,11 +204,11 @@ decompose_impl() const {
   } else {
     // Preserve the last vertex of each component triangle as the
     // last vertex of each generated triangle.
-    int vi = -2;
+    int vi = -num_unused;
     int li = 0;
     while (li < (int)ends.size()) {
       // Skip unused vertices between tristrips.
-      vi += 2;
+      vi += num_unused;
       int end = ends[li];
       nassertr(vi + 2 <= end, NULL);
       int v0 = get_vertex(vi);

+ 1 - 0
panda/src/gobj/geomTristrips.h

@@ -33,6 +33,7 @@ public:
   virtual PT(GeomPrimitive) make_copy() const;
   virtual PrimitiveType get_primitive_type() const;
   virtual int get_geom_rendering() const;
+  virtual int get_min_num_vertices_per_primitive() const;
   virtual int get_num_unused_vertices_per_primitive() const;
 
 public:

+ 4 - 0
panda/src/gobj/internalName.cxx

@@ -43,6 +43,10 @@ PT(InternalName) InternalName::_view;
 TypeHandle InternalName::_type_handle;
 TypeHandle InternalName::_texcoord_type_handle;
 
+#ifdef HAVE_PYTHON
+InternalName::PyInternTable InternalName::_py_intern_table;
+#endif
+
 ////////////////////////////////////////////////////////////////////
 //     Function: InternalName::Constructor
 //       Access: Private

+ 20 - 4
panda/src/gobj/internalName.h

@@ -43,11 +43,13 @@ class EXPCL_PANDA_GOBJ InternalName : public TypedWritableReferenceCount {
 private:
   InternalName(InternalName *parent, const string &basename);
 
+public:
+  INLINE static PT(InternalName) make(const string &name);
+
 PUBLISHED:
   virtual ~InternalName();
   virtual bool unref() const;
 
-  INLINE static PT(InternalName) make(const string &name);
   static PT(InternalName) make(const string &name, int index);
   PT(InternalName) append(const string &basename);
 
@@ -88,6 +90,22 @@ PUBLISHED:
   INLINE static PT(InternalName) get_model();
   INLINE static PT(InternalName) get_view();
 
+#ifdef HAVE_PYTHON
+#if PY_MAJOR_VERSION >= 3
+  EXTENSION(static PT(InternalName) make(PyUnicodeObject *str));
+#else
+  EXTENSION(static PT(InternalName) make(PyStringObject *str));
+#endif
+#endif
+
+public:
+#ifdef HAVE_PYTHON
+  // It's OK for us to define it here since these are just pointers of
+  // which the reference is maintained indefinitely.
+  typedef phash_map<PyObject *, InternalName *, pointer_hash> PyInternTable;
+  static PyInternTable _py_intern_table;
+#endif
+
 private:
   PT(InternalName) _parent;
   string _basename;
@@ -116,7 +134,7 @@ private:
   static PT(InternalName) _camera;
   static PT(InternalName) _model;
   static PT(InternalName) _view;
-  
+
 public:
   // Datagram stuff
   static void register_with_read_factory();
@@ -157,5 +175,3 @@ INLINE ostream &operator << (ostream &out, const InternalName &tcn);
 
 #endif
 
-
-  

+ 87 - 0
panda/src/gobj/internalName_ext.cxx

@@ -0,0 +1,87 @@
+// Filename: internalName_ext.I
+// Created by:  rdb (28Sep14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "internalName_ext.h"
+
+#ifdef HAVE_PYTHON
+
+////////////////////////////////////////////////////////////////////
+//     Function: InternalName::make
+//       Access: Published, Static
+//  Description: This extension method serves to allow coercion of
+//               Python interned strings to InternalName objects
+//               more efficiently by storing a mapping between
+//               Python and Panda interned strings.
+////////////////////////////////////////////////////////////////////
+#if PY_MAJOR_VERSION >= 3
+PT(InternalName) Extension<InternalName>::
+make(PyUnicodeObject *str) {
+  if (!PyUnicode_CHECK_INTERNED(str)) {
+    // Not an interned string; don't bother.
+    Py_ssize_t len = 0;
+    char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
+    if (c_str == NULL) {
+      return NULL;
+    }
+
+    string name(c_str, len);
+    return InternalName::make(c_str, len);
+  }
+
+  InternalName::PyInternTable::const_iterator it;
+  it = InternalName::_py_intern_table.find((PyObject*)str);
+
+  if (it != InternalName::_py_intern_table.end()) {
+    return (*it).second;
+
+  } else {
+    Py_ssize_t len = 0;
+    char *c_str = PyUnicode_AsUTF8AndSize((PyObject *)str, &len);
+    string name(c_str, len);
+
+#else
+PT(InternalName) Extension<InternalName>::
+make(PyStringObject *str) {
+  if (!PyString_CHECK_INTERNED(str)) {
+    // Not an interned string; don't bother.
+    string name(PyString_AS_STRING(str), PyString_GET_SIZE(str));
+    return InternalName::make(name);
+  }
+
+  InternalName::PyInternTable::const_iterator it;
+  it = InternalName::_py_intern_table.find((PyObject*)str);
+
+  if (it != InternalName::_py_intern_table.end()) {
+    return (*it).second;
+
+  } else {
+    string name(PyString_AS_STRING(str), PyString_GET_SIZE(str));
+
+#endif  // PY_MAJOR_VERSION
+
+    PT(InternalName) iname = InternalName::make(name);
+
+    // We basically leak references to both the PyObject and the
+    // InternalName.  We may want to change that in the future if it
+    // becomes a problem.
+    Py_INCREF(str);
+    iname->ref();
+
+    InternalName::_py_intern_table.insert(make_pair((PyObject *)str, iname.p()));
+    return iname.p();
+  }
+
+}
+
+#endif  // HAVE_PYTHON

+ 44 - 0
panda/src/gobj/internalName_ext.h

@@ -0,0 +1,44 @@
+// Filename: internalName_ext.h
+// Created by:  rdb (28Sep14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef INTERNALNAME_EXT_H
+#define INTERNALNAME_EXT_H
+
+#include "dtoolbase.h"
+
+#ifdef HAVE_PYTHON
+
+#include "extension.h"
+#include "internalName.h"
+#include "py_panda.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : Extension<InternalName>
+// Description : This class defines the extension methods for
+//               InternalName, which are called instead of
+//               any C++ methods with the same prototype.
+////////////////////////////////////////////////////////////////////
+template<>
+class Extension<InternalName> : public ExtensionBase<InternalName> {
+public:
+#if PY_MAJOR_VERSION >= 3
+  static PT(InternalName) make(PyUnicodeObject *str);
+#else
+  static PT(InternalName) make(PyStringObject *str);
+#endif
+};
+
+#endif  // HAVE_PYTHON
+
+#endif  // INTERNALNAME_EXT_H

+ 3 - 0
panda/src/gobj/p3gobj_composite1.cxx

@@ -30,3 +30,6 @@
 #include "indexBufferContext.cxx"
 #include "internalName.cxx"
 #include "lens.cxx"
+#include "material.cxx"
+#include "materialPool.cxx"
+#include "matrixLens.cxx"

+ 1 - 3
panda/src/gobj/p3gobj_composite2.cxx

@@ -1,6 +1,3 @@
-#include "material.cxx"
-#include "materialPool.cxx"
-#include "matrixLens.cxx"
 #include "occlusionQueryContext.cxx"
 #include "orthographicLens.cxx"
 #include "perspectiveLens.cxx"
@@ -21,6 +18,7 @@
 #include "textureReloadRequest.cxx"
 #include "textureStage.cxx"
 #include "textureStagePool.cxx"
+#include "timerQueryContext.cxx"
 #include "transformBlend.cxx"
 #include "transformBlendTable.cxx"
 #include "transformTable.cxx"

+ 6 - 6
panda/src/gobj/shader.I

@@ -12,16 +12,16 @@
 //
 ////////////////////////////////////////////////////////////////////
 
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Shader::get_filename
-//       Access: Public
+//       Access: Published
 //  Description: Return the Shader's filename for the given shader
 //               type.
 ////////////////////////////////////////////////////////////////////
-INLINE const Filename Shader::
+INLINE Filename Shader::
 get_filename(const ShaderType &type) const {
-  if (_filename->_separate) {
-    nassertr(type != ST_none || !_filename->_shared.empty(), "");
+  if (_filename->_separate && type == ST_none) {
     switch (type) {
       case ST_vertex:
         return _filename->_vertex;
@@ -51,7 +51,7 @@ get_filename(const ShaderType &type) const {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Shader::get_text
-//       Access: Public
+//       Access: Published
 //  Description: Return the Shader's text for the given shader type.
 ////////////////////////////////////////////////////////////////////
 INLINE const string &Shader::
@@ -146,7 +146,7 @@ have_shader_utilization() {
 //  Description: Returns the shader language in which this shader
 //               was written.
 ////////////////////////////////////////////////////////////////////
-INLINE const Shader::ShaderLanguage Shader::
+INLINE Shader::ShaderLanguage Shader::
 get_language() const {
   return _language;
 }

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

@@ -96,10 +96,10 @@ PUBLISHED:
                          const string &tess_evaluation = "");
   static PT(Shader) make_compute(const ShaderLanguage &lang, const string &body);
 
-  INLINE const Filename get_filename(const ShaderType &type = ST_none) const;
+  INLINE Filename get_filename(const ShaderType &type = ST_none) const;
   INLINE const string &get_text(const ShaderType &type = ST_none) const;
   INLINE const bool get_error_flag() const;
-  INLINE const ShaderLanguage get_language() const;
+  INLINE ShaderLanguage get_language() const;
 
   INLINE static ShaderUtilization get_shader_utilization();
   INLINE static void set_shader_utilization(ShaderUtilization utl);

+ 26 - 0
panda/src/gobj/timerQueryContext.I

@@ -0,0 +1,26 @@
+// Filename: occlusionQueryContext.I
+// Created by:  rdb (22Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: TimerQueryContext::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE TimerQueryContext::
+TimerQueryContext(int pstats_index) :
+  _pstats_index(pstats_index),
+  _frame_index(ClockObject::get_global_clock()->get_frame_count())
+{
+}

+ 35 - 0
panda/src/gobj/timerQueryContext.cxx

@@ -0,0 +1,35 @@
+// Filename: timerQueryContext.cxx
+// Created by:  rdb (22Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#include "timerQueryContext.h"
+
+TypeHandle TimerQueryContext::_type_handle;
+
+////////////////////////////////////////////////////////////////////
+//     Function: TimerQueryContext::get_timestamp
+//       Access: Public, Virtual
+//  Description: Returns the timestamp that is the result of this
+//               timer query.  There's no guarantee about which
+//               clock this uses, the only guarantee is that
+//               subtracting a start time from an end time should
+//               yield a time in seconds.
+//               If is_answer_ready() did not return true, this
+//               function may block before it returns.
+//
+//               It is only valid to call this from the draw thread.
+////////////////////////////////////////////////////////////////////
+double TimerQueryContext::
+get_timestamp() const {
+  return 0.0;
+}

+ 60 - 0
panda/src/gobj/timerQueryContext.h

@@ -0,0 +1,60 @@
+// Filename: timerQueryContext.h
+// Created by:  rdb (22Aug14)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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."
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef TIMERQUERYCONTEXT_H
+#define TIMERQUERYCONTEXT_H
+
+#include "pandabase.h"
+#include "queryContext.h"
+#include "clockObject.h"
+#include "pStatCollector.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : TimerQueryContext
+// Description :
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA_GOBJ TimerQueryContext : public QueryContext {
+public:
+  INLINE TimerQueryContext(int pstats_index);
+
+  ALLOC_DELETED_CHAIN(TimerQueryContext);
+
+  virtual double get_timestamp() const=0;
+
+  int _frame_index;
+  int _pstats_index;
+
+public:
+  static TypeHandle get_class_type() {
+    return _type_handle;
+  }
+  static void init_type() {
+    QueryContext::init_type();
+    register_type(_type_handle, "TimerQueryContext",
+                  QueryContext::get_class_type());
+  }
+  virtual TypeHandle get_type() const {
+    return get_class_type();
+  }
+  virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
+
+private:
+  static TypeHandle _type_handle;
+
+  friend class PreparedGraphicsObjects;
+};
+
+#include "timerQueryContext.I"
+
+#endif

+ 3 - 6
panda/src/gsgbase/graphicsStateGuardianBase.h

@@ -32,7 +32,6 @@ class GraphicsOutputBase;
 
 class VertexBufferContext;
 class IndexBufferContext;
-class OcclusionQueryContext;
 class GeomContext;
 class GeomNode;
 class Geom;
@@ -123,9 +122,10 @@ PUBLISHED:
 
   virtual bool get_supports_multisample() const=0;
   virtual int get_supported_geom_rendering() const=0;
-  virtual bool get_supports_occlusion_query() const=0;
   virtual bool get_supports_shadow_filter() const=0;
 
+  virtual bool get_supports_texture_srgb() const=0;
+
 public:
   // These are some general interface functions; they're defined here
   // mainly to make it easy to call these from code in some directory
@@ -153,16 +153,13 @@ public:
 
   virtual ShaderContext *prepare_shader(Shader *shader)=0;
   virtual void release_shader(ShaderContext *sc)=0;
-  
+
   virtual VertexBufferContext *prepare_vertex_buffer(GeomVertexArrayData *data)=0;
   virtual void release_vertex_buffer(VertexBufferContext *vbc)=0;
 
   virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0;
   virtual void release_index_buffer(IndexBufferContext *ibc)=0;
 
-  virtual void begin_occlusion_query()=0;
-  virtual PT(OcclusionQueryContext) end_occlusion_query()=0;
-
   virtual void dispatch_compute(int size_x, int size_y, int size_z)=0;
 
   virtual PT(GeomMunger) get_geom_munger(const RenderState *state,

+ 7 - 2
panda/src/net/connectionManager.cxx

@@ -26,6 +26,8 @@
 #if defined(WIN32_VC) || defined(WIN64_VC)
 #include <winsock2.h>  // For gethostname()
 #include <Iphlpapi.h> // For GetAdaptersAddresses()
+#elif defined(ANDROID)
+#include <net/if.h>
 #else
 #include <net/if.h>
 #include <ifaddrs.h>
@@ -542,14 +544,14 @@ scan_interfaces() {
         TextEncoder encoder;
         encoder.set_wtext(wstring(p->FriendlyName));
         string friendly_name = encoder.get_text();
-        
+
         Interface iface;
         iface.set_name(friendly_name);
 
         if (p->PhysicalAddressLength > 0) {
           iface.set_mac_address(format_mac_address((const unsigned char *)p->PhysicalAddress, p->PhysicalAddressLength));
         }
-        
+
         if (p->OperStatus == IfOperStatusUp) {
           // Prefixes are a linked list, in the order Network IP,
           // Adapter IP, Broadcast IP (plus more).
@@ -586,6 +588,9 @@ scan_interfaces() {
     PANDA_FREE_ARRAY(addresses);
   }
 
+#elif defined(ANDROID)
+  // TODO: implementation using netlink_socket?
+
 #else  // WIN32_VC
   struct ifaddrs *ifa;
   if (getifaddrs(&ifa) != 0) {

+ 39 - 37
panda/src/pgraph/cullableObject.cxx

@@ -78,11 +78,11 @@ munge_geom(GraphicsStateGuardianBase *gsg,
         nassertr(geom_reader.check_valid(&data_reader), false);
       }
 #endif  // _DEBUG
-      
+
       geom_rendering = geom_reader.get_geom_rendering();
       geom_rendering = _state->get_geom_rendering(geom_rendering);
       geom_rendering = _modelview_transform->get_geom_rendering(geom_rendering);
-      
+
       if (geom_rendering & Geom::GR_point_bits) {
         if (geom_reader.get_primitive_type() != Geom::PT_points) {
           if (singular_points) {
@@ -92,7 +92,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
         }
       }
     }
-    
+
     GraphicsStateGuardianBase *gsg = traverser->get_gsg();
     int gsg_bits = gsg->get_supported_geom_rendering();
     if (!hardware_point_sprites) {
@@ -115,7 +115,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
       // _munged_data, and might also replace _state.
       if (pgraph_cat.is_spam()) {
         pgraph_cat.spam()
-          << "munge_points_to_quads() for geometry with bits: " 
+          << "munge_points_to_quads() for geometry with bits: "
           << hex << geom_rendering << ", unsupported: "
           << (unsupported_bits & Geom::GR_point_bits) << dec << "\n";
       }
@@ -130,7 +130,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
       // If we have to compute the light vector, we have to animate
       // the vertices in the CPU--and we have to do it before we call
       // munge_geom(), which might lose the tangent and binormal.
-      CPT(GeomVertexData) animated_vertices = 
+      CPT(GeomVertexData) animated_vertices =
         _munged_data->animate_vertices(force, current_thread);
       if (animated_vertices != _munged_data) {
         cpu_animated = true;
@@ -156,7 +156,7 @@ munge_geom(GraphicsStateGuardianBase *gsg,
       // has been munged--that is, we couldn't arrange to handle the
       // animation in hardware--then we have to calculate that
       // animation now.
-      CPT(GeomVertexData) animated_vertices = 
+      CPT(GeomVertexData) animated_vertices =
         _munged_data->animate_vertices(force, current_thread);
       if (animated_vertices != _munged_data) {
         cpu_animated = true;
@@ -240,7 +240,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
 
   // Better get the animated vertices, in case we're showing sprites
   // on an animated model for some reason.
-  CPT(GeomVertexData) source_data = 
+  CPT(GeomVertexData) source_data =
     _munged_data->animate_vertices(force, current_thread);
 
   if (!force && !source_data->request_resident()) {
@@ -308,22 +308,22 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
     FormatMap::iterator fmi = _format_map.find(sformat);
     if (fmi != _format_map.end()) {
       new_format = (*fmi).second;
-      
+
     } else {
       // We have to construct the format now.
       PT(GeomVertexArrayFormat) new_array_format;
       if (sformat._retransform_sprites) {
         // With retransform_sprites in effect, we will be sending ordinary
         // 3-D points to the graphics API.
-        new_array_format = 
-          new GeomVertexArrayFormat(InternalName::get_vertex(), 3, 
+        new_array_format =
+          new GeomVertexArrayFormat(InternalName::get_vertex(), 3,
                                     Geom::NT_stdfloat,
                                     Geom::C_point);
       } else {
         // Without retransform_sprites, we will be sending 4-component
         // clip-space points.
-        new_array_format = 
-          new GeomVertexArrayFormat(InternalName::get_vertex(), 4, 
+        new_array_format =
+          new GeomVertexArrayFormat(InternalName::get_vertex(), 4,
                                     Geom::NT_stdfloat,
                                     Geom::C_clip_point);
       }
@@ -344,14 +344,14 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
           (InternalName::get_texcoord(), 2,
            Geom::NT_stdfloat,
            Geom::C_texcoord);
-        
+
       } else if (has_texcoord) {
         const GeomVertexColumn *c = texcoord.get_column();
         new_array_format->add_column
           (InternalName::get_texcoord(), c->get_num_components(),
            c->get_numeric_type(), c->get_contents());
       }
-      
+
       new_format = GeomVertexFormat::register_format(new_array_format);
       _format_map[sformat] = new_format;
     }
@@ -408,7 +408,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
       LPoint3 eye = modelview.xform_point(vertex.get_data3());
       points[vi]._eye = eye;
       points[vi]._dist = gsg->compute_distance_to(points[vi]._eye);
-    
+
       // The point in clip coordinates.
       LPoint4 p4 = LPoint4(eye[0], eye[1], eye[2], 1.0f) * projection;
 
@@ -433,7 +433,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
           scale_y /= gsg->compute_distance_to(eye);
         }
       }
-      
+
       // Also factor in the homogeneous scale for being in clip
       // coordinates still.
       scale_y *= p4[3];
@@ -447,7 +447,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
       LPoint2 c0(scale_x, scale_y);
       LPoint2 c1(-scale_x, scale_y);
 
-      if (has_rotate) { 
+      if (has_rotate) {
         // If we have a rotate factor, apply it to those two corners.
         PN_stdfloat r = rotate.get_data1f();
         LMatrix3 mat = LMatrix3::rotate_mat(r);
@@ -461,7 +461,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
       PN_stdfloat ry = 1.0f / viewport_height;
       c0.set(c0[0] * rx, c0[1] * ry);
       c1.set(c1[0] * rx, c1[1] * ry);
-    
+
       if (retransform_sprites) {
         // With retransform_sprites in effect, we must reconvert the
         // resulting quad back into the original 3-D space.
@@ -469,7 +469,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
         new_vertex.set_data4(inv_render_transform.xform(LPoint4(p4[0] + c1[0], p4[1] + c1[1], p4[2], p4[3])));
         new_vertex.set_data4(inv_render_transform.xform(LPoint4(p4[0] - c1[0], p4[1] - c1[1], p4[2], p4[3])));
         new_vertex.set_data4(inv_render_transform.xform(LPoint4(p4[0] - c0[0], p4[1] - c0[1], p4[2], p4[3])));
-      
+
         if (has_normal) {
           const LNormal &c = normal.get_data3();
           new_normal.set_data3(c);
@@ -477,7 +477,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
           new_normal.set_data3(c);
           new_normal.set_data3(c);
         }
-      
+
       } else {
         // Without retransform_sprites, we can simply load the
         // clip-space coordinates.
@@ -485,7 +485,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
         new_vertex.set_data4(p4[0] + c1[0], p4[1] + c1[1], p4[2], p4[3]);
         new_vertex.set_data4(p4[0] - c1[0], p4[1] - c1[1], p4[2], p4[3]);
         new_vertex.set_data4(p4[0] - c0[0], p4[1] - c0[1], p4[2], p4[3]);
-      
+
         if (has_normal) {
           LNormal c = render_transform.xform_vec(normal.get_data3());
           new_normal.set_data3(c);
@@ -521,18 +521,20 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
     nassertr(new_data->get_num_rows() == new_verts, false);
   }
 
-  PT(Geom) new_geom = new Geom(new_data);
-    
-  // Create an appropriate GeomVertexArrayFormat for the primitive
-  // index.
-  static CPT(GeomVertexArrayFormat) new_prim_format;
-  if (new_prim_format == (GeomVertexArrayFormat *)NULL) {
-    new_prim_format =
-      GeomVertexArrayFormat::register_format
-      (new GeomVertexArrayFormat(InternalName::get_index(), 1, 
-                                 GeomEnums::NT_uint16, GeomEnums::C_index));
+  // Determine the format we should use to store the indices.
+  const GeomVertexArrayFormat *new_prim_format = NULL;
+  if (new_verts < 0xff) {
+    new_prim_format = GeomPrimitive::get_index_format(GeomEnums::NT_uint8);
+
+  } else if (new_verts < 0xffff) {
+    new_prim_format = GeomPrimitive::get_index_format(GeomEnums::NT_uint16);
+
+  } else {
+    new_prim_format = GeomPrimitive::get_index_format(GeomEnums::NT_uint32);
   }
 
+  PT(Geom) new_geom = new Geom(new_data);
+
   // Replace each primitive in the Geom (it's presumably a GeomPoints
   // primitive, although it might be some other kind of primitive if
   // we got here because RenderModeAttrib::M_point is enabled) with a
@@ -575,11 +577,11 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
             vertices[i] = v;
           }
         }
-  
+
         // Now sort the points in order from back-to-front so they will
         // render properly with transparency, at least with each other.
         sort(vertices, vertices_end, SortPoints(points));
-  
+
         // Go through the points, now in sorted order, and generate a pair
         // of triangles for each one.  We generate indexed triangles
         // instead of two-triangle strips, since this seems to be
@@ -589,7 +591,7 @@ munge_points_to_quads(const CullTraverser *traverser, bool force) {
         PT(GeomPrimitive) new_primitive = new GeomTriangles(Geom::UH_stream);
         int new_prim_verts = 6 * num_vertices;  // two triangles per point.
 
-        PT(GeomVertexArrayData) new_index 
+        PT(GeomVertexArrayData) new_index
           = new GeomVertexArrayData(new_prim_format, GeomEnums::UH_stream);
         new_index->unclean_set_num_rows(new_prim_verts);
 
@@ -669,14 +671,14 @@ munge_texcoord_light_vector(const CullTraverser *traverser, bool force) {
       string source_name = tex_gen->get_source_name(stage);
       Light *light_obj = light.node()->as_light();
       nassertr(light_obj != (Light *)NULL, false);
-      
+
       // Determine the names of the tangent and binormal columns
       // associated with the stage's texcoord name.
       PT(InternalName) tangent_name = InternalName::get_tangent_name(source_name);
       PT(InternalName) binormal_name = InternalName::get_binormal_name(source_name);
-      
+
       PT(InternalName) texcoord_name = stage->get_texcoord_name();
-      
+
       if (_munged_data->has_column(tangent_name) &&
           _munged_data->has_column(binormal_name)) {
 

+ 1 - 1
panda/src/pgraph/lightRampAttrib.cxx

@@ -96,7 +96,7 @@ make_single_threshold(PN_stdfloat thresh0, PN_stdfloat val0) {
 CPT(RenderAttrib) LightRampAttrib::
 make_double_threshold(PN_stdfloat thresh0, PN_stdfloat val0, PN_stdfloat thresh1, PN_stdfloat val1) {
   LightRampAttrib *attrib = new LightRampAttrib();
-  attrib->_mode = LRT_single_threshold;
+  attrib->_mode = LRT_double_threshold;
   attrib->_threshold[0] = thresh0;
   attrib->_level[0] = val0;
   attrib->_threshold[1] = thresh1;

+ 1 - 1
panda/src/pgraph/transformState.cxx

@@ -2322,7 +2322,7 @@ do_calc_components() {
     _hpr.set(0.0f, 0.0f, 0.0f);
     _quat = LQuaternion::ident_quat();
     _pos.set(0.0f, 0.0f, 0.0f);
-    _flags |= F_has_components | F_components_known | F_hpr_known | F_quat_known | F_uniform_scale;
+    _flags |= F_has_components | F_components_known | F_hpr_known | F_quat_known | F_uniform_scale | F_identity_scale;
 
   } else {
     // If we don't have components and we're not identity, the only

+ 3 - 4
panda/src/pgraphnodes/shaderGenerator.cxx

@@ -1180,12 +1180,11 @@ synthesize_shader(const RenderState *rs) {
           PN_stdfloat t1 = light_ramp->get_threshold(1);
           PN_stdfloat l0 = light_ramp->get_level(0);
           PN_stdfloat l1 = light_ramp->get_level(1);
-          PN_stdfloat l2 = light_ramp->get_level(2);
           text << "\t // Double-threshold light ramp\n";
           text << "\t float lr_in = dot(tot_diffuse.rgb, float3(0.33,0.34,0.33));\n";
-          text << "\t float lr_out = " << l0 << "\n";
-          text << "\t if (lr_in > " << t0 << ") lr_out=" << l1 << ";\n";
-          text << "\t if (lr_in > " << t1 << ") lr_out=" << l2 << ";\n";
+          text << "\t float lr_out = 0.0;\n";
+          text << "\t if (lr_in > " << t0 << ") lr_out=" << l0 << ";\n";
+          text << "\t if (lr_in > " << t1 << ") lr_out=" << l1 << ";\n";
           text << "\t tot_diffuse = tot_diffuse * (lr_out / lr_in);\n";
           break;
         }

+ 11 - 0
panda/src/pstatclient/config_pstats.cxx

@@ -68,6 +68,17 @@ ConfigVariableDouble pstats_target_frame_rate
           "This frame rate is marked with a different-colored line; "
           "otherwise, this setting has no effect."));
 
+ConfigVariableBool pstats_gpu_timing
+("pstats-gpu-timing", false,
+ PRC_DESC("Set this true to query the graphics library for the actual time "
+          "that graphics operations take to execute on the video card.  "
+          "Enabling this will harm performance, but this information can "
+          "be more useful than the regular Draw information in tracking "
+          "down bottlenecks, because the CPU-based Draw collectors only "
+          "measure how long it takes for the API call to complete, which "
+          "is not usually an accurate reflectino of how long the actual "
+          "operation takes on the video card."));
+
 // The rest are different in that they directly control the server,
 // not the client.
 ConfigVariableBool pstats_scroll_mode

+ 1 - 0
panda/src/pstatclient/config_pstats.h

@@ -38,6 +38,7 @@ extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_tcp_ratio;
 extern EXPCL_PANDA_PSTATCLIENT ConfigVariableString pstats_host;
 extern EXPCL_PANDA_PSTATCLIENT ConfigVariableInt pstats_port;
 extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_target_frame_rate;
+extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_gpu_timing;
 
 extern EXPCL_PANDA_PSTATCLIENT ConfigVariableBool pstats_scroll_mode;
 extern EXPCL_PANDA_PSTATCLIENT ConfigVariableDouble pstats_history;

+ 72 - 37
panda/src/pstatclient/pStatClient.cxx

@@ -152,7 +152,7 @@ get_collector_fullname(int index) const {
   if (parent_index == 0) {
     return collector->get_name();
   } else {
-    return get_collector_fullname(parent_index) + ":" + 
+    return get_collector_fullname(parent_index) + ":" +
       collector->get_name();
   }
 }
@@ -180,7 +180,6 @@ get_main_thread() const {
   return PStatThread((PStatClient *)this, 0);
 }
 
-
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::get_current_thread
 //       Access: Published
@@ -191,7 +190,7 @@ get_main_thread() const {
 PStatThread PStatClient::
 get_current_thread() const {
   if (!client_is_connected()) {
-    // No need to make the relatively expensive call to 
+    // No need to make the relatively expensive call to
     // Thread::get_current_thread() if we're not even connected.
     return get_main_thread();
   }
@@ -222,10 +221,10 @@ main_tick() {
 
 
     _mmap_size_pcollector.set_level(MemoryUsage::get_panda_mmap_size());
-    
+
     TypeRegistry *type_reg = TypeRegistry::ptr();
     int num_typehandles = type_reg->get_num_typehandles();
-    
+
     while ((int)type_handle_cols.size() < num_typehandles) {
       type_handle_cols.push_back(TypeHandleCollector());
     }
@@ -261,7 +260,7 @@ main_tick() {
         case TypeHandle::MC_limit:
           // Not used.
           break;
-        }          
+        }
       }
     }
     size_t min_usage = (single_total_usage + array_total_usage + dc_active_total_usage + dc_inactive_total_usage) / 1024;
@@ -288,7 +287,7 @@ main_tick() {
             case TypeHandle::MC_singleton:
               category = "Heap:Single";
               break;
-              
+
             case TypeHandle::MC_array:
               category = "Heap:Array";
               break;
@@ -315,11 +314,11 @@ main_tick() {
           case TypeHandle::MC_singleton:
             single_other_usage -= usage;
             break;
-            
+
           case TypeHandle::MC_array:
             array_other_usage -= usage;
             break;
-            
+
           case TypeHandle::MC_deleted_chain_active:
             dc_active_other_usage -= usage;
             break;
@@ -348,7 +347,7 @@ main_tick() {
 #endif  // DO_MEMORY_USAGE
 
   get_global_pstats()->client_main_tick();
-}  
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::thread_tick
@@ -359,7 +358,7 @@ main_tick() {
 void PStatClient::
 thread_tick(const string &sync_name) {
   get_global_pstats()->client_thread_tick(sync_name);
-}  
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: PStatClient::client_main_tick
@@ -383,8 +382,8 @@ client_main_tick() {
       _threads_by_sync_name.find("Main");
     if (ni != _threads_by_sync_name.end()) {
       const vector_int &indices = (*ni).second;
-      for (vector_int::const_iterator vi = indices.begin(); 
-           vi != indices.end(); 
+      for (vector_int::const_iterator vi = indices.begin();
+           vi != indices.end();
            ++vi) {
         _impl->new_frame(*vi);
       }
@@ -407,8 +406,8 @@ client_thread_tick(const string &sync_name) {
       _threads_by_sync_name.find(sync_name);
     if (ni != _threads_by_sync_name.end()) {
       const vector_int &indices = (*ni).second;
-      for (vector_int::const_iterator vi = indices.begin(); 
-           vi != indices.end(); 
+      for (vector_int::const_iterator vi = indices.begin();
+           vi != indices.end();
            ++vi) {
         _impl->new_frame(*vi);
       }
@@ -463,7 +462,7 @@ PStatClient *PStatClient::
 get_global_pstats() {
   if (_global_pstats == (PStatClient *)NULL) {
     _global_pstats = new PStatClient;
-    
+
     ClockObject::_start_clock_wait = start_clock_wait;
     ClockObject::_start_clock_busy_wait = start_clock_busy_wait;
     ClockObject::_stop_clock_wait = stop_clock_wait;
@@ -616,8 +615,8 @@ do_make_thread(Thread *thread) {
     // We have seen a thread with this name before.  Can we re-use any
     // of them?
     const vector_int &indices = (*ni).second;
-    for (vector_int::const_iterator vi = indices.begin(); 
-         vi != indices.end(); 
+    for (vector_int::const_iterator vi = indices.begin();
+         vi != indices.end();
          ++vi) {
       int index = (*vi);
       nassertr(index >= 0 && index < _num_threads, PStatThread());
@@ -637,20 +636,26 @@ do_make_thread(Thread *thread) {
   int new_index = _num_threads;
   thread->set_pstats_index(new_index);
   thread->set_pstats_callback(this);
-  _threads_by_name[thread->get_name()].push_back(new_index);
-  _threads_by_sync_name[thread->get_sync_name()].push_back(new_index);
-        
+
   InternalThread *pthread = new InternalThread(thread);
   add_thread(pthread);
 
-  // We need an additional PerThreadData for this thread in all of the
-  // collectors.
-  CollectorPointer *collectors = (CollectorPointer *)_collectors;
-  for (int ci = 0; ci < _num_collectors; ++ci) {
-    Collector *collector = collectors[ci];
-    collector->_per_thread.push_back(PerThreadData());
-    nassertr((int)collector->_per_thread.size() == _num_threads, PStatThread());
-  }
+  return PStatThread(this, new_index);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::make_gpu_thread
+//       Access: Private
+//  Description: Returns a PStatThread representing the GPU.
+//               This is normally called by the GSG only.
+////////////////////////////////////////////////////////////////////
+PStatThread PStatClient::
+make_gpu_thread(const string &name) {
+  ReMutexHolder holder(_lock);
+  int new_index = _num_threads;
+
+  InternalThread *pthread = new InternalThread(name, "GPU");
+  add_thread(pthread);
 
   return PStatThread(this, new_index);
 }
@@ -960,7 +965,7 @@ get_level(int collector_index, int thread_index) const {
 //  Description: This function is added as a hook into ClockObject, so
 //               that we may time the delay for
 //               ClockObject::wait_until(), used for certain special
-//               clock modes.  
+//               clock modes.
 //
 //               This callback is a hack around the fact that we can't
 //               let the ClockObject directly create a PStatCollector,
@@ -977,7 +982,7 @@ start_clock_wait() {
 //  Description: This function is added as a hook into ClockObject, so
 //               that we may time the delay for
 //               ClockObject::wait_until(), used for certain special
-//               clock modes.  
+//               clock modes.
 //
 //               This callback is a hack around the fact that we can't
 //               let the ClockObject directly create a PStatCollector,
@@ -995,7 +1000,7 @@ start_clock_busy_wait() {
 //  Description: This function is added as a hook into ClockObject, so
 //               that we may time the delay for
 //               ClockObject::wait_until(), used for certain special
-//               clock modes.  
+//               clock modes.
 //
 //               This callback is a hack around the fact that we can't
 //               let the ClockObject directly create a PStatCollector,
@@ -1051,6 +1056,9 @@ add_collector(PStatClient::Collector *collector) {
 ////////////////////////////////////////////////////////////////////
 void PStatClient::
 add_thread(PStatClient::InternalThread *thread) {
+  _threads_by_name[thread->_name].push_back(_num_threads);
+  _threads_by_sync_name[thread->_sync_name].push_back(_num_threads);
+
   if (_num_threads >= _threads_size) {
     // We need to grow the array.  We have to be careful here, because
     // there might be clients accessing the array right now who are
@@ -1071,12 +1079,21 @@ add_thread(PStatClient::InternalThread *thread) {
     // and then no more.)
 
     new_threads[_num_threads] = thread;
-    AtomicAdjust::inc(_num_threads);
 
   } else {
     ThreadPointer *threads = (ThreadPointer *)_threads;
     threads[_num_threads] = thread;
-    AtomicAdjust::inc(_num_threads);
+  }
+
+  AtomicAdjust::inc(_num_threads);
+
+  // We need an additional PerThreadData for this thread in all of the
+  // collectors.
+  CollectorPointer *collectors = (CollectorPointer *)_collectors;
+  for (int ci = 0; ci < _num_collectors; ++ci) {
+    Collector *collector = collectors[ci];
+    collector->_per_thread.push_back(PerThreadData());
+    nassertv((int)collector->_per_thread.size() == _num_threads);
   }
 }
 
@@ -1148,7 +1165,7 @@ make_def(const PStatClient *client, int this_index) {
   if (_def == (PStatCollectorDef *)NULL) {
     _def = new PStatCollectorDef(this_index, _name);
     if (_parent_index != this_index) {
-      const PStatCollectorDef *parent_def = 
+      const PStatCollectorDef *parent_def =
         client->get_collector_def(_parent_index);
       _def->set_parent(*parent_def);
     }
@@ -1157,9 +1174,9 @@ make_def(const PStatClient *client, int this_index) {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: PStatClient::Collector::make_def
+//     Function: PStatClient::InternalThread::Constructor
 //       Access: Private
-//  Description: Creates the new PStatCollectorDef for this collector.
+//  Description:
 ////////////////////////////////////////////////////////////////////
 PStatClient::InternalThread::
 InternalThread(Thread *thread) :
@@ -1174,4 +1191,22 @@ InternalThread(Thread *thread) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: PStatClient::InternalThread::Constructor
+//       Access: Private
+//  Description:
+////////////////////////////////////////////////////////////////////
+PStatClient::InternalThread::
+InternalThread(const string &name, const string &sync_name) :
+  _thread(NULL),
+  _name(name),
+  _sync_name(sync_name),
+  _is_active(false),
+  _frame_number(0),
+  _next_packet(0.0),
+  _thread_active(true),
+  _thread_lock(string("PStatClient::InternalThread ") + name)
+{
+}
+
 #endif // DO_PSTATS

+ 5 - 2
panda/src/pstatclient/pStatClient.h

@@ -35,6 +35,7 @@
 class PStatCollector;
 class PStatCollectorDef;
 class PStatThread;
+class GraphicsStateGuardian;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : PStatClient
@@ -113,6 +114,7 @@ private:
   PStatThread do_get_current_thread() const;
   PStatThread make_thread(Thread *thread);
   PStatThread do_make_thread(Thread *thread);
+  PStatThread make_gpu_thread(const string &name);
 
   bool is_active(int collector_index, int thread_index) const;
   bool is_started(int collector_index, int thread_index) const;
@@ -171,7 +173,7 @@ private:
     INLINE const string &get_name() const;
     INLINE bool is_active() const;
     INLINE PStatCollectorDef *get_def(const PStatClient *client, int this_index) const;
-      
+
   private:
     void make_def(const PStatClient *client, int this_index);
 
@@ -201,6 +203,7 @@ private:
   class InternalThread {
   public:
     InternalThread(Thread *thread);
+    InternalThread(const string &name, const string &sync_name = "Main");
 
     WPT(Thread) _thread;
     string _name;
@@ -248,6 +251,7 @@ private:
   friend class PStatCollector;
   friend class PStatThread;
   friend class PStatClientImpl;
+  friend class GraphicsStateGuardian;
 };
 
 #include "pStatClient.I"
@@ -272,4 +276,3 @@ PUBLISHED:
 #endif  // DO_PSTATS
 
 #endif
-

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