diff --git a/direct/src/directscripts/eggcacher.py b/direct/src/directscripts/eggcacher.py index 78070df2ec..ad11303fe0 100644 --- a/direct/src/directscripts/eggcacher.py +++ b/direct/src/directscripts/eggcacher.py @@ -88,3 +88,7 @@ class EggCacher: progress += size cacher = EggCacher(sys.argv[1:]) + +# Dummy main function so this can be added to console_scripts. +def main(): + return 0 diff --git a/direct/src/directscripts/packpanda.py b/direct/src/directscripts/packpanda.py index 6d89a9a0d0..c195017f04 100755 --- a/direct/src/directscripts/packpanda.py +++ b/direct/src/directscripts/packpanda.py @@ -419,3 +419,6 @@ else: if not(os.path.exists("/usr/bin/rpmbuild") or os.path.exists("/usr/bin/dpkg-deb")): exit("To build an installer, either rpmbuild or dpkg-deb must be present on your system!") +# Dummy main function so this can be added to console_scripts. +def main(): + return 0 diff --git a/doc/ReleaseNotes b/doc/ReleaseNotes index b7462a97d6..d3e22a1fc3 100644 --- a/doc/ReleaseNotes +++ b/doc/ReleaseNotes @@ -2,6 +2,7 @@ This issue fixes several bugs that were still found in 1.9.2. +* Fix crash when using homebrew Python on Mac OS X * Fix crash when running in Steam on Linux when using OpenAL * Fix crash using wx/tkinter on Mac as long as want-wx/tk is set * Fix loading models from 'models' package with models/ prefix @@ -48,6 +49,12 @@ This issue fixes several bugs that were still found in 1.9.2. * Fix exception when trying to pickle NodePathCollection objects * Fix error when trying to raise vectors to a power * GLSL: fix error when legacy matrix generator inputs are mat3 +* Now tries to preserve refresh rate when switching fullscreen on Windows +* Fix back-to-front sorting when gl-coordinate-system is changed +* Now also compiles on older Linux distros (eg. CentOS 5 / manylinux1) +* get_keyboard_map now includes keys on layouts with special characters +* Fix crash due to incorrect alignment when compiling Eigen with AVX +* Fix crash when writing 16-bit .tif file (now silently downsamples) ------------------------ RELEASE 1.9.2 ------------------------ diff --git a/dtool/src/dtoolbase/deletedBufferChain.cxx b/dtool/src/dtoolbase/deletedBufferChain.cxx index 89f056219c..122edf8837 100644 --- a/dtool/src/dtoolbase/deletedBufferChain.cxx +++ b/dtool/src/dtoolbase/deletedBufferChain.cxx @@ -39,7 +39,7 @@ allocate(size_t size, TypeHandle type_handle) { assert(size <= _buffer_size); // Determine how much space to allocate. - const size_t alloc_size = _buffer_size + flag_reserved_bytes; + const size_t alloc_size = _buffer_size + flag_reserved_bytes + MemoryHook::get_memory_alignment() - 1; ObjectNode *obj; @@ -69,7 +69,10 @@ allocate(size_t size, TypeHandle type_handle) { // If we get here, the deleted_chain is empty; we have to allocate a new // object from the system pool. - obj = (ObjectNode *)NeverFreeMemory::alloc(alloc_size); + // Allocate memory, and make sure the object starts at the proper alignment. + void *mem = NeverFreeMemory::alloc(alloc_size); + intptr_t pad = ((intptr_t)flag_reserved_bytes - (intptr_t)mem) % MemoryHook::get_memory_alignment(); + obj = (ObjectNode *)((uintptr_t)mem + pad); #ifdef USE_DELETEDCHAINFLAG obj->_flag = DCF_alive; @@ -77,6 +80,10 @@ allocate(size_t size, TypeHandle type_handle) { void *ptr = node_to_buffer(obj); +#ifdef _DEBUG + assert(((uintptr_t)ptr % MemoryHook::get_memory_alignment()) == 0); +#endif + #ifdef DO_MEMORY_USAGE type_handle.inc_memory_usage(TypeHandle::MC_deleted_chain_active, alloc_size); #endif // DO_MEMORY_USAGE diff --git a/dtool/src/dtoolbase/deletedBufferChain.h b/dtool/src/dtoolbase/deletedBufferChain.h index 6df4583ee8..8f82c1abbc 100644 --- a/dtool/src/dtoolbase/deletedBufferChain.h +++ b/dtool/src/dtoolbase/deletedBufferChain.h @@ -95,12 +95,8 @@ private: // Without DELETEDCHAINFLAG, we don't even store the _flag member at all. static const size_t flag_reserved_bytes = 0; -#elif defined(LINMATH_ALIGN) - // With SSE2 alignment, we need all 16 bytes to preserve alignment. - static const size_t flag_reserved_bytes = 16; - #else - // Otherwise, we only need enough space for the Integer itself. + // Otherwise, we need space for the integer. static const size_t flag_reserved_bytes = sizeof(AtomicAdjust::Integer); #endif // USE_DELETEDCHAINFLAG diff --git a/dtool/src/dtoolbase/dtoolbase.h b/dtool/src/dtoolbase/dtoolbase.h index 712e7172e4..b192f24055 100644 --- a/dtool/src/dtoolbase/dtoolbase.h +++ b/dtool/src/dtoolbase/dtoolbase.h @@ -374,7 +374,7 @@ typedef struct _object PyObject; // externally. #define MEMORY_HOOK_DO_ALIGN 1 -#elif defined(IS_OSX) || defined(_WIN64) +#elif (defined(IS_OSX) || defined(_WIN64)) && !defined(__AVX__) // The OS-provided malloc implementation will do the required alignment. #undef MEMORY_HOOK_DO_ALIGN diff --git a/dtool/src/dtoolbase/memoryHook.I b/dtool/src/dtoolbase/memoryHook.I index c54e34c1f3..3d2be05097 100644 --- a/dtool/src/dtoolbase/memoryHook.I +++ b/dtool/src/dtoolbase/memoryHook.I @@ -43,10 +43,16 @@ get_memory_alignment() { #ifdef LINMATH_ALIGN // We require 16-byte alignment of certain structures, to support SSE2. We // don't strictly have to align *everything*, but it's just easier to do so. - const size_t alignment_size = 16; +#ifdef __AVX__ + // Eigen requires 32-byte alignment when using AVX instructions. + const size_t alignment_size = 32; #else - // Otherwise, use word alignment. - const size_t alignment_size = sizeof(void *); + const size_t alignment_size = 16; +#endif +#else + // Otherwise, align to two words. This seems to be pretty standard to the + // point where some code may rely on this being the case. + const size_t alignment_size = sizeof(void *) * 2; #endif return alignment_size; } @@ -64,7 +70,12 @@ get_header_reserved_bytes() { #ifdef LINMATH_ALIGN // If we're doing SSE2 alignment, we must reserve a full 16-byte block, // since anything less than that will spoil the alignment. +#ifdef __AVX__ + // Eigen requires 32-byte alignment when using AVX instructions. + static const size_t header_reserved_bytes = 32; +#else static const size_t header_reserved_bytes = 16; +#endif #elif defined(MEMORY_HOOK_DO_ALIGN) // If we're just aligning to words, we reserve a block as big as two words, @@ -72,8 +83,9 @@ get_header_reserved_bytes() { static const size_t header_reserved_bytes = sizeof(size_t) + sizeof(size_t); #else - // If we're not aligning, we just need space for the word itself. - static const size_t header_reserved_bytes = sizeof(size_t); + // Virtually all allocators align to two words, so we make sure we preserve + // that alignment for the benefit of anyone who relies upon that. + static const size_t header_reserved_bytes = sizeof(void *) * 2; #endif return header_reserved_bytes; diff --git a/dtool/src/dtoolbase/memoryHook.cxx b/dtool/src/dtoolbase/memoryHook.cxx index 1f117dbe60..e6131a5266 100644 --- a/dtool/src/dtoolbase/memoryHook.cxx +++ b/dtool/src/dtoolbase/memoryHook.cxx @@ -53,8 +53,13 @@ // drose: We require 16-byte alignment of certain structures, to // support SSE2. We don't strictly have to align *everything*, but // it's just easier to do so. +#ifdef __AVX__ +// Eigen requires 32-byte alignment when using AVX instructions. +#define MALLOC_ALIGNMENT ((size_t)32U) +#else #define MALLOC_ALIGNMENT ((size_t)16U) #endif +#endif #include "dlmalloc_src.cxx" diff --git a/dtool/src/dtoolbase/neverFreeMemory.I b/dtool/src/dtoolbase/neverFreeMemory.I index 33e5b5851c..2156209616 100644 --- a/dtool/src/dtoolbase/neverFreeMemory.I +++ b/dtool/src/dtoolbase/neverFreeMemory.I @@ -14,6 +14,8 @@ /** * Returns a pointer to a newly-allocated block of memory of the indicated * size. + * + * Please note that the resulting pointer is not aligned to any boundary. */ INLINE void *NeverFreeMemory:: alloc(size_t size) { diff --git a/dtool/src/dtoolbase/neverFreeMemory.cxx b/dtool/src/dtoolbase/neverFreeMemory.cxx index 1ec800a749..fd10b683d8 100644 --- a/dtool/src/dtoolbase/neverFreeMemory.cxx +++ b/dtool/src/dtoolbase/neverFreeMemory.cxx @@ -39,13 +39,9 @@ void *NeverFreeMemory:: ns_alloc(size_t size) { _lock.acquire(); - // We always allocate integer multiples of this many bytes, to guarantee - // this minimum alignment. - static const size_t alignment_size = MemoryHook::get_memory_alignment(); - - // Round up to the next alignment_size. - size = ((size + alignment_size - 1) / alignment_size) * alignment_size; - + //NB: we no longer do alignment here. The only class that uses this is + // DeletedBufferChain, and we can do the alignment potentially more + // efficiently there since we don't end up overallocating as much. _total_used += size; // Look for a page that has sufficient space remaining. diff --git a/dtool/src/dtoolutil/filename.I b/dtool/src/dtoolutil/filename.I index 9ab46bf501..9bc23dfca3 100644 --- a/dtool/src/dtoolutil/filename.I +++ b/dtool/src/dtoolutil/filename.I @@ -38,7 +38,6 @@ Filename(const char *filename) { (*this) = filename; } - /** * */ @@ -84,6 +83,20 @@ Filename(Filename &&from) NOEXCEPT : } #endif // USE_MOVE_SEMANTICS +/** + * Creates an empty Filename. + */ +INLINE Filename:: +Filename() : + _dirname_end(0), + _basename_start(0), + _basename_end(string::npos), + _extension_start(string::npos), + _hash_start(string::npos), + _hash_end(string::npos), + _flags(0) { +} + /** * */ @@ -155,14 +168,6 @@ pattern_filename(const string &filename) { return result; } -/** - * - */ -INLINE Filename:: -~Filename() { -} - - /** * */ @@ -233,7 +238,7 @@ operator = (string &&filename) NOEXCEPT { */ INLINE Filename &Filename:: operator = (Filename &&from) NOEXCEPT { - _filename = MOVE(from._filename); + _filename = move(from._filename); _dirname_end = from._dirname_end; _basename_start = from._basename_start; _basename_end = from._basename_end; diff --git a/dtool/src/dtoolutil/filename.h b/dtool/src/dtoolutil/filename.h index 378bf4fe1c..e0c4b8c414 100644 --- a/dtool/src/dtoolutil/filename.h +++ b/dtool/src/dtoolutil/filename.h @@ -55,20 +55,22 @@ public: }; INLINE Filename(const char *filename); - -PUBLISHED: - INLINE Filename(const string &filename = ""); + INLINE Filename(const string &filename); INLINE Filename(const wstring &filename); INLINE Filename(const Filename ©); - Filename(const Filename &dirname, const Filename &basename); - INLINE ~Filename(); #ifdef USE_MOVE_SEMANTICS INLINE Filename(string &&filename) NOEXCEPT; INLINE Filename(Filename &&from) NOEXCEPT; #endif +PUBLISHED: + INLINE Filename(); + Filename(const Filename &dirname, const Filename &basename); + #ifdef HAVE_PYTHON + EXTENSION(Filename(PyObject *path)); + EXTENSION(PyObject *__reduce__(PyObject *self) const); #endif @@ -118,6 +120,7 @@ PUBLISHED: INLINE char operator [] (size_t n) const; EXTENSION(PyObject *__repr__() const); + EXTENSION(PyObject *__fspath__() const); INLINE string substr(size_t begin) const; INLINE string substr(size_t begin, size_t end) const; diff --git a/dtool/src/interrogate/interfaceMakerPythonNative.cxx b/dtool/src/interrogate/interfaceMakerPythonNative.cxx index d4eea3fdff..a844771fa6 100644 --- a/dtool/src/interrogate/interfaceMakerPythonNative.cxx +++ b/dtool/src/interrogate/interfaceMakerPythonNative.cxx @@ -6142,7 +6142,7 @@ pack_return_value(ostream &out, int indent_level, FunctionRemap *remap, write_python_instance(out, indent_level, return_expr, owns_memory, itype, is_const); } - } else if (TypeManager::is_struct(orig_type->as_pointer_type()->_pointing_at)) { + } else if (TypeManager::is_struct(orig_type->remove_pointer())) { TypeIndex type_index = builder.get_type(TypeManager::unwrap(TypeManager::resolve_type(orig_type)),false); const InterrogateType &itype = idb->get_type(type_index); @@ -6749,6 +6749,8 @@ is_cpp_type_legal(CPPType *in_ctype) { return true; } else if (TypeManager::is_pointer_to_simple(type)) { return true; + } else if (builder.in_forcetype(type->get_local_name(&parser))) { + return true; } else if (TypeManager::is_exported(type)) { return true; } else if (TypeManager::is_pointer_to_PyObject(in_ctype)) { diff --git a/dtool/src/prc/encryptStreamBuf.cxx b/dtool/src/prc/encryptStreamBuf.cxx index acf76f9c0a..6cb0ee6b39 100644 --- a/dtool/src/prc/encryptStreamBuf.cxx +++ b/dtool/src/prc/encryptStreamBuf.cxx @@ -73,8 +73,8 @@ EncryptStreamBuf() { _key_length = encryption_key_length; _iteration_count = encryption_iteration_count; - _read_valid = false; - _write_valid = false; + _read_ctx = NULL; + _write_ctx = NULL; _read_overflow_buffer = NULL; _in_read_overflow_buffer = 0; @@ -110,7 +110,11 @@ open_read(istream *source, bool owns_source, const string &password) { _source = source; _owns_source = owns_source; - _read_valid = false; + + if (_read_ctx != NULL) { + EVP_CIPHER_CTX_free(_read_ctx); + _read_ctx = NULL; + } // Now read the header information. StreamReader sr(_source, false); @@ -143,17 +147,21 @@ open_read(istream *source, bool owns_source, const string &password) { string iv = sr.extract_bytes(iv_length); + _read_ctx = EVP_CIPHER_CTX_new(); + nassertv(_read_ctx != NULL); + // Initialize the context int result; - result = EVP_DecryptInit(&_read_ctx, cipher, NULL, (unsigned char *)iv.data()); + result = EVP_DecryptInit(_read_ctx, cipher, NULL, (unsigned char *)iv.data()); nassertv(result > 0); - result = EVP_CIPHER_CTX_set_key_length(&_read_ctx, key_length); + result = EVP_CIPHER_CTX_set_key_length(_read_ctx, key_length); if (result <= 0) { prc_cat.error() << "Invalid key length " << key_length * 8 << " bits for algorithm " << OBJ_nid2sn(nid) << "\n"; - EVP_CIPHER_CTX_cleanup(&_read_ctx); + EVP_CIPHER_CTX_free(_read_ctx); + _read_ctx = NULL; return; } @@ -167,11 +175,9 @@ open_read(istream *source, bool owns_source, const string &password) { nassertv(result > 0); // Store the key within the context. - result = EVP_DecryptInit(&_read_ctx, NULL, key, NULL); + result = EVP_DecryptInit(_read_ctx, NULL, key, NULL); nassertv(result > 0); - _read_valid = true; - _read_overflow_buffer = new unsigned char[_read_block_size]; _in_read_overflow_buffer = 0; thread_consider_yield(); @@ -182,9 +188,9 @@ open_read(istream *source, bool owns_source, const string &password) { */ void EncryptStreamBuf:: close_read() { - if (_read_valid) { - EVP_CIPHER_CTX_cleanup(&_read_ctx); - _read_valid = false; + if (_read_ctx != NULL) { + EVP_CIPHER_CTX_free(_read_ctx); + _read_ctx = NULL; } if (_read_overflow_buffer != (unsigned char *)NULL) { @@ -211,7 +217,6 @@ open_write(ostream *dest, bool owns_dest, const string &password) { close_write(); _dest = dest; _owns_dest = owns_dest; - _write_valid = false; const EVP_CIPHER *cipher = EVP_get_cipherbyname(_algorithm.c_str()); @@ -220,21 +225,23 @@ open_write(ostream *dest, bool owns_dest, const string &password) { prc_cat.error() << "Unknown encryption algorithm: " << _algorithm << "\n"; return; - }; + } int nid = EVP_CIPHER_nid(cipher); int iv_length = EVP_CIPHER_iv_length(cipher); _write_block_size = EVP_CIPHER_block_size(cipher); - unsigned char *iv = (unsigned char *)alloca(iv_length); - // Generate a random IV. It doesn't need to be cryptographically secure, // just unique. + unsigned char *iv = (unsigned char *)alloca(iv_length); RAND_pseudo_bytes(iv, iv_length); + _write_ctx = EVP_CIPHER_CTX_new(); + nassertv(_write_ctx != NULL); + int result; - result = EVP_EncryptInit(&_write_ctx, cipher, NULL, iv); + result = EVP_EncryptInit(_write_ctx, cipher, NULL, iv); nassertv(result > 0); // Store the appropriate key length in the context. @@ -242,12 +249,13 @@ open_write(ostream *dest, bool owns_dest, const string &password) { if (key_length == 0) { key_length = EVP_CIPHER_key_length(cipher); } - result = EVP_CIPHER_CTX_set_key_length(&_write_ctx, key_length); + result = EVP_CIPHER_CTX_set_key_length(_write_ctx, key_length); if (result <= 0) { prc_cat.error() << "Invalid key length " << key_length * 8 << " bits for algorithm " << OBJ_nid2sn(nid) << "\n"; - EVP_CIPHER_CTX_cleanup(&_write_ctx); + EVP_CIPHER_CTX_free(_write_ctx); + _write_ctx = NULL; return; } @@ -271,7 +279,7 @@ open_write(ostream *dest, bool owns_dest, const string &password) { nassertv(result > 0); // Store the key in the context. - result = EVP_EncryptInit(&_write_ctx, NULL, key, NULL); + result = EVP_EncryptInit(_write_ctx, NULL, key, NULL); nassertv(result > 0); // Now write the header information to the stream. @@ -284,7 +292,6 @@ open_write(ostream *dest, bool owns_dest, const string &password) { sw.add_uint16((uint16_t)count); sw.append_data(iv, iv_length); - _write_valid = true; thread_consider_yield(); } @@ -298,15 +305,16 @@ close_write() { write_chars(pbase(), n); pbump(-(int)n); - if (_write_valid) { + if (_write_ctx != NULL) { unsigned char *write_buffer = (unsigned char *)alloca(_write_block_size); int bytes_written = 0; - EVP_EncryptFinal(&_write_ctx, write_buffer, &bytes_written); + EVP_EncryptFinal(_write_ctx, write_buffer, &bytes_written); thread_consider_yield(); _dest->write((const char *)write_buffer, bytes_written); - _write_valid = false; + EVP_CIPHER_CTX_free(_write_ctx); + _write_ctx = NULL; } if (_owns_dest) { @@ -418,7 +426,7 @@ read_chars(char *start, size_t length) { do { // Get more bytes from the stream. - if (!_read_valid) { + if (_read_ctx == NULL) { return 0; } @@ -429,20 +437,21 @@ read_chars(char *start, size_t length) { int result; if (source_length != 0) { result = - EVP_DecryptUpdate(&_read_ctx, read_buffer, &bytes_read, + EVP_DecryptUpdate(_read_ctx, read_buffer, &bytes_read, source_buffer, source_length); } else { result = - EVP_DecryptFinal(&_read_ctx, read_buffer, &bytes_read); - _read_valid = false; + EVP_DecryptFinal(_read_ctx, read_buffer, &bytes_read); + EVP_CIPHER_CTX_free(_read_ctx); + _read_ctx = NULL; } if (result <= 0) { prc_cat.error() << "Error decrypting stream.\n"; - if (_read_valid) { - EVP_CIPHER_CTX_cleanup(&_read_ctx); - _read_valid = false; + if (_read_ctx != NULL) { + EVP_CIPHER_CTX_free(_read_ctx); + _read_ctx = NULL; } } thread_consider_yield(); @@ -472,13 +481,13 @@ read_chars(char *start, size_t length) { */ void EncryptStreamBuf:: write_chars(const char *start, size_t length) { - if (_write_valid && length != 0) { + if (_write_ctx != NULL && length != 0) { size_t max_write_buffer = length + _write_block_size; unsigned char *write_buffer = (unsigned char *)alloca(max_write_buffer); int bytes_written = 0; int result = - EVP_EncryptUpdate(&_write_ctx, write_buffer, &bytes_written, + EVP_EncryptUpdate(_write_ctx, write_buffer, &bytes_written, (unsigned char *)start, length); if (result <= 0) { prc_cat.error() diff --git a/dtool/src/prc/encryptStreamBuf.h b/dtool/src/prc/encryptStreamBuf.h index 0542457267..4861a89240 100644 --- a/dtool/src/prc/encryptStreamBuf.h +++ b/dtool/src/prc/encryptStreamBuf.h @@ -64,14 +64,12 @@ private: int _key_length; int _iteration_count; - bool _read_valid; - EVP_CIPHER_CTX _read_ctx; + EVP_CIPHER_CTX *_read_ctx; size_t _read_block_size; unsigned char *_read_overflow_buffer; size_t _in_read_overflow_buffer; - bool _write_valid; - EVP_CIPHER_CTX _write_ctx; + EVP_CIPHER_CTX *_write_ctx; size_t _write_block_size; }; diff --git a/dtool/src/prc/notify.cxx b/dtool/src/prc/notify.cxx index 741f3a0dd6..f97b7d78ac 100644 --- a/dtool/src/prc/notify.cxx +++ b/dtool/src/prc/notify.cxx @@ -343,6 +343,9 @@ assert_failure(const char *expression, int line, // guarantee it has already been constructed. ALIGN_16BYTE ConfigVariableBool assert_abort("assert-abort", false); if (assert_abort) { + // Make sure the error message has been flushed to the output. + nout.flush(); + #ifdef WIN32 // How to trigger an exception in VC++ that offers to take us into the // debugger? abort() doesn't do it. We used to be able to assert(false), diff --git a/dtool/src/pystub/pystub.cxx b/dtool/src/pystub/pystub.cxx index 4f2e74144a..d0d6a4ca1a 100644 --- a/dtool/src/pystub/pystub.cxx +++ b/dtool/src/pystub/pystub.cxx @@ -149,12 +149,14 @@ extern "C" { EXPCL_PYSTUB int PyUnicodeUCS2_FromStringAndSize(...); EXPCL_PYSTUB int PyUnicodeUCS2_FromWideChar(...); EXPCL_PYSTUB int PyUnicodeUCS2_AsWideChar(...); + EXPCL_PYSTUB int PyUnicodeUCS2_AsWideCharString(...); EXPCL_PYSTUB int PyUnicodeUCS2_GetSize(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromFormat(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromString(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromStringAndSize(...); EXPCL_PYSTUB int PyUnicodeUCS4_FromWideChar(...); EXPCL_PYSTUB int PyUnicodeUCS4_AsWideChar(...); + EXPCL_PYSTUB int PyUnicodeUCS4_AsWideCharString(...); EXPCL_PYSTUB int PyUnicodeUCS4_GetSize(...); EXPCL_PYSTUB int PyUnicode_AsUTF8(...); EXPCL_PYSTUB int PyUnicode_AsUTF8AndSize(...); @@ -349,12 +351,14 @@ int PyUnicodeUCS2_FromString(...) { return 0; } int PyUnicodeUCS2_FromStringAndSize(...) { return 0; } int PyUnicodeUCS2_FromWideChar(...) { return 0; } int PyUnicodeUCS2_AsWideChar(...) { return 0; } +int PyUnicodeUCS2_AsWideCharString(...) { return 0; } int PyUnicodeUCS2_GetSize(...) { return 0; } int PyUnicodeUCS4_FromFormat(...) { return 0; } int PyUnicodeUCS4_FromString(...) { return 0; } int PyUnicodeUCS4_FromStringAndSize(...) { return 0; } int PyUnicodeUCS4_FromWideChar(...) { return 0; } int PyUnicodeUCS4_AsWideChar(...) { return 0; } +int PyUnicodeUCS4_AsWideCharString(...) { return 0; } int PyUnicodeUCS4_GetSize(...) { return 0; } int PyUnicode_AsUTF8(...) { return 0; } int PyUnicode_AsUTF8AndSize(...) { return 0; } diff --git a/makepanda/installer.nsi b/makepanda/installer.nsi index d14cb44da9..d934cc4ee0 100755 --- a/makepanda/installer.nsi +++ b/makepanda/installer.nsi @@ -166,6 +166,10 @@ SectionGroup "Panda3D Libraries" SetOutPath $INSTDIR\models File /r /x CVS "${BUILT}\models\*" + SetDetailsPrint both + DetailPrint "Installing optional components..." + SetDetailsPrint listonly + RMDir /r "$SMPROGRAMS\${TITLE}" CreateDirectory "$SMPROGRAMS\${TITLE}" SectionEnd diff --git a/makepanda/makepanda.py b/makepanda/makepanda.py index 7b3ff0c3a2..a93e12fdf4 100755 --- a/makepanda/makepanda.py +++ b/makepanda/makepanda.py @@ -39,6 +39,7 @@ import sys COMPILER=0 INSTALLER=0 +WHEEL=0 GENMAN=0 COMPRESSOR="zlib" THREADCOUNT=0 @@ -87,8 +88,8 @@ PkgListSet(["PYTHON", "DIRECT", # Python support "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 + "X11", # Unix platform support + "PANDATOOL", "PVIEW", "DEPLOYTOOLS", "DIRECTSCRIPTS",# Toolchain "SKEL", # Example SKEL project "PANDAFX", # Some distortion special lenses "PANDAPARTICLESYSTEM", # Built in particle system @@ -124,6 +125,7 @@ def usage(problem): print(" --verbose (print out more information)") print(" --runtime (build a runtime build instead of an SDK build)") print(" --installer (build an installer)") + print(" --wheel (build a pip-installable .whl)") print(" --optimize X (optimization level can be 1,2,3,4)") print(" --version X (set the panda version number)") print(" --lzma (use lzma compression when building Windows installer)") @@ -159,13 +161,13 @@ def usage(problem): os._exit(1) def parseopts(args): - global INSTALLER,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION + global INSTALLER,WHEEL,RTDIST,RUNTIME,GENMAN,DISTRIBUTOR,VERSION global COMPRESSOR,THREADCOUNT,OSXTARGET,OSX_ARCHS,HOST_URL global DEBVERSION,RPMRELEASE,GIT_COMMIT,P3DSUFFIX,RTDIST_VERSION global STRDXSDKVERSION, WINDOWS_SDK, MSVC_VERSION, BOOUSEINTELCOMPILER longopts = [ "help","distributor=","verbose","runtime","osxtarget=", - "optimize=","everything","nothing","installer","rtdist","nocolor", + "optimize=","everything","nothing","installer","wheel","rtdist","nocolor", "version=","lzma","no-python","threads=","outputdir=","override=", "static","host=","debversion=","rpmrelease=","p3dsuffix=","rtdist-version=", "directx-sdk=", "windows-sdk=", "msvc-version=", "clean", "use-icl", @@ -188,6 +190,7 @@ def parseopts(args): if (option=="--help"): raise Exception elif (option=="--optimize"): optimize=value elif (option=="--installer"): INSTALLER=1 + elif (option=="--wheel"): WHEEL=1 elif (option=="--verbose"): SetVerbose(True) elif (option=="--distributor"): DISTRIBUTOR=value elif (option=="--rtdist"): RTDIST=1 @@ -416,9 +419,18 @@ if (RUNTIME): if (INSTALLER and RTDIST): exit("Cannot build an installer for the rtdist build!") +if (WHEEL and RUNTIME): + exit("Cannot build a wheel for the runtime build!") + +if (WHEEL and RTDIST): + exit("Cannot build a wheel for the rtdist build!") + if (INSTALLER) and (PkgSkip("PYTHON")) and (not RUNTIME) and GetTarget() == 'windows': exit("Cannot build installer on Windows without python") +if WHEEL and PkgSkip("PYTHON"): + exit("Cannot build wheel without Python") + if (RTDIST) and (PkgSkip("WX") and PkgSkip("FLTK")): exit("Cannot build rtdist without wx or fltk") @@ -504,9 +516,6 @@ IncDirectory("ALWAYS", GetOutputDir()+"/include") if (COMPILER == "MSVC"): PkgDisable("X11") - PkgDisable("XRANDR") - PkgDisable("XF86DGA") - PkgDisable("XCURSOR") PkgDisable("GLES") PkgDisable("GLES2") PkgDisable("EGL") @@ -796,9 +805,13 @@ if (COMPILER=="GCC"): SmartPkgEnable("JPEG", "", ("jpeg"), "jpeglib.h") SmartPkgEnable("PNG", "libpng", ("png"), "png.h", tool = "libpng-config") - if GetTarget() == "darwin" and not PkgSkip("FFMPEG"): - LibName("FFMPEG", "-Wl,-read_only_relocs,suppress") - LibName("FFMPEG", "-framework VideoDecodeAcceleration") + if not PkgSkip("FFMPEG"): + if GetTarget() == "darwin": + LibName("FFMPEG", "-Wl,-read_only_relocs,suppress") + LibName("FFMPEG", "-framework VideoDecodeAcceleration") + elif os.path.isfile(GetThirdpartyDir() + "ffmpeg/lib/libavcodec.a"): + # Needed when linking ffmpeg statically on Linux. + LibName("FFMPEG", "-Wl,-Bsymbolic") cv_lib = ChooseLib(("opencv_core", "cv"), "OPENCV") if cv_lib == "opencv_core": @@ -828,12 +841,9 @@ if (COMPILER=="GCC"): if GetTarget() != 'darwin': # CgGL is covered by the Cg framework, and we don't need X11 components on OSX if not PkgSkip("NVIDIACG") and not RUNTIME: - SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h") + SmartPkgEnable("CGGL", "", ("CgGL"), "Cg/cgGL.h", thirdparty_dir = "nvidiacg") if not RUNTIME: SmartPkgEnable("X11", "x11", "X11", ("X11", "X11/Xlib.h")) - SmartPkgEnable("XRANDR", "xrandr", "Xrandr", "X11/extensions/Xrandr.h") - 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 @@ -1680,6 +1690,11 @@ def CompileLink(dll, obj, opts): if LDFLAGS != "": cmd += " " + LDFLAGS + # Don't link libraries with Python. + if "PYTHON" in opts and GetOrigExt(dll) != ".exe" and not RTDIST: + opts = opts[:] + opts.remove("PYTHON") + for (opt, dir) in LIBDIRECTORIES: if (opt=="ALWAYS") or (opt in opts): cmd += ' -L' + BracketNameWithQuotes(dir) @@ -2182,9 +2197,6 @@ DTOOL_CONFIG=[ ("PHAVE_STDINT_H", '1', '1'), ("HAVE_RTTI", '1', '1'), ("HAVE_X11", 'UNDEF', '1'), - ("HAVE_XRANDR", 'UNDEF', '1'), - ("HAVE_XF86DGA", 'UNDEF', '1'), - ("HAVE_XCURSOR", 'UNDEF', '1'), ("IS_LINUX", 'UNDEF', '1'), ("IS_OSX", 'UNDEF', 'UNDEF'), ("IS_FREEBSD", 'UNDEF', 'UNDEF'), @@ -2289,9 +2301,6 @@ def WriteConfigSettings(): dtool_config["PHAVE_SYS_MALLOC_H"] = '1' dtool_config["HAVE_OPENAL_FRAMEWORK"] = '1' dtool_config["HAVE_X11"] = 'UNDEF' # We might have X11, but we don't need it. - dtool_config["HAVE_XRANDR"] = 'UNDEF' - dtool_config["HAVE_XF86DGA"] = 'UNDEF' - dtool_config["HAVE_XCURSOR"] = 'UNDEF' dtool_config["HAVE_GLX"] = 'UNDEF' dtool_config["IS_LINUX"] = 'UNDEF' dtool_config["HAVE_VIDEO4LINUX"] = 'UNDEF' @@ -3328,6 +3337,7 @@ if (not RUNTIME): TargetAdd('interrogate.exe', input='libp3cppParser.ilb') TargetAdd('interrogate.exe', input=COMMON_DTOOL_LIBS) TargetAdd('interrogate.exe', input='libp3interrogatedb.dll') + TargetAdd('interrogate.exe', input='libp3pystub.lib') TargetAdd('interrogate.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) TargetAdd('interrogate_module_interrogate_module.obj', opts=OPTS, input='interrogate_module.cxx') @@ -3335,6 +3345,7 @@ if (not RUNTIME): TargetAdd('interrogate_module.exe', input='libp3cppParser.ilb') TargetAdd('interrogate_module.exe', input=COMMON_DTOOL_LIBS) TargetAdd('interrogate_module.exe', input='libp3interrogatedb.dll') + TargetAdd('interrogate_module.exe', input='libp3pystub.lib') TargetAdd('interrogate_module.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) if (not RTDIST): @@ -3343,6 +3354,7 @@ if (not RUNTIME): TargetAdd('parse_file.exe', input='libp3cppParser.ilb') TargetAdd('parse_file.exe', input=COMMON_DTOOL_LIBS) TargetAdd('parse_file.exe', input='libp3interrogatedb.dll') + TargetAdd('parse_file.exe', input='libp3pystub.lib') TargetAdd('parse_file.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) # @@ -3366,6 +3378,7 @@ if (not RTDIST and not RUNTIME): TargetAdd('test_interrogate.exe', input='test_interrogate_test_interrogate.obj') TargetAdd('test_interrogate.exe', input='libp3interrogatedb.dll') TargetAdd('test_interrogate.exe', input=COMMON_DTOOL_LIBS) + TargetAdd('test_interrogate.exe', input='libp3pystub.lib') TargetAdd('test_interrogate.exe', opts=['ADVAPI', 'OPENSSL', 'WINSHELL', 'WINGDI', 'WINUSER']) # @@ -4569,7 +4582,7 @@ if (GetTarget() not in ['windows', 'darwin'] and PkgSkip("GL")==0 and PkgSkip("X TargetAdd('libpandagl.dll', input='p3glgsg_glgsg.obj') TargetAdd('libpandagl.dll', input='p3glxdisplay_composite1.obj') TargetAdd('libpandagl.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR']) + TargetAdd('libpandagl.dll', opts=['MODULE', 'GL', 'NVIDIACG', 'CGGL', 'X11']) # # DIRECTORY: panda/src/cocoadisplay/ @@ -4644,7 +4657,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES")==0 and PkgSkip("X11")==0 and not RUNTI TargetAdd('libpandagles.dll', input='p3glesgsg_glesgsg.obj') TargetAdd('libpandagles.dll', input='pandagles_egldisplay_composite1.obj') TargetAdd('libpandagles.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libpandagles.dll', opts=['MODULE', 'GLES', 'EGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR']) + TargetAdd('libpandagles.dll', opts=['MODULE', 'GLES', 'EGL', 'X11']) # # DIRECTORY: panda/src/egldisplay/ @@ -4662,7 +4675,7 @@ if (PkgSkip("EGL")==0 and PkgSkip("GLES2")==0 and PkgSkip("X11")==0 and not RUNT TargetAdd('libpandagles2.dll', input='p3gles2gsg_gles2gsg.obj') TargetAdd('libpandagles2.dll', input='pandagles2_egldisplay_composite1.obj') TargetAdd('libpandagles2.dll', input=COMMON_PANDA_LIBS) - TargetAdd('libpandagles2.dll', opts=['MODULE', 'GLES2', 'EGL', 'X11', 'XRANDR', 'XF86DGA', 'XCURSOR']) + TargetAdd('libpandagles2.dll', opts=['MODULE', 'GLES2', 'EGL', 'X11']) # # DIRECTORY: panda/src/ode/ @@ -4958,7 +4971,7 @@ if (not RUNTIME and (GetTarget() in ('windows', 'darwin') or PkgSkip("X11")==0) TargetAdd('libp3tinydisplay.dll', opts=['WINIMM', 'WINGDI', 'WINKERNEL', 'WINOLDNAMES', 'WINUSER', 'WINMM']) else: TargetAdd('libp3tinydisplay.dll', input='p3x11display_composite1.obj') - TargetAdd('libp3tinydisplay.dll', opts=['X11', 'XRANDR', 'XF86DGA', 'XCURSOR']) + TargetAdd('libp3tinydisplay.dll', opts=['X11']) TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite1.obj') TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_composite2.obj') TargetAdd('libp3tinydisplay.dll', input='p3tinydisplay_ztriangle_1.obj') @@ -4976,7 +4989,7 @@ if (PkgSkip("DIRECT")==0): OPTS=['DIR:direct/src/directbase', 'PYTHON'] TargetAdd('p3directbase_directbase.obj', opts=OPTS+['BUILDING:DIRECT'], input='directbase.cxx') - if (PkgSkip("PYTHON")==0 and not RTDIST and not RUNTIME): + if not PkgSkip("PYTHON") and not RTDIST and not RUNTIME and not PkgSkip("DIRECTSCRIPTS"): DefSymbol("BUILDING:PACKPANDA", "IMPORT_MODULE", "direct.directscripts.packpanda") TargetAdd('packpanda.obj', opts=OPTS+['BUILDING:PACKPANDA'], input='ppython.cxx') TargetAdd('packpanda.exe', input='packpanda.obj') @@ -5151,6 +5164,7 @@ if (PkgSkip("PYTHON")==0 and PkgSkip("DIRECT")==0 and not RTDIST and not RUNTIME TargetAdd('p3dcparse.exe', input='dcparse_dcparse.obj') TargetAdd('p3dcparse.exe', input='libp3direct.dll') TargetAdd('p3dcparse.exe', input=COMMON_PANDA_LIBS) + TargetAdd('p3dcparse.exe', input='libp3pystub.lib') TargetAdd('p3dcparse.exe', opts=['ADVAPI', 'PYTHON']) # @@ -7257,6 +7271,11 @@ try: MakeInstallerFreeBSD() else: exit("Do not know how to make an installer for this platform") + + if WHEEL: + ProgressOutput(100.0, "Building wheel") + from makewheel import makewheel + makewheel(VERSION, GetOutputDir()) finally: SaveDependencyCache() diff --git a/makepanda/makewheel.py b/makepanda/makewheel.py new file mode 100644 index 0000000000..6d3b6fdf72 --- /dev/null +++ b/makepanda/makewheel.py @@ -0,0 +1,598 @@ +""" +Generates a wheel (.whl) file from the output of makepanda. + +Since the wheel requires special linking, this will only work if compiled with +the `--wheel` parameter. + +Please keep this file work with Panda3D 1.9 until that reaches EOL. +""" +from __future__ import print_function, unicode_literals +from distutils.util import get_platform +import json + +import sys +import os +from os.path import join +import shutil +import zipfile +import hashlib +import tempfile +import subprocess +from distutils.sysconfig import get_config_var +from optparse import OptionParser +from makepandacore import ColorText, LocateBinary, ParsePandaVersion, GetExtensionSuffix, SetVerbose, GetVerbose +from base64 import urlsafe_b64encode + + +default_platform = get_platform() + +if default_platform.startswith("linux-"): + # Is this manylinux1? + if os.path.isfile("/lib/libc-2.5.so") and os.path.isdir("/opt/python"): + default_platform = default_platform.replace("linux", "manylinux1") + + +def get_abi_tag(): + if sys.version_info >= (3, 0): + soabi = get_config_var('SOABI') + if soabi and soabi.startswith('cpython-'): + return 'cp' + soabi.split('-')[1] + elif soabi: + return soabi.replace('.', '_').replace('-', '_') + + soabi = 'cp%d%d' % (sys.version_info[:2]) + + debug_flag = get_config_var('Py_DEBUG') + if (debug_flag is None and hasattr(sys, 'gettotalrefcount')) or debug_flag: + soabi += 'd' + + malloc_flag = get_config_var('WITH_PYMALLOC') + if malloc_flag is None or malloc_flag: + soabi += 'm' + + if sys.version_info < (3, 3): + usize = get_config_var('Py_UNICODE_SIZE') + if (usize is None and sys.maxunicode == 0x10ffff) or usize == 4: + soabi += 'u' + + return soabi + + +def is_exe_file(path): + return os.path.isfile(path) and path.lower().endswith('.exe') + + +def is_elf_file(path): + base = os.path.basename(path) + return os.path.isfile(path) and '.' not in base and \ + open(path, 'rb').read(4) == b'\x7FELF' + + +def is_mach_o_file(path): + base = os.path.basename(path) + return os.path.isfile(path) and '.' not in base and \ + open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA', + b'\xFE\xED\xFA\xCE', b'\xCE\xFA\xED\xFE', + b'\xFE\xED\xFA\xCF', b'\xCF\xFA\xED\xFE') + +def is_fat_file(path): + return os.path.isfile(path) and \ + open(path, 'rb').read(4) in (b'\xCA\xFE\xBA\xBE', b'\xBE\xBA\xFE\bCA') + + +if sys.platform in ('win32', 'cygwin'): + is_executable = is_exe_file +elif sys.platform == 'darwin': + is_executable = is_mach_o_file +else: + is_executable = is_elf_file + + +# Other global parameters +PY_VERSION = "cp{0}{1}".format(*sys.version_info) +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"] + +WHEEL_DATA = """Wheel-Version: 1.0 +Generator: makepanda +Root-Is-Purelib: false +Tag: {0}-{1}-{2} +""" + +METADATA = { + "license": "BSD", + "name": "Panda3D", + "metadata_version": "2.0", + "generator": "makepanda", + "summary": "Panda3D is a game engine, a framework for 3D rendering and " + "game development for Python and C++ programs.", + "extensions": { + "python.details": { + "project_urls": { + "Home": "https://www.panda3d.org/" + }, + "document_names": { + "license": "LICENSE.txt" + }, + "contacts": [ + { + "role": "author", + "email": "etc-panda3d@lists.andrew.cmu.edu", + "name": "Panda3D Team" + } + ] + } + }, + "classifiers": [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: End Users/Desktop", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: C++", + "Programming Language :: Python", + "Topic :: Games/Entertainment", + "Topic :: Multimedia", + "Topic :: Multimedia :: Graphics", + "Topic :: Multimedia :: Graphics :: 3D Rendering" + ] +} + +PANDA3D_TOOLS_INIT = """import os, sys +import panda3d + +if sys.platform in ('win32', 'cygwin'): + path_var = 'PATH' +elif sys.platform == 'darwin': + path_var = 'DYLD_LIBRARY_PATH' +else: + path_var = 'LD_LIBRARY_PATH' + +dir = os.path.dirname(panda3d.__file__) +del panda3d +if not os.environ.get(path_var): + os.environ[path_var] = dir +else: + os.environ[path_var] = dir + os.pathsep + os.environ[path_var] + +del os, sys, path_var, dir + + +def _exec_tool(tool): + import os, sys + from subprocess import Popen + tools_dir = os.path.dirname(__file__) + handle = Popen(sys.argv, executable=os.path.join(tools_dir, tool)) + try: + try: + return handle.wait() + except KeyboardInterrupt: + # Give the program a chance to handle the signal gracefully. + return handle.wait() + except: + handle.kill() + handle.wait() + raise + +# Register all the executables in this directory as global functions. +{0} +""" + + +def parse_dependencies_windows(data): + """ Parses the given output from dumpbin /dependents to determine the list + of dll's this executable file depends on. """ + + lines = data.splitlines() + li = 0 + while li < len(lines): + line = lines[li] + li += 1 + if line.find(' has the following dependencies') != -1: + break + + if li < len(lines): + line = lines[li] + if line.strip() == '': + # Skip a blank line. + li += 1 + + # Now we're finding filenames, until the next blank line. + filenames = [] + while li < len(lines): + line = lines[li] + li += 1 + line = line.strip() + if line == '': + # We're done. + return filenames + filenames.append(line) + + # At least we got some data. + return filenames + + +def parse_dependencies_unix(data): + """ Parses the given output from otool -XL or ldd to determine the list of + libraries this executable file depends on. """ + + lines = data.splitlines() + filenames = [] + for l in lines: + l = l.strip() + if l != "statically linked": + filenames.append(l.split(' ', 1)[0]) + return filenames + + +def scan_dependencies(pathname): + """ Checks the named file for DLL dependencies, and adds any appropriate + dependencies found into pluginDependencies and dependentFiles. """ + + if sys.platform == "darwin": + command = ['otool', '-XL', pathname] + elif sys.platform in ("win32", "cygwin"): + command = ['dumpbin', '/dependents', pathname] + else: + command = ['ldd', pathname] + + process = subprocess.Popen(command, stdout=subprocess.PIPE, universal_newlines=True) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + raise subprocess.CalledProcessError(retcode, command[0], output=output) + filenames = None + + if sys.platform in ("win32", "cygwin"): + filenames = parse_dependencies_windows(output) + else: + filenames = parse_dependencies_unix(output) + + if filenames is None: + sys.exit("Unable to determine dependencies from %s" % (pathname)) + + if sys.platform == "darwin" and len(filenames) > 0: + # Filter out the library ID. + if os.path.basename(filenames[0]).split('.', 1)[0] == os.path.basename(pathname).split('.', 1)[0]: + del filenames[0] + + return filenames + + +class WheelFile(object): + def __init__(self, name, version, platform): + self.name = name + self.version = version + self.platform = platform + + wheel_name = "{0}-{1}-{2}-{3}-{4}.whl".format( + name, version, PY_VERSION, ABI_TAG, platform) + + print("Writing %s" % (wheel_name)) + self.zip_file = zipfile.ZipFile(wheel_name, 'w', zipfile.ZIP_DEFLATED) + self.records = [] + + # Used to locate dependency libraries. + self.lib_path = [] + self.dep_paths = {} + + def consider_add_dependency(self, target_path, dep, search_path=None): + """Considers adding a dependency library. + Returns the target_path if it was added, which may be different from + target_path if it was already added earlier, or None if it wasn't.""" + + if dep in self.dep_paths: + # Already considered this. + return self.dep_paths[dep] + + self.dep_paths[dep] = None + + if dep.lower().startswith("python") or os.path.basename(dep).startswith("libpython"): + # Don't include the Python library. + return + + if sys.platform == "darwin" and dep.endswith(".so"): + # Temporary hack for 1.9, which had link deps on modules. + return + + source_path = None + + if search_path is None: + search_path = self.lib_path + + for lib_dir in search_path: + # Ignore static stuff. + path = os.path.join(lib_dir, dep) + if os.path.isfile(path): + source_path = os.path.normpath(path) + break + + if not source_path: + # Couldn't find library in the panda3d lib dir. + #print("Ignoring %s" % (dep)) + return + + self.dep_paths[dep] = target_path + self.write_file(target_path, source_path) + return target_path + + def write_file(self, target_path, source_path): + """Adds the given file to the .whl file.""" + + # If this is a .so file, we should set the rpath appropriately. + temp = None + ext = os.path.splitext(source_path)[1] + if ext in ('.so', '.dylib') or '.so.' in os.path.basename(source_path) or \ + (not ext and is_executable(source_path)): + # Scan and add Unix dependencies. + deps = scan_dependencies(source_path) + for dep in deps: + # Only include dependencies with relative path. Otherwise we + # end up overwriting system files like /lib/ld-linux.so.2! + # Yes, it happened to me. + if '/' not in dep: + target_dep = os.path.dirname(target_path) + '/' + dep + self.consider_add_dependency(target_dep, dep) + + suffix = '' + if '.so' in os.path.basename(source_path): + suffix = '.so' + elif ext == '.dylib': + suffix = '.dylib' + + temp = tempfile.NamedTemporaryFile(suffix=suffix, prefix='whl', delete=False) + + # On macOS, if no fat wheel was requested, extract the right architecture. + if sys.platform == "darwin" and is_fat_file(source_path) and not self.platform.endswith("_intel"): + if self.platform.endswith("_x86_64"): + arch = 'x86_64' + else: + arch = self.platform.split('_')[-1] + subprocess.call(['lipo', source_path, '-extract', arch, '-output', temp.name]) + else: + # Otherwise, just copy it over. + temp.write(open(source_path, 'rb').read()) + + temp.write(open(source_path, 'rb').read()) + os.fchmod(temp.fileno(), os.fstat(temp.fileno()).st_mode | 0o111) + temp.close() + + # Fix things like @loader_path/../lib references + if sys.platform == "darwin": + loader_path = [os.path.dirname(source_path)] + for dep in deps: + if '@loader_path' not in dep: + continue + + dep_path = dep.replace('@loader_path', '.') + target_dep = os.path.dirname(target_path) + '/' + os.path.basename(dep) + target_dep = self.consider_add_dependency(target_dep, dep_path, loader_path) + if not target_dep: + # It won't be included, so no use adjusting the path. + continue + + new_dep = os.path.join('@loader_path', os.path.relpath(target_dep, os.path.dirname(target_path))) + subprocess.call(["install_name_tool", "-change", dep, new_dep, temp.name]) + else: + subprocess.call(["strip", "-s", temp.name]) + subprocess.call(["patchelf", "--set-rpath", "$ORIGIN", temp.name]) + + source_path = temp.name + + ext = ext.lower() + if ext in ('.dll', '.pyd', '.exe'): + # Scan and add Win32 dependencies. + for dep in scan_dependencies(source_path): + target_dep = os.path.dirname(target_path) + '/' + dep + self.consider_add_dependency(target_dep, dep) + + # Calculate the SHA-256 hash and size. + sha = hashlib.sha256() + fp = open(source_path, 'rb') + size = 0 + data = fp.read(1024 * 1024) + while data: + size += len(data) + sha.update(data) + data = fp.read(1024 * 1024) + fp.close() + + # Save it in PEP-0376 format for writing out later. + digest = str(urlsafe_b64encode(sha.digest())) + digest = digest.rstrip('=') + self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, size)) + + if GetVerbose(): + print("Adding %s from %s" % (target_path, source_path)) + self.zip_file.write(source_path, target_path) + + #if temp: + # os.unlink(temp.name) + + def write_file_data(self, target_path, source_data): + """Adds the given file from a string.""" + + sha = hashlib.sha256() + sha.update(source_data.encode()) + digest = str(urlsafe_b64encode(sha.digest())) + digest = digest.rstrip('=') + self.records.append("{0},sha256={1},{2}\n".format(target_path, digest, len(source_data))) + + if GetVerbose(): + print("Adding %s from data" % target_path) + self.zip_file.writestr(target_path, source_data) + + def write_directory(self, target_dir, source_dir): + """Adds the given directory recursively to the .whl file.""" + + for root, dirs, files in os.walk(source_dir): + for file in files: + if os.path.splitext(file)[1] in EXCLUDE_EXT: + continue + + source_path = os.path.join(root, file) + target_path = os.path.join(target_dir, os.path.relpath(source_path, source_dir)) + target_path = target_path.replace('\\', '/') + self.write_file(target_path, source_path) + + def close(self): + # Write the RECORD file. + record_file = "{0}-{1}.dist-info/RECORD".format(self.name, self.version) + self.records.append(record_file + ",,\n") + + self.zip_file.writestr(record_file, "".join(self.records)) + self.zip_file.close() + + +def makewheel(version, output_dir, platform=default_platform): + if sys.platform not in ("win32", "darwin") and not sys.platform.startswith("cygwin"): + if not LocateBinary("patchelf"): + raise Exception("patchelf is required when building a Linux wheel.") + + platform = platform.replace('-', '_').replace('.', '_') + + # Global filepaths + panda3d_dir = join(output_dir, "panda3d") + pandac_dir = join(output_dir, "pandac") + direct_dir = join(output_dir, "direct") + models_dir = join(output_dir, "models") + etc_dir = join(output_dir, "etc") + bin_dir = join(output_dir, "bin") + if sys.platform == "win32": + libs_dir = join(output_dir, "bin") + else: + libs_dir = join(output_dir, "lib") + license_src = "LICENSE" + readme_src = "README.md" + + # Update relevant METADATA entries + METADATA['version'] = version + version_classifiers = [ + "Programming Language :: Python :: {0}".format(*sys.version_info), + "Programming Language :: Python :: {0}.{1}".format(*sys.version_info), + ] + METADATA['classifiers'].extend(version_classifiers) + + # Build out the metadata + details = METADATA["extensions"]["python.details"] + homepage = details["project_urls"]["Home"] + author = details["contacts"][0]["name"] + email = details["contacts"][0]["email"] + metadata = ''.join([ + "Metadata-Version: {metadata_version}\n" \ + "Name: {name}\n" \ + "Version: {version}\n" \ + "Summary: {summary}\n" \ + "License: {license}\n".format(**METADATA), + "Home-page: {0}\n".format(homepage), + "Author: {0}\n".format(author), + "Author-email: {0}\n".format(email), + "Platform: {0}\n".format(platform), + ] + ["Classifier: {0}\n".format(c) for c in METADATA['classifiers']]) + + # Zip it up and name it the right thing + whl = WheelFile('panda3d', version, platform) + whl.lib_path = [libs_dir] + + # Add the trees with Python modules. + whl.write_directory('direct', direct_dir) + + # Write the panda3d tree. We use a custom empty __init__ since the + # default one adds the bin directory to the PATH, which we don't have. + whl.write_file_data('panda3d/__init__.py', '') + + ext_suffix = GetExtensionSuffix() + + for file in os.listdir(panda3d_dir): + if file == '__init__.py': + pass + elif file.endswith(ext_suffix) or file.endswith('.py'): + source_path = os.path.join(panda3d_dir, file) + + if file.endswith('.pyd') and platform.startswith('cygwin'): + # Rename it to .dll for cygwin Python to be able to load it. + target_path = 'panda3d/' + os.path.splitext(file)[0] + '.dll' + else: + target_path = 'panda3d/' + file + + whl.write_file(target_path, source_path) + + # Add plug-ins. + for lib in PLUGIN_LIBS: + plugin_name = 'lib' + lib + if sys.platform in ('win32', 'cygwin'): + plugin_name += '.dll' + elif sys.platform == 'darwin': + plugin_name += '.dylib' + else: + plugin_name += '.so' + plugin_path = os.path.join(libs_dir, plugin_name) + if os.path.isfile(plugin_path): + whl.write_file('panda3d/' + plugin_name, plugin_path) + + # Add the .data directory, containing additional files. + data_dir = 'panda3d-{0}.data'.format(version) + #whl.write_directory(data_dir + '/data/etc', etc_dir) + #whl.write_directory(data_dir + '/data/models', models_dir) + + # Actually, let's not. That seems to install the files to the strangest + # places in the user's filesystem. Let's instead put them in panda3d. + whl.write_directory('panda3d/etc', etc_dir) + whl.write_directory('panda3d/models', models_dir) + + # Add the pandac tree for backward compatibility. + for file in os.listdir(pandac_dir): + if file.endswith('.py'): + whl.write_file('pandac/' + file, os.path.join(pandac_dir, file)) + + # Add a panda3d-tools directory containing the executables. + entry_points = '[console_scripts]\n' + entry_points += 'eggcacher = direct.directscripts.eggcacher:main\n' + entry_points += 'packpanda = direct.directscripts.packpanda:main\n' + tools_init = '' + for file in os.listdir(bin_dir): + basename = os.path.splitext(file)[0] + if basename in ('eggcacher', 'packpanda'): + continue + + source_path = os.path.join(bin_dir, file) + + if is_executable(source_path): + # Put the .exe files inside the panda3d-tools directory. + whl.write_file('panda3d_tools/' + file, source_path) + + # Tell pip to create a wrapper script. + funcname = basename.replace('-', '_') + entry_points += '{0} = panda3d_tools:{1}\n'.format(basename, funcname) + tools_init += '{0} = lambda: _exec_tool({1!r})\n'.format(funcname, file) + + whl.write_file_data('panda3d_tools/__init__.py', PANDA3D_TOOLS_INIT.format(tools_init)) + + # Add the dist-info directory last. + info_dir = 'panda3d-{0}.dist-info'.format(version) + whl.write_file_data(info_dir + '/entry_points.txt', entry_points) + whl.write_file_data(info_dir + '/metadata.json', json.dumps(METADATA, indent=4, separators=(',', ': '))) + whl.write_file_data(info_dir + '/METADATA', metadata) + whl.write_file_data(info_dir + '/WHEEL', WHEEL_DATA.format(PY_VERSION, ABI_TAG, platform)) + whl.write_file(info_dir + '/LICENSE.txt', license_src) + whl.write_file(info_dir + '/README.md', readme_src) + whl.write_file_data(info_dir + '/top_level.txt', 'direct\npanda3d\npandac\npanda3d_tools\n') + + whl.close() + + +if __name__ == "__main__": + version = ParsePandaVersion("dtool/PandaVersion.pp") + + parser = OptionParser() + parser.add_option('', '--version', dest = 'version', help = 'Panda3D version number (default: %s)' % (version), default = version) + parser.add_option('', '--outputdir', dest = 'outputdir', help = 'Makepanda\'s output directory (default: built)', default = 'built') + parser.add_option('', '--verbose', dest = 'verbose', help = 'Enable verbose output', action = 'store_true', default = False) + parser.add_option('', '--platform', dest = 'platform', help = 'Override platform tag (default: %s)' % (default_platform), default = get_platform()) + (options, args) = parser.parse_args() + + SetVerbose(options.verbose) + makewheel(options.version, options.outputdir, options.platform) diff --git a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm index 7106e440d7..10bef93e2e 100644 --- a/panda/src/cocoadisplay/cocoaGraphicsPipe.mm +++ b/panda/src/cocoadisplay/cocoaGraphicsPipe.mm @@ -140,11 +140,14 @@ load_display_information() { // _display_information->_device_id = CGDisplaySerialNumber(_display); // Display modes + size_t num_modes = 0; #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1060 CFArrayRef modes = CGDisplayCopyAllDisplayModes(_display, NULL); - size_t num_modes = CFArrayGetCount(modes); - _display_information->_total_display_modes = num_modes; - _display_information->_display_mode_array = new DisplayMode[num_modes]; + if (modes != NULL) { + num_modes = CFArrayGetCount(modes); + _display_information->_total_display_modes = num_modes; + _display_information->_display_mode_array = new DisplayMode[num_modes]; + } for (size_t i = 0; i < num_modes; ++i) { CGDisplayModeRef mode = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); @@ -181,13 +184,17 @@ load_display_information() { } CFRelease(encoding); } - CFRelease(modes); + if (modes != NULL) { + CFRelease(modes); + } #else CFArrayRef modes = CGDisplayAvailableModes(_display); - size_t num_modes = CFArrayGetCount(modes); - _display_information->_total_display_modes = num_modes; - _display_information->_display_mode_array = new DisplayMode[num_modes]; + if (modes != NULL) { + num_modes = CFArrayGetCount(modes); + _display_information->_total_display_modes = num_modes; + _display_information->_display_mode_array = new DisplayMode[num_modes]; + } for (size_t i = 0; i < num_modes; ++i) { CFDictionaryRef mode = (CFDictionaryRef) CFArrayGetValueAtIndex(modes, i); diff --git a/panda/src/display/get_x11.h b/panda/src/display/get_x11.h index c188164598..f9112766d7 100644 --- a/panda/src/display/get_x11.h +++ b/panda/src/display/get_x11.h @@ -49,19 +49,6 @@ struct XVisualInfo; #include #include #include - -#ifdef HAVE_XRANDR -#include -#endif // HAVE_XRANDR - -#ifdef HAVE_XCURSOR -#include -#endif - -#ifdef HAVE_XF86DGA -#include -#endif - #include "post_x11_include.h" #endif // CPPPARSER diff --git a/panda/src/display/graphicsStateGuardian.I b/panda/src/display/graphicsStateGuardian.I index ea3a9b61ac..15f95a19f4 100644 --- a/panda/src/display/graphicsStateGuardian.I +++ b/panda/src/display/graphicsStateGuardian.I @@ -62,6 +62,14 @@ release_all_index_buffers() { return _prepared_objects->release_all_index_buffers(); } +/** + * Frees the resources for all index buffers associated with this GSG. + */ +INLINE int GraphicsStateGuardian:: +release_all_shader_buffers() { + return _prepared_objects->release_all_shader_buffers(); +} + /** * Sets the active flag associated with the GraphicsStateGuardian. If the * GraphicsStateGuardian is marked inactive, nothing is rendered. This is not diff --git a/panda/src/display/graphicsStateGuardian.cxx b/panda/src/display/graphicsStateGuardian.cxx index 67fbdb2f6c..d5b2f769a0 100644 --- a/panda/src/display/graphicsStateGuardian.cxx +++ b/panda/src/display/graphicsStateGuardian.cxx @@ -62,12 +62,15 @@ #include #include -PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Vertex buffer switch:Vertex"); -PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Vertex buffer switch:Index"); +PStatCollector GraphicsStateGuardian::_vertex_buffer_switch_pcollector("Buffer switch:Vertex"); +PStatCollector GraphicsStateGuardian::_index_buffer_switch_pcollector("Buffer switch:Index"); +PStatCollector GraphicsStateGuardian::_shader_buffer_switch_pcollector("Buffer switch:Shader"); PStatCollector GraphicsStateGuardian::_load_vertex_buffer_pcollector("Draw:Transfer data:Vertex buffer"); PStatCollector GraphicsStateGuardian::_load_index_buffer_pcollector("Draw:Transfer data:Index buffer"); +PStatCollector GraphicsStateGuardian::_load_shader_buffer_pcollector("Draw:Transfer data:Shader buffer"); PStatCollector GraphicsStateGuardian::_create_vertex_buffer_pcollector("Draw:Transfer data:Create Vertex buffer"); PStatCollector GraphicsStateGuardian::_create_index_buffer_pcollector("Draw:Transfer data:Create Index buffer"); +PStatCollector GraphicsStateGuardian::_create_shader_buffer_pcollector("Draw:Transfer data:Create Shader buffer"); PStatCollector GraphicsStateGuardian::_load_texture_pcollector("Draw:Transfer data:Texture"); PStatCollector GraphicsStateGuardian::_data_transferred_pcollector("Data transferred"); PStatCollector GraphicsStateGuardian::_texmgrmem_total_pcollector("Texture manager"); @@ -104,6 +107,7 @@ PStatCollector GraphicsStateGuardian::_prepare_geom_pcollector("Draw:Prepare:Geo 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::_prepare_shader_buffer_pcollector("Draw:Prepare:Shader 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"); @@ -147,6 +151,10 @@ GraphicsStateGuardian(CoordinateSystem internal_coordinate_system, _coordinate_system = CS_invalid; _internal_transform = TransformState::make_identity(); + if (_internal_coordinate_system == CS_default) { + _internal_coordinate_system = get_default_coordinate_system(); + } + set_coordinate_system(get_default_coordinate_system()); _data_reader = (GeomVertexDataPipelineReader *)NULL; @@ -653,6 +661,22 @@ void GraphicsStateGuardian:: release_index_buffer(IndexBufferContext *) { } +/** + * Prepares the indicated buffer for retained-mode rendering. + */ +BufferContext *GraphicsStateGuardian:: +prepare_shader_buffer(ShaderBuffer *) { + return (BufferContext *)NULL; +} + +/** + * Frees the resources previously allocated via a call to prepare_data(), + * including deleting the BufferContext itself, if necessary. + */ +void GraphicsStateGuardian:: +release_shader_buffer(BufferContext *) { +} + /** * Begins a new occlusion query. After this call, you may call * begin_draw_primitives() and draw_triangles()/draw_whatever() repeatedly. diff --git a/panda/src/display/graphicsStateGuardian.h b/panda/src/display/graphicsStateGuardian.h index 825b03286d..81643a1f11 100644 --- a/panda/src/display/graphicsStateGuardian.h +++ b/panda/src/display/graphicsStateGuardian.h @@ -88,6 +88,7 @@ PUBLISHED: INLINE int release_all_geoms(); INLINE int release_all_vertex_buffers(); INLINE int release_all_index_buffers(); + INLINE int release_all_shader_buffers(); INLINE void set_active(bool active); INLINE bool is_active() const; @@ -307,6 +308,9 @@ public: virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data); virtual void release_index_buffer(IndexBufferContext *ibc); + virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data); + virtual void release_shader_buffer(BufferContext *ibc); + virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); @@ -322,7 +326,7 @@ public: virtual void set_state_and_transform(const RenderState *state, const TransformState *transform); - virtual PN_stdfloat compute_distance_to(const LPoint3 &point) const; + PN_stdfloat compute_distance_to(const LPoint3 &point) const; virtual void clear(DrawableRegion *clearable); @@ -640,10 +644,13 @@ public: // Statistics static PStatCollector _vertex_buffer_switch_pcollector; static PStatCollector _index_buffer_switch_pcollector; + static PStatCollector _shader_buffer_switch_pcollector; static PStatCollector _load_vertex_buffer_pcollector; static PStatCollector _load_index_buffer_pcollector; + static PStatCollector _load_shader_buffer_pcollector; static PStatCollector _create_vertex_buffer_pcollector; static PStatCollector _create_index_buffer_pcollector; + static PStatCollector _create_shader_buffer_pcollector; static PStatCollector _load_texture_pcollector; static PStatCollector _data_transferred_pcollector; static PStatCollector _texmgrmem_total_pcollector; @@ -680,6 +687,7 @@ public: static PStatCollector _prepare_shader_pcollector; static PStatCollector _prepare_vertex_buffer_pcollector; static PStatCollector _prepare_index_buffer_pcollector; + static PStatCollector _prepare_shader_buffer_pcollector; // A whole slew of collectors to measure the cost of individual state // changes. These are disabled by default. diff --git a/panda/src/downloader/httpClient.cxx b/panda/src/downloader/httpClient.cxx index 26baaff0c1..63ba1c6258 100644 --- a/panda/src/downloader/httpClient.cxx +++ b/panda/src/downloader/httpClient.cxx @@ -231,11 +231,13 @@ operator = (const HTTPClient ©) { */ HTTPClient:: ~HTTPClient() { - // Before we can free the context, we must remove the X509_STORE pointer - // from it, so it won't be destroyed along with it (this object is shared - // among all contexts). if (_ssl_ctx != (SSL_CTX *)NULL) { +#if OPENSSL_VERSION_NUMBER < 0x10100000 + // Before we can free the context, we must remove the X509_STORE pointer + // from it, so it won't be destroyed along with it (this object is shared + // among all contexts). _ssl_ctx->cert_store = NULL; +#endif SSL_CTX_free(_ssl_ctx); } @@ -1122,7 +1124,13 @@ get_ssl_ctx() { OpenSSLWrapper *sslw = OpenSSLWrapper::get_global_ptr(); sslw->notify_ssl_errors(); - SSL_CTX_set_cert_store(_ssl_ctx, sslw->get_x509_store()); + X509_STORE *store = sslw->get_x509_store(); +#if OPENSSL_VERSION_NUMBER >= 0x10100000 + if (store != NULL) { + X509_STORE_up_ref(store); + } +#endif + SSL_CTX_set_cert_store(_ssl_ctx, store); return _ssl_ctx; } @@ -1511,15 +1519,17 @@ x509_name_subset(X509_NAME *name_a, X509_NAME *name_b) { for (int ai = 0; ai < count_a; ai++) { X509_NAME_ENTRY *na = X509_NAME_get_entry(name_a, ai); - int bi = X509_NAME_get_index_by_OBJ(name_b, na->object, -1); + int bi = X509_NAME_get_index_by_OBJ(name_b, X509_NAME_ENTRY_get_object(na), -1); if (bi < 0) { // This entry in name_a is not defined in name_b. return false; } X509_NAME_ENTRY *nb = X509_NAME_get_entry(name_b, bi); - if (na->value->length != nb->value->length || - memcmp(na->value->data, nb->value->data, na->value->length) != 0) { + ASN1_STRING *na_value = X509_NAME_ENTRY_get_data(na); + ASN1_STRING *nb_value = X509_NAME_ENTRY_get_data(nb); + if (na_value->length != nb_value->length || + memcmp(na_value->data, nb_value->data, na_value->length) != 0) { // This entry in name_a doesn't match that of name_b. return false; } diff --git a/panda/src/express/filename_ext.cxx b/panda/src/express/filename_ext.cxx index 70afd0d07d..1c9bf78e7e 100644 --- a/panda/src/express/filename_ext.cxx +++ b/panda/src/express/filename_ext.cxx @@ -14,6 +14,115 @@ #include "filename_ext.h" #ifdef HAVE_PYTHON + +#ifndef CPPPARSER +extern Dtool_PyTypedObject Dtool_Filename; +#endif // CPPPARSER + +/** + * Constructs a Filename object from a str, bytes object, or os.PathLike. + */ +void Extension:: +__init__(PyObject *path) { + nassertv(path != NULL); + nassertv(_this != NULL); + + Py_ssize_t length; + + if (PyUnicode_CheckExact(path)) { + wchar_t *data; +#if PY_VERSION_HEX >= 0x03020000 + data = PyUnicode_AsWideCharString(path, &length); +#else + length = PyUnicode_GET_SIZE(path); + data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1)); + PyUnicode_AsWideChar((PyUnicodeObject *)path, data, length); +#endif + (*_this) = wstring(data, length); + +#if PY_VERSION_HEX >= 0x03020000 + PyMem_Free(data); +#endif + return; + } + + if (PyBytes_CheckExact(path)) { + char *data; + PyBytes_AsStringAndSize(path, &data, &length); + (*_this) = string(data, length); + return; + } + + if (Py_TYPE(path) == &Dtool_Filename._PyType) { + // Copy constructor. + (*_this) = *((Filename *)((Dtool_PyInstDef *)path)->_ptr_to_object); + return; + } + + PyObject *path_str; + +#if PY_VERSION_HEX >= 0x03060000 + // It must be an os.PathLike object. Check for an __fspath__ method. + PyObject *fspath = PyObject_GetAttrString((PyObject *)Py_TYPE(path), "__fspath__"); + if (fspath == NULL) { + PyErr_Format(PyExc_TypeError, "expected str, bytes or os.PathLike object, not %s", Py_TYPE(path)->tp_name); + return; + } + + path_str = PyObject_CallFunctionObjArgs(fspath, path, NULL); + Py_DECREF(fspath); +#else + // There is no standard path protocol before Python 3.6, but let's try and + // support taking pathlib paths anyway. We don't version check this to + // allow people to use backports of the pathlib module. + if (PyObject_HasAttrString(path, "_format_parsed_parts")) { + path_str = PyObject_Str(path); + } else { +#if PY_VERSION_HEX >= 0x03040000 + PyErr_Format(PyExc_TypeError, "expected str, bytes, Path or Filename object, not %s", Py_TYPE(path)->tp_name); +#elif PY_MAJOR_VERSION >= 3 + PyErr_Format(PyExc_TypeError, "expected str, bytes or Filename object, not %s", Py_TYPE(path)->tp_name); +#else + PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path)->tp_name); +#endif + return; + } +#endif + + if (path_str == NULL) { + return; + } + + if (PyUnicode_CheckExact(path_str)) { + wchar_t *data; +#if PY_VERSION_HEX >= 0x03020000 + data = PyUnicode_AsWideCharString(path_str, &length); +#else + length = PyUnicode_GET_SIZE(path_str); + data = (wchar_t *)alloca(sizeof(wchar_t) * (length + 1)); + PyUnicode_AsWideChar((PyUnicodeObject *)path_str, data, length); +#endif + (*_this) = Filename::from_os_specific_w(wstring(data, length)); + +#if PY_VERSION_HEX >= 0x03020000 + PyMem_Free(data); +#endif + + } else if (PyBytes_CheckExact(path_str)) { + char *data; + PyBytes_AsStringAndSize(path_str, &data, &length); + (*_this) = Filename::from_os_specific(string(data, length)); + + } else { +#if PY_MAJOR_VERSION >= 3 + PyErr_Format(PyExc_TypeError, "expected str or bytes object, not %s", Py_TYPE(path_str)->tp_name); +#else + PyErr_Format(PyExc_TypeError, "expected str or unicode object, not %s", Py_TYPE(path_str)->tp_name); +#endif + } + Py_DECREF(path_str); +} + /** * This special Python method is implement to provide support for the pickle * module. @@ -62,6 +171,16 @@ __repr__() const { return result; } +/** + * Allows a Filename object to be passed to any Python function that accepts + * an os.PathLike object. + */ +PyObject *Extension:: +__fspath__() const { + wstring filename = _this->to_os_specific_w(); + return PyUnicode_FromWideChar(filename.data(), (Py_ssize_t)filename.size()); +} + /** * This variant on scan_directory returns a Python list of strings on success, * or None on failure. diff --git a/panda/src/express/filename_ext.h b/panda/src/express/filename_ext.h index c1d5869ec7..1ebeaacc52 100644 --- a/panda/src/express/filename_ext.h +++ b/panda/src/express/filename_ext.h @@ -29,8 +29,11 @@ template<> class Extension : public ExtensionBase { public: + void __init__(PyObject *path); + PyObject *__reduce__(PyObject *self) const; PyObject *__repr__() const; + PyObject *__fspath__() const; PyObject *scan_directory() const; }; diff --git a/panda/src/ffmpeg/config_ffmpeg.cxx b/panda/src/ffmpeg/config_ffmpeg.cxx index 115b3c31c3..f0d6a5518e 100644 --- a/panda/src/ffmpeg/config_ffmpeg.cxx +++ b/panda/src/ffmpeg/config_ffmpeg.cxx @@ -76,6 +76,14 @@ ConfigVariableInt ffmpeg_read_buffer_size "This is important for performance. A typical size is that of a " "cache page, e.g. 4kb.")); +ConfigVariableBool ffmpeg_prefer_libvpx +("ffmpeg-prefer-libvpx", false, + PRC_DESC("If this is true, Panda will overrule ffmpeg's best judgment on " + "which decoder to use for decoding VP8 and VP9 files, and try to " + "choose libvpx. This is useful when you want to play WebM videos " + "with an alpha channel, which aren't supported by ffmpeg's own " + "VP8/VP9 decoders.")); + /** * 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/ffmpeg/config_ffmpeg.h b/panda/src/ffmpeg/config_ffmpeg.h index 58f81af821..c3854fc9e1 100644 --- a/panda/src/ffmpeg/config_ffmpeg.h +++ b/panda/src/ffmpeg/config_ffmpeg.h @@ -31,6 +31,7 @@ extern ConfigVariableBool ffmpeg_support_seek; extern ConfigVariableBool ffmpeg_global_lock; extern ConfigVariableEnum ffmpeg_thread_priority; extern ConfigVariableInt ffmpeg_read_buffer_size; +extern ConfigVariableBool ffmpeg_prefer_libvpx; extern EXPCL_FFMPEG void init_libffmpeg(); diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.cxx b/panda/src/ffmpeg/ffmpegVideoCursor.cxx index 8affe3c116..8e2d0b0e47 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.cxx +++ b/panda/src/ffmpeg/ffmpegVideoCursor.cxx @@ -22,6 +22,7 @@ extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" + #include "libavutil/pixdesc.h" #ifdef HAVE_SWSCALE #include "libswscale/swscale.h" #endif @@ -35,11 +36,21 @@ PStatCollector FfmpegVideoCursor::_fetch_buffer_pcollector("*:FFMPEG Video Decod PStatCollector FfmpegVideoCursor::_seek_pcollector("*:FFMPEG Video Decoding:Seek"); PStatCollector FfmpegVideoCursor::_export_frame_pcollector("*:FFMPEG Convert Video to BGR"); - #if LIBAVFORMAT_VERSION_MAJOR < 53 #define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO #endif +#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(51, 74, 100) +#define AV_PIX_FMT_NONE PIX_FMT_NONE +#define AV_PIX_FMT_BGR24 PIX_FMT_BGR24 +#define AV_PIX_FMT_BGRA PIX_FMT_BGRA +typedef PixelFormat AVPixelFormat; +#endif + +#if LIBAVUTIL_VERSION_INT < AV_VERSION_INT(52, 32, 100) +#define AV_PIX_FMT_FLAG_ALPHA PIX_FMT_ALPHA +#endif + /** * This constructor is only used when reading from a bam file. */ @@ -55,6 +66,7 @@ FfmpegVideoCursor() : _format_ctx(NULL), _video_ctx(NULL), _convert_ctx(NULL), + _pixel_format((int)AV_PIX_FMT_NONE), _video_index(-1), _frame(NULL), _frame_out(NULL), @@ -80,17 +92,6 @@ init_from(FfmpegVideo *source) { ReMutexHolder av_holder(_av_lock); -#ifdef HAVE_SWSCALE - nassertv(_convert_ctx == NULL); - _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt, -#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51, 74, 100) - _size_x, _size_y, AV_PIX_FMT_BGR24, -#else - _size_x, _size_y, PIX_FMT_BGR24, -#endif - SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL); -#endif // HAVE_SWSCALE - #if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(54, 59, 100) _frame = av_frame_alloc(); _frame_out = av_frame_alloc(); @@ -115,6 +116,25 @@ init_from(FfmpegVideo *source) { _eof_known = false; _eof_frame = 0; + // Check if we got an alpha format. Please note that some video codecs + // (eg. libvpx) change the pix_fmt after decoding the first frame, which is + // why we didn't do this earlier. + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(_video_ctx->pix_fmt); + if (desc && (desc->flags & AV_PIX_FMT_FLAG_ALPHA) != 0) { + _num_components = 4; + _pixel_format = (int)AV_PIX_FMT_BGRA; + } else { + _num_components = 3; + _pixel_format = (int)AV_PIX_FMT_BGR24; + } + +#ifdef HAVE_SWSCALE + nassertv(_convert_ctx == NULL); + _convert_ctx = sws_getContext(_size_x, _size_y, _video_ctx->pix_fmt, + _size_x, _size_y, (AVPixelFormat)_pixel_format, + SWS_BILINEAR | SWS_PRINT_INFO, NULL, NULL, NULL); +#endif // HAVE_SWSCALE + #ifdef HAVE_THREADS set_max_readahead_frames(ffmpeg_max_readahead_frames); #endif // HAVE_THREADS @@ -495,7 +515,17 @@ open_stream() { return false; } - AVCodec *pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id); + AVCodec *pVideoCodec = NULL; + if (ffmpeg_prefer_libvpx) { + if ((int)_video_ctx->codec_id == 168) { // AV_CODEC_ID_VP9 + pVideoCodec = avcodec_find_decoder_by_name("libvpx-vp9"); + } else if (_video_ctx->codec_id == AV_CODEC_ID_VP8) { + pVideoCodec = avcodec_find_decoder_by_name("libvpx"); + } + } + if (pVideoCodec == NULL) { + pVideoCodec = avcodec_find_decoder(_video_ctx->codec_id); + } if (pVideoCodec == NULL) { ffmpeg_cat.info() << "Couldn't find codec\n"; @@ -515,7 +545,7 @@ open_stream() { _size_x = _video_ctx->width; _size_y = _video_ctx->height; - _num_components = 3; // Don't know how to implement RGBA movies yet. + _num_components = 3; _length = (double)_format_ctx->duration / (double)AV_TIME_BASE; _can_seek = true; _can_seek_fast = true; @@ -1075,8 +1105,8 @@ export_frame(FfmpegBuffer *buffer) { return; } - _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * 3); - _frame_out->linesize[0] = _size_x * -3; + _frame_out->data[0] = buffer->_block + ((_size_y - 1) * _size_x * _num_components); + _frame_out->linesize[0] = _size_x * -_num_components; buffer->_begin_frame = _begin_frame; buffer->_end_frame = _end_frame; @@ -1086,7 +1116,7 @@ export_frame(FfmpegBuffer *buffer) { nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL); sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); #else - img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, + img_convert((AVPicture *)_frame_out, (AVPixelFormat)_pixel_format, (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); #endif } else { @@ -1094,7 +1124,7 @@ export_frame(FfmpegBuffer *buffer) { nassertv(_convert_ctx != NULL && _frame != NULL && _frame_out != NULL); sws_scale(_convert_ctx, _frame->data, _frame->linesize, 0, _size_y, _frame_out->data, _frame_out->linesize); #else - img_convert((AVPicture *)_frame_out, PIX_FMT_BGR24, + img_convert((AVPicture *)_frame_out, (AVPixelFormat)_pixel_format, (AVPicture *)_frame, _video_ctx->pix_fmt, _size_x, _size_y); #endif } diff --git a/panda/src/ffmpeg/ffmpegVideoCursor.h b/panda/src/ffmpeg/ffmpegVideoCursor.h index a44ec57490..b37637a0b0 100644 --- a/panda/src/ffmpeg/ffmpegVideoCursor.h +++ b/panda/src/ffmpeg/ffmpegVideoCursor.h @@ -105,6 +105,8 @@ private: ThreadPriority _thread_priority; PT(GenericThread) _thread; + int _pixel_format; + // This global Mutex protects calls to avcodec_opencloseetc. static ReMutex _av_lock; diff --git a/panda/src/glstuff/glBufferContext_src.I b/panda/src/glstuff/glBufferContext_src.I new file mode 100644 index 0000000000..bec8283a18 --- /dev/null +++ b/panda/src/glstuff/glBufferContext_src.I @@ -0,0 +1,25 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file glBufferContext_src.I + * @author rdb + * @date 2016-12-12 + */ + +/** + * + */ +INLINE CLP(BufferContext):: +CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg, + PreparedGraphicsObjects *pgo) : + BufferContext(&pgo->_sbuffer_residency), + AdaptiveLruPage(0), + _glgsg(glgsg) +{ + _index = 0; +} diff --git a/panda/src/glstuff/glBufferContext_src.cxx b/panda/src/glstuff/glBufferContext_src.cxx new file mode 100644 index 0000000000..f2e588f945 --- /dev/null +++ b/panda/src/glstuff/glBufferContext_src.cxx @@ -0,0 +1,49 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file glBufferContext_src.cxx + * @author rdb + * @date 2016-12-12 + */ + +TypeHandle CLP(BufferContext)::_type_handle; + +/** + * Evicts the page from the LRU. Called internally when the LRU determines + * that it is full. May also be called externally when necessary to + * explicitly evict the page. + * + * It is legal for this method to either evict the page as requested, do + * nothing (in which case the eviction will be requested again at the next + * epoch), or requeue itself on the tail of the queue (in which case the + * eviction will be requested again much later). + */ +void CLP(BufferContext):: +evict_lru() { + dequeue_lru(); + + // Make sure the buffer is unbound before we delete it. + if (_glgsg->_current_ibuffer_index == _index) { + if (GLCAT.is_debug() && gl_debug_buffers) { + GLCAT.debug() + << "unbinding index buffer\n"; + } + _glgsg->_glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _glgsg->_current_ibuffer_index = 0; + } + + // Free the buffer. + _glgsg->_glDeleteBuffers(1, &_index); + + // We still need a valid index number, though, in case we want to re-load + // the buffer later. + _glgsg->_glGenBuffers(1, &_index); + + update_data_size_bytes(0); + set_resident(false); +} diff --git a/panda/src/glstuff/glBufferContext_src.h b/panda/src/glstuff/glBufferContext_src.h new file mode 100644 index 0000000000..b6c46aab76 --- /dev/null +++ b/panda/src/glstuff/glBufferContext_src.h @@ -0,0 +1,52 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file glBufferContext_src.h + * @author rdb + * @date 2016-12-12 + */ + +#include "pandabase.h" +#include "bufferContext.h" +#include "deletedChain.h" + +/** + * Caches a GeomPrimitive on the GL as a buffer object. + */ +class EXPCL_GL CLP(BufferContext) : public BufferContext, public AdaptiveLruPage { +public: + INLINE CLP(BufferContext)(CLP(GraphicsStateGuardian) *glgsg, + PreparedGraphicsObjects *pgo); + ALLOC_DELETED_CHAIN(CLP(BufferContext)); + + virtual void evict_lru(); + + CLP(GraphicsStateGuardian) *_glgsg; + + // This is the GL "name" of the data object. + GLuint _index; + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + BufferContext::init_type(); + register_type(_type_handle, CLASSPREFIX_QUOTED "BufferContext", + BufferContext::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 "glBufferContext_src.I" diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx index dc2c0c45e6..98c21610c5 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.cxx +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.cxx @@ -1796,7 +1796,7 @@ reset() { } #endif -#ifndef OPENGLES +#ifndef OPENGLES_1 // Check for uniform buffers. #ifdef OPENGLES if (is_at_least_gl_version(3, 1) || has_extension("GL_ARB_uniform_buffer_object")) { @@ -1810,12 +1810,30 @@ reset() { get_extension_func("glGetActiveUniformBlockiv"); _glGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) get_extension_func("glGetActiveUniformBlockName"); - - _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) - get_extension_func("glBindBufferBase"); } else { _supports_uniform_buffers = false; } + +#ifndef OPENGLES + // Check for SSBOs. + if (is_at_least_gl_version(4, 3) || has_extension("ARB_shader_storage_buffer_object")) { + _supports_shader_buffers = true; + _glGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC) + get_extension_func("glGetProgramInterfaceiv"); + _glGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC) + get_extension_func("glGetProgramResourceName"); + _glGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC) + get_extension_func("glGetProgramResourceiv"); + } else +#endif + { + _supports_shader_buffers = false; + } + + if (_supports_uniform_buffers || _supports_shader_buffers) { + _glBindBufferBase = (PFNGLBINDBUFFERBASEPROC) + get_extension_func("glBindBufferBase"); + } #endif // Check whether we support geometry instancing and instanced vertex @@ -3031,6 +3049,9 @@ reset() { _current_vertex_buffers.clear(); _current_vertex_format.clear(); memset(_vertex_attrib_columns, 0, sizeof(const GeomVertexColumn *) * 32); + + _current_sbuffer_index = 0; + _current_sbuffer_base.clear(); #endif report_my_gl_errors(); @@ -5873,6 +5894,122 @@ setup_primitive(const unsigned char *&client_pointer, return true; } +#ifndef OPENGLES +/** + * Creates a new retained-mode representation of the given data, and returns a + * newly-allocated BufferContext pointer to reference it. It is the + * responsibility of the calling function to later call release_shader_buffer() + * with this same pointer (which will also delete the pointer). + * + * This function should not be called directly to prepare a buffer. Instead, + * call ShaderBuffer::prepare(). + */ +BufferContext *CLP(GraphicsStateGuardian):: +prepare_shader_buffer(ShaderBuffer *data) { + if (_supports_shader_buffers) { + PStatGPUTimer timer(this, _prepare_shader_buffer_pcollector); + + CLP(BufferContext) *gbc = new CLP(BufferContext)(this, _prepared_objects); + _glGenBuffers(1, &gbc->_index); + + if (GLCAT.is_debug() && gl_debug_buffers) { + GLCAT.debug() + << "creating shader buffer " << (int)gbc->_index << ": "<< *data << "\n"; + } + _glBindBuffer(GL_SHADER_STORAGE_BUFFER, gbc->_index); + _current_sbuffer_index = gbc->_index; + + if (_use_object_labels) { + string name = data->get_name(); + _glObjectLabel(GL_SHADER_STORAGE_BUFFER, gbc->_index, name.size(), name.data()); + } + + uint64_t num_bytes = data->get_data_size_bytes(); + if (_supports_buffer_storage) { + _glBufferStorage(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), 0); + } else { + _glBufferData(GL_SHADER_STORAGE_BUFFER, num_bytes, data->get_initial_data(), get_usage(data->get_usage_hint())); + } + + gbc->enqueue_lru(&_prepared_objects->_graphics_memory_lru); + + report_my_gl_errors(); + return gbc; + } + + return NULL; +} + +/** + * Binds the given shader buffer to the given binding slot. + */ +void CLP(GraphicsStateGuardian):: +apply_shader_buffer(GLuint base, ShaderBuffer *buffer) { + GLuint index = 0; + if (buffer != NULL) { + BufferContext *bc = buffer->prepare_now(get_prepared_objects(), this); + if (bc != NULL) { + CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc); + index = gbc->_index; + gbc->set_active(true); + } + } + + if (base >= _current_sbuffer_base.size()) { + _current_sbuffer_base.resize(base + 1, 0); + } + + if (_current_sbuffer_base[base] != index) { + if (GLCAT.is_spam() && gl_debug_buffers) { + GLCAT.spam() + << "binding shader buffer " << (int)index + << " to index " << base << "\n"; + } + _glBindBufferBase(GL_SHADER_STORAGE_BUFFER, base, index); + _current_sbuffer_base[base] = index; + _current_sbuffer_index = index; + + report_my_gl_errors(); + } +} + +/** + * Frees the GL resources previously allocated for the data. This function + * should never be called directly; instead, call Data::release() (or simply + * let the Data destruct). + */ +void CLP(GraphicsStateGuardian):: +release_shader_buffer(BufferContext *bc) { + nassertv(_supports_buffers); + + CLP(BufferContext) *gbc = DCAST(CLP(BufferContext), bc); + + if (GLCAT.is_debug() && gl_debug_buffers) { + GLCAT.debug() + << "deleting shader buffer " << (int)gbc->_index << "\n"; + } + + // Make sure the buffer is unbound before we delete it. Not strictly + // necessary according to the OpenGL spec, but it might help out a flaky + // driver, and we need to keep our internal state consistent anyway. + if (_current_sbuffer_index == gbc->_index) { + if (GLCAT.is_spam() && gl_debug_buffers) { + GLCAT.spam() + << "unbinding shader buffer\n"; + } + _glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); + _current_sbuffer_index = 0; + } + + _glDeleteBuffers(1, &gbc->_index); + report_my_gl_errors(); + + gbc->_index = 0; + + delete gbc; +} +#endif + #ifndef OPENGLES /** * Begins a new occlusion query. After this call, you may call @@ -6024,16 +6161,6 @@ make_geom_munger(const RenderState *state, Thread *current_thread) { return GeomMunger::register_munger(munger, current_thread); } -/** - * This function will compute the distance to the indicated point, assumed to - * be in eye coordinates, from the camera plane. The point is assumed to be - * in the GSG's internal coordinate system. - */ -PN_stdfloat CLP(GraphicsStateGuardian):: -compute_distance_to(const LPoint3 &point) const { - return -point[2]; -} - /** * Copy the pixels within the indicated display region from the framebuffer * into texture memory. @@ -6188,7 +6315,7 @@ framebuffer_copy_to_texture(Texture *tex, int view, int z, if (GLCAT.is_spam()) { GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n"; + << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n"; } } @@ -6509,7 +6636,7 @@ do_issue_shade_model() { #ifndef OPENGLES_1 /** - * + * Called when the current ShaderAttrib state has changed. */ void CLP(GraphicsStateGuardian):: do_issue_shader() { @@ -6522,21 +6649,30 @@ do_issue_shader() { shader = _default_shader; nassertv(shader != NULL); } - #endif + if (shader) { - context = shader->prepare_now(get_prepared_objects(), this); + if (_current_shader != shader) { + context = shader->prepare_now(get_prepared_objects(), this); + } else { + context = _current_shader_context; + } } + #ifndef SUPPORT_FIXED_FUNCTION // If it failed, try applying the default shader. if (shader != _default_shader && (context == 0 || !context->valid())) { shader = _default_shader; nassertv(shader != NULL); - context = shader->prepare_now(get_prepared_objects(), this); + if (_current_shader != shader) { + context = shader->prepare_now(get_prepared_objects(), this); + } else { + context = _current_shader_context; + } } #endif - if (context == 0 || (context->valid() == false)) { + if (context == 0 || !context->valid()) { if (_current_shader_context != 0) { _current_shader_context->unbind(); _current_shader = 0; @@ -6548,12 +6684,16 @@ do_issue_shader() { // bind the new one. if (_current_shader_context != NULL && _current_shader->get_language() != shader->get_language()) { + // If it's a different type of shader, make sure to unbind the old. _current_shader_context->unbind(); } context->bind(); _current_shader = shader; - _current_shader_context = context; } + + // Bind the shader storage buffers. + context->update_shader_buffer_bindings(_current_shader_context); + _current_shader_context = context; } #ifndef OPENGLES @@ -10126,6 +10266,7 @@ set_state_and_transform(const RenderState *target, } #endif + // Update all of the state that is bound to the shader program. if (_current_shader_context != NULL) { _current_shader_context->set_state_and_transform(target, transform, _projection_mat); } @@ -11365,7 +11506,7 @@ apply_texture(CLP(TextureContext) *gtc) { glBindTexture(target, gtc->_index); if (GLCAT.is_spam()) { GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n"; + << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *gtc->get_texture() << "\n"; } report_my_gl_errors(); @@ -11663,7 +11804,7 @@ upload_texture(CLP(TextureContext) *gtc, bool force, bool uses_mipmaps) { if (GLCAT.is_spam()) { GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n"; + << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n"; } } @@ -12636,14 +12777,14 @@ do_extract_texture_data(CLP(TextureContext) *gtc) { } #endif + Texture *tex = gtc->get_texture(); + glBindTexture(target, gtc->_index); if (GLCAT.is_spam()) { GLCAT.spam() - << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << ")\n"; + << "glBindTexture(0x" << hex << target << dec << ", " << gtc->_index << "): " << *tex << "\n"; } - Texture *tex = gtc->get_texture(); - GLint wrap_u, wrap_v, wrap_w; GLint minfilter, magfilter; GLfloat border_color[4]; diff --git a/panda/src/glstuff/glGraphicsStateGuardian_src.h b/panda/src/glstuff/glGraphicsStateGuardian_src.h index fe8cff8b59..85290cd6f2 100644 --- a/panda/src/glstuff/glGraphicsStateGuardian_src.h +++ b/panda/src/glstuff/glGraphicsStateGuardian_src.h @@ -344,6 +344,12 @@ public: const GeomPrimitivePipelineReader *reader, bool force); +#ifndef OPENGLES + virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data); + void apply_shader_buffer(GLuint base, ShaderBuffer *buffer); + virtual void release_shader_buffer(BufferContext *bc); +#endif + #ifndef OPENGLES virtual void begin_occlusion_query(); virtual PT(OcclusionQueryContext) end_occlusion_query(); @@ -358,8 +364,6 @@ public: virtual PT(GeomMunger) make_geom_munger(const RenderState *state, Thread *current_thread); - virtual PN_stdfloat compute_distance_to(const LPoint3 &point) const; - virtual void clear(DrawableRegion *region); virtual bool framebuffer_copy_to_texture @@ -687,6 +691,9 @@ protected: bool _use_vertex_attrib_binding; CPT(GeomVertexFormat) _current_vertex_format; const GeomVertexColumn *_vertex_attrib_columns[32]; + + GLuint _current_sbuffer_index; + pvector _current_sbuffer_base; #endif int _active_texture_stage; @@ -813,6 +820,7 @@ public: #ifndef OPENGLES_1 bool _supports_uniform_buffers; + bool _supports_shader_buffers; PFNGLBINDBUFFERBASEPROC _glBindBufferBase; bool _supports_buffer_storage; @@ -997,6 +1005,9 @@ public: PFNGLMAKETEXTUREHANDLENONRESIDENTPROC _glMakeTextureHandleNonResident; PFNGLUNIFORMHANDLEUI64PROC _glUniformHandleui64; PFNGLUNIFORMHANDLEUI64VPROC _glUniformHandleui64v; + PFNGLGETPROGRAMINTERFACEIVPROC _glGetProgramInterfaceiv; + PFNGLGETPROGRAMRESOURCENAMEPROC _glGetProgramResourceName; + PFNGLGETPROGRAMRESOURCEIVPROC _glGetProgramResourceiv; #endif // !OPENGLES GLenum _edge_clamp; @@ -1092,6 +1103,7 @@ private: friend class CLP(VertexBufferContext); friend class CLP(IndexBufferContext); + friend class CLP(BufferContext); friend class CLP(ShaderContext); friend class CLP(CgShaderContext); friend class CLP(GraphicsBuffer); diff --git a/panda/src/glstuff/glShaderContext_src.cxx b/panda/src/glstuff/glShaderContext_src.cxx index b02ec341df..6947a39235 100644 --- a/panda/src/glstuff/glShaderContext_src.cxx +++ b/panda/src/glstuff/glShaderContext_src.cxx @@ -324,6 +324,34 @@ CLP(ShaderContext)(CLP(GraphicsStateGuardian) *glgsg, Shader *s) : ShaderContext } } +#ifndef OPENGLES + // Get the used shader storage blocks. + if (_glgsg->_supports_shader_buffers) { + GLint block_count = 0, block_maxlength = 0; + + _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_ACTIVE_RESOURCES, &block_count); + _glgsg->_glGetProgramInterfaceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, GL_MAX_NAME_LENGTH, &block_maxlength); + + block_maxlength = max(64, block_maxlength); + char *block_name_cstr = (char *)alloca(block_maxlength); + + for (int i = 0; i < block_count; ++i) { + block_name_cstr[0] = 0; + _glgsg->_glGetProgramResourceName(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, block_maxlength, NULL, block_name_cstr); + + const GLenum props[] = {GL_BUFFER_BINDING, GL_BUFFER_DATA_SIZE}; + GLint values[2]; + _glgsg->_glGetProgramResourceiv(_glsl_program, GL_SHADER_STORAGE_BLOCK, i, 2, props, 2, NULL, values); + + StorageBlock block; + block._name = InternalName::make(block_name_cstr); + block._binding_index = values[0]; + block._min_size = values[1]; + _storage_blocks.push_back(block); + } + } +#endif + // Bind the program, so that we can call glUniform1i for the textures. _glgsg->_glUseProgram(_glsl_program); @@ -2486,7 +2514,10 @@ update_shader_texture_bindings(ShaderContext *prev) { bool has_write = param->has_write_access(); input._writable = has_write; - if (has_read && has_write) { + if (gl_force_image_bindings_writeonly) { + access = GL_WRITE_ONLY; + + } else if (has_read && has_write) { access = GL_READ_WRITE; } else if (has_read) { @@ -2656,6 +2687,31 @@ update_shader_texture_bindings(ShaderContext *prev) { _glgsg->report_my_gl_errors(); } +/** + * Updates the shader buffer bindings for this shader. + */ +void CLP(ShaderContext):: +update_shader_buffer_bindings(ShaderContext *prev) { +#ifndef OPENGLES + // Update the shader storage buffer bindings. + const ShaderAttrib *attrib = _glgsg->_target_shader; + + for (size_t i = 0; i < _storage_blocks.size(); ++i) { + StorageBlock &block = _storage_blocks[i]; + + ShaderBuffer *buffer = attrib->get_shader_input_buffer(block._name); +#ifndef NDEBUG + if (buffer->get_data_size_bytes() < block._min_size) { + GLCAT.error() + << "cannot bind " << *buffer << " to shader because it is too small" + " (expected at least " << block._min_size << " bytes)\n"; + } +#endif + _glgsg->apply_shader_buffer(block._binding_index, buffer); + } +#endif +} + /** * This subroutine prints the infolog for a shader. */ diff --git a/panda/src/glstuff/glShaderContext_src.h b/panda/src/glstuff/glShaderContext_src.h index 183f068277..e143774ed6 100644 --- a/panda/src/glstuff/glShaderContext_src.h +++ b/panda/src/glstuff/glShaderContext_src.h @@ -55,6 +55,7 @@ public: bool update_shader_vertex_arrays(ShaderContext *prev, bool force); void disable_shader_texture_bindings() OVERRIDE; void update_shader_texture_bindings(ShaderContext *prev) OVERRIDE; + void update_shader_buffer_bindings(ShaderContext *prev) OVERRIDE; INLINE bool uses_standard_vertex_arrays(void); INLINE bool uses_custom_vertex_arrays(void); @@ -87,6 +88,17 @@ private: pmap _glsl_uniform_handles; #endif +#ifndef OPENGLES + struct StorageBlock { + CPT(InternalName) _name; + GLuint _binding_index; + GLint _min_size; + }; + typedef pvector StorageBlocks; + StorageBlocks _storage_blocks; + BitArray _used_storage_bindings; +#endif + struct ImageInput { CPT(InternalName) _name; CLP(TextureContext) *_gtc; diff --git a/panda/src/glstuff/glmisc_src.cxx b/panda/src/glstuff/glmisc_src.cxx index c44a36aaf6..fd2bcf7aee 100644 --- a/panda/src/glstuff/glmisc_src.cxx +++ b/panda/src/glstuff/glmisc_src.cxx @@ -299,6 +299,11 @@ ConfigVariableBool gl_support_shadow_filter "cards suffered from a broken implementation of the " "shadow map filtering features.")); +ConfigVariableBool gl_force_image_bindings_writeonly + ("gl-force-image-bindings-writeonly", false, + PRC_DESC("Forces all image inputs (not textures!) to be bound as writeonly, " + "to read from an image, rebind it as sampler.")); + ConfigVariableEnum gl_coordinate_system ("gl-coordinate-system", CS_yup_right, PRC_DESC("Which coordinate system to use as the internal " @@ -326,6 +331,7 @@ void CLP(init_classes)() { CLP(SamplerContext)::init_type(); #endif CLP(VertexBufferContext)::init_type(); + CLP(BufferContext)::init_type(); CLP(GraphicsBuffer)::init_type(); #ifndef OPENGLES diff --git a/panda/src/glstuff/glmisc_src.h b/panda/src/glstuff/glmisc_src.h index b008aeb8d3..fb8040828f 100644 --- a/panda/src/glstuff/glmisc_src.h +++ b/panda/src/glstuff/glmisc_src.h @@ -80,6 +80,7 @@ extern ConfigVariableBool gl_fixed_vertex_attrib_locations; extern ConfigVariableBool gl_support_primitive_restart_index; extern ConfigVariableBool gl_support_sampler_objects; extern ConfigVariableBool gl_support_shadow_filter; +extern ConfigVariableBool gl_force_image_bindings_writeonly; extern ConfigVariableEnum gl_coordinate_system; extern EXPCL_GL void CLP(init_classes)(); diff --git a/panda/src/glstuff/glstuff_src.cxx b/panda/src/glstuff/glstuff_src.cxx index af8bcc1df3..06abef89f4 100644 --- a/panda/src/glstuff/glstuff_src.cxx +++ b/panda/src/glstuff/glstuff_src.cxx @@ -21,6 +21,7 @@ #include "glSamplerContext_src.cxx" #include "glVertexBufferContext_src.cxx" #include "glIndexBufferContext_src.cxx" +#include "glBufferContext_src.cxx" #include "glOcclusionQueryContext_src.cxx" #include "glTimerQueryContext_src.cxx" #include "glLatencyQueryContext_src.cxx" diff --git a/panda/src/glstuff/glstuff_src.h b/panda/src/glstuff/glstuff_src.h index 46edb3ac83..02e3208445 100644 --- a/panda/src/glstuff/glstuff_src.h +++ b/panda/src/glstuff/glstuff_src.h @@ -33,6 +33,7 @@ #include "glSamplerContext_src.h" #include "glVertexBufferContext_src.h" #include "glIndexBufferContext_src.h" +#include "glBufferContext_src.h" #include "glOcclusionQueryContext_src.h" #include "glTimerQueryContext_src.h" #include "glLatencyQueryContext_src.h" diff --git a/panda/src/glxdisplay/panda_glxext.h b/panda/src/glxdisplay/panda_glxext.h index 308e166224..5208302929 100644 --- a/panda/src/glxdisplay/panda_glxext.h +++ b/panda/src/glxdisplay/panda_glxext.h @@ -199,6 +199,7 @@ GLXContext glXCreateContextAttribsARB (X11_Display *dpy, GLXFBConfig config, GLX #ifndef GLX_ARB_get_proc_address #define GLX_ARB_get_proc_address 1 +typedef void (*__GLXextFuncPtr)(void); typedef __GLXextFuncPtr ( *PFNGLXGETPROCADDRESSARBPROC) (const GLubyte *procName); #ifdef GLX_GLXEXT_PROTOTYPES __GLXextFuncPtr glXGetProcAddressARB (const GLubyte *procName); diff --git a/panda/src/gobj/p3gobj_composite2.cxx b/panda/src/gobj/p3gobj_composite2.cxx index 81e23e5c07..56704b12b3 100644 --- a/panda/src/gobj/p3gobj_composite2.cxx +++ b/panda/src/gobj/p3gobj_composite2.cxx @@ -5,6 +5,7 @@ #include "samplerContext.cxx" #include "samplerState.cxx" #include "savedContext.cxx" +#include "shaderBuffer.cxx" #include "shaderContext.cxx" #include "shader.cxx" #include "simpleAllocator.cxx" diff --git a/panda/src/gobj/preparedGraphicsObjects.I b/panda/src/gobj/preparedGraphicsObjects.I index a9bd618ab5..6cc6daf914 100644 --- a/panda/src/gobj/preparedGraphicsObjects.I +++ b/panda/src/gobj/preparedGraphicsObjects.I @@ -45,6 +45,7 @@ release_all() { _texture_residency.set_levels(); _vbuffer_residency.set_levels(); _ibuffer_residency.set_levels(); + _sbuffer_residency.set_levels(); } /** @@ -58,7 +59,8 @@ get_num_queued() const { get_num_queued_geoms() + get_num_queued_shaders() + get_num_queued_vertex_buffers() + - get_num_queued_index_buffers()); + get_num_queued_index_buffers() + + get_num_queued_shader_buffers()); } /** @@ -72,7 +74,8 @@ get_num_prepared() const { get_num_prepared_geoms() + get_num_prepared_shaders() + get_num_prepared_vertex_buffers() + - get_num_prepared_index_buffers()); + get_num_prepared_index_buffers() + + get_num_prepared_shader_buffers()); } /** diff --git a/panda/src/gobj/preparedGraphicsObjects.cxx b/panda/src/gobj/preparedGraphicsObjects.cxx index 7b2b5e08a9..5c9ec855e3 100644 --- a/panda/src/gobj/preparedGraphicsObjects.cxx +++ b/panda/src/gobj/preparedGraphicsObjects.cxx @@ -41,6 +41,7 @@ PreparedGraphicsObjects() : _texture_residency(_name, "texture"), _vbuffer_residency(_name, "vbuffer"), _ibuffer_residency(_name, "ibuffer"), + _sbuffer_residency(_name, "sbuffer"), _graphics_memory_lru("graphics_memory_lru", graphics_memory_limit), _sampler_object_lru("sampler_object_lru", sampler_object_limit) { @@ -121,6 +122,16 @@ PreparedGraphicsObjects:: delete ibc; } _released_index_buffers.clear(); + + release_all_shader_buffers(); + Buffers::iterator bci; + for (bci = _released_shader_buffers.begin(); + bci != _released_shader_buffers.end(); + ++bci) { + BufferContext *bc = (BufferContext *)(*bci); + delete bc; + } + _released_shader_buffers.clear(); } /** @@ -167,6 +178,9 @@ show_residency_trackers(ostream &out) const { out << "\nIndex buffers:\n"; _ibuffer_residency.write(out, 2); + + out << "\nShader buffers:\n"; + _sbuffer_residency.write(out, 2); } /** @@ -1195,6 +1209,155 @@ prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg) { return ibc; } +/** + * Indicates that a buffer would like to be put on the list to be prepared + * when the GSG is next ready to do this (presumably at the next frame). + */ +void PreparedGraphicsObjects:: +enqueue_shader_buffer(ShaderBuffer *data) { + ReMutexHolder holder(_lock); + + _enqueued_shader_buffers.insert(data); +} + +/** + * Returns true if the index buffer has been queued on this GSG, false + * otherwise. + */ +bool PreparedGraphicsObjects:: +is_shader_buffer_queued(const ShaderBuffer *data) const { + ReMutexHolder holder(_lock); + + EnqueuedShaderBuffers::const_iterator qi = _enqueued_shader_buffers.find((ShaderBuffer *)data); + return (qi != _enqueued_shader_buffers.end()); +} + +/** + * Removes a buffer from the queued list of data arrays to be prepared. + * Normally it is not necessary to call this, unless you change your mind + * about preparing it at the last minute, since the data will automatically be + * dequeued and prepared at the next frame. + * + * The return value is true if the buffer is successfully dequeued, false if + * it had not been queued. + */ +bool PreparedGraphicsObjects:: +dequeue_shader_buffer(ShaderBuffer *data) { + ReMutexHolder holder(_lock); + + EnqueuedShaderBuffers::iterator qi = _enqueued_shader_buffers.find(data); + if (qi != _enqueued_shader_buffers.end()) { + _enqueued_shader_buffers.erase(qi); + return true; + } + return false; +} + +/** + * Returns true if the index buffer has been prepared on this GSG, false + * otherwise. + */ +bool PreparedGraphicsObjects:: +is_shader_buffer_prepared(const ShaderBuffer *data) const { + return data->is_prepared((PreparedGraphicsObjects *)this); +} + +/** + * Indicates that a data context, created by a previous call to + * prepare_shader_buffer(), is no longer needed. The driver resources will not + * be freed until some GSG calls update(), indicating it is at a stage where + * it is ready to release datas--this prevents conflicts from threading or + * multiple GSG's sharing datas (we have no way of knowing which graphics + * context is currently active, or what state it's in, at the time + * release_shader_buffer is called). + */ +void PreparedGraphicsObjects:: +release_shader_buffer(BufferContext *bc) { + ReMutexHolder holder(_lock); + + bool removed = (_prepared_shader_buffers.erase(bc) != 0); + nassertv(removed); + + _released_shader_buffers.insert(bc); +} + +/** + * Releases all datas at once. This will force them to be reloaded into data + * memory for all GSG's that share this object. Returns the number of datas + * released. + */ +int PreparedGraphicsObjects:: +release_all_shader_buffers() { + ReMutexHolder holder(_lock); + + int num_shader_buffers = (int)_prepared_shader_buffers.size() + (int)_enqueued_shader_buffers.size(); + + Buffers::iterator bci; + for (bci = _prepared_shader_buffers.begin(); + bci != _prepared_shader_buffers.end(); + ++bci) { + + BufferContext *bc = (BufferContext *)(*bci); + _released_shader_buffers.insert(bc); + } + + _prepared_shader_buffers.clear(); + _enqueued_shader_buffers.clear(); + + return num_shader_buffers; +} + +/** + * Returns the number of index buffers that have been enqueued to be prepared + * on this GSG. + */ +int PreparedGraphicsObjects:: +get_num_queued_shader_buffers() const { + return _enqueued_shader_buffers.size(); +} + +/** + * Returns the number of index buffers that have already been prepared on this + * GSG. + */ +int PreparedGraphicsObjects:: +get_num_prepared_shader_buffers() const { + return _prepared_shader_buffers.size(); +} + +/** + * Immediately creates a new BufferContext for the indicated data and + * returns it. This assumes that the GraphicsStateGuardian is the currently + * active rendering context and that it is ready to accept new datas. If this + * is not necessarily the case, you should use enqueue_shader_buffer() instead. + * + * Normally, this function is not called directly. Call Data::prepare_now() + * instead. + * + * The BufferContext contains all of the pertinent information needed by + * the GSG to keep track of this one particular data, and will exist as long + * as the data is ready to be rendered. + * + * When either the Data or the PreparedGraphicsObjects object destructs, the + * BufferContext will be deleted. + */ +BufferContext *PreparedGraphicsObjects:: +prepare_shader_buffer_now(ShaderBuffer *data, GraphicsStateGuardianBase *gsg) { + ReMutexHolder holder(_lock); + + // Ask the GSG to create a brand new BufferContext. There might be + // several GSG's sharing the same set of datas; if so, it doesn't matter + // which of them creates the context (since they're all shared anyway). + BufferContext *bc = gsg->prepare_shader_buffer(data); + + if (bc != (BufferContext *)NULL) { + bool prepared = _prepared_shader_buffers.insert(bc).second; + nassertr(prepared, bc); + } + + return bc; +} + /** * This is called by the GraphicsStateGuardian to indicate that it is about to * begin processing of the frame. @@ -1276,6 +1439,7 @@ begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread) { _texture_residency.begin_frame(current_thread); _vbuffer_residency.begin_frame(current_thread); _ibuffer_residency.begin_frame(current_thread); + _sbuffer_residency.begin_frame(current_thread); // Now prepare all the textures, geoms, and buffers awaiting preparation. EnqueuedTextures::iterator qti; @@ -1359,6 +1523,7 @@ end_frame(Thread *current_thread) { _texture_residency.end_frame(current_thread); _vbuffer_residency.end_frame(current_thread); _ibuffer_residency.end_frame(current_thread); + _sbuffer_residency.end_frame(current_thread); } /** diff --git a/panda/src/gobj/preparedGraphicsObjects.h b/panda/src/gobj/preparedGraphicsObjects.h index cc138576de..1dbde2d305 100644 --- a/panda/src/gobj/preparedGraphicsObjects.h +++ b/panda/src/gobj/preparedGraphicsObjects.h @@ -22,6 +22,7 @@ #include "geomVertexArrayData.h" #include "geomPrimitive.h" #include "shader.h" +#include "shaderBuffer.h" #include "pointerTo.h" #include "pStatCollector.h" #include "pset.h" @@ -35,6 +36,7 @@ class GeomContext; class ShaderContext; class VertexBufferContext; class IndexBufferContext; +class BufferContext; class GraphicsStateGuardianBase; /** @@ -142,6 +144,19 @@ PUBLISHED: prepare_index_buffer_now(GeomPrimitive *data, GraphicsStateGuardianBase *gsg); + void enqueue_shader_buffer(ShaderBuffer *data); + bool is_shader_buffer_queued(const ShaderBuffer *data) const; + bool dequeue_shader_buffer(ShaderBuffer *data); + bool is_shader_buffer_prepared(const ShaderBuffer *data) const; + void release_shader_buffer(BufferContext *bc); + int release_all_shader_buffers(); + int get_num_queued_shader_buffers() const; + int get_num_prepared_shader_buffers() const; + + BufferContext * + prepare_shader_buffer_now(ShaderBuffer *data, + GraphicsStateGuardianBase *gsg); + public: void begin_frame(GraphicsStateGuardianBase *gsg, Thread *current_thread); @@ -160,6 +175,7 @@ private: typedef phash_set Buffers; typedef phash_set< PT(GeomVertexArrayData) > EnqueuedVertexBuffers; typedef phash_set< PT(GeomPrimitive) > EnqueuedIndexBuffers; + typedef phash_set< PT(ShaderBuffer) > EnqueuedShaderBuffers; // Sampler states are stored a little bit differently, as they are mapped by // value and can't store the list of prepared samplers. @@ -207,6 +223,8 @@ private: EnqueuedVertexBuffers _enqueued_vertex_buffers; Buffers _prepared_index_buffers, _released_index_buffers; EnqueuedIndexBuffers _enqueued_index_buffers; + Buffers _prepared_shader_buffers, _released_shader_buffers; + EnqueuedShaderBuffers _enqueued_shader_buffers; BufferCache _vertex_buffer_cache; BufferCacheLRU _vertex_buffer_cache_lru; @@ -220,6 +238,7 @@ public: BufferResidencyTracker _texture_residency; BufferResidencyTracker _vbuffer_residency; BufferResidencyTracker _ibuffer_residency; + BufferResidencyTracker _sbuffer_residency; AdaptiveLru _graphics_memory_lru; SimpleLru _sampler_object_lru; diff --git a/panda/src/gobj/shader.cxx b/panda/src/gobj/shader.cxx index e067c18582..27a055a794 100644 --- a/panda/src/gobj/shader.cxx +++ b/panda/src/gobj/shader.cxx @@ -2380,6 +2380,12 @@ do_read_source(string &into, const Filename &fn, BamCacheRecord *record) { _last_modified = max(_last_modified, vf->get_timestamp()); _source_files.push_back(vf->get_filename()); } + + // Strip trailing whitespace. + while (!into.empty() && isspace(into[into.size() - 1])) { + into.resize(into.size() - 1); + } + return true; } @@ -2521,6 +2527,11 @@ r_preprocess_source(ostream &out, const Filename &fn, line += line2.substr(block_end + 2); } + // Strip trailing whitespace. + while (!line.empty() && isspace(line[line.size() - 1])) { + line.resize(line.size() - 1); + } + // Check if this line contains a #directive. char directive[64]; if (line.size() < 8 || sscanf(line.c_str(), " # %63s", directive) != 1) { @@ -2940,6 +2951,14 @@ load(const Filename &file, ShaderLanguage lang) { } _load_table[sfile] = shader; + + if (cache_generated_shaders) { + ShaderTable::const_iterator i = _make_table.find(shader->_text); + if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) { + return i->second; + } + _make_table[shader->_text] = shader; + } return shader; } @@ -2970,6 +2989,14 @@ load(ShaderLanguage lang, const Filename &vertex, } _load_table[sfile] = shader; + + if (cache_generated_shaders) { + ShaderTable::const_iterator i = _make_table.find(shader->_text); + if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) { + return i->second; + } + _make_table[shader->_text] = shader; + } return shader; } @@ -3025,14 +3052,21 @@ load_compute(ShaderLanguage lang, const Filename &fn) { if (!shader->read(sfile, record)) { return NULL; } + _load_table[sfile] = shader; + + if (cache_generated_shaders) { + ShaderTable::const_iterator i = _make_table.find(shader->_text); + if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) { + return i->second; + } + _make_table[shader->_text] = shader; + } // It makes little sense to cache the shader before compilation, so we keep // the record for when we have the compiled the shader. swap(shader->_record, record); shader->_cache_compiled_shader = BamCache::get_global_ptr()->get_cache_compiled_shaders(); shader->_fullpath = shader->_source_files[0]; - - _load_table[sfile] = shader; return shader; } @@ -3166,7 +3200,6 @@ make_compute(ShaderLanguage lang, const string &body) { sbody._separate = true; sbody._compute = body; - if (cache_generated_shaders) { ShaderTable::const_iterator i = _make_table.find(sbody); if (i != _make_table.end() && (lang == SL_none || lang == i->second->_language)) { diff --git a/panda/src/gobj/shaderBuffer.I b/panda/src/gobj/shaderBuffer.I new file mode 100644 index 0000000000..eb51f42f4e --- /dev/null +++ b/panda/src/gobj/shaderBuffer.I @@ -0,0 +1,63 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file shaderBuffer.I + * @author rdb + * @date 2016-12-12 + */ + +/** + * Creates an uninitialized buffer object with the given size. For now, these + * parameters cannot be modified, but this may change in the future. + */ +INLINE ShaderBuffer:: +ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint) : + Namable(name), + _data_size_bytes(size), + _usage_hint(usage_hint) { +} + +/** + * Creates a buffer object initialized with the given data. For now, these + * parameters cannot be modified, but this may change in the future. + */ +INLINE ShaderBuffer:: +ShaderBuffer(const string &name, pvector initial_data, UsageHint usage_hint) : + Namable(name), + _data_size_bytes(initial_data.size()), + _usage_hint(usage_hint), + _initial_data(initial_data) { +} + +/** + * Returns the buffer size in bytes. + */ +INLINE uint64_t ShaderBuffer:: +get_data_size_bytes() const { + return _data_size_bytes; +} + +/** + * Returns the buffer usage hint. + */ +INLINE GeomEnums::UsageHint ShaderBuffer:: +get_usage_hint() const { + return _usage_hint; +} + +/** + * Returns a pointer to the initial buffer data, or NULL if not specified. + */ +INLINE const unsigned char *ShaderBuffer:: +get_initial_data() const { + if (_initial_data.empty()) { + return NULL; + } else { + return &_initial_data[0]; + } +} diff --git a/panda/src/gobj/shaderBuffer.cxx b/panda/src/gobj/shaderBuffer.cxx new file mode 100644 index 0000000000..a7cd5f8150 --- /dev/null +++ b/panda/src/gobj/shaderBuffer.cxx @@ -0,0 +1,193 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file shaderBuffer.cxx + * @author rdb + * @date 2016-12-12 + */ + +#include "shaderBuffer.h" +#include "preparedGraphicsObjects.h" + +TypeHandle ShaderBuffer::_type_handle; + +/** + * + */ +void ShaderBuffer:: +output(ostream &out) const { + out << "buffer " << get_name() << ", " << _data_size_bytes << "B, " << _usage_hint; +} + +/** + * Indicates that the data should be enqueued to be prepared in the indicated + * prepared_objects at the beginning of the next frame. This will ensure the + * data is already loaded into the GSG if it is expected to be rendered soon. + * + * Use this function instead of prepare_now() to preload datas from a user + * interface standpoint. + */ +void ShaderBuffer:: +prepare(PreparedGraphicsObjects *prepared_objects) { + prepared_objects->enqueue_shader_buffer(this); +} + +/** + * Returns true if the data has already been prepared or enqueued for + * preparation on the indicated GSG, false otherwise. + */ +bool ShaderBuffer:: +is_prepared(PreparedGraphicsObjects *prepared_objects) const { + if (_contexts == (Contexts *)NULL) { + return false; + } + Contexts::const_iterator ci; + ci = _contexts->find(prepared_objects); + if (ci != _contexts->end()) { + return true; + } + return prepared_objects->is_shader_buffer_queued(this); +} + +/** + * Creates a context for the data on the particular GSG, if it does not + * already exist. Returns the new (or old) BufferContext. This assumes + * that the GraphicsStateGuardian is the currently active rendering context + * and that it is ready to accept new datas. If this is not necessarily the + * case, you should use prepare() instead. + * + * Normally, this is not called directly except by the GraphicsStateGuardian; + * a data does not need to be explicitly prepared by the user before it may be + * rendered. + */ +BufferContext *ShaderBuffer:: +prepare_now(PreparedGraphicsObjects *prepared_objects, + GraphicsStateGuardianBase *gsg) { + if (_contexts == (Contexts *)NULL) { + _contexts = new Contexts; + } + Contexts::const_iterator ci; + ci = _contexts->find(prepared_objects); + if (ci != _contexts->end()) { + return (*ci).second; + } + + BufferContext *vbc = prepared_objects->prepare_shader_buffer_now(this, gsg); + if (vbc != (BufferContext *)NULL) { + (*_contexts)[prepared_objects] = vbc; + } + return vbc; +} + +/** + * Frees the data context only on the indicated object, if it exists there. + * Returns true if it was released, false if it had not been prepared. + */ +bool ShaderBuffer:: +release(PreparedGraphicsObjects *prepared_objects) { + if (_contexts != (Contexts *)NULL) { + Contexts::iterator ci; + ci = _contexts->find(prepared_objects); + if (ci != _contexts->end()) { + BufferContext *vbc = (*ci).second; + prepared_objects->release_shader_buffer(vbc); + return true; + } + } + + // Maybe it wasn't prepared yet, but it's about to be. + return prepared_objects->dequeue_shader_buffer(this); +} + +/** + * Frees the context allocated on all objects for which the data has been + * declared. Returns the number of contexts which have been freed. + */ +int ShaderBuffer:: +release_all() { + int num_freed = 0; + + if (_contexts != (Contexts *)NULL) { + // We have to traverse a copy of the _contexts list, because the + // PreparedGraphicsObjects object will call clear_prepared() in response + // to each release_shader_buffer(), and we don't want to be modifying the + // _contexts list while we're traversing it. + Contexts temp = *_contexts; + num_freed = (int)_contexts->size(); + + Contexts::const_iterator ci; + for (ci = temp.begin(); ci != temp.end(); ++ci) { + PreparedGraphicsObjects *prepared_objects = (*ci).first; + BufferContext *vbc = (*ci).second; + prepared_objects->release_shader_buffer(vbc); + } + + // Now that we've called release_shader_buffer() on every known context, + // the _contexts list should have completely emptied itself. + nassertr(_contexts == NULL, num_freed); + } + + return num_freed; +} + +/** + * Tells the BamReader how to create objects of type ParamValue. + */ +void ShaderBuffer:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void ShaderBuffer:: +write_datagram(BamWriter *manager, Datagram &dg) { + dg.add_string(get_name()); + dg.add_uint64(_data_size_bytes); + dg.add_uint8(_usage_hint); + dg.add_bool(!_initial_data.empty()); + dg.append_data(_initial_data.data(), _initial_data.size()); +} + +/** + * This function is called by the BamReader's factory when a new object of + * type ParamValue is encountered in the Bam file. It should create the + * ParamValue and extract its information from the file. + */ +TypedWritable *ShaderBuffer:: +make_from_bam(const FactoryParams ¶ms) { + ShaderBuffer *param = new ShaderBuffer; + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + param->fillin(scan, manager); + + return param; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new ParamValue. + */ +void ShaderBuffer:: +fillin(DatagramIterator &scan, BamReader *manager) { + set_name(scan.get_string()); + _data_size_bytes = scan.get_uint64(); + _usage_hint = (UsageHint)scan.get_uint8(); + + if (scan.get_bool() && _data_size_bytes > 0) { + nassertv_always(_data_size_bytes <= scan.get_remaining_size()); + _initial_data.resize(_data_size_bytes); + scan.extract_bytes(&_initial_data[0], _data_size_bytes); + } else { + _initial_data.clear(); + } +} diff --git a/panda/src/gobj/shaderBuffer.h b/panda/src/gobj/shaderBuffer.h new file mode 100644 index 0000000000..549ebdc536 --- /dev/null +++ b/panda/src/gobj/shaderBuffer.h @@ -0,0 +1,100 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file shaderBuffer.h + * @author rdb + * @date 2016-12-12 + */ + +#ifndef SHADERBUFFER_H +#define SHADERBUFFER_H + +#include "pandabase.h" +#include "typedWritableReferenceCount.h" +#include "namable.h" +#include "geomEnums.h" +#include "graphicsStateGuardianBase.h" +#include "factoryParams.h" + +class BufferContext; +class PreparedGraphicsObjects; + +/** + * This is a generic buffer object that lives in graphics memory. + */ +class EXPCL_PANDA_GOBJ ShaderBuffer : public TypedWritableReferenceCount, public Namable, public GeomEnums { +private: + INLINE ShaderBuffer() DEFAULT_CTOR; + +PUBLISHED: + INLINE ShaderBuffer(const string &name, uint64_t size, UsageHint usage_hint); + INLINE ShaderBuffer(const string &name, pvector initial_data, UsageHint usage_hint); + +public: + INLINE uint64_t get_data_size_bytes() const; + INLINE UsageHint get_usage_hint() const; + INLINE const unsigned char *get_initial_data() const; + + virtual void output(ostream &out) const; + +PUBLISHED: + MAKE_PROPERTY(data_size_bytes, get_data_size_bytes); + MAKE_PROPERTY(usage_hint, get_usage_hint); + + void prepare(PreparedGraphicsObjects *prepared_objects); + bool is_prepared(PreparedGraphicsObjects *prepared_objects) const; + + BufferContext *prepare_now(PreparedGraphicsObjects *prepared_objects, + GraphicsStateGuardianBase *gsg); + bool release(PreparedGraphicsObjects *prepared_objects); + int release_all(); + +private: + uint64_t _data_size_bytes; + UsageHint _usage_hint; + pvector _initial_data; + + typedef pmap Contexts; + Contexts *_contexts; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + TypedWritableReferenceCount::init_type(); + Namable::init_type(); + register_type(_type_handle, "ShaderBuffer", + TypedWritableReferenceCount::get_class_type(), + Namable::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + +INLINE ostream &operator << (ostream &out, const ShaderBuffer &m) { + m.output(out); + return out; +} + +#include "shaderBuffer.I" + +#endif diff --git a/panda/src/gobj/shaderContext.h b/panda/src/gobj/shaderContext.h index 2b356cef8f..7a5312faa2 100644 --- a/panda/src/gobj/shaderContext.h +++ b/panda/src/gobj/shaderContext.h @@ -42,6 +42,7 @@ public: INLINE virtual bool update_shader_vertex_arrays(ShaderContext *prev, bool force) { return false; }; INLINE virtual void disable_shader_texture_bindings() {}; INLINE virtual void update_shader_texture_bindings(ShaderContext *prev) {}; + INLINE virtual void update_shader_buffer_bindings(ShaderContext *prev) {}; INLINE virtual bool uses_standard_vertex_arrays(void) { return true; }; INLINE virtual bool uses_custom_vertex_arrays(void) { return false; }; diff --git a/panda/src/gobj/texture.cxx b/panda/src/gobj/texture.cxx index 8fa7226806..115d9529bf 100644 --- a/panda/src/gobj/texture.cxx +++ b/panda/src/gobj/texture.cxx @@ -3643,13 +3643,13 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) header.pf.four_cc == 0x30315844) { // 'DX10' // A DirectX 10 style texture, which has an additional header. func = read_dds_level_generic_uncompressed; - unsigned int format = dds.get_uint32(); + unsigned int dxgi_format = dds.get_uint32(); unsigned int dimension = dds.get_uint32(); unsigned int misc_flag = dds.get_uint32(); unsigned int array_size = dds.get_uint32(); /*unsigned int alpha_mode = */dds.get_uint32(); - switch (format) { + switch (dxgi_format) { case 2: // DXGI_FORMAT_R32G32B32A32_FLOAT format = F_rgba32; component_type = T_float; @@ -3665,6 +3665,11 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) component_type = T_unsigned_short; func = read_dds_level_abgr16; break; + case 16: // DXGI_FORMAT_R32G32_FLOAT + format = F_rg32; + component_type = T_float; + func = read_dds_level_raw; + break; case 27: // DXGI_FORMAT_R8G8B8A8_TYPELESS case 28: // DXGI_FORMAT_R8G8B8A8_UNORM format = F_rgba8; @@ -3688,6 +3693,41 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) component_type = T_byte; func = read_dds_level_abgr8; break; + case 34: // DXGI_FORMAT_R16G16_FLOAT: + format = F_rg16; + component_type = T_half_float; + func = read_dds_level_raw; + break; + case 35: // DXGI_FORMAT_R16G16_UNORM: + format = F_rg16; + component_type = T_unsigned_short; + func = read_dds_level_raw; + break; + case 37: // DXGI_FORMAT_R16G16_SNORM: + format = F_rg16; + component_type = T_short; + func = read_dds_level_raw; + break; + case 40: // DXGI_FORMAT_D32_FLOAT + format = F_depth_component32; + component_type = T_float; + func = read_dds_level_raw; + break; + case 41: // DXGI_FORMAT_R32_FLOAT + format = F_r32; + component_type = T_float; + func = read_dds_level_raw; + break; + case 42: // DXGI_FORMAT_R32_UINT + format = F_r32i; + component_type = T_unsigned_int; + func = read_dds_level_raw; + break; + case 43: // DXGI_FORMAT_R32_SINT + format = F_r32i; + component_type = T_int; + func = read_dds_level_raw; + break; case 48: // DXGI_FORMAT_R8G8_TYPELESS case 49: // DXGI_FORMAT_R8G8_UNORM format = F_rg; @@ -3703,6 +3743,36 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) format = F_rg8i; component_type = T_byte; break; + case 54: // DXGI_FORMAT_R16_FLOAT: + format = F_r16; + component_type = T_half_float; + func = read_dds_level_raw; + break; + case 55: // DXGI_FORMAT_D16_UNORM: + format = F_depth_component16; + component_type = T_unsigned_short; + func = read_dds_level_raw; + break; + case 56: // DXGI_FORMAT_R16_UNORM: + format = F_r16; + component_type = T_unsigned_short; + func = read_dds_level_raw; + break; + case 57: // DXGI_FORMAT_R16_UINT: + format = F_r16i; + component_type = T_unsigned_short; + func = read_dds_level_raw; + break; + case 58: // DXGI_FORMAT_R16_SNORM: + format = F_r16; + component_type = T_short; + func = read_dds_level_raw; + break; + case 59: // DXGI_FORMAT_R16_SINT: + format = F_r16i; + component_type = T_short; + func = read_dds_level_raw; + break; case 60: // DXGI_FORMAT_R8_TYPELESS case 61: // DXGI_FORMAT_R8_UNORM format = F_red; @@ -3760,7 +3830,6 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) compression = CM_rgtc; func = read_dds_level_bc4; break; - break; case 82: // DXGI_FORMAT_BC5_TYPELESS case 83: // DXGI_FORMAT_BC5_UNORM format = F_rg; @@ -3786,7 +3855,7 @@ do_read_dds(CData *cdata, istream &in, const string &filename, bool header_only) break; default: gobj_cat.error() - << filename << ": unsupported DXGI format " << format << ".\n"; + << filename << ": unsupported DXGI format " << dxgi_format << ".\n"; return false; } @@ -7729,10 +7798,11 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, // Most common case: one byte per pixel, and the source image shows a // maxval of 255. No scaling is necessary. Because this is such a common // case, we break it out per component for best performance. + const xel *array = pnmimage.get_array(); switch (num_components) { case 1: for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); + const xel *row = array + j * x_size; for (int i = 0; i < x_size; i++) { *p++ = (uchar)PPM_GETB(row[i]); } @@ -7742,9 +7812,10 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, case 2: if (img_has_alpha) { + const xelval *alpha = pnmimage.get_alpha_array(); for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); - xelval *alpha_row = pnmimage.alpha_row(j); + const xel *row = array + j * x_size; + const xelval *alpha_row = alpha + j * x_size; for (int i = 0; i < x_size; i++) { *p++ = (uchar)PPM_GETB(row[i]); *p++ = (uchar)alpha_row[i]; @@ -7753,7 +7824,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, } } else { for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); + const xel *row = array + j * x_size; for (int i = 0; i < x_size; i++) { *p++ = (uchar)PPM_GETB(row[i]); *p++ = (uchar)255; @@ -7765,7 +7836,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, case 3: for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); + const xel *row = array + j * x_size; for (int i = 0; i < x_size; i++) { *p++ = (uchar)PPM_GETB(row[i]); *p++ = (uchar)PPM_GETG(row[i]); @@ -7777,9 +7848,10 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, case 4: if (img_has_alpha) { + const xelval *alpha = pnmimage.get_alpha_array(); for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); - xelval *alpha_row = pnmimage.alpha_row(j); + const xel *row = array + j * x_size; + const xelval *alpha_row = alpha + j * x_size; for (int i = 0; i < x_size; i++) { *p++ = (uchar)PPM_GETB(row[i]); *p++ = (uchar)PPM_GETG(row[i]); @@ -7790,7 +7862,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, } } else { for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); + const xel *row = array + j * x_size; for (int i = 0; i < x_size; i++) { *p++ = (uchar)PPM_GETB(row[i]); *p++ = (uchar)PPM_GETG(row[i]); @@ -7813,7 +7885,7 @@ convert_from_pnmimage(PTA_uchar &image, size_t page_size, for (int j = y_size-1; j >= 0; j--) { for (int i = 0; i < x_size; i++) { if (is_grayscale) { - store_unscaled_short(p, pnmimage.get_gray_val(i, j)); + store_unscaled_short(p, pnmimage.get_gray_val(i, j)); } else { store_unscaled_short(p, pnmimage.get_blue_val(i, j)); store_unscaled_short(p, pnmimage.get_green_val(i, j)); @@ -7979,11 +8051,13 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, const unsigned char *p = &image[idx]; if (component_width == 1) { + xel *array = pnmimage.get_array(); if (is_grayscale) { if (has_alpha) { + xelval *alpha = pnmimage.get_alpha_array(); for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); - xelval *alpha_row = pnmimage.alpha_row(j); + xel *row = array + j * x_size; + xelval *alpha_row = alpha + j * x_size; for (int i = 0; i < x_size; i++) { PPM_PUTB(row[i], *p++); alpha_row[i] = *p++; @@ -7991,7 +8065,7 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, } } else { for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); + xel *row = array + j * x_size; for (int i = 0; i < x_size; i++) { PPM_PUTB(row[i], *p++); } @@ -7999,9 +8073,10 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, } } else { if (has_alpha) { + xelval *alpha = pnmimage.get_alpha_array(); for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); - xelval *alpha_row = pnmimage.alpha_row(j); + xel *row = array + j * x_size; + xelval *alpha_row = alpha + j * x_size; for (int i = 0; i < x_size; i++) { PPM_PUTB(row[i], *p++); PPM_PUTG(row[i], *p++); @@ -8011,7 +8086,7 @@ convert_to_pnmimage(PNMImage &pnmimage, int x_size, int y_size, } } else { for (int j = y_size-1; j >= 0; j--) { - xel *row = pnmimage.row(j); + xel *row = array + j * x_size; for (int i = 0; i < x_size; i++) { PPM_PUTB(row[i], *p++); PPM_PUTG(row[i], *p++); @@ -8258,6 +8333,7 @@ read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, int n size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n); size_t row_bytes = x_size * 16; + nassertr(row_bytes * y_size == size, PTA_uchar()); PTA_uchar image = PTA_uchar::empty_array(size); for (int y = y_size - 1; y >= 0; --y) { unsigned char *p = image.p() + y * row_bytes; @@ -8274,6 +8350,26 @@ read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, int n return image; } +/** + * Called by read_dds for a DDS file that needs no transformations applied. + */ +PTA_uchar Texture:: +read_dds_level_raw(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in) { + int x_size = tex->do_get_expected_mipmap_x_size(cdata, n); + int y_size = tex->do_get_expected_mipmap_y_size(cdata, n); + + size_t size = tex->do_get_expected_ram_mipmap_page_size(cdata, n); + size_t row_bytes = x_size * cdata->_num_components * cdata->_component_width; + nassertr(row_bytes * y_size == size, PTA_uchar()); + PTA_uchar image = PTA_uchar::empty_array(size); + for (int y = y_size - 1; y >= 0; --y) { + unsigned char *p = image.p() + y * row_bytes; + in.read((char *)p, row_bytes); + } + + return image; +} + /** * Called by read_dds for a DDS file whose format isn't one we've specifically * optimized. @@ -8932,13 +9028,20 @@ compare_images(const PNMImage &a, const PNMImage &b) { nassertr(a.get_x_size() == b.get_x_size() && a.get_y_size() == b.get_y_size(), false); + const xel *a_array = a.get_array(); + const xel *b_array = b.get_array(); + const xelval *a_alpha = a.get_alpha_array(); + const xelval *b_alpha = b.get_alpha_array(); + + int x_size = a.get_x_size(); + int delta = 0; for (int yi = 0; yi < a.get_y_size(); ++yi) { - xel *a_row = a.row(yi); - xel *b_row = b.row(yi); - xelval *a_alpha_row = a.alpha_row(yi); - xelval *b_alpha_row = b.alpha_row(yi); - for (int xi = 0; xi < a.get_x_size(); ++xi) { + const xel *a_row = a_array + yi * x_size; + const xel *b_row = b_array + yi * x_size; + const xelval *a_alpha_row = a_alpha + yi * x_size; + const xelval *b_alpha_row = b_alpha + yi * x_size; + for (int xi = 0; xi < x_size; ++xi) { delta += abs(PPM_GETR(a_row[xi]) - PPM_GETR(b_row[xi])); delta += abs(PPM_GETG(a_row[xi]) - PPM_GETG(b_row[xi])); delta += abs(PPM_GETB(a_row[xi]) - PPM_GETB(b_row[xi])); diff --git a/panda/src/gobj/texture.h b/panda/src/gobj/texture.h index cd05019416..e40ca6f8e5 100644 --- a/panda/src/gobj/texture.h +++ b/panda/src/gobj/texture.h @@ -817,6 +817,8 @@ private: int n, istream &in); static PTA_uchar read_dds_level_abgr32(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in); + static PTA_uchar read_dds_level_raw(Texture *tex, CData *cdata, const DDSHeader &header, + int n, istream &in); static PTA_uchar read_dds_level_generic_uncompressed(Texture *tex, CData *cdata, const DDSHeader &header, int n, istream &in); diff --git a/panda/src/grutil/config_grutil.cxx b/panda/src/grutil/config_grutil.cxx index 5564a3719d..94613f4da6 100644 --- a/panda/src/grutil/config_grutil.cxx +++ b/panda/src/grutil/config_grutil.cxx @@ -131,6 +131,6 @@ init_libgrutil() { MovieTexture::register_with_read_factory(); TexturePool *ts = TexturePool::get_global_ptr(); - ts->register_texture_type(MovieTexture::make_texture, "avi m2v mov mpg mpeg mp4 wmv asf flv nut ogm mkv ogv"); + ts->register_texture_type(MovieTexture::make_texture, "avi m2v mov mpg mpeg mp4 wmv asf flv nut ogm mkv ogv webm"); #endif } diff --git a/panda/src/gsgbase/graphicsStateGuardianBase.h b/panda/src/gsgbase/graphicsStateGuardianBase.h index 90455869ac..c45f36aca0 100644 --- a/panda/src/gsgbase/graphicsStateGuardianBase.h +++ b/panda/src/gsgbase/graphicsStateGuardianBase.h @@ -31,6 +31,7 @@ class GraphicsOutputBase; class VertexBufferContext; class IndexBufferContext; +class BufferContext; class GeomContext; class GeomNode; class Geom; @@ -57,6 +58,7 @@ class SamplerContext; class SamplerState; class Shader; class ShaderContext; +class ShaderBuffer; class RenderState; class TransformState; class Material; @@ -162,6 +164,9 @@ public: virtual IndexBufferContext *prepare_index_buffer(GeomPrimitive *data)=0; virtual void release_index_buffer(IndexBufferContext *ibc)=0; + virtual BufferContext *prepare_shader_buffer(ShaderBuffer *data)=0; + virtual void release_shader_buffer(BufferContext *ibc)=0; + virtual void dispatch_compute(int size_x, int size_y, int size_z)=0; virtual PT(GeomMunger) get_geom_munger(const RenderState *state, diff --git a/panda/src/movies/dr_flac.h b/panda/src/movies/dr_flac.h index 26d133191d..fdac6a2f79 100644 --- a/panda/src/movies/dr_flac.h +++ b/panda/src/movies/dr_flac.h @@ -534,7 +534,9 @@ static DRFLAC_INLINE uint64_t drflac__swap_endian_uint64(uint64_t n) static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n) { -#ifdef __linux__ +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER == __ORDER_LITTLE_ENDIAN__) + return drflac__swap_endian_uint32(n); +#elif defined(__linux__) return be32toh(n); #else if (drflac__is_little_endian()) { @@ -547,7 +549,9 @@ static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n) static DRFLAC_INLINE uint64_t drflac__be2host_64(uint64_t n) { -#ifdef __linux__ +#if defined(__BYTE_ORDER__) && (__BYTE_ORDER == __ORDER_LITTLE_ENDIAN__) + return drflac__swap_endian_uint64(n); +#elif defined(__linux__) return be64toh(n); #else if (drflac__is_little_endian()) { diff --git a/panda/src/pgraph/light.h b/panda/src/pgraph/light.h index 05fa4c10f6..eeca3409bd 100644 --- a/panda/src/pgraph/light.h +++ b/panda/src/pgraph/light.h @@ -90,6 +90,7 @@ protected: CP_point_priority, CP_directional_priority, CP_spot_priority, + CP_area_priority, }; private: diff --git a/panda/src/pgraph/nodePath.I b/panda/src/pgraph/nodePath.I index 1e9813debc..cfcef09112 100644 --- a/panda/src/pgraph/nodePath.I +++ b/panda/src/pgraph/nodePath.I @@ -1253,6 +1253,14 @@ set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z set_shader_input(new ShaderInput(id, tex, read, write, z, n, priority)); } +/** + * + */ +INLINE void NodePath:: +set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority) { + set_shader_input(new ShaderInput(id, buf, priority)); +} + /** * */ diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index a30fc1d40d..1bbe0845cf 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -61,6 +61,7 @@ class GlobPattern; class PreparedGraphicsObjects; class SamplerState; class Shader; +class ShaderBuffer; class ShaderInput; // @@ -632,6 +633,7 @@ PUBLISHED: INLINE void set_shader_input(CPT_InternalName id, Texture *tex, int priority=0); INLINE void set_shader_input(CPT_InternalName id, Texture *tex, const SamplerState &sampler, int priority=0); INLINE void set_shader_input(CPT_InternalName id, Texture *tex, bool read, bool write, int z=-1, int n=0, int priority=0); + INLINE void set_shader_input(CPT_InternalName id, ShaderBuffer *buf, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const NodePath &np, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const PTA_float &v, int priority=0); INLINE void set_shader_input(CPT_InternalName id, const PTA_double &v, int priority=0); diff --git a/panda/src/pgraph/shaderAttrib.cxx b/panda/src/pgraph/shaderAttrib.cxx index 93ccf45bc2..2e6ac7cd77 100644 --- a/panda/src/pgraph/shaderAttrib.cxx +++ b/panda/src/pgraph/shaderAttrib.cxx @@ -25,6 +25,7 @@ #include "datagram.h" #include "datagramIterator.h" #include "nodePath.h" +#include "shaderBuffer.h" TypeHandle ShaderAttrib::_type_handle; int ShaderAttrib::_attrib_slot; @@ -429,7 +430,8 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const { nassertr(!np.is_empty(), LMatrix4::ident_mat()); return np.get_transform()->get_mat(); - } else if (p->get_value_type() == ShaderInput::M_numeric && p->get_ptr()._size == 16) { + } else if (p->get_value_type() == ShaderInput::M_numeric && + p->get_ptr()._size >= 16 && (p->get_ptr()._size & 15) == 0) { const Shader::ShaderPtrData &ptr = p->get_ptr(); switch (ptr._type) { @@ -455,12 +457,40 @@ get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const { } ostringstream strm; - strm << "Shader input " << id->get_name() << " is not a NodePath or LMatrix4.\n"; + strm << "Shader input " << id->get_name() << " is not a NodePath, LMatrix4 or PTA_LMatrix4.\n"; nassert_raise(strm.str()); return LMatrix4::ident_mat(); } } +/** + * Returns the ShaderInput as a ShaderBuffer. Assertion fails if there is + * none, or if it is not a ShaderBuffer. + */ +ShaderBuffer *ShaderAttrib:: +get_shader_input_buffer(const InternalName *id) const { + Inputs::const_iterator i = _inputs.find(id); + if (i == _inputs.end()) { + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not present.\n"; + nassert_raise(strm.str()); + return NULL; + } else { + const ShaderInput *p = (*i).second; + + if (p->get_value_type() == ShaderInput::M_buffer) { + ShaderBuffer *value; + DCAST_INTO_R(value, p->_value, NULL); + return value; + } + + ostringstream strm; + strm << "Shader input " << id->get_name() << " is not a ShaderBuffer.\n"; + nassert_raise(strm.str()); + return NULL; + } +} + /** * Returns the shader object associated with the node. If get_override * returns true, but get_shader returns NULL, that means that this attribute diff --git a/panda/src/pgraph/shaderAttrib.h b/panda/src/pgraph/shaderAttrib.h index 08676286f5..5a28e929b1 100644 --- a/panda/src/pgraph/shaderAttrib.h +++ b/panda/src/pgraph/shaderAttrib.h @@ -111,6 +111,7 @@ PUBLISHED: Texture *get_shader_input_texture(const InternalName *id, SamplerState *sampler=NULL) const; const Shader::ShaderPtrData *get_shader_input_ptr(const InternalName *id) const; const LMatrix4 &get_shader_input_matrix(const InternalName *id, LMatrix4 &matrix) const; + ShaderBuffer *get_shader_input_buffer(const InternalName *id) const; static void register_with_read_factory(); diff --git a/panda/src/pgraph/shaderInput.I b/panda/src/pgraph/shaderInput.I index 52f46369ad..7344717e47 100644 --- a/panda/src/pgraph/shaderInput.I +++ b/panda/src/pgraph/shaderInput.I @@ -55,6 +55,18 @@ ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority) : { } +/** + * + */ +INLINE ShaderInput:: +ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority) : + _name(MOVE(name)), + _type(M_buffer), + _priority(priority), + _value(buf) +{ +} + /** * */ diff --git a/panda/src/pgraph/shaderInput.h b/panda/src/pgraph/shaderInput.h index 260d5a6ab1..c91c790618 100644 --- a/panda/src/pgraph/shaderInput.h +++ b/panda/src/pgraph/shaderInput.h @@ -31,6 +31,7 @@ #include "samplerState.h" #include "shader.h" #include "texture.h" +#include "shaderBuffer.h" /** * This is a small container class that can hold any one of the value types @@ -52,6 +53,7 @@ PUBLISHED: INLINE ShaderInput(CPT_InternalName name, int priority=0); INLINE ShaderInput(CPT_InternalName name, Texture *tex, int priority=0); INLINE ShaderInput(CPT_InternalName name, ParamValueBase *param, int priority=0); + INLINE ShaderInput(CPT_InternalName name, ShaderBuffer *buf, int priority=0); INLINE ShaderInput(CPT_InternalName name, const PTA_float &ptr, int priority=0); INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase4f &ptr, int priority=0); INLINE ShaderInput(CPT_InternalName name, const PTA_LVecBase3f &ptr, int priority=0); @@ -96,7 +98,8 @@ PUBLISHED: M_numeric, M_texture_sampler, M_param, - M_texture_image + M_texture_image, + M_buffer, }; INLINE const InternalName *get_name() const; @@ -123,6 +126,8 @@ private: int _priority; int _type; + friend class ShaderAttrib; + public: static TypeHandle get_class_type() { return _type_handle; diff --git a/panda/src/pgraphnodes/config_pgraphnodes.cxx b/panda/src/pgraphnodes/config_pgraphnodes.cxx index 47b0ed3baf..f512110858 100644 --- a/panda/src/pgraphnodes/config_pgraphnodes.cxx +++ b/panda/src/pgraphnodes/config_pgraphnodes.cxx @@ -26,6 +26,7 @@ #include "lodNode.h" #include "nodeCullCallbackData.h" #include "pointLight.h" +#include "rectangleLight.h" #include "selectiveChildNode.h" #include "sequenceNode.h" #include "shaderGenerator.h" @@ -121,6 +122,7 @@ init_libpgraphnodes() { LODNode::init_type(); NodeCullCallbackData::init_type(); PointLight::init_type(); + RectangleLight::init_type(); SelectiveChildNode::init_type(); SequenceNode::init_type(); ShaderGenerator::init_type(); @@ -137,6 +139,7 @@ init_libpgraphnodes() { LightNode::register_with_read_factory(); LODNode::register_with_read_factory(); PointLight::register_with_read_factory(); + RectangleLight::register_with_read_factory(); SelectiveChildNode::register_with_read_factory(); SequenceNode::register_with_read_factory(); SphereLight::register_with_read_factory(); diff --git a/panda/src/pgraphnodes/p3pgraphnodes_composite2.cxx b/panda/src/pgraphnodes/p3pgraphnodes_composite2.cxx index 952a2448c9..4dd4f04bff 100644 --- a/panda/src/pgraphnodes/p3pgraphnodes_composite2.cxx +++ b/panda/src/pgraphnodes/p3pgraphnodes_composite2.cxx @@ -1,5 +1,6 @@ #include "nodeCullCallbackData.cxx" #include "pointLight.cxx" +#include "rectangleLight.cxx" #include "sceneGraphAnalyzer.cxx" #include "selectiveChildNode.cxx" #include "sequenceNode.cxx" diff --git a/panda/src/pgraphnodes/rectangleLight.I b/panda/src/pgraphnodes/rectangleLight.I new file mode 100644 index 0000000000..c97dd536f2 --- /dev/null +++ b/panda/src/pgraphnodes/rectangleLight.I @@ -0,0 +1,59 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file rectangleLight.I + * @author rdb + * @date 2016-12-19 + */ + +/** + * + */ +INLINE RectangleLight::CData:: +CData() : + _max_distance(make_inf((PN_stdfloat)0)) +{ +} + +/** + * + */ +INLINE RectangleLight::CData:: +CData(const RectangleLight::CData ©) : + _max_distance(copy._max_distance) +{ +} + +/** + * Returns the color of specular highlights generated by the light. This is + * usually the same as get_color(). + */ +INLINE const LColor &RectangleLight:: +get_specular_color() const { + return get_color(); +} + +/** + * Returns the maximum distance at which the light has any effect, as previously + * specified by set_max_distance. + */ +INLINE PN_stdfloat RectangleLight:: +get_max_distance() const { + CDReader cdata(_cycler); + return cdata->_max_distance; +} + +/** + * Sets the radius of the light's sphere of influence. Beyond this distance, the + * light may be attenuated to zero, if this is supported by the shader. + */ +INLINE void RectangleLight:: +set_max_distance(PN_stdfloat max_distance) { + CDWriter cdata(_cycler); + cdata->_max_distance = max_distance; +} diff --git a/panda/src/pgraphnodes/rectangleLight.cxx b/panda/src/pgraphnodes/rectangleLight.cxx new file mode 100644 index 0000000000..187d35cd2d --- /dev/null +++ b/panda/src/pgraphnodes/rectangleLight.cxx @@ -0,0 +1,150 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file rectangleLight.cxx + * @author rdb + * @date 2016-12-19 + */ + +#include "rectangleLight.h" +#include "graphicsStateGuardianBase.h" +#include "bamWriter.h" +#include "bamReader.h" +#include "datagram.h" +#include "datagramIterator.h" + +TypeHandle RectangleLight::_type_handle; + +/** + * + */ +CycleData *RectangleLight::CData:: +make_copy() const { + return new CData(*this); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void RectangleLight::CData:: +write_datagram(BamWriter *manager, Datagram &dg) const { + dg.add_stdfloat(_max_distance); +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new Light. + */ +void RectangleLight::CData:: +fillin(DatagramIterator &scan, BamReader *manager) { + _max_distance = scan.get_stdfloat(); +} + +/** + * + */ +RectangleLight:: +RectangleLight(const string &name) : + LightLensNode(name) +{ +} + +/** + * Do not call the copy constructor directly; instead, use make_copy() or + * copy_subgraph() to make a copy of a node. + */ +RectangleLight:: +RectangleLight(const RectangleLight ©) : + LightLensNode(copy), + _cycler(copy._cycler) +{ +} + +/** + * Returns a newly-allocated PandaNode that is a shallow copy of this one. It + * will be a different pointer, but its internal data may or may not be shared + * with that of the original PandaNode. No children will be copied. + */ +PandaNode *RectangleLight:: +make_copy() const { + return new RectangleLight(*this); +} + +/** + * + */ +void RectangleLight:: +write(ostream &out, int indent_level) const { + LightLensNode::write(out, indent_level); + indent(out, indent_level) << *this << "\n"; +} + +/** + * Returns the relative priority associated with all lights of this class. + * This priority is used to order lights whose instance priority + * (get_priority()) is the same--the idea is that other things being equal, + * AmbientLights (for instance) are less important than DirectionalLights. + */ +int RectangleLight:: +get_class_priority() const { + return (int)CP_area_priority; +} + +/** + * + */ +void RectangleLight:: +bind(GraphicsStateGuardianBase *gsg, const NodePath &light, int light_id) { +} + +/** + * Tells the BamReader how to create objects of type RectangleLight. + */ +void RectangleLight:: +register_with_read_factory() { + BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); +} + +/** + * Writes the contents of this object to the datagram for shipping out to a + * Bam file. + */ +void RectangleLight:: +write_datagram(BamWriter *manager, Datagram &dg) { + LightLensNode::write_datagram(manager, dg); + manager->write_cdata(dg, _cycler); +} + +/** + * This function is called by the BamReader's factory when a new object of + * type RectangleLight is encountered in the Bam file. It should create the + * RectangleLight and extract its information from the file. + */ +TypedWritable *RectangleLight:: +make_from_bam(const FactoryParams ¶ms) { + RectangleLight *node = new RectangleLight(""); + DatagramIterator scan; + BamReader *manager; + + parse_params(params, scan, manager); + node->fillin(scan, manager); + + return node; +} + +/** + * This internal function is called by make_from_bam to read in all of the + * relevant data from the BamFile for the new RectangleLight. + */ +void RectangleLight:: +fillin(DatagramIterator &scan, BamReader *manager) { + LightLensNode::fillin(scan, manager); + + manager->read_cdata(scan, _cycler); +} diff --git a/panda/src/pgraphnodes/rectangleLight.h b/panda/src/pgraphnodes/rectangleLight.h new file mode 100644 index 0000000000..00d4d586ea --- /dev/null +++ b/panda/src/pgraphnodes/rectangleLight.h @@ -0,0 +1,98 @@ +/** + * PANDA 3D SOFTWARE + * Copyright (c) Carnegie Mellon University. All rights reserved. + * + * All use of this software is subject to the terms of the revised BSD + * license. You should have received a copy of this license along + * with this source code in a file named "LICENSE." + * + * @file rectangleLight.h + * @author rdb + * @date 2016-12-19 + */ + +#ifndef RECTANGLELIGHT_H +#define RECTANGLELIGHT_H + +#include "pandabase.h" + +#include "lightLensNode.h" +#include "pointLight.h" + +/** + * This is a type of area light that is an axis aligned rectangle, pointing + * along the Y axis in the positive direction. + */ +class EXPCL_PANDA_PGRAPHNODES RectangleLight : public LightLensNode { +PUBLISHED: + RectangleLight(const string &name); + +protected: + RectangleLight(const RectangleLight ©); + +public: + virtual PandaNode *make_copy() const; + virtual void write(ostream &out, int indent_level) const; + +PUBLISHED: + INLINE const LColor &get_specular_color() const FINAL; + + INLINE PN_stdfloat get_max_distance() const; + INLINE void set_max_distance(PN_stdfloat max_distance); + MAKE_PROPERTY(max_distance, get_max_distance, set_max_distance); + + virtual int get_class_priority() const; + +public: + virtual void bind(GraphicsStateGuardianBase *gsg, const NodePath &light, + int light_id); + +private: + // This is the data that must be cycled between pipeline stages. + class EXPCL_PANDA_PGRAPHNODES CData : public CycleData { + public: + INLINE CData(); + INLINE CData(const CData ©); + virtual CycleData *make_copy() const; + virtual void write_datagram(BamWriter *manager, Datagram &dg) const; + virtual void fillin(DatagramIterator &scan, BamReader *manager); + virtual TypeHandle get_parent_type() const { + return RectangleLight::get_class_type(); + } + + PN_stdfloat _max_distance; + }; + + PipelineCycler _cycler; + typedef CycleDataReader CDReader; + typedef CycleDataWriter CDWriter; + +public: + static void register_with_read_factory(); + virtual void write_datagram(BamWriter *manager, Datagram &dg); + +protected: + static TypedWritable *make_from_bam(const FactoryParams ¶ms); + void fillin(DatagramIterator &scan, BamReader *manager); + +public: + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + LightLensNode::init_type(); + register_type(_type_handle, "RectangleLight", + LightLensNode::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 "rectangleLight.I" + +#endif diff --git a/panda/src/pipeline/pythonThread.cxx b/panda/src/pipeline/pythonThread.cxx index 976e13aa57..28bef610b5 100644 --- a/panda/src/pipeline/pythonThread.cxx +++ b/panda/src/pipeline/pythonThread.cxx @@ -37,6 +37,13 @@ PythonThread(PyObject *function, PyObject *args, } set_args(args); + +#ifndef SIMPLE_THREADS + // Ensure that the Python threading system is initialized and ready to go. +#ifdef WITH_THREAD // This symbol defined within Python.h + PyEval_InitThreads(); +#endif +#endif } /** @@ -44,9 +51,20 @@ PythonThread(PyObject *function, PyObject *args, */ PythonThread:: ~PythonThread() { + // Unfortunately, we need to grab the GIL to release these things, + // since the destructor could be called from any thread. +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_STATE gstate; + gstate = PyGILState_Ensure(); +#endif + Py_DECREF(_function); Py_XDECREF(_args); Py_XDECREF(_result); + +#if defined(HAVE_THREADS) && !defined(SIMPLE_THREADS) + PyGILState_Release(gstate); +#endif } /** diff --git a/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx b/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx index 46248cf9f0..d4870c22d3 100644 --- a/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx +++ b/panda/src/pnmimagetypes/pnmFileTypeTIFF.cxx @@ -1046,13 +1046,13 @@ write_data(xel *array, xelval *alpha) { bytesperrow = _x_size * samplesperpixel; } else if ( grayscale ) { samplesperpixel = 1; - bitspersample = pm_maxvaltobits( _maxval ); + bitspersample = min(8, pm_maxvaltobits(_maxval)); photometric = PHOTOMETRIC_MINISBLACK; i = 8 / bitspersample; bytesperrow = ( _x_size + i - 1 ) / i; } else { samplesperpixel = 1; - bitspersample = 8; + bitspersample = min(8, pm_maxvaltobits(_maxval)); photometric = PHOTOMETRIC_PALETTE; bytesperrow = _x_size; } diff --git a/panda/src/pstatclient/pStatProperties.cxx b/panda/src/pstatclient/pStatProperties.cxx index ca8c7c8ead..7b14891daa 100644 --- a/panda/src/pstatclient/pStatProperties.cxx +++ b/panda/src/pstatclient/pStatProperties.cxx @@ -151,9 +151,9 @@ static TimeCollectorProperties time_properties[] = { static LevelCollectorProperties level_properties[] = { { 1, "Graphics memory", { 0.0, 0.0, 1.0 }, "MB", 64, 1048576 }, - { 1, "Vertex buffer switch", { 0.0, 0.6, 0.8 }, "", 500 }, - { 1, "Vertex buffer switch:Vertex", { 0.8, 0.0, 0.6 } }, - { 1, "Vertex buffer switch:Index", { 0.8, 0.6, 0.3 } }, + { 1, "Buffer switch", { 0.0, 0.6, 0.8 }, "", 500 }, + { 1, "Buffer switch:Vertex", { 0.8, 0.0, 0.6 } }, + { 1, "Buffer switch:Index", { 0.8, 0.6, 0.3 } }, { 1, "Geom cache size", { 0.6, 0.8, 0.6 }, "", 500 }, { 1, "Geom cache size:Active", { 0.9, 1.0, 0.3 }, "", 500 }, { 1, "Geom cache operations", { 1.0, 0.6, 0.6 }, "", 500 }, diff --git a/panda/src/putil/cachedTypedWritableReferenceCount.I b/panda/src/putil/cachedTypedWritableReferenceCount.I index 050c95eb3f..1d60f9a614 100644 --- a/panda/src/putil/cachedTypedWritableReferenceCount.I +++ b/panda/src/putil/cachedTypedWritableReferenceCount.I @@ -126,7 +126,7 @@ cache_ref() const { #endif ref(); - AtomicAdjust::inc(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count); + AtomicAdjust::inc(_cache_ref_count); } /** @@ -147,7 +147,7 @@ cache_unref() const { // you can't use PointerTo's? nassertr(_cache_ref_count > 0, 0); - AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count); + AtomicAdjust::dec(_cache_ref_count); return ReferenceCount::unref(); } @@ -164,6 +164,19 @@ test_ref_count_integrity() const { #endif } +/** + * Decrements the cache reference count without affecting the normal reference + * count. Don't use this. + */ +INLINE void CachedTypedWritableReferenceCount:: +cache_ref_only() const { +#ifdef _DEBUG + nassertv(test_ref_count_integrity()); +#endif + + AtomicAdjust::inc(_cache_ref_count); +} + /** * Decrements the cache reference count without affecting the normal reference * count. Intended to be called by derived classes only, presumably to @@ -180,7 +193,7 @@ cache_unref_only() const { // you can't use PointerTo's? nassertv(_cache_ref_count > 0); - AtomicAdjust::dec(((CachedTypedWritableReferenceCount *)this)->_cache_ref_count); + AtomicAdjust::dec(_cache_ref_count); } /** diff --git a/panda/src/putil/cachedTypedWritableReferenceCount.h b/panda/src/putil/cachedTypedWritableReferenceCount.h index 15772048dd..f2e9362dca 100644 --- a/panda/src/putil/cachedTypedWritableReferenceCount.h +++ b/panda/src/putil/cachedTypedWritableReferenceCount.h @@ -46,12 +46,15 @@ PUBLISHED: MAKE_PROPERTY(cache_ref_count, get_cache_ref_count); +public: + INLINE void cache_ref_only() const; + protected: INLINE void cache_unref_only() const; bool do_test_ref_count_integrity() const; private: - AtomicAdjust::Integer _cache_ref_count; + mutable AtomicAdjust::Integer _cache_ref_count; public: static TypeHandle get_class_type() { diff --git a/panda/src/putil/copyOnWriteObject.cxx b/panda/src/putil/copyOnWriteObject.cxx index 0faa2f23ce..172f354319 100644 --- a/panda/src/putil/copyOnWriteObject.cxx +++ b/panda/src/putil/copyOnWriteObject.cxx @@ -35,4 +35,21 @@ unref() const { } return is_zero; } + +/** + * Explicitly increments the cache reference count only. Don't use this. + * + * In the case of a CopyOnWriteObject, when the reference count decrements + * down to the cache reference count, the object is implicitly unlocked. + */ +void CopyOnWriteObject:: +cache_ref_only() const { + MutexHolder holder(_lock_mutex); + CachedTypedWritableReferenceCount::cache_ref_only(); + if (get_ref_count() == get_cache_ref_count()) { + ((CopyOnWriteObject *)this)->_lock_status = LS_unlocked; + ((CopyOnWriteObject *)this)->_locking_thread = NULL; + ((CopyOnWriteObject *)this)->_lock_cvar.notify(); + } +} #endif // COW_THREADED diff --git a/panda/src/putil/copyOnWriteObject.h b/panda/src/putil/copyOnWriteObject.h index 8cada6bfd7..12ea5baf24 100644 --- a/panda/src/putil/copyOnWriteObject.h +++ b/panda/src/putil/copyOnWriteObject.h @@ -49,6 +49,9 @@ PUBLISHED: virtual bool unref() const; INLINE void cache_ref() const; INLINE bool cache_unref() const; + +public: + void cache_ref_only() const; #endif // COW_THREADED protected: diff --git a/panda/src/putil/copyOnWritePointer.I b/panda/src/putil/copyOnWritePointer.I index 98a0987791..a44731bcbc 100644 --- a/panda/src/putil/copyOnWritePointer.I +++ b/panda/src/putil/copyOnWritePointer.I @@ -74,23 +74,58 @@ INLINE CopyOnWritePointer:: * */ INLINE CopyOnWritePointer:: -CopyOnWritePointer(CopyOnWritePointer &&move) NOEXCEPT : - _cow_object(move._cow_object) +CopyOnWritePointer(CopyOnWritePointer &&from) NOEXCEPT : + _cow_object(from._cow_object) { // Steal the other's reference count. - move._cow_object = (CopyOnWriteObject *)NULL; + from._cow_object = (CopyOnWriteObject *)NULL; +} + +/** + * + */ +INLINE CopyOnWritePointer:: +CopyOnWritePointer(PointerTo &&from) NOEXCEPT : + _cow_object(from.p()) +{ + // Steal the other's reference count, but because it is a regular pointer, + // we do need to include the cache reference count. + if (_cow_object != (CopyOnWriteObject *)NULL) { + _cow_object->cache_ref_only(); + } + from.cheat() = NULL; } /** * */ INLINE void CopyOnWritePointer:: -operator = (CopyOnWritePointer &&move) NOEXCEPT { +operator = (CopyOnWritePointer &&from) NOEXCEPT { // Protect against self-move-assignment. - if (move._cow_object != _cow_object) { + if (from._cow_object != _cow_object) { CopyOnWriteObject *old_object = _cow_object; - _cow_object = move._cow_object; - move._cow_object = NULL; + _cow_object = from._cow_object; + from._cow_object = NULL; + + if (old_object != (CopyOnWriteObject *)NULL) { + cache_unref_delete(old_object); + } + } +} + +/** + * + */ +INLINE void CopyOnWritePointer:: +operator = (PointerTo &&from) NOEXCEPT { + if (from.p() != _cow_object) { + CopyOnWriteObject *old_object = _cow_object; + + // Steal the other's reference count, but because it is a regular pointer, + // we do need to include the cache reference count. + _cow_object = from.p(); + _cow_object->cache_ref_only(); + from.cheat() = NULL; if (old_object != (CopyOnWriteObject *)NULL) { cache_unref_delete(old_object); @@ -262,20 +297,60 @@ operator = (To *object) { */ template INLINE CopyOnWritePointerTo:: -CopyOnWritePointerTo(CopyOnWritePointerTo &&move) NOEXCEPT : - CopyOnWritePointer((CopyOnWritePointer &&)move) +CopyOnWritePointerTo(CopyOnWritePointerTo &&from) NOEXCEPT : + CopyOnWritePointer((CopyOnWritePointer &&)from) { } #endif // CPPPARSER +#ifndef CPPPARSER +/** + * + */ +template +INLINE CopyOnWritePointerTo:: +CopyOnWritePointerTo(PointerTo &&from) NOEXCEPT { + // Steal the other's reference count, but because it is a regular pointer, + // we do need to include the cache reference count. + _cow_object = from.p(); + if (_cow_object != (CopyOnWriteObject *)NULL) { + _cow_object->cache_ref_only(); + } + from.cheat() = NULL; +} +#endif // CPPPARSER + #ifndef CPPPARSER /** * */ template INLINE void CopyOnWritePointerTo:: -operator = (CopyOnWritePointerTo &&move) NOEXCEPT { - CopyOnWritePointer::operator = ((CopyOnWritePointer &&)move); +operator = (CopyOnWritePointerTo &&from) NOEXCEPT { + CopyOnWritePointer::operator = ((CopyOnWritePointer &&)from); +} +#endif // CPPPARSER + +#ifndef CPPPARSER +/** + * + */ +template +INLINE void CopyOnWritePointerTo:: +operator = (PointerTo &&from) NOEXCEPT { + if (from.p() != _cow_object) { + CopyOnWriteObject *old_object = _cow_object; + + // Steal the other's reference count, but because it is a regular pointer, + // we do need to include the cache reference count. + _cow_object = from.p(); + _cow_object->cache_ref_only(); + from.cheat() = NULL; + + if (old_object != (CopyOnWriteObject *)NULL) { + cache_unref_delete(old_object); + } + } } #endif // CPPPARSER #endif // USE_MOVE_SEMANTICS diff --git a/panda/src/putil/copyOnWritePointer.cxx b/panda/src/putil/copyOnWritePointer.cxx index de0a2e8abf..e2396e84a9 100644 --- a/panda/src/putil/copyOnWritePointer.cxx +++ b/panda/src/putil/copyOnWritePointer.cxx @@ -90,19 +90,17 @@ get_write_pointer() { } PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy(); + _cow_object->CachedTypedWritableReferenceCount::cache_unref(); + _cow_object->_lock_mutex.release(); - // We can't call cache_unref_delete, because we hold the lock. - if (!_cow_object->CachedTypedWritableReferenceCount::cache_unref()) { - _cow_object->_lock_mutex.release(); - delete _cow_object; - } else { - _cow_object->_lock_mutex.release(); - } + MutexHolder holder(new_object->_lock_mutex); _cow_object = new_object; - _cow_object->cache_ref(); + _cow_object->CachedTypedWritableReferenceCount::cache_ref(); _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write; _cow_object->_locking_thread = current_thread; + return new_object; + } else if (_cow_object->get_cache_ref_count() > 1) { // No one else has it specifically read-locked, but there are other // CopyOnWritePointers holding the same object, so we should make our own @@ -115,19 +113,17 @@ get_write_pointer() { } PT(CopyOnWriteObject) new_object = _cow_object->make_cow_copy(); + _cow_object->CachedTypedWritableReferenceCount::cache_unref(); + _cow_object->_lock_mutex.release(); - // We can't call cache_unref_delete, because we hold the lock. - if (!_cow_object->CachedTypedWritableReferenceCount::cache_unref()) { - _cow_object->_lock_mutex.release(); - delete _cow_object; - } else { - _cow_object->_lock_mutex.release(); - } + MutexHolder holder(new_object->_lock_mutex); _cow_object = new_object; - _cow_object->cache_ref(); + _cow_object->CachedTypedWritableReferenceCount::cache_ref(); _cow_object->_lock_status = CopyOnWriteObject::LS_locked_write; _cow_object->_locking_thread = current_thread; + return new_object; + } else { // No other thread has the pointer locked, and we're the only // CopyOnWritePointer with this object. We can safely write to it without diff --git a/panda/src/putil/copyOnWritePointer.h b/panda/src/putil/copyOnWritePointer.h index c40c432670..1a7f0e099d 100644 --- a/panda/src/putil/copyOnWritePointer.h +++ b/panda/src/putil/copyOnWritePointer.h @@ -37,8 +37,10 @@ public: INLINE ~CopyOnWritePointer(); #ifdef USE_MOVE_SEMANTICS - INLINE CopyOnWritePointer(CopyOnWritePointer &&move) NOEXCEPT; - INLINE void operator = (CopyOnWritePointer &&move) NOEXCEPT; + INLINE CopyOnWritePointer(CopyOnWritePointer &&from) NOEXCEPT; + INLINE CopyOnWritePointer(PointerTo &&from) NOEXCEPT; + INLINE void operator = (CopyOnWritePointer &&from) NOEXCEPT; + INLINE void operator = (PointerTo &&from) NOEXCEPT; #endif INLINE bool operator == (const CopyOnWritePointer &other) const; @@ -61,7 +63,7 @@ public: INLINE bool test_ref_count_integrity() const; INLINE bool test_ref_count_nonzero() const; -private: +protected: CopyOnWriteObject *_cow_object; }; @@ -84,8 +86,10 @@ public: INLINE void operator = (To *object); #ifdef USE_MOVE_SEMANTICS - INLINE CopyOnWritePointerTo(CopyOnWritePointerTo &&move) NOEXCEPT; - INLINE void operator = (CopyOnWritePointerTo &&move) NOEXCEPT; + INLINE CopyOnWritePointerTo(CopyOnWritePointerTo &&from) NOEXCEPT; + INLINE CopyOnWritePointerTo(PointerTo &&from) NOEXCEPT; + INLINE void operator = (CopyOnWritePointerTo &&from) NOEXCEPT; + INLINE void operator = (PointerTo &&from) NOEXCEPT; #endif #ifdef COW_THREADED diff --git a/panda/src/vision/webcamVideoV4L.cxx b/panda/src/vision/webcamVideoV4L.cxx index a71f0c9d96..b07e1b2071 100644 --- a/panda/src/vision/webcamVideoV4L.cxx +++ b/panda/src/vision/webcamVideoV4L.cxx @@ -22,6 +22,72 @@ #include #include +#ifndef CPPPARSER +#ifndef VIDIOC_ENUM_FRAMESIZES +enum v4l2_frmsizetypes { + V4L2_FRMSIZE_TYPE_DISCRETE = 1, + V4L2_FRMSIZE_TYPE_CONTINUOUS = 2, + V4L2_FRMSIZE_TYPE_STEPWISE = 3, +}; + +struct v4l2_frmsize_discrete { + __u32 width; + __u32 height; +}; + +struct v4l2_frmsize_stepwise { + __u32 min_width; + __u32 max_width; + __u32 step_width; + __u32 min_height; + __u32 max_height; + __u32 step_height; +}; + +struct v4l2_frmsizeenum { + __u32 index; + __u32 pixel_format; + __u32 type; + union { + struct v4l2_frmsize_discrete discrete; + struct v4l2_frmsize_stepwise stepwise; + }; + __u32 reserved[2]; +}; + +#define VIDIOC_ENUM_FRAMESIZES _IOWR('V', 74, struct v4l2_frmsizeenum) +#endif + +#ifndef VIDIOC_ENUM_FRAMEINTERVALS +enum v4l2_frmivaltypes { + V4L2_FRMIVAL_TYPE_DISCRETE = 1, + V4L2_FRMIVAL_TYPE_CONTINUOUS = 2, + V4L2_FRMIVAL_TYPE_STEPWISE = 3, +}; + +struct v4l2_frmival_stepwise { + struct v4l2_fract min; + struct v4l2_fract max; + struct v4l2_fract step; +}; + +struct v4l2_frmivalenum { + __u32 index; + __u32 pixel_format; + __u32 width; + __u32 height; + __u32 type; + union { + struct v4l2_fract discrete; + struct v4l2_frmival_stepwise stepwise; + }; + __u32 reserved[2]; +}; + +#define VIDIOC_ENUM_FRAMEINTERVALS _IOWR('V', 75, struct v4l2_frmivalenum) +#endif +#endif + TypeHandle WebcamVideoV4L::_type_handle; /** diff --git a/panda/src/windisplay/winGraphicsWindow.cxx b/panda/src/windisplay/winGraphicsWindow.cxx index b45b0105a4..1ccede6968 100644 --- a/panda/src/windisplay/winGraphicsWindow.cxx +++ b/panda/src/windisplay/winGraphicsWindow.cxx @@ -2229,7 +2229,15 @@ hide_or_show_cursor(bool hide_cursor) { bool WinGraphicsWindow:: find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp, DEVMODE &dm) { + + // Get the current mode. We'll try to match the refresh rate. + DEVMODE cur_dm; + ZeroMemory(&cur_dm, sizeof(cur_dm)); + cur_dm.dmSize = sizeof(cur_dm); + EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &cur_dm); + int modenum = 0; + int saved_modenum = -1; while (1) { ZeroMemory(&dm, sizeof(dm)); @@ -2241,11 +2249,28 @@ find_acceptable_display_mode(DWORD dwWidth, DWORD dwHeight, DWORD bpp, if ((dm.dmPelsWidth == dwWidth) && (dm.dmPelsHeight == dwHeight) && (dm.dmBitsPerPel == bpp)) { - return true; + // If this also matches in refresh rate, we're done here. Otherwise, + // save this as a second choice for later. + if (dm.dmDisplayFrequency == cur_dm.dmDisplayFrequency) { + return true; + } else if (saved_modenum == -1) { + saved_modenum = modenum; + } } modenum++; } + // Failed to find an exact match, but we do have a match that didn't match + // the refresh rate. + if (saved_modenum != -1) { + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(dm); + + if (EnumDisplaySettings(NULL, saved_modenum, &dm)) { + return true; + } + } + return false; } @@ -2545,7 +2570,7 @@ ButtonMap *WinGraphicsWindow:: get_keyboard_map() const { ButtonMap *map = new ButtonMap; - char text[256]; + wchar_t text[256]; UINT vsc = 0; unsigned short ex_vsc[] = {0x57, 0x58, 0x011c, 0x011d, 0x0135, 0x0137, 0x0138, 0x0145, 0x0147, 0x0148, 0x0149, 0x014b, 0x014d, 0x014f, 0x0150, 0x0151, 0x0152, 0x0153, 0x015b, 0x015c, 0x015d}; @@ -2583,14 +2608,15 @@ get_keyboard_map() const { UINT vk = MapVirtualKeyA(vsc, MAPVK_VSC_TO_VK_EX); button = lookup_key(vk); - if (button == ButtonHandle::none()) { - continue; - } + //if (button == ButtonHandle::none()) { + // continue; + //} } - int len = GetKeyNameTextA(lparam, text, 256); - string label (text, len); - map->map_button(raw_button, button, label); + int len = GetKeyNameTextW(lparam, text, 256); + TextEncoder enc; + enc.set_wtext(wstring(text, len)); + map->map_button(raw_button, button, enc.get_text()); } return map; diff --git a/panda/src/x11display/config_x11display.cxx b/panda/src/x11display/config_x11display.cxx index 13b225e418..c2b89c42b2 100644 --- a/panda/src/x11display/config_x11display.cxx +++ b/panda/src/x11display/config_x11display.cxx @@ -60,6 +60,11 @@ ConfigVariableInt x_wheel_right_button "mouse button number does the system report when one scrolls " "to the right?")); +ConfigVariableInt x_cursor_size +("x-cursor-size", -1, + PRC_DESC("This sets the cursor size when using XCursor to change the mouse " + "cursor. The default is to use the default size for the display.")); + ConfigVariableString x_wm_class_name ("x-wm-class-name", "", PRC_DESC("Specify the value to use for the res_name field of the window's " diff --git a/panda/src/x11display/config_x11display.h b/panda/src/x11display/config_x11display.h index 88fb2f2133..bc40aad627 100644 --- a/panda/src/x11display/config_x11display.h +++ b/panda/src/x11display/config_x11display.h @@ -32,6 +32,7 @@ extern ConfigVariableInt x_wheel_down_button; extern ConfigVariableInt x_wheel_left_button; extern ConfigVariableInt x_wheel_right_button; +extern ConfigVariableInt x_cursor_size; extern ConfigVariableString x_wm_class_name; extern ConfigVariableString x_wm_class; diff --git a/panda/src/x11display/x11GraphicsPipe.I b/panda/src/x11display/x11GraphicsPipe.I index cb452bef23..fb56700299 100644 --- a/panda/src/x11display/x11GraphicsPipe.I +++ b/panda/src/x11display/x11GraphicsPipe.I @@ -57,6 +57,38 @@ get_hidden_cursor() { return _hidden_cursor; } +/** + * Returns true if relative mouse mode is supported on this display. + */ +INLINE bool x11GraphicsPipe:: +supports_relative_mouse() const { + return (_XF86DGADirectVideo != NULL); +} + +/** + * Enables relative mouse mode for this display. Returns false if unsupported. + */ +INLINE bool x11GraphicsPipe:: +enable_relative_mouse() { + if (_XF86DGADirectVideo != NULL) { + x11display_cat.info() << "Enabling relative mouse using XF86DGA extension\n"; + _XF86DGADirectVideo(_display, _screen, XF86DGADirectMouse); + return true; + } + return false; +} + +/** + * Disables relative mouse mode for this display. + */ +INLINE void x11GraphicsPipe:: +disable_relative_mouse() { + if (_XF86DGADirectVideo != NULL) { + x11display_cat.info() << "Disabling relative mouse using XF86DGA extension\n"; + _XF86DGADirectVideo(_display, _screen, 0); + } +} + /** * Globally disables the printing of error messages that are raised by the X11 * system, for instance in order to test whether a particular X11 operation diff --git a/panda/src/x11display/x11GraphicsPipe.cxx b/panda/src/x11display/x11GraphicsPipe.cxx index 9eee9f6d7a..a0d4e8ffae 100644 --- a/panda/src/x11display/x11GraphicsPipe.cxx +++ b/panda/src/x11display/x11GraphicsPipe.cxx @@ -17,6 +17,8 @@ #include "frameBufferProperties.h" #include "displayInformation.h" +#include + TypeHandle x11GraphicsPipe::_type_handle; bool x11GraphicsPipe::_error_handlers_installed = false; @@ -31,7 +33,11 @@ LightReMutex x11GraphicsPipe::_x_mutex; * */ x11GraphicsPipe:: -x11GraphicsPipe(const string &display) { +x11GraphicsPipe(const string &display) : + _have_xrandr(false), + _xcursor_size(-1), + _XF86DGADirectVideo(NULL) { + string display_spec = display; if (display_spec.empty()) { display_spec = display_cfg; @@ -86,34 +92,116 @@ x11GraphicsPipe(const string &display) { _display_height = DisplayHeight(_display, _screen); _is_valid = true; -#ifdef HAVE_XRANDR - // Use Xrandr to fill in the supported resolution list. - int num_sizes, num_rates; - XRRScreenSize *xrrs; - xrrs = XRRSizes(_display, 0, &num_sizes); - _display_information->_total_display_modes = 0; - for (int i = 0; i < num_sizes; ++i) { - XRRRates(_display, 0, i, &num_rates); - _display_information->_total_display_modes += num_rates; - } + // Dynamically load the xf86dga extension. + void *xf86dga = dlopen("libXxf86dga.so.1", RTLD_NOW | RTLD_LOCAL); + if (xf86dga != NULL) { + pfn_XF86DGAQueryVersion _XF86DGAQueryVersion = (pfn_XF86DGAQueryVersion)dlsym(xf86dga, "XF86DGAQueryVersion"); + _XF86DGADirectVideo = (pfn_XF86DGADirectVideo)dlsym(xf86dga, "XF86DGADirectVideo"); - short *rates; - short counter = 0; - _display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes]; - for (int i = 0; i < num_sizes; ++i) { - int num_rates; - rates = XRRRates(_display, 0, i, &num_rates); - for (int j = 0; j < num_rates; ++j) { - DisplayMode* dm = _display_information->_display_mode_array + counter; - dm->width = xrrs[i].width; - dm->height = xrrs[i].height; - dm->refresh_rate = rates[j]; - dm->bits_per_pixel = -1; - dm->fullscreen_only = false; - ++counter; + int major_ver, minor_ver; + if (_XF86DGAQueryVersion == NULL || _XF86DGADirectVideo == NULL) { + x11display_cat.warning() + << "libXxf86dga.so.1 does not provide required functions; relative mouse mode will not work.\n"; + + } else if (!_XF86DGAQueryVersion(_display, &major_ver, &minor_ver)) { + _XF86DGADirectVideo = NULL; + } + } else { + _XF86DGADirectVideo = NULL; + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "cannot dlopen libXxf86dga.so.1; cursor changing will not work.\n"; + } + } + + // Dynamically load the XCursor extension. + void *xcursor = dlopen("libXcursor.so.1", RTLD_NOW | RTLD_LOCAL); + if (xcursor != NULL) { + pfn_XcursorGetDefaultSize _XcursorGetDefaultSize = (pfn_XcursorGetDefaultSize)dlsym(xcursor, "XcursorGetDefaultSize"); + _XcursorXcFileLoadImages = (pfn_XcursorXcFileLoadImages)dlsym(xcursor, "XcursorXcFileLoadImages"); + _XcursorImagesLoadCursor = (pfn_XcursorImagesLoadCursor)dlsym(xcursor, "XcursorImagesLoadCursor"); + _XcursorImagesDestroy = (pfn_XcursorImagesDestroy)dlsym(xcursor, "XcursorImagesDestroy"); + _XcursorImageCreate = (pfn_XcursorImageCreate)dlsym(xcursor, "XcursorImageCreate"); + _XcursorImageLoadCursor = (pfn_XcursorImageLoadCursor)dlsym(xcursor, "XcursorImageLoadCursor"); + _XcursorImageDestroy = (pfn_XcursorImageDestroy)dlsym(xcursor, "XcursorImageDestroy"); + + if (_XcursorGetDefaultSize == NULL || _XcursorXcFileLoadImages == NULL || + _XcursorImagesLoadCursor == NULL || _XcursorImagesDestroy == NULL || + _XcursorImageCreate == NULL || _XcursorImageLoadCursor == NULL || + _XcursorImageDestroy == NULL) { + _xcursor_size = -1; + x11display_cat.warning() + << "libXcursor.so.1 does not provide required functions; cursor changing will not work.\n"; + + } else if (x_cursor_size.get_value() >= 0) { + _xcursor_size = x_cursor_size; + } else { + _xcursor_size = _XcursorGetDefaultSize(_display); + } + } else { + _xcursor_size = -1; + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "cannot dlopen libXcursor.so.1; cursor changing will not work.\n"; + } + } + + // Dynamically load the XRandr extension. + void *xrandr = dlopen("libXrandr.so.2", RTLD_NOW | RTLD_LOCAL); + if (xrandr != NULL) { + pfn_XRRQueryExtension _XRRQueryExtension = (pfn_XRRQueryExtension)dlsym(xrandr, "XRRQueryExtension"); + _XRRSizes = (pfn_XRRSizes)dlsym(xrandr, "XRRSizes"); + _XRRRates = (pfn_XRRRates)dlsym(xrandr, "XRRRates"); + _XRRGetScreenInfo = (pfn_XRRGetScreenInfo)dlsym(xrandr, "XRRGetScreenInfo"); + _XRRConfigCurrentConfiguration = (pfn_XRRConfigCurrentConfiguration)dlsym(xrandr, "XRRConfigCurrentConfiguration"); + _XRRSetScreenConfig = (pfn_XRRSetScreenConfig)dlsym(xrandr, "XRRSetScreenConfig"); + + if (_XRRQueryExtension == NULL || _XRRSizes == NULL || _XRRRates == NULL || + _XRRGetScreenInfo == NULL || _XRRConfigCurrentConfiguration == NULL || + _XRRSetScreenConfig == NULL) { + _have_xrandr = false; + x11display_cat.warning() + << "libXrandr.so.2 does not provide required functions; resolution setting will not work.\n"; + } else { + int event, error; + _have_xrandr = _XRRQueryExtension(_display, &event, &error); + } + } else { + _have_xrandr = false; + if (x11display_cat.is_debug()) { + x11display_cat.debug() + << "cannot dlopen libXrandr.so.2; resolution setting will not work.\n"; + } + } + + // Use Xrandr to fill in the supported resolution list. + if (_have_xrandr) { + int num_sizes, num_rates; + XRRScreenSize *xrrs; + xrrs = _XRRSizes(_display, 0, &num_sizes); + _display_information->_total_display_modes = 0; + for (int i = 0; i < num_sizes; ++i) { + _XRRRates(_display, 0, i, &num_rates); + _display_information->_total_display_modes += num_rates; + } + + short *rates; + short counter = 0; + _display_information->_display_mode_array = new DisplayMode[_display_information->_total_display_modes]; + for (int i = 0; i < num_sizes; ++i) { + int num_rates; + rates = _XRRRates(_display, 0, i, &num_rates); + for (int j = 0; j < num_rates; ++j) { + DisplayMode* dm = _display_information->_display_mode_array + counter; + dm->width = xrrs[i].width; + dm->height = xrrs[i].height; + dm->refresh_rate = rates[j]; + dm->bits_per_pixel = -1; + dm->fullscreen_only = false; + ++counter; + } } } -#endif // Connect to an input method for supporting international text entry. _im = XOpenIM(_display, NULL, NULL, NULL); diff --git a/panda/src/x11display/x11GraphicsPipe.h b/panda/src/x11display/x11GraphicsPipe.h index ab06a9b692..4da09d3144 100644 --- a/panda/src/x11display/x11GraphicsPipe.h +++ b/panda/src/x11display/x11GraphicsPipe.h @@ -21,6 +21,22 @@ #include "lightReMutex.h" #include "windowHandle.h" #include "get_x11.h" +#include "config_x11display.h" + +// Excerpt the few definitions we need for the extensions. +#define XF86DGADirectMouse 0x0004 + +typedef struct _XcursorFile XcursorFile; +typedef struct _XcursorImage XcursorImage; +typedef struct _XcursorImages XcursorImages; + +typedef unsigned short Rotation; +typedef unsigned short SizeID; +typedef struct _XRRScreenConfiguration XRRScreenConfiguration; +typedef struct { + int width, height; + int mwidth, mheight; +} XRRScreenSize; class FrameBufferProperties; @@ -40,6 +56,10 @@ public: INLINE X11_Cursor get_hidden_cursor(); + INLINE bool supports_relative_mouse() const; + INLINE bool enable_relative_mouse(); + INLINE void disable_relative_mouse(); + static INLINE int disable_x_error_messages(); static INLINE int enable_x_error_messages(); static INLINE int get_x_error_count(); @@ -61,6 +81,38 @@ public: Atom _net_wm_state_add; Atom _net_wm_state_remove; + // Extension functions. + typedef int (*pfn_XcursorGetDefaultSize)(X11_Display *); + typedef XcursorImages *(*pfn_XcursorXcFileLoadImages)(XcursorFile *, int); + typedef X11_Cursor (*pfn_XcursorImagesLoadCursor)(X11_Display *, const XcursorImages *); + typedef void (*pfn_XcursorImagesDestroy)(XcursorImages *); + typedef XcursorImage *(*pfn_XcursorImageCreate)(int, int); + typedef X11_Cursor (*pfn_XcursorImageLoadCursor)(X11_Display *, const XcursorImage *); + typedef void (*pfn_XcursorImageDestroy)(XcursorImage *); + + int _xcursor_size; + pfn_XcursorXcFileLoadImages _XcursorXcFileLoadImages; + pfn_XcursorImagesLoadCursor _XcursorImagesLoadCursor; + pfn_XcursorImagesDestroy _XcursorImagesDestroy; + pfn_XcursorImageCreate _XcursorImageCreate; + pfn_XcursorImageLoadCursor _XcursorImageLoadCursor; + pfn_XcursorImageDestroy _XcursorImageDestroy; + + typedef Bool (*pfn_XRRQueryExtension)(X11_Display *, int*, int*); + typedef XRRScreenSize *(*pfn_XRRSizes)(X11_Display*, int, int*); + typedef short *(*pfn_XRRRates)(X11_Display*, int, int, int*); + typedef XRRScreenConfiguration *(*pfn_XRRGetScreenInfo)(X11_Display*, X11_Window); + typedef SizeID (*pfn_XRRConfigCurrentConfiguration)(XRRScreenConfiguration*, Rotation*); + typedef Status (*pfn_XRRSetScreenConfig)(X11_Display*, XRRScreenConfiguration *, + Drawable, int, Rotation, Time); + + bool _have_xrandr; + pfn_XRRSizes _XRRSizes; + pfn_XRRRates _XRRRates; + pfn_XRRGetScreenInfo _XRRGetScreenInfo; + pfn_XRRConfigCurrentConfiguration _XRRConfigCurrentConfiguration; + pfn_XRRSetScreenConfig _XRRSetScreenConfig; + protected: X11_Display *_display; int _screen; @@ -69,6 +121,10 @@ protected: X11_Cursor _hidden_cursor; + typedef Bool (*pfn_XF86DGAQueryVersion)(X11_Display *, int*, int*); + typedef Status (*pfn_XF86DGADirectVideo)(X11_Display *, int, int); + pfn_XF86DGADirectVideo _XF86DGADirectVideo; + private: void make_hidden_cursor(); void release_hidden_cursor(); diff --git a/panda/src/x11display/x11GraphicsWindow.cxx b/panda/src/x11display/x11GraphicsWindow.cxx index 732932a1e4..3ea2cd3fb0 100644 --- a/panda/src/x11display/x11GraphicsWindow.cxx +++ b/panda/src/x11display/x11GraphicsWindow.cxx @@ -38,7 +38,24 @@ #include #endif -#ifdef HAVE_XCURSOR +struct _XcursorFile { + void *closure; + int (*read)(XcursorFile *, unsigned char *, int); + int (*write)(XcursorFile *, unsigned char *, int); + int (*seek)(XcursorFile *, long, int); +}; + +typedef struct _XcursorImage { + unsigned int version; + unsigned int size; + unsigned int width; + unsigned int height; + unsigned int xhot; + unsigned int yhot; + unsigned int delay; + unsigned int *pixels; +} XcursorImage; + static int xcursor_read(XcursorFile *file, unsigned char *buf, int len) { istream* str = (istream*) file->closure; str->read((char*) buf, len); @@ -66,7 +83,6 @@ static int xcursor_seek(XcursorFile *file, long offset, int whence) { return str->tellg(); } -#endif TypeHandle x11GraphicsWindow::_type_handle; @@ -92,14 +108,14 @@ x11GraphicsWindow(GraphicsEngine *engine, GraphicsPipe *pipe, _xwindow = (X11_Window)NULL; _ic = (XIC)NULL; _visual_info = NULL; - -#ifdef HAVE_XRANDR _orig_size_id = -1; - int event, error; - _have_xrandr = XRRQueryExtension(_display, &event, &error); -#else - _have_xrandr = false; -#endif + + if (x11_pipe->_have_xrandr) { + // We may still need these functions after the pipe is already destroyed, + // so we copy them into the x11GraphicsWindow. + _XRRGetScreenInfo = x11_pipe->_XRRGetScreenInfo; + _XRRSetScreenConfig = x11_pipe->_XRRSetScreenConfig; + } _awaiting_configure = false; _dga_mouse_enabled = false; @@ -474,10 +490,9 @@ set_properties_now(WindowProperties &properties) { if (is_fullscreen != want_fullscreen || (is_fullscreen && properties.has_size())) { if (want_fullscreen) { - if (_have_xrandr) { -#ifdef HAVE_XRANDR - XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->get_root()); - SizeID old_size_id = XRRConfigCurrentConfiguration(conf, &_orig_rotation); + if (x11_pipe->_have_xrandr) { + XRRScreenConfiguration* conf = _XRRGetScreenInfo(_display, x11_pipe->get_root()); + SizeID old_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation); SizeID new_size_id = (SizeID) -1; int num_sizes = 0, reqsizex, reqsizey; if (properties.has_size()) { @@ -488,7 +503,7 @@ set_properties_now(WindowProperties &properties) { reqsizey = _properties.get_y_size(); } XRRScreenSize *xrrs; - xrrs = XRRSizes(_display, 0, &num_sizes); + xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes); for (int i = 0; i < num_sizes; ++i) { if (xrrs[i].width == reqsizex && xrrs[i].height == reqsizey) { @@ -502,14 +517,13 @@ set_properties_now(WindowProperties &properties) { } else { if (new_size_id != old_size_id) { - XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime); + _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime); if (_orig_size_id == (SizeID) -1) { // Remember the original resolution so we can switch back to it. _orig_size_id = old_size_id; } } } -#endif } else { // If we don't have Xrandr support, we fake the fullscreen support by // setting the window size to the desktop size. @@ -517,15 +531,13 @@ set_properties_now(WindowProperties &properties) { x11_pipe->get_display_height()); } } else { -#ifdef HAVE_XRANDR // Change the resolution back to what it was. Don't remove the SizeID // typecast! - if (_have_xrandr && _orig_size_id != (SizeID) -1) { - XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->get_root()); - XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), _orig_size_id, _orig_rotation, CurrentTime); + if (_orig_size_id != (SizeID) -1) { + XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, x11_pipe->get_root()); + _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), _orig_size_id, _orig_rotation, CurrentTime); _orig_size_id = (SizeID) -1; } -#endif // Set the origin back to what it was if (!properties.has_origin() && _properties.has_origin()) { properties.set_origin(_properties.get_x_origin(), _properties.get_y_origin()); @@ -686,23 +698,17 @@ set_properties_now(WindowProperties &properties) { switch (properties.get_mouse_mode()) { case WindowProperties::M_absolute: XUngrabPointer(_display, CurrentTime); -#ifdef HAVE_XF86DGA if (_dga_mouse_enabled) { - x11display_cat.info() << "Disabling relative mouse using XF86DGA extension\n"; - XF86DGADirectVideo(_display, _screen, 0); + x11_pipe->disable_relative_mouse(); _dga_mouse_enabled = false; } -#endif _properties.set_mouse_mode(WindowProperties::M_absolute); properties.clear_mouse_mode(); break; case WindowProperties::M_relative: -#ifdef HAVE_XF86DGA if (!_dga_mouse_enabled) { - int major_ver, minor_ver; - if (XF86DGAQueryVersion(_display, &major_ver, &minor_ver)) { - + if (x11_pipe->supports_relative_mouse()) { X11_Cursor cursor = None; if (_properties.get_cursor_hidden()) { x11GraphicsPipe *x11_pipe; @@ -714,8 +720,7 @@ set_properties_now(WindowProperties &properties) { GrabModeAsync, _xwindow, cursor, CurrentTime) != GrabSuccess) { x11display_cat.error() << "Failed to grab pointer!\n"; } else { - x11display_cat.info() << "Enabling relative mouse using XF86DGA extension\n"; - XF86DGADirectVideo(_display, _screen, XF86DGADirectMouse); + x11_pipe->enable_relative_mouse(); _properties.set_mouse_mode(WindowProperties::M_relative); properties.clear_mouse_mode(); @@ -730,25 +735,24 @@ set_properties_now(WindowProperties &properties) { _input_devices[0].set_pointer_in_window(event.xbutton.x, event.xbutton.y); } } else { - x11display_cat.info() << "XF86DGA extension not available\n"; + x11display_cat.info() + << "XF86DGA extension not available, cannot enable relative mouse mode\n"; _dga_mouse_enabled = false; } } -#endif break; case WindowProperties::M_confined: { -#ifdef HAVE_XF86DGA + x11GraphicsPipe *x11_pipe; + DCAST_INTO_V(x11_pipe, _pipe); + if (_dga_mouse_enabled) { - XF86DGADirectVideo(_display, _screen, 0); + x11_pipe->disable_relative_mouse(); _dga_mouse_enabled = false; } -#endif X11_Cursor cursor = None; if (_properties.get_cursor_hidden()) { - x11GraphicsPipe *x11_pipe; - DCAST_INTO_V(x11_pipe, _pipe); cursor = x11_pipe->get_hidden_cursor(); } @@ -813,10 +817,9 @@ close_window() { XFlush(_display); } -#ifdef HAVE_XRANDR // Change the resolution back to what it was. Don't remove the SizeID // typecast! - if (_have_xrandr && _orig_size_id != (SizeID) -1) { + if (_orig_size_id != (SizeID) -1) { X11_Window root; if (_pipe != NULL) { x11GraphicsPipe *x11_pipe; @@ -827,11 +830,10 @@ close_window() { // closed. Oh well, let's get the root window by ourselves. root = RootWindow(_display, _screen); } - XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, root); - XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime); + XRRScreenConfiguration *conf = _XRRGetScreenInfo(_display, root); + _XRRSetScreenConfig(_display, conf, root, _orig_size_id, _orig_rotation, CurrentTime); _orig_size_id = -1; } -#endif GraphicsWindow::close_window(); } @@ -859,15 +861,14 @@ open_window() { _properties.set_size(100, 100); } -#ifdef HAVE_XRANDR - if (_properties.get_fullscreen() && _have_xrandr) { - XRRScreenConfiguration* conf = XRRGetScreenInfo(_display, x11_pipe->get_root()); + if (_properties.get_fullscreen() && x11_pipe->_have_xrandr) { + XRRScreenConfiguration* conf = _XRRGetScreenInfo(_display, x11_pipe->get_root()); if (_orig_size_id == (SizeID) -1) { - _orig_size_id = XRRConfigCurrentConfiguration(conf, &_orig_rotation); + _orig_size_id = x11_pipe->_XRRConfigCurrentConfiguration(conf, &_orig_rotation); } int num_sizes, new_size_id = -1; XRRScreenSize *xrrs; - xrrs = XRRSizes(_display, 0, &num_sizes); + xrrs = x11_pipe->_XRRSizes(_display, 0, &num_sizes); for (int i = 0; i < num_sizes; ++i) { if (xrrs[i].width == _properties.get_x_size() && xrrs[i].height == _properties.get_y_size()) { @@ -882,12 +883,11 @@ open_window() { return false; } if (new_size_id != _orig_size_id) { - XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime); + _XRRSetScreenConfig(_display, conf, x11_pipe->get_root(), new_size_id, _orig_rotation, CurrentTime); } else { _orig_size_id = -1; } } -#endif X11_Window parent_window = x11_pipe->get_root(); WindowHandle *window_handle = _properties.get_parent_window(); @@ -2081,11 +2081,15 @@ check_event(X11_Display *display, XEvent *event, char *arg) { */ X11_Cursor x11GraphicsWindow:: get_cursor(const Filename &filename) { -#ifndef HAVE_XCURSOR - x11display_cat.info() - << "XCursor support not enabled in build; cannot change mouse cursor.\n"; - return None; -#else // HAVE_XCURSOR + x11GraphicsPipe *x11_pipe; + DCAST_INTO_R(x11_pipe, _pipe, None); + + if (x11_pipe->_xcursor_size == -1) { + x11display_cat.info() + << "libXcursor.so.1 not available; cannot change mouse cursor.\n"; + return None; + } + // First, look for the unresolved filename in our index. pmap::iterator fi = _cursor_filenames.find(filename); if (fi != _cursor_filenames.end()) { @@ -2135,10 +2139,10 @@ get_cursor(const Filename &filename) { xcfile.write = &xcursor_write; xcfile.seek = &xcursor_seek; - XcursorImages *images = XcursorXcFileLoadImages(&xcfile, XcursorGetDefaultSize(_display)); + XcursorImages *images = x11_pipe->_XcursorXcFileLoadImages(&xcfile, x11_pipe->_xcursor_size); if (images != NULL) { - h = XcursorImagesLoadCursor(_display, images); - XcursorImagesDestroy(images); + h = x11_pipe->_XcursorImagesLoadCursor(_display, images); + x11_pipe->_XcursorImagesDestroy(images); } } else if (memcmp(magic, "\0\0\1\0", 4) == 0 @@ -2159,18 +2163,19 @@ get_cursor(const Filename &filename) { _cursor_filenames[resolved] = h; return h; -#endif // HAVE_XCURSOR } -#ifdef HAVE_XCURSOR /** * Reads a Windows .ico or .cur file from the indicated stream and returns it * as an X11 Cursor. If the file cannot be loaded, returns None. */ X11_Cursor x11GraphicsWindow:: read_ico(istream &ico) { - // Local structs, this is just POD, make input easier - typedef struct { + x11GraphicsPipe *x11_pipe; + DCAST_INTO_R(x11_pipe, _pipe, None); + + // Local structs, this is just POD, make input easier + typedef struct { uint16_t reserved, type, count; } IcoHeader; @@ -2204,7 +2209,7 @@ read_ico(istream &ico) { XcursorImage *image = NULL; X11_Cursor ret = None; - int def_size = XcursorGetDefaultSize(_display); + int def_size = x11_pipe->_xcursor_size; // Get our header, note that ICO = type 1 and CUR = type 2. ico.read(reinterpret_cast(&header), sizeof(IcoHeader)); @@ -2240,7 +2245,7 @@ read_ico(istream &ico) { } img.set_maxval(255); - image = XcursorImageCreate(img.get_x_size(), img.get_y_size()); + image = x11_pipe->_XcursorImageCreate(img.get_x_size(), img.get_y_size()); xel *ptr = img.get_array(); xelval *alpha = img.get_alpha_array(); @@ -2285,7 +2290,7 @@ read_ico(istream &ico) { ico.read(andBmp, andBmpSize); if (!ico.good()) goto cleanup; - image = XcursorImageCreate(infoHeader.width, infoHeader.height / 2); + image = x11_pipe->_XcursorImageCreate(infoHeader.width, infoHeader.height / 2); // Support all the formats that GIMP supports. switch (bitsPerPixel) { @@ -2370,10 +2375,10 @@ read_ico(istream &ico) { image->yhot = 0; } - ret = XcursorImageLoadCursor(_display, image); + ret = x11_pipe->_XcursorImageLoadCursor(_display, image); cleanup: - XcursorImageDestroy(image); + x11_pipe->_XcursorImageDestroy(image); delete[] entries; delete[] palette; delete[] xorBmp; @@ -2381,4 +2386,3 @@ cleanup: return ret; } -#endif // HAVE_XCURSOR diff --git a/panda/src/x11display/x11GraphicsWindow.h b/panda/src/x11display/x11GraphicsWindow.h index fe160284f8..5b55bf3619 100644 --- a/panda/src/x11display/x11GraphicsWindow.h +++ b/panda/src/x11display/x11GraphicsWindow.h @@ -20,11 +20,6 @@ #include "graphicsWindow.h" #include "buttonHandle.h" -#ifdef HAVE_XRANDR -typedef unsigned short Rotation; -typedef unsigned short SizeID; -#endif - /** * Interfaces to the X11 window system. */ @@ -76,9 +71,7 @@ protected: private: X11_Cursor get_cursor(const Filename &filename); -#ifdef HAVE_XCURSOR X11_Cursor read_ico(istream &ico); -#endif protected: X11_Display *_display; @@ -87,12 +80,8 @@ protected: Colormap _colormap; XIC _ic; XVisualInfo *_visual_info; - - bool _have_xrandr; -#ifdef HAVE_XRANDR Rotation _orig_rotation; SizeID _orig_size_id; -#endif LVecBase2i _fixed_size; @@ -109,6 +98,9 @@ protected: }; pvector _mouse_device_info; + x11GraphicsPipe::pfn_XRRGetScreenInfo _XRRGetScreenInfo; + x11GraphicsPipe::pfn_XRRSetScreenConfig _XRRSetScreenConfig; + public: static TypeHandle get_class_type() { return _type_handle;