From 3732d3f1e68447af150571d2370d13ea029196a7 Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 22 Mar 2021 21:48:53 +0100 Subject: [PATCH 1/7] PythonUtil: Fix Python 3 next() for SerialNumGen, AlphabetCounter --- direct/src/showbase/PythonUtil.py | 9 +++++++++ tests/showbase/test_PythonUtil.py | 22 ++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/direct/src/showbase/PythonUtil.py b/direct/src/showbase/PythonUtil.py index 98753a7622..7f8830fc85 100644 --- a/direct/src/showbase/PythonUtil.py +++ b/direct/src/showbase/PythonUtil.py @@ -1245,18 +1245,24 @@ class SerialNumGen: if start is None: start = 0 self.__counter = start-1 + def next(self): self.__counter += 1 return self.__counter + __next__ = next + class SerialMaskedGen(SerialNumGen): def __init__(self, mask, start=None): self._mask = mask SerialNumGen.__init__(self, start) + def next(self): v = SerialNumGen.next(self) return v & self._mask + __next__ = next + _serialGen = SerialNumGen() def serialNum(): global _serialGen @@ -2517,6 +2523,7 @@ class AlphabetCounter: # object that produces 'A', 'B', 'C', ... 'AA', 'AB', etc. def __init__(self): self._curCounter = ['A'] + def next(self): result = ''.join([c for c in self._curCounter]) index = -1 @@ -2540,6 +2547,8 @@ class AlphabetCounter: break return result + __next__ = next + if __debug__ and __name__ == '__main__': def testAlphabetCounter(): tempList = [] diff --git a/tests/showbase/test_PythonUtil.py b/tests/showbase/test_PythonUtil.py index 6b9d50cde0..df174ef76b 100644 --- a/tests/showbase/test_PythonUtil.py +++ b/tests/showbase/test_PythonUtil.py @@ -157,3 +157,25 @@ def test_weighted_choice(): # When subtracting that number by each weight, it will reach 0 # by the time it hits 'item6' in the iteration. assert item == items[5] + + +def test_serial(): + gen = PythonUtil.SerialNumGen() + assert gen.next() == 0 + assert next(gen) == 1 + assert next(gen) == 2 + assert gen.next() == 3 + + +def test_alphabet_counter(): + counter = PythonUtil.AlphabetCounter() + assert next(counter) == 'A' + assert counter.next() == 'B' + assert counter.next() == 'C' + assert next(counter) == 'D' + + for i in range(26 - 4): + next(counter) + + assert next(counter) == 'AA' + assert next(counter) == 'AB' From 3ecc8c857a9416c5929bd389150110f8c7b91beb Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 22 Mar 2021 21:50:41 +0100 Subject: [PATCH 2/7] gobj: Also fix GeomPrimitive::offset_vertices() overload with begin/end See 40b94c1f9776e42c59ef95140f2a4a5fab3030d8 --- panda/src/gobj/geomPrimitive.cxx | 47 +++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/panda/src/gobj/geomPrimitive.cxx b/panda/src/gobj/geomPrimitive.cxx index ca51d95079..68647082ae 100644 --- a/panda/src/gobj/geomPrimitive.cxx +++ b/panda/src/gobj/geomPrimitive.cxx @@ -542,16 +542,49 @@ offset_vertices(int offset, int begin_row, int end_row) { consider_elevate_index_type(cdata, max_vertex + offset); - GeomVertexRewriter index(do_modify_vertices(cdata), 0); - index.set_row_unsafe(begin_row); - for (int j = begin_row; j < end_row; ++j) { - int vertex = index.get_data1i(); - if (vertex != strip_cut_index) { - index.set_data1i(vertex + offset); + { + GeomVertexArrayDataHandle handle(cdata->_vertices.get_write_pointer(), + Thread::get_current_thread()); + + unsigned char *ptr = handle.get_write_pointer(); + switch (cdata->_index_type) { + case GeomEnums::NT_uint8: + for (int i = begin_row; i < end_row; ++i) { + uint8_t &v = ((uint8_t *)ptr)[i]; + if (v != 0xff) { + v += offset; + } + } + break; + + case GeomEnums::NT_uint16: + for (int i = begin_row; i < end_row; ++i) { + uint16_t &v = ((uint16_t *)ptr)[i]; + if (v != 0xffff) { + v += offset; + } + } + break; + + case GeomEnums::NT_uint32: + for (int i = begin_row; i < end_row; ++i) { + uint32_t &v = ((uint32_t *)ptr)[i]; + if (v != 0xffffffff) { + v += offset; + } + } + break; + + default: + nassert_raise("unsupported index type"); + break; } } - } else { + cdata->_modified = Geom::get_next_modified(); + cdata->_got_minmax = false; + } + else { // The supplied values cover all vertices, so we don't need to make it // indexed. CDWriter cdata(_cycler, true); From 8bde2f2b1b172971c5e0e5778a5c7d636e64d0fb Mon Sep 17 00:00:00 2001 From: rdb Date: Mon, 22 Mar 2021 22:04:08 +0100 Subject: [PATCH 3/7] tests: Add unit tests for GeomPrimitive::offset_vertices() --- tests/gobj/test_geom_primitives.py | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/gobj/test_geom_primitives.py b/tests/gobj/test_geom_primitives.py index 49cce11e31..a4646512bf 100644 --- a/tests/gobj/test_geom_primitives.py +++ b/tests/gobj/test_geom_primitives.py @@ -81,3 +81,65 @@ def test_geom_linestrips_adjacency(): 3, 4, 5, 6, 4, 5, 6, 6, ) + + +def test_geom_linestrips_offset_indexed(): + prim = core.GeomLinestrips(core.GeomEnums.UH_static) + prim.add_vertex(0) + prim.add_vertex(1) + prim.close_primitive() + prim.add_vertex(1) + prim.add_vertex(2) + prim.add_vertex(3) + prim.close_primitive() + prim.add_vertex(3) + prim.add_vertex(4) + prim.add_vertex(5) + prim.add_vertex(6) + prim.close_primitive() + + prim.offset_vertices(100) + verts = prim.get_vertex_list() + cut = prim.strip_cut_index + assert tuple(verts) == ( + 100, 101, + cut, + 101, 102, 103, + cut, + 103, 104, 105, 106, + ) + + prim.offset_vertices(-100) + verts = prim.get_vertex_list() + cut = prim.strip_cut_index + assert tuple(verts) == ( + 0, 1, + cut, + 1, 2, 3, + cut, + 3, 4, 5, 6, + ) + + prim.offset_vertices(100, 4, 9) + verts = prim.get_vertex_list() + cut = prim.strip_cut_index + assert tuple(verts) == ( + 0, 1, + cut, + 1, 102, 103, + cut, + 103, 104, 5, 6, + ) + + # Automatically upgrade to uint32 + prim.offset_vertices(100000) + assert prim.index_type == core.GeomEnums.NT_uint32 + verts = prim.get_vertex_list() + cut = prim.strip_cut_index + assert tuple(verts) == ( + 100000, 100001, + cut, + 100001, 100102, 100103, + cut, + 100103, 100104, 100005, 100006, + ) From 94806801e34ee05fe801bdaad43dfa4b74cef1fd Mon Sep 17 00:00:00 2001 From: rdb Date: Tue, 23 Mar 2021 15:04:20 +0100 Subject: [PATCH 4/7] Add p3headlessgl render plug-in on Linux using EGL as fallback for GLX Fixes #1086 --- direct/src/dist/commands.py | 2 +- makepanda/makepanda.py | 35 ++++++++++++++++--- makepanda/makewheel.py | 3 +- panda/metalibs/pandagl/pandagl.cxx | 13 +++++-- panda/metalibs/pandagl/pandagl.h | 4 +++ panda/src/egldisplay/config_egldisplay.cxx | 2 +- panda/src/egldisplay/eglGraphicsPipe.cxx | 6 ++-- panda/src/egldisplay/eglGraphicsPipe.h | 4 ++- panda/src/egldisplay/eglGraphicsPixmap.cxx | 4 +-- panda/src/egldisplay/eglGraphicsPixmap.h | 4 +-- .../egldisplay/eglGraphicsStateGuardian.cxx | 10 +++--- .../src/egldisplay/eglGraphicsStateGuardian.h | 4 +-- panda/src/egldisplay/eglGraphicsWindow.cxx | 4 +-- panda/src/egldisplay/eglGraphicsWindow.h | 4 +-- 14 files changed, 69 insertions(+), 30 deletions(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index b51c2dd642..ca2b1aa519 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -277,7 +277,7 @@ class build_apps(setuptools.Command): 'libpthread.so.*', 'libc.so.*', 'ld-linux-x86-64.so.*', 'libgl.so.*', 'libx11.so.*', 'libncursesw.so.*', 'libz.so.*', 'librt.so.*', 'libutil.so.*', 'libnsl.so.1', 'libXext.so.6', - 'libXrender.so.1', 'libICE.so.6', 'libSM.so.6', + 'libXrender.so.1', 'libICE.so.6', 'libSM.so.6', 'libEGL.so.1', 'libgobject-2.0.so.0', 'libgthread-2.0.so.0', 'libglib-2.0.so.0', # macOS diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index c457a7fe12..71f8c00e3e 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -1255,6 +1255,14 @@ if not PkgSkip("EIGEN"): # will turn them into runtime assertions. DefSymbol("ALWAYS", "EIGEN_NO_STATIC_ASSERT") +if not PkgSkip("EGL"): + DefSymbol('EGL', 'HAVE_EGL', '') + if PkgSkip("X11"): + DefSymbol('EGL', 'EGL_NO_X11', '') + +if not PkgSkip("X11"): + DefSymbol('X11', 'USE_X11', '') + ######################################################################## ## ## Give a Status Report on Command-Line Options @@ -3243,6 +3251,8 @@ configprc = configprc.replace('\r\n', '\n') if (GetTarget() == 'windows'): configprc = configprc.replace("$XDG_CACHE_HOME/panda3d", "$USER_APPDATA/Panda3D-%s" % MAJOR_VERSION) +elif not PkgSkip("X11") and not PkgSkip("GL") and not PkgSkip("EGL") and not GetLinkAllStatic(): + configprc = configprc.replace("#load-display pandadx9", "aux-display p3headlessgl") else: configprc = configprc.replace("aux-display pandadx9", "") @@ -3591,9 +3601,10 @@ elif GetTarget() == 'darwin': elif GetTarget() == 'android': CopyAllHeaders('panda/src/android') CopyAllHeaders('panda/src/androiddisplay') -else: +if not PkgSkip('X11'): CopyAllHeaders('panda/src/x11display') - CopyAllHeaders('panda/src/glxdisplay') + if not PkgSkip('GL'): + CopyAllHeaders('panda/src/glxdisplay') CopyAllHeaders('panda/src/egldisplay') CopyAllHeaders('panda/metalibs/pandagl') CopyAllHeaders('panda/metalibs/pandagles') @@ -5156,7 +5167,6 @@ if (GetTarget() == 'windows' and PkgSkip("GL")==0 and not RUNTIME): # If we're not compiling with any windowing system at all, but we do have EGL, # we can use that to create a headless libpandagl instead. if not PkgSkip("EGL") and not PkgSkip("GL") and PkgSkip("X11") and GetTarget() not in ('windows', 'darwin') and not RUNTIME: - DefSymbol('EGL', 'HAVE_EGL', '') OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGL', 'GL', 'EGL'] TargetAdd('pandagl_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx') OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'GL', 'EGL'] @@ -5168,13 +5178,28 @@ if not PkgSkip("EGL") and not PkgSkip("GL") and PkgSkip("X11") and GetTarget() n TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS) TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'EGL', 'CGGL']) +elif not PkgSkip("EGL") and not PkgSkip("GL") and GetTarget() not in ('windows', 'darwin') and not RUNTIME: + # As a temporary solution for #1086, build this module, which we can use as a + # fallback to OpenGL for headless systems. + DefSymbol('EGL', 'HAVE_EGL', '') + OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGL', 'GL', 'EGL'] + TargetAdd('p3headlessgl_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx') + OPTS=['DIR:panda/metalibs/pandagl', 'BUILDING:PANDAGL', 'GL', 'EGL'] + TargetAdd('p3headlessgl_pandagl.obj', opts=OPTS, input='pandagl.cxx') + TargetAdd('libp3headlessgl.dll', input='p3headlessgl_pandagl.obj') + TargetAdd('libp3headlessgl.dll', input='p3glgsg_config_glgsg.obj') + TargetAdd('libp3headlessgl.dll', input='p3glgsg_glgsg.obj') + TargetAdd('libp3headlessgl.dll', input='p3headlessgl_egldisplay_composite1.obj') + TargetAdd('libp3headlessgl.dll', input=COMMON_PANDA_LIBS) + TargetAdd('libp3headlessgl.dll', opts=['MODULE', 'GL', 'EGL', 'CGGL']) + # # DIRECTORY: panda/src/egldisplay/ # if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and not RUNTIME): DefSymbol('GLES', 'OPENGLES_1', '') - OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] + OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES', 'GLES', 'EGL', 'X11'] TargetAdd('pandagles_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx') OPTS=['DIR:panda/metalibs/pandagles', 'BUILDING:PANDAGLES', 'GLES', 'EGL'] TargetAdd('pandagles_pandagles.obj', opts=OPTS, input='pandagles.cxx') @@ -5193,7 +5218,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and not RUNTIME): if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and not RUNTIME): DefSymbol('GLES2', 'OPENGLES_2', '') - OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES2', 'GLES2', 'EGL'] + OPTS=['DIR:panda/src/egldisplay', 'DIR:panda/src/glstuff', 'BUILDING:PANDAGLES2', 'GLES2', 'EGL', 'X11'] TargetAdd('pandagles2_egldisplay_composite1.obj', opts=OPTS, input='p3egldisplay_composite1.cxx') OPTS=['DIR:panda/metalibs/pandagles2', 'BUILDING:PANDAGLES2', 'GLES2', 'EGL'] TargetAdd('pandagles2_pandagles2.obj', opts=OPTS, input='pandagles2.cxx') diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index aaf7efa24e..57d0fe2242 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -90,7 +90,7 @@ ABI_TAG = get_abi_tag() EXCLUDE_EXT = [".pyc", ".pyo", ".N", ".prebuilt", ".xcf", ".plist", ".vcproj", ".sln"] # Plug-ins to install. -PLUGIN_LIBS = ["pandagl", "pandagles", "pandagles2", "pandadx9", "p3tinydisplay", "p3ptloader", "p3assimp", "p3ffmpeg", "p3openal_audio", "p3fmod_audio"] +PLUGIN_LIBS = ["pandagl", "pandagles", "pandagles2", "pandadx9", "p3tinydisplay", "p3ptloader", "p3assimp", "p3ffmpeg", "p3openal_audio", "p3fmod_audio", "p3headlessgl"] # Libraries included in manylinux ABI that should be ignored. See PEP 513/571/599. MANYLINUX_LIBS = [ @@ -103,6 +103,7 @@ MANYLINUX_LIBS = [ # These are not mentioned in manylinux1 spec but should nonetheless always # be excluded. "linux-vdso.so.1", "linux-gate.so.1", "ld-linux.so.2", "libdrm.so.2", + "libEGL.so.1", ] # Binaries to never scan for dependencies on non-Windows systems. diff --git a/panda/metalibs/pandagl/pandagl.cxx b/panda/metalibs/pandagl/pandagl.cxx index e6f01d9ce2..1a317fbdca 100644 --- a/panda/metalibs/pandagl/pandagl.cxx +++ b/panda/metalibs/pandagl/pandagl.cxx @@ -26,7 +26,7 @@ #include "glxGraphicsPipe.h" #endif -#if defined(HAVE_EGL) && !defined(HAVE_X11) +#ifdef HAVE_EGL #include "config_egldisplay.h" #include "eglGraphicsPipe.h" #endif @@ -59,7 +59,7 @@ init_libpandagl() { init_libglxdisplay(); #endif -#if defined(HAVE_EGL) && !defined(HAVE_X11) +#ifdef HAVE_EGL init_libegldisplay(); #endif } @@ -84,9 +84,16 @@ get_pipe_type_pandagl() { return glxGraphicsPipe::get_class_type().get_index(); #endif -#if defined(HAVE_EGL) && !defined(HAVE_X11) +#ifdef HAVE_EGL return eglGraphicsPipe::get_class_type().get_index(); #endif return 0; } + +#if defined(HAVE_EGL) && !defined(USE_X11) +int +get_pipe_type_p3headlessgl() { + return eglGraphicsPipe::get_class_type().get_index(); +} +#endif diff --git a/panda/metalibs/pandagl/pandagl.h b/panda/metalibs/pandagl/pandagl.h index 2a25a86587..3fb84b2152 100644 --- a/panda/metalibs/pandagl/pandagl.h +++ b/panda/metalibs/pandagl/pandagl.h @@ -12,4 +12,8 @@ EXPCL_PANDAGL void init_libpandagl(); extern "C" EXPCL_PANDAGL int get_pipe_type_pandagl(); +#if defined(HAVE_EGL) && !defined(USE_X11) +extern "C" EXPCL_PANDAGL int get_pipe_type_p3headlessgl(); +#endif + #endif diff --git a/panda/src/egldisplay/config_egldisplay.cxx b/panda/src/egldisplay/config_egldisplay.cxx index e80137290d..0903a3bd88 100644 --- a/panda/src/egldisplay/config_egldisplay.cxx +++ b/panda/src/egldisplay/config_egldisplay.cxx @@ -48,7 +48,7 @@ init_libegldisplay() { eglGraphicsBuffer::init_type(); eglGraphicsPipe::init_type(); -#ifdef HAVE_X11 +#ifdef USE_X11 eglGraphicsPixmap::init_type(); eglGraphicsWindow::init_type(); #endif diff --git a/panda/src/egldisplay/eglGraphicsPipe.cxx b/panda/src/egldisplay/eglGraphicsPipe.cxx index 6ce38433cb..a75aec5170 100644 --- a/panda/src/egldisplay/eglGraphicsPipe.cxx +++ b/panda/src/egldisplay/eglGraphicsPipe.cxx @@ -53,7 +53,7 @@ eglGraphicsPipe() { //NB. if the X11 display failed to open, _display will be 0, which is a valid // input to eglGetDisplay - it means to open the default display. -#ifdef HAVE_X11 +#ifdef USE_X11 _egl_display = eglGetDisplay((NativeDisplayType) _display); #else _egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); @@ -207,7 +207,7 @@ make_output(const std::string &name, // First thing to try: an eglGraphicsWindow if (retry == 0) { -#ifdef HAVE_X11 +#ifdef USE_X11 if (!_display) { return nullptr; } @@ -290,7 +290,7 @@ make_output(const std::string &name, // Fourth thing to try: an eglGraphicsPixmap. if (retry == 3) { -#ifdef HAVE_X11 +#ifdef USE_X11 if (!_display) { return nullptr; } diff --git a/panda/src/egldisplay/eglGraphicsPipe.h b/panda/src/egldisplay/eglGraphicsPipe.h index def344a61c..ec99f4a446 100644 --- a/panda/src/egldisplay/eglGraphicsPipe.h +++ b/panda/src/egldisplay/eglGraphicsPipe.h @@ -16,12 +16,14 @@ #include "pandabase.h" -#ifdef HAVE_X11 +#ifdef USE_X11 #include "x11GraphicsPipe.h" typedef x11GraphicsPipe BaseGraphicsPipe; #else #include "graphicsPipe.h" typedef GraphicsPipe BaseGraphicsPipe; +#undef EGL_NO_X11 +#define EGL_NO_X11 1 #endif #ifdef OPENGLES_2 diff --git a/panda/src/egldisplay/eglGraphicsPixmap.cxx b/panda/src/egldisplay/eglGraphicsPixmap.cxx index 946499746c..64630e43de 100644 --- a/panda/src/egldisplay/eglGraphicsPixmap.cxx +++ b/panda/src/egldisplay/eglGraphicsPixmap.cxx @@ -13,7 +13,7 @@ #include "eglGraphicsPixmap.h" -#ifdef HAVE_X11 +#ifdef USE_X11 #include "eglGraphicsWindow.h" #include "eglGraphicsStateGuardian.h" @@ -245,4 +245,4 @@ open_buffer() { return true; } -#endif // HAVE_X11 +#endif // USE_X11 diff --git a/panda/src/egldisplay/eglGraphicsPixmap.h b/panda/src/egldisplay/eglGraphicsPixmap.h index 95cb7842c0..cddb3430d3 100644 --- a/panda/src/egldisplay/eglGraphicsPixmap.h +++ b/panda/src/egldisplay/eglGraphicsPixmap.h @@ -16,7 +16,7 @@ #include "pandabase.h" -#ifdef HAVE_X11 +#ifdef USE_X11 #include "eglGraphicsPipe.h" #include "graphicsBuffer.h" @@ -69,6 +69,6 @@ private: static TypeHandle _type_handle; }; -#endif // HAVE_X11 +#endif // USE_X11 #endif diff --git a/panda/src/egldisplay/eglGraphicsStateGuardian.cxx b/panda/src/egldisplay/eglGraphicsStateGuardian.cxx index 26f55f3b74..70924505ad 100644 --- a/panda/src/egldisplay/eglGraphicsStateGuardian.cxx +++ b/panda/src/egldisplay/eglGraphicsStateGuardian.cxx @@ -187,7 +187,7 @@ choose_pixel_format(const FrameBufferProperties &properties, best_props = fbprops; } } -#ifdef HAVE_X11 +#ifdef USE_X11 X11_Display *display = egl_pipe->get_display(); if (display) { int screen = egl_pipe->get_screen(); @@ -233,7 +233,7 @@ choose_pixel_format(const FrameBufferProperties &properties, int err = eglGetError(); if (_context && err == EGL_SUCCESS) { -#ifdef HAVE_X11 +#ifdef USE_X11 if (!display || _visual) #endif { @@ -262,7 +262,7 @@ choose_pixel_format(const FrameBufferProperties &properties, << get_egl_error_string(err) << "\n"; _fbconfig = 0; _context = 0; -#ifdef HAVE_X11 +#ifdef USE_X11 _visual = 0; #endif } @@ -306,7 +306,7 @@ egl_is_at_least_version(int major_version, int minor_version) const { */ void eglGraphicsStateGuardian:: gl_flush() const { -#ifdef HAVE_X11 +#ifdef USE_X11 // This call requires synchronization with X. LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); #endif @@ -318,7 +318,7 @@ gl_flush() const { */ GLenum eglGraphicsStateGuardian:: gl_get_error() const { -#ifdef HAVE_X11 +#ifdef USE_X11 // This call requires synchronization with X. LightReMutexHolder holder(eglGraphicsPipe::_x_mutex); #endif diff --git a/panda/src/egldisplay/eglGraphicsStateGuardian.h b/panda/src/egldisplay/eglGraphicsStateGuardian.h index 608e9aa080..1a7a036609 100644 --- a/panda/src/egldisplay/eglGraphicsStateGuardian.h +++ b/panda/src/egldisplay/eglGraphicsStateGuardian.h @@ -17,7 +17,7 @@ #include "pandabase.h" #include "eglGraphicsPipe.h" -#ifdef HAVE_X11 +#ifdef USE_X11 #include "get_x11.h" #endif @@ -55,7 +55,7 @@ public: EGLContext _share_context; EGLContext _context; EGLDisplay _egl_display; -#ifdef HAVE_X11 +#ifdef USE_X11 XVisualInfo *_visual = nullptr; #endif EGLConfig _fbconfig; diff --git a/panda/src/egldisplay/eglGraphicsWindow.cxx b/panda/src/egldisplay/eglGraphicsWindow.cxx index c85ff9d8df..31002c9fa6 100644 --- a/panda/src/egldisplay/eglGraphicsWindow.cxx +++ b/panda/src/egldisplay/eglGraphicsWindow.cxx @@ -13,7 +13,7 @@ #include "eglGraphicsWindow.h" -#ifdef HAVE_X11 +#ifdef USE_X11 #include "eglGraphicsStateGuardian.h" #include "config_egldisplay.h" @@ -279,4 +279,4 @@ open_window() { return true; } -#endif // HAVE_X11 +#endif // USE_X11 diff --git a/panda/src/egldisplay/eglGraphicsWindow.h b/panda/src/egldisplay/eglGraphicsWindow.h index 1c359aa666..1a5efad7fd 100644 --- a/panda/src/egldisplay/eglGraphicsWindow.h +++ b/panda/src/egldisplay/eglGraphicsWindow.h @@ -16,7 +16,7 @@ #include "pandabase.h" -#ifdef HAVE_X11 +#ifdef USE_X11 #include "eglGraphicsPipe.h" #include "x11GraphicsWindow.h" @@ -67,6 +67,6 @@ private: #include "eglGraphicsWindow.I" -#endif // HAVE_X11 +#endif // USE_X11 #endif From 23150bd6d6014a55b125e7aeee2f042e52420f0e Mon Sep 17 00:00:00 2001 From: LD Date: Wed, 3 Mar 2021 22:04:29 +0100 Subject: [PATCH 5/7] pgraph: Add the shader attrib to the filled wireframe render state Fixes #1021 Closes #1124 --- panda/src/pgraph/config_pgraph.cxx | 7 ++++++ panda/src/pgraph/config_pgraph.h | 2 ++ panda/src/pgraph/cullResult.cxx | 38 +++++++++++++++++++++++++----- panda/src/pgraph/cullResult.h | 1 + samples/shader-terrain/main.py | 1 + 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 5729e22067..795300223a 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -373,6 +373,13 @@ ConfigVariableBool allow_live_flatten "only has an effect when Panda is not compiled for a release " "build.")); +ConfigVariableBool filled_wireframe_apply_shader +("filled-wireframe-apply-shader", false, + PRC_DESC("Set this true to apply any shader configured on nodes onto the " + "filled wireframe overlay. The wireframe color is multiplied with " + "the result of the fragment shader. This is helpful when the shader " + "alters the position of the vertices and makes the overlay wrong.")); + /** * Initializes the library. This must be called at least once before any of * the functions or classes in this library can be used. Normally it will be diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index 11094b9d16..f1bec0168e 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -74,6 +74,8 @@ extern ConfigVariableString default_model_extension; extern ConfigVariableBool allow_live_flatten; +extern ConfigVariableBool filled_wireframe_apply_shader; + extern EXPCL_PANDA_PGRAPH void init_libpgraph(); #endif diff --git a/panda/src/pgraph/cullResult.cxx b/panda/src/pgraph/cullResult.cxx index 65c9555bff..b6b238f838 100644 --- a/panda/src/pgraph/cullResult.cxx +++ b/panda/src/pgraph/cullResult.cxx @@ -28,6 +28,7 @@ #include "config_pgraph.h" #include "depthOffsetAttrib.h" #include "colorBlendAttrib.h" +#include "shaderAttrib.h" TypeHandle CullResult::_type_handle; @@ -133,7 +134,9 @@ add_object(CullableObject *object, const CullTraverser *traverser) { if (object->_state->get_attrib(rmode)) { if (rmode->get_mode() == RenderModeAttrib::M_filled_wireframe) { CullableObject *wireframe_part = new CullableObject(*object); - wireframe_part->_state = get_wireframe_overlay_state(rmode); + const ShaderAttrib *shader = nullptr; + object->_state->get_attrib(shader); + wireframe_part->_state = get_wireframe_overlay_state(rmode, shader); if (wireframe_part->munge_geom (_gsg, _gsg->get_geom_munger(wireframe_part->_state, current_thread), @@ -521,13 +524,36 @@ get_wireframe_filled_state() { */ CPT(RenderState) CullResult:: get_wireframe_overlay_state(const RenderModeAttrib *rmode) { - return RenderState::make( + return get_wireframe_overlay_state(rmode, nullptr); +} + +/** + * Returns a RenderState that renders only the wireframe part of an + * M_filled_wireframe model. + * If a shader attrib is provided, a constant color is used in ColorBlendAttrib + * to emulate the flat color. + */ +CPT(RenderState) CullResult:: +get_wireframe_overlay_state(const RenderModeAttrib *rmode, const ShaderAttrib *shader) { + CPT(RenderState) state = RenderState::make( DepthOffsetAttrib::make(1, 0, 0.99999f), - ColorAttrib::make_flat(rmode->get_wireframe_color()), - ColorBlendAttrib::make(ColorBlendAttrib::M_add, - ColorBlendAttrib::O_incoming_alpha, - ColorBlendAttrib::O_one_minus_incoming_alpha), RenderModeAttrib::make(RenderModeAttrib::M_wireframe, rmode->get_thickness(), rmode->get_perspective())); + if (filled_wireframe_apply_shader) { + state = state->add_attrib(ColorBlendAttrib::make(ColorBlendAttrib::M_add, + ColorBlendAttrib::O_zero, + ColorBlendAttrib::O_constant_color, + ColorBlendAttrib::M_add, + ColorBlendAttrib::O_one, + ColorBlendAttrib::O_one_minus_incoming_alpha, + rmode->get_wireframe_color())); + state = state->add_attrib(shader); + } else { + state = state->add_attrib(ColorBlendAttrib::make(ColorBlendAttrib::M_add, + ColorBlendAttrib::O_incoming_alpha, + ColorBlendAttrib::O_one_minus_incoming_alpha)); + state = state->add_attrib(ColorAttrib::make_flat(rmode->get_wireframe_color())); + } + return state; } diff --git a/panda/src/pgraph/cullResult.h b/panda/src/pgraph/cullResult.h index df1d166426..24c0df3d78 100644 --- a/panda/src/pgraph/cullResult.h +++ b/panda/src/pgraph/cullResult.h @@ -78,6 +78,7 @@ private: static const RenderState *get_dual_opaque_state(); static const RenderState *get_wireframe_filled_state(); static CPT(RenderState) get_wireframe_overlay_state(const RenderModeAttrib *rmode); + static CPT(RenderState) get_wireframe_overlay_state(const RenderModeAttrib *rmode, const ShaderAttrib *shader); GraphicsStateGuardianBase *_gsg; PStatCollector _draw_region_pcollector; diff --git a/samples/shader-terrain/main.py b/samples/shader-terrain/main.py index 10bffb3767..8d500b4d05 100644 --- a/samples/shader-terrain/main.py +++ b/samples/shader-terrain/main.py @@ -20,6 +20,7 @@ class ShaderTerrainDemo(ShowBase): textures-power-2 none gl-coordinate-system default window-title Panda3D ShaderTerrainMesh Demo + filled-wireframe-apply-shader true """) # Initialize the showbase From 1fb848058544e913729e80a8c6e73793fdc57653 Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 24 Mar 2021 11:56:24 +0100 Subject: [PATCH 6/7] Exclude GLVND from built distributions --- direct/src/dist/commands.py | 1 + makepanda/makewheel.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/direct/src/dist/commands.py b/direct/src/dist/commands.py index ca2b1aa519..5a81ec49dc 100644 --- a/direct/src/dist/commands.py +++ b/direct/src/dist/commands.py @@ -278,6 +278,7 @@ class build_apps(setuptools.Command): 'libgl.so.*', 'libx11.so.*', 'libncursesw.so.*', 'libz.so.*', 'librt.so.*', 'libutil.so.*', 'libnsl.so.1', 'libXext.so.6', 'libXrender.so.1', 'libICE.so.6', 'libSM.so.6', 'libEGL.so.1', + 'libOpenGL.so.0', 'libGLdispatch.so.0', 'libGLX.so.0', 'libgobject-2.0.so.0', 'libgthread-2.0.so.0', 'libglib-2.0.so.0', # macOS diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py index 57d0fe2242..b9de5fd225 100644 --- a/makepanda/makewheel.py +++ b/makepanda/makewheel.py @@ -103,7 +103,7 @@ MANYLINUX_LIBS = [ # These are not mentioned in manylinux1 spec but should nonetheless always # be excluded. "linux-vdso.so.1", "linux-gate.so.1", "ld-linux.so.2", "libdrm.so.2", - "libEGL.so.1", + "libEGL.so.1", "libOpenGL.so.0", "libGLX.so.0", "libGLdispatch.so.0", ] # Binaries to never scan for dependencies on non-Windows systems. From 8372b8150aeff041482deef0997647775c47b94f Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 24 Mar 2021 12:50:42 +0100 Subject: [PATCH 7/7] gobj: Cube map sampling support in TexturePeeker Closes #1098 Co-authored-by: Mitchell Stokes --- panda/src/gobj/texturePeeker.I | 8 ++ panda/src/gobj/texturePeeker.cxx | 184 +++++++++++++++++++------------ panda/src/gobj/texturePeeker.h | 6 +- tests/gobj/test_texture_peek.py | 48 ++++++++ 4 files changed, 172 insertions(+), 74 deletions(-) diff --git a/panda/src/gobj/texturePeeker.I b/panda/src/gobj/texturePeeker.I index ccf6bdcc45..d53e5d5a91 100644 --- a/panda/src/gobj/texturePeeker.I +++ b/panda/src/gobj/texturePeeker.I @@ -56,3 +56,11 @@ INLINE bool TexturePeeker:: has_pixel(int x, int y) const { return x >= 0 && y >= 0 && x < _x_size && y < _y_size; } + +/** + * Returns whether a given coordinate is inside of the texture dimensions. + */ +INLINE bool TexturePeeker:: +has_pixel(int x, int y, int z) const { + return x >= 0 && y >= 0 && z >= 0 && x < _x_size && y < _y_size && z < _z_size; +} diff --git a/panda/src/gobj/texturePeeker.cxx b/panda/src/gobj/texturePeeker.cxx index ee82e4644b..97da4edec2 100644 --- a/panda/src/gobj/texturePeeker.cxx +++ b/panda/src/gobj/texturePeeker.cxx @@ -74,56 +74,42 @@ static double get_signed_int_i(const unsigned char *&p) { */ TexturePeeker:: TexturePeeker(Texture *tex, Texture::CData *cdata) { - if (cdata->_texture_type == Texture::TT_cube_map) { - // Cube map texture. We'll need to map from (u, v, w) to (u, v) within - // the appropriate page, where w indicates the page. + // Simple ram images are possible if it is a 2-d texture. + if (tex->do_has_ram_image(cdata) && cdata->_ram_image_compression == Texture::CM_off) { + // Get the regular RAM image if it is available. + _image = tex->do_get_ram_image(cdata); + _x_size = cdata->_x_size; + _y_size = cdata->_y_size; + _z_size = cdata->_z_size; + _pixel_width = cdata->_component_width * cdata->_num_components; + _format = cdata->_format; + _component_type = cdata->_component_type; + } + else if (!cdata->_simple_ram_image._image.empty()) { + // Get the simple RAM image if *that* is available. + _image = cdata->_simple_ram_image._image; + _x_size = cdata->_simple_x_size; + _y_size = cdata->_simple_y_size; + _z_size = 1; - // TODO: handle cube maps. - return; - - } else { - // Regular 1-d, 2-d, or 3-d texture. The coordinates map directly. - // Simple ram images are possible if it is a 2-d texture. - if (tex->do_has_ram_image(cdata) && cdata->_ram_image_compression == Texture::CM_off) { - // Get the regular RAM image if it is available. - _image = tex->do_get_ram_image(cdata); - _x_size = cdata->_x_size; - _y_size = cdata->_y_size; - _z_size = cdata->_z_size; - _component_width = cdata->_component_width; - _num_components = cdata->_num_components; - _format = cdata->_format; - _component_type = cdata->_component_type; - - } else if (!cdata->_simple_ram_image._image.empty()) { - // Get the simple RAM image if *that* is available. - _image = cdata->_simple_ram_image._image; - _x_size = cdata->_simple_x_size; - _y_size = cdata->_simple_y_size; - _z_size = 1; - - _component_width = 1; - _num_components = 4; - _format = Texture::F_rgba; - _component_type = Texture::T_unsigned_byte; - - } else { - // Failing that, reload and get the uncompressed RAM image. - _image = tex->do_get_uncompressed_ram_image(cdata); - _x_size = cdata->_x_size; - _y_size = cdata->_y_size; - _z_size = cdata->_z_size; - _component_width = cdata->_component_width; - _num_components = cdata->_num_components; - _format = cdata->_format; - _component_type = cdata->_component_type; - } + _pixel_width = 4; + _format = Texture::F_rgba; + _component_type = Texture::T_unsigned_byte; + } + else { + // Failing that, reload and get the uncompressed RAM image. + _image = tex->do_get_uncompressed_ram_image(cdata); + _x_size = cdata->_x_size; + _y_size = cdata->_y_size; + _z_size = cdata->_z_size; + _pixel_width = cdata->_component_width * cdata->_num_components; + _format = cdata->_format; + _component_type = cdata->_component_type; } if (_image.is_null()) { return; } - _pixel_width = _component_width * _num_components; if (Texture::is_integer(_format)) { switch (_component_type) { @@ -291,8 +277,10 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { _image.clear(); return; } -} + _is_cube = (cdata->_texture_type == Texture::TT_cube_map || + cdata->_texture_type == Texture::TT_cube_map_array); +} /** * Fills "color" with the RGBA color of the texel at point (u, v). @@ -304,22 +292,95 @@ TexturePeeker(Texture *tex, Texture::CData *cdata) { */ void TexturePeeker:: lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const { - int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size; - int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size; - fetch_pixel(color, x, y); + if (!_is_cube) { + int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size; + int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size; + fetch_pixel(color, x, y); + } + else { + lookup(color, u, v, 0); + } } /** - * Works like TexturePeeker::lookup(), but instead uv-coordinates integer - * coordinates are used. + * Fills "color" with the RGBA color of the texel at point (u, v, w). + * + * The texel color is determined via nearest-point sampling (no filtering of + * adjacent pixels), regardless of the filter type associated with the + * texture. u, v, and w will wrap around regardless of the texture's wrap + * mode. */ void TexturePeeker:: -fetch_pixel(LColor& color, int x, int y) const { +lookup(LColor &color, PN_stdfloat u, PN_stdfloat v, PN_stdfloat w) const { + if (!_is_cube) { + int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size; + int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size; + int z = int((w - cfloor(w)) * (PN_stdfloat)_z_size) % _z_size; + + nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size && + z >= 0 && z < _z_size); + const unsigned char *p = _image.p() + (z * _x_size * _y_size + y * _x_size + x) * _pixel_width; + + (*_get_texel)(color, p, _get_component); + } + else { + PN_stdfloat absu = fabs(u), + absv = fabs(v), + absw = fabs(w); + PN_stdfloat magnitude; + PN_stdfloat u2d, v2d; + int z; + + // The following was pulled from: + // https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/ + if (absw >= absu && absw >= absv) { + z = 4 + (w < 0.0); + magnitude = 0.5 / absw; + u2d = w < 0.0 ? -u : u; + v2d = -v; + } + else if (absv >= absu) { + z = 2 + (v < 0.0); + magnitude = 0.5 / absv; + u2d = u; + v2d = v < 0.0 ? -w : w; + } + else { + z = 0 + (u < 0.0); + magnitude = 0.5 / absu; + u2d = u < 0.0 ? w : -w; + v2d = -v; + } + u2d = u2d * magnitude + 0.5; + v2d = v2d * magnitude + 0.5; + + int x = int((u2d - cfloor(u2d)) * (PN_stdfloat)_x_size) % _x_size; + int y = int((v2d - cfloor(v2d)) * (PN_stdfloat)_y_size) % _y_size; + fetch_pixel(color, x, y, z); + } +} + +/** + * Works like TexturePeeker::lookup(), but instead uv-coordinates integer + * coordinates are used. + */ +void TexturePeeker:: +fetch_pixel(LColor &color, int x, int y) const { nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size); const unsigned char *p = _image.p() + (y * _x_size + x) * _pixel_width; (*_get_texel)(color, p, _get_component); } +/** + * Works like TexturePeeker::lookup(), but instead uv-coordinates integer + * coordinates are used. + */ +void TexturePeeker:: +fetch_pixel(LColor &color, int x, int y, int z) const { + nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size && z >= 0 && z < _z_size); + const unsigned char *p = _image.p() + ((z * _y_size + y) * _x_size + x) * _pixel_width; + (*_get_texel)(color, p, _get_component); +} /** * Performs a bilinear lookup to retrieve the color value stored at the uv @@ -370,27 +431,6 @@ lookup_bilinear(LColor &color, PN_stdfloat u, PN_stdfloat v) const { return true; } -/** - * Fills "color" with the RGBA color of the texel at point (u, v, w). - * - * The texel color is determined via nearest-point sampling (no filtering of - * adjacent pixels), regardless of the filter type associated with the - * texture. u, v, and w will wrap around regardless of the texture's wrap - * mode. - */ -void TexturePeeker:: -lookup(LColor &color, PN_stdfloat u, PN_stdfloat v, PN_stdfloat w) const { - int x = int((u - cfloor(u)) * (PN_stdfloat)_x_size) % _x_size; - int y = int((v - cfloor(v)) * (PN_stdfloat)_y_size) % _y_size; - int z = int((w - cfloor(w)) * (PN_stdfloat)_z_size) % _z_size; - - nassertv(x >= 0 && x < _x_size && y >= 0 && y < _y_size && - z >= 0 && z < _z_size); - const unsigned char *p = _image.p() + (z * _x_size * _y_size + y * _x_size + x) * _pixel_width; - - (*_get_texel)(color, p, _get_component); -} - /** * Fills "color" with the average RGBA color of the texels within the * rectangle defined by the specified coordinate range. diff --git a/panda/src/gobj/texturePeeker.h b/panda/src/gobj/texturePeeker.h index e88603bcea..2894e349cc 100644 --- a/panda/src/gobj/texturePeeker.h +++ b/panda/src/gobj/texturePeeker.h @@ -37,9 +37,11 @@ PUBLISHED: INLINE int get_z_size() const; INLINE bool has_pixel(int x, int y) const; + INLINE bool has_pixel(int x, int y, int z) const; void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v) const; void lookup(LColor &color, PN_stdfloat u, PN_stdfloat v, PN_stdfloat w) const; void fetch_pixel(LColor &color, int x, int y) const; + void fetch_pixel(LColor &color, int x, int y, int z) const; bool lookup_bilinear(LColor &color, PN_stdfloat u, PN_stdfloat v) const; void filter_rect(LColor &color, PN_stdfloat min_u, PN_stdfloat min_v, @@ -86,8 +88,8 @@ private: int _x_size; int _y_size; int _z_size; - int _component_width; - int _num_components; + int _is_cube; + int _unused1; int _pixel_width; Texture::Format _format; Texture::ComponentType _component_type; diff --git a/tests/gobj/test_texture_peek.py b/tests/gobj/test_texture_peek.py index b113312dbb..339b285ddb 100644 --- a/tests/gobj/test_texture_peek.py +++ b/tests/gobj/test_texture_peek.py @@ -158,3 +158,51 @@ def test_texture_peek_int_i(): col = LColor() peeker.fetch_pixel(col, 0, 0) assert col == (minval, -1, 0, maxval) + + +def test_texture_peek_cube(): + maxval = 255 + data_list = [] + for z in range(6): + for y in range(3): + for x in range(3): + data_list += [z, y, x, maxval] + data = array('B', data_list) + tex = Texture("") + tex.setup_cube_map(3, Texture.T_unsigned_byte, Texture.F_rgba8i) + tex.set_ram_image(data) + peeker = tex.peek() + assert peeker.has_pixel(0, 0) + assert peeker.has_pixel(0, 0, 0) + + # If no z is specified, face 0 is used by default + col = LColor() + peeker.fetch_pixel(col, 1, 2) + assert col == (1, 2, 0, maxval) + + # Now try each face + for faceidx in range(6): + col = LColor() + peeker.fetch_pixel(col, 0, 0, faceidx) + assert col == (0, 0, faceidx, maxval) + + # Try some vector lookups. + def lookup(*vec): + col = LColor() + peeker.lookup(col, *vec) + return col + assert lookup(1, 0, 0) == (1, 1, 0, maxval) + assert lookup(-1, 0, 0) == (1, 1, 1, maxval) + assert lookup(0, 1, 0) == (1, 1, 2, maxval) + assert lookup(0, -1, 0) == (1, 1, 3, maxval) + assert lookup(0, 0, 1) == (1, 1, 4, maxval) + assert lookup(0, 0, -1) == (1, 1, 5, maxval) + + # Magnitude shouldn't matter + assert lookup(0, 2, 0) == (1, 1, 2, maxval) + assert lookup(0, 0, -0.5) == (1, 1, 5, maxval) + + # Sample in corner (slight bias to disambiguate which face is selected) + assert lookup(1.00001, 1, 1) == (0, 0, 0, maxval) + assert lookup(1.00001, 1, 0) == (1, 0, 0, maxval) + assert lookup(1, 1.00001, 0) == (2, 1, 2, maxval)