Merge branch 'master' into cmake

This commit is contained in:
Sam Edwards 2018-09-22 15:55:59 -06:00
commit e917c54433
50 changed files with 455 additions and 155 deletions

View File

@ -61,7 +61,14 @@ class DirectFrame(DirectGuiWidget):
def destroy(self):
DirectGuiWidget.destroy(self)
def setText(self):
def clearText(self):
self['text'] = None
self.setText()
def setText(self, text=None):
if text is not None:
self['text'] = text
# Determine if user passed in single string or a sequence
if self['text'] == None:
textList = (None,) * self['numStates']
@ -100,7 +107,14 @@ class DirectFrame(DirectGuiWidget):
sort = DGG.TEXT_SORT_INDEX,
)
def setGeom(self):
def clearGeom(self):
self['geom'] = None
self.setGeom()
def setGeom(self, geom=None):
if geom is not None:
self['geom'] = geom
# Determine argument type
geom = self['geom']
@ -142,7 +156,14 @@ class DirectFrame(DirectGuiWidget):
geom = geom, scale = 1,
sort = DGG.GEOM_SORT_INDEX)
def setImage(self):
def clearImage(self):
self['image'] = None
self.setImage()
def setImage(self, image=None):
if image is not None:
self['image'] = image
# Determine argument type
arg = self['image']
if arg == None:

View File

