Merge remote-tracking branch 'origin/master' into deploy-ng
This commit is contained in:
commit
c839dc60d6
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 ------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -49,19 +49,6 @@ struct XVisualInfo;
|
|||
#include <X11/Xutil.h>
|
||||
#include <X11/keysym.h>
|
||||
#include <X11/Xatom.h>
|
||||
|
||||
#ifdef HAVE_XRANDR
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
#endif // HAVE_XRANDR
|
||||
|
||||
#ifdef HAVE_XCURSOR
|
||||
#include <X11/Xcursor/Xcursor.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XF86DGA
|
||||
#include <X11/extensions/Xxf86dga.h>
|
||||
#endif
|
||||
|
||||
#include "post_x11_include.h"
|
||||
|
||||
#endif // CPPPARSER
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -62,12 +62,15 @@
|
|||
#include <algorithm>
|
||||
#include <limits.h>
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Filename>::
|
||||
__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<Filename>::
|
||||
__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.
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@
|
|||
template<>
|
||||
class Extension<Filename> : public ExtensionBase<Filename> {
|
||||
public:
|
||||
void __init__(PyObject *path);
|
||||
|
||||
PyObject *__reduce__(PyObject *self) const;
|
||||
PyObject *__repr__() const;
|
||||
PyObject *__fspath__() const;
|
||||
PyObject *scan_directory() const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ extern ConfigVariableBool ffmpeg_support_seek;
|
|||
extern ConfigVariableBool ffmpeg_global_lock;
|
||||
extern ConfigVariableEnum<ThreadPriority> ffmpeg_thread_priority;
|
||||
extern ConfigVariableInt ffmpeg_read_buffer_size;
|
||||
extern ConfigVariableBool ffmpeg_prefer_libvpx;
|
||||
|
||||
extern EXPCL_FFMPEG void init_libffmpeg();
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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<GLuint> _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);
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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<GLint, GLuint64> _glsl_uniform_handles;
|
||||
#endif
|
||||
|
||||
#ifndef OPENGLES
|
||||
struct StorageBlock {
|
||||
CPT(InternalName) _name;
|
||||
GLuint _binding_index;
|
||||
GLint _min_size;
|
||||
};
|
||||
typedef pvector<StorageBlock> StorageBlocks;
|
||||
StorageBlocks _storage_blocks;
|
||||
BitArray _used_storage_bindings;
|
||||
#endif
|
||||
|
||||
struct ImageInput {
|
||||
CPT(InternalName) _name;
|
||||
CLP(TextureContext) *_gtc;
|
||||
|
|
|
|||
|
|
@ -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<CoordinateSystem> 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
|
||||
|
|
|
|||
|
|
@ -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<CoordinateSystem> gl_coordinate_system;
|
||||
|
||||
extern EXPCL_GL void CLP(init_classes)();
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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<BufferContext *, pointer_hash> 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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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<unsigned char> 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];
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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<unsigned char> 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<unsigned char> _initial_data;
|
||||
|
||||
typedef pmap<PreparedGraphicsObjects *, BufferContext *> 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
|
||||
|
|
@ -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; };
|
||||
|
|
|
|||
|
|
@ -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]));
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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()) {
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ protected:
|
|||
CP_point_priority,
|
||||
CP_directional_priority,
|
||||
CP_spot_priority,
|
||||
CP_area_priority,
|
||||
};
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "nodeCullCallbackData.cxx"
|
||||
#include "pointLight.cxx"
|
||||
#include "rectangleLight.cxx"
|
||||
#include "sceneGraphAnalyzer.cxx"
|
||||
#include "selectiveChildNode.cxx"
|
||||
#include "sequenceNode.cxx"
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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<CData> _cycler;
|
||||
typedef CycleDataReader<CData> CDReader;
|
||||
typedef CycleDataWriter<CData> 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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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<CopyOnWriteObject> &&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<CopyOnWriteObject> &&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<class T>
|
||||
INLINE CopyOnWritePointerTo<T>::
|
||||
CopyOnWritePointerTo(CopyOnWritePointerTo<T> &&move) NOEXCEPT :
|
||||
CopyOnWritePointer((CopyOnWritePointer &&)move)
|
||||
CopyOnWritePointerTo(CopyOnWritePointerTo<T> &&from) NOEXCEPT :
|
||||
CopyOnWritePointer((CopyOnWritePointer &&)from)
|
||||
{
|
||||
}
|
||||
#endif // CPPPARSER
|
||||
|
||||
#ifndef CPPPARSER
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE CopyOnWritePointerTo<T>::
|
||||
CopyOnWritePointerTo(PointerTo<T> &&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<class T>
|
||||
INLINE void CopyOnWritePointerTo<T>::
|
||||
operator = (CopyOnWritePointerTo<T> &&move) NOEXCEPT {
|
||||
CopyOnWritePointer::operator = ((CopyOnWritePointer &&)move);
|
||||
operator = (CopyOnWritePointerTo<T> &&from) NOEXCEPT {
|
||||
CopyOnWritePointer::operator = ((CopyOnWritePointer &&)from);
|
||||
}
|
||||
#endif // CPPPARSER
|
||||
|
||||
#ifndef CPPPARSER
|
||||
/**
|
||||
*
|
||||
*/
|
||||
template<class T>
|
||||
INLINE void CopyOnWritePointerTo<T>::
|
||||
operator = (PointerTo<T> &&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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<CopyOnWriteObject> &&from) NOEXCEPT;
|
||||
INLINE void operator = (CopyOnWritePointer &&from) NOEXCEPT;
|
||||
INLINE void operator = (PointerTo<CopyOnWriteObject> &&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<T> &&from) NOEXCEPT;
|
||||
INLINE void operator = (CopyOnWritePointerTo &&from) NOEXCEPT;
|
||||
INLINE void operator = (PointerTo<T> &&from) NOEXCEPT;
|
||||
#endif
|
||||
|
||||
#ifdef COW_THREADED
|
||||
|
|
|
|||
|
|
@ -22,6 +22,72 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 "
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#include "frameBufferProperties.h"
|
||||
#include "displayInformation.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -38,7 +38,24 @@
|
|||
#include <linux/input.h>
|
||||
#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<Filename, X11_Cursor>::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<char *>(&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
|
||||
|
|
|
|||
|
|
@ -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<MouseDeviceInfo> _mouse_device_info;
|
||||
|
||||
x11GraphicsPipe::pfn_XRRGetScreenInfo _XRRGetScreenInfo;
|
||||
x11GraphicsPipe::pfn_XRRSetScreenConfig _XRRSetScreenConfig;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
return _type_handle;
|
||||
|
|
|
|||
Loading…
Reference in New Issue