@ -934,8 +934,8 @@ class Loader(DirectObject):
just as in loadModel(); otherwise, the loading happens before
loadSound() returns."""
if not isinstance(soundPath, (MovieAudio, tuple, list, set)):
# We were given a single sound pathname.
if not isinstance(soundPath, (tuple, list, set)):
# We were given a single sound pathname or a MovieAudio instance.
soundList = [soundPath]
gotList = False
else:

View File

@ -49,6 +49,8 @@
// interrogate pass (CPPPARSER isn't defined), this maps to public.
#define PUBLISHED __published
#define PHAVE_ATOMIC 1
typedef int ios_openmode;
typedef int ios_fmtflags;
typedef int ios_iostate;
@ -93,6 +95,23 @@ typedef std::ios::iostate ios_iostate;
typedef std::ios::seekdir ios_seekdir;
#endif
#ifdef _MSC_VER
#define ALWAYS_INLINE __forceinline
#elif defined(__GNUC__)
#define ALWAYS_INLINE __attribute__((always_inline)) inline
#else
#define ALWAYS_INLINE inline
#endif
#ifdef FORCE_INLINING
// If FORCE_INLINING is defined, we use the keyword __forceinline, which tells
// MS VC++ to override its internal benefit heuristic and inline the fn if it
// is technically possible to do so.
#define INLINE ALWAYS_INLINE
#else
#define INLINE inline
#endif
// Apple has an outdated libstdc++. Not all is lost, though, as we can fill
// in some important missing functions.
#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20070719
@ -115,24 +134,38 @@ namespace std {
}
template<class T> struct owner_less;
typedef enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst,
} memory_order;
#define ATOMIC_FLAG_INIT { 0 }
class atomic_flag {
bool _flag;
public:
atomic_flag() noexcept = default;
ALWAYS_INLINE constexpr atomic_flag(bool flag) noexcept : _flag(flag) {}
atomic_flag(const atomic_flag &) = delete;
~atomic_flag() noexcept = default;
atomic_flag &operator = (const atomic_flag&) = delete;
ALWAYS_INLINE bool test_and_set(memory_order order = memory_order_seq_cst) noexcept {
return __atomic_test_and_set(&_flag, order);
}
ALWAYS_INLINE void clear(memory_order order = memory_order_seq_cst) noexcept {
__atomic_clear(&_flag, order);
}
};
};
#endif
#ifdef _MSC_VER
#define ALWAYS_INLINE __forceinline
#elif defined(__GNUC__)
#define ALWAYS_INLINE __attribute__((always_inline)) inline
#else
#define ALWAYS_INLINE inline
#endif
#ifdef FORCE_INLINING
// If FORCE_INLINING is defined, we use the keyword __forceinline, which tells
// MS VC++ to override its internal benefit heuristic and inline the fn if it
// is technically possible to do so.
#define INLINE ALWAYS_INLINE
#else
#define INLINE inline
// Expect that we have access to the <atomic> header.
#define PHAVE_ATOMIC 1
#endif
// Determine the availability of C++11 features.

View File

@ -11,13 +11,6 @@
* @date 2006-04-11
*/
/**
*
*/
constexpr MutexSpinlockImpl::
MutexSpinlockImpl() : _lock(0) {
}
/**
*
*/
@ -33,7 +26,7 @@ lock() {
*/
INLINE bool MutexSpinlockImpl::
try_lock() {
return (AtomicAdjust::compare_and_exchange(_lock, 0, 1) == 0);
return !_flag.test_and_set(std::memory_order_acquire);
}
/**
@ -41,5 +34,5 @@ try_lock() {
*/
INLINE void MutexSpinlockImpl::
unlock() {
AtomicAdjust::set(_lock, 0);
_flag.clear(std::memory_order_release);
}

View File

@ -17,12 +17,21 @@
#include "mutexSpinlockImpl.h"
#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64)
#include <emmintrin.h>
#define PAUSE() _mm_pause()
#else
#define PAUSE()
#endif
/**
*
*/
void MutexSpinlockImpl::
do_lock() {
while (AtomicAdjust::compare_and_exchange(_lock, 0, 1) != 0) {
// Loop until we changed the flag from 0 to 1 (and it wasn't already 1).
while (_flag.test_and_set(std::memory_order_acquire)) {
PAUSE();
}
}

View File

@ -19,7 +19,9 @@
#ifdef MUTEX_SPINLOCK
#include "atomicAdjust.h"
#ifdef PHAVE_ATOMIC
#include <atomic>
#endif
/**
* Uses a simple user-space spinlock to implement a mutex. It is usually not
@ -29,7 +31,7 @@
*/
class EXPCL_DTOOL_DTOOLBASE MutexSpinlockImpl {
public:
constexpr MutexSpinlockImpl();
constexpr MutexSpinlockImpl() noexcept = default;
MutexSpinlockImpl(const MutexSpinlockImpl &copy) = delete;
MutexSpinlockImpl &operator = (const MutexSpinlockImpl &copy) = delete;
@ -42,7 +44,7 @@ public:
private:
void do_lock();
TVOLATILE AtomicAdjust::Integer _lock;
std::atomic_flag _flag = ATOMIC_FLAG_INIT;
};
#include "mutexSpinlockImpl.I"

View File

@ -254,14 +254,13 @@ call_function(ostream &out, int indent_level, bool convert_result,
&parser);
out << " = " << call << ";\n";
// MOVE() expands to std::move() when we are compiling with a compiler
// that supports rvalue references. It basically turns an lvalue into
// Use of the C++11 std::move function basically turns an lvalue into
// an rvalue, allowing a move constructor to be called instead of a
// copy constructor (since we won't be using the return value any
// more), which is usually more efficient if it exists. If it
// doesn't, it shouldn't do any harm.
string new_str =
_return_type->prepare_return_expr(out, indent_level, "MOVE(result)");
_return_type->prepare_return_expr(out, indent_level, "std::move(result)");
return_expr = _return_type->get_return_expr(new_str);
} else {

View File

@ -5491,7 +5491,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
// Use move constructor when available for functions that take an
// actual PointerTo. This eliminates an unref()ref() pair.
pexpr_string = "MOVE(" + param_name + "_this)";
pexpr_string = "std::move(" + param_name + "_this)";
} else {
// This is a move-assignable type, such as TypeHandle or LVecBase4.
@ -6156,7 +6156,7 @@ write_function_instance(ostream &out, FunctionRemap *remap,
indent(out, indent_level) << "return true;\n";
} else if (TypeManager::is_reference_count(remap->_cpptype)) {
indent(out, indent_level) << "coerced = MOVE(" << return_expr << ");\n";
indent(out, indent_level) << "coerced = std::move(" << return_expr << ");\n";
indent(out, indent_level) << "return true;\n";
} else {

View File

@ -516,7 +516,7 @@ main(int argc, char **argv) {
cerr << "Error parsing file: '" << argv[i] << "'\n";
exit(1);
}
builder.add_source_file(filename);
builder.add_source_file(filename.to_os_generic());
}
// Now that we've parsed all the source code, change the way things are

View File

@ -40,7 +40,7 @@ pass_parameter(std::ostream &out, const std::string &variable_name) {
if (variable_name.size() > 1 && variable_name[0] == '&') {
// Prevent generating something like *&param Also, if this is really some
// local type, we can presumably just move it?
out << "MOVE(" << variable_name.substr(1) << ")";
out << "std::move(" << variable_name.substr(1) << ")";
} else {
out << "*" << variable_name;
}

View File

@ -42,7 +42,7 @@ pass_parameter(std::ostream &out, const std::string &variable_name) {
// this parameter is an rvalue reference, but CPPParser can't know that,
// and it might have an overload that takes an rvalue reference. It
// shouldn't hurt either way.
out << "MOVE(" << variable_name.substr(1) << ")";
out << "std::move(" << variable_name.substr(1) << ")";
} else {
out << "*" << variable_name;
}

View File

@ -2271,6 +2271,7 @@ DTOOL_CONFIG=[
("OS_SIMPLE_THREADS", '1', '1'),
("DEBUG_THREADS", 'UNDEF', 'UNDEF'),
("HAVE_POSIX_THREADS", 'UNDEF', '1'),
("MUTEX_SPINLOCK", 'UNDEF', 'UNDEF'),
("HAVE_AUDIO", '1', '1'),
("NOTIFY_DEBUG", 'UNDEF', 'UNDEF'),
("DO_PSTATS", 'UNDEF', 'UNDEF'),

View File

@ -28,7 +28,7 @@ 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"):
if (os.path.isfile("/lib/libc-2.5.so") or os.path.isfile("/lib64/libc-2.5.so")) and os.path.isdir("/opt/python"):
default_platform = default_platform.replace("linux", "manylinux1")

View File

@ -248,8 +248,8 @@ inline std::ostream &operator <<(std::ostream &out, const PartBundle &bundle) {
return out;
}
std::ostream &operator <<(std::ostream &out, PartBundle::BlendType blend_type);
std::istream &operator >>(std::istream &in, PartBundle::BlendType &blend_type);
EXPCL_PANDA_CHAN std::ostream &operator <<(std::ostream &out, PartBundle::BlendType blend_type);
EXPCL_PANDA_CHAN std::istream &operator >>(std::istream &in, PartBundle::BlendType &blend_type);
#include "partBundle.I"

View File

@ -3463,7 +3463,7 @@ do_issue_material() {
cur_material.Emissive = *(D3DCOLORVALUE *)(color.get_data());
cur_material.Power = material->get_shininess();
if (material->has_diffuse()) {
if (material->has_diffuse() || material->has_base_color()) {
// If the material specifies an diffuse color, use it.
set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
} else {
@ -3476,7 +3476,7 @@ do_issue_material() {
set_render_state(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_COLOR1);
}
}
if (material->has_ambient()) {
if (material->has_ambient() || material->has_base_color()) {
// If the material specifies an ambient color, use it.
set_render_state(D3DRS_AMBIENTMATERIALSOURCE, D3DMCS_MATERIAL);
} else {
@ -3490,7 +3490,7 @@ do_issue_material() {
}
}
if (material->has_specular()) {
if (material->has_specular() || material->has_base_color()) {
set_render_state(D3DRS_SPECULARENABLE, TRUE);
} else {
set_render_state(D3DRS_SPECULARENABLE, FALSE);

View File

@ -30,7 +30,7 @@
* connectivity, and generates a set of EggTriangleStrips that represent the
* same geometry.
*/
class EggMesher {
class EXPCL_PANDA_EGG EggMesher {
public:
EggMesher();

View File

@ -26,7 +26,7 @@ class EggMesherStrip;
* connected triangles. The edge is actually represented as a pair of vertex
* indices into the same vertex pool.
*/
class EggMesherEdge {
class EXPCL_PANDA_EGG EggMesherEdge {
public:
INLINE EggMesherEdge(int vi_a, int vi_b);
INLINE EggMesherEdge(const EggMesherEdge &copy);

View File

@ -31,7 +31,7 @@ class EggMesher;
* This class is used by EggMesher::find_fans() to attempt to make an
* EggTriangleFan out of the polygons connected to the indicated vertex.
*/
class EggMesherFanMaker {
class EXPCL_PANDA_EGG EggMesherFanMaker {
public:
typedef plist<const EggMesherEdge *> Edges;
typedef plist<EggMesherStrip *> Strips;

View File

@ -27,7 +27,7 @@ class EggMesherEdge;
* mesher. It might also represent a single polygon such as a triangle or
* quad, since that's how strips generally start out.
*/
class EggMesherStrip {
class EXPCL_PANDA_EGG EggMesherStrip {
public:
enum PrimType {
PT_poly,

View File

@ -677,7 +677,15 @@ transform(const LMatrix4d &mat) {
typedef pvector<EggVertex *> Verts;
Verts verts;
verts.reserve(size());
// Work around MSVC 2017 compiler bug, see GitHub issue #379
#ifdef _MSC_VER
for (const IndexVertices::value_type &v : _index_vertices) {
verts.push_back(v.second);
}
#else
std::copy(begin(), end(), std::back_inserter(verts));
#endif
Verts::const_iterator vi;
for (vi = verts.begin(); vi != verts.end(); ++vi) {

View File

@ -27,7 +27,7 @@ class EggLoader;
* It is used to collect similar polygons together for a Geom, as well as to
* group related LOD children together under a single LOD node.
*/
class EggBinner : public EggBinMaker {
class EXPCL_PANDA_EGG2PG EggBinner : public EggBinMaker {
public:
// The BinNumber serves to identify why a particular EggBin was created.
enum BinNumber {

View File

@ -64,7 +64,7 @@ class CharacterMaker;
*
* This class isn't exported from this package.
*/
class EggLoader {
class EXPCL_PANDA_EGG2PG EggLoader {
public:
EggLoader();
EggLoader(const EggData *data);

View File

@ -36,7 +36,7 @@ class EggMaterial;
* should be assigned to each primitive. It is assigned to EggPrimitive
* objects via the EggBinner.
*/
class EggRenderState : public EggUserData {
class EXPCL_PANDA_EGG2PG EggRenderState : public EggUserData {
public:
INLINE EggRenderState(EggLoader &loader);
INLINE void add_attrib(const RenderAttrib *attrib);

View File

@ -50,7 +50,7 @@ class EggVertex;
* complete (some Panda or egg constructs are not fully supported by this
* class).
*/
class EggSaver {
class EXPCL_PANDA_EGG2PG EggSaver {
PUBLISHED:
EggSaver(EggData *data = nullptr);

View File

@ -7627,7 +7627,7 @@ do_issue_material() {
call_glMaterialfv(face, GL_EMISSION, material->get_emission());
glMaterialf(face, GL_SHININESS, max(min(material->get_shininess(), (PN_stdfloat)128), (PN_stdfloat)0));
if (material->has_ambient() && material->has_diffuse()) {
if ((material->has_ambient() && material->has_diffuse()) || material->has_base_color()) {
// The material has both an ambient and diffuse specified. This means we
// do not need glMaterialColor().
glDisable(GL_COLOR_MATERIAL);

View File

@ -422,7 +422,7 @@ void Material::
write(std::ostream &out, int indent_level) const {
indent(out, indent_level) << "Material " << get_name() << "\n";
if (has_base_color()) {
indent(out, indent_level + 2) << "base_color = " << get_ambient() << "\n";
indent(out, indent_level + 2) << "base_color = " << get_base_color() << "\n";
}
if (has_ambient()) {
indent(out, indent_level + 2) << "ambient = " << get_ambient() << "\n";

View File

@ -43,7 +43,7 @@ class MovieAudioCursor;
*/
class EXPCL_PANDA_MOVIES MovieAudio : public TypedWritableReferenceCount, public Namable {
PUBLISHED:
MovieAudio(const std::string &name = "Blank Audio");
explicit MovieAudio(const std::string &name = "Blank Audio");
virtual ~MovieAudio();
virtual PT(MovieAudioCursor) open();
static PT(MovieAudio) get(const Filename &name);

View File

@ -258,6 +258,11 @@ analyze_renderstate(ShaderKey &key, const RenderState *rs) {
// states to be rehashed.
mat->mark_used_by_auto_shader();
key._material_flags = mat->get_flags();
if ((key._material_flags & Material::F_base_color) != 0) {
key._material_flags |= (Material::F_diffuse | Material::F_specular | Material::F_ambient);
key._material_flags &= ~Material::F_base_color;
}
}
// Break out the lights by type.

View File

@ -147,8 +147,8 @@ void PGItem::
transform_changed() {
LightReMutexHolder holder(_lock);
PandaNode::transform_changed();
if (has_notify()) {
get_notify()->item_transform_changed(this);
if (_notify != nullptr) {
_notify->item_transform_changed(this);
}
}
@ -161,8 +161,8 @@ void PGItem::
draw_mask_changed() {
LightReMutexHolder holder(_lock);
PandaNode::draw_mask_changed();
if (has_notify()) {
get_notify()->item_draw_mask_changed(this);
if (_notify != nullptr) {
_notify->item_draw_mask_changed(this);
}
}
@ -530,8 +530,8 @@ enter_region(const MouseWatcherParameter &param) {
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_enter(this, param);
if (_notify != nullptr) {
_notify->item_enter(this, param);
}
}
@ -554,8 +554,8 @@ exit_region(const MouseWatcherParameter &param) {
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_exit(this, param);
if (_notify != nullptr) {
_notify->item_exit(this, param);
}
// pgui_cat.info() << get_name() << "::exit()" << endl;
@ -580,8 +580,8 @@ within_region(const MouseWatcherParameter &param) {
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_within(this, param);
if (_notify != nullptr) {
_notify->item_within(this, param);
}
}
@ -602,8 +602,8 @@ without_region(const MouseWatcherParameter &param) {
play_sound(event);
throw_event(event, EventParameter(ep));
if (has_notify()) {
get_notify()->item_without(this, param);
if (_notify != nullptr) {
_notify->item_without(this, param);
}
}
@ -623,8 +623,8 @@ focus_in() {
play_sound(event);
throw_event(event);
if (has_notify()) {
get_notify()->item_focus_in(this);
if (_notify != nullptr) {
_notify->item_focus_in(this);
}
}
@ -644,8 +644,8 @@ focus_out() {
play_sound(event);
throw_event(event);
if (has_notify()) {
get_notify()->item_focus_out(this);
if (_notify != nullptr) {
_notify->item_focus_out(this);
}
}
@ -673,8 +673,8 @@ press(const MouseWatcherParameter &param, bool background) {
throw_event(event, EventParameter(ep));
}
if (has_notify()) {
get_notify()->item_press(this, param);
if (_notify != nullptr) {
_notify->item_press(this, param);
}
}
@ -697,8 +697,8 @@ release(const MouseWatcherParameter &param, bool background) {
throw_event(event, EventParameter(ep));
}
if (has_notify()) {
get_notify()->item_release(this, param);
if (_notify != nullptr) {
_notify->item_release(this, param);
}
}
@ -757,8 +757,8 @@ move(const MouseWatcherParameter &param) {
<< *this << "::move(" << param << ")\n";
}
if (has_notify()) {
get_notify()->item_move(this, param);
if (_notify != nullptr) {
_notify->item_move(this, param);
}
}
@ -1169,9 +1169,10 @@ mouse_to_local(const LPoint2 &mouse_point) const {
*/
void PGItem::
frame_changed() {
LightReMutexHolder holder(_lock);
mark_frames_stale();
if (has_notify()) {
get_notify()->item_frame_changed(this);
if (_notify != nullptr) {
_notify->item_frame_changed(this);
}
}

View File

@ -19,19 +19,18 @@ TypeHandle PGScrollFrame::_type_handle;
*
*/
PGScrollFrame::
PGScrollFrame(const std::string &name) : PGVirtualFrame(name)
PGScrollFrame(const std::string &name) :
PGVirtualFrame(name),
_needs_remanage(false),
_needs_recompute_clip(false),
_has_virtual_frame(false),
_virtual_frame(0.0f, 0.0f, 0.0f, 0.0f),
_manage_pieces(false),
_auto_hide(false)
{
set_cull_callback();
_canvas_computed.test_and_set();
_needs_remanage = false;
_needs_recompute_canvas = false;
_needs_recompute_clip = false;
_has_virtual_frame = false;
_virtual_frame.set(0.0f, 0.0f, 0.0f, 0.0f);
_manage_pieces = false;
_auto_hide = false;
_horizontal_slider = nullptr;
_vertical_slider = nullptr;
set_cull_callback();
}
/**
@ -55,8 +54,8 @@ PGScrollFrame(const PGScrollFrame &copy) :
_auto_hide(copy._auto_hide)
{
_needs_remanage = false;
_needs_recompute_canvas = true;
_needs_recompute_clip = true;
_canvas_computed.clear();
}
/**
@ -97,7 +96,7 @@ cull_callback(CullTraverser *trav, CullTraverserData &data) {
if (_needs_recompute_clip) {
recompute_clip();
}
if (_needs_recompute_canvas) {
if (!_canvas_computed.test_and_set()) {
recompute_canvas();
}
return PGVirtualFrame::cull_callback(trav, data);
@ -257,7 +256,7 @@ remanage() {
// Showing or hiding one of the scroll bars might have set this flag again
// indirectly; we clear it again to avoid a feedback loop.
_needs_remanage = false;
}
}
// Are either or both of the scroll bars hidden?
if (got_horizontal && _horizontal_slider->is_overall_hidden()) {
@ -329,19 +328,20 @@ item_draw_mask_changed(PGItem *) {
*/
void PGScrollFrame::
slider_bar_adjust(PGSliderBar *) {
LightReMutexHolder holder(_lock);
_needs_recompute_canvas = true;
// Indicate that recompute_canvas() needs to be called.
_canvas_computed.clear();
}
/**
* Recomputes the clipping window of the PGScrollFrame, based on the position
* of the slider bars.
*
* Assumes the lock is held.
*/
void PGScrollFrame::
recompute_clip() {
LightReMutexHolder holder(_lock);
_needs_recompute_clip = false;
_needs_recompute_canvas = true;
_canvas_computed.clear();
// Figure out how to remove the scroll bars from the clip region.
LVecBase4 clip = get_frame_style(get_state()).get_internal_frame(get_frame());
@ -361,34 +361,40 @@ recompute_clip() {
/**
* Recomputes the portion of the virtual canvas that is visible within the
* PGScrollFrame, based on the values of the slider bars.
*
* Assumes the lock is held.
*/
void PGScrollFrame::
recompute_canvas() {
LightReMutexHolder holder(_lock);
_needs_recompute_canvas = false;
const LVecBase4 &clip = _has_clip_frame ? _clip_frame : get_frame();
const LVecBase4 &clip = get_clip_frame();
// Set this to true before we sample the slider bar ratios.
// If slider_bar_adjust happens to get called while we do this, no big deal,
// this method will just be called again next frame.
_canvas_computed.test_and_set();
PN_stdfloat x = interpolate_canvas(clip[0], clip[1],
_virtual_frame[0], _virtual_frame[1],
_horizontal_slider);
PN_stdfloat cx, cy;
cx = interpolate_canvas(clip[0], clip[1],
_virtual_frame[0], _virtual_frame[1],
_horizontal_slider);
PN_stdfloat y = interpolate_canvas(clip[3], clip[2],
_virtual_frame[3], _virtual_frame[2],
_vertical_slider);
cy = interpolate_canvas(clip[3], clip[2],
_virtual_frame[3], _virtual_frame[2],
_vertical_slider);
get_canvas_node()->set_transform(TransformState::make_pos(LVector3::rfu(x, 0, y)));
_canvas_node->set_transform(TransformState::make_pos(LVector3::rfu(cx, 0, cy)));
}
/**
* Computes the linear translation that should be applied to the virtual
* canvas node, based on the corresponding slider bar's position.
*
* Assumes the lock is held.
*/
PN_stdfloat PGScrollFrame::
interpolate_canvas(PN_stdfloat clip_min, PN_stdfloat clip_max,
PN_stdfloat canvas_min, PN_stdfloat canvas_max,
PGSliderBar *slider_bar) {
LightReMutexHolder holder(_lock);
PN_stdfloat t = 0.0f;
if (slider_bar != nullptr) {
t = slider_bar->get_ratio();

View File

@ -20,6 +20,10 @@
#include "pgSliderBarNotify.h"
#include "pgSliderBar.h"
#ifdef PHAVE_ATOMIC
#include <atomic>
#endif
/**
* This is a special kind of frame that pretends to be much larger than it
* actually is. You can scroll through the frame, as if you're looking
@ -92,7 +96,7 @@ private:
private:
bool _needs_remanage;
bool _needs_recompute_clip;
bool _needs_recompute_canvas;
std::atomic_flag _canvas_computed;
bool _has_virtual_frame;
LVecBase4 _virtual_frame;

View File

@ -72,7 +72,7 @@ protected:
private:
void setup_child_nodes();
private:
protected:
bool _has_clip_frame;
LVecBase4 _clip_frame;

View File

@ -16,6 +16,14 @@
#ifdef MUTEX_SPINLOCK
#include "conditionVarSpinlockImpl.h"
#include "trueClock.h"
#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64)
#include <emmintrin.h>
#define PAUSE() _mm_pause()
#else
#define PAUSE()
#endif
/**
*
@ -23,12 +31,31 @@
void ConditionVarSpinlockImpl::
wait() {
AtomicAdjust::Integer current = _event;
_mutex.release();
_mutex.unlock();
while (AtomicAdjust::get(_event) == current) {
PAUSE();
}
_mutex.acquire();
_mutex.lock();
}
/**
*
*/
void ConditionVarSpinlockImpl::
wait(double timeout) {
TrueClock *clock = TrueClock::get_global_ptr();
double end_time = clock->get_short_time() + timeout;
AtomicAdjust::Integer current = _event;
_mutex.unlock();
while (AtomicAdjust::get(_event) == current && clock->get_short_time() < end_time) {
PAUSE();
}
_mutex.lock();
}
#endif // MUTEX_SPINLOCK

View File

@ -37,6 +37,7 @@ public:
INLINE ~ConditionVarSpinlockImpl();
void wait();
void wait(double timeout);
INLINE void notify();
INLINE void notify_all();

View File

@ -11,21 +11,6 @@
* @date 2008-10-08
*/
/**
*
*/
INLINE LightReMutexDirect::
LightReMutexDirect()
#ifndef HAVE_REMUTEXIMPL
: _cvar_impl(_lock_impl)
#endif
{
#ifndef HAVE_REMUTEXIMPL
_locking_thread = nullptr;
_lock_count = 0;
#endif
}
/**
* Alias for acquire() to match C++11 semantics.
* @see acquire()

View File

@ -29,7 +29,7 @@ class Thread;
*/
class EXPCL_PANDA_PIPELINE LightReMutexDirect {
protected:
INLINE LightReMutexDirect();
LightReMutexDirect() = default;
LightReMutexDirect(const LightReMutexDirect &copy) = delete;
~LightReMutexDirect() = default;
@ -57,7 +57,7 @@ PUBLISHED:
private:
#ifdef HAVE_REMUTEXTRUEIMPL
mutable ReMutexImpl _impl;
mutable ReMutexTrueImpl _impl;
#else
// If we don't have a reentrant mutex, use the one we hand-rolled in

View File

@ -43,6 +43,13 @@ typedef MutexImpl MutexTrueImpl;
#if HAVE_REMUTEXIMPL
typedef ReMutexImpl ReMutexTrueImpl;
#define HAVE_REMUTEXTRUEIMPL 1
#elif MUTEX_SPINLOCK
// This is defined here because it needs code from pipeline.
#include "reMutexSpinlockImpl.h"
typedef ReMutexSpinlockImpl ReMutexTrueImpl;
#define HAVE_REMUTEXTRUEIMPL 1
#else
#undef HAVE_REMUTEXTRUEIMPL
#endif // HAVE_REMUTEXIMPL

View File

@ -13,6 +13,7 @@
#include "reMutex.cxx"
#include "reMutexDirect.cxx"
#include "reMutexHolder.cxx"
#include "reMutexSpinlockImpl.cxx"
#include "thread.cxx"
#include "threadDummyImpl.cxx"
#include "threadPosixImpl.cxx"

View File

@ -33,7 +33,11 @@ ReMutexDirect()
INLINE void ReMutexDirect::
lock() {
TAU_PROFILE("void ReMutexDirect::acquire()", " ", TAU_USER);
#ifdef HAVE_REMUTEXTRUEIMPL
_impl.lock();
#else
((ReMutexDirect *)this)->do_lock();
#endif // HAVE_REMUTEXTRUEIMPL
}
/**
@ -43,7 +47,11 @@ lock() {
INLINE bool ReMutexDirect::
try_lock() {
TAU_PROFILE("void ReMutexDirect::try_acquire()", " ", TAU_USER);
#ifdef HAVE_REMUTEXTRUEIMPL
return _impl.try_lock();
#else
return ((ReMutexDirect *)this)->do_try_lock();
#endif // HAVE_REMUTEXTRUEIMPL
}
/**
@ -53,7 +61,11 @@ try_lock() {
INLINE void ReMutexDirect::
unlock() {
TAU_PROFILE("void ReMutexDirect::unlock()", " ", TAU_USER);
#ifdef HAVE_REMUTEXTRUEIMPL
_impl.unlock();
#else
((ReMutexDirect *)this)->do_unlock();
#endif // HAVE_REMUTEXTRUEIMPL
}
/**

View File

@ -141,11 +141,11 @@ do_elevate_lock() {
* mutex).
*/
void ReMutexDirect::
do_unlock() {
do_unlock(Thread *current_thread) {
_lock_impl.lock();
#ifdef _DEBUG
if (_locking_thread != Thread::get_current_thread()) {
if (_locking_thread != current_thread) {
std::ostringstream ostr;
ostr << *_locking_thread << " attempted to release "
<< *this << " which it does not own";

View File

@ -59,7 +59,7 @@ PUBLISHED:
private:
#ifdef HAVE_REMUTEXTRUEIMPL
mutable ReMutexImpl _impl;
mutable ReMutexTrueImpl _impl;
#else
// If we don't have a reentrant mutex, we have to hand-roll one.
@ -68,7 +68,7 @@ private:
INLINE bool do_try_lock();
bool do_try_lock(Thread *current_thread);
void do_elevate_lock();
void do_unlock();
void do_unlock(Thread *current_thread = Thread::get_current_thread());
Thread *_locking_thread;
int _lock_count;

View File

@ -0,0 +1,23 @@
/**
* 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 reMutexSpinlockImpl.I
* @author rdb
* @date 2018-09-03
*/
/**
*
*/
INLINE void ReMutexSpinlockImpl::
unlock() {
assert(_counter > 0);
if (!--_counter) {
AtomicAdjust::set_ptr(_locking_thread, nullptr);
}
}

View File

@ -0,0 +1,57 @@
/**
* 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 reMutexSpinlockImpl.cxx
* @author rdb
* @date 2018-09-03
*/
#include "selectThreadImpl.h"
#ifdef MUTEX_SPINLOCK
#include "reMutexSpinlockImpl.h"
#include "thread.h"
#if defined(__i386__) || defined(__x86_64) || defined(_M_IX86) || defined(_M_X64)
#include <emmintrin.h>
#define PAUSE() _mm_pause()
#else
#define PAUSE()
#endif
/**
*
*/
void ReMutexSpinlockImpl::
lock() {
Thread *current_thread = Thread::get_current_thread();
Thread *locking_thread = (Thread *)AtomicAdjust::compare_and_exchange_ptr(_locking_thread, nullptr, current_thread);
while (locking_thread != nullptr && locking_thread != current_thread) {
PAUSE();
locking_thread = (Thread *)AtomicAdjust::compare_and_exchange_ptr(_locking_thread, nullptr, current_thread);
}
++_counter;
}
/**
*
*/
bool ReMutexSpinlockImpl::
try_lock() {
Thread *current_thread = Thread::get_current_thread();
Thread *locking_thread = (Thread *)AtomicAdjust::compare_and_exchange_ptr(_locking_thread, nullptr, current_thread);
if (locking_thread == nullptr || locking_thread == current_thread) {
++_counter;
return true;
} else {
return false;
}
}
#endif // MUTEX_SPINLOCK

View File

@ -0,0 +1,54 @@
/**
* 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 reMutexSpinlockImpl.h
* @author rdb
* @date 2018-09-03
*/
#ifndef REMUTEXSPINLOCKIMPL_H
#define REMUTEXSPINLOCKIMPL_H
#include "dtoolbase.h"
#include "selectThreadImpl.h"
#ifdef MUTEX_SPINLOCK
#include "atomicAdjust.h"
class Thread;
/**
* Uses a simple user-space spinlock to implement a mutex. It is usually not
* a good idea to use this implementation, unless you are building Panda for a
* specific application on a specific SMP machine, and you are confident that
* you have at least as many CPU's as you have threads.
*/
class EXPCL_PANDA_PIPELINE ReMutexSpinlockImpl {
public:
constexpr ReMutexSpinlockImpl() noexcept = default;
ReMutexSpinlockImpl(const ReMutexSpinlockImpl &copy) = delete;
ReMutexSpinlockImpl &operator = (const ReMutexSpinlockImpl &copy) = delete;
public:
void lock();
bool try_lock();
INLINE void unlock();
private:
AtomicAdjust::Pointer _locking_thread = nullptr;
unsigned int _counter = 0;
};
#include "reMutexSpinlockImpl.I"
#endif // MUTEX_SPINLOCK
#endif

View File

@ -1056,7 +1056,9 @@ add_collector(PStatClient::Collector *collector) {
// the lock.
int new_collectors_size = (_collectors_size == 0) ? 128 : _collectors_size * 2;
CollectorPointer *new_collectors = new CollectorPointer[new_collectors_size];
memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer));
if (_collectors != nullptr) {
memcpy(new_collectors, _collectors, _num_collectors * sizeof(CollectorPointer));
}
AtomicAdjust::set_ptr(_collectors, new_collectors);
AtomicAdjust::set(_collectors_size, new_collectors_size);
@ -1091,7 +1093,9 @@ add_thread(PStatClient::InternalThread *thread) {
// the lock.
int new_threads_size = (_threads_size == 0) ? 128 : _threads_size * 2;
ThreadPointer *new_threads = new ThreadPointer[new_threads_size];
memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer));
if (_threads != nullptr) {
memcpy(new_threads, _threads, _num_threads * sizeof(ThreadPointer));
}
// We assume that assignment to a pointer and to an int are each atomic.
AtomicAdjust::set_ptr(_threads, new_threads);
AtomicAdjust::set(_threads_size, new_threads_size);

View File

@ -137,15 +137,6 @@ output(std::ostream &out) const {
out << get_name();
}
/**
* Returns a special zero-valued ButtonHandle that is used to indicate no
* button.
*/
INLINE ButtonHandle ButtonHandle::
none() {
return _none;
}
/**
* ButtonHandle::none() evaluates to false, everything else evaluates to true.
*/

View File

@ -14,9 +14,6 @@
#include "buttonHandle.h"
#include "buttonRegistry.h"
// This is initialized to zero by static initialization.
ButtonHandle ButtonHandle::_none;
TypeHandle ButtonHandle::_type_handle;
/**

View File

@ -53,7 +53,7 @@ PUBLISHED:
constexpr int get_index() const;
INLINE void output(std::ostream &out) const;
INLINE static ButtonHandle none();
constexpr static ButtonHandle none() { return ButtonHandle(0); }
INLINE operator bool () const;
@ -65,7 +65,6 @@ PUBLISHED:
private:
int _index;
static ButtonHandle _none;
public:
static TypeHandle get_class_type() {

View File

@ -2951,7 +2951,7 @@ setup_material(GLMaterial *gl_material, const Material *material) {
_color_material_flags = CMF_ambient | CMF_diffuse;
if (material->has_ambient()) {
if (material->has_ambient() || material->has_base_color()) {
const LColor &ambient = material->get_ambient();
gl_material->ambient.v[0] = ambient[0];
gl_material->ambient.v[1] = ambient[1];
@ -2961,7 +2961,7 @@ setup_material(GLMaterial *gl_material, const Material *material) {
_color_material_flags &= ~CMF_ambient;
}
if (material->has_diffuse()) {
if (material->has_diffuse() || material->has_base_color()) {
const LColor &diffuse = material->get_diffuse();
gl_material->diffuse.v[0] = diffuse[0];
gl_material->diffuse.v[1] = diffuse[1];

View File

@ -0,0 +1,60 @@
from panda3d.core import Mutex, ReMutex
def test_mutex_acquire_release():
m = Mutex()
m.acquire()
# Assert that the lock is truly held now
assert m.debug_is_locked()
# Release the lock
m.release()
# Make sure the lock is properly released
assert m.try_acquire()
# Clean up
m.release()
def test_mutex_try_acquire():
m = Mutex()
# Trying to acquire the lock should succeed
assert m.try_acquire()
# Assert that the lock is truly held now
assert m.debug_is_locked()
# Clean up
m.release()
def test_remutex_acquire_release():
m = ReMutex()
m.acquire()
m.acquire()
m.release()
m.release()
def test_remutex_try_acquire():
m = ReMutex()
# Trying to acquire the lock should succeed
assert m.try_acquire()
# Should report being locked
assert m.debug_is_locked()
# Trying a second time should succeed
assert m.try_acquire()
# Should still report being locked
assert m.debug_is_locked()
# Clean up
m.release()
m.release()